#ifndef Terrain_h__ #define Terrain_h__ #include "Globals.h" #include "math/LifeMath.h" #include "camera/Camera.h" #include "ResourceManager.h" #include namespace LifeGraphics { enum TerrainDimention { TD_X128 = 128, TD_X256 = 256, TD_X512 = 512, TD_X1024 = 1024, TD_X2048 = 2048, TD_X4096 = 4096, TD_X8192 = 8192 }; struct TerraVertex { int index; char nX; char nZ; USHORT height; TerraVertex() { index = 0; nX = nZ = 0; height = 0; } TerraVertex(int _index, char _nx, char _nz, USHORT _height) { index = _index; nX = _nx; nZ = _nz; height = _height; } }; struct TerraChunk { int LOD; int Offset; LifeCore::BoundingBox3D BoundingBox; IIndexBuffer* IB; int Column, Row; bool Visible; int NumVerts; }; struct LOD { IIndexBuffer* Top; IIndexBuffer* Bottom; IIndexBuffer* Left; IIndexBuffer* Right; IIndexBuffer* TopLeft; IIndexBuffer* TopRight; IIndexBuffer* BottomLeft; IIndexBuffer* BottomRight; IIndexBuffer* full; IIndexBuffer* allSides; }; #define MAX_VISIBLE_DISTANCE 1025 //must be n ^ 2 + 1 struct TERRA_DATA { float size; LifeMath::float3 sunDir; }; class Terrain { public: std::vector lods; IVertexBuffer* vb; int vertexCount; IInputLayout* layout; TerraChunk* chunks; IShader* terraVs; IShader* terraPs; IConstantBuffer* terraCb; IShader* terraVs_g; IShader* terraPs_g; texture_ptr diffuse; IRasterizerState* solidState; IRasterizerState* wireState; void* data; IBitmapInfo* info; USHORT* heightMap; int TerrainWidth; std::vector visibleChunks; static INLINE int fround(float val) { return static_cast(floor(val + 0.5f)); } static INLINE float clampFloat(float val, float min, float max) { if(val < min)return min; if(val > max)return max; return val; } static INLINE UINT blendrgb(UINT d, UINT s, UINT a) { const UINT dstrb = d & 0xFF00FF; const UINT dstg = d & 0xFF00; const UINT srcrb = s & 0xFF00FF; const UINT srcg = s & 0xFF00; UINT drb = srcrb - dstrb; UINT dg = srcg - dstg; drb *= a; dg *= a; drb >>= 8; dg >>= 8; UINT rb = (drb + dstrb) & 0xFF00FF; UINT g = (dg + dstg) & 0xFF00; return rb | g; } static INLINE UINT sampleTex(void* tex, IBitmapInfo* info, float tX, float tY) { tX = clampFloat(tX, 0.0f, 1.0f); tY = clampFloat(tY, 0.0f, 1.0f); float texelW = 1.0f / (float)(info->Width); float texelH = 1.0f / (float)(info->Height); // tX *= (1.0f - texelW * 2.0f); // tY *= (1.0f - texelH * 2.0f); int x = fround((tX * (float)(info->Width))); int y = fround((tY * (float)(info->Height))); UCHAR* ptr = reinterpret_cast(tex); float pX = tX * info->Width; float pY = tY * info->Height; UINT frX = static_cast((pX - floorf(pX))* 255.0f); UINT frY = static_cast((pY - floorf(pY))* 255.0f); UINT* ptr2 = reinterpret_cast(tex); UINT color; if(info->Bpp < 4) { UINT tl = 0xff000000; UINT tr = 0xff000000; UINT rl = 0xff000000; UINT rr = 0xff000000; memcpy(&tl, ptr+ (y*info->Width + x)* info->Bpp, 3); memcpy(&tr, ptr+ (y*info->Width + x + 1)* info->Bpp, 3); memcpy(&rl, ptr+ ((y + 1)*info->Width + x)* info->Bpp, 3); memcpy(&rr, ptr+ ((y + 1)*info->Width + x + 1)* info->Bpp, 3); color = blendrgb ( blendrgb ( tl, tr, frX), blendrgb ( rl, rr, frX), frY); } return color; } #define MAX_HEIGHT 128 #define AS_FLOAT(ololo) ololo##.0f Terrain(TerrainDimention dim, int chunkSize = 64, int maxVisibleRange = 100) { terraCb = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(TERRA_DATA)); diffuse = ResourceManager::me()->GetTexture(String(_text("root/Data/Textures/Terrain/shortGrass.png"))); ResourceManager::me()->ReadBitmap(String(_text("root/Data/Textures/hm.bmp")), &data, &info); terraVs = ResourceManager::me()->CompileUserShaderVS(String(_text("Terrain.vsh"))); terraPs = ResourceManager::me()->CompileUserShaderPS(String(_text("Terrain.psh"))); terraVs_g = ResourceManager::me()->CompileUserShaderVS(String(_text("Terrain_g.vsh"))); terraPs_g = ResourceManager::me()->CompileUserShaderPS(String(_text("Terrain_g.psh"))); visibleCunkOffset = maxVisibleRange / chunkSize; TerrainWidth = (int)dim; ChunkWidth = chunkSize; //Generate LOD IndexBuffers //------------------------------------LOD_1------------------- LOD1Full = GenerateLOD(chunkSize, 1, false, false, false, false); LOD1TrimAllSides = GenerateLOD(chunkSize, 1, true, true, true, true); LOD1TrimNorthEast = GenerateLOD(chunkSize, 1, true, false, false, true); LOD1TrimNorthWest = GenerateLOD(chunkSize, 1, true, false, true, false); LOD1TrimSouthEast = GenerateLOD(chunkSize, 1, false, true, false, true); LOD1TrimSouthWest = GenerateLOD(chunkSize, 1, false, true, true, false); //------------------------------------LOD_2------------------- LOD2Full = GenerateLOD(chunkSize, 2, false, false, false, false); LOD2TrimAllSides = GenerateLOD(chunkSize, 2, true, true, true, true); LOD2TrimNorth = GenerateLOD(chunkSize, 2, true, false, false, false); LOD2TrimSouth = GenerateLOD(chunkSize, 2, false, true, false, false); LOD2TrimWest = GenerateLOD(chunkSize, 2, false, false, true, false); LOD2TrimEast = GenerateLOD(chunkSize, 2, false, false, false, true); LOD2TrimNorthEast = GenerateLOD(chunkSize, 2, true, false, false, true); LOD2TrimNorthWest = GenerateLOD(chunkSize, 2, true, false, true, false); LOD2TrimSouthEast = GenerateLOD(chunkSize, 2, false, true, false, true); LOD2TrimSouthWest = GenerateLOD(chunkSize, 2, false, true, true, false); //------------------------------------LOD_3------------------- LOD3Full = GenerateLOD(chunkSize, 3, false, false, false, false); LOD3TrimAllSides = GenerateLOD(chunkSize, 3, true, true, true, true); LOD3TrimNorth = GenerateLOD(chunkSize, 3, true, false, false, false); LOD3TrimSouth = GenerateLOD(chunkSize, 3, false, true, false, false); LOD3TrimWest = GenerateLOD(chunkSize, 3, false, false, true, false); LOD3TrimEast = GenerateLOD(chunkSize, 3, false, false, false, true); LOD3TrimNorthEast = GenerateLOD(chunkSize, 3, true, false, false, true); LOD3TrimNorthWest = GenerateLOD(chunkSize, 3, true, false, true, false); LOD3TrimSouthEast = GenerateLOD(chunkSize, 3, false, true, false, true); LOD3TrimSouthWest = GenerateLOD(chunkSize, 3, false, true, true, false); //------------------------------------LOD_4------------------- LOD4Full = GenerateLOD(chunkSize, 4, false, false, false, false); LOD lod1; lod1.allSides = LOD1TrimAllSides; LOD lod2; lod2.Top = LOD2TrimNorth; lod2.Bottom = LOD2TrimSouth; lod2.Left = LOD2TrimWest; lod2.Right = LOD2TrimEast; lod2.TopLeft = LOD2TrimNorthWest; lod2.TopRight = LOD2TrimNorthEast; lod2.BottomLeft = LOD2TrimSouthWest; lod2.BottomRight = LOD2TrimSouthEast; lod2.full = LOD2Full; lod2.allSides = LOD2TrimAllSides; LOD lod3; lod3.Top = LOD3TrimNorth; lod3.Bottom = LOD3TrimSouth; lod3.Left = LOD3TrimWest; lod3.Right = LOD3TrimEast; lod3.TopLeft = LOD3TrimNorthWest; lod3.TopRight = LOD3TrimNorthEast; lod3.BottomLeft = LOD3TrimSouthWest; lod3.BottomRight = LOD3TrimSouthEast; lod3.full = LOD3Full; lod3.allSides = LOD3TrimAllSides; LOD lod4; lod4.full = LOD4Full; lods.push_back(lod1); lods.push_back(lod2); lods.push_back(lod3); lods.push_back(lod4); solidState = CG::me()->GetRenderer()->CreateRasterizerState(FILL_SOLID); wireState = CG::me()->GetRenderer()->CreateRasterizerState(FILL_WIREFRAME); vertexCount = (TerrainWidth + 1) * (TerrainWidth + 1); vb = CG::me()->GetRenderer()->CreateVertexBufferDynamic(vertexCount * sizeof(TerraVertex), sizeof(TerraVertex)); float multiplier = (float)(0xffff/MAX_HEIGHT); heightMap = new USHORT[(TerrainWidth + 1) * (TerrainWidth + 1)]; //load height map for(int y = 0; y <= TerrainWidth; y++) { for(int x = 0; x <= TerrainWidth; x++) { UINT col = sampleTex(data, info, (float)x / (TerrainWidth + 2), 1.0f - ((float)y / (TerrainWidth + 2))); USHORT h = (USHORT)(col & 0xff) * (0xffff / 0xff); int index = y * (TerrainWidth + 1) + x; heightMap[index] = h; } } //smooth terrain heights int numSmoothSteps = 1; for(int i = 0; i < numSmoothSteps; i++) { //smooth by X for(int y = 0; y <= TerrainWidth; y++) { for(int x = 1; x < TerrainWidth; x++) { int idL = y * (TerrainWidth + 1) + (x - 1); int idC = y * (TerrainWidth + 1) + (x); int idR = y * (TerrainWidth + 1) + (x + 1); float h1 = (float)heightMap[idL]; float h2 = (float)heightMap[idC]; float h3 = (float)heightMap[idR]; USHORT avg = (USHORT)((h1 + h2 + h3) / 3.0f); heightMap[idL] = avg; heightMap[idC] = avg; heightMap[idR] = avg; } } //smooth by Y for(int y = 1; y < TerrainWidth; y++) { for(int x = 0; x <= TerrainWidth; x++) { int idT = (y - 1) * (TerrainWidth + 1) + x; int idC = y * (TerrainWidth + 1) + x; int idB = (y + 1) * (TerrainWidth + 1) + x; float h1 = (float)heightMap[idT]; float h2 = (float)heightMap[idC]; float h3 = (float)heightMap[idB]; USHORT avg = (USHORT)((h1 + h2 + h3) / 3.0f); heightMap[idT] = avg; heightMap[idC] = avg; heightMap[idB] = avg; } } } //build terrain geometry TerraVertex* vertexData = new TerraVertex[vertexCount]; for(int y = 0; y <= TerrainWidth; y++) { for(int x = 0; x <= TerrainWidth; x++) { int index = y * (TerrainWidth + 1) + x; vertexData[index] = TerraVertex(index, 0,0, heightMap[index]); } } delete info; delete[] data; //compute normals LifeMath::float3 p1, p2, p3, d1, d2, n; TerraVertex v1, v2, v3; for(int y = 0; y < TerrainWidth; y++) { for(int x = 0; x < TerrainWidth; x++) { //first triangle int i1 = y * (TerrainWidth + 1) + x; int i2 = y * (TerrainWidth + 1) + x + 1; int i3 = (y + 1) * (TerrainWidth + 1) + x; v1 = vertexData[i1]; v2 = vertexData[i2]; v3 = vertexData[i3]; p1 = LifeMath::float3((float)(x), (float)v1.height / multiplier,-(float)(y)); p2 = LifeMath::float3((float)(x + 1), v2.height / multiplier, -(float)(y)); p3 = LifeMath::float3((float)(x), (float)v3.height / multiplier, -(float)(y + 1)); d1 = p2 - p1; d2 = p3 - p1; n = LifeMath::Vec3Cross(d1,d2); LifeMath::Vec3Normalize(&n); printf("ACHTUNG! Maybe wrong value computed for normal! What about NEAGTIVE numbers?\n"); char nnx = (char)(n.X * 127.0f); char nnz = (char)(n.Z * 127.0f); vertexData[i1].nX = nnx; vertexData[i1].nZ = nnz; vertexData[i2].nX = nnx; vertexData[i2].nZ = nnz; vertexData[i3].nX = nnx; vertexData[i3].nZ = nnz; //second triangle i1 = (y + 1) * (TerrainWidth + 1) + x; i2 = y * (TerrainWidth + 1) + x + 1; i3 = (y + 1) * (TerrainWidth + 1) + x + 1; v1 = vertexData[i1]; v2 = vertexData[i2]; v3 = vertexData[i3]; p1 = LifeMath::float3((float)(x), v1.height / multiplier, -(float)(y + 1)); p2 = LifeMath::float3((float)(x + 1), v2.height / multiplier, -(float)(y)); p3 = LifeMath::float3((float)(x + 1), v3.height / multiplier, -(float)(y + 1)); d1 = p2 - p1; d2 = p3 - p1; n = LifeMath::Vec3Cross(d1,d2); LifeMath::Vec3Normalize(&n); nnx = (char)(n.X * 127.0f); nnz = (char)(n.Z * 127.0f); vertexData[i1].nX = nnx; vertexData[i1].nZ = nnz; vertexData[i2].nX = nnx; vertexData[i2].nZ = nnz; vertexData[i3].nX = nnx; vertexData[i3].nZ = nnz; } } LifeCore::IDataStream* stm = vb->Map(0,LF_Discard); stm->Write(vertexData, vertexCount * sizeof(TerraVertex)); vb->Unmap(); int chunkStride = (TerrainWidth / chunkSize); //create and process chunk info chunks = new TerraChunk[chunkStride * chunkStride]; for(int x = 0; x < chunkStride; x++) { for(int y = 0; y < chunkStride; y++) { int index = y * chunkStride + x; chunks[index].Offset = y * (TerrainWidth + 1) * chunkSize + x * chunkSize; chunks[index].Column = x; chunks[index].Row = y; chunks[index].LOD = 1; chunks[index].IB = LOD1Full; //now compute chunk bounding box int minX = x * ChunkWidth; int maxX = minX + ChunkWidth; int minY = y * ChunkWidth; int maxY = minY + ChunkWidth; UINT minH; UINT maxH; minH = maxH = vertexData[minY * (TerrainWidth + 1) + minX].height; for (int i = minX; i<=maxX; i++) { for (int j = minY; j<=maxY; j++) { UINT h = vertexData[j * (TerrainWidth + 1) + i].height; if(minH > h) minH = h; if(maxH < h) maxH = h; } } chunks[index].BoundingBox.resize(LifeMath::float3((float)(x * ChunkWidth), (float)minH / multiplier, -(float)(maxY)), LifeMath::float3((float)(maxX), (float)maxH / multiplier, -(float)(minY))); /*printf("Chunk= X:%i Y:%i\nMin= X:%f Z:%f\nMax= X:%f Z:%f\n\n", x,y,chunks[index].BoundingBox.minimum().X, chunks[index].BoundingBox.minimum().Z, chunks[index].BoundingBox.maximum().X, chunks[index].BoundingBox.maximum().Z );*/ } } delete[] vertexData; IInputElement elements[] = { IInputElement("POSITION", 0, 0, FORMAT_R32_UINT, 0, 0, 0), IInputElement("TEXCOORD", 0, 4, FORMAT_R8G8_SINT, 0, 0, 0), IInputElement("TEXCOORD", 1, 6, FORMAT_R16_UINT, 0, 0, 0), }; layout = CG::me()->GetRenderer()->CreateInputLayout(elements, 3, terraVs); } ~Terrain() { delete[] chunks; SAFE_DELETE(vb); SAFE_DELETE(layout); SAFE_DELETE(terraVs); SAFE_DELETE(terraPs_g); diffuse->Release(); SAFE_DELETE(solidState); SAFE_DELETE(wireState); SAFE_DELETE_ARRAY(heightMap); SAFE_DELETE(LOD1Full); SAFE_DELETE(LOD1TrimAllSides); SAFE_DELETE(LOD1TrimNorthEast); SAFE_DELETE(LOD1TrimNorthWest); SAFE_DELETE(LOD1TrimSouthEast); SAFE_DELETE(LOD1TrimSouthWest); SAFE_DELETE(LOD2Full); SAFE_DELETE(LOD2TrimAllSides); SAFE_DELETE(LOD2TrimNorth); SAFE_DELETE(LOD2TrimSouth); SAFE_DELETE(LOD2TrimEast); SAFE_DELETE(LOD2TrimWest); SAFE_DELETE(LOD2TrimNorthEast); SAFE_DELETE(LOD2TrimNorthWest); SAFE_DELETE(LOD2TrimSouthEast); SAFE_DELETE(LOD2TrimSouthWest); SAFE_DELETE(LOD3Full); SAFE_DELETE(LOD3TrimAllSides); SAFE_DELETE(LOD3TrimNorth); SAFE_DELETE(LOD3TrimSouth); SAFE_DELETE(LOD3TrimEast); SAFE_DELETE(LOD3TrimWest); SAFE_DELETE(LOD3TrimNorthEast); SAFE_DELETE(LOD3TrimNorthWest); SAFE_DELETE(LOD3TrimSouthEast); SAFE_DELETE(LOD3TrimSouthWest); SAFE_DELETE(LOD4Full); } void Update(Camera* cam, const LifeMath::float3& sunDir) { TERRA_DATA newData; newData.size = (float)TerrainWidth; newData.sunDir = sunDir; terraCb->Update(&newData, sizeof(TERRA_DATA)); visibleChunks.clear(); LifeMath::float3 pos = cam->getPosition(); int pX = static_cast(floorf(pos.X)); int pZ = static_cast(floorf(pos.Z)); int col = pX / ChunkWidth; int row = -pZ / ChunkWidth; //int vis = 0; //update all terrain chunks to minimal LOD (FIX IT: should not update ALL terrain) int chunkStride = TerrainWidth / ChunkWidth; for(int x = 0; x < chunkStride; x++) { for(int y = 0; y < chunkStride; y++) { if(cam->getFrustum()->BoxInFrustum(chunks[y * chunkStride + x].BoundingBox)) { visibleChunks.push_back(&chunks[y * chunkStride + x]); } } } //check if cam is in terrain area if(col >= 0 && row >= 0 && col < (TerrainWidth / ChunkWidth) && row < (TerrainWidth / ChunkWidth)) { int numChunks = visibleChunks.size(); for(int i = 0; i < numChunks; i++) { TerraChunk* c = visibleChunks[i]; int diffCol = col - c->Column; int diffRow = row - c->Row; int lodNum = abs(diffCol) > abs(diffRow) ? abs(diffCol) : abs(diffRow); if(lodNum < 3) { switch(lodNum) { case 0: c->LOD = 1; c->IB = LOD1TrimAllSides; break; default: LOD* curLod = &lods[lodNum]; c->LOD = lodNum + 1; if(abs(diffRow) > abs(diffCol)) { if(diffRow > 0) c->IB = curLod->Left; else c->IB = curLod->Right; } else if(abs(diffRow) < abs(diffCol)) { if(diffCol > 0) c->IB = curLod->Top; else c->IB = curLod->Bottom; } else { if(diffCol < 0 && diffRow < 0) c->IB = curLod->BottomRight; else if(diffCol > 0 && diffRow < 0) c->IB = curLod->TopRight; else if(diffCol < 0 && diffRow > 0) c->IB = curLod->BottomLeft; else c->IB = curLod->TopLeft; } break; } } else { c->LOD = 4; c->IB = LOD4Full; } } } // printf("Visible chunks: %i\n", visibleChunks.size()); } void Render(PassType pass) { IRenderer* renderer = CG::me()->GetRenderer(); renderer->SetPSConstantBuffer(terraCb, 2); //renderer->SetRasterizerState(wireState); renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); renderer->SetVertexBuffer(vb); if(pass == PT_COMPOSE_G_BUF) { renderer->SetPixelShader(terraPs_g); renderer->SetVertexShader(terraVs_g); } else if(pass == PT_RENDER_SHADOW_MAP) { renderer->SetVertexShader(terraVs); } else { renderer->SetPixelShader(terraPs); renderer->SetVertexShader(terraVs); } renderer->SetInputLayout(layout); renderer->SetPSTexture(diffuse.ptr(), 0); int chunkStride = TerrainWidth / ChunkWidth; int renderCount = visibleChunks.size(); for (int j = 0; j < renderCount; j++) { TerraChunk* c = visibleChunks[j]; //CG.GraphicsDevice.VertexFormat = g_terra.TerrainGeometryBuffer.Description.FVF; //if (c->Visible) //{ switch (c->LOD) { case 1: renderer->SetIndexBuffer(c->IB); //renderer->DrawIndexed(chunks[index].Offset, (ChunkWidth / 1) * (ChunkWidth / 1) * 2); renderer->DrawIndexed(0, (ChunkWidth / 1) * (ChunkWidth / 1) * 2, c->Offset); break; case 2: renderer->SetIndexBuffer(c->IB); renderer->DrawIndexed(0, (ChunkWidth / 2) * (ChunkWidth / 2) * 2, c->Offset); break; case 3: renderer->SetIndexBuffer(c->IB); renderer->DrawIndexed(0, (ChunkWidth / 4) * (ChunkWidth / 4) * 2, c->Offset); break; case 4: renderer->SetIndexBuffer(c->IB); renderer->DrawIndexed(0, (ChunkWidth / 8) * (ChunkWidth / 8) * 2, c->Offset); break; } //} } //renderer->SetRasterizerState(solidState); } float GetHeight(float x, float z) const { //return 0.0f; /* UINT col = sampleTex(data, info, (float)x / (TerrainWidth + 2), 1.0f - ((float)y / (TerrainWidth + 2))); float r = (float)(col & 0xff) / 255.0f * AS_FLOAT(MAX_HEIGHT); float g = (float)((col & 0xff00) >> 8) / 255.0f * AS_FLOAT(MAX_HEIGHT); float b = (float)((col & 0xff0000) >> 16) / 255.0f * AS_FLOAT(MAX_HEIGHT); float h = (r + g + b) / 3.0f * multiplier;*/ float multiplier = (float)(0xffff/MAX_HEIGHT); z = -z; if(x < 0 || x > TerrainWidth || z < 0 || z > TerrainWidth) return 0; int iX = (int)x; int iZ = (int)z; float fX = x - (float)iX; float fZ = z - (float)iZ; float y1 = ((float)heightMap[(iZ + 0) * (TerrainWidth + 1) + (iX + 0)]) / multiplier; float y2 = ((float)heightMap[(iZ + 0) * (TerrainWidth + 1) + (iX + 1)]) / multiplier; float y3 = ((float)heightMap[(iZ + 1) * (TerrainWidth + 1) + (iX + 1)]) / multiplier; float y4 = ((float)heightMap[(iZ + 1) * (TerrainWidth + 1) + (iX + 0)]) / multiplier; if((1.0f - fZ) >= (fX)) return y1 + (y2 - y1) * fX + (y4 - y1) * fZ; //top triangle else return y3 + (y4 - y3) * (1.0f - fX) + (y2 - y3) * (1.0f - fZ); //bottom triangle } private: IIndexBuffer* LOD1Full; IIndexBuffer* LOD1TrimAllSides; IIndexBuffer* LOD1TrimNorthEast; IIndexBuffer* LOD1TrimNorthWest; IIndexBuffer* LOD1TrimSouthEast; IIndexBuffer* LOD1TrimSouthWest; IIndexBuffer* LOD2Full; IIndexBuffer* LOD2TrimAllSides; IIndexBuffer* LOD2TrimNorth; IIndexBuffer* LOD2TrimSouth; IIndexBuffer* LOD2TrimEast; IIndexBuffer* LOD2TrimWest; IIndexBuffer* LOD2TrimNorthEast; IIndexBuffer* LOD2TrimNorthWest; IIndexBuffer* LOD2TrimSouthEast; IIndexBuffer* LOD2TrimSouthWest; IIndexBuffer* LOD3Full; IIndexBuffer* LOD3TrimAllSides; IIndexBuffer* LOD3TrimNorth; IIndexBuffer* LOD3TrimSouth; IIndexBuffer* LOD3TrimEast; IIndexBuffer* LOD3TrimWest; IIndexBuffer* LOD3TrimNorthEast; IIndexBuffer* LOD3TrimNorthWest; IIndexBuffer* LOD3TrimSouthEast; IIndexBuffer* LOD3TrimSouthWest; IIndexBuffer* LOD4Full; int ChunkWidth; int visibleCunkOffset; IIndexBuffer* GenerateLOD(uint32 chunkLength, uint32 lodNumber, bool trimTop, bool trimBottom, bool trimLeft, bool trimRight) { uint32 mNum = 1, Counter = 0; switch (lodNumber) { case 1: mNum = 1; break; case 2: mNum = 2; break; case 3: mNum = 4; break; case 4: mNum = 8; break; } unsigned int* Indices = new unsigned int[(chunkLength / mNum) * (chunkLength / mNum) * 6]; for (uint32 i = 0; i < chunkLength / mNum; i++) { for (uint32 j = 0; j < chunkLength / mNum; j++) { Indices[Counter] = (i + 1) * (TerrainWidth + 1) * mNum + j * mNum; Counter += 1; Indices[Counter] = i * (TerrainWidth + 1) * mNum + j * mNum; Counter += 1; Indices[Counter] = i * (TerrainWidth + 1) * mNum + (j + 1) * mNum; Counter += 1; Indices[Counter] = (i + 1) * (TerrainWidth + 1) * mNum + j * mNum; Counter += 1; Indices[Counter] = i * (TerrainWidth + 1) * mNum + (j + 1) * mNum; Counter += 1; Indices[Counter] = (i + 1) * (TerrainWidth + 1) * mNum + (j + 1) * mNum; Counter += 1; } } //-----------------LOD trimmers---------------------------- //trim top corner if (trimTop) { for (unsigned int i = 0; i < (chunkLength / mNum); i++) { if ((int)((float)((i + 1) / 2.0f)) == (float)((i + 1) / 2.0f)) { //÷¸òíàÿ Indices[i * chunkLength / mNum * 6 + 0] = 0; Indices[i * chunkLength / mNum * 6 + 1] = 0; Indices[i * chunkLength / mNum * 6 + 2] = 0; } else { Indices[i * chunkLength / mNum * 6 + 3] = Indices[(i + 1) * chunkLength / mNum * 6 + 3]; if (trimLeft == true) { if (i != 0) Indices[i * chunkLength / mNum * 6 + 4] -= (mNum); else Indices[i * chunkLength / mNum * 6 + 0] -= (mNum); } else { Indices[i * chunkLength / mNum * 6 + 4] -= (mNum); } Indices[i * chunkLength / mNum * 6 + 0] += (mNum); } } } //trim bottom corner if (trimBottom) { for (unsigned int i = 0; i < (chunkLength / mNum); i++) { if ((int)((float)((i + 1) / 2.0f)) == (float)((i + 1) / 2.0f)) { //÷¸òíàÿ Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 0] = 0; Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 1] = 0; Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 2] = 0; Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 4] -= mNum; } else { Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 5] = Indices[(i + 2) * (chunkLength / mNum) * 6 - 6 + 5]; } } } int offset = (chunkLength / mNum) * ((chunkLength / mNum) - 1) * 6; if (trimRight) { //Trim right corner for (unsigned int i = 1; i < (chunkLength / mNum) + 1; i++) { if ((int)((float)i / 2.0f) == (float)i / 2.0f) { //÷¸òíàÿ Indices[offset + (i - 1) * 6 + 0] += (mNum); Indices[offset + (i - 1) * 6 + 3] = 0; Indices[offset + (i - 1) * 6 + 4] = 0; Indices[offset + (i - 1) * 6 + 5] = 0; } else { Indices[offset + (i - 1) * 6 + 5] += (mNum); } } } if (trimLeft) { //Trim left corner offset = (chunkLength / mNum) * 6; for (unsigned int i = 1; i < (chunkLength / mNum) + 1; i++) { if ((int)((float)i / 2.0f) == (float)i / 2.0f) { Indices[(i - 1) * 6 + 0] = 0; Indices[(i - 1) * 6 + 1] = 0; Indices[(i - 1) * 6 + 2] = 0; } else { Indices[(i - 1) * 6 + 0] += (mNum); Indices[(i - 1) * 6 + 2] += (mNum); Indices[(i - 1) * 6 + 4] -= (mNum); } } } IIndexBuffer* _ib = CG::me()->GetRenderer()->CreateIndexBuffer((chunkLength / mNum) * (chunkLength / mNum) * 6 * sizeof(unsigned int),Indices, FALSE); return _ib; } static void GenerateMaxLOD(int chunkLength, int* indices) { // int[] indices = new int[chunkLength * chunkLength * 6]; int counter = 0; for (int i = 0; i < chunkLength; i++) { for (int j = 0; j < chunkLength; j++) { //First cell's triangle indices[counter] = i * (chunkLength + 1) + (j + 1); counter += 1; indices[counter] = (i + 1) * (chunkLength + 1) + j; counter += 1; indices[counter] = i * (chunkLength + 1) + j; counter += 1; //Second cell's triangle indices[counter] = (i + 1) * (chunkLength + 1) + (j + 1); counter += 1; indices[counter] = (i + 1) * (chunkLength + 1) + j; counter += 1; indices[counter] = i * (chunkLength + 1) + (j + 1); counter += 1; } } } }; } #endif // Terrain_h__