Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / client / src / landscape_poly_drawer.cpp
blob714684e263d966b4fea757fb5f7be62c11ac69a5
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
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 "stdpch.h"
18 #include "landscape_poly_drawer.h"
20 // 3D Interfaces
21 #include "nel/3d/u_driver.h"
22 #include "nel/3d/u_scene.h"
23 #include "nel/3d/u_landscape.h"
25 // 3d
26 #include "nel/3d/zone.h"
27 #include "nel/3d/driver_user.h"
29 // client
30 #include "decal.h"
32 using namespace NLMISC;
33 using namespace NL3D;
34 using namespace std;
37 // EXTERN
38 extern UDriver *Driver;
39 extern UScene *Scene;
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();
80 _InitStencil->init();
82 // shadow rectangle
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);
96 _InitStencil->init();
99 //-----------------------------------------------------------------------------------------------------------
101 CLandscapePolyDrawer::~CLandscapePolyDrawer()
103 delete _InitStencil;
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
173 CVector faces[6][4];
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
182 CPlane nearPlane;
183 nearPlane.make(CVector(0, 1, 0), CVector(0, Driver->getFrustum().Near, 0));
184 CVector out[10];
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
195 // scissor rectangle
196 if(outNb!=0)
198 for(uint v=0; v<outNb; v++)
200 CVector & point = out[v];
201 //project
202 point = _FiniteFrustum.project(point);
203 //clamp
204 NLMISC::clamp(point.x, 0, 1);
205 NLMISC::clamp(point.y, 0, 1);
207 // search for bounding rectangle of scissor test
208 if(f==0 && v==0)
210 rectMax = rectMin = point;
212 else
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()
231 uint i;
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
241 float depthRangeMin;
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.
255 setScissor(i-1);
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);
264 // ZFAIL algorithm
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);
292 // enable light
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);
302 drawPolygon(i);
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);
320 // reset viewport
321 Driver->setViewport(oldViewPort);
324 //-----------------------------------------------------------------------------------------------------------
326 void
327 CLandscapePolyDrawer::infiniteFrustum()
329 // setup new values of depth range
330 Driver->setDepthRange(0.0, 1.0);
332 // znear and zfar initial values
333 float znear, zfar;
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
346 // OpenGL
347 if(factor == 2)
349 frustumMatrix.setCoefficient((float)(epsilon-1), 2, 2);
350 frustumMatrix.setCoefficient((float)(znear*(epsilon-2)), 2, 3);
352 // Direct3D
353 else
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 //-----------------------------------------------------------------------------------------------------------
365 void
366 CLandscapePolyDrawer::finiteFrustum()
368 // depth range
369 Driver->setDepthRange(0.0, _MaxDepthRange);
371 // projection matrix
372 Driver->setFrustum(_FiniteFrustum);
375 //-----------------------------------------------------------------------------------------------------------
377 void CLandscapePolyDrawer::buildShadowVolume(uint poly)
379 uint i, v1, v2, v3, v4;
380 CVertexBuffer vb;
381 CIndexBuffer ib;
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;
401 vb.lock(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.
411 int index = 0;
412 ib.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
413 ib.setNumIndexes(12*verticesNb);
415 CIndexBufferReadWrite iba;
416 ib.lock (iba);
418 // near cap
419 for(v1=1; v1<=verticesNb; v1++)
421 v2 = v1+1;
422 if(v2 == verticesNb+1)
424 v2 = 1;
427 iba.setTri(index, v1, v2, 0);
428 index += 3;
431 // far cap
432 for(v1=verticesNb+2; v1<=2*verticesNb+1; v1++)
434 v2 = v1+1;
435 if(v2 == 2*verticesNb+2)
437 v2 = verticesNb+2;
440 iba.setTri(index, v2, v1, verticesNb+1);
441 index += 3;
444 // surrounding faces
445 for(v1=1; v1<=verticesNb; v1++)
447 v3 = v1+verticesNb+1;
449 v2 = v1+1;
450 if(v2 == verticesNb+1)
452 v2 = 1;
455 v4 = v3+1;
456 if(v4 == 2*verticesNb+2)
458 v4 = verticesNb+2;
461 // first triangle
462 iba.setTri(index, v2, v1, v3);
463 index += 3;
465 // second triangle
466 iba.setTri(index, v2, v3, v4);
467 index += 3;
471 _PolyIB.push_back(ib);
474 //-----------------------------------------------------------------------------------------------------------
476 void CLandscapePolyDrawer::drawShadowVolume(uint poly, bool firstPass)
478 if(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();
487 uint i;
488 CVector2f vertex;
489 CVector * vertexVB = NULL;
490 const CVector cameraPos = Scene->getCam().getPos();
492 float height = 2000.0;
494 CVertexBufferReadWrite vba;
495 vb.lock(vba);
497 // top vertices
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;
508 // bottom vertices
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
530 infiniteFrustum();
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());
539 if(!firstPass)
541 // we recover initial (finite) projection matrix
542 finiteFrustum();
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()
580 uint i;
582 _Polygons.clear();
583 _Barycenters.clear();
584 _PolyColor.clear();
585 _BBoxes.clear();
587 for(i=0; i<_PolyVB.size(); i++)
589 _PolyVB[i].deleteAllVertices();
591 _PolyVB.clear();
593 for(i=0; i<_PolyIB.size(); i++)
595 _PolyIB[i].deleteAllIndexes();
597 _PolyIB.clear();
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);
608 return;
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);
624 // init bounding box
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".
630 uint zoneDim = 160;
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++)
654 if (Landscape)
656 const CZone* zone = Landscape->getZone(*it);
657 if(zone)
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
662 // on z axis.
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)
691 if(firstExtend)
693 destBBox.setCenter(CVector(destBBox.getCenter().x, destBBox.getCenter().y,
694 center.z));
695 destBBox.setHalfSize(CVector(destBBox.getHalfSize().x, destBBox.getHalfSize().y,
696 halfSize.z));
697 firstExtend = false;
699 else
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,
712 (zmax-zmin)/2));
722 //-----------------------------------------------------------------------------------------------------------