1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/pacs/edge_quad.h"
20 #include "nel/pacs/global_retriever.h"
23 using namespace NLMISC
;
30 // ***************************************************************************
31 const float CEdgeQuad::_QuadElementSize
= 4; // = 4 meters.
34 // ***************************************************************************
35 CEdgeQuad::CEdgeQuad()
40 // ***************************************************************************
41 CEdgeQuad::~CEdgeQuad()
45 // ***************************************************************************
46 CEdgeQuad::CEdgeQuad(const CEdgeQuad
&o
)
52 // ***************************************************************************
53 CEdgeQuad
&CEdgeQuad::operator=(const CEdgeQuad
&o
)
55 // Alloc good quaddata.
56 _QuadDataLen
= o
._QuadDataLen
;
60 _QuadData
= (uint8
*)new uint8
[_QuadDataLen
];
62 memcpy(_QuadData
, o
._QuadData
, _QuadDataLen
);
72 _EdgeEntries
= o
._EdgeEntries
;
74 // copy good pointers.
76 _Quad
.resize(o
._Quad
.size(), NULL
);
77 for(sint i
=0; i
<(sint
)_Quad
.size(); i
++)
81 uint32 off
= (uint32
)(o
._Quad
[i
]-o
._QuadData
);
82 _Quad
[i
]= _QuadData
+off
;
90 // ***************************************************************************
91 void CEdgeQuad::clear()
105 // ***************************************************************************
106 void CEdgeQuad::getGridBounds(sint32
&x0
, sint32
&y0
, sint32
&x1
, sint32
&y1
, const CVector
&minP
, const CVector
&maxP
) const
108 x0
= (sint32
)floor(minP
.x
/ _QuadElementSize
) - _X
;
109 y0
= (sint32
)floor(minP
.y
/ _QuadElementSize
) - _Y
;
110 x1
= (sint32
) ceil(maxP
.x
/ _QuadElementSize
) - _X
;
111 y1
= (sint32
) ceil(maxP
.y
/ _QuadElementSize
) - _Y
;
112 x0
= max(x0
, (sint32
)0);
113 y0
= max(y0
, (sint32
)0);
114 x1
= min(x1
, (sint32
)_Width
);
115 y1
= min(y1
, (sint32
)_Height
);
120 // ***************************************************************************
121 void CEdgeQuad::build(const CExteriorMesh
&em
,
122 const CGlobalRetriever
&global
,
123 CCollisionSurfaceTemp
&cst
,
126 const std::vector
<CExteriorMesh::CEdge
> &edges
= em
.getEdges();
128 vector
< list
<uint16
> > tempQuad
;
131 // first, clear any pr-build.
137 // don't care about the origin of the instance
138 CVector origin
= global
.getInstance(thisInstance
).getOrigin();
140 // 0. Find BBox of the grid. Allocate grid.
141 //=========================================
143 CAABBox chainquadBBox
;
145 for (i
=0; i
<(sint
)edges
.size()-1; i
++)
149 first
= false, chainquadBBox
.setCenter(edges
[i
].Start
);
151 chainquadBBox
.extend(edges
[i
].Start
);
154 // compute X,Y,Width, Height.
155 _X
= (sint32
)floor(chainquadBBox
.getMin().x
/ _QuadElementSize
);
156 _Y
= (sint32
)floor(chainquadBBox
.getMin().y
/ _QuadElementSize
);
157 _Width
= (sint32
)ceil(chainquadBBox
.getMax().x
/ _QuadElementSize
) - _X
;
158 _Height
= (sint32
)ceil(chainquadBBox
.getMax().y
/ _QuadElementSize
) - _Y
;
160 tempQuad
.resize(_Width
*_Height
);
161 _Quad
.resize(_Width
*_Height
, NULL
);
164 // 1. For each edge, add them to the quadgrid.
165 //=========================================
167 for (i
=0; i
<(sint
)edges
.size()-1; i
++)
169 if (edges
[i
].Link
== -2)
172 float dnorm
= (edges
[i
+1].Start
-edges
[i
].Start
).norm();
173 uint numStep
= (uint
)(dnorm
/0.1f
)+1;
176 CVector pbegin
= edges
[i
].Start
+origin
,
177 pend
= edges
[i
+1].Start
+origin
;
179 CVector opbegin
= edges
[i
].Start
,
180 opend
= edges
[i
+1].Start
;
182 for (step
=0; step
<numStep
; ++step
)
184 float lambda0
= (float)(step
)/(float)(numStep
);
185 float lambda1
= (float)(step
+1)/(float)(numStep
);
186 CVector p0
= pbegin
*(1.0f
-lambda0
)+pend
*(lambda0
),
187 p1
= pbegin
*(1.0f
-lambda1
)+pend
*(lambda1
);
188 CVector op0
= opbegin
*(1.0f
-lambda0
)+opend
*(lambda0
),
189 op1
= opbegin
*(1.0f
-lambda1
)+opend
*(lambda1
);
193 uint prevEdge
= (i
-1)%(edges
.size()-1);
194 bool prio0
= (edges
[i
].Link
!=-1) || (edges
[prevEdge
].Link
!=-1);
196 UGlobalPosition gp0
= global
.retrievePosition(p0
);
197 global
.updateHeight(gp0
);
198 UGlobalPosition gp1
= global
.retrievePosition(p1
);
199 global
.updateHeight(gp1
);
208 if (gp0
.InstanceId
== -1)
215 const TCollisionSurfaceDescVector
*pcd
= global
.testCylinderMove(gp0
, p1
-p0
, 0.01f
, cst
);
219 // nlwarning("in CEdgeQuad::build(): testCylinderMove() returned NULL");
223 TCollisionSurfaceDescVector cd
= (*pcd
);
225 if (edges
[i
].Link
!= -1 && !cd
.empty())
227 nlwarning ("In NLPACS::CEdgeQuad::build()");
228 nlwarning ("ERROR: exterior edge %d with interior link crosses some surfaces", i
);
232 // add start surface to the collision description
233 CCollisionSurfaceDesc stcd
;
234 stcd
.ContactTime
= 0.0f
;
235 stcd
.ContactSurface
.RetrieverInstanceId
= gp0
.InstanceId
;
236 stcd
.ContactSurface
.SurfaceId
= gp0
.LocalPosition
.Surface
;
237 cd
.insert(cd
.begin(), stcd
);
239 // get the surface, chain ...
243 CSurfaceIdent interior
;
244 if (edges
[i
].Link
== -1)
246 interior
.RetrieverInstanceId
= -1;
247 interior
.SurfaceId
= -1;
252 interior
.RetrieverInstanceId
= thisInstance
;
253 interior
.SurfaceId
= em
.getLink(edges
[i
].Link
).SurfaceId
;
254 chainId
= em
.getLink(edges
[i
].Link
).ChainId
;
258 // add end point to the collision description
260 stcd
.ContactTime
= 1.0f
;
263 for (j
=0; j
<(sint
)cd
.size()-1; ++j
)
265 s0
= op0
*(float)(1.0-cd
[j
].ContactTime
) + op1
*(float)(cd
[j
].ContactTime
);
266 s1
= op0
*(float)(1.0-cd
[j
+1].ContactTime
) + op1
*(float)(cd
[j
+1].ContactTime
);
271 // PrecisionPb: extend a little this edge. This is important for special case like borders on zones.
273 mins
.x
-=0.001f
, maxs
.x
+=0.001f
;
275 mins
.y
-=0.001f
, maxs
.y
+=0.001f
;
277 // get bounding coordinate of this edge in the quadgrid.
278 sint32 x0
, y0
, x1
, y1
;
280 getGridBounds(x0
, y0
, x1
, y1
, mins
, maxs
);
282 CSurfaceIdent exterior
= cd
[j
].ContactSurface
;
285 for (entry
=0; entry
<_EdgeEntries
.size(); ++entry
)
287 if (_EdgeEntries
[entry
].EdgeId
== edgeId
&&
288 _EdgeEntries
[entry
].Exterior
== exterior
)
290 if (_EdgeEntries
[entry
].ChainId
!= chainId
||
291 _EdgeEntries
[entry
].Interior
!= interior
)
293 nlwarning("In NLPACS::CEdgeQuad::build()");
294 nlerror("exterior edge %d has different interior linkage", edgeId
);
301 // if this entry didn't exist before create a new one...
302 if (entry
== _EdgeEntries
.size())
304 _EdgeEntries
.push_back(CExteriorEdgeEntry());
305 _EdgeEntries
.back().EdgeId
= uint16(edgeId
);
306 _EdgeEntries
.back().ChainId
= chainId
;
307 _EdgeEntries
.back().Interior
= interior
;
308 _EdgeEntries
.back().Exterior
= exterior
;
311 // add this edge to all the quadnode it touches.
316 // check we don't push this entry twice
317 list
<uint16
>::iterator it
;
318 for (it
=tempQuad
[y
*_Width
+x
].begin(); it
!=tempQuad
[y
*_Width
+x
].end(); ++it
)
321 if (it
== tempQuad
[y
*_Width
+x
].end())
322 tempQuad
[y
*_Width
+x
].push_back(uint16(entry
));
330 nlinfo("Built ExteriorEdgeQuad, linked following doors:");
331 for (i
=0; i
<(sint
)_EdgeEntries
.size(); ++i
)
333 if (edges
[_EdgeEntries
[i
].EdgeId
].Link
!= -1 &&
334 (_EdgeEntries
[i
].Interior
.RetrieverInstanceId
== -1 || _EdgeEntries
[i
].Interior
.SurfaceId
== -1 ||
335 _EdgeEntries
[i
].Exterior
.RetrieverInstanceId
== -1 || _EdgeEntries
[i
].Exterior
.SurfaceId
== -1))
337 nlwarning("In NLPACS::CEdgeQuad::build(): exterior door %d has corrupted link", i
);
339 else if (edges
[_EdgeEntries
[i
].EdgeId
].Link
!= -1)
341 nlinfo("Inst=%d ExtEdge=%d IntInst=%d IntSurf=%d IntChain=%d ExtInst=%d ExtSurf=%d", thisInstance
, _EdgeEntries
[i
].EdgeId
,
342 _EdgeEntries
[i
].Interior
.RetrieverInstanceId
, _EdgeEntries
[i
].Interior
.SurfaceId
, _EdgeEntries
[i
].ChainId
,
343 _EdgeEntries
[i
].Exterior
.RetrieverInstanceId
, _EdgeEntries
[i
].Exterior
.SurfaceId
);
347 // 2. Mem optimisation: Use only 1 block for ALL quads of the grid.
348 //=========================================
351 for(i
=0;i
<(sint
)tempQuad
.size();i
++)
353 list
<uint16
> &quadNode
= tempQuad
[i
];
355 if(!quadNode
.empty())
357 // add an entry for Len.
358 memSize
+= sizeof(uint16
);
359 // add N entry of CEdgeChainEntry.
360 memSize
+= (sint
)quadNode
.size()*sizeof(uint16
);
365 _QuadData
= (uint8
*)new uint8
[memSize
];
366 _QuadDataLen
= memSize
;
369 // 3. Fill _QuadData with lists.
370 //=========================================
371 uint8
*ptr
= _QuadData
;
372 for(i
=0;i
<(sint
)tempQuad
.size();i
++)
374 list
<uint16
> &srcQuadNode
= tempQuad
[i
];
375 list
<uint16
>::iterator it
;
377 if(!srcQuadNode
.empty())
382 uint16 len
= uint16(srcQuadNode
.size());
383 *((uint16
*)ptr
)= len
;
384 ptr
+= sizeof(uint16
);
387 it
= srcQuadNode
.begin();
388 for(j
=0; j
<len
; j
++, it
++)
390 *((uint16
*)ptr
)= *it
;
391 ptr
+= sizeof(uint16
);
400 // ***************************************************************************
401 sint
CEdgeQuad::selectEdges(const NLMISC::CAABBox
&bbox
, CCollisionSurfaceTemp
&cst
) const
405 uint16
*indexLUT
= cst
.OChainLUT
;
407 // start: no edge found.
408 cst
.ExteriorEdgeIndexes
.clear();
410 // get bounding coordinate of this bbox in the quadgrid.
411 sint32 x0
, y0
, x1
, y1
;
412 getGridBounds(x0
, y0
, x1
, y1
, bbox
.getMin(), bbox
.getMax());
415 // run all intersected quads.
416 for (sint y
= y0
; y
<y1
; y
++)
418 for (sint x
= x0
; x
<x1
; x
++)
420 uint8
*quadNode
= _Quad
[y
*_Width
+x
];
422 // no edgechain entry??
426 // get edgechain entries
427 sint numExteriorEdgeIndexes
= *((uint16
*)quadNode
);
428 quadNode
+= sizeof(uint16
);
429 uint16
*ptrExteriorEdgeIndex
= (uint16
*)quadNode
;
431 // For each one, add it to the result list.
432 for (i
=0;i
<numExteriorEdgeIndexes
;i
++)
434 uint16 index
= ptrExteriorEdgeIndex
[i
];
436 // if ochain not yet inserted.
437 if (indexLUT
[index
]==0xFFFF)
440 indexLUT
[index
]= uint16(nRes
);
441 cst
.ExteriorEdgeIndexes
.push_back(index
);
449 // reset LUT to 0xFFFF for all ochains selected.
451 indexLUT
[cst
.ExteriorEdgeIndexes
[i
]]= 0xFFFF;
456 sint
CEdgeQuad::selectEdges(CVector start
, CVector end
, CCollisionSurfaceTemp
&cst
) const
460 uint16
*indexLUT
= cst
.OChainLUT
;
462 // start: no edge found.
463 cst
.ExteriorEdgeIndexes
.clear();
468 float minx
= _X
*_QuadElementSize
,
469 miny
= _Y
*_QuadElementSize
,
470 maxx
= minx
+ _Width
*_QuadElementSize
,
471 maxy
= miny
+ _Height
*_QuadElementSize
;
473 if (start
.x
> maxx
|| end
.x
< minx
|| start
.y
> maxy
|| end
.y
< miny
)
478 start
.y
= start
.y
+(end
.y
-start
.y
)*(minx
-start
.x
)/(end
.x
-start
.x
);
484 start
.x
= start
.x
+(end
.x
-start
.x
)*(miny
-start
.y
)/(end
.y
-start
.y
);
490 end
.y
= start
.y
+(end
.y
-start
.y
)*(minx
-start
.x
)/(end
.x
-start
.x
);
496 end
.x
= start
.x
+(end
.x
-start
.x
)*(miny
-start
.y
)/(end
.y
-start
.y
);
500 sint32 x0
, x1
, ya
, yb
;
502 float fx
, fxa
, fxb
, fya
, fyb
;
504 x0
= (sint32
)floor(start
.x
/ _QuadElementSize
) - _X
;
505 x1
= (sint32
)ceil(end
.x
/ _QuadElementSize
) - _X
;
506 fx
= (x0
+_X
)*_QuadElementSize
;
508 for (x
=x0
; x
<x1
; ++x
)
510 fxa
= (fx
< start
.x
) ? start
.x
: fx
;
511 fxb
= (fx
+_QuadElementSize
> end
.x
) ? end
.x
: fx
+_QuadElementSize
;
513 fya
= start
.y
+(end
.y
-start
.y
)*(fxa
-start
.x
)/(end
.x
-start
.x
);
514 fyb
= start
.y
+(end
.y
-start
.y
)*(fxb
-start
.x
)/(end
.x
-start
.x
);
519 ya
= (sint32
)floor(fya
/ _QuadElementSize
) - _Y
;
520 yb
= (sint32
)ceil(fyb
/ _QuadElementSize
) - _Y
;
522 fx
+= _QuadElementSize
;
524 for (y
=ya
; y
<yb
; ++y
)
526 uint8
*quadNode
= _Quad
[y
*_Width
+x
];
528 // no edgechain entry??
532 // get edgechain entries
533 sint numExteriorEdgeIndexes
= *((uint16
*)quadNode
);
534 quadNode
+= sizeof(uint16
);
535 uint16
*ptrExteriorEdgeIndex
= (uint16
*)quadNode
;
537 // For each one, add it to the result list.
538 for(i
=0;i
<numExteriorEdgeIndexes
;i
++)
540 uint16 index
= ptrExteriorEdgeIndex
[i
];
542 // if ochain not yet inserted.
543 if(indexLUT
[index
]==0xFFFF)
546 indexLUT
[index
]= uint16(nRes
);
547 cst
.ExteriorEdgeIndexes
.push_back(ptrExteriorEdgeIndex
[i
]);
554 // reset LUT to 0xFFFF for all ochains selected.
556 indexLUT
[cst
.ExteriorEdgeIndexes
[i
]]= 0xFFFF;
561 // ***************************************************************************
562 void CEdgeQuad::serial(NLMISC::IStream
&f
)
568 (void)f
.serialVersion(0);
572 f
.serial(_X
, _Y
, _Width
, _Height
, _QuadDataLen
);
573 f
.serialCont(_EdgeEntries
);
580 _QuadData
= (uint8
*)new uint8
[_QuadDataLen
];
584 // Since we have only uint16 (see CEdgeChainEntry), serial them in a single block.
585 uint16
*ptrQData
= (uint16
*)_QuadData
;
586 for(i
=0;i
<_QuadDataLen
/2; i
++, ptrQData
++)
593 std::vector
<uint32
> offsets
;
604 // read offsets -> ptrs.
611 _Quad
[i
]= _QuadData
+val
;
617 len
= (uint32
)_Quad
.size();
623 uint8
*ptr
= _Quad
[i
];
627 val
= (uint32
)(ptr
-_QuadData
);