Game Development Community

Textured Atlas Terrain

by Vincent BILLET · in Torque Game Engine Advanced · 01/06/2006 (3:19 am) · 3 replies

The main issue (In my opinion) for Atlas is textured terrains.

For this I made a simple shader, and some code modifications. But before coding, you must understand How it works.

Texture Library
Each Pixel of the terrain (in TQT file) represent a texture. I wanted 64 textures for my terrain. So I made big texture(2048x2048) filled with 8x8 texture of 256x256 pixel size.

Blending Texture
I made a texture for blending terrain. The idea is to use this "blending texture" as 4 alpha channels.
void createBlend()
{
   GBitmap tmp(512, 512, false, GFXFormatR8G8B8A8);
   FileStream fsP;
   if(!ResourceManager->openFileForWrite(fsP, "AW/data/blend.png", File::Write))
   {
      Con::errorf("SavePNG - failed to open output file!");
      return ;
   }
   ColorI Color;
   int rr,gg,bb,aa;
   rr=255;
   aa=0;
   for (int yy=0;yy<512;yy++)
   {
	    gg=255;
		bb=0;
		for (int xx=0;xx<512;xx++)
		{
			if (rr>0) {Color.red=rr;} else {Color.red=0;}
			if (gg>0) {Color.green=gg;} else {Color.green=0;}
			if (bb>0) {Color.blue=bb;} else {Color.blue=0;}
			if (aa>0) {Color.alpha=aa;} else {Color.alpha=0;}
			tmp.setColor(xx,yy,Color);
			if (gg>=0) {gg--;} else {bb++;}
		}
		if (rr>=0) {rr--;} else {aa++;}

   }
   tmp.writePNG(fsP);
   fsP.close();

}

You can call this procedure from where you want.

