1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "nel/pacs/collision_mesh_build.h"
25 #include "nel/pacs/local_retriever.h"
26 #include "nel/pacs/exterior_mesh.h"
29 #include "build_surfaces.h"
32 using namespace NLMISC
;
33 using namespace NLPACS
;
36 // a reference on an edge
43 CEdgeKey(uint32 v0, uint32 v1) : V0(v0), V1(v1) {}
45 bool operator() (const CEdgeKey &a, const CEdgeKey &b)
47 return a.V0 < b.V0 || (a.V0 == b.V0 && a.V1 < b.V1);
51 // the info on an edge
54 sint32 Left, LeftEdge;
55 sint32 Right, RightEdge;
57 CEdgeInfo(sint32 left=-1, sint32 leftEdge=-1, sint32 right=-1, sint32 rightEdge=-1) : Left(left), LeftEdge(leftEdge), Right(right), RightEdge(rightEdge) {}
60 typedef map<CEdgeKey, CEdgeInfo, CEdgeKey> TLinkRelloc;
61 typedef TLinkRelloc::iterator ItTLinkRelloc;
65 void linkMesh(CCollisionMeshBuild &cmb, bool linkInterior)
70 // check each edge of each face
71 for (i=0; i<cmb.Faces.size(); ++i)
73 if (cmb.Faces[i].Surface == CCollisionFace::ExteriorSurface && linkInterior ||
74 cmb.Faces[i].Surface >= CCollisionFace::InteriorSurfaceFirst && !linkInterior)
79 cmb.Faces[i].Edge[j] = -1;
82 uint32 va = cmb.Faces[i].V[j],
83 vb = cmb.Faces[i].V[(j+1)%3];
86 if ((it = relloc.find(CEdgeKey(va, vb))) != relloc.end())
88 // in this case, the left triangle of the edge has already been found.
89 // should throw an error
90 nlerror("On face %d, edge %d: left side of edge (%d,%d) already linked to face %d",
91 i, edge, va, vb, (*it).second.Left);
93 else if ((it = relloc.find(CEdgeKey(vb, va))) != relloc.end())
95 // in this case, we must check the right face has been set yet
96 if ((*it).second.Right != -1)
98 nlerror("On face %d, edge %d: right side of edge (%d,%d) already linked to face %d",
99 i, edge, vb, va, (*it).second.Right);
102 (*it).second.Right = i;
103 (*it).second.RightEdge = edge;
107 // if the edge wasn't present yet, create it and set it up.
108 relloc.insert(make_pair(CEdgeKey(va, vb), CEdgeInfo(i, edge, -1, -1)));
113 // for each checked edge, update the edge info inside the faces
115 for (it=relloc.begin(); it!=relloc.end(); ++it)
117 sint32 left, leftEdge;
118 sint32 right, rightEdge;
120 // get the link info on the edge
121 left = (*it).second.Left;
122 leftEdge = (*it).second.LeftEdge;
123 right = (*it).second.Right;
124 rightEdge = (*it).second.RightEdge;
128 cmb.Faces[left].Edge[leftEdge] = right;
130 cmb.Faces[right].Edge[rightEdge] = left;
137 void buildExteriorMesh(CCollisionMeshBuild
&cmb
, CExteriorMesh
&em
)
140 vector
<CExteriorMesh::CEdge
> edges
;
143 for (i
=0; i
<cmb
.Faces
.size(); ++i
)
145 cmb
.Faces
[i
].EdgeFlags
[0] = false;
146 cmb
.Faces
[i
].EdgeFlags
[1] = false;
147 cmb
.Faces
[i
].EdgeFlags
[2] = false;
152 // find the first non interior face
155 for (i
=startFace
; i
<cmb
.Faces
.size() && !found
; ++i
)
157 if (cmb
.Faces
[i
].Surface
!= CCollisionFace::ExteriorSurface
)
160 for (edge
=0; edge
<3 && !found
; ++edge
)
161 if (cmb
.Faces
[i
].Edge
[edge
] == -1 && !cmb
.Faces
[i
].EdgeFlags
[edge
])
163 // nlassert(cmb.Faces[i].Material != 0xdeadbeef);
176 // cmb.Faces[i].Material = 0xdeadbeef;
181 sint32 next
= cmb
.Faces
[current
].Edge
[edge
];
184 sint pivot
= (edge
+1)%3;
185 sint nextEdge
= edge
;
186 bool allowThis
= true;
189 uint firstEdge
= (uint
)edges
.size();
191 vector
<CExteriorMesh::CEdge
> loop
;
195 if (cmb
.Faces
[current
].EdgeFlags
[nextEdge
])
197 // if reaches the end of the border, then quits.
202 // if the next edge belongs to the border, then go on the same element
203 cmb
.Faces
[current
].EdgeFlags
[nextEdge
] = true;
204 /// \todo get the real edge link
205 sint link
= (cmb
.Faces
[current
].Visibility
[nextEdge
]) ? -1 : 0; //(numLink++);
207 //edges.push_back(CExteriorMesh::CEdge(cmb.Vertices[cmb.Faces[current].V[pivot]], link));
208 loop
.push_back(CExteriorMesh::CEdge(cmb
.Vertices
[cmb
.Faces
[current
].V
[pivot
]], link
));
211 nextEdge
= (nextEdge
+1)%3;
212 next
= cmb
.Faces
[current
].Edge
[nextEdge
];
216 // if the next element is inside the surface, then go to the next element
217 for (oedge
=0; oedge
<3 && cmb
.Faces
[next
].Edge
[oedge
]!=current
; ++oedge
)
219 nlassert(oedge
!= 3);
222 nextEdge
= (oedge
+1)%3;
223 next
= cmb
.Faces
[current
].Edge
[nextEdge
];
227 // mark the end of a ext mesh block
228 // this way, collisions won't be checked in the pacs engine
229 if (loop
.size() >= 3)
231 uint n
= (uint
)loop
.size();
232 while (loop
.front().Link
>= 0 && loop
.back().Link
>= 0 && n
> 0)
234 loop
.push_back(loop
.front());
235 loop
.erase(loop
.begin());
239 loop
.push_back(loop
.front());
240 loop
.back().Link
= -2;
241 edges
.insert(edges
.end(), loop
.begin(), loop
.end());
242 //edges.push_back(edges[firstEdge]);
243 //edges.back().Link = -2;
246 bool previousWasLink
= false;
247 sint previousLink
= -1;
248 for (i
=0; i
<edges
.size(); ++i
)
250 // nldebug("ext-mesh: vertex=%d (%.2f,%.2f,%.2f) link=%d", i, edges[i].Start.x, edges[i].Start.y, edges[i].Start.z, edges[i].Link);
251 if (edges
[i
].Link
>= 0)
253 if (!previousWasLink
)
255 edges
[i
].Link
= previousLink
;
256 previousWasLink
= true;
260 previousWasLink
= false;
269 void linkExteriorToInterior(CLocalRetriever
&lr
)
271 CExteriorMesh em
= lr
.getExteriorMesh();
272 vector
<CExteriorMesh::CEdge
> edges
= em
.getEdges();
273 vector
<CExteriorMesh::CLink
> links
;
274 const vector
<CChain
> &chains
= lr
.getChains();
275 const vector
<COrderedChain3f
> &ochains
= lr
.getFullOrderedChains();
276 const vector
<uint16
> &bchains
= lr
.getBorderChains();
281 nlinfo("Border chains (to be linked) for this retriever:");
283 for (i
=0; i
<bchains
.size(); ++i
)
286 std::stringstream ss
;
287 const CChain
&chain
= chains
[bchains
[i
]];
288 sprintf(w
, "Border chain %d: chain=%d ", i
, bchains
[i
]);
291 for (och
=0; och
<chain
.getSubChains().size(); ++och
)
293 const COrderedChain3f
&ochain
= ochains
[chain
.getSubChain(och
)];
294 sprintf(w
, "subchain=%d", chain
.getSubChain(och
));
297 for (v
=0; v
<ochain
.getVertices().size(); ++v
)
299 sprintf(w
, " (%.2f,%.2f)", ochain
[v
].x
, ochain
[v
].y
);
304 nlinfo("%s", ss
.str().c_str());
309 for (edge
=0; edge
+1<edges
.size(); )
311 if (edges
[edge
].Link
== -1 || edges
[edge
].Link
== -2)
317 uint startedge
= edge
;
320 for (stopedge
=edge
; stopedge
+1<edges
.size() && edges
[stopedge
+1].Link
== edges
[startedge
].Link
; ++stopedge
)
325 CVector start
= edges
[startedge
].Start
, stop
= edges
[stopedge
+1].Start
;
328 for (ch
=0; ch
<bchains
.size() && !found
; ++ch
)
330 // get the border chain.
331 //const CChain &chain = chains[bchains[ch]];
333 const CVector
&cstart
= lr
.getStartVector(bchains
[ch
]),
334 &cstop
= lr
.getStopVector(bchains
[ch
]);
336 float d
= (start
-cstart
).norm()+(stop
-cstop
).norm();
345 CExteriorMesh::CLink link
;
349 nlwarning("in linkInteriorToExterior():");
350 nlwarning("couldn't find any link to the exterior edge %d-%d!!", startedge
, stopedge
);
354 // set it up to point on the chain and surface
355 link
.BorderChainId
= ch
;
356 link
.ChainId
= bchains
[ch
];
357 link
.SurfaceId
= (uint16
)chains
[link
.ChainId
].getLeft();
361 if (edges
[startedge
].Link
>= (sint
)links
.size())
362 links
.resize(edges
[startedge
].Link
+1);
364 // if the link already exists, warning
365 if (links
[edges
[startedge
].Link
].BorderChainId
!= 0xFFFF ||
366 links
[edges
[startedge
].Link
].ChainId
!= 0xFFFF ||
367 links
[edges
[startedge
].Link
].SurfaceId
!= 0xFFFF)
369 nlwarning("in linkInteriorToExterior():");
370 nlwarning("link %d already set!!", edges
[startedge
].Link
);
374 links
[edges
[startedge
].Link
] = link
;
377 // em.setEdges(edges);
379 lr
.setExteriorMesh(em
);
387 void computeRetriever(CCollisionMeshBuild
&cmb
, CLocalRetriever
&lr
, CVector
&translation
, bool useCmbTrivialTranslation
)
390 lr
.setType(CLocalRetriever::Interior
);
392 // if should use the own cmb bbox, then compute it
393 if (useCmbTrivialTranslation
)
395 translation
= cmb
.computeTrivialTranslation();
396 // snap the translation vector to a meter wide grid
397 translation
.x
= (float)ceil(translation
.x
);
398 translation
.y
= (float)ceil(translation
.y
);
399 translation
.z
= 0.0f
;
404 for (i
=0; i
<cmb
.Faces
.size(); ++i
)
406 CVector normal
= ((cmb
.Vertices
[cmb
.Faces
[i
].V
[1]]-cmb
.Vertices
[cmb
.Faces
[i
].V
[0]])^(cmb
.Vertices
[cmb
.Faces
[i
].V
[2]]-cmb
.Vertices
[cmb
.Faces
[i
].V
[0]])).normed();
410 nlwarning("Face %d in cmb (%s) has negative normal! -- face is flipped", i
, cmb
.Faces
[i
].Surface
== CCollisionFace::InteriorSurfaceFirst
? "interior" : "exterior");
412 std::swap(cmb.Faces[i].V[1], cmb.Faces[i].V[2]);
413 std::swap(cmb.Faces[i].Visibility[1], cmb.Faces[i].Visibility[2]);
420 linkMesh(cmb, false);
423 vector
<string
> errors
;
425 cmb
.link(false, errors
);
426 cmb
.link(true, errors
);
430 nlwarning("Edge issues reported !!");
432 for (i
=0; i
<errors
.size(); ++i
)
433 nlwarning("%s", errors
[i
].c_str());
434 nlerror("Can't continue.");
437 // translate the meshbuild to the local axis
438 cmb
.translate(translation
);
440 // find the exterior mesh border
441 CExteriorMesh extMesh
;
442 buildExteriorMesh(cmb
, extMesh
);
443 lr
.setExteriorMesh(extMesh
);
445 // build the surfaces in the local retriever
446 buildSurfaces(cmb
, lr
);
448 // create the snapping faces and vertices
449 // after the build surfaces because the InternalSurfaceId is filled within buildSurfaces()...
450 buildSnapping(cmb
, lr
);
453 lr
.computeLoopsAndTips();
455 lr
.findBorderChains();
457 lr
.computeTopologies();
461 lr
.computeCollisionChainQuad();
464 for (i=0; i<lr.getSurfaces().size(); ++i)
468 linkExteriorToInterior(lr
);
470 // compute the bbox of the retriever
474 for (i
=0; i
<extMesh
.getEdges().size(); ++i
)
476 bbox
.extend(extMesh
.getEdge(i
).Start
);
478 bbox
.setCenter(extMesh
.getEdge(i
).Start
), first
=false;
480 for (i
=0; i
<lr
.getOrderedChains().size(); ++i
)
481 for (j
=0; j
<lr
.getOrderedChain(i
).getVertices().size(); ++j
)
483 bbox
.extend(lr
.getOrderedChain(i
)[j
].unpack3f());
485 bbox
.setCenter(lr
.getOrderedChain(i
)[j
].unpack3f()), first
=false;
487 CVector bboxhs
= bbox
.getHalfSize();
489 bbox
.setHalfSize(bboxhs
);