356 lines
10 KiB
C++
356 lines
10 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#include "std3d.h"
|
|
|
|
#include "nel/3d/mesh_instance.h"
|
|
#include "nel/3d/mesh.h"
|
|
#include "nel/3d/skeleton_model.h"
|
|
#include "nel/3d/u_scene.h"
|
|
#include "nel/3d/scene.h"
|
|
#include <list>
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
|
|
namespace NL3D
|
|
{
|
|
|
|
// ***************************************************************************
|
|
CMeshInstance::CMeshInstance()
|
|
{
|
|
_ShadowMap= NULL;
|
|
|
|
// LoadBalancing is not useful for Mesh, because meshs cannot be reduced in faces.
|
|
// Override CTransformShape state.
|
|
CTransform::setIsLoadbalancable(false);
|
|
|
|
// Mesh support shadow map casting only
|
|
CTransform::setIsShadowMapCaster(true);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
CMeshInstance::~CMeshInstance()
|
|
{
|
|
// Auto detach me from skeleton. Must do it here, not in ~CTransform().
|
|
if(_FatherSkeletonModel)
|
|
{
|
|
// detach me from the skeleton.
|
|
// hrc and clip hierarchy is modified.
|
|
_FatherSkeletonModel->detachSkeletonSon(this);
|
|
nlassert(_FatherSkeletonModel==NULL);
|
|
}
|
|
|
|
// delete the shadowMap
|
|
deleteShadowMap();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::registerBasic()
|
|
{
|
|
CScene::registerModel(MeshInstanceId, MeshBaseInstanceId, CMeshInstance::creator);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::setApplySkin(bool state)
|
|
{
|
|
// Call parents method
|
|
CMeshBaseInstance::setApplySkin (state);
|
|
|
|
// Get a pointer on the shape
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
|
|
// Recompute the id
|
|
if (state)
|
|
{
|
|
pMesh->computeBonesId (_FatherSkeletonModel);
|
|
}
|
|
|
|
// update the skeleton usage according to the mesh.
|
|
pMesh->updateSkeletonUsage(_FatherSkeletonModel, state);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
const std::vector<sint32> *CMeshInstance::getSkinBoneUsage() const
|
|
{
|
|
// Get a pointer on the shape
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
|
|
// Recompute the id
|
|
pMesh->computeBonesId (_FatherSkeletonModel);
|
|
|
|
// get ids.
|
|
return &pMesh->getMeshGeom().getSkinBoneUsage();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
const std::vector<NLMISC::CBSphere> *CMeshInstance::getSkinBoneSphere() const
|
|
{
|
|
// Get a pointer on the shape
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
|
|
// Recompute the id, and skin spheres, if needed
|
|
pMesh->computeBonesId (_FatherSkeletonModel);
|
|
|
|
// get it.
|
|
return &pMesh->getMeshGeom().getSkinBoneSphere();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
bool CMeshInstance::isSkinnable() const
|
|
{
|
|
if(Shape==NULL)
|
|
return false;
|
|
|
|
// Get a pointer on the shape
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
|
|
// true if the mesh is skinned
|
|
return pMesh->getMeshGeom().isSkinned();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::renderSkin(float alphaMRM)
|
|
{
|
|
// Don't setup lighting or matrix in Skin. Done by the skeleton
|
|
|
|
if(Shape && getVisibility() != CHrcTrav::Hide)
|
|
{
|
|
// Get a pointer on the shape
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
|
|
// render the meshGeom
|
|
CMeshGeom &meshGeom= const_cast<CMeshGeom&>(pMesh->getMeshGeom ());
|
|
meshGeom.renderSkin( this, alphaMRM );
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::initRenderFilterType()
|
|
{
|
|
if(Shape)
|
|
{
|
|
// If the Shape has a VP or not...
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
|
|
if( pMesh->getMeshGeom().hasMeshVertexProgram() )
|
|
_RenderFilterType= UScene::FilterMeshVP;
|
|
else
|
|
_RenderFilterType= UScene::FilterMeshNoVP;
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CMeshInstance::supportIntersectSkin() const
|
|
{
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
CMeshGeom &meshGeom= const_cast<CMeshGeom&>(pMesh->getMeshGeom ());
|
|
return meshGeom.supportIntersectSkin();
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CMeshInstance::intersectSkin(const CMatrix &toRaySpace, float &dist2D, float &distZ, bool computeDist2D)
|
|
{
|
|
// Get a pointer on the shape
|
|
CMesh *pMesh = NLMISC::safe_cast<CMesh *>((IShape*)Shape);
|
|
// render the meshGeom
|
|
CMeshGeom &meshGeom= const_cast<CMeshGeom&>(pMesh->getMeshGeom ());
|
|
return meshGeom.intersectSkin(this, toRaySpace, dist2D, distZ, computeDist2D);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// ShadowMapping
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::generateShadowMap(const CVector &lightDir)
|
|
{
|
|
// get the driver for Texture Render
|
|
CScene *scene= getOwnerScene();
|
|
CRenderTrav &renderTrav= scene->getRenderTrav();
|
|
IDriver *driver= renderTrav.getAuxDriver();
|
|
|
|
if(!Shape)
|
|
return;
|
|
|
|
// update ShadowMap data if needed.
|
|
// ****
|
|
updateShadowMap(driver);
|
|
|
|
if(!_ShadowMap)
|
|
return;
|
|
|
|
// compute the ProjectionMatrix.
|
|
// ****
|
|
|
|
// get the BBox and localPosMatrix
|
|
CAABBox bbShape;
|
|
Shape->getAABBox(bbShape);
|
|
CMatrix localPosMatrix= getWorldMatrix();
|
|
localPosMatrix.setPos(CVector::Null);
|
|
|
|
// setup cameraMatrix with BBox and Enlarge For 1 pixel
|
|
CMatrix cameraMatrix;
|
|
_ShadowMap->buildCasterCameraMatrix(lightDir, localPosMatrix, bbShape, cameraMatrix);
|
|
|
|
|
|
// Render.
|
|
// ****
|
|
// setup the orhtogonal frustum and viewMatrix to include all the object.
|
|
driver->setFrustum(0,1,0,1,0,1,false);
|
|
driver->setupViewMatrix(cameraMatrix.inverted());
|
|
driver->setupModelMatrix(localPosMatrix);
|
|
|
|
// render the Mesh
|
|
CMaterial &castMat= renderTrav.getShadowMapManager().getCasterShadowMaterial();
|
|
CMesh *mesh= (CMesh*)(IShape*)Shape;
|
|
driver->activeVertexBuffer( const_cast<CVertexBuffer&>(mesh->getVertexBuffer()) );
|
|
for(uint mb=0;mb<mesh->getNbMatrixBlock();mb++)
|
|
{
|
|
for(uint rp=0;rp<mesh->getNbRdrPass(mb);rp++)
|
|
{
|
|
const CIndexBuffer &pb= mesh->getRdrPassPrimitiveBlock(mb, rp);
|
|
driver->activeIndexBuffer(const_cast<CIndexBuffer&>(pb));
|
|
driver->renderTriangles(castMat, 0, pb.getNumIndexes()/3);
|
|
}
|
|
}
|
|
|
|
// Infos.
|
|
// ****
|
|
|
|
// Compute the BackPoint: the first point to be shadowed.
|
|
CVector backPoint= bbShape.getCenter();
|
|
// get the 3/4 bottom of the shape
|
|
backPoint.z-= bbShape.getHalfSize().z/2;
|
|
backPoint= localPosMatrix * backPoint;
|
|
// Use the 3/4 bottom of the BBox minus the light direction in XY. NB: little hack:
|
|
// suppose no Rotate (but Z) and no scale
|
|
CVector ldir= lightDir;
|
|
ldir.z= 0;
|
|
ldir.normalize();
|
|
float lenXY= (CVector(bbShape.getHalfSize().x, bbShape.getHalfSize().y, 0)).norm();
|
|
backPoint-= ldir*lenXY;
|
|
|
|
// Compute LocalProjectionMatrix and other infos from cameraMatrix and backPoint?
|
|
_ShadowMap->buildProjectionInfos(cameraMatrix, backPoint, getShadowMapMaxDepth());
|
|
}
|
|
|
|
// ***************************************************************************
|
|
CShadowMap *CMeshInstance::getShadowMap()
|
|
{
|
|
return _ShadowMap;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::createShadowMap()
|
|
{
|
|
// create the shadowMap
|
|
if(!_ShadowMap)
|
|
{
|
|
_ShadowMap= new CShadowMap(&getOwnerScene()->getRenderTrav().getShadowMapManager());
|
|
getOwnerScene()->registerShadowCasterToList(this);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::deleteShadowMap()
|
|
{
|
|
if(_ShadowMap)
|
|
{
|
|
delete _ShadowMap;
|
|
_ShadowMap= NULL;
|
|
getOwnerScene()->unregisterShadowCasterToList(this);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::updateShadowMap(IDriver * /* driver */)
|
|
{
|
|
nlassert(_ShadowMap);
|
|
|
|
// create/update texture
|
|
if(_ShadowMap->getTextureSize()!=getOwnerScene()->getShadowMapTextureSize())
|
|
{
|
|
_ShadowMap->initTexture(getOwnerScene()->getShadowMapTextureSize());
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
bool CMeshInstance::computeWorldBBoxForShadow(NLMISC::CAABBox &worldBB)
|
|
{
|
|
// If even not visible or empty, no-op
|
|
if(!isHrcVisible() || !Shape)
|
|
return false;
|
|
|
|
// get the shape bbox
|
|
Shape->getAABBox(worldBB);
|
|
// transform into world
|
|
worldBB= CAABBox::transformAABBox(getWorldMatrix(), worldBB);
|
|
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshInstance::renderIntoSkeletonShadowMap(CSkeletonModel *rootSkeleton, CMaterial &castMat)
|
|
{
|
|
/*
|
|
Yoyo: Not Very Robuts here:
|
|
- suppose the MeshInstance don't have sons.
|
|
- suppose that the VertexBuffer doesn't have VertexColor (else castMat.color unused).
|
|
*/
|
|
|
|
// If even not visible or empty, no-op
|
|
if(!isHrcVisible() || !Shape)
|
|
return;
|
|
|
|
// render into aux Driver
|
|
IDriver *driver= getOwnerScene()->getRenderTrav().getAuxDriver();
|
|
|
|
// **** Render the Skeleton Skins
|
|
// The model Matrix is special here. It must be the Skeleton World Matrix, minus The Root Skeleton pos.
|
|
CMatrix localPosMatrix;
|
|
localPosMatrix.setRot( getWorldMatrix() );
|
|
// NB: if this==rootSkeleton, then the final pos will be CVector::Null
|
|
localPosMatrix.setPos( getWorldMatrix().getPos() - rootSkeleton->getWorldMatrix().getPos() );
|
|
driver->setupModelMatrix(localPosMatrix);
|
|
|
|
// render the Mesh
|
|
CMesh *mesh= (CMesh*)(IShape*)Shape;
|
|
driver->activeVertexBuffer( const_cast<CVertexBuffer&>(mesh->getVertexBuffer()) );
|
|
for(uint mb=0;mb<mesh->getNbMatrixBlock();mb++)
|
|
{
|
|
for(uint rp=0;rp<mesh->getNbRdrPass(mb);rp++)
|
|
{
|
|
const CIndexBuffer &pb= mesh->getRdrPassPrimitiveBlock(mb, rp);
|
|
driver->activeIndexBuffer(const_cast<CIndexBuffer&>(pb));
|
|
driver->renderTriangles(castMat, 0, pb.getNumIndexes()/3);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
} // NL3D
|