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-2020 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/>.
21 #include "nel/pacs/build_indoor.h"
25 #include "nel/pacs/collision_mesh_build.h"
26 #include "nel/pacs/local_retriever.h"
27 #include "nel/pacs/exterior_mesh.h"
30 using namespace NLMISC
;
36 * The interior surface class. Intermediate to compute real retriever surfaces
37 * \author Benjamin Legros
38 * \author Nevrax France
41 class CInteriorSurface
44 /// The collision mesh root object
45 CCollisionMeshBuild
*CollisionMeshBuild
;
47 /// The faces that compose the surface
48 std::vector
<uint32
> Faces
;
50 /// The Id of the surface
53 /// The center of the surface
54 NLMISC::CVector Center
;
56 /// The material of the surface
60 CCollisionFace
&getFace(uint face
) { return CollisionMeshBuild
->Faces
[Faces
[face
]]; }
61 CCollisionFace
&getNeighbor(uint face
, uint edge
)
63 return CollisionMeshBuild
->Faces
[getFace(face
).Edge
[edge
]];
69 * The border of interior surfaces.
70 * \author Benjamin Legros
71 * \author Nevrax France
77 /// The vertices that compose the border
78 std::vector
<NLMISC::CVector
> Vertices
;
80 /// The left and right surfaces
87 // how to build interior snapping data
88 void buildSnapping(CCollisionMeshBuild
&cmb
, CLocalRetriever
&lr
);
91 // how to build surfaces
92 void buildSurfaces(CCollisionMeshBuild
&cmb
, CLocalRetriever
&lr
);
97 // functions to build interior surfaces and borders from mesh
100 // how to generate connex surfaces
101 void floodFillSurfaces(CCollisionMeshBuild
&cmb
, vector
<CInteriorSurface
> &surfaces
)
103 sint32 currentId
= 0;
107 for (i
=0; i
<cmb
.Faces
.size(); ++i
)
108 cmb
.Faces
[i
].InternalSurface
= -1;
110 for (i
=0; i
<cmb
.Faces
.size(); ++i
)
112 CCollisionFace
&face
= cmb
.Faces
[i
];
113 if (face
.Surface
== CCollisionFace::ExteriorSurface
|| face
.InternalSurface
!= -1)
116 vector
<sint32
> stack
;
117 stack
.push_back(sint32(i
));
118 face
.InternalSurface
= currentId
;
120 surfaces
.resize(surfaces
.size()+1);
121 surfaces
.back().Id
= currentId
;
122 surfaces
.back().CollisionMeshBuild
= &cmb
;
123 surfaces
.back().Material
= face
.Material
;
125 while (!stack
.empty())
127 uint pop
= stack
.back();
130 surfaces
.back().Faces
.push_back(pop
);
131 CCollisionFace
&popFace
= cmb
.Faces
[pop
];
134 for (edge
=0; edge
<3; ++edge
)
136 if ((neighb
= popFace
.Edge
[edge
]) != -1 &&
137 cmb
.Faces
[neighb
].InternalSurface
== -1 &&
138 cmb
.Faces
[neighb
].Surface
== popFace
.Surface
)
140 cmb
.Faces
[neighb
].InternalSurface
= currentId
;
141 stack
.push_back(neighb
);
151 // reset the edge flags of the whole collision mesh build
152 void resetEdgeFlags(CCollisionMeshBuild
&cmb
)
156 for (face
=0; face
<cmb
.Faces
.size(); ++face
)
157 for (edge
=0; edge
<3; ++edge
)
158 cmb
.Faces
[face
].EdgeFlags
[edge
] = false;
163 // how to generate the borders of a given surface
165 void followBorder(CInteriorSurface
&surface
, uint first
, uint edge
, uint sens
, vector
<CVector
> &vstore
, bool &loop
)
167 CCollisionFace
*current
= &surface
.getFace(first
);
168 CCollisionFace
*next
= (current
->Edge
[edge
] == -1) ? NULL
: &surface
.CollisionMeshBuild
->Faces
[current
->Edge
[edge
]];
169 current
->EdgeFlags
[edge
] = true;
170 sint32 currentFace
= surface
.Faces
[first
];
172 const sint32 currentSurfId
= current
->InternalSurface
;
173 const sint32 oppositeSurfId
= (next
!= NULL
) ? next
->InternalSurface
: (current
->Visibility
[edge
] ? -1 : -2);
176 sint pivot
= (edge
+sens
)%3;
177 sint nextEdge
= edge
;
179 bool allowThis
= true;
181 // adds the pivot to the border and its normal
182 vstore
.push_back(surface
.CollisionMeshBuild
->Vertices
[current
->V
[pivot
]]);
187 // -1 means no neighbor at all, -2 means a neighbor that is not available yet
188 sint32 thisOpposite
= (next
!= NULL
) ? next
->InternalSurface
: (current
->Visibility
[nextEdge
] ? -1 : -2);
189 if ((thisOpposite
!= currentSurfId
&& thisOpposite
!= oppositeSurfId
) ||
190 (loop
= (current
->EdgeFlags
[nextEdge
] && !allowThis
)))
192 // if reaches the end of the border, then quits.
195 else if (thisOpposite
== oppositeSurfId
)
197 // if the next edge belongs to the border, then go on the same element
198 current
->EdgeFlags
[nextEdge
] = true;
199 if (oppositeSurfId
>= 0)
201 for (oedge
=0; oedge
<3 && next
->Edge
[oedge
]!=currentFace
; ++oedge
)
203 nlassert(oedge
!= 3);
204 nlassert(allowThis
|| !next
->EdgeFlags
[oedge
]);
205 next
->EdgeFlags
[oedge
] = true;
207 pivot
= (pivot
+sens
)%3;
208 nextEdge
= (nextEdge
+sens
)%3;
209 next
= (current
->Edge
[nextEdge
] == -1) ? NULL
: &surface
.CollisionMeshBuild
->Faces
[current
->Edge
[nextEdge
]];
210 vstore
.push_back(surface
.CollisionMeshBuild
->Vertices
[current
->V
[pivot
]]);
214 // if the next element is inside the surface, then go to the next element
216 nlassert(next
->InternalSurface
== currentSurfId
);
218 for (oedge
=0; oedge
<3 && next
->Edge
[oedge
]!=currentFace
; ++oedge
)
220 nlassert(oedge
!= 3);
221 currentFace
= current
->Edge
[nextEdge
];
223 pivot
= (oedge
+3-sens
)%3;
224 nextEdge
= (oedge
+sens
)%3;
225 next
= (current
->Edge
[nextEdge
] == -1) ? NULL
: &surface
.CollisionMeshBuild
->Faces
[current
->Edge
[nextEdge
]];
233 void computeSurfaceBorders(CInteriorSurface
&surface
, vector
<CInteriorBorder
> &borders
)
237 for (face
=0; face
<surface
.Faces
.size(); ++face
)
240 // scan for a edge that points to a different surface
241 CCollisionFace
&cf
= surface
.getFace(face
);
243 for (edge
=0; edge
<3; ++edge
)
245 if ((cf
.Edge
[edge
] == -1 || surface
.getNeighbor(face
, edge
).InternalSurface
!= surface
.Id
) &&
248 borders
.resize(borders
.size()+1);
249 CInteriorBorder
&border
= borders
.back();
251 border
.Left
= cf
.InternalSurface
;
253 if (cf
.Edge
[edge
] == -1)
255 // link on a neighbor retriever or not at all
256 border
.Right
= cf
.Visibility
[edge
] ? -1 : -2;
260 // link on the neighbor surface
261 border
.Right
= surface
.CollisionMeshBuild
->Faces
[cf
.Edge
[edge
]].InternalSurface
;
264 nldebug("generate border %d (%d-%d)", borders
.size()-1, border
.Left
, border
.Right
);
267 vector
<CVector
> bwdVerts
;
268 vector
<CVector
> &fwdVerts
= border
.Vertices
;
270 followBorder(surface
, face
, edge
, 2, bwdVerts
, loop
);
274 fwdVerts
.reserve(bwdVerts
.size());
277 for (i
=(sint
)(bwdVerts
.size()-1); i
>=0; --i
)
279 fwdVerts
.push_back(bwdVerts
[i
]);
284 fwdVerts
.push_back(fwdVerts
.front());
288 fwdVerts
.resize(fwdVerts
.size()-2);
289 followBorder(surface
, face
, edge
, 1, fwdVerts
, loop
);
297 void computeSurfaceCenter(CInteriorSurface
&surface
)
299 CCollisionMeshBuild
&cmb
= *(surface
.CollisionMeshBuild
);
301 CVector center
= CVector::Null
;
302 float totalWeight
= 0.0f
;
306 for (i
=0; i
<surface
.Faces
.size(); ++i
)
308 CCollisionFace
&face
= surface
.getFace(i
);
312 v
[j
] = cmb
.Vertices
[face
.V
[j
]];
314 float weight
= ((v
[2]-v
[0])^(v
[1]-v
[0])).norm();
316 center
+= (v
[0]+v
[1]+v
[2])*(weight
/3.0f
);
317 totalWeight
+= weight
;
320 surface
.Center
= center
/totalWeight
;
324 void computeSurfaceQuadTree(CInteriorSurface
&surface
, CSurfaceQuadTree
&quad
)
330 for (i
=0; i
<surface
.Faces
.size(); ++i
)
334 const CVector
&v
= surface
.CollisionMeshBuild
->Vertices
[surface
.CollisionMeshBuild
->Faces
[surface
.Faces
[i
]].V
[j
]];
336 box
.setCenter(v
), first
=false;
343 quad
.init(4.0f
, 6, box
.getCenter(), std::max(box
.getHalfSize().x
, box
.getHalfSize().y
));
345 for (i
=0; i
<surface
.Faces
.size(); ++i
)
349 const CVector
&v
= surface
.CollisionMeshBuild
->Vertices
[surface
.CollisionMeshBuild
->Faces
[surface
.Faces
[i
]].V
[j
]];
358 void buildSurfaces(CCollisionMeshBuild
&cmb
, CLocalRetriever
&lr
)
360 vector
<CInteriorSurface
> surfaces
;
361 vector
<CInteriorBorder
> borders
;
363 floodFillSurfaces(cmb
, surfaces
);
369 for (surf
=0; surf
<surfaces
.size(); ++surf
)
371 CSurfaceQuadTree quad
;
372 computeSurfaceBorders(surfaces
[surf
], borders
);
373 computeSurfaceCenter(surfaces
[surf
]);
374 computeSurfaceQuadTree(surfaces
[surf
], quad
);
375 lr
.addSurface(0, 0, (uint8
)surfaces
[surf
].Material
, 0, 0, false, 0.0f
, false, surfaces
[surf
].Center
, quad
);
376 //lr.addSurface(0, 0, (uint8)surfaces[surf].Material, 0, 0, false, 0.0f, /*false,*/ surfaces[surf].Center, quad);
379 sint numBorderChains
= 0;
381 for (bord
=0; bord
<borders
.size(); ++bord
)
383 sint32 left
= borders
[bord
].Left
;
384 sint32 right
= (borders
[bord
].Right
== -2) ? CChain::convertBorderChainId(numBorderChains
++) : borders
[bord
].Right
;
386 if (left
<0 || left
>=(sint
)surfaces
.size() ||
387 right
>(sint
)surfaces
.size())
390 lr
.addChain(borders
[bord
].Vertices
, left
, right
);
397 void buildSnapping(CCollisionMeshBuild
&cmb
, CLocalRetriever
&lr
)
400 lr
.getInteriorVertices() = cmb
.Vertices
;
404 vector
<CLocalRetriever::CInteriorFace
> &faces
= lr
.getInteriorFaces();
405 for (i
=0; i
<cmb
.Faces
.size(); ++i
)
407 faces
.push_back(CLocalRetriever::CInteriorFace());
408 faces
.back().Verts
[0] = cmb
.Faces
[i
].V
[0];
409 faces
.back().Verts
[1] = cmb
.Faces
[i
].V
[1];
410 faces
.back().Verts
[2] = cmb
.Faces
[i
].V
[2];
411 faces
.back().Surface
= cmb
.Faces
[i
].InternalSurface
;
414 // create the face grid
420 // functions to build local retrievers
423 void buildExteriorMesh(CCollisionMeshBuild
&cmb
, CExteriorMesh
&em
)
425 // find the first non interior face
429 vector
<CExteriorMesh::CEdge
> edges
;
432 for (i
=0; i
<cmb
.Faces
.size(); ++i
)
434 cmb
.Faces
[i
].EdgeFlags
[0] = false;
435 cmb
.Faces
[i
].EdgeFlags
[1] = false;
436 cmb
.Faces
[i
].EdgeFlags
[2] = false;
444 for (; i
<cmb
.Faces
.size() && !found
; ++i
)
446 if (cmb
.Faces
[i
].Surface
!= CCollisionFace::ExteriorSurface
)
449 for (edge
=0; edge
<3 && !found
; ++edge
)
450 if (cmb
.Faces
[i
].Edge
[edge
] == -1 && !cmb
.Faces
[i
].EdgeFlags
[edge
])
465 sint32 next
= cmb
.Faces
[current
].Edge
[edge
];
468 sint pivot
= (edge
+1)%3;
469 sint nextEdge
= edge
;
471 uint firstExtEdge
= (uint
)edges
.size();
475 if (cmb
.Faces
[current
].EdgeFlags
[nextEdge
])
477 // if reaches the end of the border, then quits.
482 // if the next edge belongs to the border, then go on the same element
483 cmb
.Faces
[current
].EdgeFlags
[nextEdge
] = true;
484 sint link
= (cmb
.Faces
[current
].Visibility
[nextEdge
]) ? -1 : sint((numLink
++));
485 edges
.push_back(CExteriorMesh::CEdge(cmb
.Vertices
[cmb
.Faces
[current
].V
[pivot
]], link
));
486 nldebug("border: vertex=%d (%.2f,%.2f,%.2f) link=%d", cmb
.Faces
[current
].V
[pivot
], cmb
.Vertices
[cmb
.Faces
[current
].V
[pivot
]].x
, cmb
.Vertices
[cmb
.Faces
[current
].V
[pivot
]].y
, cmb
.Vertices
[cmb
.Faces
[current
].V
[pivot
]].z
, link
);
488 nextEdge
= (nextEdge
+1)%3;
489 next
= cmb
.Faces
[current
].Edge
[nextEdge
];
493 // if the next element is inside the surface, then go to the next element
494 for (oedge
=0; oedge
<3 && cmb
.Faces
[next
].Edge
[oedge
]!=current
; ++oedge
)
496 nlassert(oedge
!= 3);
499 nextEdge
= (oedge
+1)%3;
500 next
= cmb
.Faces
[current
].Edge
[nextEdge
];
504 edges
.push_back(edges
[firstExtEdge
]);
505 edges
.back().Link
= -2;
513 void linkExteriorToInterior(CLocalRetriever
&lr
)
515 CExteriorMesh em
= lr
.getExteriorMesh();
516 vector
<CExteriorMesh::CEdge
> edges
= em
.getEdges();
517 vector
<CExteriorMesh::CLink
> links
;
518 const vector
<CChain
> &chains
= lr
.getChains();
519 const vector
<COrderedChain3f
> &ochains
= lr
.getFullOrderedChains();
520 const vector
<uint16
> &bchains
= lr
.getBorderChains();
525 for (i
=0; i
<bchains
.size(); ++i
)
528 std::stringstream ss
;
529 const CChain
&chain
= chains
[bchains
[i
]];
530 sprintf(w
, "chain=%d ", bchains
[i
]);
533 for (och
=0; och
<chain
.getSubChains().size(); ++och
)
535 const COrderedChain3f
&ochain
= ochains
[chain
.getSubChain(och
)];
536 sprintf(w
, "subchain=%d", chain
.getSubChain(och
));
539 for (v
=0; v
<ochain
.getVertices().size(); ++v
)
541 sprintf(w
, " (%.2f,%.2f)", ochain
[v
].x
, ochain
[v
].y
);
546 nlinfo("%s", ss
.str().c_str());
551 for (edge
=0; edge
+1<edges
.size(); ++edge
)
553 if (edges
[edge
].Link
== -1)
556 CVector start
= edges
[edge
].Start
, stop
= edges
[edge
+1].Start
;
559 for (ch
=0; ch
<bchains
.size() && !found
; ++ch
)
561 // get the border chain.
562 //const CChain &chain = chains[bchains[ch]];
564 const CVector
&cstart
= lr
.getStartVector(bchains
[ch
]),
565 &cstop
= lr
.getStopVector(bchains
[ch
]);
567 float d
= (start
-cstart
).norm()+(stop
-cstop
).norm();
576 CExteriorMesh::CLink link
;
580 nlwarning("in linkInteriorToExterior():");
581 nlwarning("couldn't find any link to the exterior edge %d!!", edge
);
585 // set it up to point on the chain and surface
586 link
.BorderChainId
= uint16(ch
);
587 link
.ChainId
= bchains
[ch
];
588 link
.SurfaceId
= (uint16
)chains
[link
.ChainId
].getLeft();
592 if (edges
[edge
].Link
>= (sint
)links
.size())
593 links
.resize(edges
[edge
].Link
+1);
595 // if the link already exists, warning
596 if (links
[edges
[edge
].Link
].BorderChainId
!= 0xFFFF ||
597 links
[edges
[edge
].Link
].ChainId
!= 0xFFFF ||
598 links
[edges
[edge
].Link
].SurfaceId
!= 0xFFFF)
600 nlwarning("in linkInteriorToExterior():");
601 nlwarning("link %d already set!!", edges
[edge
].Link
);
605 links
[edges
[edge
].Link
] = link
;
610 lr
.setExteriorMesh(em
);
618 bool computeRetriever(CCollisionMeshBuild
&cmb
, CLocalRetriever
&lr
, CVector
&translation
, string
&error
, bool useCmbTrivialTranslation
)
621 lr
.setType(CLocalRetriever::Interior
);
623 // if should use the own cmb bbox, then compute it
624 if (useCmbTrivialTranslation
)
626 translation
= cmb
.computeTrivialTranslation();
627 // snap the translation vector to a meter wide grid
628 translation
.x
= (float)ceil(translation
.x
);
629 translation
.y
= (float)ceil(translation
.y
);
630 translation
.z
= 0.0f
;
633 vector
<string
> errors
;
635 cmb
.link(false, errors
);
636 cmb
.link(true, errors
);
640 nlwarning("Edge issues reported !!");
643 for (i
=0; i
<errors
.size(); ++i
)
644 error
+= errors
[i
]+"\n";
648 // translate the meshbuild to the local axis
649 cmb
.translate(translation
);
651 // find the exterior mesh border
652 CExteriorMesh extMesh
;
653 buildExteriorMesh(cmb
, extMesh
);
654 lr
.setExteriorMesh(extMesh
);
656 // build the surfaces in the local retriever
657 buildSurfaces(cmb
, lr
);
659 // create the snapping faces and vertices
660 // after the build surfaces because the InternalSurfaceId is filled within buildSurfaces()...
661 buildSnapping(cmb
, lr
);
664 lr
.computeLoopsAndTips();
666 lr
.findBorderChains();
668 lr
.computeTopologies();
672 lr
.computeCollisionChainQuad();
675 for (i=0; i<lr.getSurfaces().size(); ++i)
679 linkExteriorToInterior(lr
);
681 // compute the bbox of the retriever
686 for (i
=0; i
<extMesh
.getEdges().size(); ++i
)
688 bbox
.extend(extMesh
.getEdge(i
).Start
);
690 bbox
.setCenter(extMesh
.getEdge(i
).Start
), first
=false;
692 for (i
=0; i
<lr
.getOrderedChains().size(); ++i
)
693 for (j
=0; j
<lr
.getOrderedChain(i
).getVertices().size(); ++j
)
695 bbox
.extend(lr
.getOrderedChain(i
)[j
].unpack3f());
697 bbox
.setCenter(lr
.getOrderedChain(i
)[j
].unpack3f()), first
=false;
699 CVector bboxhs
= bbox
.getHalfSize();
701 bbox
.setHalfSize(bboxhs
);