// NeL - MMORPG Framework
// 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 .
#include "std3d.h"
#include "nel/3d/zone_tgt_smoother.h"
#include "nel/misc/plane.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
void CZoneTgtSmoother::makeVerticesCoplanar(std::vector &zones)
{
sint i,j, numZone;
sint centerZoneId;
nlassert(zones.size()>=1);
centerZoneId= zones[0].ZoneId;
// 0. CenterZone.
//===============
// First, make connectivity patch/vertex
for(i=0;i<(sint)zones[0].Patchs.size();i++)
{
CPatchInfo &pa= zones[0].Patchs[i];
for(j=0;j<4;j++)
{
sint vtx= pa.BaseVertices[j];
CPatchId pid;
pid.ZoneId= centerZoneId;
pid.PatchId= i;
pid.Patch= &pa;
pid.IdVert= j;
VertexMap[vtx].Patchs.push_back(pid);
}
}
// Second, what vertices of this zone are one border?
for(i=0;i<(sint)zones[0].BorderVertices.size();i++)
{
CBorderVertex &bv= zones[0].BorderVertices[i];
sint vtx= bv.CurrentVertex;
VertexMap[vtx].OnBorder= true;
}
// 1. Neighbor zones.
//===================
map tempMap;
for(numZone= 1; numZone<(sint)zones.size(); numZone++)
{
sint adjZoneId= zones[numZone].ZoneId;
tempMap.clear();
// Tests which vertices points on the center zone.
for(i=0;i<(sint)zones[numZone].BorderVertices.size();i++)
{
CBorderVertex &bv= zones[numZone].BorderVertices[i];
if(bv.NeighborZoneId== centerZoneId)
{
tempMap[bv.CurrentVertex]= bv.NeighborVertex;
}
}
// Tests patchs which points on center zone.
for(i=0;i<(sint)zones[numZone].Patchs.size();i++)
{
CPatchInfo &pa= zones[numZone].Patchs[i];
for(j=0;j<4;j++)
{
sint vtx= pa.BaseVertices[j];
if(tempMap.find(vtx)!=tempMap.end())
{
CPatchId pid;
pid.ZoneId= adjZoneId;
pid.PatchId= i;
pid.Patch= &pa;
pid.IdVert= j;
// Fill the vertex of the center zone.
VertexMap[tempMap[vtx]].Patchs.push_back(pid);
}
}
}
}
// 2. Process each vertex.
//========================
ItVertexMap itVert;
for(itVert= VertexMap.begin(); itVert!=VertexMap.end(); itVert++)
{
CVertexInfo &vert= itVert->second;
// a. verify if coplanar is possible.
//===================================
// \todo yoyo: later: do it too on non border vertices if wanted (with a normal threshold...).
if(!vert.OnBorder)
continue;
// \todo yoyo: later: formula with 3, 5 ... patchs around the vertex.
if(vert.Patchs.size()!=4)
continue;
// Test if there is no bind 1/x on this patch, around this vertex.
// \todo yoyo: later: binds should works...
std::list::iterator itPatch;
bool bindFound= false;
for(itPatch= vert.Patchs.begin(); itPatch!= vert.Patchs.end(); itPatch++)
{
// Tests the two edges around the vertex (before: e0, and after: e1).
sint e0= (itPatch->IdVert+4-1)%4;
sint e1= itPatch->IdVert;
if(itPatch->Patch->BindEdges[e0].NPatchs!= 1 || itPatch->Patch->BindEdges[e1].NPatchs!= 1)
{
bindFound= true;
break;
}
}
if(bindFound)
continue;
// b. maps patchs on tangents.
//=========================
vector tangents;
for(itPatch= vert.Patchs.begin(); itPatch!= vert.Patchs.end(); itPatch++)
{
CPatchInfo &pa= *(itPatch->Patch);
// The edges, before and after the veterx.
sint edgeNum[2]= {(itPatch->IdVert+4-1)%4, itPatch->IdVert };
// The tangents, before and after the veterx.
sint tgtNum[2]= {(itPatch->IdVert*2+8-1)%8, itPatch->IdVert*2 };
// For the 2 edges around this vertex.
for(sint ed= 0; ed<2;ed++)
{
sint patchId, zoneId, edgeId;
sint tgt;
// get neighbor edge id.
zoneId= pa.BindEdges[ edgeNum[ed] ].ZoneId;
patchId= pa.BindEdges[ edgeNum[ed] ].Next[0];
edgeId= pa.BindEdges[ edgeNum[ed] ].Edge[0];
// Search if tangent already inserted, mapped to this "neighbor edge".
for(tgt= 0; tgt<(sint)tangents.size();tgt++)
{
if(tangents[tgt].ZoneId==zoneId && tangents[tgt].PatchId==patchId && tangents[tgt].EdgeId==edgeId)
break;
}
// If not found, add the tangent, and map ME to it.
if(tgt==(sint)tangents.size())
{
CTangentId tangent;
// Set OUR edge Id.
tangent.ZoneId= itPatch->ZoneId;
tangent.PatchId= itPatch->PatchId;
tangent.EdgeId= edgeNum[ed];
// Get the tangent, before or after the vertex.
tangent.Tangent= pa.Patch.Tangents[ tgtNum[ed] ];
// Which patchs this edge share. (0 is those which insert this tgt)
tangent.Patchs[0]= &pa;
tangents.push_back(tangent);
}
else
{
// Which patchs this edge share. (0 is those which access this tgt)
tangents[tgt].Patchs[1]= &pa;
}
// Map the patch to this tangent.
itPatch->Tangents[ed]= tgt;
}
}
// There should be 4 tangents.
if (tangents.size()!=4)
{
nlinfo ("ERROR: vertex %d should have 4 tangents. It got %d. (MAXINDICES +1!!)", itVert->first, tangents.size());
continue;
}
// c. get the vertex.
//===================
CVector vertexValue;
itPatch= vert.Patchs.begin();
vertexValue= itPatch->Patch->Patch.Vertices[itPatch->IdVert];
// d. project the tangents.
//=========================
// better coplanar than Max... (with orthogonal angles: use p0/p1).
for(i=0;i<(sint)tangents.size();i++)
{
// For following tangents, search the opposite.
// Begin at i+1 so we are sure to do this only one time.
for(j=i+1;j<(sint)tangents.size();j++)
{
if(tangents[i].isOppositeOf(tangents[j]))
{
CVector &tgt0= tangents[i].Tangent;
CVector &tgt1= tangents[j].Tangent;
// Colinear the tangents. Must keep the length of vectors.
float l0= (tgt0-vertexValue).norm();
float l1= (tgt1-vertexValue).norm();
// Average the tangents. Normalize them before, to keep much as possible the orientation.
CVector d0= (vertexValue-tgt0).normed();
CVector d1= (tgt1-vertexValue).normed();
CVector dir= (d0+d1).normed();
// Copy to tangents.
tgt0= vertexValue-dir*l0;
tgt1= vertexValue+dir*l1;
}
}
}
// e. assign tangents to patchs, rebuild interior.
//==============================
for(itPatch= vert.Patchs.begin(); itPatch!= vert.Patchs.end(); itPatch++)
{
CPatchInfo &pa= *(itPatch->Patch);
// The tangents, before and after the vertex.
sint tgtNum[2]= {(itPatch->IdVert*2+8-1)%8, itPatch->IdVert*2 };
sint t0= tgtNum[0];
sint t1= tgtNum[1];
sint smoothEdge0= pa.getSmoothFlag (t0/2);
sint smoothEdge1= pa.getSmoothFlag (t1/2);
// Smooth this edge ?
if (smoothEdge0)
pa.Patch.Tangents[t0]= tangents[itPatch->Tangents[0]].Tangent;
if (smoothEdge1)
pa.Patch.Tangents[t1]= tangents[itPatch->Tangents[1]].Tangent;
// Setup the coplanared interior. just the sum of 2 vector tangents.
if (smoothEdge0&&smoothEdge1)
pa.Patch.Interiors[itPatch->IdVert]= pa.Patch.Tangents[t0] + pa.Patch.Tangents[t1] - vertexValue;
}
}
}
/*
// OLD CODE FOR 3DS MAX LIKE COPLANAR.
// WORKS, BUT NOT AS GOOD AS REAL COPLANAR.
// b. build the plane.
//====================
CVector planeNormal(0,0,0);
CVector planeCenter;
for(itPatch= vert.Patchs.begin(); itPatch!= vert.Patchs.end(); itPatch++)
{
CPatchInfo &pa= *(itPatch->Patch);
CVector a,b,c, pvect;
sint t0= (itPatch->IdVert*2+8-1)%8;
sint t1= itPatch->IdVert*2;
// CCW order.
a= pa.Patch.Tangents[t0];
b= pa.Patch.Vertices[itPatch->IdVert];
c= pa.Patch.Tangents[t1];
pvect= (b-a)^(c-b);
planeNormal+= pvect.normed();
// yes, done 4 times... :(
planeCenter= b;
}
// Average of all normals...
planeNormal.normalize();
CPlane plane;
plane.make(planeNormal, planeCenter);
// c. projects the tangents, rebuild interior.
//============================================
for(itPatch= vert.Patchs.begin(); itPatch!= vert.Patchs.end(); itPatch++)
{
CPatchInfo &pa= *(itPatch->Patch);
sint t0= (itPatch->IdVert*2+8-1)%8;
sint t1= itPatch->IdVert*2;
pa.Patch.Tangents[t0]= plane.project(pa.Patch.Tangents[t0]);
pa.Patch.Tangents[t1]= plane.project(pa.Patch.Tangents[t1]);
// Setup the coplanared interior. just the sum of 2 vector tangents.
pa.Patch.Interiors[itPatch->IdVert]= pa.Patch.Tangents[t0] + pa.Patch.Tangents[t1] - planeCenter;
}
*/
} // NL3D