📄 terrdata.cc
字号:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "terrain/terrData.h"
#include "dgl/dgl.h"
#include "dgl/gBitmap.h"
#include "dgl/gTexManager.h"
#include "dgl/materialList.h"
#include "dgl/materialPropertyMap.h"
#include "math/mMath.h"
#include "math/mathIO.h"
#include "core/fileStream.h"
#include "core/bitStream.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "sim/netConnection.h"
#include "terrain/terrRender.h"
#include "terrain/blender.h"
#ifdef TGE_RPG /// TGE_Collision 地形的全局指针,通常,RPG只有一个地形便可
TerrainBlock* g_pTerrainBlock = 0;
#endif
extern bool gDGLRender;
IMPLEMENT_CO_NETOBJECT_V1(TerrainBlock);
namespace
{
void terrainTextureEventCB(const U32 eventCode, void *userData)
{
TerrainBlock* pTerr = reinterpret_cast<TerrainBlock*>(userData);
pTerr->processTextureEvent(eventCode);
}
Point3F sgTexGenS;
Point3F sgTexGenT;
Point3F sgLMGenS;
Point3F sgLMGenT;
} // namespace {}
//--------------------------------------
TerrainBlock::TerrainBlock()
{
squareSize = 8;
mBlender = NULL;
lightMap = 0;
for(S32 i = BlockShift; i >= 0; i--)
gridMap[i] = NULL;
mTerrFileName = NULL; /// TGE_Map
#ifdef TGE_RPG /// TGE_TerrainScene
mSceneFileName = NULL;
#endif
heightMap = NULL;
materialMap = NULL;
mBaseMaterialMap = NULL;
mMaterialFileName = NULL;
mTypeMask = TerrainObjectType | StaticObjectType | StaticRenderedObjectType;
mNetFlags.set(Ghostable | ScopeAlways);
mCollideEmpty = false;
mDetailTextureName = NULL;
mBumpTextureName = NULL;
mBumpScale = 1.0;
mBumpOffset = 0.01;
mZeroBumpScale = 8;
#ifdef TGE_RPGCLIENT2//TGE_RPGClient2
mCRC = 1;
#else
mCRC = 0;
#endif
flagMap = 0;
mVertexBuffer = -1;
mTile = true;
}
//--------------------------------------
TerrainBlock::~TerrainBlock()
{
delete lightMap;
}
//--------------------------------------
void TerrainBlock::setFile(Resource<TerrainFile> terr)
{
mFile = terr;
for(U32 i = 0; i <= BlockShift; i++)
gridMap[i] = mFile->mGridMap[i];
mBaseMaterialMap = mFile->mBaseMaterialMap;
mMaterialFileName= mFile->mMaterialFileName;
mChunkMap = mFile->mChunkMap;
materialMap = mFile->mMaterialMap;
heightMap = mFile->mHeightMap;
flagMap = mFile->mFlagMap;
}
#ifdef TGE_RPG /// TGE_TerrainScene
void TerrainBlock::setScene(Resource<TerrainScene> scene)
{
mTerrainScene = scene;
}
#endif
//--------------------------------------------------------------------------
bool TerrainBlock::save(const char *filename)
{
#ifdef TGE_RPG /// TGE_TerrainScene
char *fn = dStrdup(filename);
char* ext = dStrrchr(fn, '.');
if (!ext || dStricmp(ext, ".sce") != 0)
{
*ext = 0;
dStrcat(fn, ".sce");
}
mTerrainScene->save(fn);
#endif
return mFile->save(filename);
}
//--------------------------------------
static U16 calcDev(PlaneF &pl, Point3F &pt)
{
F32 z = (pl.d + pl.x * pt.x + pl.y * pt.y) / -pl.z;
F32 diff = z - pt.z;
if(diff < 0)
diff = -diff;
if(diff > 0xFFFF)
return 0xFFFF;
else
return U16(diff);
}
static U16 Umax(U16 u1, U16 u2)
{
return u1 > u2 ? u1 : u2;
}
//------------------------------------------------------------------------------
bool TerrainBlock::unpackEmptySquares()
{
U32 size = BlockSquareWidth * BlockSquareWidth;
U32 i;
for(i = 0; i < size; i++)
materialMap[i].flags &= ~Material::Empty;
// walk the pairs
for(i = 0; i < mEmptySquareRuns.size(); i++)
{
/// TGE_Map
U32 offset = mEmptySquareRuns[i] & BlockSizeSquareMask;
U32 run = BlockMask&(U32(mEmptySquareRuns[i]) >> 16);
//U32 offset = mEmptySquareRuns[i] & 0xffff;
//U32 run = U32(mEmptySquareRuns[i]) >> 16;
//
for(U32 j = 0; j < run; j++)
{
if((offset+j) >= size)
{
Con::errorf(ConsoleLogEntry::General, "TerrainBlock::unpackEmpties: invalid entry.");
return(false);
}
materialMap[offset+j].flags |= Material::Empty;
}
}
rebuildEmptyFlags();
return(true);
}
void TerrainBlock::packEmptySquares()
{
AssertFatal(isServerObject(), "TerrainBlock::packEmptySquares: client!");
mEmptySquareRuns.clear();
// walk the map
U32 run = 0;
U32 offset;
U32 size = BlockSquareWidth * BlockSquareWidth;
for(U32 i = 0; i < size; i++)
{
if(materialMap[i].flags & Material::Empty)
{
if(!run)
offset = i;
run++;
}
else if(run)
{
mEmptySquareRuns.push_back((run << 16) | offset);
run = 0;
// cap it
if(mEmptySquareRuns.size() == MaxEmptyRunPairs)
break;
}
}
//
if(run && mEmptySquareRuns.size() != MaxEmptyRunPairs)
mEmptySquareRuns.push_back((run << 16) | offset);
if(mEmptySquareRuns.size() == MaxEmptyRunPairs)
Con::warnf(ConsoleLogEntry::General, "TerrainBlock::packEmptySquares: More than %d run pairs encountered. Extras will not persist.", MaxEmptyRunPairs);
//
mNetFlags |= EmptyMask;
}
//------------------------------------------------------------------------------
void TerrainBlock::rebuildEmptyFlags()
{
// rebuild entire maps empty flags!
for(U32 y = 0; y < TerrainBlock::ChunkSquareWidth; y++)
{
for(U32 x = 0; x < TerrainBlock::ChunkSquareWidth; x++)
{
GridChunk &gc = *(mChunkMap + x + (y << TerrainBlock::ChunkShift));
gc.emptyFlags = 0;
U32 sx = x << TerrainBlock::ChunkDownShift;
U32 sy = y << TerrainBlock::ChunkDownShift;
for(U32 j = 0; j < 4; j++)
{
for(U32 i = 0; i < 4; i++)
{
TerrainBlock::Material *mat = getMaterial(sx + i, sy + j);
if(mat->flags & TerrainBlock::Material::Empty)
gc.emptyFlags |= (1 << (j * 4 + i));
}
}
}
}
for(S32 i = BlockShift; i >= 0; i--)
{
S32 squareCount = 1 << (BlockShift - i);
S32 squareSize = (TerrainBlock::BlockSize) / squareCount;
for(S32 squareX = 0; squareX < squareCount; squareX++)
{
for(S32 squareY = 0; squareY < squareCount; squareY++)
{
GridSquare *parent = NULL;
if(i < BlockShift)
parent = findSquare(i+1, Point2I(squareX * squareSize, squareY * squareSize));
bool empty = true;
for(S32 sizeX = 0; empty && sizeX <= squareSize; sizeX++)
{
for(S32 sizeY = 0; empty && sizeY <= squareSize; sizeY++)
{
S32 x = squareX * squareSize + sizeX;
S32 y = squareY * squareSize + sizeY;
if(sizeX != squareSize && sizeY != squareSize)
{
TerrainBlock::Material *mat = getMaterial(x, y);
if(!(mat->flags & TerrainBlock::Material::Empty))
empty = false;
}
}
}
GridSquare *sq = findSquare(i, Point2I(squareX * squareSize, squareY * squareSize));
if(empty)
sq->flags |= GridSquare::Empty;
else
sq->flags &= ~GridSquare::Empty;
}
}
}
}
//------------------------------------------------------------------------------
void TerrainBlock::setHeight(const Point2I & pos, float height)
{
// set the height
U16 ht = floatToFixed(height);
*((U16*)getHeightAddress(pos.x, pos.y)) = ht;
}
inline void getMinMax(U16 &min, U16 &max, U16 height)
{
if(height < min)
min = height;
if(height > max)
max = height;
}
inline void checkSquareMinMax(GridSquare *parent, GridSquare *child)
{
if(parent->minHeight > child->minHeight)
parent->minHeight = child->minHeight;
if(parent->maxHeight < child->maxHeight)
parent->maxHeight = child->maxHeight;
}
void TerrainBlock::updateGridMaterials(Point2I min, Point2I max)
{
// ok:
// build the chunk materials flags for all the chunks in the bounding rect
// ((min - 1) >> ChunkDownShift) up to ((max + ChunkWidth) >> ChunkDownShift)
// we have to make sure to cover boundary conditions as as stated above
// since, for example, x = 0 affects 2 chunks
for (S32 y = min.y - 1; y < max.y + 1; y++)
{
for (S32 x=min.x - 1; x < max.x + 1; x++)
{
GridSquare *sq = findSquare(0, Point2I(x, y));
sq->flags &= (GridSquare::MaterialStart -1);
S32 xpl = (x + 1) & TerrainBlock::BlockMask;
S32 ypl = (y + 1) & TerrainBlock::BlockMask;
for(U32 i = 0; i < TerrainBlock::MaterialGroups; i++)
{
if (mFile->mMaterialAlphaMap[i] != NULL) {
U32 mapVal = (mFile->mMaterialAlphaMap[i][(y << TerrainBlock::BlockShift) + x] |
mFile->mMaterialAlphaMap[i][(ypl << TerrainBlock::BlockShift) + x] |
mFile->mMaterialAlphaMap[i][(ypl << TerrainBlock::BlockShift) + xpl] |
mFile->mMaterialAlphaMap[i][(y << TerrainBlock::BlockShift) + xpl]);
if(mapVal)
sq->flags |= (GridSquare::MaterialStart << i);
}
}
}
}
for (S32 y = min.y - 2; y < max.y + 2; y += 2)
{
for (S32 x= min.x - 2; x < max.x + 2; x += 2)
{
GridSquare *sq = findSquare(1, Point2I(x, y));
GridSquare *s1 = findSquare(0, Point2I(x, y));
GridSquare *s2 = findSquare(0, Point2I(x+1, y));
GridSquare *s3 = findSquare(0, Point2I(x, y+1));
GridSquare *s4 = findSquare(0, Point2I(x+1, y+1));
sq->flags |= (s1->flags | s2->flags | s3->flags | s4->flags) & ~(GridSquare::MaterialStart -1);
}
}
TerrainRender::flushCacheRect(RectI(min, max - min));
}
void TerrainBlock::updateGrid(Point2I min, Point2I max)
{
// ok:
// build the chunk deviance for all the chunks in the bounding rect,
// ((min - 1) >> ChunkDownShift) up to ((max + ChunkWidth) >> ChunkDownShift)
// update the chunk deviances for the affected chunks
// we have to make sure to cover boundary conditions as as stated above
// since, for example, x = 0 affects 2 chunks
for(S32 x = (min.x - 1) >> ChunkDownShift;x < (max.x + ChunkSize) >> ChunkDownShift; x++)
{
for(S32 y = (min.y - 1) >> ChunkDownShift;y < (max.y + ChunkSize) >> ChunkDownShift; y++)
{
S32 px = x;
S32 py = y;
if(px < 0)
px += BlockSize >> ChunkDownShift;
if(py < 0)
py += BlockSize >> ChunkDownShift;
buildChunkDeviance(px, py);
}
}
// ok the chunk deviances are rebuilt... now rebuild the affected area
// of the grid map:
// here's how it works:
// for the current terrain renderer we only care about
// the minHeight and maxHeight on the GridSquare
// so we do one pass through, updating minHeight and maxHeight
// on the level 0 squares, then we loop up the grid map from 1 to
// the top, expanding the bounding boxes as necessary.
// this should end up being way, way, way, way faster for the terrain
// editor
for(S32 y = min.y - 1; y < max.y + 1; y++)
{
for(S32 x = min.x - 1; x < max.x + 1; x++)
{
S32 px = x;
S32 py = y;
if(px < 0)
px += BlockSize;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -