//*********************************************************** // // Name: Terrain.Cpp // Last Update: 23/2/02 // Author: Poya Manouchehri // // Description: CTerrain handles the terrain portion of the // engine. It holds open the file to the terrain // information, so terrain data can be loaded // dynamically. We use a ROAM method to render // the terrain, ie using binary triangle trees. // The terrain consists of smaller PATCHS, which // do most of the work. // //*********************************************************** #include "res/tex.h" #include "res/mem.h" #include #include "Terrain.h" CTerrain::CTerrain() { m_Heightmap = NULL; m_Patches = NULL; m_MapSize = 0; m_MapSizePatches = 0; } CTerrain::~CTerrain() { Reset(); } void CTerrain::Reset() { delete[] m_Heightmap; delete[] m_Patches; } bool CTerrain::Initialize(u32 size,const u16* data) { // clean up any previous terrain Reset(); // store terrain size m_MapSize=(size*PATCH_SIZE)+1; m_MapSizePatches=size; // allocate data for new terrain m_Heightmap=new u16[m_MapSize*m_MapSize]; m_Patches=new CPatch[m_MapSizePatches*m_MapSizePatches]; // given a heightmap? if (data) { // yes; keep a copy of it memcpy(m_Heightmap,data,m_MapSize*m_MapSize*sizeof(u16)); } else { // build a flat terrain memset(m_Heightmap,0,m_MapSize*m_MapSize*sizeof(u16)); } // setup patch parents, indices etc InitialisePatches(); return true; } void CTerrain::CalcPosition(u32 i,u32 j,CVector3D& pos) { u16 height=m_Heightmap[j*m_MapSize + i]; pos.X = float(i)*CELL_SIZE; pos.Y = float(height)*HEIGHT_SCALE; pos.Z = float(j)*CELL_SIZE; } void CTerrain::CalcNormal(u32 i,u32 j,CVector3D& normal) { CVector3D left, right, up, down; left.Clear(); right.Clear(); up.Clear(); down.Clear(); // get position of vertex where normal is being evaluated CVector3D basepos; CalcPosition(i,j,basepos); CVector3D tmp; if (i>0) { CalcPosition(i-1,j,tmp); left=tmp-basepos; } if (i0) { CalcPosition(i,j-1,tmp); up=tmp-basepos; } if (j0.00001f) normal*=1.0f/nlen; } CPatch* CTerrain::GetPatch(int32 x,int32 z) { if (x<0 || x>=int32(m_MapSizePatches)) return 0; if (z<0 || z>=int32(m_MapSizePatches)) return 0; return &m_Patches[(z*m_MapSizePatches)+x]; } CMiniPatch* CTerrain::GetTile(int32 x,int32 z) { if (x<0 || x>=int32(m_MapSize)-1) return 0; if (z<0 || z>=int32(m_MapSize)-1) return 0; CPatch* patch=GetPatch(x/16,z/16); return &patch->m_MiniPatches[z%16][x%16]; } void CTerrain::Resize(u32 size) { if (size==m_MapSizePatches) { // inexplicable request to resize terrain to the same size .. ignore it return; } if (!m_Heightmap) { // not yet created a terrain; build a default terrain of the given size now Initialize(size,0); return; } // allocate data for new terrain u32 newMapSize=(size*PATCH_SIZE)+1; u16* newHeightmap=new u16[newMapSize*newMapSize]; CPatch* newPatches=new CPatch[size*size]; if (size>m_MapSizePatches) { // new map is bigger than old one - zero the heightmap so we don't get uninitialised // height data along the expanded edges memset(newHeightmap,0,newMapSize*newMapSize); } // now copy over rows of data u32 j; u16* src=m_Heightmap; u16* dst=newHeightmap; u32 copysize=newMapSize>m_MapSize ? m_MapSize : newMapSize; for (j=0;jm_MapSize) { // entend the last height to the end of the row for (u32 i=0;im_MapSize) { // copy over heights of the last row to any remaining rows src=newHeightmap+((m_MapSize-1)*newMapSize); dst=src+newMapSize; for (u32 i=0;im_MapSizePatches) { // copy over the last tile from each column for (u32 n=0;nm_MapSizePatches) { // copy over the last tile from each column CPatch* srcpatch=&newPatches[(m_MapSizePatches-1)*size]; CPatch* dstpatch=srcpatch+size; for (u32 p=0;pm_MiniPatches[15][k]; CMiniPatch& dst=dstpatch->m_MiniPatches[m][k]; dst.Tex1=src.Tex1; dst.Tex1Priority=src.Tex1Priority; } } srcpatch++; dstpatch++; } } } // release all the original data Reset(); // store new data m_Heightmap=newHeightmap; m_Patches=newPatches; m_MapSize=newMapSize; m_MapSizePatches=size; // initialise all the new patches InitialisePatches(); } void CTerrain::InitialisePatches() { for (u32 j=0;jInitialize(this,i,j); } } } // SetHeightMap: set up a new heightmap from 16-bit source data; // assumes heightmap matches current terrain size void CTerrain::SetHeightMap(u16* heightmap) { // keep a copy of the given heightmap memcpy(m_Heightmap,heightmap,m_MapSize*m_MapSize*sizeof(u16)); // recalculate patch bounds, invalidate vertices for (u32 j=0;jCalcBounds(); if (patch->m_RenderData) patch->m_RenderData->m_UpdateFlags|=RENDERDATA_UPDATE_VERTICES; } } }