Here is the texture splatting module with dynamic management

emre2345

09-01-2009 23:55:08

Hi, i wrote the texture splatting module from the beginning with dynamic management of terrain material. It creates texture units and passes when needed automatically so the user don' t have to write texturing pass to the material. Here is the code:

TextureSplatting.h

#pragma once

/*
* Texture types to create
*/
enum eTEXTURETYPE
{
eCOVERAGEMAP, eSPLATTINGTEXTURE,
};

class CLSplattingManager
{
public:
/*
* material: Terrain material
* szBaseTextureName: base texture of terrain
* nTextHeight: texture height
* nTextWidth : texture width
*/
CLSplattingManager(MaterialPtr material, String szBaseTextureName, int nTextHeight, int nTextWidth);
~CLSplattingManager();

/*
* iVertexX: x index of vertex when the left mouse button clicked
* iVertexZ: y index of vertex when the left mouse button clicked
*/
void PaintTerrain(int iVertexX, int iVertexZ, float fPower);

/*
* Creates covarege map
*/
void CreateMap();

/*
* Adds texture unit state to the material
*/
void AddTextureToMaterial(String szTextureName, eTEXTURETYPE whichType);

/*
* Adds pass to the material when the texture count is more than 16
*/
void AddPassToMaterial(String szPassName);

/*
* Gets the coverage map named szMapName
*/
CLTextureMap *GetCoverageMap(const Ogre::String &szMapName);

void SaveMaps();

/*
* Sets the selected texture name
*/
void SetSelectedTexture(const Ogre::String &szTextureName);

/*
* Activates the selected texture
*/
void SetActiveTexture();

inline const Ogre::String &GetBaseTexture(){return szBaseTextureName;}

private:
//szTextureName: name of the texture which will be used for splatting
int UpdateMaterial(String szTextureName);
//Gets the coverage map of the splatting texture
std::pair<int, CLTextureMap *> GetActiveCoverageMap(String szTextureName);
//Sets the balance between other weights
void Balance(int xPos, int yPos);
//Updates all coverage maps
void UpdateMaps();

MaterialPtr terrainMaterial;
Pass *pActivePass;

//Height of coverage maps
int nHeight;
//Width of coverage maps
int nWidth;
//Texture which is selected
int nTextureID;

Ogre::String szBaseTextureName;

std::pair<int, CLTextureMap *> activeMap;

std::list<CLTextureMap *> coverageMapList;
typedef std::pair<eTEXTURETYPE, String> texturePair;
std::list<texturePair> textureList;

String szSelectedTexture;
};


TextureSplatting.cpp

#include "TextureSplatting.h"

CLSplattingManager::CLSplattingManager(MaterialPtr material, String szBaseTextureName,
int nTextHeight, int nTextWidth)
{
this->terrainMaterial = material;
this->nHeight = nTextHeight;
this->nWidth = nTextWidth;
this->pActivePass = material->getTechnique("Default")->getPass("Texturing");

AddPassToMaterial("Texturing");
CreateMap();
AddTextureToMaterial(szBaseTextureName, eSPLATTINGTEXTURE);

this->szBaseTextureName = szBaseTextureName;
}

CLSplattingManager::~CLSplattingManager()
{
list<CLTextureMap *>::iterator Iter;

for(Iter = coverageMapList.begin(); Iter != coverageMapList.end(); ++Iter)
{
CLTextureMap *map = (*Iter);
coverageMapList.erase(Iter);
delete map;
}
}

void CLSplattingManager::PaintTerrain(int iVertexX, int iVertexZ, float fPower)
{
iVertexX -= CLBrush::GetSingleton()->GetBrushWidth() / 2;
iVertexZ -= CLBrush::GetSingleton()->GetBrushHeight() / 2;

for(int i = 0; i < CLBrush::GetSingleton()->GetBrushWidth(); ++i)
{
int nPosX = iVertexX + i;
if(nPosX < 0 || nPosX >= activeMap.second->GetWidth())
continue;

for(int j = 0; j < CLBrush::GetSingleton()->GetBrushHeight(); j++)
{
int nPosZ = iVertexZ + j;
if(nPosZ < 0 || nPosZ >= activeMap.second->GetHeight())
continue;

activeMap.second->SetValueAt(nPosX, nPosZ, activeMap.first, fPower * CLBrush::GetSingleton()->GetValueAt(i, j));

Balance(nPosX, nPosZ);
}
}

UpdateMaps();
}

void CLSplattingManager::SaveMaps()
{
list<CLTextureMap *>::iterator Iter;

for(Iter = coverageMapList.begin(); Iter != coverageMapList.end(); ++Iter)
{
(*Iter)->SaveMap("media/CovMaps/" + (*Iter)->GetName() + ".png");
}
}

