Files
2025-05-13 02:46:21 +03:00

924 lines
24 KiB
C++

#ifndef Terrain_h__
#define Terrain_h__
#include "Globals.h"
#include "math/LifeMath.h"
#include "camera/Camera.h"
#include "ResourceManager.h"
#include <vector>
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<LOD> 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<TerraChunk*> visibleChunks;
static INLINE int fround(float val)
{
return static_cast<int>(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<UCHAR*>(tex);
float pX = tX * info->Width;
float pY = tY * info->Height;
UINT frX = static_cast<UINT>((pX - floorf(pX))* 255.0f);
UINT frY = static_cast<UINT>((pY - floorf(pY))* 255.0f);
UINT* ptr2 = reinterpret_cast<UINT*>(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<int>(floorf(pos.X));
int pZ = static_cast<int>(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__