TGEA 1.7.0 Beta 1 - Bug Texture Editor Dead
by Michael Perry · in Torque Game Engine Advanced · 03/24/2008 (12:07 pm) · 5 replies
While writing a tutorial about using the Terrain Texture Editor, I noticed a problem. The Texture Editor is still coded to work on a single Legacy TerrainBlock, and only if it is named "Terrain." I tracked down what function eventually gets called in the engine that paints your terrain based on what placement procedure you pick:
In engine/source/gui/missionEditor/terraformerTexture.cpp
At the very start of the function, we are dead in the water on various levels. I put in a fix that works for me, but I'm not sure if it needs to be optimized or tweaked to act as a proper bug fix.
Fix in next post...
In engine/source/gui/missionEditor/terraformerTexture.cpp
bool Terraformer::setMaterials(const char *r_src, const char *materials )
{
TerrainBlock *serverTerrBlock = dynamic_cast<TerrainBlock*>(Sim::findObject("Terrain"));
if (!serverTerrBlock)
return false;
...
}At the very start of the function, we are dead in the water on various levels. I put in a fix that works for me, but I'm not sure if it needs to be optimized or tweaked to act as a proper bug fix.
Fix in next post...
About the author
Programmer.
#2
Instead of searching for a single object named "Terrain":
I iterate through our Root Group and grab all non-server terrain objects:
Instead of applying the functionality to a single TerrainBlock:
I iterate through a vector of TerrainBlock pointers I found earlier:
03/24/2008 (12:10 pm)
Just to point out the changes:Instead of searching for a single object named "Terrain":
TerrainBlock *serverTerrBlock = dynamic_cast<TerrainBlock*>(Sim::findObject("Terrain"));
if (!serverTerrBlock)
return false;
NetConnection* toServer = NetConnection::getConnectionToServer();
NetConnection* toClient = NetConnection::getLocalClientConnection();
S32 index = toClient->getGhostIndex(serverTerrBlock);
TerrainBlock *clientTerrBlock = dynamic_cast<TerrainBlock*>(toServer->resolveGhost(index));
if (!clientTerrBlock)
return false;I iterate through our Root Group and grab all non-server terrain objects:
Vector<TerrainBlock *> terrains;
for(SimGroupIterator itr(Sim::getRootGroup()); *itr; ++itr)
{
const char* className = (*itr)->getClassName();
if (!dStrcmp(className, "TerrainBlock"))
{
TerrainBlock *clientTerrBlock = dynamic_cast<TerrainBlock*>((*itr));
if (!clientTerrBlock || clientTerrBlock->isServerObject())
continue;
terrains.push_back(clientTerrBlock);
}
}Instead of applying the functionality to a single TerrainBlock:
if (!clientTerrBlock)
return false;I iterate through a vector of TerrainBlock pointers I found earlier:
if(terrains.size()<1)
return false;
for (int i = 0; i < terrains.size(); i++)
{
TerrainBlock* clientTerrBlock = terrains[i];
VectorPtr<Heightfield*> src;
...
#3
03/26/2008 (6:02 pm)
Fixed for the next Beta.
#4
03/27/2008 (7:50 am)
Haven't had a chance to fully debug everything in this, just got the terrain painting working and went back to writing in the docs. Could still probably use a real run through to see if all the features still function.
Employee Michael Perry
ZombieShortbus
//-------------------------------------- bool Terraformer::setMaterials(const char *r_src, const char *materials ) { Vector<TerrainBlock *> terrains; for(SimGroupIterator itr(Sim::getRootGroup()); *itr; ++itr) { const char* className = (*itr)->getClassName(); if (!dStrcmp(className, "TerrainBlock")) { TerrainBlock *clientTerrBlock = dynamic_cast<TerrainBlock*>((*itr)); if (!clientTerrBlock || clientTerrBlock->isServerObject()) continue; terrains.push_back(clientTerrBlock); } } if(terrains.size()<1) return false; for (int i = 0; i < terrains.size(); i++) { TerrainBlock* clientTerrBlock = terrains[i]; VectorPtr<Heightfield*> src; VectorPtr<char*> dml; Vector<S32> dmlIndex; //-------------------------------------- // extract the source registers char buffer[1024]; dStrcpy(buffer, r_src); char *str = dStrtok(buffer, " [[6287d1253fcdd]]"); while (str) { src.push_back( getRegister(dAtoi(str)) ); str = dStrtok(NULL, " [[6287d1253fcdd]]"); } //-------------------------------------- // extract the materials dStrcpy(buffer, materials); str = dStrtok(buffer, " [[6287d1253fcdd]]"); while (str) { S32 i; for (i=0; i<dml.size(); i++) if (dStricmp(str, dml[i]) == 0) break; // a unique material list ? if (i == dml.size()) dml.push_back(str); // map register to dml dmlIndex.push_back(i); str = dStrtok(NULL, " [[6287d1253fcdd]]"); } if (dml.size() > TerrainBlock::MaterialGroups) { Con::printf("maximum number of DML Material Exceeded"); return false; } // install the new DMLs clientTerrBlock->setBaseMaterials(dml.size(), (const char**)dml.address()); //-------------------------------------- // build alpha masks for each material type for (S32 y=0; y<blockSize; y++) { for (S32 x=0; x<blockSize; x++) { // skip? (cannot skip if index is out of range...) F32 total = 0; F32 matVals[TerrainBlock::MaterialGroups]; S32 i; for(i = 0; i < TerrainBlock::MaterialGroups; i++) matVals[i] = 0; for(i = 0; i < src.size(); i++) { matVals[dmlIndex[i]] += src[i]->val(x,y); total += src[i]->val(x,y); } if(total == 0) { matVals[0] = 1; total = 1; } // axe out any amount that is less than the threshold F32 threshold = 0.15 * total; for(i = 0; i < TerrainBlock::MaterialGroups; i++) if(matVals[i] < threshold) matVals[i] = 0; total = 0; for(i = 0; i < TerrainBlock::MaterialGroups; i++) total += matVals[i]; for(i = 0; i < TerrainBlock::MaterialGroups; i++) { U8 *map = clientTerrBlock->getMaterialAlphaMap(i); map[x + (y << TerrainBlock::BlockShift)] = (U8)(255 * matVals[i] / total); } S32 material = 0; F32 best = 0.0f; for (i=0; i<src.size(); i++) { F32 value = src[i]->val(x,y); if ( value > best) { material = dmlIndex[i]; best = value; } } // place the material *clientTerrBlock->getBaseMaterialAddress(x, y) = material; } } // make it so! clientTerrBlock->buildGridMap(); clientTerrBlock->buildMaterialMap(); // reload the material lists? if(gEditingMission) clientTerrBlock->refreshMaterialLists(); //-------------------------------------------------------------------------- // for mow steal the first bitmap out of each dml if (Con::getBoolVariable("$terrainTestBmp", false) == true) { VectorPtr<GBitmap*> mats; for (S32 i=0; i<dmlIndex.size(); i++) { /* Resource<MaterialList> mlist = ResourceManager->load(dml[dmlIndex[i]]); mlist->load(); GBitmap *bmp = mlist->getMaterial(0).getBitmap(); mats.push_back(bmp); */ } GBitmap *texture = merge(src,mats); FileStream stream; stream.open("terrain.png", FileStream::Write); texture->writePNG(stream); stream.close(); delete texture; } } return true; }*Note* - You cannot see the effect immediately. I had to open up the Terrain Painter [F8], and paint a single square once on each of the terrains in the scene. I'm not sure if a "refresh" or "postInspectApply" kind of thing needs to be called or not.
Thoughts?