mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-12-27 07:54:51 +01:00
24cb244d43
This was a _ton_ of changes, made 15 years ago, so there are probably some problems to work out still. Among others: Engine/Base/Stream.* was mostly abandoned and will need to be re-ported. Still, this is a pretty good start, and probably holds a world record for lines of changes or something. :)
2148 lines
66 KiB
C++
2148 lines
66 KiB
C++
/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
|
|
|
|
#include "Engine/StdH.h"
|
|
#include <Engine/Base/Stream.h>
|
|
#include <Engine/Base/ListIterator.inl>
|
|
#include <Engine/Math/Projection.h>
|
|
#include <Engine/Math/FixInt.h>
|
|
#include <Engine/Graphics/DrawPort.h>
|
|
#include <Engine/Graphics/ImageInfo.h>
|
|
#include <Engine/Graphics/GfxLibrary.h>
|
|
#include <Engine/Terrain/Terrain.h>
|
|
#include <Engine/Terrain/TerrainRender.h>
|
|
#include <Engine/Terrain/TerrainEditing.h>
|
|
#include <Engine/Terrain/TerrainMisc.h>
|
|
#include <Engine/Templates/Stock_CTextureData.h>
|
|
#include <Engine/Entities/Entity.h>
|
|
#include <Engine/Entities/ShadingInfo.h>
|
|
#include <Engine/Graphics/Font.h>
|
|
#include <Engine/Base/Console.h>
|
|
#include <Engine/Rendering/Render.h>
|
|
|
|
extern CTerrain *_ptrTerrain;
|
|
|
|
extern BOOL _bWorldEditorApp; // is this world edtior app
|
|
|
|
|
|
static INDEX _iTerrainVersion = 9; // Current terrain version
|
|
static INDEX ctGeneratedTopMaps = 0; // TEMP
|
|
static INDEX ctGlobalTopMaps = 0; // TEMP
|
|
INDEX _ctShadowMapUpdates = 0; // TEMP
|
|
// TEMP
|
|
|
|
INDEX _ctNodesVis = 0;
|
|
INDEX _ctTris = 0;
|
|
INDEX _ctDelayedNodes = 0;
|
|
static void ShowTerrainInfo(CAnyProjection3D &apr, CDrawPort *pdp, CTerrain *ptrTerrain); // TEMP
|
|
|
|
/*
|
|
* Terrain initialization
|
|
*/
|
|
CTerrain::CTerrain()
|
|
{
|
|
tr_vStretch = FLOAT3D(1,0.05f,1);
|
|
tr_fDistFactor = 32;
|
|
tr_iMaxTileLod = 0;
|
|
tr_iSelectedLayer = 0;
|
|
tr_ctDriverChanges = -1;
|
|
|
|
tr_pixHeightMapWidth = 256;
|
|
tr_pixHeightMapHeight = 256;
|
|
tr_pixTopMapWidth = 256;
|
|
tr_pixTopMapHeight = 256;
|
|
tr_pixFirstMipTopMapWidth = 256;
|
|
tr_pixFirstMipTopMapHeight = 256;
|
|
tr_ptdDetailMap = NULL;
|
|
tr_auwHeightMap = NULL;
|
|
tr_aubEdgeMap = NULL;
|
|
tr_auwShadingMap = NULL;
|
|
|
|
// TEMP
|
|
try {
|
|
tr_ptdDetailMap = _pTextureStock->Obtain_t((CTString)"Textures\\Detail\\Crumples04.tex");
|
|
} catch(char *) {
|
|
}
|
|
|
|
// Set size of shadow and shading maps
|
|
SetShadowMapsSize(0,0);
|
|
|
|
// Set terrain size
|
|
SetTerrainSize(FLOAT3D(256,80,256));
|
|
// Set num of quads in one tile
|
|
SetQuadsPerTileRow(32);
|
|
|
|
SetFlags(TR_REGENERATE);
|
|
}
|
|
|
|
// Render visible terrain tiles
|
|
void CTerrain::Render(CAnyProjection3D &apr, CDrawPort *pdp)
|
|
{
|
|
// prepare gfx stuff
|
|
PrepareScene(apr, pdp, this);
|
|
|
|
if(tr_ctDriverChanges!=_pGfx->gl_ctDriverChanges) {
|
|
tr_ctDriverChanges = _pGfx->gl_ctDriverChanges;
|
|
// RefreshTerrain();
|
|
}
|
|
|
|
// if terrain is not frozen
|
|
extern INDEX ter_bNoRegeneration;
|
|
if(GetFlags()&TR_REGENERATE && !ter_bNoRegeneration) {
|
|
// Regenerate tiles
|
|
ReGenerate();
|
|
}
|
|
// if shadow map must be regenerated
|
|
if(GetFlags()&TR_UPDATE_SHADOWMAP) {
|
|
UpdateShadowMap();
|
|
}
|
|
|
|
|
|
// if top map regen is allowed
|
|
if(GetFlags()&TR_ALLOW_TOP_MAP_REGEN) {
|
|
// if top map regen is requested
|
|
if(GetFlags()&TR_REGENERATE_TOP_MAP) {
|
|
// update terrain top map
|
|
UpdateTopMap(-1);
|
|
// remove request for top map regen
|
|
RemoveFlag(TR_REGENERATE_TOP_MAP);
|
|
}
|
|
// remove flag that allows terrain to regenerate top map
|
|
RemoveFlag(TR_ALLOW_TOP_MAP_REGEN);
|
|
}
|
|
|
|
// show
|
|
RenderTerrain();
|
|
|
|
// if flag show brush selection has been set
|
|
if(GetFlags()&TR_SHOW_SELECTION) {
|
|
ShowSelectionInternal(this);
|
|
// remove show selection terrain flag
|
|
RemoveFlag(TR_SHOW_SELECTION);
|
|
}
|
|
|
|
// if flag for quadtree rebuilding is set
|
|
if(GetFlags()&TR_REBUILD_QUADTREE) {
|
|
// Resize quadtree
|
|
UpdateQuadTree();
|
|
// remove flag for quadtree rebuilding
|
|
RemoveFlag(TR_REBUILD_QUADTREE);
|
|
}
|
|
|
|
RemoveFlag(TR_HAS_FOG);
|
|
RemoveFlag(TR_HAS_HAZE);
|
|
|
|
extern INDEX ter_bShowWireframe;
|
|
// if wireframe mode forced
|
|
if(ter_bShowWireframe) {
|
|
COLOR colWire = 0xFFFFFFFF;
|
|
RenderTerrainWire(colWire);
|
|
}
|
|
|
|
extern INDEX ter_bShowQuadTree;
|
|
// if showing of quad tree is required
|
|
if(ter_bShowQuadTree) {
|
|
DrawQuadTree();
|
|
}
|
|
|
|
extern INDEX ter_bShowInfo;
|
|
if(ter_bShowInfo) {
|
|
ShowTerrainInfo(apr, pdp, this);
|
|
}
|
|
|
|
_ptrTerrain = NULL;
|
|
}
|
|
|
|
void CTerrain::RenderWireFrame(CAnyProjection3D &apr, CDrawPort *pdp, COLOR &colEdges)
|
|
{
|
|
// prepare gfx stuff
|
|
PrepareScene(apr, pdp, this);
|
|
// Regenerate tiles
|
|
if(tr_ctTiles>=0) {
|
|
CTerrainTile &tt = tr_attTiles[0];
|
|
if(tt.tt_iLod == -1) {
|
|
ReGenerate();
|
|
}
|
|
}
|
|
// show
|
|
RenderTerrainWire(colEdges);
|
|
}
|
|
|
|
// Create empty terrain with given size
|
|
void CTerrain::CreateEmptyTerrain_t(PIX pixWidth,PIX pixHeight)
|
|
{
|
|
_ptrTerrain = this;
|
|
// Clear old terrain data if exists
|
|
Clear();
|
|
|
|
ASSERT(tr_auwHeightMap==NULL);
|
|
ASSERT(tr_aubEdgeMap==NULL);
|
|
|
|
AllocateHeightMap(pixWidth,pixHeight);
|
|
|
|
AddDefaultLayer_t();
|
|
// Rebuild terrain
|
|
ReBuildTerrain();
|
|
_ptrTerrain = NULL;
|
|
}
|
|
|
|
// Import height map from targa file
|
|
void CTerrain::ImportHeightMap_t(CTFileName fnHeightMap, BOOL bUse16b/*=TRUE*/)
|
|
{
|
|
_ptrTerrain = this;
|
|
BOOL bResizeTerrain = FALSE;
|
|
|
|
// Load targa file
|
|
CImageInfo iiHeightMap;
|
|
iiHeightMap.LoadAnyGfxFormat_t(fnHeightMap);
|
|
|
|
// if new width and height are same
|
|
if(tr_pixHeightMapWidth==iiHeightMap.ii_Width && tr_pixHeightMapHeight==iiHeightMap.ii_Height) {
|
|
// Clear terrain data without removing layers
|
|
bResizeTerrain = FALSE;
|
|
} else {
|
|
// Clear all terrain data
|
|
bResizeTerrain = TRUE;
|
|
}
|
|
bResizeTerrain = TRUE;
|
|
|
|
FLOAT fLogWidht = Log2(iiHeightMap.ii_Width-1);
|
|
FLOAT fLogHeight = Log2(iiHeightMap.ii_Height-1);
|
|
if(fLogWidht!=INDEX(fLogWidht) || fLogHeight!=INDEX(fLogHeight)) {
|
|
ThrowF_t("Invalid terrain width or height");
|
|
}
|
|
if(iiHeightMap.ii_Width!= iiHeightMap.ii_Height) {
|
|
ThrowF_t("Only terrains with same width and height are supported in this version");
|
|
}
|
|
|
|
// Reallocate memory for terrain with size
|
|
ReAllocateHeightMap(iiHeightMap.ii_Width, iiHeightMap.ii_Height);
|
|
|
|
INDEX iHeightMapSize = iiHeightMap.ii_Width * iiHeightMap.ii_Height;
|
|
|
|
UBYTE *puwSrc = &iiHeightMap.ii_Picture[0];
|
|
UWORD *puwDst = &tr_auwHeightMap[0];
|
|
INDEX iBpp = iiHeightMap.ii_BitsPerPixel/8;
|
|
|
|
// for each word in loaded image
|
|
for(INDEX iw=0;iw<iHeightMapSize;iw++) {
|
|
// use 16 bits for importing
|
|
if(bUse16b) {
|
|
*puwDst = *(UWORD*)puwSrc;
|
|
// use 8 bits for importing
|
|
} else {
|
|
*puwDst = *(UBYTE*)puwSrc<<8;
|
|
}
|
|
puwDst++;
|
|
puwSrc+=iBpp;
|
|
}
|
|
|
|
// Rebuild terrain
|
|
ReBuildTerrain();
|
|
|
|
_ptrTerrain = NULL;
|
|
}
|
|
|
|
// Export height map to targa file
|
|
void CTerrain::ExportHeightMap_t(CTFileName fnHeightMap, BOOL bUse16b/*=TRUE*/)
|
|
{
|
|
ASSERT(tr_auwHeightMap!=NULL);
|
|
INDEX iSize = tr_pixHeightMapWidth*tr_pixHeightMapHeight;
|
|
|
|
CImageInfo iiHeightMap;
|
|
iiHeightMap.ii_Width = tr_pixHeightMapWidth;
|
|
iiHeightMap.ii_Height = tr_pixHeightMapHeight;
|
|
iiHeightMap.ii_BitsPerPixel = 32;
|
|
iiHeightMap.ii_Picture = (UBYTE*)AllocMemory(iSize*iiHeightMap.ii_BitsPerPixel/8);
|
|
|
|
GFXColor *pacolImage = (GFXColor*)&iiHeightMap.ii_Picture[0];
|
|
UWORD *puwHeight = tr_auwHeightMap;
|
|
for(INDEX ipix=0;ipix<iSize;ipix++) {
|
|
*pacolImage = 0x00000000;
|
|
if(bUse16b) {
|
|
UWORD *puwData = (UWORD*)&pacolImage[0];
|
|
*puwData = *puwHeight;
|
|
} else {
|
|
UBYTE *pubData = (UBYTE*)&pacolImage[0];
|
|
UWORD *puwHData = puwHeight;
|
|
*pubData = (UBYTE)(*puwHData>>8);
|
|
}
|
|
pacolImage++;
|
|
puwHeight++;
|
|
}
|
|
|
|
iiHeightMap.SaveTGA_t(fnHeightMap);
|
|
iiHeightMap.Clear();
|
|
}
|
|
|
|
// Rebuild all terrain
|
|
void CTerrain::ReBuildTerrain(BOOL bDelayTileRegen/*=FALSE*/)
|
|
{
|
|
_ptrTerrain = this;
|
|
|
|
ClearTopMaps();
|
|
ClearTiles();
|
|
ClearArrays();
|
|
ClearQuadTree();
|
|
|
|
// Make sure terrain is same size (in metars)
|
|
SetTerrainSize(tr_vTerrainSize);
|
|
// Build terrain data
|
|
BuildTerrainData();
|
|
// Build terrain quadtree
|
|
BuildQuadTree();
|
|
// Generate global top map
|
|
GenerateTerrainTopMap();
|
|
// Clear current regen list
|
|
ClearRegenList();
|
|
// Add all tiles to reqen queue
|
|
AddAllTilesToRegenQueue();
|
|
|
|
// if not delaying tile regen
|
|
if(!bDelayTileRegen) {
|
|
// Regenerate tiles now
|
|
ReGenerate();
|
|
// Update shadow map
|
|
UpdateShadowMap();
|
|
}
|
|
}
|
|
|
|
// Refresh terrain
|
|
void CTerrain::RefreshTerrain(void)
|
|
{
|
|
ReBuildTerrain();
|
|
}
|
|
|
|
// Set terrain size
|
|
void CTerrain::SetTerrainSize(FLOAT3D vSize)
|
|
{
|
|
tr_vStretch(1) = vSize(1) / (tr_pixHeightMapWidth-1);
|
|
tr_vStretch(2) = vSize(2) / 65535.0f;
|
|
tr_vStretch(3) = vSize(3) / (tr_pixHeightMapHeight-1);
|
|
// remember new size
|
|
tr_vTerrainSize = vSize;
|
|
}
|
|
|
|
template <class Type>
|
|
static void CropMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData)
|
|
{
|
|
INDEX iWidth = Min(iOldWidth,iNewWidth);
|
|
INDEX iHeight = Min(iOldHeight,iNewHeight);
|
|
INDEX iNewStepX = ClampDn(iNewWidth-iOldWidth,0L);
|
|
INDEX iOldStepX = ClampDn(iOldWidth-iNewWidth,0L);
|
|
|
|
INDEX iNew = 0;
|
|
INDEX iOld = 0;
|
|
for(INDEX iy=0;iy<iHeight;iy++) {
|
|
for(INDEX ix=0;ix<iWidth;ix++) {
|
|
pNewData[iNew] = pOldData[iOld];
|
|
iNew++;
|
|
iOld++;
|
|
}
|
|
iNew += iNewStepX;
|
|
iOld += iOldStepX;
|
|
}
|
|
}
|
|
|
|
template <class Type>
|
|
static void StretchMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData)
|
|
{
|
|
int a=0;
|
|
CropMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData);
|
|
}
|
|
|
|
template <class Type>
|
|
static void ShrinkMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData)
|
|
{
|
|
FLOAT fWidth = iNewWidth;
|
|
FLOAT fHeight = iNewHeight;
|
|
FLOAT fDiffX = (FLOAT)iNewWidth / iOldWidth;
|
|
FLOAT fDiffY = (FLOAT)iNewHeight / iOldHeight;
|
|
|
|
ULONG *pulNewData = (ULONG*)AllocMemory(iNewWidth * iNewHeight * sizeof(ULONG));
|
|
memset(pulNewData,0,iNewWidth * iNewHeight * sizeof(ULONG));
|
|
|
|
INDEX iOldPix = 0;
|
|
for(FLOAT fy=0;fy<iNewHeight;fy+=fDiffY) {
|
|
for(FLOAT fx=0;fx<iNewWidth;fx+=fDiffX) {
|
|
INDEX iNewPix = floor(fx) + floor(fy) * iNewWidth;
|
|
pulNewData[iNewPix] += pOldData[iOldPix];
|
|
iOldPix++;
|
|
}
|
|
}
|
|
|
|
ULONG ulDiv = ceil(1.0f/fDiffX) * ceil(1.0f/fDiffY);
|
|
for(INDEX ii=0;ii<iNewWidth*iNewHeight;ii++) {
|
|
pNewData[ii] = pulNewData[ii] / ulDiv;
|
|
}
|
|
|
|
FreeMemory(pulNewData);
|
|
ASSERT(_CrtCheckMemory());
|
|
}
|
|
|
|
template <class Type>
|
|
static void ResizeMap(INDEX iNewWidth, INDEX iNewHeight, INDEX iOldWidth, INDEX iOldHeight, Type *pNewData, Type *pOldData)
|
|
{
|
|
CropMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData);
|
|
/*
|
|
if(iNewWidth>=iOldWidth && iNewHeight>=iOldHeight) {
|
|
StretchMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData);
|
|
} else if(iNewWidth<=iOldWidth && iNewHeight<=iOldHeight) {
|
|
ShrinkMap(iNewWidth,iNewHeight,iOldWidth,iOldHeight,pNewData,pOldData);
|
|
} else {
|
|
INDEX iTempWidth = Max(iNewWidth ,iOldWidth);
|
|
INDEX iTempHeight = Max(iNewHeight,iOldHeight);
|
|
INDEX iTempSize = iTempWidth*iTempHeight*sizeof(Type);
|
|
Type *pTempData = (Type*)AllocMemory(iTempSize);
|
|
memset(pTempData,0,iTempSize);
|
|
StretchMap(iTempWidth,iTempHeight,iOldWidth,iOldHeight,pTempData,pOldData);
|
|
ShrinkMap(iNewWidth,iNewHeight,iTempWidth,iTempHeight,pNewData, pTempData);
|
|
FreeMemory(pTempData);
|
|
}
|
|
*/
|
|
}
|
|
|
|
void CTerrain::AllocateHeightMap(PIX pixWidth, PIX pixHeight)
|
|
{
|
|
ASSERT(tr_auwHeightMap==NULL);
|
|
ASSERT(tr_aubEdgeMap==NULL);
|
|
|
|
FLOAT fLogWidht = Log2(pixWidth-1);
|
|
FLOAT fLogHeight = Log2(pixHeight-1);
|
|
if(fLogWidht!=INDEX(fLogWidht) || fLogHeight!=INDEX(fLogHeight)) {
|
|
ASSERTALWAYS("Invalid terrain width or height");
|
|
return;
|
|
}
|
|
if(pixWidth != pixHeight) {
|
|
ASSERTALWAYS("Only terrains with same width and height are supported in this version");
|
|
return;
|
|
}
|
|
|
|
INDEX iSize = pixWidth * pixHeight * sizeof(UBYTE);
|
|
|
|
// Allocate memory for maps
|
|
tr_auwHeightMap = (UWORD*)AllocMemory(iSize*2);
|
|
tr_aubEdgeMap = (UBYTE*)AllocMemory(iSize);
|
|
memset(tr_auwHeightMap,0,iSize*2);
|
|
memset(tr_aubEdgeMap,255,iSize);
|
|
|
|
tr_pixHeightMapWidth = pixWidth;
|
|
tr_pixHeightMapHeight = pixHeight;
|
|
|
|
// Update shadow map size cos it depends on size of height map
|
|
SetShadowMapsSize(tr_iShadowMapSizeAspect,tr_iShadingMapSizeAspect);
|
|
}
|
|
|
|
void CTerrain::ReAllocateHeightMap(PIX pixWidth, PIX pixHeight)
|
|
{
|
|
ASSERT(tr_auwHeightMap!=NULL);
|
|
ASSERT(tr_aubEdgeMap!=NULL);
|
|
|
|
FLOAT fLogWidht = Log2(pixWidth-1);
|
|
FLOAT fLogHeight = Log2(pixHeight-1);
|
|
if(fLogWidht!=INDEX(fLogWidht) || fLogHeight!=INDEX(fLogHeight)) {
|
|
ASSERTALWAYS("Invalid terrain width or height");
|
|
return;
|
|
}
|
|
if(pixWidth != pixHeight) {
|
|
ASSERTALWAYS("Only terrains with same width and height are supported in this version");
|
|
return;
|
|
}
|
|
|
|
INDEX iSize = pixWidth * pixHeight * sizeof(UBYTE);
|
|
|
|
// Allocate memory for maps
|
|
UWORD *auwHeightMap = (UWORD*)AllocMemory(iSize*2);
|
|
UBYTE *aubEdgeMap = (UBYTE*)AllocMemory(iSize);
|
|
|
|
// Resize height map
|
|
memset(auwHeightMap,0,iSize*2);
|
|
ResizeMap(pixWidth,pixHeight,tr_pixHeightMapWidth,tr_pixHeightMapHeight,auwHeightMap,tr_auwHeightMap);
|
|
|
|
// Resize edge map
|
|
memset(aubEdgeMap,255,iSize);
|
|
ResizeMap(pixWidth,pixHeight,tr_pixHeightMapWidth,tr_pixHeightMapHeight,aubEdgeMap,tr_aubEdgeMap);
|
|
|
|
// for each layer
|
|
INDEX cttl = tr_atlLayers.Count();
|
|
for(INDEX itl=0;itl<cttl;itl++) {
|
|
CTerrainLayer &tl = tr_atlLayers[itl];
|
|
// Allocate memory for layer mask
|
|
UBYTE *aubLayerMask = (UBYTE*)AllocMemory(iSize);
|
|
memset(aubLayerMask,0,iSize);
|
|
ASSERT(tl.tl_iMaskWidth == tr_pixHeightMapWidth);
|
|
ASSERT(tl.tl_iMaskHeight == tr_pixHeightMapHeight);
|
|
// resize layer
|
|
ResizeMap(pixWidth,pixHeight,tl.tl_iMaskWidth,tl.tl_iMaskHeight,aubLayerMask,tl.tl_aubColors);
|
|
// Free old mask
|
|
FreeMemory(tl.tl_aubColors);
|
|
// Apply changes
|
|
tl.tl_aubColors = aubLayerMask;
|
|
tl.tl_iMaskWidth = pixWidth;
|
|
tl.tl_iMaskHeight = pixHeight;
|
|
// if this is first layer
|
|
if(itl==0) {
|
|
// fill it
|
|
tl.ResetLayerMask(255);
|
|
}
|
|
}
|
|
|
|
// Free old maps
|
|
FreeMemory(tr_auwHeightMap);
|
|
FreeMemory(tr_aubEdgeMap);
|
|
|
|
// Apply changes
|
|
tr_auwHeightMap = auwHeightMap;
|
|
tr_aubEdgeMap = aubEdgeMap;
|
|
tr_pixHeightMapWidth = pixWidth;
|
|
tr_pixHeightMapHeight = pixHeight;
|
|
|
|
ASSERT(_CrtCheckMemory());
|
|
|
|
// Update shadow map size cos it depends on size of height map
|
|
SetShadowMapsSize(tr_iShadowMapSizeAspect,tr_iShadingMapSizeAspect);
|
|
/*
|
|
// Clear current maps if they exists
|
|
ClearHeightMap();
|
|
ClearEdgeMap();
|
|
ClearLayers();
|
|
*/
|
|
/*
|
|
ASSERT(tr_auwHeightMap==NULL);
|
|
ASSERT(tr_aubEdgeMap==NULL);
|
|
|
|
INDEX iSize = sizeof(UBYTE)*pixWidth*pixHeight;
|
|
// Allocate memory for heightmap
|
|
tr_auwHeightMap = (UWORD*)AllocMemory(iSize*2);
|
|
// Allocate memory for edge map
|
|
tr_aubEdgeMap = (UBYTE*)AllocMemory(iSize);
|
|
|
|
// Reset height map to 0
|
|
memset(tr_auwHeightMap,0,iSize*2);
|
|
// Reset edge map to 255
|
|
memset(tr_aubEdgeMap,255,iSize);
|
|
|
|
tr_pixHeightMapWidth = pixWidth;
|
|
tr_pixHeightMapHeight = pixHeight;
|
|
*/
|
|
}
|
|
|
|
// Set shadow map size aspect (relative to height map size) and shading map aspect (relative to shadow map size)
|
|
void CTerrain::SetShadowMapsSize(INDEX iShadowMapAspect, INDEX iShadingMapAspect)
|
|
{
|
|
// TEMP
|
|
#pragma message(">> Clamp dn SetShadowMapsSize")
|
|
|
|
if(iShadingMapAspect<0) {
|
|
iShadingMapAspect = 0;
|
|
}
|
|
ASSERT(iShadingMapAspect>=0);
|
|
|
|
|
|
tr_iShadowMapSizeAspect = iShadowMapAspect;
|
|
tr_iShadingMapSizeAspect = iShadingMapAspect;
|
|
|
|
if(GetShadowMapWidth()<32 || GetShadingMapHeight()<32) {
|
|
tr_iShadowMapSizeAspect = -(FastLog2(tr_pixHeightMapWidth-1)-5);
|
|
}
|
|
|
|
if(GetShadingMapWidth()<32 || GetShadingMapWidth()<32) {
|
|
tr_iShadingMapSizeAspect = 0;
|
|
}
|
|
|
|
PIX pixShadowMapWidth = GetShadowMapWidth();
|
|
PIX pixShadowMapHeight = GetShadowMapHeight();
|
|
|
|
PIX pixShadingMapWidth = GetShadingMapWidth();
|
|
PIX pixShadingMapHeight = GetShadingMapHeight();
|
|
|
|
|
|
// Clear current shadow map
|
|
ClearShadowMap();
|
|
|
|
ULONG ulShadowMapFlags = 0;
|
|
// if current app is world editor app
|
|
if(_bWorldEditorApp) {
|
|
// force texture to be static
|
|
ulShadowMapFlags = TEX_STATIC;
|
|
}
|
|
|
|
// Create new shadow map texture
|
|
ASSERT(tr_tdShadowMap.td_pulFrames==NULL);
|
|
CreateTexture(tr_tdShadowMap,pixShadowMapWidth,pixShadowMapHeight,ulShadowMapFlags);
|
|
// Reset shadow map texture
|
|
memset(&tr_tdShadowMap.td_pulFrames[0],0,sizeof(COLOR)*pixShadowMapWidth*pixShadowMapHeight);
|
|
|
|
// Create new shading map
|
|
ASSERT(tr_auwShadingMap==NULL);
|
|
tr_auwShadingMap = (UWORD*)AllocMemory(pixShadingMapWidth*pixShadingMapHeight*sizeof(UWORD));
|
|
// Reset shading map
|
|
memset(&tr_auwShadingMap[0],0,pixShadingMapWidth*pixShadingMapHeight*sizeof(UWORD));
|
|
}
|
|
|
|
// Set size of terrain top map texture
|
|
void CTerrain::SetGlobalTopMapSize(PIX pixTopMapSize)
|
|
{
|
|
FLOAT fLogSize = Log2(pixTopMapSize);
|
|
if(fLogSize!=INDEX(fLogSize)) {
|
|
ASSERTALWAYS("Invalid top map size");
|
|
return;
|
|
}
|
|
|
|
tr_pixTopMapWidth = pixTopMapSize;
|
|
tr_pixTopMapHeight = pixTopMapSize;
|
|
}
|
|
|
|
// Set size of top map texture for tiles in lower lods
|
|
void CTerrain::SetTileTopMapSize(PIX pixLodTopMapSize)
|
|
{
|
|
FLOAT fLogSize = Log2(pixLodTopMapSize);
|
|
if(fLogSize!=INDEX(fLogSize)) {
|
|
ASSERTALWAYS("Invalid top map size");
|
|
return;
|
|
}
|
|
|
|
tr_pixFirstMipTopMapWidth = pixLodTopMapSize;
|
|
tr_pixFirstMipTopMapHeight = pixLodTopMapSize;
|
|
}
|
|
|
|
// Set lod distance factor
|
|
void CTerrain::SetLodDistanceFactor(FLOAT fLodDistance)
|
|
{
|
|
tr_fDistFactor = ClampDn(fLodDistance,0.1f);
|
|
}
|
|
|
|
// Get shadow map size
|
|
PIX CTerrain::GetShadowMapWidth(void)
|
|
{
|
|
if(tr_iShadowMapSizeAspect<0) {
|
|
return (tr_pixHeightMapWidth-1)>>-tr_iShadowMapSizeAspect;
|
|
} else {
|
|
return (tr_pixHeightMapWidth-1)<<tr_iShadowMapSizeAspect;
|
|
}
|
|
}
|
|
PIX CTerrain::GetShadowMapHeight(void)
|
|
{
|
|
if(tr_iShadowMapSizeAspect<0) {
|
|
return (tr_pixHeightMapHeight-1)>>-tr_iShadowMapSizeAspect;
|
|
} else {
|
|
return (tr_pixHeightMapHeight-1)<<tr_iShadowMapSizeAspect;
|
|
}
|
|
}
|
|
|
|
// Get shading map size
|
|
PIX CTerrain::GetShadingMapWidth(void)
|
|
{
|
|
ASSERT(tr_iShadingMapSizeAspect>=0);
|
|
return GetShadowMapWidth()>>tr_iShadingMapSizeAspect;
|
|
}
|
|
PIX CTerrain::GetShadingMapHeight(void)
|
|
{
|
|
ASSERT(tr_iShadingMapSizeAspect>=0);
|
|
return GetShadowMapHeight()>>tr_iShadingMapSizeAspect;
|
|
}
|
|
|
|
// Get reference to layer
|
|
CTerrainLayer &CTerrain::GetLayer(INDEX iLayer)
|
|
{
|
|
INDEX cttl = tr_atlLayers.Count();
|
|
|
|
ASSERT(iLayer<cttl);
|
|
ASSERT(iLayer>=0);
|
|
return tr_atlLayers[iLayer];
|
|
}
|
|
|
|
// Add new layer
|
|
CTerrainLayer &CTerrain::AddLayer_t(CTFileName fnTexture, LayerType ltType/*=LT_NORMAL*/, BOOL bUpdateTerrain/*=TRUE*/)
|
|
{
|
|
CTerrainLayer &tl = tr_atlLayers.Push();
|
|
|
|
// Set layer properties
|
|
tl.tl_ltType = ltType;
|
|
tl.SetLayerSize(tr_pixHeightMapWidth, tr_pixHeightMapHeight);
|
|
tl.SetLayerTexture_t(fnTexture);
|
|
|
|
// if update terrain flag has been set
|
|
if(bUpdateTerrain) {
|
|
// Refresh whole terrain
|
|
RefreshTerrain();
|
|
}
|
|
return tl;
|
|
}
|
|
|
|
// Remove one layer
|
|
void CTerrain::RemoveLayer(INDEX iLayer, BOOL bUpdateTerrain/*=TRUE*/)
|
|
{
|
|
CStaticStackArray<class CTerrainLayer> atlLayers;
|
|
INDEX cttl = tr_atlLayers.Count();
|
|
|
|
if(iLayer<0 || iLayer>=cttl) {
|
|
ASSERTALWAYS("Invalid layer index");
|
|
return;
|
|
}
|
|
|
|
if(iLayer==0 && cttl==1) {
|
|
ASSERTALWAYS("Can't remove last layer");
|
|
return;
|
|
}
|
|
|
|
// for each exisiting layer
|
|
for(INDEX itl=0;itl<cttl;itl++) {
|
|
// if this layer index is not same as index of layer that need to be removed
|
|
if(itl!=iLayer) {
|
|
// Add new layer
|
|
CTerrainLayer &tl = atlLayers.Push();
|
|
// Copy this layer into new array
|
|
tl = tr_atlLayers[itl];
|
|
}
|
|
}
|
|
|
|
ASSERT(atlLayers.Count() == cttl-1);
|
|
// Clear old layers
|
|
tr_atlLayers.Clear();
|
|
// Copy new layers insted of old one
|
|
tr_atlLayers = atlLayers;
|
|
|
|
// if update terrain flag has been set
|
|
if(bUpdateTerrain) {
|
|
// Refresh whole terrain
|
|
RefreshTerrain();
|
|
}
|
|
}
|
|
|
|
// Move layer to new position
|
|
INDEX CTerrain::SetLayerIndex(INDEX iLayer, INDEX iNewIndex, BOOL bUpdateTerrain/*=TRUE*/)
|
|
{
|
|
CStaticStackArray<class CTerrainLayer> atlLayers;
|
|
INDEX cttl = tr_atlLayers.Count();
|
|
|
|
if(iLayer<0 || iLayer>=cttl) {
|
|
ASSERTALWAYS("Invalid layer index");
|
|
return iLayer;
|
|
}
|
|
|
|
if(iLayer==0 && cttl==1) {
|
|
ASSERTALWAYS("Can't move only layer");
|
|
return iLayer;
|
|
}
|
|
|
|
if(iLayer==iNewIndex) {
|
|
ASSERTALWAYS("Old layer index is same as new one");
|
|
return iLayer;
|
|
}
|
|
|
|
|
|
CStaticStackArray<class CTerrainLayer> &atlFrom = tr_atlLayers;
|
|
CStaticStackArray<class CTerrainLayer> &atlTo = atlLayers;
|
|
|
|
atlTo.Push(cttl);
|
|
|
|
INDEX iOld = iLayer;
|
|
INDEX iNew = iNewIndex;
|
|
|
|
for(INDEX iFrom=0; iFrom<cttl; iFrom++) {
|
|
INDEX iTo=-1;
|
|
if (iNew==iOld) {
|
|
iTo = iFrom;
|
|
} if ((iFrom<iOld && iFrom<iNew) || (iFrom>iOld && iFrom>iNew)) {
|
|
iTo = iFrom;
|
|
} else if (iFrom==iOld) {
|
|
iTo = iNew;
|
|
} else {
|
|
if (iNew>iOld) {
|
|
iTo = iFrom-1;
|
|
} else {
|
|
iTo = iFrom+1;
|
|
}
|
|
}
|
|
atlTo[iTo] = atlFrom[iFrom];
|
|
}
|
|
|
|
ASSERT(atlLayers.Count() == cttl);
|
|
// Clear old layers
|
|
tr_atlLayers.Clear();
|
|
// Copy new layers insted of old one
|
|
tr_atlLayers = atlLayers;
|
|
|
|
// if update terrain flag has been set
|
|
if(bUpdateTerrain) {
|
|
// Refresh whole terrain
|
|
RefreshTerrain();
|
|
}
|
|
return iNewIndex;
|
|
}
|
|
|
|
// Add tile to reqen queue
|
|
void CTerrain::AddTileToRegenQueue(INDEX iTileIndex)
|
|
{
|
|
INDEX &iRegenIndex = tr_auiRegenList.Push();
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
|
|
iRegenIndex = iTileIndex;
|
|
tt.AddFlag(TT_REGENERATE);
|
|
}
|
|
|
|
// Add all tiles to regen queue
|
|
void CTerrain::AddAllTilesToRegenQueue()
|
|
{
|
|
// for each terrain tile
|
|
for(INDEX itt=0;itt<tr_ctTiles;itt++) {
|
|
// Add tile to reqen queue
|
|
CTerrainTile &tt = tr_attTiles[itt];
|
|
AddTileToRegenQueue(itt);
|
|
}
|
|
}
|
|
|
|
// Clear current regen list
|
|
void CTerrain::ClearRegenList(void)
|
|
{
|
|
tr_auiRegenList.PopAll();
|
|
}
|
|
|
|
void CTerrain::UpdateShadowMap(FLOATaabbox3D *pbboxUpdate/*=NULL*/, BOOL bAbsoluteSpace/*=FALSE*/)
|
|
{
|
|
// if this is world editor app
|
|
if(!_bWorldEditorApp) {
|
|
// Shadow map can only be updated from world editor
|
|
return;
|
|
}
|
|
// if shadow update is allowed
|
|
if(_wrpWorldRenderPrefs.GetShadowsType()==CWorldRenderPrefs::SHT_FULL) {
|
|
// update terrain shadow map
|
|
UpdateTerrainShadowMap(this,pbboxUpdate,bAbsoluteSpace);
|
|
// don't update shadow map next frame
|
|
RemoveFlag(TR_UPDATE_SHADOWMAP);
|
|
// else
|
|
} else {
|
|
// dont update shadow map but remeber that it's not up to date
|
|
AddFlag(TR_UPDATE_SHADOWMAP);
|
|
}
|
|
}
|
|
|
|
// Temp:
|
|
__forceinline void CopyPixel(COLOR *pubSrc,COLOR *pubDst,FLOAT fMaskStrength)
|
|
{
|
|
GFXColor *pcolSrc = (GFXColor*)pubSrc;
|
|
GFXColor *pcolDst = (GFXColor*)pubDst;
|
|
pcolSrc->ub.r = Lerp(pcolSrc->ub.r,pcolDst->ub.r,fMaskStrength);
|
|
pcolSrc->ub.g = Lerp(pcolSrc->ub.g,pcolDst->ub.g,fMaskStrength);
|
|
pcolSrc->ub.b = Lerp(pcolSrc->ub.b,pcolDst->ub.b,fMaskStrength);
|
|
pcolSrc->ub.a = 255;
|
|
}
|
|
|
|
static INDEX _ctSavedTopMaps=0;
|
|
static void SaveAsTga(CTextureData *ptdTex)
|
|
{
|
|
INDEX iSize = ptdTex->td_mexWidth * ptdTex->td_mexHeight * 4;
|
|
CImageInfo iiHeightMap;
|
|
iiHeightMap.ii_Width = ptdTex->td_mexWidth;
|
|
iiHeightMap.ii_Height = ptdTex->td_mexHeight;
|
|
iiHeightMap.ii_BitsPerPixel = 32;
|
|
iiHeightMap.ii_Picture = (UBYTE*)AllocMemory(iSize);
|
|
|
|
memcpy(&iiHeightMap.ii_Picture[0],&ptdTex->td_pulFrames[0],iSize);
|
|
|
|
|
|
CTString strTopMap = CTString(0,"Temp\\Topmap%d.tga",++_ctSavedTopMaps);
|
|
iiHeightMap.SaveTGA_t(strTopMap);
|
|
iiHeightMap.Clear();
|
|
|
|
|
|
/*
|
|
GFXColor *pacolImage = (GFXColor*)&iiHeightMap.ii_Picture[0];
|
|
UWORD *puwHeight = tr_auwHeightMap;
|
|
for(INDEX ipix=0;ipix<iSize;ipix++) {
|
|
*pacolImage = 0x00000000;
|
|
if(bUse16b) {
|
|
UWORD *puwData = (UWORD*)&pacolImage[0];
|
|
*puwData = *puwHeight;
|
|
} else {
|
|
UBYTE *pubData = (UBYTE*)&pacolImage[0];
|
|
UWORD *puwHData = puwHeight;
|
|
*pubData = (UBYTE)(*puwHData>>8);
|
|
}
|
|
pacolImage++;
|
|
puwHeight++;
|
|
}
|
|
*/
|
|
|
|
}
|
|
|
|
static void AddTileLayerToTopMap(CTerrain *ptrTerrain, INDEX iTileIndex, INDEX iLayer)
|
|
{
|
|
CTerrainLayer &tl = ptrTerrain->tr_atlLayers[iLayer];
|
|
CTextureData *ptdSrc = tl.tl_ptdTexture;
|
|
CTextureData *ptdDst;
|
|
|
|
INDEX ctQuadsPerTile = ptrTerrain->tr_ctQuadsInTileRow;
|
|
INDEX iOffsetX = 0;
|
|
INDEX iOffsetZ = 0;
|
|
|
|
if(iTileIndex==(-1)) {
|
|
ptdDst = &ptrTerrain->tr_tdTopMap;
|
|
ctQuadsPerTile = ptrTerrain->tr_ctTilesX * ptrTerrain->tr_ctQuadsInTileRow;
|
|
} else {
|
|
CTerrainTile &tt = ptrTerrain->tr_attTiles[iTileIndex];
|
|
ptdDst = tt.GetTopMap();
|
|
iOffsetX = tt.tt_iOffsetX*ctQuadsPerTile;
|
|
iOffsetZ = tt.tt_iOffsetZ*ctQuadsPerTile;
|
|
}
|
|
|
|
ULONG *pulFirstInTopMap = ptdDst->td_pulFrames;
|
|
UBYTE *pubFirstInLayerMask = tl.tl_aubColors;
|
|
|
|
// Calculate width and height of quad that will be draw in top map
|
|
PIX pixDstQuadWidth = ptdDst->GetPixWidth() / ctQuadsPerTile;
|
|
PIX pixSrcQuadWidth = tl.tl_pixTileWidth;
|
|
|
|
// if dst quad is smaller then one pixel
|
|
if(pixDstQuadWidth==0) {
|
|
return;
|
|
}
|
|
|
|
ASSERT(tl.tl_ctTilesInRow==tl.tl_ctTilesInCol);
|
|
|
|
INDEX iSrcMipmap = FastLog2((ptdSrc->GetPixWidth() / tl.tl_ctTilesInRow) / pixDstQuadWidth);
|
|
INDEX iSrcMipMapOffset = GetMipmapOffset(iSrcMipmap,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight());
|
|
INDEX iSrcMipWidth = ptdSrc->GetPixWidth() >> iSrcMipmap;
|
|
INDEX iSrcMipQuadWidth = pixSrcQuadWidth >> iSrcMipmap;
|
|
INDEX iSrcMipQuadHeight = iSrcMipQuadWidth;
|
|
|
|
ASSERT(pixDstQuadWidth==iSrcMipQuadWidth);
|
|
|
|
ULONG *pulSrcMip = &ptdSrc->td_pulFrames[iSrcMipMapOffset];
|
|
|
|
INDEX iMaskIndex = iOffsetX + iOffsetZ*ptrTerrain->tr_pixHeightMapWidth;
|
|
INDEX iMaskStepX = ptrTerrain->tr_pixHeightMapWidth - ctQuadsPerTile;
|
|
|
|
// for each quad in tile
|
|
for(INDEX iQuadY=0;iQuadY<ctQuadsPerTile;iQuadY++) {
|
|
for(INDEX iQuadX=0;iQuadX<ctQuadsPerTile;iQuadX++) {
|
|
|
|
UBYTE ubMask = pubFirstInLayerMask[iMaskIndex];
|
|
BOOL bFlipX = (ubMask&TL_FLIPX)>>TL_FLIPX_SHIFT;
|
|
BOOL bFlipY = (ubMask&TL_FLIPY)>>TL_FLIPY_SHIFT;
|
|
BOOL bSwapXY = (ubMask&TL_SWAPXY)>>TL_SWAPXY_SHIFT;
|
|
BOOL bVisible = (ubMask&TL_VISIBLE)>>TL_VISIBLE_SHIFT;
|
|
INDEX iTile = ubMask&TL_TILE_INDEX;
|
|
INDEX iTileX = iTile%tl.tl_ctTilesInRow;
|
|
INDEX iTileY = iTile/tl.tl_ctTilesInRow;
|
|
|
|
// if not visible
|
|
if(!bVisible) {
|
|
iMaskIndex++;
|
|
continue; // skip it
|
|
}
|
|
|
|
ASSERT(iTileX<tl.tl_ctTilesInRow);
|
|
ASSERT(iTileY<tl.tl_ctTilesInCol);
|
|
|
|
INDEX iFirstDstQuadPixel = (iQuadX*pixDstQuadWidth) + iQuadY*pixDstQuadWidth*ptdDst->GetPixWidth();
|
|
ULONG *pulDstPixel = &pulFirstInTopMap[iFirstDstQuadPixel];
|
|
PIX pixSrc = iTileX*iSrcMipQuadWidth + iTileY*iSrcMipQuadWidth*iSrcMipWidth;
|
|
PIX pixDst = 0;
|
|
PIX pixDstModulo = ptdDst->GetPixWidth() - pixDstQuadWidth;
|
|
PIX pixSrcStepY = iSrcMipWidth;
|
|
PIX pixSrcStepX = 1;
|
|
|
|
if(bFlipY) {
|
|
pixSrcStepY = -pixSrcStepY;
|
|
pixSrc+=iSrcMipQuadHeight*iSrcMipWidth;
|
|
}
|
|
if(bFlipX) {
|
|
pixSrcStepX = -pixSrcStepX;
|
|
pixSrc+=iSrcMipQuadWidth;
|
|
}
|
|
|
|
if(bSwapXY) {
|
|
Swap(pixSrcStepX, pixSrcStepY);
|
|
}
|
|
|
|
pixSrcStepY -= pixDstQuadWidth*pixSrcStepX;
|
|
|
|
// for each pixel in this quad
|
|
for(PIX pixY=0;pixY<pixDstQuadWidth;pixY++) {
|
|
for(PIX pixX=0;pixX<pixDstQuadWidth;pixX++) {
|
|
if((pulSrcMip[pixSrc]&0xFF000000) > 0x80000000) {
|
|
pulDstPixel[pixDst] = pulSrcMip[pixSrc];
|
|
}
|
|
pixSrc+=pixSrcStepX;
|
|
pixDst++;
|
|
}
|
|
pixDst+=pixDstModulo;
|
|
pixSrc+=pixSrcStepY;
|
|
}
|
|
iMaskIndex++;
|
|
}
|
|
iMaskIndex+=iMaskStepX;
|
|
}
|
|
}
|
|
|
|
void CTerrain::UpdateTopMap(INDEX iTileIndex, Rect *prcDest/*=NULL*/)
|
|
{
|
|
//ReGenerateTopMap(this, iTileIndex);
|
|
|
|
if(iTileIndex==(-1)) {
|
|
ctGlobalTopMaps++;
|
|
} else {
|
|
ctGeneratedTopMaps++;
|
|
}
|
|
|
|
FIX16_16 fiMaskDiv = 1;
|
|
INDEX iFirstInMask = 0;
|
|
INDEX iMaskWidth = tr_pixHeightMapWidth;
|
|
INDEX iTiling = 1;
|
|
INDEX iSrcMipWidth = 1;
|
|
|
|
|
|
// destionation texture (must have set allocated memory)
|
|
CTextureData *ptdDest;
|
|
|
|
// if global top map
|
|
if(iTileIndex==(-1)) {
|
|
ptdDest = &tr_tdTopMap;
|
|
// else tile top map
|
|
} else {
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
ptdDest = tt.GetTopMap();
|
|
fiMaskDiv = tr_ctTilesX;
|
|
iFirstInMask = iMaskWidth * tt.tt_iOffsetZ * (tr_ctVerticesInTileRow-1) + (tt.tt_iOffsetX * (tr_ctVerticesInTileRow-1));
|
|
}
|
|
|
|
ASSERT(ptdDest->td_pulFrames==NULL);
|
|
PrepareSharedTopMapMemory(ptdDest, iTileIndex);
|
|
|
|
ASSERT(ptdDest!=NULL);
|
|
ASSERT(ptdDest->td_pulFrames!=NULL);
|
|
|
|
// ASSERT(ptdDest->GetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight());
|
|
// iTiling = ClampDn(iTiling,(INDEX)1);
|
|
// INDEX iSrcMipWidth = ClampDn(ptdDest->GetWidth()/iTiling,(INDEX)1);
|
|
|
|
|
|
INDEX ctLayers = tr_atlLayers.Count();
|
|
// for each layer
|
|
for(INDEX itl=0;itl<ctLayers;itl++) {
|
|
CTerrainLayer &tl = tr_atlLayers[itl];
|
|
// if layer isn't visible
|
|
if(!tl.tl_bVisible) {
|
|
// skip it
|
|
continue;
|
|
}
|
|
if(tl.tl_ltType==LT_TILE) {
|
|
AddTileLayerToTopMap(this,iTileIndex,itl);
|
|
continue;
|
|
}
|
|
|
|
if(iTileIndex==(-1)) {
|
|
// ptdDest = &tr_tdTopMap;
|
|
iTiling = (INDEX)(tr_ctTilesX*tr_ctQuadsInTileRow*tl.tl_fStretchX);
|
|
|
|
// else tile top map
|
|
} else {
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
// ptdDest = tt.GetTopMap();
|
|
fiMaskDiv = tr_ctTilesX;
|
|
iFirstInMask = iMaskWidth * tt.tt_iOffsetZ * (tr_ctVerticesInTileRow-1)
|
|
+ (tt.tt_iOffsetX * (tr_ctVerticesInTileRow-1));
|
|
iTiling = (INDEX)(tr_ctQuadsInTileRow*tl.tl_fStretchX);
|
|
}
|
|
|
|
ASSERT(ptdDest->GetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight());
|
|
ASSERT(iTiling>=1);
|
|
iTiling = ClampDn(iTiling,(INDEX)1);
|
|
|
|
// get source texture
|
|
CTextureData *ptdSrc = tl.tl_ptdTexture;
|
|
INDEX iSrcMipWidth = ClampDn( ptdSrc->GetPixWidth() /iTiling, 1L);
|
|
INDEX iSrcMipHeight = ClampDn( ptdSrc->GetPixHeight()/iTiling, 1L);
|
|
|
|
// Get mipmap of source texture
|
|
INDEX immW = FastLog2( ptdSrc->GetPixWidth() / iSrcMipWidth);
|
|
INDEX immH = FastLog2( ptdSrc->GetPixHeight() / iSrcMipHeight);
|
|
// get address of first byte in source mipmap
|
|
INDEX imm = Max( immW, immH);
|
|
INDEX iMipAdr = GetMipmapOffset(imm,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight());
|
|
|
|
// Mask thing
|
|
// get first byte in layer mask
|
|
UBYTE *ubFirstInMask = &tl.tl_aubColors[iFirstInMask];
|
|
// get first byte in edge map
|
|
UBYTE *ubFirstInEdgeMap = &tr_aubEdgeMap[iFirstInMask];
|
|
FIX16_16 fiHMaskStep = FIX16_16(iMaskWidth-1) / FIX16_16(ptdDest->GetWidth()-1) / fiMaskDiv;
|
|
FIX16_16 fiVMaskStep = FIX16_16(iMaskWidth-1) / FIX16_16(ptdDest->GetWidth()-1) / fiMaskDiv;
|
|
|
|
SLONG xHMaskStep = fiHMaskStep.slHolder;
|
|
SLONG xVMaskStep = fiVMaskStep.slHolder;
|
|
SLONG xMaskVPos=0;
|
|
|
|
// get first byte in destination texture
|
|
ULONG *pulTexDst = (ULONG*)&ptdDest->td_pulFrames[0];
|
|
// get first byte in source texture
|
|
ULONG *pulFirstInMipSrc = (ULONG*)&ptdSrc->td_pulFrames[iMipAdr];
|
|
|
|
// for each row
|
|
for(UINT ir=0;ir<ptdDest->GetPixHeight();ir++)
|
|
{
|
|
// get first byte for src mip texture in this row
|
|
ULONG *pulSrcRow = &pulFirstInMipSrc[(ir&(iSrcMipWidth-1))*iSrcMipWidth];//%
|
|
INDEX iMaskVPos = (INDEX)(xMaskVPos>>16) * (iMaskWidth);
|
|
UBYTE *pubMaskRow = &ubFirstInMask[iMaskVPos];
|
|
UBYTE *pubEdgeMaskRow = &ubFirstInEdgeMap[iMaskVPos];
|
|
SLONG xMaskHPos = 0;
|
|
// for each column
|
|
for(UINT ic=0;ic<ptdDest->GetPixWidth();ic++)
|
|
{
|
|
ULONG *ulSrc = &pulSrcRow[ic&(iSrcMipWidth-1)];
|
|
INDEX iMask = (INDEX)(xMaskHPos>>16);
|
|
|
|
SLONG x1 = (SLONG)(pubMaskRow[iMask+0]) <<0; //NormByteToFixInt(pubMaskRow[iMask]);
|
|
SLONG x2 = (SLONG)(pubMaskRow[iMask+1]) <<0; //NormByteToFixInt(pubMaskRow[iMask+1]);
|
|
SLONG x3 = (SLONG)(pubMaskRow[iMask+iMaskWidth+0]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+0]);
|
|
SLONG x4 = (SLONG)(pubMaskRow[iMask+iMaskWidth+1]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+1]);
|
|
SLONG xFactH = xMaskHPos - (xMaskHPos&0xFFFF0000);
|
|
SLONG xFactV = xMaskVPos - (xMaskVPos&0xFFFF0000);
|
|
|
|
SLONG xStrengthX1 = (x1<<7) + (SLONG)(((x2-x1)*xFactH)>>9); //Lerp(fi1,fi2,fiFactH);
|
|
SLONG xStrengthX2 = (x3<<7) + (SLONG)(((x4-x3)*xFactH)>>9); //Lerp(fi3,fi4,fiFactH);
|
|
SLONG xStrength = (xStrengthX1<<1) + (SLONG)((((xStrengthX2>>0)-(xStrengthX1>>0))*xFactV)>>15); //Lerp(fiStrengthX1,fiStrengthX2,fiFactV);
|
|
|
|
GFXColor *pcolSrc = (GFXColor*)pulTexDst;
|
|
GFXColor *pcolDst = (GFXColor*)ulSrc;
|
|
pcolSrc->ub.r = (BYTE)( (ULONG)pcolSrc->ub.r + ((((ULONG)pcolDst->ub.r - (ULONG)pcolSrc->ub.r) * xStrength)>>16));
|
|
pcolSrc->ub.g = (BYTE)( (ULONG)pcolSrc->ub.g + ((((ULONG)pcolDst->ub.g - (ULONG)pcolSrc->ub.g) * xStrength)>>16));
|
|
pcolSrc->ub.b = (BYTE)( (ULONG)pcolSrc->ub.b + ((((ULONG)pcolDst->ub.b - (ULONG)pcolSrc->ub.b) * xStrength)>>16));
|
|
pcolSrc->ub.a = pubEdgeMaskRow[iMask];
|
|
|
|
pulTexDst++;
|
|
xMaskHPos += xHMaskStep;
|
|
}
|
|
xMaskVPos += xVMaskStep;
|
|
}
|
|
}
|
|
// make mipmaps
|
|
INDEX ctMipMaps = GetNoOfMipmaps(ptdDest->GetPixWidth(),ptdDest->GetPixHeight());
|
|
MakeMipmaps(ctMipMaps, ptdDest->td_pulFrames, ptdDest->GetPixWidth(), ptdDest->GetPixHeight());
|
|
|
|
#pragma message(">> Fix DitherMipmaps")
|
|
INDEX iDithering = 4;
|
|
DitherMipmaps(iDithering,ptdDest->td_pulFrames,ptdDest->td_pulFrames,ptdDest->GetPixWidth(),ptdDest->GetPixHeight());
|
|
// force topmap upload
|
|
ptdDest->SetAsCurrent(0,TRUE);
|
|
|
|
// Free shared memory
|
|
FreeSharedTopMapMemory(ptdDest, iTileIndex);
|
|
}
|
|
|
|
void CTerrain::GetAllTerrainBBox(FLOATaabbox3D &bbox)
|
|
{
|
|
// Get last quad tree level
|
|
INDEX ctqtl = tr_aqtlQuadTreeLevels.Count();
|
|
QuadTreeLevel &qtl = tr_aqtlQuadTreeLevels[ctqtl-1];
|
|
|
|
ASSERT(qtl.qtl_ctNodes==1);
|
|
// Get quad tree node for last level
|
|
QuadTreeNode &qtn = tr_aqtnQuadTreeNodes[qtl.qtl_iFirstNode];
|
|
bbox = qtn.qtn_aabbox;
|
|
}
|
|
|
|
// Get shading color from tex coords in shading map
|
|
COLOR CTerrain::GetShadeColor(CShadingInfo *psi)
|
|
{
|
|
ASSERT(psi!=NULL);
|
|
ASSERT(tr_auwShadingMap!=NULL);
|
|
|
|
PIX pixShadowU = Clamp(psi->si_pixShadowU,0L,GetShadingMapWidth()-2L);
|
|
PIX pixShadowV = Clamp(psi->si_pixShadowV,0L,GetShadingMapHeight()-2L);
|
|
FLOAT fUDRatio = psi->si_fUDRatio;
|
|
FLOAT fLRRatio = psi->si_fLRRatio;
|
|
|
|
PIX pixWidth = GetShadingMapWidth();
|
|
PIX pixShadow = pixShadowU + pixShadowV*pixWidth;
|
|
UWORD auwShade[4];
|
|
SLONG aslr[4], aslg[4], aslb[4];
|
|
|
|
auwShade[0] = tr_auwShadingMap[pixShadow];
|
|
auwShade[1] = tr_auwShadingMap[pixShadow+1];
|
|
auwShade[2] = tr_auwShadingMap[pixShadow+pixWidth+0];
|
|
auwShade[3] = tr_auwShadingMap[pixShadow+pixWidth+1];
|
|
|
|
for(INDEX ish=0;ish<4;ish++) {
|
|
aslr[ish] = (auwShade[ish]&0x7C00)>>10;
|
|
aslg[ish] = (auwShade[ish]&0x03E0)>> 5;
|
|
aslb[ish] = (auwShade[ish]&0x001F)>> 0;
|
|
|
|
aslr[ish] = (aslr[ish]<<3) | (aslr[ish]>>2);
|
|
aslg[ish] = (aslg[ish]<<3) | (aslg[ish]>>2);
|
|
aslb[ish] = (aslb[ish]<<3) | (aslb[ish]>>2);
|
|
}
|
|
|
|
SLONG slRed = Lerp( Lerp(aslr[0], aslr[1], fLRRatio), Lerp(aslr[2], aslr[3], fLRRatio), fUDRatio);
|
|
SLONG slGreen = Lerp( Lerp(aslg[0], aslg[1], fLRRatio), Lerp(aslg[2], aslg[3], fLRRatio), fUDRatio);
|
|
SLONG slBlue = Lerp( Lerp(aslb[0], aslb[1], fLRRatio), Lerp(aslb[2], aslb[3], fLRRatio), fUDRatio);
|
|
|
|
ULONG ulPixel = ((slRed <<24)&0xFF000000) |
|
|
((slGreen<<16)&0x00FF0000) |
|
|
((slBlue << 8)&0x0000FF00) | 0xFF;
|
|
|
|
return ulPixel;
|
|
}
|
|
|
|
// Get plane from given point
|
|
FLOATplane3D CTerrain::GetPlaneFromPoint(FLOAT3D &vAbsPoint)
|
|
{
|
|
ASSERT(tr_penEntity!=NULL);
|
|
FLOAT3D vRelPoint = (vAbsPoint-tr_penEntity->en_plPlacement.pl_PositionVector) * !tr_penEntity->en_mRotation;
|
|
vRelPoint(1) /= tr_vStretch(1);
|
|
vRelPoint(3) /= tr_vStretch(3);
|
|
PIX pixX = (PIX) floor(vRelPoint(1));
|
|
PIX pixZ = (PIX) floor(vRelPoint(3));
|
|
PIX pixWidth = tr_pixHeightMapWidth;
|
|
FLOAT fXRatio = vRelPoint(1) - pixX;
|
|
FLOAT fZRatio = vRelPoint(3) - pixZ;
|
|
|
|
INDEX iPix = pixX + pixZ*pixWidth;
|
|
BOOL bFacing = (iPix)&1;
|
|
|
|
FLOAT3D vx0 = FLOAT3D((pixX+0)*tr_vStretch(1),tr_auwHeightMap[iPix] * tr_vStretch(2) ,(pixZ+0)*tr_vStretch(3));
|
|
FLOAT3D vx1 = FLOAT3D((pixX+1)*tr_vStretch(1),tr_auwHeightMap[iPix+1] * tr_vStretch(2) ,(pixZ+0)*tr_vStretch(3));
|
|
FLOAT3D vx2 = FLOAT3D((pixX+0)*tr_vStretch(1),tr_auwHeightMap[iPix+pixWidth] * tr_vStretch(2) ,(pixZ+1)*tr_vStretch(3));
|
|
FLOAT3D vx3 = FLOAT3D((pixX+1)*tr_vStretch(1),tr_auwHeightMap[iPix+pixWidth+1]* tr_vStretch(2) ,(pixZ+1)*tr_vStretch(3));
|
|
|
|
vx0 = vx0 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector;
|
|
vx1 = vx1 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector;
|
|
vx2 = vx2 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector;
|
|
vx3 = vx3 * tr_penEntity->en_mRotation + tr_penEntity->en_plPlacement.pl_PositionVector;
|
|
|
|
if(bFacing) {
|
|
if(fXRatio>=fZRatio) {
|
|
return FLOATplane3D(vx0,vx2,vx1);
|
|
} else {
|
|
return FLOATplane3D(vx1,vx2,vx3);
|
|
}
|
|
} else {
|
|
if(fXRatio>=fZRatio) {
|
|
return FLOATplane3D(vx2,vx3,vx0);
|
|
} else {
|
|
return FLOATplane3D(vx0,vx3,vx1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets number of quads in row of one tile
|
|
void CTerrain::SetQuadsPerTileRow(INDEX ctQuadsPerTileRow)
|
|
{
|
|
tr_ctQuadsInTileRow = Clamp(ctQuadsPerTileRow,(INDEX)4,(INDEX)(tr_pixHeightMapWidth-1));
|
|
if(tr_ctQuadsInTileRow!=ctQuadsPerTileRow) {
|
|
CPrintF("Warning: Quads per tile has been changed from requested %d to %d\n",ctQuadsPerTileRow,tr_ctQuadsInTileRow);
|
|
}
|
|
// TODO: Assert that it is 2^n
|
|
tr_ctVerticesInTileRow = tr_ctQuadsInTileRow+1;
|
|
}
|
|
|
|
// Set Terrain stretch
|
|
void CTerrain::SetTerrainStretch(FLOAT3D vStretch)
|
|
{
|
|
tr_vStretch = vStretch;
|
|
}
|
|
|
|
// Build terrain data
|
|
void CTerrain::BuildTerrainData()
|
|
{
|
|
// Allocate space for terrain tiles
|
|
tr_ctTilesX = (tr_pixHeightMapWidth-1) / tr_ctQuadsInTileRow;
|
|
tr_ctTilesY = (tr_pixHeightMapHeight-1) / tr_ctQuadsInTileRow;
|
|
tr_ctTiles = tr_ctTilesX*tr_ctTilesY;
|
|
tr_attTiles.New(tr_ctTiles);
|
|
|
|
// Calculate max posible lod
|
|
INDEX ctVtxInLod = tr_ctQuadsInTileRow;
|
|
tr_iMaxTileLod = 0;
|
|
while(ctVtxInLod>2) {
|
|
tr_iMaxTileLod++;
|
|
ctVtxInLod = ctVtxInLod>>1;
|
|
}
|
|
|
|
// Allocate memory for terrain tile arrays
|
|
tr_aArrayHolders.New(tr_iMaxTileLod+1);
|
|
INDEX ctah = tr_aArrayHolders.Count();
|
|
// for each array handler
|
|
for(INDEX iah=0;iah<ctah;iah++) {
|
|
CArrayHolder &ah = tr_aArrayHolders[iah];
|
|
// set its lod index
|
|
ah.ah_iLod = iah;
|
|
ah.ah_ptrTerrain = this;
|
|
}
|
|
|
|
// for each tile row
|
|
for(INDEX iy=0;iy<tr_ctTilesY;iy++) {
|
|
// for each tile col
|
|
for(INDEX ix=0;ix<tr_ctTilesX;ix++) {
|
|
// Initialize terrain tile
|
|
UINT iTileIndex = ix+iy*tr_ctTilesX;
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
tt.tt_iIndex = iTileIndex;
|
|
tt.tt_iOffsetX = ix;
|
|
tt.tt_iOffsetZ = iy;
|
|
tt.tt_ctVtxX = tr_ctVerticesInTileRow;
|
|
tt.tt_ctVtxY = tr_ctVerticesInTileRow;
|
|
tt.tt_ctLodVtxX = tt.tt_ctVtxX;
|
|
tt.tt_ctLodVtxY = tt.tt_ctVtxY;
|
|
tt.tt_iLod = 0;
|
|
tt.tt_fLodLerpFactor = 0;
|
|
// Reset tile neighbours
|
|
tt.tt_aiNeighbours[NB_TOP] =-1; tt.tt_aiNeighbours[NB_LEFT] =-1;
|
|
tt.tt_aiNeighbours[NB_BOTTOM]=-1; tt.tt_aiNeighbours[NB_RIGHT] =-1;
|
|
// Set tile neighbours
|
|
if(iy>0) tt.tt_aiNeighbours[NB_TOP] = iTileIndex-tr_ctTilesX;
|
|
if(ix>0) tt.tt_aiNeighbours[NB_LEFT] = iTileIndex-1;
|
|
if(iy<tr_ctTilesX-1) tt.tt_aiNeighbours[NB_BOTTOM] = iTileIndex+tr_ctTilesX;
|
|
if(ix<tr_ctTilesY-1) tt.tt_aiNeighbours[NB_RIGHT] = iTileIndex+1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
#if 1
|
|
void CTerrain::GenerateTopMap(INDEX iTileIndex)
|
|
{
|
|
FIX16_16 fiMaskDiv = 1;
|
|
INDEX iFirstInMask = 0;
|
|
INDEX iMaskWidth = tr_pixHeightMapWidth;
|
|
INDEX iTiling = (INDEX)(tr_ctTilesX*tr_ctQuadsInTileRow*tr_fTexStretch);
|
|
|
|
// destionation texture (must have set allocated memory)
|
|
CTextureData *ptdDest;
|
|
// if global top map
|
|
if(iTileIndex==(-1)) {
|
|
ptdDest = &tr_tdTopMap;
|
|
// else tile top map
|
|
} else {
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
ptdDest = tt.GetTopMap();
|
|
fiMaskDiv = tr_ctTilesX;
|
|
iFirstInMask = iMaskWidth * tt.tt_iOffsetZ * (tr_ctVerticesInTileRow-1) + (tt.tt_iOffsetX * (tr_ctVerticesInTileRow-1));
|
|
iTiling = (INDEX)(tr_ctQuadsInTileRow*tr_fTexStretch);
|
|
}
|
|
|
|
ASSERT(ptdDest->GetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight());
|
|
INDEX iSrcMipWidth = ClampDn(ptdDest->GetWidth()/iTiling,(INDEX)1);
|
|
|
|
|
|
INDEX ctLayers = tr_atlLayers.Count();
|
|
// for each layer
|
|
for(INDEX itl=0;itl<ctLayers;itl++) {
|
|
CTerrainLayer &tl = tr_atlLayers[itl];
|
|
// get source texture
|
|
CTextureData *ptdSrc = tl.tl_ptdTexture;
|
|
// Get mipmap of source texture
|
|
INDEX imm = FastLog2(ptdSrc->GetPixWidth()/iSrcMipWidth);
|
|
// get address of first byte in source mipmap
|
|
INDEX iMipAdr = GetMipmapOffset(imm,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight());
|
|
|
|
// Mask thing
|
|
// get first byte in layer mask
|
|
UBYTE *ubFirstInMask = &tl.tl_aubColors[iFirstInMask];
|
|
FIX16_16 fiHMaskStep = FIX16_16(iMaskWidth-1)/FIX16_16(ptdDest->GetWidth()-1)/fiMaskDiv;
|
|
FIX16_16 fiVMaskStep = FIX16_16(iMaskWidth-1)/FIX16_16(ptdDest->GetWidth()-1)/fiMaskDiv;
|
|
|
|
SLONG xHMaskStep = fiHMaskStep.slHolder;
|
|
SLONG xVMaskStep = fiVMaskStep.slHolder;
|
|
SLONG xMaskVPos=0;
|
|
|
|
// get first byte in destination texture
|
|
ULONG *pulTexDst = (ULONG*)&ptdDest->td_pulFrames[0];
|
|
// get first byte in source texture
|
|
ULONG *pulFirstInMipSrc = (ULONG*)&ptdSrc->td_pulFrames[iMipAdr];
|
|
|
|
// for each row
|
|
for(UINT ir=0;ir<ptdDest->GetHeight();ir++) {
|
|
// get first byte for src mip texture in this row
|
|
ULONG *pulSrcRow = &pulFirstInMipSrc[(ir&(iSrcMipWidth-1))*iSrcMipWidth];//%
|
|
INDEX iMaskVPos = (INDEX)(xMaskVPos>>16) * (iMaskWidth);
|
|
UBYTE *pubMaskRow = &ubFirstInMask[iMaskVPos];
|
|
SLONG xMaskHPos = 0;
|
|
// for each column
|
|
for(UINT ic=0;ic<ptdDest->GetWidth();ic++) {
|
|
|
|
ULONG *ulSrc = &pulSrcRow[ic&(iSrcMipWidth-1)];
|
|
INDEX iMask = (INDEX)(xMaskHPos>>16);
|
|
|
|
SLONG x1 = (SLONG)(pubMaskRow[iMask+0]) <<0; //NormByteToFixInt(pubMaskRow[iMask]);
|
|
SLONG x2 = (SLONG)(pubMaskRow[iMask+1]) <<0; //NormByteToFixInt(pubMaskRow[iMask+1]);
|
|
SLONG x3 = (SLONG)(pubMaskRow[iMask+iMaskWidth+0]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+0]);
|
|
SLONG x4 = (SLONG)(pubMaskRow[iMask+iMaskWidth+1]) <<0;//NormByteToFixInt(pubMaskRow[iMask+iMaskWidth+1]);
|
|
SLONG xFactH = xMaskHPos - (xMaskHPos&0xFFFF0000);
|
|
SLONG xFactV = xMaskVPos - (xMaskVPos&0xFFFF0000);
|
|
|
|
SLONG xStrengthX1 = (x1<<7) + (SLONG)(((x2-x1)*xFactH)>>9); //Lerp(fi1,fi2,fiFactH);
|
|
SLONG xStrengthX2 = (x3<<7) + (SLONG)(((x4-x3)*xFactH)>>9); //Lerp(fi3,fi4,fiFactH);
|
|
SLONG xStrength = (xStrengthX1<<1) + (SLONG)((((xStrengthX2>>0)-(xStrengthX1>>0))*xFactV)>>15); //Lerp(fiStrengthX1,fiStrengthX2,fiFactV);
|
|
|
|
GFXColor *pcolSrc = (GFXColor*)pulTexDst;
|
|
GFXColor *pcolDst = (GFXColor*)ulSrc;
|
|
pcolSrc->r = (BYTE)( (ULONG)pcolSrc->r + ((((ULONG)pcolDst->r - (ULONG)pcolSrc->r) * xStrength)>>16));
|
|
pcolSrc->g = (BYTE)( (ULONG)pcolSrc->g + ((((ULONG)pcolDst->g - (ULONG)pcolSrc->g) * xStrength)>>16));
|
|
pcolSrc->b = (BYTE)( (ULONG)pcolSrc->b + ((((ULONG)pcolDst->b - (ULONG)pcolSrc->b) * xStrength)>>16));
|
|
pcolSrc->a = 255;
|
|
|
|
pulTexDst++;
|
|
xMaskHPos += xHMaskStep;
|
|
}
|
|
xMaskVPos += xVMaskStep;
|
|
}
|
|
}
|
|
// make mipmaps
|
|
MakeMipmaps(32, ptdDest->td_pulFrames, ptdDest->GetWidth(), ptdDest->GetHeight());
|
|
// force topmap upload
|
|
ptdDest->SetAsCurrent(0,TRUE);
|
|
}
|
|
#else
|
|
void CTerrain::GenerateTopMap(INDEX iTileIndex)
|
|
{
|
|
INDEX iMaskDiv = 1;
|
|
INDEX iFirstInMask = 0;
|
|
INDEX iMaskWidth = tr_pixHeightMapWidth;
|
|
INDEX iTiling = (INDEX)(tr_ctTilesX*tr_ctQuadsInTileRow*tr_fTexStretch);
|
|
|
|
// destionation texture (must have set required width and height)
|
|
CTextureData *ptdDest;
|
|
// if global top map
|
|
if(iTileIndex==(-1)) {
|
|
ptdDest = &tr_tdTopMap;
|
|
// else tile top map
|
|
} else {
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
ptdDest = &tt.GetTopMap();
|
|
iMaskDiv = tr_ctTilesX;
|
|
iFirstInMask = iMaskWidth * tt.tt_iOffsetZ * (tr_ctVerticesInTileRow-1) + (tt.tt_iOffsetX * (tr_ctVerticesInTileRow-1));
|
|
iTiling = (INDEX)(tr_ctQuadsInTileRow*tr_fTexStretch);
|
|
}
|
|
|
|
ASSERT(ptdDest->GetPixWidth()>0 && ptdDest->GetPixHeight()>0 && ptdDest->GetPixWidth()==ptdDest->GetPixHeight());
|
|
|
|
INDEX iSrcMipWidth = ptdDest->GetWidth()/iTiling;
|
|
|
|
INDEX ctLayers = tr_atlLayers.Count();
|
|
// for each layer
|
|
for(INDEX ilr=0;ilr<ctLayers;ilr++) {
|
|
CTerrainLayer &tl = tr_atlLayers[ilr];
|
|
// get source texture
|
|
CTextureData *ptdSrc = tl.tl_ptdTexture;
|
|
// Get mipmap of source texture
|
|
INDEX imm = FastLog2(ptdSrc->GetPixWidth()/iSrcMipWidth);
|
|
// get address of first byte in source mipmap
|
|
INDEX iMipAdr = GetMipmapOffset(imm,ptdSrc->GetPixWidth(),ptdSrc->GetPixHeight());
|
|
|
|
// Mask thing
|
|
// get first byte in layer mask
|
|
UBYTE *ubFirstInMask = &tl.tl_aubColors[iFirstInMask];
|
|
FLOAT fHMaskStep = FLOAT(iMaskWidth-1)/(ptdDest->GetWidth()-1)/iMaskDiv;
|
|
FLOAT fVMaskStep = FLOAT(iMaskWidth-1)/(ptdDest->GetWidth()-1)/iMaskDiv;
|
|
FLOAT fMaskVPos=0;
|
|
|
|
// get first byte in destination texture
|
|
ULONG *pulTexDst = (ULONG*)&ptdDest->td_pulFrames[0];
|
|
// get first byte in source texture
|
|
ULONG *pulFirstInMipSrc = &ptdSrc->td_pulFrames[iMipAdr];
|
|
|
|
// for each row
|
|
for(UINT ir=0;ir<ptdDest->GetWidth();ir++) {
|
|
// get first byte for src mip texture in this row
|
|
ULONG *pulSrcRow = &pulFirstInMipSrc[(ir&(iSrcMipWidth-1))*iSrcMipWidth];//%
|
|
INDEX iMaskVPos = (INDEX)fMaskVPos * (iMaskWidth);
|
|
UBYTE *pubMaskRow = &ubFirstInMask[iMaskVPos];
|
|
FLOAT fMaskHPos = 0;
|
|
// for each column
|
|
for(UINT ic=0;ic<ptdDest->GetWidth();ic++) {
|
|
|
|
ULONG *ulSrc = &pulSrcRow[ic&(iSrcMipWidth-1)];
|
|
INDEX iMask = (INDEX)fMaskHPos;
|
|
FLOAT f1 = NormByteToFloat(pubMaskRow[iMask]);
|
|
FLOAT f2 = NormByteToFloat(pubMaskRow[iMask+1]);
|
|
FLOAT f3 = NormByteToFloat(pubMaskRow[iMask+iMaskWidth+0]);
|
|
FLOAT f4 = NormByteToFloat(pubMaskRow[iMask+iMaskWidth+1]);
|
|
FLOAT fStrengthX1 = Lerp(f1,f2,fMaskHPos-(INDEX)fMaskHPos);
|
|
FLOAT fStrengthX2 = Lerp(f3,f4,fMaskHPos-(INDEX)fMaskHPos);
|
|
FLOAT fStrength = Lerp(fStrengthX1,fStrengthX2,fMaskVPos-(INDEX)fMaskVPos);
|
|
|
|
CopyPixel(pulTexDst,ulSrc,fStrength);
|
|
pulTexDst++;
|
|
fMaskHPos+=fHMaskStep;
|
|
}
|
|
fMaskVPos+=fVMaskStep;
|
|
}
|
|
}
|
|
// make mipmaps
|
|
MakeMipmaps( 32, ptdDest->td_pulFrames, ptdDest->GetWidth(), ptdDest->GetHeight());
|
|
// force topmap upload
|
|
ptdDest->SetAsCurrent(0,TRUE);
|
|
}
|
|
#endif
|
|
*/
|
|
|
|
void CTerrain::GenerateTerrainTopMap()
|
|
{
|
|
CreateTopMap(tr_tdTopMap,tr_pixTopMapWidth,tr_pixTopMapHeight);
|
|
UpdateTopMap(-1);
|
|
}
|
|
|
|
// Add default layer
|
|
void CTerrain::AddDefaultLayer_t(void)
|
|
{
|
|
// Add one layer using default texture, but do not refresh terrain
|
|
CTerrainLayer &tl = AddLayer_t((CTString)"Textures\\Editor\\Default.TEX", LT_NORMAL, FALSE);
|
|
// fill this layer
|
|
tl.ResetLayerMask(255);
|
|
}
|
|
|
|
// Build quadtree for terrain
|
|
void CTerrain::BuildQuadTree(void)
|
|
{
|
|
INDEX ctQuadNodeRows = tr_ctTilesX;
|
|
INDEX ctQuadNodeCols = tr_ctTilesY;
|
|
INDEX ctQuadNodes = 0;
|
|
|
|
// Create quad tree levels
|
|
while(TRUE) {
|
|
QuadTreeLevel &qtl = tr_aqtlQuadTreeLevels.Push();
|
|
// Remember first node
|
|
qtl.qtl_iFirstNode = ctQuadNodes;
|
|
// Add nodes in this level to total node count
|
|
ctQuadNodes += ClampDn(ctQuadNodeRows,(INDEX)1) * ClampDn(ctQuadNodeCols,(INDEX)1);
|
|
|
|
// Count nodes in this level
|
|
qtl.qtl_ctNodes = ctQuadNodes - qtl.qtl_iFirstNode;
|
|
qtl.qtl_ctNodesCol = ctQuadNodeCols;
|
|
qtl.qtl_ctNodesRow = ctQuadNodeRows;
|
|
|
|
// if only one node is in this level
|
|
if(qtl.qtl_ctNodes == 1) {
|
|
// this is last level so exit loop
|
|
break;
|
|
}
|
|
if(ctQuadNodeCols%2 == 1 && ctQuadNodeCols != 1) {
|
|
ctQuadNodeCols = (ctQuadNodeCols+1)>>1;
|
|
} else {
|
|
ctQuadNodeCols = ctQuadNodeCols>>1;
|
|
}
|
|
|
|
if(ctQuadNodeRows%2 == 1 && ctQuadNodeRows != 1) {
|
|
ctQuadNodeRows = (ctQuadNodeRows+1)>>1;
|
|
} else {
|
|
ctQuadNodeRows = ctQuadNodeRows>>1;
|
|
}
|
|
}
|
|
|
|
QuadTreeLevel &qtlFirst = tr_aqtlQuadTreeLevels[0];
|
|
// Add quadtree nodes for first level
|
|
tr_aqtnQuadTreeNodes.Push(qtlFirst.qtl_ctNodes);
|
|
// for each quad tree node in first level
|
|
for(INDEX iqn=0;iqn<qtlFirst.qtl_ctNodes;iqn++) {
|
|
// Generate vertices for tile in first lod with no vertex lerping
|
|
CTerrainTile &tt = tr_attTiles[iqn];
|
|
QuadTreeNode &qtn = tr_aqtnQuadTreeNodes[iqn];
|
|
tt.tt_iLod = -1;
|
|
tt.tt_iRequestedLod = 0;
|
|
tt.tt_fLodLerpFactor = 0;
|
|
tt.AddFlag(TT_REGENERATE|TT_NO_TOPMAP_REGEN);
|
|
ReGenerateTile(iqn);
|
|
tt.RemoveFlag(TT_REGENERATE|TT_NO_TOPMAP_REGEN);
|
|
|
|
// Set quad tree bbox as first vertex in tile
|
|
GFXVertex4 &vtxFirst = tt.GetVertices()[0];
|
|
qtn.qtn_aabbox = FLOATaabbox3D(FLOAT3D(vtxFirst.x,vtxFirst.y,vtxFirst.z));
|
|
// for each vertex after first
|
|
INDEX ctVtx = tt.GetVertices().Count();
|
|
for(INDEX ivx=1;ivx<ctVtx;ivx++) {
|
|
// Add vertex in quad tree node bbox
|
|
GFXVertex4 &vtx = tt.GetVertices()[ivx];
|
|
qtn.qtn_aabbox |= FLOATaabbox3D(FLOAT3D(vtx.x,vtx.y,vtx.z));
|
|
}
|
|
// release arrays of tile
|
|
tt.ReleaseTileArrays();
|
|
tt.tt_iLod = -1;
|
|
tt.tt_iRequestedLod = 0;
|
|
qtn.qtn_iTileIndex = iqn;
|
|
// nodes in first level does not have children
|
|
qtn.qtn_iChild[0] = -1;
|
|
qtn.qtn_iChild[1] = -1;
|
|
qtn.qtn_iChild[2] = -1;
|
|
qtn.qtn_iChild[3] = -1;
|
|
}
|
|
|
|
// Create all other levels of quad tree
|
|
INDEX ctQuadLevels = tr_aqtlQuadTreeLevels.Count();
|
|
// for each quadtree level after first
|
|
for(INDEX iql=1;iql<ctQuadLevels;iql++) {
|
|
QuadTreeLevel &qtl = tr_aqtlQuadTreeLevels[iql];
|
|
QuadTreeLevel &qtlPrev = tr_aqtlQuadTreeLevels[iql-1];
|
|
// for each quadtree node row
|
|
for(INDEX ir=0;ir<qtlPrev.qtl_ctNodesRow;ir+=2) {
|
|
// for each quadtree node col
|
|
for(INDEX ic=0;ic<qtlPrev.qtl_ctNodesCol;ic+=2) {
|
|
// Set quadtree node children
|
|
INDEX iqt = qtlPrev.qtl_iFirstNode + ic+ir*qtlPrev.qtl_ctNodesCol;
|
|
QuadTreeNode &qtn = tr_aqtnQuadTreeNodes.Push();
|
|
QuadTreeNode *pqtnFirst = &tr_aqtnQuadTreeNodes[iqt];
|
|
// Set first child node
|
|
qtn.qtn_aabbox = pqtnFirst->qtn_aabbox;
|
|
qtn.qtn_iChild[0] = iqt;
|
|
qtn.qtn_iChild[1] = -1;
|
|
qtn.qtn_iChild[2] = -1;
|
|
qtn.qtn_iChild[3] = -1;
|
|
qtn.qtn_iTileIndex = -1;
|
|
// If second child node exists
|
|
if(ic+1<qtlPrev.qtl_ctNodesCol) {
|
|
// Set second child
|
|
qtn.qtn_iChild[1] = iqt+1;
|
|
qtn.qtn_aabbox |= pqtnFirst[1].qtn_aabbox;
|
|
}
|
|
// if fourth child exist
|
|
if(ir+1<qtlPrev.qtl_ctNodesRow) {
|
|
// Set third child
|
|
qtn.qtn_iChild[2] = iqt+qtlPrev.qtl_ctNodesCol;
|
|
qtn.qtn_aabbox |= pqtnFirst[qtlPrev.qtl_ctNodesCol].qtn_aabbox;
|
|
// Set fourth child
|
|
if(ic+1<qtlPrev.qtl_ctNodesCol) {
|
|
qtn.qtn_iChild[3] = iqt+qtlPrev.qtl_ctNodesCol+1;
|
|
qtn.qtn_aabbox |= pqtnFirst[qtlPrev.qtl_ctNodesCol+1].qtn_aabbox;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Update higher quadtree levels from first one
|
|
void CTerrain::UpdateQuadTree()
|
|
{
|
|
// for all quad tree levels after first one
|
|
INDEX ctqtl=tr_aqtlQuadTreeLevels.Count();
|
|
for(INDEX iqtl=1;iqtl<ctqtl;iqtl++) {
|
|
QuadTreeLevel &qtl = tr_aqtlQuadTreeLevels[iqtl];
|
|
|
|
// for each quad tree node in this level
|
|
INDEX ctqtn=qtl.qtl_iFirstNode+qtl.qtl_ctNodes;
|
|
for(INDEX iqtn=qtl.qtl_iFirstNode;iqtn<ctqtn;iqtn++) {
|
|
QuadTreeNode &qtn = tr_aqtnQuadTreeNodes[iqtn];
|
|
// trash qtn box
|
|
qtn.qtn_aabbox.maxvect = FLOAT3D(-1,-1,-1);
|
|
qtn.qtn_aabbox.minvect = FLOAT3D( 1, 1, 1);
|
|
|
|
// for each child of qtn
|
|
for(INDEX ichild=0;ichild<4;ichild++) {
|
|
INDEX iqtnChild = qtn.qtn_iChild[ichild];
|
|
// if child exists
|
|
if(iqtnChild!=(-1)) {
|
|
QuadTreeNode &qtnChild = tr_aqtnQuadTreeNodes[iqtnChild];
|
|
// if qtn box is empty
|
|
if(qtn.qtn_aabbox.IsEmpty()) {
|
|
// set qtn box as box of this child
|
|
qtn.qtn_aabbox = qtnChild.qtn_aabbox;
|
|
// if it had some data
|
|
} else {
|
|
// just add child box to qtn box
|
|
qtn.qtn_aabbox |= qtnChild.qtn_aabbox;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Generation
|
|
*/
|
|
|
|
void CTerrain::ReGenerate(void)
|
|
{
|
|
// for each tile in terrain
|
|
for(INDEX it=0;it<tr_ctTiles;it++) {
|
|
CTerrainTile &tt = tr_attTiles[it];
|
|
// calculate new lod
|
|
tt.tt_iRequestedLod = tt.CalculateLOD();
|
|
}
|
|
|
|
// for each tile that is waiting in regen queue
|
|
INDEX ctrt = tr_auiRegenList.Count();
|
|
INDEX irt;
|
|
for(irt=0;irt<ctrt;irt++) {
|
|
// mark tile as ready for regeneration
|
|
INDEX iTileIndex = tr_auiRegenList[irt];
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
tt.AddFlag(TT_REGENERATE);
|
|
}
|
|
|
|
// for each tile that is waiting in regen queue
|
|
for(irt=0;irt<ctrt;irt++) {
|
|
INDEX iTileIndex = tr_auiRegenList[irt];
|
|
CTerrainTile &tt = tr_attTiles[iTileIndex];
|
|
// if tile needs to be regenerated
|
|
if(tt.GetFlags() & TT_REGENERATE) {
|
|
// Regenerate it now
|
|
ReGenerateTile(tt.tt_iIndex);
|
|
// remove flag for regeneration
|
|
tt.RemoveFlag(TT_REGENERATE);
|
|
}
|
|
}
|
|
|
|
// clear regenration list
|
|
ClearRegenList();
|
|
}
|
|
|
|
extern CStaticStackArray<GFXVertex4> _avLerpedVerices;
|
|
static void ShowTerrainInfo(CAnyProjection3D &apr, CDrawPort *pdp, CTerrain *ptrTerrain)
|
|
{
|
|
pdp->SetFont( _pfdConsoleFont);
|
|
pdp->SetTextAspect( 1.0f);
|
|
pdp->SetOrtho();
|
|
CTString strInfo;
|
|
INDEX ctTopMaps = ptrTerrain->tr_atdTopMaps.Count() + 1;
|
|
strInfo.PrintF("Tris = %d\nNodes = %d\nDelayed nodes = %d\nTop maps = %d\nTexgens = %d, %d\nShadowmap updates = %d\n",
|
|
_ctTris,_ctNodesVis,_ctDelayedNodes,ctTopMaps,ctGeneratedTopMaps,ctGlobalTopMaps,_ctShadowMapUpdates);
|
|
|
|
CStaticStackArray<INDEX> iaLodInfo;
|
|
iaLodInfo.Push(ptrTerrain->tr_iMaxTileLod+1);
|
|
memset(&iaLodInfo[0],0,sizeof(INDEX)*iaLodInfo.sa_Count);
|
|
// build lod info
|
|
for(INDEX it=0;it<ptrTerrain->tr_ctTiles;it++) {
|
|
CTerrainTile &tt = ptrTerrain->tr_attTiles[it];
|
|
INDEX &ili = iaLodInfo[tt.tt_iLod];
|
|
ili++;
|
|
}
|
|
// Show how many tiles are in witch lod
|
|
CTString strTemp = "LodInfo:\n";
|
|
for(INDEX itti=0;itti<ptrTerrain->tr_iMaxTileLod+1;itti++) {
|
|
CTString str;
|
|
CArrayHolder &ah = ptrTerrain->tr_aArrayHolders[itti];
|
|
str.PrintF("L%d = mem = %d KB, nodes = %d\n",itti, ah.GetUsedMemory()/1024, iaLodInfo[itti]);
|
|
strTemp += str;
|
|
}
|
|
strTemp += "\n";
|
|
strInfo +=strTemp;
|
|
|
|
// Show memory usage
|
|
SLONG slUsedMemory=0;
|
|
// Height map usage
|
|
SLONG slHeightMap = ptrTerrain->tr_pixHeightMapWidth*ptrTerrain->tr_pixHeightMapHeight*sizeof(UWORD);
|
|
// Edge map usage
|
|
SLONG slEdgeMap = ptrTerrain->tr_pixHeightMapWidth*ptrTerrain->tr_pixHeightMapHeight*sizeof(UBYTE);
|
|
// Shadow map usage
|
|
SLONG slShadowMap = ptrTerrain->tr_tdShadowMap.GetUsedMemory();
|
|
// Quad tree usage
|
|
SLONG slQTNodes = sizeof(QuadTreeNode)*ptrTerrain->tr_aqtnQuadTreeNodes.Count();
|
|
SLONG slQTLevels = sizeof(QuadTreeLevel)*ptrTerrain->tr_aqtlQuadTreeLevels.Count();
|
|
// Tiles usage
|
|
SLONG slTiles = 0;
|
|
INDEX cttt = ptrTerrain->tr_ctTiles;
|
|
for(INDEX itt=0;itt<cttt;itt++) {
|
|
CTerrainTile &tt = ptrTerrain->tr_attTiles[itt];
|
|
slTiles+=tt.GetUsedMemory();
|
|
}
|
|
// Arrays holders usage
|
|
SLONG slArrayHoldes=0;
|
|
INDEX ctah=ptrTerrain->tr_aArrayHolders.Count();
|
|
for(INDEX iah=0;iah<ctah;iah++) {
|
|
CArrayHolder &ah = ptrTerrain->tr_aArrayHolders[iah];
|
|
slArrayHoldes+=ah.GetUsedMemory();
|
|
}
|
|
SLONG slLayers=0;
|
|
// Terrain layers usage
|
|
INDEX cttl = ptrTerrain->tr_atlLayers.Count();
|
|
for(INDEX itl=0;itl<cttl;itl++) {
|
|
CTerrainLayer &tl = ptrTerrain->tr_atlLayers[itl];
|
|
slLayers+=tl.GetUsedMemory();
|
|
}
|
|
SLONG slTopMaps=0;
|
|
// Top maps usage
|
|
INDEX cttm=ptrTerrain->tr_atdTopMaps.Count();
|
|
for(INDEX itm=0;itm<cttm;itm++) {
|
|
CTextureData *ptdTopMap = &ptrTerrain->tr_atdTopMaps[itm];
|
|
slTopMaps+=ptdTopMap->GetUsedMemory();
|
|
}
|
|
SLONG slGlobalTopMap = ptrTerrain->tr_tdTopMap.GetUsedMemory();
|
|
SLONG slTileBatchingSize = GetUsedMemoryForTileBatching();
|
|
SLONG slVertexSmoothing = _avLerpedVerices.sa_Count * sizeof(GFXVertex4);
|
|
extern SLONG _slSharedTopMapSize; // Shared top map size
|
|
// Global top map usage
|
|
SLONG slTotal = slHeightMap+slEdgeMap+slShadowMap+slQTNodes+slQTLevels+slTiles+slArrayHoldes+slLayers+
|
|
slTopMaps+slGlobalTopMap+slTileBatchingSize+slVertexSmoothing;
|
|
CTString strMemoryUsed;
|
|
strMemoryUsed.PrintF("Heightmap = %d KB\nEdgemap = %d KB\nShadowMap = %d KB\nQuadTree = %d KB\nTiles = %d KB\nArrays = %d KB\nLayers = %d KB\nTopMaps = %d KB\nGlobal TM = %d KB\nShared TM = %d KB\nVtx lerp = %d KB\nBatching = %d KB\nTotal = %d KB\n",
|
|
slHeightMap/1024,slEdgeMap/1024,slShadowMap/1024,(slQTNodes+slQTLevels)/1024,slTiles/1024,slArrayHoldes/1024,slLayers/1024,slTopMaps/1024,slGlobalTopMap/1024,_slSharedTopMapSize/1024,slVertexSmoothing/1024,slTileBatchingSize/1024,slTotal/1024);
|
|
|
|
|
|
strInfo += strMemoryUsed;
|
|
|
|
extern FLOAT3D _vDirection;
|
|
strInfo += CTString(0,"Shadow map size = %d,%d [%d]\nShading map size= %d,%d [%d]\n",
|
|
ptrTerrain->GetShadowMapWidth(), ptrTerrain->GetShadowMapHeight(),
|
|
ptrTerrain->tr_iShadowMapSizeAspect,ptrTerrain->GetShadingMapWidth(),
|
|
ptrTerrain->GetShadingMapHeight(),ptrTerrain->tr_iShadingMapSizeAspect);
|
|
pdp->PutText(strInfo,0,40);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void ReadOldShadowMap(CTerrain *ptrTerrain, CTStream *istrFile)
|
|
{
|
|
// Read terrain shadow map
|
|
// Read shadow map size
|
|
INDEX pixShadowMapWidth;
|
|
INDEX pixShadowMapHeight;
|
|
(*istrFile)>>pixShadowMapWidth;
|
|
(*istrFile)>>pixShadowMapHeight;
|
|
BOOL bHaveShadowMap;
|
|
(*istrFile)>>bHaveShadowMap;
|
|
// is shadow map saved
|
|
if(bHaveShadowMap) {
|
|
// skip reading of first mip of shadow map
|
|
istrFile->Seek_t(pixShadowMapWidth*pixShadowMapHeight*sizeof(GFXColor),CTStream::SD_CUR);
|
|
//istrFile->Read_t(&tr_tdShadowMap.td_pulFrames[0],tr_pixShadowMapWidth*tr_pixShadowMapHeight*sizeof(GFXColor));
|
|
}
|
|
// Set default shadow map size
|
|
ptrTerrain->SetShadowMapsSize(0,0);
|
|
}
|
|
|
|
void CTerrain::ReadVersion_t( CTStream *istrFile, INDEX iSavedVersion)
|
|
{
|
|
// set current terrain
|
|
_ptrTerrain = this;
|
|
|
|
ASSERT(_CrtCheckMemory());
|
|
PIX pixWidth;
|
|
PIX pixHeight;
|
|
// read height map width and height
|
|
(*istrFile)>>pixWidth;
|
|
(*istrFile)>>pixHeight;
|
|
|
|
// Reallocate memory for terrain with size
|
|
AllocateHeightMap(pixWidth,pixHeight);
|
|
|
|
// read terrain stretch
|
|
(*istrFile)>>tr_vStretch;
|
|
// read texture stretch
|
|
if(iSavedVersion<6) {
|
|
FLOAT fTemp; // Read temp stretch
|
|
(*istrFile)>>fTemp;
|
|
}
|
|
// read lod distance factor
|
|
(*istrFile)>>tr_fDistFactor;
|
|
|
|
if(iSavedVersion>6) {
|
|
// Read terrain size
|
|
(*istrFile)>>tr_vTerrainSize;
|
|
|
|
istrFile->ExpectID_t("TRSM"); // 'Terrain shadowmap'
|
|
|
|
// if version is smaller than 8
|
|
if(iSavedVersion<8) {
|
|
// read old shadow map format
|
|
ReadOldShadowMap(this,istrFile);
|
|
} else {
|
|
INDEX iShadowMapAspect;
|
|
INDEX iShadingMapAspect;
|
|
(*istrFile)>>iShadowMapAspect;
|
|
(*istrFile)>>iShadingMapAspect;
|
|
SetShadowMapsSize(iShadowMapAspect,iShadingMapAspect);
|
|
INDEX iShadowMapSize = GetShadowMapWidth() * GetShadowMapHeight();
|
|
INDEX iShadingMapSize = GetShadingMapWidth() * GetShadingMapHeight();
|
|
// Read shadow map
|
|
ASSERT(tr_tdShadowMap.td_pulFrames!=NULL);
|
|
for (INDEX i = 0; i < iShadowMapSize; i++)
|
|
(*istrFile)>>tr_tdShadowMap.td_pulFrames[i];
|
|
// Read shading map
|
|
ASSERT(tr_auwShadingMap!=NULL);
|
|
for (INDEX i = 0; i < iShadingMapSize; i++)
|
|
(*istrFile)>>tr_auwShadingMap[i];
|
|
}
|
|
|
|
// Create shadow map mipmaps
|
|
INDEX ctMipMaps = GetNoOfMipmaps(GetShadowMapWidth(),GetShadowMapHeight());
|
|
MakeMipmaps(ctMipMaps, tr_tdShadowMap.td_pulFrames, GetShadowMapWidth(), GetShadowMapHeight());
|
|
// Upload shadow map
|
|
tr_tdShadowMap.SetAsCurrent(0,TRUE);
|
|
|
|
istrFile->ExpectID_t("TSEN"); // 'Terrain shadowmap end'
|
|
|
|
|
|
// if there is edge map saved
|
|
if(istrFile->PeekID_t()==CChunkID("TREM")) { // 'Terrain edge map'
|
|
// Read terrain edge map
|
|
istrFile->ExpectID_t("TREM"); // 'Terrain edge map'
|
|
// read edge map
|
|
istrFile->Read_t(&tr_aubEdgeMap[0],tr_pixHeightMapWidth*tr_pixHeightMapHeight);
|
|
istrFile->ExpectID_t("TEEN"); // 'Terrain edge map end'
|
|
}
|
|
}
|
|
|
|
(*istrFile).ExpectID_t("TRHM"); // 'Terrain heightmap'
|
|
|
|
// read height map
|
|
for (ULONG i = 0; i < tr_pixHeightMapWidth*tr_pixHeightMapHeight; i++)
|
|
(*istrFile)>>tr_auwHeightMap[i];
|
|
(*istrFile).ExpectID_t("THEN"); // 'Terrain heightmap end'
|
|
|
|
// Terrain will be rebuild in entity.cpp
|
|
_ptrTerrain = NULL;
|
|
}
|
|
|
|
// Read from stream.
|
|
void CTerrain::Read_t( CTStream *istrFile)
|
|
{
|
|
(*istrFile).ExpectID_t("TERR"); // 'Terrain'
|
|
// read the version number
|
|
INDEX iSavedVersion;
|
|
(*istrFile)>>iSavedVersion;
|
|
// is this version 6
|
|
if(iSavedVersion>4) {
|
|
ReadVersion_t(istrFile,iSavedVersion);
|
|
// else unknown version
|
|
} else {
|
|
// report error
|
|
ThrowF_t( TRANS("The terrain version on disk is %d.\n"
|
|
"Current supported version is %d."), iSavedVersion, _iTerrainVersion);
|
|
}
|
|
|
|
// Read Terrain layers
|
|
(*istrFile).ExpectID_t("TRLR"); // 'Terrain layers'
|
|
// Read terrain layers
|
|
INDEX cttl;
|
|
(*istrFile)>>cttl;
|
|
// Create layers
|
|
tr_atlLayers.Push(cttl);
|
|
// for each terrain layer
|
|
for(INDEX itl=0;itl<cttl;itl++) {
|
|
// Read layer texture and mask
|
|
CTerrainLayer &tl = tr_atlLayers[itl];
|
|
tl.Read_t(istrFile,iSavedVersion);
|
|
}
|
|
(*istrFile).ExpectID_t("TLEN"); // 'Terrain layers end'
|
|
|
|
// End of reading
|
|
(*istrFile).ExpectID_t("TREN"); // 'terrain end'
|
|
}
|
|
|
|
// Write to stream.
|
|
void CTerrain::Write_t( CTStream *ostrFile)
|
|
{
|
|
(*ostrFile).WriteID_t("TERR"); // 'Terrain'
|
|
|
|
// write the terrain version
|
|
(*ostrFile)<<_iTerrainVersion;
|
|
// write height map width and height
|
|
(*ostrFile)<<tr_pixHeightMapWidth;
|
|
(*ostrFile)<<tr_pixHeightMapHeight;
|
|
// write terrain stretch
|
|
(*ostrFile)<<tr_vStretch;
|
|
// write lod distance factor
|
|
(*ostrFile)<<tr_fDistFactor;
|
|
// Write terrain size
|
|
(*ostrFile)<<tr_vTerrainSize;
|
|
|
|
// Write terrain shadow map
|
|
ostrFile->WriteID_t("TRSM"); // 'Terrain shadowmap'
|
|
|
|
(*ostrFile)<<tr_iShadowMapSizeAspect;
|
|
(*ostrFile)<<tr_iShadingMapSizeAspect;
|
|
INDEX iShadowMapSize = GetShadowMapWidth() * GetShadowMapHeight() * sizeof(ULONG);
|
|
INDEX iShadingMapSize = GetShadingMapWidth() * GetShadingMapHeight() * sizeof(UWORD);
|
|
// Write shadow map
|
|
ASSERT(tr_tdShadowMap.td_pulFrames!=NULL);
|
|
ostrFile->Write_t(&tr_tdShadowMap.td_pulFrames[0],iShadowMapSize);
|
|
// Write shading map
|
|
ASSERT(tr_auwShadingMap!=NULL);
|
|
ostrFile->Write_t(&tr_auwShadingMap[0],iShadingMapSize);
|
|
|
|
ostrFile->WriteID_t("TSEN"); // 'Terrain shadowmap end'
|
|
|
|
// if edge map exists
|
|
if(tr_aubEdgeMap!=NULL) {
|
|
ostrFile->WriteID_t("TREM"); // 'Terrain edge map'
|
|
// Write edge map
|
|
ostrFile->Write_t(&tr_aubEdgeMap[0],sizeof(UBYTE)*tr_pixHeightMapWidth*tr_pixHeightMapHeight);
|
|
ostrFile->WriteID_t("TEEN"); // 'Terrain edge map end'
|
|
}
|
|
|
|
(*ostrFile).WriteID_t("TRHM"); // 'Terrain heightmap'
|
|
// write height map
|
|
(*ostrFile).Write_t(&tr_auwHeightMap[0],sizeof(UWORD)*tr_pixHeightMapWidth*tr_pixHeightMapHeight);
|
|
(*ostrFile).WriteID_t("THEN"); // 'Terrain heightmap end'
|
|
|
|
(*ostrFile).WriteID_t("TRLR"); // 'Terrain layers'
|
|
// write terrain layers
|
|
INDEX cttl = tr_atlLayers.Count();
|
|
(*ostrFile)<<cttl;
|
|
for(INDEX itl=0;itl<cttl;itl++) {
|
|
CTerrainLayer &tl = tr_atlLayers[itl];
|
|
tl.Write_t(ostrFile);
|
|
}
|
|
|
|
(*ostrFile).WriteID_t("TLEN"); // 'Terrain layers end'
|
|
|
|
(*ostrFile).WriteID_t("TREN"); // 'terrain end'
|
|
}
|
|
|
|
// Copy terrain data from other terrain
|
|
void CTerrain::Copy(CTerrain &trOther)
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
CTerrain::~CTerrain()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
// Discard all cached shading info for models
|
|
void CTerrain::DiscardShadingInfos(void)
|
|
{
|
|
FORDELETELIST( CShadingInfo, si_lnInPolygon, tr_lhShadingInfos, itsi) {
|
|
itsi->si_penEntity->en_ulFlags &= ~ENF_VALIDSHADINGINFO;
|
|
itsi->si_lnInPolygon.Remove();
|
|
itsi->si_pbpoPolygon = NULL;
|
|
}
|
|
}
|
|
|
|
// Clear height map
|
|
void CTerrain::ClearHeightMap(void)
|
|
{
|
|
// if height map space was allocated
|
|
if(tr_auwHeightMap!=NULL) {
|
|
// release it
|
|
FreeMemory(tr_auwHeightMap);
|
|
tr_auwHeightMap = NULL;
|
|
}
|
|
}
|
|
|
|
// Clear shadow map
|
|
void CTerrain::ClearShadowMap(void)
|
|
{
|
|
// Clear current terrain shadow map
|
|
tr_tdShadowMap.Clear();
|
|
|
|
// Also clear shading map
|
|
if(tr_auwShadingMap!=NULL) {
|
|
FreeMemory(tr_auwShadingMap);
|
|
tr_auwShadingMap = NULL;
|
|
}
|
|
}
|
|
|
|
void CTerrain::ClearEdgeMap(void)
|
|
{
|
|
// if space for edge map was allocated
|
|
if(tr_aubEdgeMap!=NULL) {
|
|
// release it
|
|
FreeMemory(tr_aubEdgeMap);
|
|
tr_aubEdgeMap = NULL;
|
|
}
|
|
}
|
|
|
|
// Clear all topmaps
|
|
void CTerrain::ClearTopMaps(void)
|
|
{
|
|
// for each topmap in terrain
|
|
INDEX cttm = tr_atdTopMaps.Count();
|
|
for(INDEX itm=0;itm<cttm;itm++) {
|
|
CTextureData *ptdTopMap = &tr_atdTopMaps[0];
|
|
// Remove memory pointer cos it is shared memory
|
|
ptdTopMap->td_pulFrames = NULL;
|
|
// Clear tile topmap
|
|
ptdTopMap->Clear();
|
|
// remove topmap from container
|
|
tr_atdTopMaps.Remove(ptdTopMap);
|
|
delete ptdTopMap;
|
|
ptdTopMap = NULL;
|
|
}
|
|
tr_atdTopMaps.Clear();
|
|
ASSERT(_CrtCheckMemory());
|
|
|
|
// Remove memory pointer from global top map cos it is shared memory
|
|
tr_tdTopMap.td_pulFrames = NULL;
|
|
// Clear global topmap
|
|
tr_tdTopMap.Clear();
|
|
}
|
|
|
|
// Clear tiles
|
|
void CTerrain::ClearTiles(void)
|
|
{
|
|
// for each tile
|
|
for(INDEX itt=0;itt<tr_ctTiles;itt++) {
|
|
CTerrainTile &tt = tr_attTiles[itt];
|
|
// Clear tile
|
|
tt.Clear();
|
|
}
|
|
tr_attTiles.Clear();
|
|
tr_ctTiles = 0;
|
|
tr_ctTilesX = 0;
|
|
tr_ctTilesY = 0;
|
|
}
|
|
|
|
// Clear arrays
|
|
void CTerrain::ClearArrays(void)
|
|
{
|
|
// Clear Array holders
|
|
tr_aArrayHolders.Clear();
|
|
}
|
|
|
|
// Clear quadtree
|
|
void CTerrain::ClearQuadTree(void)
|
|
{
|
|
// Destroy quad tree nodes
|
|
tr_aqtnQuadTreeNodes.Clear();
|
|
// Clear quad tree levels
|
|
tr_aqtlQuadTreeLevels.Clear();
|
|
}
|
|
|
|
// Clear layers
|
|
void CTerrain::ClearLayers(void)
|
|
{
|
|
// Clear layers
|
|
tr_atlLayers.Clear();
|
|
}
|
|
|
|
// Clean terrain data (does not remove layers)
|
|
void CTerrain::Clean(BOOL bCleanLayers/*=TRUE*/)
|
|
{
|
|
ASSERT(FALSE);
|
|
ClearHeightMap();
|
|
ClearShadowMap();
|
|
ClearEdgeMap();
|
|
ClearTopMaps();
|
|
ClearTiles();
|
|
ClearArrays();
|
|
ClearQuadTree();
|
|
|
|
// if layers clear is required
|
|
if(bCleanLayers) {
|
|
ClearLayers();
|
|
}
|
|
}
|
|
|
|
// Clear terrain data
|
|
void CTerrain::Clear(void)
|
|
{
|
|
DiscardShadingInfos();
|
|
ClearHeightMap();
|
|
ClearShadowMap();
|
|
ClearEdgeMap();
|
|
ClearTopMaps();
|
|
ClearTiles();
|
|
ClearArrays();
|
|
ClearQuadTree();
|
|
ClearLayers();
|
|
|
|
if(tr_ptdDetailMap!=NULL) {
|
|
_pTextureStock->Release(tr_ptdDetailMap);
|
|
tr_ptdDetailMap = NULL;
|
|
}
|
|
}
|
|
|
|
|