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