khanat-opennel-code/code/nel/src/3d/mesh_instance.cpp

356 lines
11 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