// 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