From 87b5a77d4fb29c00301d153bb322f58f9e3b9b79 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 5 Jan 2021 22:41:55 +0800 Subject: [PATCH] Add elevation snippets to weld bound vertices --- .../3d/zone_elevation/zone_elevation.cpp | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/nel/tools/3d/zone_elevation/zone_elevation.cpp b/nel/tools/3d/zone_elevation/zone_elevation.cpp index 598cfda31..abe9b511c 100644 --- a/nel/tools/3d/zone_elevation/zone_elevation.cpp +++ b/nel/tools/3d/zone_elevation/zone_elevation.cpp @@ -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 zoneBorderVertices; zone.retrieve(zonePatches, zoneBorderVertices); + // Bkup the original patchs. + vector 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);