// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include "nel/3d/visual_collision_mesh.h" #include "nel/3d/quad_grid.h" #include "nel/3d/camera_col.h" #include "nel/3d/driver.h" #include "nel/3d/shadow_map.h" using namespace std; using namespace NLMISC; // TestYoyo. external debug flag bool TESTYOYO_VCM_RedShadow= false; namespace NL3D { // *************************************************************************** #define NL3D_VCM_SHADOW_NUM_CLIP_PLANE 7 #define NL3D_VCM_SHADOW_NUM_CLIP_PLANE_SHIFT (1<0 && isPowerOf2(nbQuads)); // init the grid _GridSize= nbQuads; _GridSizePower= getPowerOf2(nbQuads); _Grid.resize(_GridSize*_GridSize); // start with 0 elt in each case memset(_Grid.getPtr(), 0, _Grid.size() * sizeof(CCase)); // init the Elt Build _EltBuild.resize(nbElts); // total size is 0 _GridDataSize= 0; // bbox init _GridPos= gridBBox.getMin(); _GridScale= gridBBox.getSize(); _GridScale.x= _GridSize / _GridScale.x; _GridScale.y= _GridSize / _GridScale.y; // reset intersection data _ItSession= 0; } // *************************************************************************** void CVisualCollisionMesh::CStaticGrid::add(uint16 id, const NLMISC::CAABBox &bbox) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ CVector minp= bbox.getMin() - _GridPos; CVector maxp= bbox.getMax() - _GridPos; // compute the 2D bbox sint xmin= (sint)floorf(minp.x*_GridScale.x); sint ymin= (sint)floorf(minp.y*_GridScale.y); sint xmax= (sint)ceilf(maxp.x*_GridScale.x); sint ymax= (sint)ceilf(maxp.y*_GridScale.y); clamp(xmin, 0, (sint)_GridSize-1); clamp(ymin, 0, (sint)_GridSize-1); clamp(xmax, xmin+1, (sint)_GridSize); clamp(ymax, ymin+1, (sint)_GridSize); // set in the elt build _EltBuild[id].X0= xmin; _EltBuild[id].Y0= ymin; _EltBuild[id].X1= xmax; _EltBuild[id].Y1= ymax; // for each case touched, increment NumElts for(uint y=ymin;y<(uint)ymax;y++) { for(uint x=xmin;x<(uint)xmax;x++) { _Grid[(y<<_GridSizePower)+x].NumElts++; _GridDataSize++; } } } // *************************************************************************** void CVisualCollisionMesh::CStaticGrid::compile() { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ uint i; // create the data _GridData.resize(_GridDataSize); // Init Start ptr for each case uint idx= 0; for(i=0;i<_Grid.size();i++) { _Grid[i].Start= idx; idx+= _Grid[i].NumElts; // reset NumElts, because use it like an index below _Grid[i].NumElts= 0; } nlassert(_GridDataSize==idx); // For each element, fill the grid and grid data for(i=0;i<_EltBuild.size();i++) { CEltBuild &eb= _EltBuild[i]; for(uint y=eb.Y0;y &res) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ // increment the intersection session _ItSession++; // enlarge the result array as needed if(res.size()<_Sessions.size()) res.resize(_Sessions.size()); // the number of selected element uint numSel= 0; // compute the 2D bbox CVector minp= bbox.getMin() - _GridPos; CVector maxp= bbox.getMax() - _GridPos; sint xmin= (sint)floorf(minp.x*_GridScale.x); sint ymin= (sint)floorf(minp.y*_GridScale.y); sint xmax= (sint)ceilf(maxp.x*_GridScale.x); sint ymax= (sint)ceilf(maxp.y*_GridScale.y); clamp(xmin, 0, (sint)_GridSize-1); clamp(ymin, 0, (sint)_GridSize-1); clamp(xmax, xmin+1, (sint)_GridSize); clamp(ymax, ymin+1, (sint)_GridSize); // for each case touched, increment NumElts for(uint y=ymin;y<(uint)ymax;y++) { for(uint x=xmin;x<(uint)xmax;x++) { CCase &gcase= _Grid[(y<<_GridSizePower)+x]; // for each element in this case for(uint i= gcase.Start;i &vertices, const std::vector &triangles, CVertexBuffer &vbForShadowRender) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ uint i; // if no vertices, or no triangles, abort if(vertices.empty()) return false; if(triangles.empty()) return false; // vertices and triangles id are stored in uint16 form. so their should not be more than 65535*3 indices if(vertices.size()>65535 || triangles.size()>65535*3) return false; // copy _Vertices= vertices; // compress indexes to 16 bits _Triangles.resize(triangles.size()); for(i=0;i<_Triangles.size();i++) _Triangles[i]= (uint16)triangles[i]; // Build the Local bbox for this col mesh CAABBox localBBox; localBBox.setCenter(vertices[0]); for(i=1;i selection; uint numSel= _QuadGrid.select(camColLocal.getBBox(), selection); // **** For all triangles, test if intersect the camera collision float sqrMinDist= FLT_MAX; for(uint i=0;i0) { f= sqrtf(sqrMinDist) / d; f= min(f, 1.f); } return f; } } // *************************************************************************** NLMISC::CAABBox CVisualCollisionMesh::computeWorldBBox(const CMatrix &instanceMatrix) { CAABBox ret; if(!_Vertices.empty()) { ret.setCenter(instanceMatrix*_Vertices[0]); for(uint i=1;i<_Vertices.size();i++) { ret.extend(instanceMatrix*_Vertices[i]); } } return ret; } // *************************************************************************** void CVisualCollisionMesh::receiveShadowMap(const NLMISC::CMatrix &instanceMatrix, const CShadowContext &shadowContext) { // empty mesh => no op if(_Vertices.empty()) return; // The VertexBuffer RefPtr has been released? quit if(_VertexBuffer == NULL) return; // **** Select triangles to be rendered with quadGrid // select with quadGrid local in mesh CAABBox localBB; localBB= CAABBox::transformAABBox(instanceMatrix.inverted(), shadowContext.ShadowWorldBB); static std::vector triInQuadGrid; uint numTrisInQuadGrid= _QuadGrid.select(localBB, triInQuadGrid); // no intersection at all? quit if(numTrisInQuadGrid==0) return; // **** prepare more precise Clip with shadow pyramid // enlarge temp flag array static std::vector vertexFlags; if(vertexFlags.size()<_Vertices.size()) vertexFlags.resize(_Vertices.size()); // reset all to 0 memset(&vertexFlags[0], 0, _Vertices.size()*sizeof(uint8)); // Compute the "LocalToInstance" shadow Clip Volume static std::vector localClipPlanes; /* We want to apply to plane this matrix: IM-1 * MCasterPos, where IM=instanceMatrix and MCasterPos= matrix translation of "shadowContext.CasterPos" BUT, since to transform a plane, we must do plane * M-1, then compute this matrix: localMat= MCasterPos-1 * IM */ CMatrix localMat; localMat.setPos(-shadowContext.CasterPos); localMat*= instanceMatrix; // Allow max bits of planes clip. localClipPlanes.resize(min((uint)shadowContext.ShadowMap->LocalClipPlanes.size(), (uint)NL3D_VCM_SHADOW_NUM_CLIP_PLANE)); // Transform into Mesh local space for(uint i=0;iLocalClipPlanes[i] * localMat; } // **** Clip and fill the triangles uint currentTriIdx= 0; // enlarge the index buffer as max of triangles possibly intersected shadowContext.IndexBuffer.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT); if(shadowContext.IndexBuffer.getNumIndexes() 0; vf|= ((uint)out)< 0; vf|= ((uint)out)<setupModelMatrix(instanceMatrix); // update the material projection matrix, cause matrix changed shadowContext.ShadowMapProjector.applyToMaterial(instanceMatrix, shadowContext.ShadowMaterial); // render drv->activeVertexBuffer(*_VertexBuffer); drv->activeIndexBuffer(shadowContext.IndexBuffer); drv->renderTriangles(shadowContext.ShadowMaterial, 0, currentTriIdx/3); // TestYoyo. Show in Red triangles selected /*if(TESTYOYO_VCM_RedShadow) { static CMaterial tam; tam.initUnlit(); tam.setColor(CRGBA(255,0,0,128)); tam.setZFunc(CMaterial::always); tam.setZWrite(false); tam.setBlend(true); tam.setBlendFunc(CMaterial::srcalpha, CMaterial::invsrcalpha); tam.setDoubleSided(true); drv->renderTriangles(tam, 0, currentTriIdx/3); }*/ } } } // NL3D