1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
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/>.
18 #include "landscape_poly_drawer.h"
21 #include "nel/3d/u_driver.h"
22 #include "nel/3d/u_scene.h"
23 #include "nel/3d/u_landscape.h"
26 #include "nel/3d/zone.h"
27 #include "nel/3d/driver_user.h"
32 using namespace NLMISC
;
38 extern UDriver
*Driver
;
40 extern UMaterial GenericMat
;
41 extern ULandscape
*Landscape
;
44 //-----------------------------------------------------------------------------------------------------------
45 //---------------------------------------- CInitStencil -----------------------------------------------------
46 //-----------------------------------------------------------------------------------------------------------
48 void CInitStencil::init()
50 // set ILandscapePolyDrawingCallback attribute of current scene
51 Scene
->setLandscapePolyDrawingCallback(this);
54 //-----------------------------------------------------------------------------------------------------------
56 void CInitStencil::beginPolyDrawing()
58 // The eighth bit will be written with a 1 during next render (landscape)
59 Driver
->stencilOp(UDriver::keep
, UDriver::keep
, UDriver::replace
);
62 //-----------------------------------------------------------------------------------------------------------
64 void CInitStencil::endPolyDrawing()
66 // The eighth bit will be written with a 0 during next render (veget,...)
67 Driver
->stencilOp(UDriver::keep
, UDriver::keep
, UDriver::zero
);
71 //-----------------------------------------------------------------------------------------------------------
72 //---------------------------------- CLandscapePolyDrawer ---------------------------------------------------
73 //-----------------------------------------------------------------------------------------------------------
75 CLandscapePolyDrawer::CLandscapePolyDrawer()
77 // set ILandscapePolyDrawingCallback attribute of current scene to modify stencil operation
78 // from NEL classes with callbacks.
79 _InitStencil
= new CInitStencil();
83 _Shadow
.V0
= CVector(0.0, 0.0, 0.0);
84 _Shadow
.V1
= CVector(1.0, 0.0, 0.0);
85 _Shadow
.V2
= CVector(1.0, 1.0, 0.0);
86 _Shadow
.V3
= CVector(0.0, 1.0, 0.0);
89 //-----------------------------------------------------------------------------------------------------------
91 void CLandscapePolyDrawer::initLandscapePolyDrawingCallback()
93 // set ILandscapePolyDrawingCallback attribute of current scene to modify stencil operation
94 // from NEL classes with callbacks.
95 nlassert(_InitStencil
);
99 //-----------------------------------------------------------------------------------------------------------
101 CLandscapePolyDrawer::~CLandscapePolyDrawer()
106 //-----------------------------------------------------------------------------------------------------------
108 void CLandscapePolyDrawer::addPoly(const NLMISC::CPolygon2D
&poly
,
109 const NLMISC::CRGBA
& color
, const NLMISC::CAABBox
& bBox
)
111 nlassert(!poly
.Vertices
.empty());
112 _Polygons
.push_back(poly
);
113 _PolyColor
.push_back(color
);
114 _BBoxes
.push_back(bBox
);
116 buildShadowVolume((uint
)_Polygons
.size());
119 //-----------------------------------------------------------------------------------------------------------
121 void CLandscapePolyDrawer::beginRenderLandscapePolyPart()
123 // activation of stencil test
124 Driver
->enableStencilTest(true);
126 Driver
->stencilFunc(UDriver::always
, 128, 0xff);
128 // the eighth bit will be written with a 0 during next render to mark stencil buffer
129 // parts which will support Shadow Volume algorithm (landscape).
130 // When stencil operation will be modify with "replace" operation, the eighth bit will be written
131 // with a 1 (vegeation, shadow...)
132 Driver
->stencilOp(UDriver::keep
, UDriver::keep
, UDriver::zero
);
135 //-----------------------------------------------------------------------------------------------------------
137 inline void createFace(CVector
* face
, const vector
<CVector
> & vertices
, uint v1
,
138 uint v2
, uint v3
, uint v4
)
140 face
[0] = vertices
[v1
];
141 face
[1] = vertices
[v2
];
142 face
[2] = vertices
[v3
];
143 face
[3] = vertices
[v4
];
146 void CLandscapePolyDrawer::setScissor(uint polyId
)
148 // vector of bounding box vertices
149 const CAABBox
& bBox
= _BBoxes
[polyId
];
150 const CVector
& center
= bBox
.getCenter();
151 const CVector
& halfSize
= bBox
.getHalfSize();
153 vector
<CVector
> vertices(8);
154 vertices
[0] = CVector(center
.x
-halfSize
.x
, center
.y
+halfSize
.y
, center
.z
+halfSize
.z
);
155 vertices
[1] = CVector(center
.x
+halfSize
.x
, center
.y
+halfSize
.y
, center
.z
+halfSize
.z
);
156 vertices
[2] = CVector(center
.x
+halfSize
.x
, center
.y
-halfSize
.y
, center
.z
+halfSize
.z
);
157 vertices
[3] = CVector(center
.x
-halfSize
.x
, center
.y
-halfSize
.y
, center
.z
+halfSize
.z
);
158 vertices
[4] = CVector(center
.x
-halfSize
.x
, center
.y
+halfSize
.y
, center
.z
-halfSize
.z
);
159 vertices
[5] = CVector(center
.x
+halfSize
.x
, center
.y
+halfSize
.y
, center
.z
-halfSize
.z
);
160 vertices
[6] = CVector(center
.x
+halfSize
.x
, center
.y
-halfSize
.y
, center
.z
-halfSize
.z
);
161 vertices
[7] = CVector(center
.x
-halfSize
.x
, center
.y
-halfSize
.y
, center
.z
-halfSize
.z
);
163 // transform each point to obtain vertices coordinates in camera location
164 CMatrix transformMatrix
= Scene
->getCam().getMatrix();
165 transformMatrix
.invert();
166 for(uint i
=0; i
<8; i
++)
168 CVector
& point
= vertices
[i
];
169 point
= transformMatrix
.mulPoint(point
);
172 // clip bounding box faces
174 createFace(faces
[0], vertices
, 3, 2, 1, 0);
175 createFace(faces
[1], vertices
, 7, 6, 5, 4);
176 createFace(faces
[2], vertices
, 0, 1, 5, 4);
177 createFace(faces
[3], vertices
, 7, 6, 2, 3);
178 createFace(faces
[4], vertices
, 2, 6, 5, 1);
179 createFace(faces
[5], vertices
, 7, 3, 0, 4);
181 // "near" plane of current frustum
183 nearPlane
.make(CVector(0, 1, 0), CVector(0, Driver
->getFrustum().Near
, 0));
186 _FiniteFrustum
= Driver
->getFrustum();
187 CVector2f
rectMax(0,0), rectMin(0,0);
189 // for each face, we research its intersection points with "near" plane (if exist)
190 for(uint f
=0; f
<6; f
++)
192 uint outNb
= nearPlane
.clipPolygonFront(faces
[f
], out
, 4);
194 // if intersection points exist, we project them in windows coordinates to recover
198 for(uint v
=0; v
<outNb
; v
++)
200 CVector
& point
= out
[v
];
202 point
= _FiniteFrustum
.project(point
);
204 NLMISC::clamp(point
.x
, 0, 1);
205 NLMISC::clamp(point
.y
, 0, 1);
207 // search for bounding rectangle of scissor test
210 rectMax
= rectMin
= point
;
214 rectMax
.x
= std::max(rectMax
.x
, point
.x
);
215 rectMax
.y
= std::max(rectMax
.y
, point
.y
);
216 rectMin
.x
= std::min(rectMin
.x
, point
.x
);
217 rectMin
.y
= std::min(rectMin
.y
, point
.y
);
223 CScissor
scissor(rectMin
.x
, rectMin
.y
, rectMax
.x
- rectMin
.x
, rectMax
.y
- rectMin
.y
);
224 Driver
->setScissor(scissor
);
227 //-----------------------------------------------------------------------------------------------------------
229 void CLandscapePolyDrawer::renderLandscapePolyPart()
233 UCamera cam
= Scene
->getCam();
234 Driver
->setMatrixMode3D(cam
);
236 CViewport oldViewPort
= Driver
->getViewport();
238 Driver
->setViewport(Scene
->getViewport());
240 // get initial values of depth range
242 Driver
->getDepthRange(depthRangeMin
, _MaxDepthRange
);
244 // get original (finite) frustum
245 _FiniteFrustum
= Driver
->getFrustum();
247 // we can write in stencil buffer only the last 7 bits.
248 // The eighth bit mustn't be written because it indicates if a part of stencil buffer
249 // can support shadow volume algorithm.
250 Driver
->stencilMask(0x7f);
252 for(i
=1; i
<=_Polygons
.size(); i
++)
254 // calculate and set scissor rectangle to optimize algorithm.
257 // disable color buffer update
258 Driver
->setColorMask(false, false, false, false);
259 Scene
->enableLightingSystem(false);
261 // disable Z buffer update
262 GenericMat
.setZWrite(false);
266 // test and write only if eighth bit is a 1 (landscape).
267 Driver
->stencilFunc(UDriver::notequal
, 0, 0x80);
269 // display of shadow volumes in two passes
271 GenericMat
.setDoubleSided(false);
273 // render the shadow volume back faces and increment the stencil buffer
274 // when the depth test fails
275 Driver
->setCullMode(UDriver::CW
);
276 Driver
->stencilOp(UDriver::keep
, UDriver::incr
, UDriver::keep
);
277 drawShadowVolume(i
, true);
279 // render the shadow volume front faces and decrement the stencil buffer
280 // when the depth test fails
281 Driver
->setCullMode(UDriver::CCW
);
282 Driver
->stencilOp(UDriver::keep
, UDriver::decr
, UDriver::keep
);
283 drawShadowVolume(i
, false);
285 GenericMat
.setDoubleSided(true);
288 // enable color and Z Buffer updates
289 Driver
->setColorMask(true, true, true, true);
290 GenericMat
.setZWrite(true);
293 Scene
->enableLightingSystem(true);
295 // render of the polygon : render of a rectangle whith polygon color.
296 // It's renderer only if stencil buffer is different to 0 on seven last bits
297 Driver
->stencilFunc(UDriver::notequal
, 0, 0x7f);
299 // if stencil buffer is different to 0 (polygon), value must be
300 // replace by 0 (seven last bits).
301 Driver
->stencilOp(UDriver::keep
, UDriver::keep
, UDriver::replace
);
304 // reset neutral values
305 Driver
->stencilOp(UDriver::keep
, UDriver::keep
, UDriver::keep
);
306 Driver
->setScissor(CScissor(0, 0, 1, 1));
309 // render decals on landscape only
310 Driver
->stencilFunc(UDriver::equal
, 0x80, 0x80);
312 // call to render the decals just before the projected polygons on landscape
313 CDecalRenderList::getInstance().renderAllDecals();
315 // disable stencil test
316 Driver
->enableStencilTest(false);
317 Driver
->stencilMask(0xff);
318 Driver
->stencilFunc(UDriver::always
, 0, 0xff);
321 Driver
->setViewport(oldViewPort
);
324 //-----------------------------------------------------------------------------------------------------------
327 CLandscapePolyDrawer::infiniteFrustum()
329 // setup new values of depth range
330 Driver
->setDepthRange(0.0, 1.0);
332 // znear and zfar initial values
334 znear
= _FiniteFrustum
.Near
;
335 zfar
= _FiniteFrustum
.Far
;
337 // initial projection matrix
338 CMatrix frustumMatrix
= Driver
->getFrustumMatrix();
340 // epsilon depends from OpenGL or Direct3D use
341 double factor
= Driver
->getClipSpaceZMin() == -1.f
? 2 : 1;
342 double epsilon
= factor
*(1 - _MaxDepthRange
*(zfar
/(zfar
- znear
)));
344 // update of coefficients to obtain "robust" infinite frustum,
345 // and join depth range used in main scen display
349 frustumMatrix
.setCoefficient((float)(epsilon
-1), 2, 2);
350 frustumMatrix
.setCoefficient((float)(znear
*(epsilon
-2)), 2, 3);
355 frustumMatrix
.setCoefficient((float)(1-epsilon
), 2, 2);
356 frustumMatrix
.setCoefficient((float)(znear
*(epsilon
-1)), 3, 2);
359 // initial projection matrix
360 Driver
->setFrustumMatrix(frustumMatrix
);
363 //-----------------------------------------------------------------------------------------------------------
366 CLandscapePolyDrawer::finiteFrustum()
369 Driver
->setDepthRange(0.0, _MaxDepthRange
);
372 Driver
->setFrustum(_FiniteFrustum
);
375 //-----------------------------------------------------------------------------------------------------------
377 void CLandscapePolyDrawer::buildShadowVolume(uint poly
)
379 uint i
, v1
, v2
, v3
, v4
;
382 CVector2f
barycenter(0, 0);
383 const CPolygon2D
& polygon
= _Polygons
[poly
-1];
384 uint verticesNb
= (uint
)polygon
.Vertices
.size();
386 // barycenter polygon
387 for(i
=0; i
<verticesNb
; i
++)
389 barycenter
+= polygon
.Vertices
[i
];
391 barycenter
= barycenter
/((float)verticesNb
);
392 _Barycenters
.push_back(barycenter
);
394 // vertex buffer initialization.
395 // vertices coordinates aren't calculated immediately but in drawShadowVolume method,
396 // because they are calculated in camera location.
397 vb
.setVertexFormat(CVertexBuffer::PositionFlag
);
398 vb
.setNumVertices(2*verticesNb
+ 2);
400 CVertexBufferReadWrite vba
;
403 for(i
=0; i
<2*verticesNb
+ 2; i
++)
405 vba
.setVertexCoord(i
, CVector(0.0, 0.0, 0.0));
408 _PolyVB
.push_back(vb
);
410 // index buffer initialization.
412 ib
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
413 ib
.setNumIndexes(12*verticesNb
);
415 CIndexBufferReadWrite iba
;
419 for(v1
=1; v1
<=verticesNb
; v1
++)
422 if(v2
== verticesNb
+1)
427 iba
.setTri(index
, v1
, v2
, 0);
432 for(v1
=verticesNb
+2; v1
<=2*verticesNb
+1; v1
++)
435 if(v2
== 2*verticesNb
+2)
440 iba
.setTri(index
, v2
, v1
, verticesNb
+1);
445 for(v1
=1; v1
<=verticesNb
; v1
++)
447 v3
= v1
+verticesNb
+1;
450 if(v2
== verticesNb
+1)
456 if(v4
== 2*verticesNb
+2)
462 iba
.setTri(index
, v2
, v1
, v3
);
466 iba
.setTri(index
, v2
, v3
, v4
);
471 _PolyIB
.push_back(ib
);
474 //-----------------------------------------------------------------------------------------------------------
476 void CLandscapePolyDrawer::drawShadowVolume(uint poly
, bool firstPass
)
480 // render shadow volume in camera location, in order to minimize
481 // polygon coordinates
482 CVertexBuffer
& vb
= _PolyVB
[poly
-1];
483 const CPolygon2D
& polygon
= _Polygons
[poly
-1];
484 const CVector2f
& barycenter
= _Barycenters
[poly
-1];
485 uint verticesNb
= (uint
)polygon
.Vertices
.size();
489 CVector
* vertexVB
= NULL
;
490 const CVector cameraPos
= Scene
->getCam().getPos();
492 float height
= 2000.0;
494 CVertexBufferReadWrite vba
;
498 vertexVB
= vba
.getVertexCoordPointer(0);
499 *vertexVB
= CVector(barycenter
.x
, barycenter
.y
, height
) - cameraPos
;
501 for(i
=0; i
<verticesNb
; i
++)
503 vertex
= polygon
.Vertices
[i
];
504 vertexVB
= vba
.getVertexCoordPointer(i
+1);
505 *vertexVB
= CVector(vertex
.x
, vertex
.y
, height
) - cameraPos
;
509 vertexVB
= vba
.getVertexCoordPointer(verticesNb
+1);
510 *vertexVB
= CVector(barycenter
.x
, barycenter
.y
, -height
) - cameraPos
;
512 for(i
=0; i
<verticesNb
; i
++)
514 vertex
= polygon
.Vertices
[i
];
515 vertexVB
= vba
.getVertexCoordPointer(i
+verticesNb
+2);
516 *vertexVB
= CVector(vertex
.x
, vertex
.y
, -height
) - cameraPos
;
520 // new matrix model/view to reposition in camera position
521 Driver
->setModelMatrix(CMatrix::Identity
);
523 CMatrix viewMatrix
= Driver
->getViewMatrix();
524 _OldViewMatrix
= viewMatrix
;
525 viewMatrix
.setPos(CVector::Null
);
526 Driver
->setViewMatrix(viewMatrix
);
528 // before drawing shadow volume, we setup an infinite frustum
529 // and a [0, 1] depth range
533 // render Vertex Buffer
534 ((CDriverUser
*)Driver
)->getDriver()->activeVertexBuffer(_PolyVB
[poly
-1]);
535 ((CDriverUser
*)Driver
)->getDriver()->activeIndexBuffer(_PolyIB
[poly
-1]);
536 ((CDriverUser
*)Driver
)->getDriver()->renderTriangles(
537 *GenericMat
.getObjectPtr(), 0, 4*(uint32
)_Polygons
[poly
-1].Vertices
.size());
541 // we recover initial (finite) projection matrix
544 // setup old model/view matrix
545 Driver
->setViewMatrix(_OldViewMatrix
);
549 //-----------------------------------------------------------------------------------------------------------
551 void CLandscapePolyDrawer::drawPolygon(uint poly
)
553 GenericMat
.setZWrite(false);
555 const CRGBA
& currentColor
= GenericMat
.getColor();
557 // activation of transparency
558 GenericMat
.setBlend(true);
559 GenericMat
.setBlendFunc(UMaterial::srcalpha
, UMaterial::invsrcalpha
);
560 GenericMat
.getObjectPtr()->setOpacity(128);
561 GenericMat
.getObjectPtr()->setColor(_PolyColor
[poly
-1]);
563 // draw a half transparent rectangle which covers all scissor rectangle
564 Driver
->setMatrixMode2D11();
565 Driver
->drawQuad(_Shadow
, GenericMat
);
566 UCamera cam
= Scene
->getCam();
567 Driver
->setMatrixMode3D(cam
);
569 // reset old material of scene
570 GenericMat
.setBlend(false);
571 GenericMat
.setColor(currentColor
);
573 GenericMat
.setZWrite(true);
576 //-----------------------------------------------------------------------------------------------------------
578 void CLandscapePolyDrawer::deletePolygons()
583 _Barycenters
.clear();
587 for(i
=0; i
<_PolyVB
.size(); i
++)
589 _PolyVB
[i
].deleteAllVertices();
593 for(i
=0; i
<_PolyIB
.size(); i
++)
595 _PolyIB
[i
].deleteAllIndexes();
600 //-----------------------------------------------------------------------------------------------------------
602 void CLandscapePolyDrawer::computeBBoxFromPolygon(const NLMISC::CPolygon2D
&poly2D
,
603 NLMISC::CAABBox
&destBBox
)
605 if (poly2D
.Vertices
.empty())
607 destBBox
.setMinMax(CVector::Null
, CVector::Null
);
611 // search for min and max dimensions of polygon on x and y axes.
612 CVector2f point
= poly2D
.Vertices
[0];
613 CVector2f
rectMax(point
), rectMin(point
);
614 for(uint i
=1; i
<poly2D
.Vertices
.size(); i
++)
616 point
= poly2D
.Vertices
[i
];
618 rectMax
.x
= std::max(rectMax
.x
, point
.x
);
619 rectMax
.y
= std::max(rectMax
.y
, point
.y
);
620 rectMin
.x
= std::min(rectMin
.x
, point
.x
);
621 rectMin
.y
= std::min(rectMin
.y
, point
.y
);
625 float w
= rectMax
.x
- rectMin
.x
, h
= rectMax
.y
- rectMin
.y
;
626 destBBox
.setCenter(CVector(rectMin
.x
+ w
/2 , rectMin
.y
+ h
/2, 0));
627 destBBox
.setHalfSize(CVector(w
/2 , h
/2, 0));
629 // search for zones list on which the polygon will be "projected".
631 sint32 xmin
= ((uint
)(rectMin
.x
/zoneDim
))*zoneDim
;
632 sint32 xmax
= ((uint
)(rectMax
.x
/zoneDim
))*zoneDim
;
633 sint32 ymin
= (((uint
)(rectMin
.y
/zoneDim
)))*zoneDim
;
634 sint32 ymax
= (((uint
)(rectMax
.y
/zoneDim
)))*zoneDim
;
636 std::list
<uint16
> zoneIds
;
637 for(sint32 x
=xmin
; x
<=xmax
; x
+=zoneDim
)
639 for(sint32 y
=ymin
; y
<=ymax
; y
+=zoneDim
)
641 float xcount
= (float)(x
/zoneDim
);
642 float ycount
= (float)(-y
/zoneDim
) + 1;
644 uint16 zoneId
= (uint16
) ((ycount
-1)*256+xcount
);
645 zoneIds
.push_back(zoneId
);
649 // search for min and max dimensions on z axis.
650 bool firstExtend
= true;
651 std::list
<uint16
>::iterator it
;
652 for(it
=zoneIds
.begin(); it
!=zoneIds
.end(); it
++)
656 const CZone
* zone
= Landscape
->getZone(*it
);
659 // For each zone, we traverse its patchs and check intersection between our polygon
660 // bounding rectangle on (x, y) plane and the bounding sphere patch .
661 // If intersection, we use the bounding box of the patch to extend the polygon boudning box
663 // NB : The patch bounding box isn't used immediately because it's more expensive
664 // to obtain than bounding sphere.
665 sint numPatchs
= zone
->getNumPatchs();
666 for(sint i
=0; i
<numPatchs
; i
++)
668 const CBSphere
& bSphere
= zone
->getPatchBSphere(i
);
670 const CVector2f sphereMax
= CVector2f(bSphere
.Center
.x
+ bSphere
.Radius
,
671 bSphere
.Center
.y
+ bSphere
.Radius
);
672 const CVector2f sphereMin
= CVector2f(bSphere
.Center
.x
- bSphere
.Radius
,
673 bSphere
.Center
.y
- bSphere
.Radius
);
675 // intersection beetween patch bounding sphere and polygon bounding rectangle
676 if(sphereMin
.x
<rectMax
.x
&& sphereMax
.x
>rectMin
.x
&& sphereMin
.y
<rectMax
.y
677 && sphereMax
.y
>rectMin
.y
)
679 const CPatch
* patch
= zone
->getPatch(i
);
680 const CAABBox
& patchBBox
= patch
->buildBBox();
681 const CVector
& center
= patchBBox
.getCenter();
682 const CVector
& halfSize
= patchBBox
.getHalfSize();
684 const CVector2f patchMax
= CVector2f(center
.x
+ halfSize
.x
, center
.y
+ halfSize
.y
);
685 const CVector2f patchMin
= CVector2f(center
.x
- halfSize
.x
, center
.y
- halfSize
.y
);
687 // intersection beetween patch bounding box and polygon bounding rectangle
688 if(patchMin
.x
<rectMax
.x
&& patchMax
.x
>rectMin
.x
&& patchMin
.y
<rectMax
.y
689 && patchMax
.y
>rectMin
.y
)
693 destBBox
.setCenter(CVector(destBBox
.getCenter().x
, destBBox
.getCenter().y
,
695 destBBox
.setHalfSize(CVector(destBBox
.getHalfSize().x
, destBBox
.getHalfSize().y
,
701 float zmin
=destBBox
.getCenter().z
-destBBox
.getHalfSize().z
;
702 float zmax
=destBBox
.getCenter().z
+destBBox
.getHalfSize().z
;
704 if(center
.z
-halfSize
.z
< zmin
)
705 zmin
= center
.z
-halfSize
.z
;
706 if(center
.z
+halfSize
.z
> zmax
)
707 zmax
= center
.z
+halfSize
.z
;
709 destBBox
.setCenter(CVector(destBBox
.getCenter().x
, destBBox
.getCenter().y
,
710 zmin
+ (zmax
-zmin
)/2));
711 destBBox
.setHalfSize(CVector(destBBox
.getHalfSize().x
, destBBox
.getHalfSize().y
,
722 //-----------------------------------------------------------------------------------------------------------