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/chain_quad.h"
22 using namespace NLMISC
;
29 // ***************************************************************************
30 const float CChainQuad::_QuadElementSize
= 4; // = 4 meters.
33 // ***************************************************************************
34 CChainQuad::CChainQuad()
39 // ***************************************************************************
40 CChainQuad::~CChainQuad()
46 // ***************************************************************************
47 CChainQuad::CChainQuad(const CChainQuad
&o
)
53 // ***************************************************************************
54 CChainQuad
&CChainQuad::operator=(const CChainQuad
&o
)
56 // Alloc good quaddata.
57 _QuadDataLen
= o
._QuadDataLen
;
61 _QuadData
= (uint8
*)new uint8
[_QuadDataLen
];
63 memcpy(_QuadData
, o
._QuadData
, _QuadDataLen
);
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
;
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
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
;
118 // first, clear any pr-build.
125 // 0. Find BBox of the grid. Allocate grid.
126 //=========================================
128 CAABBox chainquadBBox
;
130 for(i
=0;i
<(sint
)ochains
.size();i
++)
132 const std::vector
<CVector2s
> &vertices
= ochains
[i
].getVertices();
135 for(j
= 0; j
<(sint
)vertices
.size();j
++)
139 first
= false, chainquadBBox
.setCenter(vertices
[j
].unpack3f());
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 //=========================================
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;
165 for(j
= 0; j
<numEdges
; j
++)
167 const CVector p0
= vertices
[j
].unpack3f();
168 const CVector p1
= vertices
[j
+1].unpack3f();
172 // PrecisionPb: extend a little this edge. This is important for special case like borders on zones.
174 minP
.x
-=0.001f
, maxP
.x
+=0.001f
;
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 //=========================================
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
);
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())
232 uint16 len
= uint16(srcQuadNode
.size());
233 *((uint16
*)ptr
)= len
;
234 ptr
+= sizeof(uint16
);
237 it
= srcQuadNode
.begin();
238 for(j
=0; j
<len
; j
++, it
++)
240 *((CEdgeChainEntry
*)ptr
)= *it
;
241 ptr
+= sizeof(CEdgeChainEntry
);
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));
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
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??
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)
319 ochainLUT
[ochainId
]= uint16(nRes
);
320 cst
.EdgeChainEntries
.push_back(ptrEdgeChainEntry
[i
]);
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.
339 uint16 ochainId
= cst
.EdgeChainEntries
[i
].OChainId
;
340 ochainLUT
[ochainId
]= 0xFFFF;
347 sint
CChainQuad::selectEdges(CVector start
, CVector end
, CCollisionSurfaceTemp
&cst
) const
351 uint16
*ochainLUT
= cst
.OChainLUT
;
353 // start: no edge found.
354 cst
.EdgeChainEntries
.clear();
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
)
369 start
.y
= start
.y
+(end
.y
-start
.y
)*(minx
-start
.x
)/(end
.x
-start
.x
);
375 start
.x
= start
.x
+(end
.x
-start
.x
)*(miny
-start
.y
)/(end
.y
-start
.y
);
381 end
.y
= start
.y
+(end
.y
-start
.y
)*(minx
-start
.x
)/(end
.x
-start
.x
);
387 end
.x
= start
.x
+(end
.x
-start
.x
)*(miny
-start
.y
)/(end
.y
-start
.y
);
391 sint32 x0
, x1
, ya
, yb
;
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
);
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??
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)
437 ochainLUT
[ochainId
]= uint16(nRes
);
438 cst
.EdgeChainEntries
.push_back(ptrEdgeChainEntry
[i
]);
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.
456 uint16 ochainId
= cst
.EdgeChainEntries
[i
].OChainId
;
457 ochainLUT
[ochainId
]= 0xFFFF;
463 // ***************************************************************************
464 void CChainQuad::serial(NLMISC::IStream
&f
)
470 (void)f
.serialVersion(0);
474 f
.serial(_X
, _Y
, _Width
, _Height
, _QuadDataLen
);
482 _QuadData
= (uint8
*)new uint8
[_QuadDataLen
];
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
++)
495 std::vector
<uint32
> offsets
;
506 // read offsets -> ptrs.
513 _Quad
[i
]= _QuadData
+val
;
519 len
= (uint32
)_Quad
.size();
525 uint8
*ptr
= _Quad
[i
];
529 val
= (uint32
)(ptr
-_QuadData
);