khanat-opennel-code/code/nel/tools/3d/zone_welder/internal_weld.cpp

663 lines
18 KiB
C++

// 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);
}