Game Development Community

FoliageReplicator Upgrades For TSE

by J.C. Smith · in Torque Game Engine Advanced · 01/18/2007 (10:27 pm) · 64 replies

Hello all. I was looking at this resource the other day from Jeff Loveless:

http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=11963

which implemented clustering and multiple foliage textures for TGE. So I decided to port that resource to TSE. The clustering works in the same way though needed some code changes for the TSE version. Along the way though I decided to do the multi foliage types using a single texture. It works by using a flag to denote if your foliage is normal or a QuadTexture (the UseQuadTexture flag in Mission Editor). So you'd just make a PNG or DDS file and split it into 4 imaginary quadrants, and paste a foliage texture in each of the 4 quadrants. If you leave the quadtexture flag unchecked then it works as normal.

As another bonus I added in some simple color variations. This will allow you to take you texture and blend some different colors with it to give some variation so all your blades of grass aren't the same color for example. That works by setting three four fields: GradientTexture, UseBlend, BlendStart and BlendEnd. If you check the UseBlend flag then it will activate this effect. BlendStart and BlendEnd are a float value from 0.0 to 1.0, which reflects which parts of the gradient texture range you want to use. The GradientTexture is a texture file that you will need to create in photoshop or whatever that stores the color range vertically. It only uses the Y pixels, and is always using the first pixel X wise. So for example if you made a 64x64 texture, and the first line started with a green color, and moving down the Y axis by the time you reached the bottom line it was yellow, and you set the BlendStart to 0 and the BlendEnd to 1 then your foliage would be colored a random shade of green to yellow. If you set the blendEnd to 0.5 then it would start at green and end at a greenishyellow variant. If your using a grayscale image, this allows you to make some nice variations on your foliage colors.

I'm pasting the source code into here in it's entirety for the four files which require changes:

shaders/FxFoliageReplicatorP.hlsl

#include "shdrConsts.h"

//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
struct ConnectData
{
   float2 texCoord        : TEXCOORD0;
   float4 lum		  : COLOR0;
   float4 groundAlphaCoeff : COLOR1;
   float2 alphaLookup	  : TEXCOORD1;
   float2 blendColor      : TEXCOORD2;
};

struct Fragout
{
   float4 col : COLOR0;
};

//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
Fragout main( ConnectData IN,
              uniform sampler2D diffuseMap      : register(S0),
              uniform sampler2D alphaMap		: register(S1),
              uniform sampler2D blendMap        : register(S2),
              uniform float4    groundAlpha     : register(C1)       
)
{
   Fragout OUT;

   float4 alpha = tex2D(alphaMap, IN.alphaLookup);
   OUT.col = IN.lum * (tex2D(diffuseMap, IN.texCoord) * tex2D(blendMap, IN.blendColor));
   OUT.col.a = OUT.col.a * min(alpha, groundAlpha + IN.groundAlphaCoeff.x);
   
   return OUT;
}
#41
03/11/2007 (5:44 pm)
Would anyone have any idea how to stop the foliage billboard from rotating and how to add 2 additional planes in a crosshatch fashion?

cheers

addikt

EDIT: In case anyones wondering heres some testing weve done with the foliage replicator...


img90.imageshack.us/img90/5023/flowergrass1wa3.th.jpg

img263.imageshack.us/img263/8220/grasstest8hh9.th.jpg
#42
03/12/2007 (5:53 am)
For the forward facing, it currently orients them all in the vertex shader. I don't have much time to toy with it atm, but just peeked at it quickly, if you comment out the o[_][_] = [_] area then you won't be forward facing. You'd then have to modify the C++ code to change the orientation and store it for each

There's probably more to it than that but I don't have time to try anything. That should get you started.

Those screenshots look very nice btw.
#43
03/12/2007 (10:28 am)
Foliage underwater: This is no flaw. But for performance reason, the foliage is the second last thing to be rendered (only transparency is rendered after). Due to that, the water and refraction are rendered before foliage so foliage can not show through water / refraction at all.

