412 lines
11 KiB
C++
Executable File
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;
|
|
}
|