void CLSplattingManager::CreateMap()
{
int iMapID = coverageMapList.size();
String szMapName = "CoverageMap" + StringConverter::toString(iMapID);

CLTextureMap *tempMap = new CLTextureMap(iMapID,
szMapName, nWidth, nHeight);

coverageMapList.push_back(tempMap);

AddTextureToMaterial(szMapName, eCOVERAGEMAP);
}

void CLSplattingManager::AddTextureToMaterial(String szTextureName, eTEXTURETYPE whichType)
{
TextureUnitState *pTexUnitState = pActivePass->createTextureUnitState(szTextureName);
pTexUnitState->setName(StringConverter::toString(textureList.size() - 1));
textureList.push_front(texturePair(whichType, szTextureName));
}

void CLSplattingManager::AddPassToMaterial(String szPassName)
{
Technique *pTechnique = terrainMaterial->getTechnique("Default");
Pass *pNewPass = pTechnique->createPass();
pNewPass->setName(szPassName);
pNewPass->setLightingEnabled(false);
pNewPass->setSceneBlending(SceneBlendType::SBT_MODULATE);
pNewPass->setVertexProgram("Shader/TextureVertex");
pNewPass->setFragmentProgram("Shader/TexturePixel");
pActivePass = pNewPass;
}

CLTextureMap *CLSplattingManager::GetCoverageMap(const Ogre::String &szMapName)
{
list<CLTextureMap *>::iterator Iter;

for(Iter = coverageMapList.begin(); Iter != coverageMapList.end(); ++Iter)
{
if((*Iter)->GetName() == szMapName)
return (*Iter);
}

return 0;
}

int CLSplattingManager::UpdateMaterial(String szTextureName)
{
std::list<texturePair>::iterator Iter = textureList.begin();
while(Iter != textureList.end())
{
if((*Iter).second == szTextureName)
return 0;

Iter++;
}

int nTextureNum = textureList.size();
int nPassID = nTextureNum % 16;
int nCovMapID = nTextureNum % 4;

//If the amount of sended textures is more than 16, add a new pass
if(nPassID == 0)
{
AddPassToMaterial("Texturing" + StringConverter::toString(int(nTextureNum / 16)));
}
if(nCovMapID == 0)
{
CreateMap();
}

AddTextureToMaterial(szTextureName, eSPLATTINGTEXTURE);
return 0;
}

std::pair<int, CLTextureMap *> CLSplattingManager::GetActiveCoverageMap(String szTextureName)
{
std::list<texturePair>::iterator Iter;

for(Iter = textureList.begin(); Iter != textureList.end(); ++Iter)
{
if((*Iter).second == szTextureName)
{
for(int i = 0; i < 4; i++)
{
if((*Iter).first == eCOVERAGEMAP)
{
return pair<int, CLTextureMap *>(4 - i, GetCoverageMap((*Iter).second));
}

++Iter;
}
}
}

return std::pair<int, CLTextureMap *>(0, 0);
}

void CLSplattingManager::SetSelectedTexture(const Ogre::String &szTextureName)
{
szSelectedTexture = szTextureName;
}

void CLSplattingManager::Balance( int xPos, int yPos )
{
std::list<CLTextureMap *>::iterator Iter;
float fWeightSum = 0;
//ColourValue pixelColour;

for (Iter = coverageMapList.begin(); Iter != coverageMapList.end(); ++Iter)
{
ColourValue pixelColour = (*Iter)->GetValueAt(xPos, yPos);

fWeightSum += pixelColour.r + pixelColour.g + pixelColour.b;
}

if(fWeightSum > 255)
{
float fWeightDiff = fWeightSum - 255;
float fDiscardValue = fWeightDiff / ((coverageMapList.size() * 3) - 1);

for (Iter = coverageMapList.begin(); Iter != coverageMapList.end(); ++Iter)
{
for(int i = 1; i < 4; i++)
if(i == activeMap.first && (*Iter) == activeMap.second)
continue;
else
(*Iter)->SetValueAt(xPos, yPos, i, -fDiscardValue);
}
}
}

void CLSplattingManager::UpdateMaps()
{
std::list<CLTextureMap *>::iterator Iter;

for(Iter = coverageMapList.begin(); Iter != coverageMapList.end(); ++Iter)
(*Iter)->UpdateImage();
}

void CLSplattingManager::SetActiveTexture()
{
UpdateMaterial(szSelectedTexture);
activeMap = GetActiveCoverageMap(szSelectedTexture);
}


TextureMap.h

#pragma once