To alter that, change the order of the types in renderInstMgr.h in RenderInstance folder. (ie move foliage in the enum before water)
This will as well show the foliage through refraction which currently does not happen.
The price is a cut of 50% of the FPS on my system (80 to 40) with a simple Atlas2 Terrain and foliage and water.
#44
03/12/2007 (3:08 pm)
Thanks guys. I got it showing underwater. I managed to stop the rotation, but it would then require multiplane grass to make it look ok, which would probably be no real benefit to the single plane rotating billboard...

Is it possible to make a replicator dedicated to grass? We are basically trying to achieve density without cutting fps in half...

addikt
#45
03/12/2007 (11:54 pm)
There are two ideas that come to mind...

First is combining the existing atlas shaders w/ a fur shader. That would create a "fur" of grass, but it would cover the entire terrain.

The second is way trickier. There was a resource for TGE where you could restrict foliage to specific blended terrain textures. It might be possible to modify that resource to work with blended atlas terrains.
#46
03/13/2007 (2:45 am)
Alternatively you could try to modify the fxGrassReplicator for TGE to work with TGEA.
In the first you wouldn't even need special shaders for it. Just clone the fxFoliageReplicator shaders.

As the fxGrass is itself a modified FoliageReplicator, it should be possible to port it by looking at the laters TGE and TGEA versions.
#47
03/13/2007 (3:30 pm)
What sort of things would be involved in porting the fxGrassRep from TGE over to TGEA? Ive never done anything like that before. I assume that you would have to ensure it has all the right include references and the shaders would need to be copied from the foliage Rep... Is there anything else required? Did anyone ever try that fxGrassRep and was it good performance wise over the foliage rep?

Cheers guys!

addikt
#48
03/13/2007 (5:08 pm)
I looked over the code just now, looks most of it it would be an easy port. There is a glrotatef though that will not port over directly. You basically need to pass the angle to the shader, so you'll have to expose it someplace and pass it through a constant in the renderobject or setupbuffers area in C++. That seems to be the main problem, but I only looked quickly. Busy couple weeks at work readying for LOTRO launch.
#49
03/14/2007 (5:02 pm)
Where do you work JC? I know a few guys who Beta Tested LOTRO, looks pretty neat!

I might have to get someone a bit more C++ savvy to look at that fxGrass.

addikt
#50
03/14/2007 (7:05 pm)
I run mmog databases for a living, currently I do the databases at mmodb.com.
#51
03/20/2007 (12:33 pm)
Hi !

I'm not sure what I'm doing wrong, but whatever I do
I just can't seem to get this work.

I followed the instructions closely and it compiles OK with these warnings:
(which is ok, as happened with addiktive)

fxFoliageReplicator.cpp
..\engine\game\fx\fxFoliageReplicator.cpp(655) : warning C4244: '=' : conversion from 'float' to 'U32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(656) : warning C4244: '=' : conversion from 'float' to 'U32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(701) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(701) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(701) : warning C4244: '=' : conversion from 'F32' to 'U32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(702) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(702) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(702) : warning C4244: '=' : conversion from 'F32' to 'U32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(711) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(711) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(712) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data
..\engine\game\fx\fxFoliageReplicator.cpp(712) : warning C4244: 'argument' : conversion from 'U32' to 'F32', possible loss of data

1>Torque Game Engine Advanced - 0 error(s), 12 warning(s)
========== Build: 1 succeeded, 0 failed, 5 up-to-date, 0 skipped ==========

but as I start the game, nothing has changed. It's still the same old FxFoliage Replicator
without any cluster options etc.

I've fought with this allready 4 hours, could someone help me please ?

PS. I'm using the TGEA_SDK_Comm-1-0 version
#52
03/20/2007 (2:20 pm)
Still struggling. I've double-triple-hyper-checked everything.
Compiles without errors and still nothing changes in the FXReplicator in the editor.

Arrrrghhhhh
#53
03/20/2007 (2:50 pm)
Are you placing a new foliage replicator or using one already placed?

I assume you are copying all the relavent files from your compile folder to your project folder and have the Shaders etc....

Should be working....

