#ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "quadtree.h" #include "textures.h" #include "course.h" #include "ogl.h" #include #define TERRAIN_ERROR_SCALE 0.1 #define VERTEX_FORCE_THRESHOLD 100 #define ERROR_MAGNIFICATION_THRESHOLD 20 #define ERROR_MAGNIFICATION_AMOUNT 3 #define ENV_MAP_ALPHA 50 #define colorval(j,ch) \ VNCArray[j*STRIDE_GL_ARRAY+STRIDE_GL_ARRAY-4+(ch)] #define setalphaval(i) colorval(VertexIndices[i], 3) = \ ( terrain <= VertexTerrains[i] ) ? 255 : 0 #define update_min_max( idx ) \ if ( idx > VertexArrayMaxIdx ) { \ VertexArrayMaxIdx = idx; \ } \ if ( idx < VertexArrayMinIdx ) { \ VertexArrayMinIdx = idx; \ } static void make_tri_list(void(*tri_func)(int, int, int, int), unsigned char EnabledFlags, int flags, int terrain) { if ((EnabledFlags & 1) == 0) { tri_func(0, 2, 8, terrain); } else { if (flags & 8) tri_func(0, 1, 8, terrain); if (flags & 1) tri_func(0, 2, 1, terrain); } if ((EnabledFlags & 2) == 0) { tri_func(0, 4, 2, terrain); } else { if (flags & 1) tri_func(0, 3, 2, terrain); if (flags & 2) tri_func(0, 4, 3, terrain); } if ((EnabledFlags & 4) == 0) { tri_func(0, 6, 4, terrain); } else { if (flags & 2) tri_func(0, 5, 4, terrain); if (flags & 4) tri_func(0, 6, 5, terrain); } if ((EnabledFlags & 8) == 0) { tri_func(0, 8, 6, terrain); } else { if (flags & 4) tri_func(0, 7, 6, terrain); if (flags & 8) tri_func(0, 8, 7, terrain); } } GLuint *quadsquare::VertexArrayIndices = NULL; GLuint quadsquare::VertexArrayCounter; GLuint quadsquare::VertexArrayMinIdx; GLuint quadsquare::VertexArrayMaxIdx; quadsquare::quadsquare(quadcornerdata* pcd) { pcd->Square = this; Static = false; ForceEastVert = false; ForceSouthVert = false; Dirty = true; for (int i = 0; i < 4; i++) { Child[i] = (quadsquare*) NULL; } EnabledFlags = 0; for (int i = 0; i < 2; i++) SubEnabledCount[i] = 0; Vertex[0].Y = 0.25 * (pcd->Verts[0].Y + pcd->Verts[1].Y + pcd->Verts[2].Y + pcd->Verts[3].Y); Vertex[1].Y = 0.5 * (pcd->Verts[3].Y + pcd->Verts[0].Y); Vertex[2].Y = 0.5 * (pcd->Verts[0].Y + pcd->Verts[1].Y); Vertex[3].Y = 0.5 * (pcd->Verts[1].Y + pcd->Verts[2].Y); Vertex[4].Y = 0.5 * (pcd->Verts[2].Y + pcd->Verts[3].Y); for (int i = 0; i < 2; i++) Error[i] = 0; for (int i = 0; i < 4; i++) { Error[i+2] = fabs((Vertex[0].Y + pcd->Verts[i].Y) - (Vertex[i+1].Y + Vertex[((i+1)&3) + 1].Y)) * 0.25; } MinY = MaxY = pcd->Verts[0].Y; for (int i = 1; i < 4; i++) { float y = pcd->Verts[i].Y; if (y < MinY) MinY = y; if (y > MaxY) MaxY = y; } } quadsquare::~quadsquare() { for (int i = 0; i < 4; i++) { if (Child[i]) delete Child[i]; } } void quadsquare::SetStatic(const quadcornerdata &cd) { if (Static == false) { Static = true; if (cd.Parent && cd.Parent->Square) { cd.Parent->Square->SetStatic(*cd.Parent); } } } int quadsquare::CountNodes() { int count = 1; for (int i = 0; i < 4; i++) { if (Child[i]) count += Child[i]->CountNodes(); } return count; } float quadsquare::GetHeight(const quadcornerdata &cd, float x, float z) { int half = 1 << cd.Level; float lx = (x - cd.xorg) / float(half); float lz = (z - cd.zorg) / float(half); int ix = (int) floor(lx); int iz = (int) floor(lz); if (ix < 0) ix = 0; if (ix > 1) ix = 1; if (iz < 0) iz = 0; if (iz > 1) iz = 1; int index = (ix ^ (iz ^ 1)) + (iz << 1); if (Child[index] && Child[index]->Static) { quadcornerdata q; SetupCornerData(&q, cd, index); return Child[index]->GetHeight(q, x, z); } lx -= ix; if (lx < 0) lx = 0; if (lx > 1) lx = 1; lz -= iz; if (lx < 0) lz = 0; if (lz > 1) lz = 1; float s00, s01, s10, s11; switch (index) { default: case 0: s00 = Vertex[2].Y; s01 = cd.Verts[0].Y; s10 = Vertex[0].Y; s11 = Vertex[1].Y; break; case 1: s00 = cd.Verts[1].Y; s01 = Vertex[2].Y; s10 = Vertex[3].Y; s11 = Vertex[0].Y; break; case 2: s00 = Vertex[3].Y; s01 = Vertex[0].Y; s10 = cd.Verts[2].Y; s11 = Vertex[4].Y; break; case 3: s00 = Vertex[0].Y; s01 = Vertex[1].Y; s10 = Vertex[4].Y; s11 = cd.Verts[3].Y; break; } return (s00 * (1-lx) + s01 * lx) * (1 - lz) + (s10 * (1-lx) + s11 * lx) * lz; } quadsquare* quadsquare::GetNeighbor(int dir, const quadcornerdata& cd) { if (cd.Parent == 0) return 0; quadsquare* p = 0; int index = cd.ChildIndex ^ 1 ^ ((dir & 1) << 1); bool SameParent = ((dir - cd.ChildIndex) & 2) ? true : false; if (SameParent) { p = cd.Parent->Square; } else { p = cd.Parent->Square->GetNeighbor(dir, *cd.Parent); if (p == 0) return 0; } quadsquare* n = p->Child[index]; return n; } float quadsquare::RecomputeError(const quadcornerdata& cd) { int half = 1 << cd.Level; int whole = half << 1; float terrain_error; float maxerror = 0; float e; size_t numTerr = Course.TerrList.size(); if (cd.ChildIndex & 1) { e = fabs(Vertex[0].Y - (cd.Verts[1].Y + cd.Verts[3].Y) * 0.5); } else { e = fabs(Vertex[0].Y - (cd.Verts[0].Y + cd.Verts[2].Y) * 0.5); } if (e > maxerror) maxerror = e; MaxY = Vertex[0].Y; MinY = Vertex[0].Y; for (int i = 0; i < 4; i++) { float y = cd.Verts[i].Y; if (y < MinY) MinY = y; if (y > MaxY) MaxY = y; } e = fabs(Vertex[1].Y - (cd.Verts[0].Y + cd.Verts[3].Y) * 0.5); if (e > maxerror) maxerror = e; Error[0] = e; e = fabs(Vertex[4].Y - (cd.Verts[2].Y + cd.Verts[3].Y) * 0.5); if (e > maxerror) maxerror = e; Error[1] = e; if (cd.Level == 0 && cd.xorg <= RowSize-1 && cd.zorg <= NumRows-1) { int x = cd.xorg + half; int z = cd.zorg + whole; int idx = x + z * RowSize; bool different_terrains = false; terrain_error = 0.f; if (x < RowSize && z < NumRows) { if (x < RowSize - 1) { if (Terrain[idx] != Terrain[idx+1]) { different_terrains = true; } } if (z >= 1) { idx -= RowSize; if (Terrain[idx] != Terrain[idx+1]) { different_terrains = true; } } if (different_terrains) { ForceSouthVert = true; terrain_error = TERRAIN_ERROR_SCALE * whole * whole; } else { ForceSouthVert = false; } if (terrain_error > Error[0]) { Error[0] = terrain_error; } if (Error[0] > maxerror) { maxerror = Error[0]; } } x = cd.xorg + whole; z = cd.zorg + half; idx = x + z * RowSize; terrain_error = 0; different_terrains = false; if (x < RowSize && z < NumRows) { if (z >= 1) { if (Terrain[idx] != Terrain[idx-RowSize]) { different_terrains = true; } } if (z >= 1 && x < RowSize - 1) { idx += 1; if (Terrain[idx] != Terrain[idx-RowSize]) { different_terrains = true; } } if (different_terrains) { ForceEastVert = true; terrain_error = TERRAIN_ERROR_SCALE * whole * whole; } else { ForceEastVert = false; } if (terrain_error > Error[1]) { Error[1] = terrain_error; } if (Error[1] > maxerror) { maxerror = Error[1]; } } } for (int i = 0; i < 4; i++) { float y = Vertex[1 + i].Y; if (y < MinY) MinY = y; if (y > MaxY) MaxY = y; } for (int i = 0; i < 4; i++) { quadcornerdata q; if (Child[i]) { SetupCornerData(&q, cd, i); Error[i+2] = Child[i]->RecomputeError(q); if (Child[i]->MinY < MinY) MinY = Child[i]->MinY; if (Child[i]->MaxY > MaxY) MaxY = Child[i]->MaxY; } else { Error[i+2] = fabs((Vertex[0].Y + cd.Verts[i].Y) - (Vertex[i+1].Y + Vertex[((i+1)&3) + 1].Y)) * 0.25; } if (Error[i+2] > maxerror) maxerror = Error[i+2]; } int *terrain_count = new int[numTerr]; memset(terrain_count, 0, sizeof(*terrain_count)*numTerr); for (int i=cd.xorg; i<=cd.xorg+whole; i++) { for (int j=cd.zorg; j<=cd.zorg+whole; j++) { if (i < 0 || i >= RowSize || j < 0 || j >= NumRows) { continue; } int terrain = (int) Terrain[i + RowSize*j]; terrain_count[ terrain ] += 1; } } int max_count = 0; int total = 0; for (size_t t=0; t max_count) { max_count = terrain_count[t]; } total += terrain_count[t]; } delete [] terrain_count; if (total > 0) { terrain_error = (1.0 - max_count / total); if (numTerr > 1) { terrain_error *= numTerr / (numTerr - 1.0); } } else terrain_error = 0; terrain_error *= whole * whole; terrain_error *= TERRAIN_ERROR_SCALE; if (terrain_error > maxerror) maxerror = terrain_error; if (terrain_error > Error[0]) Error[0] = terrain_error; if (terrain_error > Error[1]) Error[1] = terrain_error; Dirty = false; return maxerror; } void quadsquare::ResetTree() { for (int i = 0; i < 4; i++) { if (Child[i]) { Child[i]->ResetTree(); if (Child[i]->Static == false) { delete Child[i]; Child[i] = 0; } } } EnabledFlags = 0; SubEnabledCount[0] = 0; SubEnabledCount[1] = 0; Dirty = true; } void quadsquare::StaticCullData(const quadcornerdata& cd, float ThresholdDetail) { ResetTree(); if (Dirty) RecomputeError(cd); for (int level = 0; level <= cd.Level; level++) { StaticCullAux(cd, ThresholdDetail, level); } } void quadsquare::StaticCullAux(const quadcornerdata& cd, float ThresholdDetail, int TargetLevel) { quadcornerdata q; if (cd.Level > TargetLevel) { for (int j = 0; j < 4; j++) { int i; if (j < 2) i = 1 - j; else i = j; if (Child[i]) { SetupCornerData(&q, cd, i); Child[i]->StaticCullAux(q, ThresholdDetail, TargetLevel); } } return; } float size = 2 << cd.Level; // Edge length. if (Child[0] == NULL && Child[3] == NULL && Error[0] * ThresholdDetail < size) { quadsquare* s = GetNeighbor(0, cd); if (s == NULL || (s->Child[1] == NULL && s->Child[2] == NULL)) { float y = (cd.Verts[0].Y + cd.Verts[3].Y) * 0.5; Vertex[1].Y = y; Error[0] = 0; if (s) s->Vertex[3].Y = y; Dirty = true; } } if (Child[2] == NULL && Child[3] == NULL && Error[1] * ThresholdDetail < size) { quadsquare* s = GetNeighbor(3, cd); if (s == NULL || (s->Child[0] == NULL && s->Child[1] == NULL)) { float y = (cd.Verts[2].Y + cd.Verts[3].Y) * 0.5; Vertex[4].Y = y; Error[1] = 0; if (s) s->Vertex[2].Y = y; Dirty = true; } } bool StaticChildren = false; for (int i = 0; i < 4; i++) { if (Child[i]) { StaticChildren = true; if (Child[i]->Dirty) Dirty = true; } } if (StaticChildren == false && cd.Parent != NULL) { bool NecessaryEdges = false; for (int i = 0; i < 4; i++) { float diff = fabs(Vertex[i+1].Y - (cd.Verts[i].Y + cd.Verts[(i+3)&3].Y) * 0.5); if (diff > 0.00001) { NecessaryEdges = true; } } if (!NecessaryEdges) { size *= 1.414213562; if (cd.Parent->Square->Error[2 + cd.ChildIndex] * ThresholdDetail < size) { delete cd.Parent->Square->Child[cd.ChildIndex]; cd.Parent->Square->Child[cd.ChildIndex] = 0; } } } } void quadsquare::EnableEdgeVertex(int index, bool IncrementCount, const quadcornerdata& cd) { int ct = 0; int stack[32]; if ((EnabledFlags & (1 << index)) && IncrementCount == false) return; EnabledFlags |= 1 << index; if (IncrementCount == true && (index == 0 || index == 3)) { SubEnabledCount[index & 1]++; } quadsquare* p = this; const quadcornerdata* pcd = &cd; for (;;) { int ci = pcd->ChildIndex; if (pcd->Parent == NULL || pcd->Parent->Square == NULL) return; p = pcd->Parent->Square; pcd = pcd->Parent; bool SameParent = ((index - ci) & 2) ? true : false; ci = ci ^ 1 ^ ((index & 1) << 1); stack[ct] = ci; ct++; if (SameParent) break; } p = p->EnableDescendant(ct, stack, *pcd); index ^= 2; p->EnabledFlags |= (1 << index); if (IncrementCount == true && (index == 0 || index == 3)) { p->SubEnabledCount[index & 1]++; } } quadsquare* quadsquare::EnableDescendant(int count, int path[], const quadcornerdata& cd) { count--; int ChildIndex = path[count]; if ((EnabledFlags & (16 << ChildIndex)) == 0) { EnableChild(ChildIndex, cd); } if (count > 0) { quadcornerdata q; SetupCornerData(&q, cd, ChildIndex); return Child[ChildIndex]->EnableDescendant(count, path, q); } else { return Child[ChildIndex]; } } void quadsquare::CreateChild(int index, const quadcornerdata& cd) { if (Child[index] == 0) { quadcornerdata q; SetupCornerData(&q, cd, index); Child[index] = new quadsquare(&q); } } void quadsquare::EnableChild(int index, const quadcornerdata& cd) { if ((EnabledFlags & (16 << index)) == 0) { EnabledFlags |= (16 << index); EnableEdgeVertex(index, true, cd); EnableEdgeVertex((index + 1) & 3, true, cd); if (Child[index] == 0) { CreateChild(index, cd); } } } void quadsquare::NotifyChildDisable(const quadcornerdata& cd, int index) { EnabledFlags &= ~(16 << index); quadsquare* s; if (index & 2) s = this; else s = GetNeighbor(1, cd); if (s) { s->SubEnabledCount[1]--; } if (index == 1 || index == 2) s = GetNeighbor(2, cd); else s = this; if (s) { s->SubEnabledCount[0]--; } } static float DetailThreshold = 100; bool quadsquare::VertexTest(int x, float y, int z, float error, const float Viewer[3], int level, vertex_loc_t vertex_loc) { float dx = fabs(x - Viewer[0]) * fabs(ScaleX); float dy = fabs(y - Viewer[1]); float dz = fabs(z - Viewer[2]) * fabs(ScaleZ); float d = max(dx, max(dy, dz)); if (vertex_loc == South && ForceSouthVert && d < VERTEX_FORCE_THRESHOLD) { return true; } if (vertex_loc == East && ForceEastVert && d < VERTEX_FORCE_THRESHOLD) { return true; } if (d < ERROR_MAGNIFICATION_THRESHOLD) { error *= ERROR_MAGNIFICATION_AMOUNT; } return error * DetailThreshold > d; } bool quadsquare::BoxTest(int x, int z, float size, float miny, float maxy, float error, const float Viewer[3]) { float half = size * 0.5; float dx = (fabs(x + half - Viewer[0]) - half) * fabs(ScaleX); float dy = fabs((miny + maxy) * 0.5 - Viewer[1]) - (maxy - miny) * 0.5; float dz = (fabs(z + half - Viewer[2]) - half) * fabs(ScaleZ); float d = max(dx, max(dy , dz)); if (d < ERROR_MAGNIFICATION_THRESHOLD) { error *= ERROR_MAGNIFICATION_AMOUNT; } if (error * DetailThreshold > d) { return true; } if ((x < RowSize-1 && x+size >= RowSize) || (z < NumRows-1 && z+size >= NumRows)) { return true; } return false; } void quadsquare::Update(const quadcornerdata& cd, const TVector3d& ViewerLocation, float Detail) { float Viewer[3]; DetailThreshold = Detail; Viewer[0] = ViewerLocation.x / ScaleX; Viewer[1] = ViewerLocation.y; Viewer[2] = ViewerLocation.z / ScaleZ; UpdateAux(cd, Viewer, 0, SomeClip); } void quadsquare::UpdateAux(const quadcornerdata& cd, const float ViewerLocation[3], float CenterError, clip_result_t vis) { if (vis != NoClip) { vis = ClipSquare(cd); if (vis == NotVisible) { return; } } if (Dirty) { RecomputeError(cd); } int half = 1 << cd.Level; int whole = half << 1; if ((EnabledFlags & 1) == 0 && VertexTest(cd.xorg + whole, Vertex[1].Y, cd.zorg + half, Error[0], ViewerLocation, cd.Level, East) == true) { EnableEdgeVertex(0, false, cd); } if ((EnabledFlags & 8) == 0 && VertexTest(cd.xorg + half, Vertex[4].Y, cd.zorg + whole, Error[1], ViewerLocation, cd.Level, South) == true) { EnableEdgeVertex(3, false, cd); } if (cd.Level > 0) { if ((EnabledFlags & 32) == 0) { if (BoxTest(cd.xorg, cd.zorg, half, MinY, MaxY, Error[3], ViewerLocation) == true) EnableChild(1, cd); } if ((EnabledFlags & 16) == 0) { if (BoxTest(cd.xorg + half, cd.zorg, half, MinY, MaxY, Error[2], ViewerLocation) == true) EnableChild(0, cd); } if ((EnabledFlags & 64) == 0) { if (BoxTest(cd.xorg, cd.zorg + half, half, MinY, MaxY, Error[4], ViewerLocation) == true) EnableChild(2, cd); } if ((EnabledFlags & 128) == 0) { if (BoxTest(cd.xorg + half, cd.zorg + half, half, MinY, MaxY, Error[5], ViewerLocation) == true) EnableChild(3, cd); } quadcornerdata q; if (EnabledFlags & 32) { SetupCornerData(&q, cd, 1); Child[1]->UpdateAux(q, ViewerLocation, Error[3], vis); } if (EnabledFlags & 16) { SetupCornerData(&q, cd, 0); Child[0]->UpdateAux(q, ViewerLocation, Error[2], vis); } if (EnabledFlags & 64) { SetupCornerData(&q, cd, 2); Child[2]->UpdateAux(q, ViewerLocation, Error[4], vis); } if (EnabledFlags & 128) { SetupCornerData(&q, cd, 3); Child[3]->UpdateAux(q, ViewerLocation, Error[5], vis); } } if ((EnabledFlags & 1) && SubEnabledCount[0] == 0 && VertexTest(cd.xorg + whole, Vertex[1].Y, cd.zorg + half, Error[0], ViewerLocation, cd.Level, East) == false) { EnabledFlags &= ~1; quadsquare* s = GetNeighbor(0, cd); if (s) s->EnabledFlags &= ~4; } if ((EnabledFlags & 8) && SubEnabledCount[1] == 0 && VertexTest(cd.xorg + half, Vertex[4].Y, cd.zorg + whole, Error[1], ViewerLocation, cd.Level, South) == false) { EnabledFlags &= ~8; quadsquare* s = GetNeighbor(3, cd); if (s) s->EnabledFlags &= ~2; } if (EnabledFlags == 0 && cd.Parent != NULL && BoxTest(cd.xorg, cd.zorg, whole, MinY, MaxY, CenterError, ViewerLocation) == false) { cd.Parent->Square->NotifyChildDisable(*cd.Parent, cd.ChildIndex); } } GLuint VertexIndices[9]; int VertexTerrains[9]; void quadsquare::InitVert(int i, int x, int z) { if (x >= RowSize) x = RowSize-1; if (z >= NumRows) z = NumRows - 1; int idx = x + RowSize * z; VertexIndices[i] = idx; VertexTerrains[i] = Terrain[idx]; } GLubyte *VNCArray; void quadsquare::DrawTris() { int tmp_min_idx = VertexArrayMinIdx; if (glLockArraysEXT_p) { if (tmp_min_idx == 0) tmp_min_idx = 1; glLockArraysEXT_p(tmp_min_idx, VertexArrayMaxIdx - tmp_min_idx + 1); } glDrawElements(GL_TRIANGLES, VertexArrayCounter, GL_UNSIGNED_INT, VertexArrayIndices); if (glUnlockArraysEXT_p) glUnlockArraysEXT_p(); } void quadsquare::InitArrayCounters() { VertexArrayCounter = 0; VertexArrayMinIdx = INT_MAX; VertexArrayMaxIdx = 0; } void quadsquare::Render(const quadcornerdata& cd, GLubyte *vnc_array) { VNCArray = vnc_array; bool fog_on; int nx, ny; Course.GetDivisions(&nx, &ny); const TTerrType *TerrList = &Course.TerrList[0]; size_t numTerrains = Course.TerrList.size(); // fog_on = is_fog_on (); fog_on = true; for (size_t j=0; jBind(); DrawTris(); } } if (param.perf_level > 1) { InitArrayCounters(); RenderAux(cd, SomeClip, -1); if (VertexArrayCounter != 0) { glDisable(GL_FOG); for (GLuint i=0; iBind(); DrawTris(); if (fog_on) glEnable(GL_FOG); glBlendFunc(GL_SRC_ALPHA, GL_ONE); for (GLuint i=0; i 0) { Course.TerrList[j].texture->Bind(); for (GLuint i=0; i= RowSize-1) { return NotVisible; } if (cd.zorg >= NumRows-1) { return NotVisible; } int whole = 2 << cd.Level; TVector3d min, max; min.x = cd.xorg * ScaleX; min.y = MinY; min.z = cd.zorg * ScaleZ; max.x = (cd.xorg + whole) * ScaleX; max.y = MaxY; max.z = (cd.zorg + whole) * ScaleZ; if (min.x > max.x) { double tmp = min.x; min.x = max.x; max.x = tmp; } if (min.z > max.z) { double tmp = min.z; min.z = max.z; max.z = tmp; } clip_result_t clip_result = clip_aabb_to_view_frustum(min, max); if (clip_result == NotVisible || clip_result == SomeClip) { return clip_result; } if (cd.xorg + whole >= RowSize) { return SomeClip; } if (cd.zorg + whole >= NumRows) { return SomeClip; } return clip_result; } inline void quadsquare::MakeTri(int a, int b, int c, int terrain) { if ((VertexTerrains[a] == terrain || VertexTerrains[b] == terrain || VertexTerrains[c] == terrain)) { VertexArrayIndices[VertexArrayCounter++] = VertexIndices[a]; setalphaval(a); update_min_max(VertexIndices[a]); VertexArrayIndices[VertexArrayCounter++] = VertexIndices[b]; setalphaval(b); update_min_max(VertexIndices[b]); VertexArrayIndices[VertexArrayCounter++] = VertexIndices[c]; setalphaval(c); update_min_max(VertexIndices[c]); } } inline void quadsquare::MakeSpecialTri(int a, int b, int c, int terrain) { if (VertexTerrains[a] != VertexTerrains[b] && VertexTerrains[a] != VertexTerrains[c] && VertexTerrains[b] != VertexTerrains[c]) { VertexArrayIndices[VertexArrayCounter++] = VertexIndices[a]; update_min_max(VertexIndices[a]); VertexArrayIndices[VertexArrayCounter++] = VertexIndices[b]; update_min_max(VertexIndices[b]); VertexArrayIndices[VertexArrayCounter++] = VertexIndices[c]; update_min_max(VertexIndices[c]); } } inline void quadsquare::MakeNoBlendTri(int a, int b, int c, int terrain) { if ((VertexTerrains[a] == terrain || VertexTerrains[b] == terrain || VertexTerrains[c] == terrain) && (VertexTerrains[a] >= terrain && VertexTerrains[b] >= terrain && VertexTerrains[c] >= terrain)) { VertexArrayIndices[VertexArrayCounter++] = VertexIndices[a]; setalphaval(a); update_min_max(VertexIndices[a]); VertexArrayIndices[VertexArrayCounter++] = VertexIndices[b]; setalphaval(b); update_min_max(VertexIndices[b]); VertexArrayIndices[VertexArrayCounter++] = VertexIndices[c]; setalphaval(c); update_min_max(VertexIndices[c]); } } void quadsquare::RenderAux(const quadcornerdata& cd, clip_result_t vis, int terrain) { int half = 1 << cd.Level; int whole = 2 << cd.Level; if (vis != NoClip) { vis = ClipSquare(cd); if (vis == NotVisible) return; } int flags = 0; int mask = 1; quadcornerdata q; for (int i = 0; i < 4; i++, mask <<= 1) { if (EnabledFlags & (16 << i)) { SetupCornerData(&q, cd, i); Child[i]->RenderAux(q, vis, terrain); } else { flags |= mask; } } if (flags == 0) return; InitVert(0, cd.xorg + half, cd.zorg + half); InitVert(1, cd.xorg + whole, cd.zorg + half); InitVert(2, cd.xorg + whole, cd.zorg); InitVert(3, cd.xorg + half, cd.zorg); InitVert(4, cd.xorg, cd.zorg); InitVert(5, cd.xorg, cd.zorg + half); InitVert(6, cd.xorg, cd.zorg + whole); InitVert(7, cd.xorg + half, cd.zorg + whole); InitVert(8, cd.xorg + whole, cd.zorg + whole); if (terrain == -1) { make_tri_list(MakeSpecialTri, EnabledFlags, flags, terrain); } else if (param.perf_level > 1) { make_tri_list(MakeTri, EnabledFlags, flags, terrain); } else { make_tri_list(MakeNoBlendTri, EnabledFlags, flags, terrain); } } void quadsquare::SetupCornerData(quadcornerdata* q, const quadcornerdata& cd, int ChildIndex) { int half = 1 << cd.Level; q->Parent = &cd; q->Square = Child[ChildIndex]; q->Level = cd.Level - 1; q->ChildIndex = ChildIndex; switch (ChildIndex) { default: case 0: q->xorg = cd.xorg + half; q->zorg = cd.zorg; q->Verts[0] = cd.Verts[0]; q->Verts[1] = Vertex[2]; q->Verts[2] = Vertex[0]; q->Verts[3] = Vertex[1]; break; case 1: q->xorg = cd.xorg; q->zorg = cd.zorg; q->Verts[0] = Vertex[2]; q->Verts[1] = cd.Verts[1]; q->Verts[2] = Vertex[3]; q->Verts[3] = Vertex[0]; break; case 2: q->xorg = cd.xorg; q->zorg = cd.zorg + half; q->Verts[0] = Vertex[0]; q->Verts[1] = Vertex[3]; q->Verts[2] = cd.Verts[2]; q->Verts[3] = Vertex[4]; break; case 3: q->xorg = cd.xorg + half; q->zorg = cd.zorg + half; q->Verts[0] = Vertex[1]; q->Verts[1] = Vertex[0]; q->Verts[2] = Vertex[4]; q->Verts[3] = cd.Verts[3]; break; } } int quadsquare::RowSize; int quadsquare::NumRows; void quadsquare::AddHeightMap(const quadcornerdata& cd, const HeightMapInfo& hm) { RowSize = hm.RowWidth; NumRows = hm.ZSize; if (cd.Parent == NULL) { if (VertexArrayIndices != NULL) { delete VertexArrayIndices; } VertexArrayIndices = new GLuint[6 * RowSize * NumRows]; } int BlockSize = 2 << cd.Level; if (cd.xorg > hm.XOrigin + ((hm.XSize + 2) << hm.Scale) || cd.xorg + BlockSize < hm.XOrigin - (1 << hm.Scale) || cd.zorg > hm.ZOrigin + ((hm.ZSize + 2) << hm.Scale) || cd.zorg + BlockSize < hm.ZOrigin - (1 << hm.Scale)) { return; } if (cd.Parent && cd.Parent->Square) { cd.Parent->Square->EnableChild(cd.ChildIndex, *cd.Parent); } int half = 1 << cd.Level; for (int i = 0; i < 4; i++) { quadcornerdata q; SetupCornerData(&q, cd, i); if (Child[i] == NULL && cd.Level > hm.Scale) { Child[i] = new quadsquare(&q); } if (Child[i]) { Child[i]->AddHeightMap(q, hm); } } float s[5]; s[0] = hm.Sample(cd.xorg + half, cd.zorg + half); s[1] = hm.Sample(cd.xorg + half*2, cd.zorg + half); s[2] = hm.Sample(cd.xorg + half, cd.zorg); s[3] = hm.Sample(cd.xorg, cd.zorg + half); s[4] = hm.Sample(cd.xorg + half, cd.zorg + half*2); for (int i = 0; i < 5; i++) { if (s[i] != 0) { Dirty = true; Vertex[i].Y += s[i]; } } if (!Dirty) { for (int i = 0; i < 4; i++) { if (Child[i] && Child[i]->Dirty) { Dirty = true; break; } } } if (Dirty) SetStatic(cd); } double quadsquare::ScaleX; double quadsquare::ScaleZ; void quadsquare::SetScale(double x, double z) { ScaleX = x; ScaleZ = z; } char* quadsquare::Terrain; void quadsquare::SetTerrain(char *t) { Terrain = t; } float HeightMapInfo::Sample(int x, int z) const { if (x >= XSize) { x = XSize - 1; } if (z >= ZSize) { z = ZSize - 1; } return Data[ x + z * RowWidth ]; } // -------------------------------------------------------------------- // global calls // -------------------------------------------------------------------- #define CULL_DETAIL_FACTOR 25 static quadsquare *root = (quadsquare*) NULL; static quadcornerdata root_corner_data = {(quadcornerdata*)NULL }; void ResetQuadtree() { if (root != NULL) { delete root; root = (quadsquare*) NULL; } } static int get_root_level(int nx, int nz) { int xlev = (int)(log(static_cast(nx)) / log(2.0)); int zlev = (int)(log(static_cast(nz)) / log(2.0)); return max(xlev, zlev); } void InitQuadtree(double *elevation, int nx, int nz, double scalex, double scalez, const TVector3d& view_pos, double detail) { HeightMapInfo hm; hm.Data = elevation; hm.XOrigin = 0; hm.ZOrigin = 0; hm.XSize = nx; hm.ZSize = nz; hm.RowWidth = hm.XSize; hm.Scale = 0; root_corner_data.Square = (quadsquare*)NULL; root_corner_data.ChildIndex = 0; root_corner_data.Level = get_root_level(nx, nz); root_corner_data.xorg = 0; root_corner_data.zorg = 0; for (int i=0; i<4; i++) { root_corner_data.Verts[i].Y = 0; root_corner_data.Verts[i].Y = 0; } root = new quadsquare(&root_corner_data); root->AddHeightMap(root_corner_data, hm); root->SetScale(scalex, scalez); root->SetTerrain(Course.terrain); root->StaticCullData(root_corner_data, CULL_DETAIL_FACTOR); for (int i = 0; i < 10; i++) { root->Update(root_corner_data, view_pos, detail); } } void UpdateQuadtree(const TVector3d& view_pos, float detail) { root->Update(root_corner_data, view_pos, detail); } void RenderQuadtree() { GLubyte *vnc_array = Course.GetGLArrays(); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, STRIDE_GL_ARRAY, vnc_array); glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT, STRIDE_GL_ARRAY, vnc_array + 4 * sizeof(GLfloat)); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_UNSIGNED_BYTE, STRIDE_GL_ARRAY, vnc_array + 8 * sizeof(GLfloat)); root->Render(root_corner_data, vnc_array); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); }