2 CDTestFramework http://codercorner.com
3 Copyright (c) 2007-2008 Pierre Terdiman, pierre@codercorner.com
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
18 inline float NxAngle(const Point& v0, const Point& v1)
20 float cos = v0|v1; // |v0|*|v1|*Cos(Angle)
21 float sin = (v0^v1).Magnitude(); // |v0|*|v1|*Sin(Angle)
22 return ::atan2(sin, cos);
25 static float computeAngle(const Point* verts, const udword* refs, udword vref)
33 else if(vref==refs[1])
38 else if(vref==refs[2])
47 Point edge0 = verts[refs[e0]] - verts[vref];
48 Point edge1 = verts[refs[e2]] - verts[vref];
50 return NxAngle(edge0, edge1);
52 static bool buildSmoothNormals(
53 udword nbTris, udword nbVerts,
55 const udword* dFaces, const uword* wFaces,
60 if(!verts || !normals || !nbTris || !nbVerts) return false;
62 // Get correct destination buffers
63 // - if available, write directly to user-provided buffers
64 // - else get some ram and keep track of it
65 Point* FNormals = new Point[nbTris];
66 if(!FNormals) return false;
68 // Compute face normals
70 for(udword i=0;i<nbTris;i++)
72 udword Ref0 = dFaces ? dFaces[i*3+0] : wFaces ? wFaces[i*3+0] : 0;
73 udword Ref1 = dFaces ? dFaces[i*3+1+c] : wFaces ? wFaces[i*3+1+c] : 1;
74 udword Ref2 = dFaces ? dFaces[i*3+2-c] : wFaces ? wFaces[i*3+2-c] : 2;
76 FNormals[i] = (verts[Ref2]-verts[Ref0])^(verts[Ref1] - verts[Ref0]);
77 assert(!FNormals[i].IsZero());
78 FNormals[i].Normalize();
81 // Compute vertex normals
82 memset(normals, 0, nbVerts*sizeof(Point));
84 Point* TmpNormals = new Point[nbVerts];
85 memset(TmpNormals, 0, nbVerts*sizeof(Point));
86 for(udword i=0;i<nbTris;i++)
89 Ref[0] = dFaces ? dFaces[i*3+0] : wFaces ? wFaces[i*3+0] : 0;
90 Ref[1] = dFaces ? dFaces[i*3+1] : wFaces ? wFaces[i*3+1] : 1;
91 Ref[2] = dFaces ? dFaces[i*3+2] : wFaces ? wFaces[i*3+2] : 2;
93 for(udword j=0;j<3;j++)
95 if(TmpNormals[Ref[j]].IsZero())
96 TmpNormals[Ref[j]] = FNormals[i];
100 for(udword i=0;i<nbTris;i++)
103 Ref[0] = dFaces ? dFaces[i*3+0] : wFaces ? wFaces[i*3+0] : 0;
104 Ref[1] = dFaces ? dFaces[i*3+1] : wFaces ? wFaces[i*3+1] : 1;
105 Ref[2] = dFaces ? dFaces[i*3+2] : wFaces ? wFaces[i*3+2] : 2;
107 normals[Ref[0]] += FNormals[i] * computeAngle(verts, Ref, Ref[0]);
108 normals[Ref[1]] += FNormals[i] * computeAngle(verts, Ref, Ref[1]);
109 normals[Ref[2]] += FNormals[i] * computeAngle(verts, Ref, Ref[2]);
112 // Normalize vertex normals
113 for(udword i=0;i<nbVerts;i++)
115 if(normals[i].IsZero())
116 normals[i] = TmpNormals[i];
117 assert(!normals[i].IsZero());
118 normals[i].Normalize();
121 DELETEARRAY(TmpNormals);
122 DELETEARRAY(FNormals);
130 #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX))
131 #define RAND_MAX_OVER_TWO (RAND_MAX / 2 + 1)
133 TerrainData::TerrainData() :
147 TerrainData::~TerrainData()
152 void TerrainData::release()
154 DELETEARRAY(normals);
160 void TerrainData::init(udword s, float o, float w, float c, bool flat, const Point* pos)
169 nbFaces = (size-1)*(size-1)*2;
173 // Initialize terrain vertices
174 verts = new Point[nbVerts];
175 for(udword y=0;y<size;y++)
177 for(udword x=0;x<size;x++)
179 verts[x+y*size] = Point(float(x)-(float(size-1)*0.5f), 0.0f, float(y)-(float(size-1)*0.5f)) * width;
183 // Initialize terrain colors
185 colors = new Point[nbVerts];
186 for(udword y=0;y<size;y++)
188 for(udword x=0;x<size;x++)
190 colors[x+y*size] = Point(0.5f, 0.4f, 0.2f);
195 // Initialize terrain faces
196 faces = new udword[nbFaces*3];
198 for(udword j=0;j<size-1;j++)
200 for(udword i=0;i<size-1;i++)
202 // Create first triangle
203 faces[k++] = i + j*size;
204 faces[k++] = i + (j+1)*size;
205 faces[k++] = i+1 + (j+1)*size;
207 // Create second triangle
208 faces[k++] = i + j*size;
209 faces[k++] = i+1 + (j+1)*size;
210 faces[k++] = i+1 + j*size;
216 static void _Compute(bool* done, Point* field, udword x0, udword y0, udword currentSize, float value, udword initSize)
220 if(!currentSize) return;
222 // Compute new heights
223 float v0 = (value * float(rand()-RAND_MAX_OVER_TWO) * ONE_OVER_RAND_MAX);
224 float v1 = (value * float(rand()-RAND_MAX_OVER_TWO) * ONE_OVER_RAND_MAX);
225 float v2 = (value * float(rand()-RAND_MAX_OVER_TWO) * ONE_OVER_RAND_MAX);
226 float v3 = (value * float(rand()-RAND_MAX_OVER_TWO) * ONE_OVER_RAND_MAX);
227 float v4 = (value * float(rand()-RAND_MAX_OVER_TWO) * ONE_OVER_RAND_MAX);
229 udword x1 = (x0+currentSize) % initSize;
230 udword x2 = (x0+currentSize+currentSize) % initSize;
231 udword y1 = (y0+currentSize) % initSize;
232 udword y2 = (y0+currentSize+currentSize) % initSize;
234 if(!done[x1 + y0*initSize]) field[x1 + y0*initSize].y = v0 + 0.5f * (field[x0 + y0*initSize].y + field[x2 + y0*initSize].y);
235 if(!done[x0 + y1*initSize]) field[x0 + y1*initSize].y = v1 + 0.5f * (field[x0 + y0*initSize].y + field[x0 + y2*initSize].y);
236 if(!done[x2 + y1*initSize]) field[x2 + y1*initSize].y = v2 + 0.5f * (field[x2 + y0*initSize].y + field[x2 + y2*initSize].y);
237 if(!done[x1 + y2*initSize]) field[x1 + y2*initSize].y = v3 + 0.5f * (field[x0 + y2*initSize].y + field[x2 + y2*initSize].y);
238 if(!done[x1 + y1*initSize]) field[x1 + y1*initSize].y = v4 + 0.5f * (field[x0 + y1*initSize].y + field[x2 + y1*initSize].y);
240 done[x1 + y0*initSize] = true;
241 done[x0 + y1*initSize] = true;
242 done[x2 + y1*initSize] = true;
243 done[x1 + y2*initSize] = true;
244 done[x1 + y1*initSize] = true;
246 // Recurse through 4 corners
248 _Compute(done, field, x0, y0, currentSize, value, initSize);
249 _Compute(done, field, x0, y1, currentSize, value, initSize);
250 _Compute(done, field, x1, y0, currentSize, value, initSize);
251 _Compute(done, field, x1, y1, currentSize, value, initSize);
257 bool* done = new bool[nbVerts];
258 memset(done,0,nbVerts*sizeof(bool));
260 verts[size-1].y = 10.0f;
261 verts[size*(size-1)].y = 10.0f;
262 verts[nbVerts-1].y = 10.0f;
263 Local::_Compute(done, verts, 0, 0, size, chaos, size);
264 for(udword i=0;i<nbVerts;i++) verts[i].y += offset;
268 // Create a flat area in our terrain
271 udword a = ((size)/2) - ((size)/8);
272 udword b = ((size)/2) + ((size)/8);
273 for(udword y=a;y<b;y++)
275 for(udword x=a;x<b;x++)
277 verts[x+y*size].y = 0.0f;
278 colors[x+y*size].x = 0.3f;
279 colors[x+y*size].y = 0.3f;
280 colors[x+y*size].z = 0.3f;
287 for(udword y=0;y<size;y++)
288 for(udword x=0;x<size;x++)
289 verts[x+y*size] += *pos;
292 // Build vertex normals
293 normals = new Point[nbVerts];
294 buildSmoothNormals(nbFaces, nbVerts, verts, faces, NULL, normals, true);
297 static void renderTerrain(const TerrainData& terrain, bool addWireframe)
299 float* pVertList = new float[terrain.nbFaces*3*3];
300 float* pNormList = new float[terrain.nbFaces*3*3];
301 float* pColorList = new float[terrain.nbFaces*4*3];
303 const udword* faces = terrain.faces;
304 const Point* colors = terrain.colors;
305 const Point* normals = terrain.normals;
306 const Point* verts = terrain.verts;
311 for(int i=0;i<(int)terrain.nbFaces;i++)
315 pVertList[vertIndex++] = verts[faces[i*3+j]].x;
316 pVertList[vertIndex++] = verts[faces[i*3+j]].y;
317 pVertList[vertIndex++] = verts[faces[i*3+j]].z;
319 pNormList[normIndex++] = normals[faces[i*3+j]].x;
320 pNormList[normIndex++] = normals[faces[i*3+j]].y;
321 pNormList[normIndex++] = normals[faces[i*3+j]].z;
323 pColorList[colorIndex++] = colors[faces[i*3+j]].x;
324 pColorList[colorIndex++] = colors[faces[i*3+j]].y;
325 pColorList[colorIndex++] = colors[faces[i*3+j]].z;
326 pColorList[colorIndex++] = 1.0f;
330 glEnableClientState(GL_VERTEX_ARRAY);
331 glVertexPointer(3,GL_FLOAT, 0, pVertList);
332 glEnableClientState(GL_NORMAL_ARRAY);
333 glNormalPointer(GL_FLOAT, 0, pNormList);
334 glColorPointer(4,GL_FLOAT, 0, pColorList);
335 glEnableClientState(GL_COLOR_ARRAY);
336 glDrawArrays(GL_TRIANGLES, 0, terrain.nbFaces*3);
340 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
342 glVertexPointer(3,GL_FLOAT, 0, pVertList);
343 glNormalPointer(GL_FLOAT, 0, pNormList);
345 glDisableClientState(GL_COLOR_ARRAY);
349 glColor4f(0.2f, 0.2f, 0.6f, 1.0f);
350 glDrawArrays(GL_TRIANGLES, 0, terrain.nbFaces*3);
352 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
355 glDisableClientState(GL_NORMAL_ARRAY);
356 glDisableClientState(GL_VERTEX_ARRAY);
357 glDisableClientState(GL_COLOR_ARRAY);
364 static void renderTerrainTriangles(const TerrainData& terrain, udword nbTriangles, const udword* indices)
366 float* pVertList = new float[nbTriangles*3*3];
367 float* pNormList = new float[nbTriangles*3*3];
369 const udword* faces = terrain.faces;
370 const Point* normals = terrain.normals;
371 const Point* verts = terrain.verts;
375 for(int i=0;i<(int)nbTriangles;i++)
377 udword index = *indices++;
381 pVertList[vertIndex++] = verts[faces[index*3+j]].x;
382 pVertList[vertIndex++] = verts[faces[index*3+j]].y;
383 pVertList[vertIndex++] = verts[faces[index*3+j]].z;
385 pNormList[normIndex++] = normals[faces[index*3+j]].x;
386 pNormList[normIndex++] = normals[faces[index*3+j]].y;
387 pNormList[normIndex++] = normals[faces[index*3+j]].z;
391 glEnableClientState(GL_VERTEX_ARRAY);
392 glVertexPointer(3,GL_FLOAT, 0, pVertList);
393 glEnableClientState(GL_NORMAL_ARRAY);
394 glNormalPointer(GL_FLOAT, 0, pNormList);
395 glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
396 glDrawArrays(GL_TRIANGLES, 0, nbTriangles*3);
397 glDisableClientState(GL_NORMAL_ARRAY);
398 glDisableClientState(GL_VERTEX_ARRAY);
410 static TerrainData* gTerrainData = NULL;
411 static Model* gOpcodeModel = null;
412 static MeshInterface gMeshInterface;
414 const Model* GetTerrainModel()
419 void ReleaseTerrain()
421 DELETESINGLE(gOpcodeModel);
422 DELETESINGLE(gTerrainData);
428 #define TERRAIN_SIZE 64
429 #define TERRAIN_CHAOS 30.0f
431 #define TERRAIN_OFFSET -10.0f
432 #define TERRAIN_WIDTH 2.0f
436 gTerrainData = new TerrainData;
437 gTerrainData->init(TERRAIN_SIZE, TERRAIN_OFFSET, TERRAIN_WIDTH, TERRAIN_CHAOS);
439 // Build OPCODE model
441 gMeshInterface.SetNbTriangles(gTerrainData->nbFaces);
442 gMeshInterface.SetNbVertices(gTerrainData->nbVerts);
443 gMeshInterface.SetPointers((const IndexedTriangle*)gTerrainData->faces, gTerrainData->verts);
446 Create.mIMesh = &gMeshInterface;
447 Create.mSettings.mLimit = 1;
448 Create.mSettings.mRules = SPLIT_SPLATTER_POINTS|SPLIT_GEOM_CENTER;
449 Create.mNoLeaf = true;
450 Create.mQuantized = true;
451 Create.mKeepOriginal = false;
452 Create.mCanRemap = false;
454 gOpcodeModel = new Model;
455 if(!gOpcodeModel->Build(Create))
463 renderTerrain(*gTerrainData, true);
466 void RenderTerrainTriangles(udword nbTriangles, const udword* indices)
469 renderTerrainTriangles(*gTerrainData, nbTriangles, indices);