addikt
#54
03/20/2007 (3:40 pm)
Yeah, as I set up the compiler as stated in the Step Four - Configure the VS2005 compiler tutorial.

"Open the "Debugging" dialog as shown below.
Select "All Configurations" from the dropdown in the upper left of the dialog.
Then change the Working Directory to be "..\example": "

That's what I did.
#55
03/20/2007 (3:45 pm)
I mean it still doesn't work :) even as I set it up by following to tutorials.
#56
03/20/2007 (3:53 pm)
Where it should output those compiled files anyway ?
#57
03/20/2007 (4:49 pm)
Well it should be outputting to the example folder. Then personally we have a seperate project folder which I then copy the compiled engine into... But that depends on how you are setup.

If it compiles and you are running the engine from the Example folder then the foliage options should be available. Make sure youve got the foliage replicator shaders too.

Goodluck.

addikt
#58
04/20/2007 (7:30 pm)
Hi.
I am getting these errors when compiling:

1>c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(253) : error C2086: 'U32 fxFoliageReplicator::tagFieldData::mInnerRadiusX' : redefinition
1> c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(233) : see declaration of 'fxFoliageReplicator::tagFieldData::mInnerRadiusX'
1>c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(254) : error C2086: 'U32 fxFoliageReplicator::tagFieldData::mInnerRadiusY' : redefinition
1> c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(234) : see declaration of 'fxFoliageReplicator::tagFieldData::mInnerRadiusY'
1>c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(255) : error C2086: 'U32 fxFoliageReplicator::tagFieldData::mOuterRadiusX' : redefinition
1> c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(235) : see declaration of 'fxFoliageReplicator::tagFieldData::mOuterRadiusX'
1>c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(256) : error C2086: 'U32 fxFoliageReplicator::tagFieldData::mOuterRadiusY' : redefinition
1> c:\program files\garagegames\tgea_sdk\engine\game\fx\fxfoliagereplicator.h(236) : see declaration of 'fxFoliageReplicator::tagFieldData::mOuterRadiusY'

I have checked my code and it "looks" like i haven't made any typos. I am wondering if this is one of those errors that you can turn off checking for.

As it is right now, it wont build with this happening.

Any suggestions?
#59
10/06/2007 (7:49 pm)
I was playing with the replicator today, moving the code over to use non-billboarded. For whoever it was who was asking about that, this code works. However, rather than add extra data I am passing the Y value which was previously used for lighting and I am instead passing the sunlight value in the pixel shader for some custom code (MMOKit). so be aware that this version doesn't work with stock lighting but could get you pointed in the right direction:

In the cpp file:

