Initial commit
This commit is contained in:
411
Torque/SDK/engine/editor/terraformerTexture.cc
Normal file
411
Torque/SDK/engine/editor/terraformerTexture.cc
Normal file
@@ -0,0 +1,411 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/console.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "editor/terraformer.h"
|
||||
#include "gui/editor/guiFilterCtrl.h"
|
||||
#include "editor/editor.h"
|
||||
#include "platform/event.h"
|
||||
#include "game/gameConnection.h"
|
||||
|
||||
#include "core/fileStream.h"
|
||||
|
||||
|
||||
|
||||
inline F32 lerp(F32 t, F32 a, F32 b)
|
||||
{
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
|
||||
inline F32 curve(F32 t)
|
||||
{
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
|
||||
F32 getAlpha(U32 x, U32 y, Heightfield *alpha)
|
||||
{
|
||||
F32 xFactor = F32(x & 7) * (1.0f/8.0f);
|
||||
F32 yFactor = F32(y & 7) * (1.0f/8.0f);
|
||||
U32 xi = x >> 3;
|
||||
U32 yi = y >> 3;
|
||||
|
||||
F32 a0 = alpha->val(xi, yi);
|
||||
F32 a1 = alpha->val(xi+1, yi);
|
||||
F32 a2 = alpha->val(xi+1, yi+1);
|
||||
F32 a3 = alpha->val(xi, yi+1);
|
||||
|
||||
// F32 ah0 = (a0 * (1.0f-xFactor)) + (a1 * xFactor);
|
||||
// F32 ah1 = (a3 * (1.0f-xFactor)) + (a2 * xFactor);
|
||||
//
|
||||
// F32 a = (ah0 * (1.0f-yFactor)) + (ah1 * yFactor);
|
||||
|
||||
//xFactor = curve(xFactor);
|
||||
//yFactor = curve(yFactor);
|
||||
|
||||
F32 ah0 = lerp(xFactor, a0, a1);
|
||||
F32 ah1 = lerp(xFactor, a3, a2);
|
||||
F32 a = lerp(yFactor, ah0, ah1);
|
||||
|
||||
return (a*a);
|
||||
}
|
||||
|
||||
|
||||
GBitmap* merge(VectorPtr<Heightfield*> &alpha, VectorPtr<GBitmap*> &material)
|
||||
{
|
||||
// due to memory constraints we build the the output bitmap one scan-line at a time.
|
||||
F32 sum[2048];
|
||||
GBitmap *bitmap = new GBitmap(2048, 2048, false, GBitmap::RGB);
|
||||
VectorPtr<Heightfield*>::iterator itrA;
|
||||
VectorPtr<GBitmap*>::iterator itrM;
|
||||
|
||||
for (S32 y = 0; y<2048; y++)
|
||||
{
|
||||
// first compute the sum of the alphas at each pixel
|
||||
S32 x;
|
||||
for (x = 0; x<2048; x++)
|
||||
{
|
||||
sum[x] = 0.0f;
|
||||
for (itrA = alpha.begin(); itrA != alpha.end(); itrA++)
|
||||
sum[x] += getAlpha(x,y,*itrA);
|
||||
}
|
||||
|
||||
// blend the pixels
|
||||
for (x = 0; x<2048; x++)
|
||||
{
|
||||
ColorI blend(0,0,0,0);
|
||||
if (sum[x] > 0.0f)
|
||||
{
|
||||
F32 fsum = sum[x];
|
||||
F32 scaleFactor = (1.0f/fsum);
|
||||
for (itrA = alpha.begin(), itrM = material.begin(); itrM != material.end(); itrM++, itrA++)
|
||||
{
|
||||
ColorI color;
|
||||
GBitmap *bmp = *itrM;
|
||||
bmp->getColor(x % bmp->getWidth(), y % bmp->getHeight(), color);
|
||||
color *= getAlpha(x,y,*itrA) * scaleFactor;
|
||||
blend.red += color.red;
|
||||
blend.green += color.green;
|
||||
blend.blue += color.blue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GBitmap *mat = *material.begin();
|
||||
mat->getColor(x % mat->getWidth(), y % mat->getHeight(), blend);
|
||||
}
|
||||
bitmap->setColor(x,y,blend);
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
bool Terraformer::setMaterials(const char *r_src, const char *materials )
|
||||
{
|
||||
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;
|
||||
|
||||
VectorPtr<Heightfield*> src;
|
||||
VectorPtr<char*> dml;
|
||||
Vector<S32> dmlIndex;
|
||||
|
||||
//--------------------------------------
|
||||
// extract the source registers
|
||||
char buffer[1024];
|
||||
dStrcpy(buffer, r_src);
|
||||
char *str = dStrtok(buffer, " \0");
|
||||
while (str)
|
||||
{
|
||||
src.push_back( getRegister(dAtof(str)) );
|
||||
str = dStrtok(NULL, " \0");
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// extract the materials
|
||||
dStrcpy(buffer, materials);
|
||||
str = dStrtok(buffer, " \0");
|
||||
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, " \0");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
bool Terraformer::mergeMasks(const char *r_src, U32 r_dst)
|
||||
{
|
||||
Heightfield *dst = getRegister(r_dst);
|
||||
VectorPtr<Heightfield*> src;
|
||||
|
||||
// extract the source registers
|
||||
char buffer[1024];
|
||||
dStrcpy(buffer, r_src);
|
||||
char *reg = dStrtok(buffer, " \0");
|
||||
while (reg)
|
||||
{
|
||||
src.push_back( getRegister(dAtoi(reg)) );
|
||||
reg = dStrtok(NULL, " \0");
|
||||
}
|
||||
|
||||
// if no masks set the destination to Zero
|
||||
if (src.size() == 0)
|
||||
{
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) = 0.0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (src.size() == 1)
|
||||
{
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) = src[0]->val(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
// store the MAX of the masks into dst
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
{
|
||||
F32 value = src[0]->val(i);
|
||||
for (S32 j=1; j<src.size(); j++)
|
||||
value *= src[j]->val(i);
|
||||
dst->val(i) = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
bool Terraformer::maskFBm(U32 r_dst, U32 interval, F32 roughness, U32 seed, const Filter &filter, bool distort, U32 r_distort)
|
||||
{
|
||||
Heightfield *dst = getRegister(r_dst);
|
||||
noise.setSeed(seed);
|
||||
noise.fBm(dst, blockSize, interval, 1.0-roughness, 3.0f);
|
||||
|
||||
scale(r_dst, r_dst, 0.0f, 1.0f);
|
||||
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) = filter.getValue( dst->val(i) );
|
||||
|
||||
if (distort)
|
||||
{
|
||||
Heightfield *d = getRegister(r_distort);
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) *= d->val(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
bool Terraformer::maskHeight(U32 r_src, U32 r_dst, const Filter &filter, bool distort, U32 r_distort)
|
||||
{
|
||||
Heightfield *src = getRegister(r_src);
|
||||
Heightfield *dst = getRegister(r_dst);
|
||||
|
||||
scale(r_src, r_dst, 0.0f, 1.0f);
|
||||
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) = filter.getValue(dst->val(i));
|
||||
|
||||
if (distort)
|
||||
{
|
||||
Heightfield *d = getRegister(r_distort);
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) *= d->val(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
bool Terraformer::maskSlope(U32 r_src, U32 r_dst, const Filter &filter, bool distort, U32 r_distort)
|
||||
{
|
||||
Heightfield *src = getRegister(r_src);
|
||||
Heightfield *dst = getRegister(r_dst);
|
||||
|
||||
F32 fmin, fmax;
|
||||
getMinMax(r_src, &fmin, &fmax);
|
||||
F32 scale = worldHeight / (fmax-fmin);
|
||||
|
||||
for (S32 y=0; y<blockSize; y++)
|
||||
{
|
||||
for (S32 x=0; x<blockSize; x++)
|
||||
{
|
||||
// for each height look at the immediate surrounding heights and find max slope
|
||||
F32 array[9];
|
||||
F32 maxDelta = 0;
|
||||
src->block(x,y,array);
|
||||
F32 height = array[4];
|
||||
|
||||
for (S32 i=0; i<9; i++)
|
||||
{
|
||||
F32 delta = mFabs(array[i] - height);
|
||||
if ( (i&1) == 0)
|
||||
delta *= 0.70711f; // compensate for diagonals
|
||||
|
||||
if (delta > maxDelta)
|
||||
maxDelta = delta;
|
||||
}
|
||||
F32 slopeVal = mAtan( maxDelta * scale, worldTileSize ) * (2.0f/M_PI);
|
||||
dst->val(x, y) = filter.getValue( mPow(slopeVal, 1.5f) );
|
||||
}
|
||||
}
|
||||
|
||||
if (distort)
|
||||
{
|
||||
Heightfield *d = getRegister(r_distort);
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) *= d->val(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
bool Terraformer::maskWater(U32 r_src, U32 r_dst, bool distort, U32 r_distort)
|
||||
{
|
||||
Heightfield *src = getRegister(r_src);
|
||||
Heightfield *dst = getRegister(r_dst);
|
||||
|
||||
scale(r_src, r_dst, 0.0f, 1.0f);
|
||||
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) = (dst->val(i) > worldWater) ? 0.0f : 1.0f;
|
||||
|
||||
if (distort)
|
||||
{
|
||||
Heightfield *d = getRegister(r_distort);
|
||||
for (S32 i=0; i < (blockSize*blockSize); i++)
|
||||
dst->val(i) *= d->val(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user