// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "../zone_lib/zone_utility.h" // #include "nel/misc/types_nl.h" #include "nel/misc/path.h" #include "nel/misc/file.h" #include "nel/misc/aabbox.h" // #include "nel/3d/quad_grid.h" #include "nel/3d/bezier_patch.h" #include "nel/3d/zone.h" // #include <vector> #include <algorithm> #include <memory> #include <set> using namespace NLMISC; using namespace NL3D; // a patch vertex info, for quadtree insertion struct CPatchVertexInfo { uint ZoneIndex; // this zone index, 0 for the midlle zone, and from 1 to 8 for the zones around uint PatchIndex; // the Patch of this zone uint PatchVertex; // the vertex of thi patch 0..3 CVector Pos; CPatchVertexInfo() {} CPatchVertexInfo(uint zoneIndex, uint patchIndex, uint patchVertex, const CVector &pos ) : ZoneIndex(zoneIndex), PatchIndex(patchIndex), PatchVertex(patchVertex), Pos(pos) { } }; typedef std::vector<CPatchVertexInfo *> TPVVect; typedef CQuadGrid<CPatchVertexInfo> TPVQuadGrid; // *************************************************************************** void bind_1_1 (std::vector<CPatchInfo> &zoneInfos, uint patch0, uint edge0, uint patch1, uint edge1, uint zoneId) { // Bind type zoneInfos[patch0].BindEdges[edge0].NPatchs = 1; zoneInfos[patch1].BindEdges[edge1].NPatchs = 1; // Zone ID zoneInfos[patch0].BindEdges[edge0].ZoneId = zoneId; zoneInfos[patch1].BindEdges[edge1].ZoneId = zoneId; // Next zoneInfos[patch0].BindEdges[edge0].Next[0] = patch1; zoneInfos[patch1].BindEdges[edge1].Next[0] = patch0; // Edge zoneInfos[patch0].BindEdges[edge0].Edge[0] = edge1; zoneInfos[patch1].BindEdges[edge1].Edge[0] = edge0; } // *************************************************************************** void bind_1_2 (std::vector<CPatchInfo> &zoneInfos, uint patch, uint edge, uint patch0, uint edge0, uint patch1, uint edge1, uint zoneId) { // Bind type zoneInfos[patch].BindEdges[edge].NPatchs = 2; zoneInfos[patch0].BindEdges[edge0].NPatchs = 5; zoneInfos[patch1].BindEdges[edge1].NPatchs = 5; // Zone ID zoneInfos[patch].BindEdges[edge].ZoneId = zoneId; zoneInfos[patch0].BindEdges[edge0].ZoneId = zoneId; zoneInfos[patch1].BindEdges[edge1].ZoneId = zoneId; // Next zoneInfos[patch].BindEdges[edge].Next[0] = patch0; zoneInfos[patch].BindEdges[edge].Next[1] = patch1; zoneInfos[patch0].BindEdges[edge0].Next[0] = patch; zoneInfos[patch1].BindEdges[edge1].Next[0] = patch; // Edge zoneInfos[patch].BindEdges[edge].Edge[0] = edge0; zoneInfos[patch].BindEdges[edge].Edge[1] = edge1; zoneInfos[patch0].BindEdges[edge0].Edge[0] = edge; zoneInfos[patch1].BindEdges[edge1].Edge[0] = edge; } // *************************************************************************** void bind_1_4 (std::vector<CPatchInfo> &zoneInfos, uint patch, uint edge, uint patch0, uint edge0, uint patch1, uint edge1, uint patch2, uint edge2, uint patch3, uint edge3, uint zoneId) { // Bind type zoneInfos[patch].BindEdges[edge].NPatchs = 4; zoneInfos[patch0].BindEdges[edge0].NPatchs = 5; zoneInfos[patch1].BindEdges[edge1].NPatchs = 5; zoneInfos[patch2].BindEdges[edge2].NPatchs = 5; zoneInfos[patch3].BindEdges[edge3].NPatchs = 5; // Zone ID zoneInfos[patch].BindEdges[edge].ZoneId = zoneId; zoneInfos[patch0].BindEdges[edge0].ZoneId = zoneId; zoneInfos[patch1].BindEdges[edge1].ZoneId = zoneId; zoneInfos[patch2].BindEdges[edge2].ZoneId = zoneId; zoneInfos[patch3].BindEdges[edge3].ZoneId = zoneId; // Next zoneInfos[patch].BindEdges[edge].Next[0] = patch0; zoneInfos[patch].BindEdges[edge].Next[1] = patch1; zoneInfos[patch].BindEdges[edge].Next[2] = patch2; zoneInfos[patch].BindEdges[edge].Next[3] = patch3; zoneInfos[patch0].BindEdges[edge0].Next[0] = patch; zoneInfos[patch1].BindEdges[edge1].Next[0] = patch; zoneInfos[patch2].BindEdges[edge2].Next[0] = patch; zoneInfos[patch3].BindEdges[edge3].Next[0] = patch; // Edge zoneInfos[patch].BindEdges[edge].Edge[0] = edge0; zoneInfos[patch].BindEdges[edge].Edge[1] = edge1; zoneInfos[patch].BindEdges[edge].Edge[2] = edge2; zoneInfos[patch].BindEdges[edge].Edge[3] = edge3; zoneInfos[patch0].BindEdges[edge0].Edge[0] = edge; zoneInfos[patch1].BindEdges[edge1].Edge[0] = edge; zoneInfos[patch2].BindEdges[edge2].Edge[0] = edge; zoneInfos[patch3].BindEdges[edge3].Edge[0] = edge; } // *************************************************************************** /** Test whether 2 vertices could be welded */ static inline bool CanWeld(const CVector &v1, const CVector &v2, float weldThreshold) { return (v1 - v2).norm() < weldThreshold; } // *************************************************************************** uint getOtherCountAndPos (const std::vector<CPatchInfo> &zoneInfo, uint patch, uint edge, uint &otherPos) { // Must be a multiple patch bind if (zoneInfo[patch].BindEdges[edge].NPatchs == 5) { uint i; const CPatchInfo &otherPatchRef = zoneInfo[zoneInfo[patch].BindEdges[edge].Next[0]]; uint otherEdge = zoneInfo[patch].BindEdges[edge].Edge[0]; for (i=0; i<otherPatchRef.BindEdges[otherEdge].NPatchs; i++) { if ( (otherPatchRef.BindEdges[otherEdge].Next[i] == patch) && (otherPatchRef.BindEdges[otherEdge].Edge[i] == edge) ) { otherPos = i; return otherPatchRef.BindEdges[otherEdge].NPatchs; } } } return 1; } // *************************************************************************** /** Get all vertices that are near the given one */ static void GetCandidateVertices(const CVector &pos, TPVQuadGrid &qg, TPVVect &dest, uint patchToExclude, uint patchToExcludeZone, float weldThreshold, bool exclude ) { dest.clear(); const CVector half(weldThreshold, weldThreshold, weldThreshold); float weldThresholdSrt = weldThreshold * weldThreshold; qg.select(pos - half, pos + half); for (TPVQuadGrid::CIterator it = qg.begin(); it != qg.end(); ++it) { if ( ::CanWeld((*it).Pos, pos, weldThreshold) ) { if ( (!exclude) || (! ((*it).ZoneIndex == patchToExcludeZone && (*it).PatchIndex == patchToExclude) ) ) { // Final distance test if ( (pos - (*it).Pos).sqrnorm () <= weldThresholdSrt ) dest.push_back(&(*it)); } } } } // *************************************************************************** bool isBinded (std::vector<CPatchInfo> &zoneInfos, uint patch0, uint edge0, uint patch1, uint edge1) { // Binded ? if ( (zoneInfos[patch0].BindEdges[edge0].NPatchs != 0) && (zoneInfos[patch1].BindEdges[edge1].NPatchs != 0) ) { // Binded ? return (zoneInfos[patch0].BindEdges[edge0].Next[0] == patch1) || (zoneInfos[patch1].BindEdges[edge1].Next[0] == patch0); } return false; } // *************************************************************************** CVector evalPatchEdge (CPatchInfo &patch, uint edge, float lambda) { // index of this border vertices static const float indexToST[][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; const uint vIndex[] = { edge, (edge + 1) & 0x03 }; return patch.Patch.eval((1.f - lambda) * indexToST[vIndex[0]][0] + lambda * indexToST[vIndex[1]][0], (1.f - lambda) * indexToST[vIndex[0]][1] + lambda * indexToST[vIndex[1]][1]); } // *************************************************************************** void getFirst (CVector &a, CVector &b, CVector &c, CVector &d) { // ab CVector ab = (a+b)/2.f; // bc CVector bc = (b+c)/2.f; // cd CVector cd = (c+d)/2.f; b = ab; c = (ab + bc) / 2.f; d = ( (bc + cd) / 2.f + c ) / 2.f; } // *************************************************************************** void getSecond (CVector &a, CVector &b, CVector &c, CVector &d) { // ab CVector ab = (a+b)/2.f; // bc CVector bc = (b+c)/2.f; // cd CVector cd = (c+d)/2.f; c = cd; b = (bc + cd) / 2.f; a = ( (ab + bc) / 2.f + b ) / 2.f; } // *************************************************************************** void CleanZone ( std::vector<CPatchInfo> &zoneInfos, uint zoneId, const CAABBoxExt &zoneBBox, float weldThreshold) { uint l, m, n, p, q; // some loop counters /////////////////////////////// // retrieve datas from zones // /////////////////////////////// // fill the quad grid float zoneSize = 2.f * weldThreshold + std::max(zoneBBox.getMax().x - zoneBBox.getMin().x, zoneBBox.getMax().y - zoneBBox.getMin().y); TPVQuadGrid qg; const uint numQGElt = 128; qg.create (numQGElt, zoneSize / numQGElt); for (l = 0; l < zoneInfos.size(); ++l) { CPatchInfo &patch = zoneInfos[l]; // for each base vertex of the patch for (m = 0; m < 4; ++m) { CVector &pos = patch.Patch.Vertices[m]; // yes, insert it in the tree const CVector half(weldThreshold, weldThreshold, weldThreshold); qg.insert(pos - half, pos + half, CPatchVertexInfo(zoneId, l, m, pos)); } } ///////////////////////////////////////////////// // check whether each patch is correctly bound // ///////////////////////////////////////////////// uint pass = 0; while (1) { uint bind1Count = 0; uint bind2Count = 0; uint bind4Count = 0; for (l = 0; l < zoneInfos.size(); ++l) { // Ref on patch CPatchInfo &patch = zoneInfos[l]; // deals with each border for (m = 0; m < 4; ++m) { const uint vIndex[] = { m, (m + 1) & 0x03 }; // if this border is said to be bound, no need to test.. if (patch.BindEdges[m].NPatchs == 0) { static TPVVect verts[2]; // Get vertices from other patch that could be welded with this patch boder's vertices. for (q = 0; q < 2; ++q) { ::GetCandidateVertices(patch.Patch.Vertices[vIndex[q]], qg, verts[q], l, zoneId, weldThreshold, true); } uint bindCount; for (bindCount = 1; bindCount<5; bindCount<<=1) { // Float middle float middle = 1.f / (float)bindCount; // 0 = 0.5; 1 = 0.25 // Try to find a patch that shares 2 consecutives vertices static TPVVect binded4[5]; binded4[0] = verts[0]; binded4[bindCount] = verts[1]; // Compute points along the border and found list of vertex binded to it. float lambda = middle; for (n = 1; n <bindCount; ++n) { CVector borderPos = evalPatchEdge (patch, m, lambda); ::GetCandidateVertices(borderPos, qg, binded4[n], l, zoneId, weldThreshold, true); lambda += middle; } // Binded patches and edges uint neighborPatches[4]; uint neighborEdges[4]; // Patch binded for (q = 0; q < bindCount; q++) { for (n = 0; n < binded4[q].size(); n++) { for (p = 0; p < binded4[q+1].size(); p++) { // Ref on the two patches const CPatchVertexInfo &pv0 = *(binded4[q][n]); const CPatchVertexInfo &pv1 = *(binded4[q+1][p]); // Direct or indirect ? // Vertex id uint v0 = pv0.PatchVertex; uint v1 = pv1.PatchVertex; if ( pv0.ZoneIndex == pv1.ZoneIndex && pv0.PatchIndex == pv1.PatchIndex ) { // Direct edge ? if ( ( ( pv0.PatchVertex - pv1.PatchVertex) & 3 ) == 1 ) { // Patch id uint patchId2 = pv0.PatchIndex; // Edge id uint edge = v1; // Edge not binded ? if (zoneInfos[patchId2].BindEdges[edge].NPatchs == 0) { // Save the patch neighborPatches[q] = patchId2; neighborEdges[q] = edge; goto exit24; } } } } } if (n == binded4[q].size()) // No bind found, stop break; exit24:; } // Find all binded patches ? if (q == bindCount) { // Check The patches are binded together for (q=0; q<bindCount-1; q++) { if (!isBinded (zoneInfos, neighborPatches[q], (neighborEdges[q]-1)&3, neighborPatches[q+1], (neighborEdges[q+1]+1)&3)) break; } // Not breaked ? if (q == (bindCount-1) ) { // Bind it if (bindCount == 1) { bind_1_1 (zoneInfos, l, m, neighborPatches[0], neighborEdges[0], zoneId); bind1Count++; } else if (bindCount == 2) { bind_1_2 (zoneInfos, l, m, neighborPatches[0], neighborEdges[0], neighborPatches[1], neighborEdges[1], zoneId); bind2Count++; } else { bind_1_4 (zoneInfos, l, m, neighborPatches[0], neighborEdges[0], neighborPatches[1], neighborEdges[1], neighborPatches[2], neighborEdges[2], neighborPatches[3], neighborEdges[3], zoneId); bind4Count++; } // Exit connectivity loop break; } } } } } } // Print binds if (bind1Count || bind2Count || bind4Count) { printf ("Internal bind pass %d: ", pass); if (bind1Count) printf ("bind1-1 %d; \n", bind1Count); if (bind2Count) printf ("bind1-2 %d; \n", bind2Count); if (bind4Count) printf ("bind1-4 %d; \n", bind4Count); } else // No more bind, stop break; // Next pass pass++; } // Insert vertex binded in the map for (l = 0; l < zoneInfos.size(); ++l) { CPatchInfo &patch = zoneInfos[l]; // for each edge int edge; for (edge = 0; edge < 4; ++edge) { // Binded ? uint bindCount = patch.BindEdges[edge].NPatchs; if ( (bindCount == 2) || (bindCount == 4) ) { // Start float middle = 1.f / (float)bindCount; // 0 = 0.5; 1 = 0.25 float lambda = middle; // For all binded vertices uint vert; for (vert = 1; vert < bindCount; vert++) { // Eval the binded position CVector borderPos = evalPatchEdge (patch, edge, lambda); // yes, insert it in the tree const CVector half(weldThreshold, weldThreshold, weldThreshold); qg.insert (borderPos - half, borderPos + half, CPatchVertexInfo(zoneId, l, 5, borderPos)); // New position lambda += middle; } } } } // Weld all the vertices ! uint weldCount = 0; for (l = 0; l < zoneInfos.size(); ++l) { CPatchInfo &patch = zoneInfos[l]; // for each edge int vert; for (vert = 0; vert < 4; ++vert) { // Not on an opened edge ? if ( (patch.BindEdges[vert].NPatchs != 0) && (patch.BindEdges[(vert-1)&3].NPatchs != 0) ) { // Welded ? bool welded = false; // Get the vertex to weld static TPVVect toWeld; CVector pos = patch.Patch.Vertices[vert]; ::GetCandidateVertices (pos, qg, toWeld, l, zoneId, weldThreshold, false); // Weld it CVector average (0,0,0); uint w; bool absolutePosition = false; for (w = 0; w < toWeld.size (); w++) { // Welded vertex ? if (toWeld[w]->PatchVertex == 5) { absolutePosition = true; average = toWeld[w]->Pos; } // Add it; if (!absolutePosition) average += toWeld[w]->Pos; // Not the same ? float dist = (pos - toWeld[w]->Pos).norm(); if ( (pos - toWeld[w]->Pos).sqrnorm() > 0.0001 ) welded = true; } // Average if (!absolutePosition) average /= (float)toWeld.size (); // Weld ? if (welded) { // Welded weldCount++; // Set the pos for (w = 0; w < toWeld.size (); w++) { if (toWeld[w]->PatchVertex != 5) { toWeld[w]->Pos = average; zoneInfos[toWeld[w]->PatchIndex].Patch.Vertices[toWeld[w]->PatchVertex] = average; } } } } } } if (weldCount) printf ("Internal vertices welded: %d\n", weldCount); // Weld all the Tangents ! weldCount = 0; for (l = 0; l < zoneInfos.size(); ++l) { CPatchInfo &patch = zoneInfos[l]; // for each edge int edge; for (edge = 0; edge < 4; ++edge) { // Binded ? uint bindCount = patch.BindEdges[edge].NPatchs; if ( /*(bindCount == 1) || */(bindCount == 5) ) { // Neighbor patch uint otherPatch = patch.BindEdges[edge].Next[0]; uint otherEdge = patch.BindEdges[edge].Edge[0]; nlassert (otherPatch<zoneInfos.size ()); nlassert (otherEdge<4); // Get the vertices CVector A, B, C, D; A = zoneInfos[otherPatch].Patch.Vertices[otherEdge]; B = zoneInfos[otherPatch].Patch.Tangents[otherEdge*2]; C = zoneInfos[otherPatch].Patch.Tangents[otherEdge*2+1]; D = zoneInfos[otherPatch].Patch.Vertices[(otherEdge+1)&3]; // Pos uint otherPos; uint otherCount = getOtherCountAndPos (zoneInfos, l, edge, otherPos); nlassert ( ( (bindCount == 1) && (otherCount == 1) ) || ( (bindCount == 5) && ( (otherCount == 2) || (otherCount == 4) ) ) ); // Calc tangents if (otherCount == 2) { if (otherPos == 0) getFirst (A, B, C, D); else getSecond (A, B, C, D); } else if (otherCount == 4) { if (otherPos == 0) { getFirst (A, B, C, D); getFirst (A, B, C, D); } else if (otherPos == 1) { getFirst (A, B, C, D); getSecond (A, B, C, D); } else if (otherPos == 2) { getSecond (A, B, C, D); getFirst (A, B, C, D); } else if (otherPos == 3) { getSecond (A, B, C, D); getSecond (A, B, C, D); } } // 2 tangents uint tang; for (tang=0; tang<2; tang++) { nlassert (2*edge+tang < 8); // Eval the binded position const CVector &tangVect = (tang==0) ? C : B; // Next offset float dist = (patch.Patch.Tangents[2*edge+tang] - tangVect).norm(); if ( (patch.Patch.Tangents[2*edge+tang] - tangVect).sqrnorm() > 0.0001 ) weldCount++; // Fix it! if (bindCount == 1) { patch.Patch.Tangents[2*edge+tang] += tangVect; patch.Patch.Tangents[2*edge+tang] /= 2; } else patch.Patch.Tangents[2*edge+tang] = tangVect; } } } } if (weldCount) printf ("Internal tangents welded: %d\n", weldCount); }