void fxFoliageReplicator::SetupBuffers()
{
	// Following two arrays are used to build the vertex and primitive buffers.	
	Point3F basePoints[24];
	basePoints[0] = Point3F(-0.5f, -0.5f, 1.0f);
	basePoints[1] = Point3F(-0.5f, -0.5f, 0.0f);
	basePoints[2] = Point3F(0.5f, 0.5f, 0.0f);
	basePoints[3] = Point3F(0.5f, 0.5f, 1.0f);
	
	basePoints[4] = Point3F(-0.5f, 0.0f, 1.0f);
	basePoints[5] = Point3F(-0.5f, 0.0f, 0.0f);
	basePoints[6] = Point3F(0.5f, 0.0f, 0.0f);
	basePoints[7] = Point3F(0.5f, 0.0f, 1.0f);

	basePoints[8] = Point3F(-0.5f, 0.5f, 1.0f);
	basePoints[9] = Point3F(-0.5f, 0.5f, 0.0f);
	basePoints[10] = Point3F(0.5f, -0.5f, 0.0f);
	basePoints[11] = Point3F(0.5f, -0.5f, 1.0f);

	Point2F texCoords[4];
	texCoords[0] = Point2F(0.0, 0.0);
	texCoords[1] = Point2F(0.0, 0.5);
	texCoords[2] = Point2F(0.5, 0.5);
	texCoords[3] = Point2F(0.5, 0.0);
	
	// Init our Primitive Buffer
	U32 indexSize = mFieldData.mFoliageCount * 6;
	U16* indices = new U16[indexSize];
	// Two triangles per particle
	for (U16 i = 0; i < mFieldData.mFoliageCount; i++) {
		U16* idx = &indices[i*6];		// hey, no offset math below, neat
		U16 vertOffset = i*4;
		idx[0] = vertOffset + 0;
		idx[1] = vertOffset + 1;
		idx[2] = vertOffset + 2;
		idx[3] = vertOffset + 2;
		idx[4] = vertOffset + 3;
		idx[5] = vertOffset + 0;
	}
	// Init the prim buffer and copy our indexes over
	U16 *ibIndices;
	mPrimBuffer.set(GFX, indexSize, 0, GFXBufferTypeStatic);
	mPrimBuffer.lock(&ibIndices);
	dMemcpy(ibIndices, indices, indexSize * sizeof(U16));
	mPrimBuffer.unlock();
	delete[] indices;

	U32 rotatetemp = 0;	// this is a temp value used for deciding which way to face the foliage
	// Now, let's init the vertex buffer
	U32 currPrimitiveStartIndex = 0;
	mVertexBuffer.set(GFX, mFieldData.mFoliageCount * 4, GFXBufferTypeStatic);
	mVertexBuffer.lock();
	U32 idx = 0;	
	for (S32 qtIdx = 0; qtIdx < mFoliageQuadTree.size(); qtIdx++) {
		fxFoliageQuadrantNode* quadNode = mFoliageQuadTree[qtIdx];
		if (quadNode->Level == 0) {
			quadNode->startIndex = currPrimitiveStartIndex;
			quadNode->primitiveCount = 0;
			// Ok, there should be data in here!
			for (S32 i = 0; i < quadNode->RenderList.size(); i++) {
				fxFoliageItem* pFoliageItem = quadNode->RenderList[i];
				if (pFoliageItem->LastFrameSerialID == 0) {
					pFoliageItem->LastFrameSerialID++;
					// Dump it into the vertex buffer
					rotatetemp = rotatetemp +4;
					if (rotatetemp > 11)
						rotatetemp = 0;
					for (U32 vertIndex = 0; vertIndex < 4; vertIndex++) {
						GFXVertexFoliage *vert = &mVertexBuffer[(idx*4) + vertIndex];
						// This is the position of the billboard.
						vert->point = pFoliageItem->Transform.getPosition();			
						// Normal contains the point of the billboard (except for the y component, see below)
						vert->normal = basePoints[vertIndex+rotatetemp];


						vert->normal.x *= pFoliageItem->Width;
						vert->normal.y *= pFoliageItem->Width;
						vert->normal.z *= pFoliageItem->Height;
						// Handle texture coordinates
						vert->texCoord = texCoords[vertIndex]; 				
						if (pFoliageItem->Flipped)
							vert->texCoord.x = 1.0f - vert->texCoord.x;
						vert->texCoord.x = vert->texCoord.x + pFoliageItem->quadTextureX;	// J.C. add our x/y offset for quad foliages
						vert->texCoord.y = vert->texCoord.y + pFoliageItem->quadTextureY;
						// Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, 
						// the y coordinate determines if this vertex actually sways or not.
						if ((vertIndex == 0) || (vertIndex == 3)) {
							vert->texCoord2.set(pFoliageItem->SwayTimeRatio / mGlobalSwayTimeRatio, 1.0f);
						} else {
							vert->texCoord2.set(0.0f, 0.0f);
						}
						vert->texCoord3.x = 0.0f;
						vert->texCoord3.y = pFoliageItem->blendColor,pFoliageItem->blendColor;
						// Handle lighting, lighting happens at the same time as global so this is just an offset.
						//vert->normal.y = pFoliageItem->LightPhase;
					}
					idx++;
					quadNode->primitiveCount += 2;
					currPrimitiveStartIndex += 6; 
				}
			}
		}
	}
	mVertexBuffer.unlock();	

	DestroyFoliageItems();
}
#60
10/06/2007 (7:51 pm)
And because this wouldn't fit in the above post, here's the shader code for non-billboarding:

