tge/engine/editor/terraformerTexture.cc
2025-02-17 23:17:30 -06:00

412 lines
11 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// 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;
}