Atlas update
In void AtlasInstance::renderObject(SceneState *state, SceneRenderImage *image)
void AtlasInstance::renderObject(SceneState *state, SceneRenderImage *image)
{
   PROFILE_START(ChunkInstance_renderObject);
   GFX->pushState();
   // Set up rendering state
   GFX->disableShaders();
   GFX->setTextureStageColorOp( 0, GFXTOPModulate );
   GFX->setTextureStageColorOp( 1, GFXTOPModulate );
   GFX->setTextureStageColorOp( 2, GFXTOPModulate );
   GFX->setTextureStageColorOp( 3, GFXTOPModulate );
   GFX->setTextureStageColorOp( 4, GFXTOPDisable );
   GFX->setAlphaBlendEnable(false);
just after GFX->setAlphaBlendEnable(false);

GFX->setTextureStageMagFilter(0, GFXTextureFilterPoint );//<=========Terrain
   GFX->setTextureStageMinFilter(0, GFXTextureFilterPoint );
   GFX->setTextureStageMipFilter(0, GFXTextureFilterPoint);

   GFX->setTextureStageMagFilter(1, GFXTextureFilterLinear );//<=========Fog
   GFX->setTextureStageMinFilter(1, GFXTextureFilterLinear );
   GFX->setTextureStageMipFilter(1, GFXTextureFilterLinear);

   GFX->setTextureStageMagFilter(2, GFXTextureFilterLinear );//<=========Detail
   GFX->setTextureStageMinFilter(2, GFXTextureFilterLinear );
   GFX->setTextureStageMipFilter(2, GFXTextureFilterLinear);

   GFX->setTextureStageMagFilter(3, GFXTextureFilterLinear );//<=========Texture Library (2048x2048)
   GFX->setTextureStageMinFilter(3, GFXTextureFilterLinear );
   GFX->setTextureStageMipFilter(3, GFXTextureFilterLinear);

   GFX->setTextureStageMagFilter(4, GFXTextureFilterLinear );//<=========Blending texture (made with createBlend)
   GFX->setTextureStageMinFilter(4, GFXTextureFilterLinear );
   GFX->setTextureStageMipFilter(4, GFXTextureFilterLinear);
   GFX->setTextureStageAddressModeU( 0, GFXAddressClamp );
   GFX->setTextureStageAddressModeV( 0, GFXAddressClamp );
   // ^=- This texture is set by the quad tree as we recurse.

   GFX->setTextureStageAddressModeU( 1, GFXAddressClamp );
   GFX->setTextureStageAddressModeV( 1, GFXAddressClamp );
   GFX->setTexture(1, gClientSceneGraph->getFogTexture());

   // And the detail textures...
   GFX->setTextureStageAddressModeU( 2, GFXAddressWrap  );
   GFX->setTextureStageAddressModeV( 2, GFXAddressWrap  );
   GFX->setTexture(2, mDetailTex);

   GFX->setTextureStageAddressModeU( 3, GFXAddressClamp  );
   GFX->setTextureStageAddressModeV( 3, GFXAddressClamp  );
   GFX->setTexture(3, mDetailTex1);
   GFX->setTextureStageAddressModeU( 4, GFXAddressWrap  );
   GFX->setTextureStageAddressModeV( 4, GFXAddressWrap  );
   GFX->setTexture(4, mDetailTex2);

#1
01/06/2006 (3:20 am)
Continued due to post limit.
In bool AtlasInstance::onAdd(). Just after mDetailTex1.set(mDetailName1, &GFXDefaultStaticDiffuseProfile);
mDetailTex1.set(mDetailName2, &GFXDefaultStaticDiffuseProfile);
      mDetailTex2.set(mDetailName2, &GFXDefaultStaticDiffuseProfile);

In AtlasInstance::AtlasInstance(), add just after mDetailName1 = StringTable->insert("terrain_water_demo/data/terrains/details/detail1");
mDetailName1   = StringTable->insert("terrain_water_demo/data/terrains/details/detail1");
   mDetailName2   = StringTable->insert("terrain_water_demo/data/terrains/details/detail1");

In void AtlasInstance::initPersistFields(), just add before the end
addField("detailTexture1",  TypeFilename,  Offset(mDetailName1,     AtlasInstance), 
      "Texture Library.");
   addField("detailTexture2",  TypeFilename,  Offset(mDetailName2,     AtlasInstance), 
      "Blending.");

In U32 AtlasInstance::packUpdate(NetConnection * conn, U32 mask, BitStream *stream) just add
stream->writeString(mDetailName1);
   stream->writeString(mDetailName2);

In void AtlasInstance::unpackUpdate(NetConnection * conn, BitStream *stream) just add
mDetailName1   = stream->readSTString();
   mDetailName2   = stream->readSTString();

In atlasInstance.h, just after GFXTexHandle mDetailTex; add:
StringTableEntry        mDetailName1;
   StringTableEntry        mDetailName2;
   GFXTexHandle            mDetailTex1;
   GFXTexHandle            mDetailTex2;
#2
01/06/2006 (3:20 am)
Continued due to post limit.
Shaders
Here are the shaders I used : atlasSurfaceV.hlsl
#define IN_HLSL
#include "shdrConsts.h"

//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
struct VertData
{
   float4 position        : POSITION;
   float2 texCoord        : TEXCOORD0;
//   float3 normal          : NORMAL;
};


struct ConnectData
{
   float4 hpos            : POSITION;
   float2 texCoord        : TEXCOORD0;
   float2 fogCoord        : TEXCOORD1;
   float2 detailCoord     : TEXCOORD2;
   float2 detailCoord2    : TEXCOORD3;
};

//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
ConnectData main( VertData IN,
                  uniform float4x4 modelview       : register(VC_WORLD_PROJ),
                  uniform float4x4 objTrans        : register(VC_OBJ_TRANS),
                  uniform float3   eyePos          : register(VC_EYE_POS),
                  uniform float3   fogData         : register(VC_FOGDATA),
                  uniform float4   texGen          : register(C44),
                  uniform float4   scaleOff        : register(C45),
                  uniform float4   morphInfo       : register(C46)
                  )
{
   ConnectData OUT;
   
   // Do positional transform on the heightfield data, and apply morph.
   float4  realPos;
   realPos.x = IN.position.x * scaleOff.z + scaleOff.x;
   realPos.y = IN.position.y * scaleOff.z + scaleOff.y;
   realPos.z = IN.position.z + IN.texCoord.x * morphInfo.x;
   realPos.w = IN.position.w;
   
   OUT.hpos        = mul(modelview, realPos);
   float3 worldPos = mul(objTrans,  realPos.xyz);
   float3 worldEye = mul(objTrans,  eyePos);

   /// Generate texture co-ords.
   OUT.texCoord.y = realPos.x * texGen.z + texGen.x;
   OUT.texCoord.x = realPos.y * texGen.z + texGen.y;

   // And fog texture co-ords.   
   OUT.fogCoord.x = 1.0 - ( distance( worldPos, worldEye ) / fogData.z );
   OUT.fogCoord.y = (worldPos.z - fogData.x) * fogData.y;

   // And detail texture co-ords.

   OUT.detailCoord  = realPos / 8;
   OUT.detailCoord2 = realPos /8;

   return OUT;
}

atlasSurfaceP.hlsl
//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
struct ConnectData
{
   float2 texCoord        : TEXCOORD0;
   float2 fogCoord        : TEXCOORD1;
   float2 detailCoord     : TEXCOORD2;
   float2 detailCoord2    : TEXCOORD3;
};


struct Fragout
{
   float4 col : COLOR0;
};


float4 Couleur(float4 diffuseColor, sampler2D detailMap1, float2 TC)
{
   float num,num1;
   float2 CoordVB;
   num = int(diffuseColor.r*20);//-1.0;
   num1 = int(diffuseColor.b*20);//-1.0;
   CoordVB.x = (TC.x-int(TC.x))/8.0;
   CoordVB.y = (TC.y-int(TC.y))/8.0;
   if (CoordVB.x<=1/2) {CoordVB.x +=1/512;} else {CoordVB.x -=1/512;}
   if (CoordVB.y<=1/2) {CoordVB.y +=1/512;} else {CoordVB.y -=1/512;}
   CoordVB.x += num/8.0;
   CoordVB.y += num1/8.0;
   return tex2D(detailMap1,  CoordVB);
   
}

//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
Fragout main( ConnectData IN,
              uniform sampler2D diffuseMap      : register(S0),
              uniform sampler2D fogMap          : register(S1),
              uniform sampler2D detailMap       : register(S2),
              uniform sampler2D detailMap1      : register(S3),
              uniform sampler2D detailMap2      : register(S4),
              uniform sampler2D detailMap3      : register(S5),
              uniform sampler2D detailMap4      : register(S6),
              uniform sampler2D detailMap5      : register(S7)
              )
{
   Fragout OUT;
   float Offset = 1.0/512.0;
   float4 color,colorU,colorD,colorR,colorL,couleur;

   float4 diffuseColor = tex2D(diffuseMap,IN.texCoord);
   color = Couleur(diffuseColor, detailMap1, IN.detailCoord2);

   diffuseColor = tex2D(diffuseMap,float2(IN.texCoord.x,IN.texCoord.y+Offset));
   colorU = Couleur(diffuseColor, detailMap1, IN.detailCoord2);
   diffuseColor = tex2D(diffuseMap,float2(IN.texCoord.x,IN.texCoord.y-Offset));
   colorD = Couleur(diffuseColor, detailMap1, IN.detailCoord2);
   diffuseColor = tex2D(diffuseMap,float2(IN.texCoord.x+Offset,IN.texCoord.y));
   colorL = Couleur(diffuseColor, detailMap1, IN.detailCoord2);
   diffuseColor = tex2D(diffuseMap,float2(IN.texCoord.x-Offset,IN.texCoord.y));
   colorR = Couleur(diffuseColor, detailMap1, IN.detailCoord2);

   float4 coulb = tex2D(detailMap2,IN.detailCoord*4)/2;
   color = lerp(color,colorR,coulb.r);
   color = lerp(color,colorD,coulb.g);
   color = lerp(color,colorU,coulb.b);
   color = lerp(color,colorL,coulb.a);

   float4 fogCol       = tex2D(fogMap,    IN.fogCoord);
   float4 detailCol    = tex2D(detailMap, IN.detailCoord);

   OUT.col = lerp( color *(diffuseColor.g*2+0.4), fogCol, fogCol.a );

   return OUT;
}
#3
01/06/2006 (3:22 am)
Continued due to post limit.
How to use this code with HTC ?
Go to the texture window. Select a colorset (for example the first). set the colors to 10 40 0 - 20 40 0 - 30 40 0 - etc.. Select the second colorset and the the colors : 10 40 10 - 20 40 10 - 30 40 10 - etc.. and so on for each textureset. Set Bumping to 0. Create your texture Library with texture of equivalent color.
www.solu-si.com/dragonhead/shots/htctex2.jpg
Final Words
You must understand, as I said in beginning of this post, that each color represent a texture. Red component is the "x" Texture coordinate in Library, blue is the "y" coordinate. e.g. I want to paint the texture placed in 2,3 in my texture library. The color is equal to 20,?,30.
The green component is used to make shadows. the less this value is, the more shadows are.

I Hope this migth help some of you, and perhaps some gives some ideas to improve this.

www.solu-si.com/dragonhead/shots/aw11.jpg