Pixel Shader:

#include "shdrConsts.h"

//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
struct ConnectData
{
   float2 texCoord        : TEXCOORD0;
   float4 groundAlphaCoeff : COLOR1;
   float2 alphaLookup	  : TEXCOORD1;
   float2 blendColor      : TEXCOORD2;
};

struct Fragout
{
   float4 col : COLOR0;
};

//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
Fragout main( ConnectData IN,
              uniform sampler2D diffuseMap      : register(S0),
              uniform sampler2D alphaMap			: register(S1),
              uniform float4    groundAlpha     : register(C1),              
              uniform sampler2D blendMap        : register(S2),
		  uniform float sunlight : register (C6)
)
{
   Fragout OUT;

	float4 alpha = tex2D(alphaMap, IN.alphaLookup);
   OUT.col = sunlight * (tex2D(diffuseMap, IN.texCoord) * tex2D(blendMap, IN.blendColor));
   OUT.col.a = OUT.col.a * min(alpha, groundAlpha + IN.groundAlphaCoeff.x);
   
   return OUT;
}

Vertex Shader:
//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
struct VertData
{
   float2 texCoord   : TEXCOORD0;
   float2 waveScale	: TEXCOORD1;
   float3 normal     : NORMAL;
   float4 position   : POSITION;
   float2 blendColor : TEXCOORD2;
};

struct ConnectData
{
   float4 hpos            : POSITION;
   float2 outTexCoord     : TEXCOORD0;
   float4 groundAlphaCoeff : COLOR1;
	float2 alphaLookup	  : TEXCOORD1;   
	float2 outBlendColor	  : TEXCOORD2;
};

//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
ConnectData main( VertData IN,
                  uniform float4x4 projection     		: register(C0),
                  uniform float4x4 world	   					: register(C4),
                  uniform float    GlobalSwayPhase 		: register(C8),
                  uniform float 	 SwayMagnitudeSide 	: register(C9),
                  uniform float 	 SwayMagnitudeFront	: register(C10),
                  uniform float    GlobalLightPhase		: register(C11),
                  uniform float    LuminanceMagnitude : register(C12),
                  uniform float		 LuminanceMidpoint	: register(C13),
                  uniform float	DistanceRange : register(C14),
                  uniform float3 CameraPos : register(C15)
)
{
  ConnectData OUT;

	// Init a transform matrix to be used in the following steps
	float4x4 trans = 0;
	trans[0][0] = 1;
	trans[1][1] = 1;
	trans[2][2] = 1;
	trans[3][3] = 1;
	trans[0][3] = IN.position.x;
	trans[1][3] = IN.position.y;
	trans[2][3] = IN.position.z;
	
	// Billboard transform * world matrix
	float4x4 o = world;
	o = mul(o, trans);		
	
	// Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, 
	// the y coordinate determines if this vertex actually sways or not.
	float xSway, ySway;
	float wavePhase = GlobalSwayPhase * IN.waveScale.x;
	sincos(wavePhase, ySway, xSway);
	xSway = xSway * IN.waveScale.y * SwayMagnitudeSide;
	ySway = ySway * IN.waveScale.y * SwayMagnitudeFront;
	float4 p;	
	p = mul(o, float4(IN.normal.x + xSway, IN.normal.y +ySway, IN.normal.z, 1));
		
	// Project the point	
	OUT.hpos = mul(projection, p);
	

	// Alpha
	float3 worldPos = float3(IN.position.x, IN.position.y, IN.position.z);
	float alpha = abs(distance(worldPos, CameraPos)) / DistanceRange;			
	alpha = clamp(alpha, 0.0f, 1.0f); //pass it through	

	OUT.alphaLookup = float2(alpha, 0.0f);
	OUT.groundAlphaCoeff = all(IN.normal.z);
	OUT.outTexCoord = IN.texCoord;	
      OUT.outBlendColor = IN.blendColor;

	return OUT;
}

Basically this version just alternates between three different angles, better ways to do it, one way would be to line them up so they display in a star or a triangle shape around the same area. Don't want to spend too much time on it though and this works.