|
|
|
@ -186,6 +186,111 @@ NLMISC::CVector getHeightNormal(float x, float y)
|
|
|
|
|
// compute the heightmap normal with the tangents
|
|
|
|
|
return (ds ^ dt).normed();
|
|
|
|
|
}
|
|
|
|
|
// ***************************************************************************
|
|
|
|
|
// void CExport::computeSubdividedTangents(uint numBinds, const NL3D::CBezierPatch &patch, uint edge, NLMISC::CVector subTangents[8])
|
|
|
|
|
void computeSubdividedTangents(uint numBinds, const NL3D::CBezierPatch &patch, uint edge, NLMISC::CVector subTangents[8])
|
|
|
|
|
{
|
|
|
|
|
// Subdivide the Bezier patch to get the correct tangents to apply to neighbors
|
|
|
|
|
CBezierPatch subPatchs1_2[2];
|
|
|
|
|
CBezierPatch subPatchs1_4[4];
|
|
|
|
|
|
|
|
|
|
// subdivide on s if edge is horizontal
|
|
|
|
|
bool subDivideOnS= (edge&1)==1;
|
|
|
|
|
|
|
|
|
|
// Subdivide one time.
|
|
|
|
|
if(subDivideOnS) patch.subdivideS(subPatchs1_2[0], subPatchs1_2[1]);
|
|
|
|
|
else patch.subdivideT(subPatchs1_2[0], subPatchs1_2[1]);
|
|
|
|
|
|
|
|
|
|
// Subdivide again for bind 1/4.
|
|
|
|
|
if(numBinds==4)
|
|
|
|
|
{
|
|
|
|
|
if(subDivideOnS)
|
|
|
|
|
{
|
|
|
|
|
subPatchs1_2[0].subdivideS(subPatchs1_4[0], subPatchs1_4[1]);
|
|
|
|
|
subPatchs1_2[1].subdivideS(subPatchs1_4[2], subPatchs1_4[3]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
subPatchs1_2[0].subdivideT(subPatchs1_4[0], subPatchs1_4[1]);
|
|
|
|
|
subPatchs1_2[1].subdivideT(subPatchs1_4[2], subPatchs1_4[3]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now, fill the tangents according to edge.
|
|
|
|
|
bool invertPaSrc= edge>=2;
|
|
|
|
|
// Bind 1/2 case.
|
|
|
|
|
if(numBinds==2)
|
|
|
|
|
{
|
|
|
|
|
// 4 tangents to fill.
|
|
|
|
|
for(uint i=0;i<4;i++)
|
|
|
|
|
{
|
|
|
|
|
// get patch id from 0 to 1.
|
|
|
|
|
uint paSrcId= i/2;
|
|
|
|
|
// invert if edge is 2 or 3
|
|
|
|
|
if(invertPaSrc) paSrcId= 1-paSrcId;
|
|
|
|
|
// get tg id in this patch.
|
|
|
|
|
uint tgSrcId= (i&1) + edge*2;
|
|
|
|
|
// fill result.
|
|
|
|
|
subTangents[i]= subPatchs1_2[paSrcId].Tangents[tgSrcId];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Bind 1/4 case.
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 8 tangents to fill.
|
|
|
|
|
for(uint i=0;i<8;i++)
|
|
|
|
|
{
|
|
|
|
|
// get patch id from 0 to 3.
|
|
|
|
|
uint paSrcId= i/2;
|
|
|
|
|
// invert if edge is 2 or 3
|
|
|
|
|
if(invertPaSrc) paSrcId= 3-paSrcId;
|
|
|
|
|
// get tg id in this patch.
|
|
|
|
|
uint tgSrcId= (i&1) + edge*2;
|
|
|
|
|
// fill result.
|
|
|
|
|
subTangents[i]= subPatchs1_4[paSrcId].Tangents[tgSrcId];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
|
// bool CExport::applyVertexBind(NL3D::CPatchInfo &pa, NL3D::CPatchInfo &oldPa, uint edgeToModify, bool startEdge, ...
|
|
|
|
|
bool applyVertexBind(NL3D::CPatchInfo &pa, NL3D::CPatchInfo &oldPa, uint edgeToModify, bool startEdge,
|
|
|
|
|
const NLMISC::CMatrix &oldTgSpace, const NLMISC::CMatrix &newTgSpace,
|
|
|
|
|
const NLMISC::CVector &bindedPos, const NLMISC::CVector &bindedTangent )
|
|
|
|
|
{
|
|
|
|
|
// Get the vertex to modify according to edge/startEdge
|
|
|
|
|
uint vertexToModify= edgeToModify + (startEdge?0:1);
|
|
|
|
|
vertexToModify&=3;
|
|
|
|
|
|
|
|
|
|
// If already moved, no-op
|
|
|
|
|
if(pa.Patch.Vertices[vertexToModify]==bindedPos)
|
|
|
|
|
return false;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Change the vertex
|
|
|
|
|
pa.Patch.Vertices[vertexToModify]= bindedPos;
|
|
|
|
|
|
|
|
|
|
// change the tangent, according to startEdge
|
|
|
|
|
pa.Patch.Tangents[edgeToModify*2 + (startEdge?0:1) ]= bindedTangent;
|
|
|
|
|
|
|
|
|
|
// Must change the tangent which is on the other side of the vertex:
|
|
|
|
|
uint tgToModify= 8 + edgeToModify*2 + (startEdge?-1:+2);
|
|
|
|
|
tgToModify&=7;
|
|
|
|
|
/* To keep the same continuity aspect around the vertex, we compute the original tangent in a
|
|
|
|
|
special space: the Binded Patch Tangent Space. Once we have the original tangent in the original patch TgSpace,
|
|
|
|
|
we reapply it in the transformed patch TgSpace, to get the transformed tangent
|
|
|
|
|
*/
|
|
|
|
|
pa.Patch.Tangents[tgToModify]= newTgSpace * ( oldTgSpace.inverted() * oldPa.Patch.Tangents[tgToModify] );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Do the same to the associated interior.
|
|
|
|
|
pa.Patch.Interiors[vertexToModify]= newTgSpace * ( oldTgSpace.inverted() * oldPa.Patch.Interiors[vertexToModify] );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// modified
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void applyZoneHeightmap()
|
|
|
|
|
{
|
|
|
|
@ -201,6 +306,9 @@ void applyZoneHeightmap()
|
|
|
|
|
std::vector<CBorderVertex> zoneBorderVertices;
|
|
|
|
|
zone.retrieve(zonePatches, zoneBorderVertices);
|
|
|
|
|
|
|
|
|
|
// Bkup the original patchs.
|
|
|
|
|
vector<CPatchInfo> oldPatchInfos = zonePatches;
|
|
|
|
|
|
|
|
|
|
// Apply the Heighmap to all vertices/tangents/interiors (see Land Export tool.)
|
|
|
|
|
for (size_t i = 0; i < zonePatches.size(); ++i)
|
|
|
|
|
{
|
|
|
|
@ -254,6 +362,94 @@ void applyZoneHeightmap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See `void CExport::transformZone (CZone &zeZone, sint32 nPosX, sint32 nPosY, uint8 nRot, uint8 nFlip, bool computeHeightmap)`
|
|
|
|
|
// 3. For all binds, reset the position of near vertices/tangents/interiors. Must do it at last
|
|
|
|
|
// --------
|
|
|
|
|
bool bindVertexModified = true;
|
|
|
|
|
// Since this is a recursive problem (binded patchs may bind other patchs), do it unitl all vertices no more move :)
|
|
|
|
|
while (bindVertexModified)
|
|
|
|
|
{
|
|
|
|
|
bindVertexModified = false;
|
|
|
|
|
for (size_t i = 0; i < zonePatches.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
CPatchInfo &rPI = zonePatches[i];
|
|
|
|
|
|
|
|
|
|
// For all edges
|
|
|
|
|
for (size_t j = 0; j < 4; ++j)
|
|
|
|
|
{
|
|
|
|
|
uint numBinds = rPI.BindEdges[j].NPatchs;
|
|
|
|
|
// If this edge is binded on 2 or 4 patches.
|
|
|
|
|
if (numBinds == 2 || numBinds == 4)
|
|
|
|
|
{
|
|
|
|
|
// compute the 4 or 8 tangents along the edge (in CCW)
|
|
|
|
|
CVector subTangents[8];
|
|
|
|
|
computeSubdividedTangents(numBinds, rPI.Patch, j, subTangents);
|
|
|
|
|
|
|
|
|
|
// For all vertex to bind: 1 or 3.
|
|
|
|
|
for (uint vb = 0; vb < numBinds - 1; vb++)
|
|
|
|
|
{
|
|
|
|
|
// compute the s/t coordinate
|
|
|
|
|
float bindS, bindT;
|
|
|
|
|
// 0.5, or 0.25, 0.5, 0.75
|
|
|
|
|
float ec = (float)(vb + 1) / (float)numBinds;
|
|
|
|
|
switch (j)
|
|
|
|
|
{
|
|
|
|
|
case 0: bindS= 0; bindT= ec; break;
|
|
|
|
|
case 1: bindS= ec; bindT= 1; break;
|
|
|
|
|
case 2: bindS= 1; bindT= 1-ec; break;
|
|
|
|
|
case 3: bindS= 1-ec; bindT= 0; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// compute the vertex position from big patch.
|
|
|
|
|
CVector bindedPos;
|
|
|
|
|
bindedPos = rPI.Patch.eval(bindS, bindT);
|
|
|
|
|
|
|
|
|
|
// Compute a TgSpace matrix around this position.
|
|
|
|
|
CMatrix oldTgSpace;
|
|
|
|
|
CMatrix newTgSpace;
|
|
|
|
|
|
|
|
|
|
// Build the original tgtSpace (patch before deformation)
|
|
|
|
|
oldTgSpace.setRot(oldPatchInfos[i].Patch.evalTangentS(bindS, bindT),
|
|
|
|
|
oldPatchInfos[i].Patch.evalTangentT(bindS, bindT),
|
|
|
|
|
oldPatchInfos[i].Patch.evalNormal(bindS, bindT));
|
|
|
|
|
oldTgSpace.normalize(CMatrix::ZYX);
|
|
|
|
|
oldTgSpace.setPos(oldPatchInfos[i].Patch.eval(bindS, bindT));
|
|
|
|
|
|
|
|
|
|
// Build the new tgtSpace
|
|
|
|
|
newTgSpace.setRot(rPI.Patch.evalTangentS(bindS, bindT),
|
|
|
|
|
rPI.Patch.evalTangentT(bindS, bindT),
|
|
|
|
|
rPI.Patch.evalNormal(bindS, bindT));
|
|
|
|
|
newTgSpace.normalize(CMatrix::ZYX);
|
|
|
|
|
newTgSpace.setPos(bindedPos);
|
|
|
|
|
|
|
|
|
|
// apply to the 2 smaller binded patchs which share this vertex.
|
|
|
|
|
uint edgeToModify;
|
|
|
|
|
sint ngbId;
|
|
|
|
|
CVector bindedTangent;
|
|
|
|
|
|
|
|
|
|
// The first patch (CCW) must change the vertex which starts on the edge (CCW)
|
|
|
|
|
edgeToModify = rPI.BindEdges[j].Edge[vb];
|
|
|
|
|
// get the tangent to set
|
|
|
|
|
bindedTangent = subTangents[vb * 2 + 1];
|
|
|
|
|
// get the patch id.
|
|
|
|
|
ngbId = rPI.BindEdges[j].Next[vb];
|
|
|
|
|
bindVertexModified |= applyVertexBind(zonePatches[ngbId], oldPatchInfos[ngbId], edgeToModify,
|
|
|
|
|
true, oldTgSpace, newTgSpace, bindedPos, bindedTangent);
|
|
|
|
|
|
|
|
|
|
// The second patch (CCW) must change the vertex which ends on the edge (CCW)
|
|
|
|
|
edgeToModify = rPI.BindEdges[j].Edge[vb + 1];
|
|
|
|
|
// get the tangent to set
|
|
|
|
|
bindedTangent = subTangents[vb * 2 + 2];
|
|
|
|
|
// get the patch id.
|
|
|
|
|
ngbId = rPI.BindEdges[j].Next[vb + 1];
|
|
|
|
|
bindVertexModified |= applyVertexBind(zonePatches[ngbId], oldPatchInfos[ngbId], edgeToModify,
|
|
|
|
|
false, oldTgSpace, newTgSpace, bindedPos, bindedTangent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save zone
|
|
|
|
|
zone.build(zoneId, zonePatches, zoneBorderVertices);
|
|
|
|
|
COFile centerSave(s_OutputZone);
|
|
|
|
|