class CLTextureMap
{
public:
CLTextureMap(int nId, String szName, size_t width, size_t height);
~CLTextureMap();

size_t GetWidth(){return nWidth;}
size_t GetHeight(){return nHeight;}
size_t GetSize(){return nWidth * nHeight;}
size_t GetByteSize(){return nWidth * nHeight * 4;}
bool IsInitialized(){return bInitialized;}
String GetName(){return szName;}
int GetID(){return nId;}

void SetValueAt(unsigned int xPos, unsigned int yPos, ColourValue newValue);
void SetValueAt(unsigned int xPos, unsigned int yPos, unsigned int channel, float fPower);
ColourValue GetValueAt(unsigned int xPos, unsigned int yPos);
unsigned int GetValueAt(unsigned int xPos, unsigned int yPos, unsigned int channel);

void SaveMap(String szFileName);
void LoadMap(String szFileName, String szGroupName);

void UpdateImage();

private:
int nId;

String szName;

size_t nWidth;
size_t nHeight;

TexturePtr data;
Image mapImage;

uchar *pData;

static bool bInitialized;
};


TextureMap.cpp

#include "TextureMap.h"

bool CLTextureMap::bInitialized = false;

CLTextureMap::CLTextureMap(int nId, Ogre::String szName, size_t width, size_t height)
{
this->nId = nId;
this->szName = szName;
this->nWidth = width;
this->nHeight = height;

data = TextureManager::getSingleton().createManual(szName, "General", TEX_TYPE_2D, unsigned int(width),
unsigned int(height), 1, PF_R8G8B8A8);

pData = OGRE_ALLOC_T(uchar, width * height * 4, MEMCATEGORY_GENERAL);

for(int j = 0; j < nHeight; ++j)
{
for(int i = 0; i < nWidth; ++i)
{
if(nId == 0)
SetValueAt(i, j, ColourValue(255, 0, 0, 255));
else
SetValueAt(i, j, ColourValue(0, 0, 0, 255));
}
}

mapImage.loadDynamicImage(pData, nWidth, nHeight, 1, PF_R8G8B8A8);

data->getBuffer(0, 0)->blitFromMemory(mapImage.getPixelBox(0, 0));

bInitialized = true;
}

CLTextureMap::~CLTextureMap()
{
delete [] pData;
}

void CLTextureMap::SetValueAt(unsigned int xPos, unsigned int yPos, ColourValue newValue)
{
unsigned int index = (xPos + yPos * nWidth) * 4;

pData[index] = (uchar)newValue.a;
pData[index + 1] = (uchar)newValue.b;
pData[index + 2] = (uchar)newValue.g;
pData[index + 3] = (uchar)newValue.r;
}

void CLTextureMap::SetValueAt(unsigned int xPos, unsigned int yPos, unsigned int channel, float fPower)
{
unsigned int index = ((xPos + yPos * nWidth) * 4);

if(channel < 4)
{
float fValue = float(pData[index + channel]);
if((fValue + fPower) <= 255 && (fValue + fPower >= 0))
pData[index + channel] = (uchar)(fValue + fPower);
}
}

ColourValue CLTextureMap::GetValueAt(unsigned int xPos, unsigned int yPos)
{
ColourValue color;
unsigned int index = (xPos + yPos * nWidth) * 4;

color.a = (float)pData[index];
color.b = (float)pData[index + 1];
color.g = (float)pData[index + 2];
color.r = (float)pData[index + 3];

return color;
}

unsigned int CLTextureMap::GetValueAt(unsigned int xPos, unsigned int yPos, unsigned int channel)
{
unsigned int index = (xPos + yPos * nWidth) * 4;

return (unsigned int)pData[index + channel];
}

void CLTextureMap::UpdateImage()
{
mapImage.loadDynamicImage(pData, nWidth, nHeight, PF_R8G8B8A8);

data->getBuffer(0, 0)->blitFromMemory(mapImage.getPixelBox(0, 0));
}

void CLTextureMap::SaveMap(Ogre::String szFileName)
{
mapImage.save(szFileName);
}

void CLTextureMap::LoadMap(String szFileName, String szGroupName)
{
mapImage.load(szFileName, szGroupName);

if(mapImage.getWidth() == nWidth && mapImage.getHeight() == nHeight && mapImage.getFormat() == PF_R8G8B8A8)
data->getBuffer(0, 0)->blitFromMemory(mapImage.getPixelBox(0, 0));
}



CLBrush is the singleton class of mine. You can use your own brush class or anything to get the same variables. I wroted this classes for my own purposes so there can be some code chunks that you can't understand. Sorry for these situations, i can help in that times.

SongOfTheWeave

11-01-2009 00:53:58

Thanks for providing this. I know at least several of us using ETM have written code to accomplish this. Maybe if some others work form this code we will end up with a modular, extensible ETM "add-on".