Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / pacs / chain_quad.cpp
blob321761953ceded084545439d6fcc5234b7bfd55f
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "stdpacs.h"
19 #include "nel/pacs/chain_quad.h"
21 using namespace std;
22 using namespace NLMISC;
25 namespace NLPACS
29 // ***************************************************************************
30 const float CChainQuad::_QuadElementSize= 4; // = 4 meters.
33 // ***************************************************************************
34 CChainQuad::CChainQuad()
36 _QuadData= NULL;
37 _QuadDataLen= 0;
39 // ***************************************************************************
40 CChainQuad::~CChainQuad()
42 delete [] _QuadData;
43 _QuadData= NULL;
44 _QuadDataLen= 0;
46 // ***************************************************************************
47 CChainQuad::CChainQuad(const CChainQuad &o)
49 _QuadData= NULL;
50 _QuadDataLen= 0;
51 *this= o;
53 // ***************************************************************************
54 CChainQuad &CChainQuad::operator=(const CChainQuad &o)
56 // Alloc good quaddata.
57 _QuadDataLen= o._QuadDataLen;
58 delete [] _QuadData;
59 if(_QuadDataLen>0)
61 _QuadData= (uint8*)new uint8[_QuadDataLen];
62 // copy contents.
63 memcpy(_QuadData, o._QuadData, _QuadDataLen);
65 else
66 _QuadData= NULL;
68 // copy infos.
69 _Width= o._Width;
70 _Height= o._Height;
71 _X= o._X;
72 _Y= o._Y;
74 // copy good pointers.
75 _Quad.clear();
76 _Quad.resize(o._Quad.size(), NULL);
77 for(sint i=0; i<(sint)_Quad.size(); i++)
79 if(o._Quad[i])
81 uint32 off= (uint32)(o._Quad[i]-o._QuadData);
82 _Quad[i]= _QuadData+off;
87 return *this;
92 // ***************************************************************************
93 void CChainQuad::getGridBounds(sint32 &x0, sint32 &y0, sint32 &x1, sint32 &y1, const CVector &minP, const CVector &maxP) const
95 x0= (sint32)floor(minP.x / _QuadElementSize) - _X;
96 y0= (sint32)floor(minP.y / _QuadElementSize) - _Y;
97 x1= (sint32) ceil(maxP.x / _QuadElementSize) - _X;
98 y1= (sint32) ceil(maxP.y / _QuadElementSize) - _Y;
99 // Manage selection of a point exactly on a quad bound
100 if(x1-x0==0)
101 x0--, x1++;
102 if(y1-y0==0)
103 y0--, y1++;
104 // clamp
105 x0= max(x0, (sint32)0);
106 y0= max(y0, (sint32)0);
107 x1= min(x1, (sint32)_Width);
108 y1= min(y1, (sint32)_Height);
112 // ***************************************************************************
113 void CChainQuad::build(const std::vector<COrderedChain> &ochains)
115 vector< list<CEdgeChainEntry> > tempQuad;
116 sint i,j;
118 // first, clear any pr-build.
119 contReset(_Quad);
120 delete [] _QuadData;
121 _QuadData= NULL;
122 _QuadDataLen= 0;
125 // 0. Find BBox of the grid. Allocate grid.
126 //=========================================
127 bool first=true;
128 CAABBox chainquadBBox;
129 // run all chains.
130 for(i=0;i<(sint)ochains.size();i++)
132 const std::vector<CVector2s> &vertices= ochains[i].getVertices();
134 // run all vertices.
135 for(j= 0; j<(sint)vertices.size();j++)
137 // enlarge bbox.
138 if(first)
139 first= false, chainquadBBox.setCenter(vertices[j].unpack3f());
140 else
141 chainquadBBox.extend(vertices[j].unpack3f());
145 // compute X,Y,Width, Height.
146 _X= (sint32)floor(chainquadBBox.getMin().x / _QuadElementSize);
147 _Y= (sint32)floor(chainquadBBox.getMin().y / _QuadElementSize);
148 _Width= (sint32)ceil(chainquadBBox.getMax().x / _QuadElementSize) - _X;
149 _Height= (sint32)ceil(chainquadBBox.getMax().y / _QuadElementSize) - _Y;
151 tempQuad.resize(_Width*_Height);
152 _Quad.resize(_Width*_Height, NULL);
155 // 1. For each edge, add them to the quadgrid.
156 //=========================================
157 // run all chains.
158 for(i=0;i<(sint)ochains.size();i++)
160 const std::vector<CVector2s> &vertices= ochains[i].getVertices();
162 sint numEdges= (sint)vertices.size()-1;
164 // run all edges.
165 for(j= 0; j<numEdges; j++)
167 const CVector p0= vertices[j].unpack3f();
168 const CVector p1= vertices[j+1].unpack3f();
169 CVector minP,maxP;
170 minP.minof(p0, p1);
171 maxP.maxof(p0, p1);
172 // PrecisionPb: extend a little this edge. This is important for special case like borders on zones.
173 if(minP.x-maxP.x==0)
174 minP.x-=0.001f, maxP.x+=0.001f;
175 if(minP.y-maxP.y==0)
176 minP.y-=0.001f, maxP.y+=0.001f;
179 // get bounding coordinate of this edge in the quadgrid.
180 sint32 x0, y0, x1, y1;
181 getGridBounds(x0, y0, x1, y1, minP, maxP);
183 // add this edge to all the quadnode it touch.
184 for(sint y= y0; y<y1; y++)
186 for(sint x= x0; x<x1; x++)
188 list<CEdgeChainEntry> &quadNode= tempQuad[y*_Width+x];
190 addEdgeToQuadNode(quadNode, i, j);
197 // 2. Mem optimisation: Use only 1 block for ALL quads of the grid.
198 //=========================================
199 sint memSize= 0;
200 // run all quads.
201 for(i=0;i<(sint)tempQuad.size();i++)
203 list<CEdgeChainEntry> &quadNode= tempQuad[i];
205 if(!quadNode.empty())
207 // add an entry for Len.
208 memSize+= sizeof(uint16);
209 // add N entry of CEdgeChainEntry.
210 memSize+= (sint)quadNode.size()*sizeof(CEdgeChainEntry);
214 // allocate.
215 _QuadData= (uint8*)new uint8[memSize];
216 _QuadDataLen= memSize;
219 // 3. Fill _QuadData with lists.
220 //=========================================
221 uint8 *ptr= _QuadData;
222 for(i=0;i<(sint)tempQuad.size();i++)
224 list<CEdgeChainEntry> &srcQuadNode= tempQuad[i];
225 list<CEdgeChainEntry>::iterator it;
227 if(!srcQuadNode.empty())
229 _Quad[i]= ptr;
231 // write len.
232 uint16 len= uint16(srcQuadNode.size());
233 *((uint16*)ptr)= len;
234 ptr+= sizeof(uint16);
236 // add entries.
237 it= srcQuadNode.begin();
238 for(j=0; j<len; j++, it++)
240 *((CEdgeChainEntry*)ptr)= *it;
241 ptr+= sizeof(CEdgeChainEntry);
247 // End.
251 // ***************************************************************************
252 void CChainQuad::addEdgeToQuadNode(list<CEdgeChainEntry> &quadNode, sint ochainId, sint edgeId)
254 // 0. try to find, insert an edge in an existing CEdgeChainEntry.
255 //=========================================
256 list<CEdgeChainEntry>::iterator it;
257 for(it= quadNode.begin(); it!=quadNode.end();it++)
259 if(it->OChainId==ochainId)
261 // selection is faster if we only manages a single start/end block.
262 it->EdgeStart= min(it->EdgeStart, (uint16)edgeId);
263 it->EdgeEnd= max(it->EdgeEnd, (uint16)(edgeId+1));
264 return;
269 // 1. else, create new one.
270 //=========================================
271 CEdgeChainEntry entry;
272 entry.OChainId= uint16(ochainId);
273 entry.EdgeStart= uint16(edgeId);
274 entry.EdgeEnd= uint16(edgeId+1);
275 quadNode.push_back(entry);
279 // ***************************************************************************
280 sint CChainQuad::selectEdges(const NLMISC::CAABBox &bbox, CCollisionSurfaceTemp &cst) const
282 sint nRes=0;
283 sint i;
284 uint16 *ochainLUT= cst.OChainLUT;
286 // start: no edge found.
287 cst.EdgeChainEntries.clear();
289 // get bounding coordinate of this bbox in the quadgrid.
290 sint32 x0, y0, x1, y1;
291 getGridBounds(x0, y0, x1, y1, bbox.getMin(), bbox.getMax());
294 // run all intersected quads.
295 for(sint y= y0; y<y1; y++)
297 for(sint x= x0; x<x1; x++)
299 uint8 *quadNode= _Quad[y*_Width+x];
301 // no edgechain entry??
302 if(!quadNode)
303 continue;
305 // get edgechain entries
306 sint numEdgeChainEntries= *((uint16*)quadNode);
307 quadNode+= sizeof(uint16);
308 CEdgeChainEntry *ptrEdgeChainEntry= (CEdgeChainEntry*)quadNode;
310 // For each one, add it to the result list.
311 for(i=0;i<numEdgeChainEntries;i++)
313 uint16 ochainId= ptrEdgeChainEntry[i].OChainId;
315 // if ochain not yet inserted.
316 if(ochainLUT[ochainId]==0xFFFF)
318 // inc the list.
319 ochainLUT[ochainId]= uint16(nRes);
320 cst.EdgeChainEntries.push_back(ptrEdgeChainEntry[i]);
321 nRes++;
323 else
325 // extend the entry in the list.
326 uint16 id= ochainLUT[ochainId];
327 CEdgeChainEntry &ece= cst.EdgeChainEntries[id];
328 ece.EdgeStart= min(ece.EdgeStart, ptrEdgeChainEntry[i].EdgeStart);
329 ece.EdgeEnd= max(ece.EdgeEnd, ptrEdgeChainEntry[i].EdgeEnd);
336 // reset LUT to 0xFFFF for all ochains selected.
337 for(i=0;i<nRes;i++)
339 uint16 ochainId= cst.EdgeChainEntries[i].OChainId;
340 ochainLUT[ochainId]= 0xFFFF;
344 return nRes;
347 sint CChainQuad::selectEdges(CVector start, CVector end, CCollisionSurfaceTemp &cst) const
349 sint nRes=0;
350 sint i;
351 uint16 *ochainLUT= cst.OChainLUT;
353 // start: no edge found.
354 cst.EdgeChainEntries.clear();
356 if (end.x < start.x)
357 swap(start, end);
359 float minx = _X*_QuadElementSize,
360 miny = _Y*_QuadElementSize,
361 maxx = minx + _Width*_QuadElementSize,
362 maxy = miny + _Height*_QuadElementSize;
364 if (start.x > maxx || end.x < minx || start.y > maxy || end.y < miny)
365 return nRes;
367 if (start.x < minx)
369 start.y = start.y+(end.y-start.y)*(minx-start.x)/(end.x-start.x);
370 start.x = minx;
373 if (start.y < miny)
375 start.x = start.x+(end.x-start.x)*(miny-start.y)/(end.y-start.y);
376 start.y = miny;
379 if (end.x > maxx)
381 end.y = start.y+(end.y-start.y)*(minx-start.x)/(end.x-start.x);
382 end.x = maxx;
385 if (end.y > maxy)
387 end.x = start.x+(end.x-start.x)*(miny-start.y)/(end.y-start.y);
388 end.y = maxy;
391 sint32 x0, x1, ya, yb;
392 sint x, y;
393 float fx, fxa, fxb, fya, fyb;
395 x0 = (sint32)floor(start.x / _QuadElementSize) - _X;
396 x1 = (sint32)ceil(end.x / _QuadElementSize) - _X;
397 fx = (x0+_X)*_QuadElementSize;
399 for (x=x0; x<x1; ++x)
401 fxa = (fx < start.x) ? start.x : fx;
402 fxb = (fx+_QuadElementSize > end.x) ? end.x : fx+_QuadElementSize;
404 fya = start.y+(end.y-start.y)*(fxa-start.x)/(end.x-start.x);
405 fyb = start.y+(end.y-start.y)*(fxb-start.x)/(end.x-start.x);
407 if (fya > fyb)
408 swap (fya, fyb);
410 ya = (sint32)floor(fya / _QuadElementSize) - _Y;
411 yb = (sint32)ceil(fyb / _QuadElementSize) - _Y;
413 fx += _QuadElementSize;
415 for (y=ya; y<yb; ++y)
417 uint8 *quadNode= _Quad[y*_Width+x];
419 // no edgechain entry??
420 if(!quadNode)
421 continue;
423 // get edgechain entries
424 sint numEdgeChainEntries= *((uint16*)quadNode);
425 quadNode+= sizeof(uint16);
426 CEdgeChainEntry *ptrEdgeChainEntry= (CEdgeChainEntry*)quadNode;
428 // For each one, add it to the result list.
429 for(i=0;i<numEdgeChainEntries;i++)
431 uint16 ochainId= ptrEdgeChainEntry[i].OChainId;
433 // if ochain not yet inserted.
434 if(ochainLUT[ochainId]==0xFFFF)
436 // inc the list.
437 ochainLUT[ochainId]= uint16(nRes);
438 cst.EdgeChainEntries.push_back(ptrEdgeChainEntry[i]);
439 nRes++;
441 else
443 // extend the entry in the list.
444 uint16 id= ochainLUT[ochainId];
445 CEdgeChainEntry &ece= cst.EdgeChainEntries[id];
446 ece.EdgeStart= min(ece.EdgeStart, ptrEdgeChainEntry[i].EdgeStart);
447 ece.EdgeEnd= max(ece.EdgeEnd, ptrEdgeChainEntry[i].EdgeEnd);
453 // reset LUT to 0xFFFF for all ochains selected.
454 for(i=0;i<nRes;i++)
456 uint16 ochainId= cst.EdgeChainEntries[i].OChainId;
457 ochainLUT[ochainId]= 0xFFFF;
460 return nRes;
463 // ***************************************************************************
464 void CChainQuad::serial(NLMISC::IStream &f)
467 Version 0:
468 - base version.
470 (void)f.serialVersion(0);
471 uint i;
473 // serial basics.
474 f.serial(_X, _Y, _Width, _Height, _QuadDataLen);
477 // serial _QuadData.
478 if(f.isReading())
480 delete [] _QuadData;
481 if(_QuadDataLen>0)
482 _QuadData= (uint8*)new uint8[_QuadDataLen];
483 else
484 _QuadData= NULL;
486 // Since we have only uint16 (see CEdgeChainEntry), serial them in a single block.
487 uint16 *ptrQData= (uint16*)_QuadData;
488 for(i=0;i<_QuadDataLen/2; i++, ptrQData++)
490 f.serial(*ptrQData);
494 // serial _Quad.
495 std::vector<uint32> offsets;
496 uint32 len;
497 uint32 val;
498 if(f.isReading())
500 // len/resize.
501 f.serial(len);
502 offsets.resize(len);
503 contReset(_Quad);
504 _Quad.resize(len);
506 // read offsets -> ptrs.
507 for(i=0; i<len; i++)
509 f.serial(val);
510 if(val== 0xFFFFFFFF)
511 _Quad[i]= NULL;
512 else
513 _Quad[i]= _QuadData+val;
516 else
518 // len/resize.
519 len= (uint32)_Quad.size();
520 f.serial(len);
522 // write offsets.
523 for(i=0; i<len; i++)
525 uint8 *ptr= _Quad[i];
526 if(ptr==NULL)
527 val= 0xFFFFFFFF;
528 else
529 val= (uint32)(ptr-_QuadData);
530 f.serial(val);
538 } // NLPACS