mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-22 17:08:43 +00:00
949 lines
27 KiB
C++
949 lines
27 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_multi_lod.h"
|
|
#include "nel/3d/mesh_multi_lod_instance.h"
|
|
#include "nel/3d/mesh_instance.h"
|
|
#include "nel/3d/mesh_mrm.h"
|
|
#include "nel/3d/scene.h"
|
|
#include "nel/3d/coarse_mesh_manager.h"
|
|
#include "nel/3d/skeleton_model.h"
|
|
#include "nel/misc/fast_floor.h"
|
|
#include "nel/3d/mesh_blender.h"
|
|
#include "nel/3d/visual_collision_mesh.h"
|
|
|
|
#include "nel/misc/debug.h"
|
|
#include "nel/misc/hierarchical_timer.h"
|
|
|
|
using namespace NLMISC;
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
namespace NL3D
|
|
{
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::build(CMeshMultiLodBuild &mbuild)
|
|
{
|
|
// Clear the mesh
|
|
clear ();
|
|
|
|
// Build the base mesh
|
|
CMeshBase::buildMeshBase (mbuild.BaseMesh);
|
|
|
|
// Static flag
|
|
_StaticLod=mbuild.StaticLod;
|
|
|
|
// Resize the array
|
|
_MeshVector.resize (mbuild.LodMeshes.size());
|
|
|
|
// For each slots
|
|
for (uint slot=0; slot<mbuild.LodMeshes.size(); slot++)
|
|
{
|
|
// Dist max
|
|
_MeshVector[slot].DistMax=mbuild.LodMeshes[slot].DistMax;
|
|
|
|
// BlendLength
|
|
_MeshVector[slot].BlendLength=mbuild.LodMeshes[slot].BlendLength;
|
|
|
|
// Flags
|
|
_MeshVector[slot].Flags=0;
|
|
|
|
// Blend in ?
|
|
if (mbuild.LodMeshes[slot].Flags & CMeshMultiLodBuild::CBuildSlot::BlendIn)
|
|
_MeshVector[slot].Flags|=CMeshSlot::BlendIn;
|
|
|
|
// Blend out ?
|
|
if (mbuild.LodMeshes[slot].Flags & CMeshMultiLodBuild::CBuildSlot::BlendOut)
|
|
_MeshVector[slot].Flags|=CMeshSlot::BlendOut;
|
|
|
|
// Coarse mesh ?
|
|
if (mbuild.LodMeshes[slot].Flags & CMeshMultiLodBuild::CBuildSlot::CoarseMesh)
|
|
{
|
|
// Flag
|
|
_MeshVector[slot].Flags|=CMeshSlot::CoarseMesh;
|
|
}
|
|
|
|
// Is opaque
|
|
if (mbuild.LodMeshes[slot].Flags & CMeshMultiLodBuild::CBuildSlot::IsOpaque)
|
|
_MeshVector[slot].Flags|=CMeshSlot::IsOpaque;
|
|
|
|
// Is transparent
|
|
if (mbuild.LodMeshes[slot].Flags & CMeshMultiLodBuild::CBuildSlot::IsTransparent)
|
|
_MeshVector[slot].Flags|=CMeshSlot::IsTransparent;
|
|
|
|
// MeshGeom
|
|
nlassert (mbuild.LodMeshes[slot].MeshGeom);
|
|
|
|
// Valid pointer ?
|
|
if (_MeshVector[slot].Flags&CMeshSlot::CoarseMesh)
|
|
{
|
|
// If it is a coarse mesh, it must be a CMeshGeom.
|
|
if (dynamic_cast<CMeshGeom*>(mbuild.LodMeshes[slot].MeshGeom)==NULL)
|
|
{
|
|
// If it is a coarse mesh, it must be a CMeshGeom.
|
|
_MeshVector[slot].MeshGeom = NULL;
|
|
delete mbuild.LodMeshes[slot].MeshGeom;
|
|
}
|
|
else
|
|
// Ok, no prb
|
|
_MeshVector[slot].MeshGeom = mbuild.LodMeshes[slot].MeshGeom;
|
|
}
|
|
else
|
|
// Ok, no prb
|
|
_MeshVector[slot].MeshGeom = mbuild.LodMeshes[slot].MeshGeom;
|
|
}
|
|
|
|
// Sort the slot by the distance...
|
|
for (int i=(uint)mbuild.LodMeshes.size()-1; i>0; i--)
|
|
for (int j=0; j<i; j++)
|
|
{
|
|
// Bad sort ?
|
|
if (_MeshVector[j].DistMax>_MeshVector[j+1].DistMax)
|
|
{
|
|
// Exchange slots
|
|
CMeshSlot tmp=_MeshVector[j];
|
|
_MeshVector[j]=_MeshVector[j+1];
|
|
_MeshVector[j+1]=tmp;
|
|
tmp.MeshGeom=NULL;
|
|
}
|
|
}
|
|
|
|
// Calc start and end polygon count
|
|
for (uint k=0; k<mbuild.LodMeshes.size(); k++)
|
|
{
|
|
// Get start distance
|
|
float startDist;
|
|
if (k==0)
|
|
startDist=0;
|
|
else
|
|
startDist=_MeshVector[k-1].DistMax;
|
|
|
|
// Get start poly count
|
|
float startPolyCount;
|
|
startPolyCount=_MeshVector[k].MeshGeom->getNumTriangles (startDist);
|
|
|
|
// Get end distance
|
|
float endDist=_MeshVector[k].DistMax;
|
|
|
|
// Get end poly count
|
|
if (k==mbuild.LodMeshes.size()-1)
|
|
{
|
|
_MeshVector[k].EndPolygonCount=_MeshVector[k].MeshGeom->getNumTriangles (endDist);
|
|
if (startPolyCount==_MeshVector[k].EndPolygonCount)
|
|
_MeshVector[k].EndPolygonCount=startPolyCount/2;
|
|
}
|
|
else
|
|
_MeshVector[k].EndPolygonCount=_MeshVector[k+1].MeshGeom->getNumTriangles (endDist);
|
|
|
|
// Calc A
|
|
if (endDist==startDist)
|
|
_MeshVector[k].A=0;
|
|
else
|
|
_MeshVector[k].A=(_MeshVector[k].EndPolygonCount-startPolyCount)/(endDist-startDist);
|
|
|
|
// Calc A
|
|
_MeshVector[k].B=_MeshVector[k].EndPolygonCount-_MeshVector[k].A*endDist;
|
|
}
|
|
|
|
// End: compile some stuff
|
|
compileRunTime();
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
CTransformShape *CMeshMultiLod::createInstance(CScene &scene)
|
|
{
|
|
// Create a CMeshInstance, an instance of a multi lod mesh.
|
|
CMeshMultiLodInstance *mi=(CMeshMultiLodInstance*)scene.createModel(NL3D::MeshMultiLodInstanceId);
|
|
mi->Shape= this;
|
|
mi->_LastLodMatrixDate=0;
|
|
|
|
// instanciate the material part of the Mesh, ie the CMeshBase.
|
|
CMeshBase::instanciateMeshBase(mi, &scene);
|
|
|
|
// Create the necessary space for Coarse Instanciation
|
|
instanciateCoarseMeshSpace(mi);
|
|
|
|
// For all lods, do some instance init for MeshGeom
|
|
for(uint i=0; i<_MeshVector.size(); i++)
|
|
{
|
|
if(_MeshVector[i].MeshGeom)
|
|
_MeshVector[i].MeshGeom->initInstance(mi);
|
|
}
|
|
|
|
// init the Filter type
|
|
mi->initRenderFilterType();
|
|
|
|
|
|
return mi;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
bool CMeshMultiLod::clip(const std::vector<CPlane> &pyramid, const CMatrix &worldMatrix)
|
|
{
|
|
// Look for the biggest mesh
|
|
uint meshCount=(uint)_MeshVector.size();
|
|
for (uint i=0; i<meshCount; i++)
|
|
{
|
|
// Ref on slot
|
|
CMeshSlot &slot=_MeshVector[i];
|
|
|
|
// Is mesh present ?
|
|
if (slot.MeshGeom)
|
|
{
|
|
// Clip this mesh
|
|
return slot.MeshGeom->clip (pyramid, worldMatrix);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::render(IDriver *drv, CTransformShape *trans, bool passOpaque)
|
|
{
|
|
// Render good meshes
|
|
CMeshMultiLodInstance *instance=safe_cast<CMeshMultiLodInstance*>(trans);
|
|
|
|
// Static or dynamic coarse mesh ?
|
|
CCoarseMeshManager *manager;
|
|
// Get the coarse mesh manager
|
|
manager=instance->getOwnerScene()->getCoarseMeshManager();
|
|
|
|
// *** Render Lods
|
|
|
|
// Second lod ?
|
|
if ( (instance->Lod1!=0xffffffff) && (passOpaque==false) )
|
|
{
|
|
// build rdrFlags to rdr both transparent and opaque materials,
|
|
// use globalAlphaBlend, and disable ZWrite for Lod1
|
|
uint32 rdrFlags= IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderTransparentMaterial |
|
|
IMeshGeom::RenderGlobalAlpha | IMeshGeom::RenderGADisableZWrite;
|
|
// NB: very important to render Lod1 first, because Lod0 is still rendered with ZWrite enabled.
|
|
renderMeshGeom (instance->Lod1, drv, instance, instance->PolygonCountLod1, rdrFlags, 1.f-instance->BlendFactor, manager);
|
|
}
|
|
|
|
|
|
// Have an opaque pass ?
|
|
if ( (instance->Flags&CMeshMultiLodInstance::Lod0Blend) == 0)
|
|
{
|
|
// Is this slot a CoarseMesh?
|
|
if ( _MeshVector[instance->Lod0].Flags&CMeshSlot::CoarseMesh )
|
|
{
|
|
// render as a CoarseMesh the lod 0, only in opaque pass
|
|
if(passOpaque)
|
|
renderCoarseMesh (instance->Lod0, drv, instance, manager);
|
|
}
|
|
else
|
|
{
|
|
// build rdrFlags the normal way (as CMesh::render() for example)
|
|
uint32 mask= (0-(uint32)passOpaque);
|
|
uint32 rdrFlags;
|
|
// select rdrFlags, without ifs.
|
|
rdrFlags= mask & (IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderPassOpaque);
|
|
rdrFlags|= ~mask & (IMeshGeom::RenderTransparentMaterial);
|
|
// Only render the normal way the first lod
|
|
renderMeshGeom (instance->Lod0, drv, instance, instance->PolygonCountLod0, rdrFlags, 1, manager);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Should not be in opaque
|
|
nlassert (passOpaque==false);
|
|
|
|
// build rdrFlags to rdr both transparent and opaque materials,
|
|
// use globalAlphaBlend, BUT Don't disable ZWrite for Lod0
|
|
uint32 rdrFlags= IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderTransparentMaterial |
|
|
IMeshGeom::RenderGlobalAlpha;
|
|
|
|
// Render first lod in blend mode. Don't disable ZWrite for Lod0
|
|
renderMeshGeom (instance->Lod0, drv, instance, instance->PolygonCountLod0, rdrFlags, instance->BlendFactor, manager);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
/* ***********************************************
|
|
* 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
|
|
* ***********************************************/
|
|
|
|
// Serial a version number
|
|
(void)f.serialVersion (0);
|
|
|
|
// serial Materials infos contained in CMeshBase.
|
|
CMeshBase::serialMeshBase(f);
|
|
|
|
// Static lod flag
|
|
f.serial (_StaticLod);
|
|
|
|
// Serial the values
|
|
f.serialCont (_MeshVector);
|
|
|
|
|
|
// if reading, compile some stuff
|
|
if (f.isReading())
|
|
compileRunTime();
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
float CMeshMultiLod::getNumTrianglesWithCoarsestDist(float distance, float coarsestMeshDist) const
|
|
{
|
|
// Look in the table for good distances..
|
|
uint meshCount=(uint)_MeshVector.size();
|
|
|
|
// At least on mesh
|
|
if (meshCount>0)
|
|
{
|
|
|
|
if (coarsestMeshDist != -1)
|
|
{
|
|
if (coarsestMeshDist != 0)
|
|
{
|
|
// rescale distance to new coarse mesh distance..
|
|
distance *= _MeshVector[meshCount - 1].DistMax / coarsestMeshDist;
|
|
}
|
|
}
|
|
|
|
uint i=0;
|
|
// Look for good i
|
|
while ( _MeshVector[i].DistMax < distance)
|
|
{
|
|
if (i==meshCount-1)
|
|
// Abort if last one
|
|
break;
|
|
i++;
|
|
}
|
|
|
|
// Ref on slot
|
|
const CMeshSlot &slot=_MeshVector[i];
|
|
|
|
// Is mesh present ?
|
|
if (slot.MeshGeom)
|
|
{
|
|
// Get the polygon count with the distance
|
|
float polyCount=slot.A * distance + slot.B;
|
|
|
|
/*// Get the perfect polygon count in this slot for the asked distance
|
|
float goodPolyCount=slot.MeshGeom->getNumTriangles (distance);
|
|
|
|
// Get the next slot perfect polygon count
|
|
float realEndPolyCount;
|
|
|
|
// Last slot ?
|
|
if ( (i<meshCount-1) && _MeshVector[i+1].MeshGeom )
|
|
// Take end number polygon count in the next slot
|
|
realEndPolyCount=_MeshVector[i+1].MeshGeom->getNumTriangles (slot.DistMax);
|
|
else
|
|
// Take end number polygon count in the this slot
|
|
realEndPolyCount=slot.EndPolygonCount;
|
|
|
|
// Return blended polygon count to have a continous decreasing function
|
|
return (goodPolyCount-slot.BeginPolygonCount) * (realEndPolyCount-slot.BeginPolygonCount) /
|
|
(slot.EndPolygonCount-slot.BeginPolygonCount) + slot.BeginPolygonCount;*/
|
|
return polyCount;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::getAABBox(NLMISC::CAABBox &bbox) const
|
|
{
|
|
// Get count
|
|
uint count=(uint)_MeshVector.size();
|
|
for (uint slot=0; slot<count; slot++)
|
|
{
|
|
// Shape ?
|
|
if (_MeshVector[slot].MeshGeom)
|
|
{
|
|
// Get the bounding box
|
|
bbox=_MeshVector[slot].MeshGeom->getBoundingBox().getAABBox();
|
|
|
|
// ok
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::clear ()
|
|
{
|
|
_MeshVector.clear ();
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::CMeshSlot::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
// Check version
|
|
(void)f.serialVersion (0);
|
|
|
|
f.serialPolyPtr (MeshGeom);
|
|
f.serial (A);
|
|
f.serial (B);
|
|
f.serial (DistMax);
|
|
f.serial (EndPolygonCount);
|
|
f.serial (BlendLength);
|
|
f.serial (Flags);
|
|
|
|
if (f.isReading())
|
|
{
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
CMeshMultiLod::CMeshSlot::CMeshSlot ()
|
|
{
|
|
MeshGeom=NULL;
|
|
CoarseNumTris= 0;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
CMeshMultiLod::CMeshSlot::~CMeshSlot ()
|
|
{
|
|
if (MeshGeom)
|
|
delete MeshGeom;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::renderMeshGeom (uint slot, IDriver *drv, CMeshMultiLodInstance *trans, float numPoylgons, uint32 rdrFlags, float alpha, CCoarseMeshManager *manager)
|
|
{
|
|
// Ref
|
|
CMeshSlot &slotRef=_MeshVector[slot];
|
|
|
|
// MeshGeom exist?
|
|
if (slotRef.MeshGeom)
|
|
{
|
|
// NB Here, the meshGeom may still be a coarseMesh, but rendered through CMeshGeom
|
|
if(slotRef.Flags&CMeshSlot::CoarseMesh)
|
|
{
|
|
// Render only for opaque material
|
|
if(manager && (rdrFlags & IMeshGeom::RenderOpaqueMaterial) )
|
|
{
|
|
bool gaDisableZWrite= (rdrFlags & IMeshGeom::RenderGADisableZWrite)?true:false;
|
|
|
|
// Render the CoarseMesh with the manager material
|
|
CMaterial &material= manager->getMaterial();
|
|
|
|
// modulate material for alphaBlend transition
|
|
// ----------
|
|
// get average sun color for this coarseMesh
|
|
CRGBA newCol= trans->getCoarseMeshLighting();
|
|
|
|
// Use a CMeshBlender to modify material and driver.
|
|
CMeshBlender blender;
|
|
blender.prepareRenderForGlobalAlphaCoarseMesh(material, drv, newCol, alpha, gaDisableZWrite);
|
|
|
|
|
|
// render simple the coarseMesh
|
|
CMeshGeom *meshGeom= safe_cast<CMeshGeom*>(slotRef.MeshGeom);
|
|
|
|
// Force corse mesh vertex buffer in system memory
|
|
const_cast<CVertexBuffer&>(meshGeom->getVertexBuffer ()).setPreferredMemory (CVertexBuffer::RAMPreferred, false);
|
|
|
|
meshGeom->renderSimpleWithMaterial(drv, trans->getWorldMatrix(), material);
|
|
|
|
|
|
// resetup standard CoarseMeshMaterial material values
|
|
// ----------
|
|
// blender restore
|
|
blender.restoreRenderCoarseMesh(material, drv, gaDisableZWrite);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Render the geom mesh
|
|
// Disable ZWrite only if in transition and for rendering Lod1
|
|
slotRef.MeshGeom->render (drv, trans, numPoylgons, rdrFlags, alpha);
|
|
}
|
|
}
|
|
}
|
|
// ***************************************************************************
|
|
|
|
void CMeshMultiLod::renderCoarseMesh (uint slot, IDriver *drv, CMeshMultiLodInstance *trans, CCoarseMeshManager *manager)
|
|
{
|
|
// if the manager is NULL, quit.
|
|
if(manager==NULL)
|
|
return;
|
|
|
|
// get the scene
|
|
CScene *scene= trans->getOwnerScene();
|
|
if(!scene)
|
|
return;
|
|
|
|
// If filtered...
|
|
if( (scene->getFilterRenderFlags() & UScene::FilterCoarseMesh)==0 )
|
|
return;
|
|
|
|
// Ref
|
|
CMeshSlot &slotRef=_MeshVector[slot];
|
|
|
|
// the slot must be a Coarse mesh
|
|
nlassert(slotRef.Flags&CMeshSlot::CoarseMesh);
|
|
|
|
// Get a pointer on the geom mesh
|
|
CMeshGeom *meshGeom= safe_cast<CMeshGeom*>(slotRef.MeshGeom);
|
|
|
|
// ** If not the same as Before (or if NULL before...)
|
|
if ( trans->_LastCoarseMesh!=meshGeom )
|
|
{
|
|
uint numVerts= meshGeom->getVertexBuffer().getNumVertices();
|
|
uint numTris= slotRef.CoarseNumTris;
|
|
// If empty meshGeom, erase cache (each frame, ugly but error mgt here...)
|
|
if( numTris==0 || numVerts==0 )
|
|
trans->_LastCoarseMesh= NULL;
|
|
else
|
|
{
|
|
// Cache
|
|
trans->_LastCoarseMesh= meshGeom;
|
|
trans->_LastCoarseMeshNumVertices= numVerts;
|
|
|
|
// Check setuped size.
|
|
nlassert( trans->_CoarseMeshVB.size() >= numVerts*manager->getVertexSize() );
|
|
|
|
// Fill only UVs here. (Pos updated in Matrix pass. Color in Lighting Pass)
|
|
trans->setUVCoarseMesh( *meshGeom, manager->getVertexSize(), manager->getUVOff() );
|
|
|
|
// Dirt the matrix
|
|
trans->_LastLodMatrixDate=0;
|
|
// Dirt the lighting. NB: period maximum is 255. Hence the -256, to ensure lighting compute now
|
|
trans->_LastLodLightingDate= -0x100;
|
|
}
|
|
}
|
|
|
|
// ** If setuped, update and render
|
|
if( trans->_LastCoarseMesh )
|
|
{
|
|
// Matrix has changed ?
|
|
if ( trans->ITransformable::compareMatrixDate (trans->_LastLodMatrixDate) )
|
|
{
|
|
// Get date
|
|
trans->_LastLodMatrixDate = trans->ITransformable::getMatrixDate();
|
|
|
|
// Set matrix
|
|
trans->setPosCoarseMesh ( *meshGeom, trans->getMatrix(), manager->getVertexSize() );
|
|
}
|
|
|
|
// Lighting: test if must update lighting, according to date of HrcTrav (num of CScene::render() call).
|
|
sint64 currentDate= scene->getHrcTrav().CurrentDate;
|
|
if( trans->_LastLodLightingDate < currentDate - scene->getCoarseMeshLightingUpdate() )
|
|
{
|
|
// reset the date.
|
|
trans->_LastLodLightingDate= currentDate;
|
|
|
|
// get average sun color
|
|
CRGBA sunContrib= trans->getCoarseMeshLighting();
|
|
|
|
// Invert BR if driver is BGRA
|
|
if(drv->getVertexColorFormat()==CVertexBuffer::TBGRA)
|
|
sunContrib.swapBR();
|
|
|
|
// Set color
|
|
trans->setColorCoarseMesh ( sunContrib, manager->getVertexSize(), manager->getColorOff());
|
|
}
|
|
|
|
// Add dynamic to the manager
|
|
if( !manager->addMesh(trans->_LastCoarseMeshNumVertices, &trans->_CoarseMeshVB[0], slotRef.CoarseNumTris, &slotRef.CoarseTriangles[0] ) )
|
|
{
|
|
// If failure, flush the manager
|
|
manager->flushRender(drv);
|
|
// then try to re-add. No-op if fails this time..
|
|
manager->addMesh(trans->_LastCoarseMeshNumVertices, &trans->_CoarseMeshVB[0], slotRef.CoarseNumTris, &slotRef.CoarseTriangles[0] );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::compileDistMax()
|
|
{
|
|
// Last element
|
|
if(_MeshVector.empty())
|
|
IShape::_DistMax= -1;
|
|
else
|
|
IShape::_DistMax= _MeshVector.back().DistMax;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
const IMeshGeom& CMeshMultiLod::getMeshGeom (uint slot) const
|
|
{
|
|
// Checks
|
|
nlassert (slot<getNumSlotMesh ());
|
|
|
|
return *_MeshVector[slot].MeshGeom;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::changeMRMDistanceSetup(float distanceFinest, float distanceMiddle, float distanceCoarsest)
|
|
{
|
|
// no-op if empty.
|
|
if(getNumSlotMesh ()==0)
|
|
return;
|
|
|
|
// If not NULL
|
|
if(_MeshVector[0].MeshGeom==NULL)
|
|
return;
|
|
|
|
// verify it is a CMeshMRMGeom. else no-op.
|
|
CMeshMRMGeom *mgeom= dynamic_cast<CMeshMRMGeom*>(_MeshVector[0].MeshGeom);
|
|
if(mgeom==NULL)
|
|
return;
|
|
|
|
// ok, setup.
|
|
mgeom->changeMRMDistanceSetup(distanceFinest, distanceMiddle, distanceCoarsest);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
IMeshGeom *CMeshMultiLod::supportMeshBlockRendering (CTransformShape *trans, float &polygonCount ) const
|
|
{
|
|
IMeshGeom *ret= NULL;
|
|
|
|
// get the instance
|
|
CMeshMultiLodInstance *instance=safe_cast<CMeshMultiLodInstance*>(trans);
|
|
|
|
// Must not be in blend transition.
|
|
if ( (instance->Flags&CMeshMultiLodInstance::Lod0Blend) == 0)
|
|
{
|
|
uint slot= instance->Lod0;
|
|
// The slot must not be a CoarseMesh
|
|
if ( (_MeshVector[slot].Flags&CMeshSlot::CoarseMesh)==0 )
|
|
{
|
|
// MeshGeom exist?
|
|
ret= _MeshVector[slot].MeshGeom;
|
|
}
|
|
}
|
|
|
|
// Ok if meshGeom is ok.
|
|
if( ret && ret->supportMeshBlockRendering() )
|
|
{
|
|
polygonCount= instance->PolygonCountLod0;
|
|
return ret;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::profileMeshGeom (uint slot, CRenderTrav *rdrTrav, CMeshMultiLodInstance *trans, float numPoylgons, uint32 rdrFlags)
|
|
{
|
|
// Ref
|
|
CMeshSlot &slotRef=_MeshVector[slot];
|
|
|
|
// MeshGeom exist?
|
|
if (slotRef.MeshGeom)
|
|
{
|
|
// NB Here, the meshGeom may still be a coarseMesh, but rendered through CMeshGeom
|
|
if(slotRef.Flags&CMeshSlot::CoarseMesh)
|
|
{
|
|
// Render only for opaque material
|
|
if(rdrFlags & IMeshGeom::RenderOpaqueMaterial)
|
|
{
|
|
slotRef.MeshGeom->profileSceneRender(rdrTrav, trans, numPoylgons, rdrFlags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slotRef.MeshGeom->profileSceneRender(rdrTrav, trans, numPoylgons, rdrFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::profileSceneRender(CRenderTrav *rdrTrav, CTransformShape *trans, bool passOpaque)
|
|
{
|
|
// Render good meshes
|
|
CMeshMultiLodInstance *instance=safe_cast<CMeshMultiLodInstance*>(trans);
|
|
|
|
|
|
// Second lod ?
|
|
if ( (instance->Lod1!=0xffffffff) && (passOpaque==false) )
|
|
{
|
|
// build rdrFlags to rdr both transparent and opaque materials,
|
|
// use globalAlphaBlend, and disable ZWrite for Lod1
|
|
uint32 rdrFlags= IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderTransparentMaterial |
|
|
IMeshGeom::RenderGlobalAlpha | IMeshGeom::RenderGADisableZWrite;
|
|
// NB: very important to render Lod1 first, because Lod0 is still rendered with ZWrite enabled.
|
|
profileMeshGeom (instance->Lod1, rdrTrav, instance, instance->PolygonCountLod1, rdrFlags);
|
|
}
|
|
|
|
|
|
// Have an opaque pass ?
|
|
if ( (instance->Flags&CMeshMultiLodInstance::Lod0Blend) == 0)
|
|
{
|
|
// Is this slot a CoarseMesh?
|
|
if ( _MeshVector[instance->Lod0].Flags&CMeshSlot::CoarseMesh )
|
|
{
|
|
}
|
|
else
|
|
{
|
|
// build rdrFlags the normal way (as CMesh::render() for example)
|
|
uint32 mask= (0-(uint32)passOpaque);
|
|
uint32 rdrFlags;
|
|
// select rdrFlags, without ifs.
|
|
rdrFlags= mask & (IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderPassOpaque);
|
|
rdrFlags|= ~mask & (IMeshGeom::RenderTransparentMaterial);
|
|
// Only render the normal way the first lod
|
|
profileMeshGeom (instance->Lod0, rdrTrav, instance, instance->PolygonCountLod0, rdrFlags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Should not be in opaque
|
|
nlassert (passOpaque==false);
|
|
|
|
// build rdrFlags to rdr both transparent and opaque materials,
|
|
// use globalAlphaBlend, BUT Don't disable ZWrite for Lod0
|
|
uint32 rdrFlags= IMeshGeom::RenderOpaqueMaterial | IMeshGeom::RenderTransparentMaterial |
|
|
IMeshGeom::RenderGlobalAlpha;
|
|
|
|
// Render first lod in blend mode. Don't disable ZWrite for Lod0
|
|
profileMeshGeom (instance->Lod0, rdrTrav, instance, instance->PolygonCountLod0, rdrFlags);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::instanciateCoarseMeshSpace(CMeshMultiLodInstance *mi)
|
|
{
|
|
CCoarseMeshManager *manager= mi->getOwnerScene()->getCoarseMeshManager();
|
|
|
|
if(manager)
|
|
{
|
|
// For all MeshSlots that have a CoarseMesh, count max Coarse NumVertices;
|
|
uint numVertices= 0;
|
|
for(uint i=0;i<_MeshVector.size();i++)
|
|
{
|
|
CMeshSlot &slotRef= _MeshVector[i];
|
|
if( slotRef.Flags & CMeshSlot::CoarseMesh )
|
|
{
|
|
// Get a pointer on the geom mesh
|
|
CMeshGeom *meshGeom= safe_cast<CMeshGeom*>(slotRef.MeshGeom);
|
|
numVertices= max(numVertices, (uint)meshGeom->getVertexBuffer().getNumVertices() );
|
|
}
|
|
}
|
|
|
|
// Then allocate vertex space for dest manager vertex size.
|
|
mi->_CoarseMeshVB.resize( numVertices*manager->getVertexSize() );
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::compileCoarseMeshes()
|
|
{
|
|
// For All Slots that are CoarseMeshes.
|
|
for(uint i=0;i<_MeshVector.size();i++)
|
|
{
|
|
CMeshSlot &slotRef= _MeshVector[i];
|
|
if( slotRef.Flags & CMeshSlot::CoarseMesh )
|
|
{
|
|
// reset
|
|
slotRef.CoarseNumTris= 0;
|
|
|
|
// Get a pointer on the geom mesh
|
|
CMeshGeom *meshGeom= safe_cast<CMeshGeom*>(slotRef.MeshGeom);
|
|
|
|
// For All RdrPass of the 1st matrix block
|
|
if( meshGeom->getNbMatrixBlock()>0 )
|
|
{
|
|
// 1st count
|
|
for(uint i=0;i<meshGeom->getNbRdrPass(0);i++)
|
|
{
|
|
slotRef.CoarseNumTris+= meshGeom->getRdrPassPrimitiveBlock(0, i).getNumIndexes()/3;
|
|
}
|
|
|
|
// 2nd allocate and fill
|
|
if( slotRef.CoarseNumTris )
|
|
{
|
|
slotRef.CoarseTriangles.resize(slotRef.CoarseNumTris * 3);
|
|
TCoarseMeshIndexType *dstPtr= &slotRef.CoarseTriangles[0];
|
|
uint totalTris = 0;
|
|
for(uint i=0;i<meshGeom->getNbRdrPass(0);i++)
|
|
{
|
|
const CIndexBuffer &pb= meshGeom->getRdrPassPrimitiveBlock(0, i);
|
|
CIndexBufferRead ibaRead;
|
|
pb.lock (ibaRead);
|
|
uint numTris= pb.getNumIndexes()/3;
|
|
totalTris += numTris;
|
|
if (pb.getFormat() == CIndexBuffer::Indices16)
|
|
{
|
|
if (sizeof(TCoarseMeshIndexType) == sizeof(uint16))
|
|
{
|
|
memcpy(dstPtr, (uint16 *) ibaRead.getPtr(), numTris*3*sizeof(uint16));
|
|
dstPtr+= numTris*3;
|
|
}
|
|
else
|
|
{
|
|
// 16 -> 32
|
|
uint16 *src = (uint16 *) ibaRead.getPtr();
|
|
for(uint k = 0; k < numTris; ++k)
|
|
{
|
|
*dstPtr++ = (TCoarseMeshIndexType) *src++;
|
|
*dstPtr++ = (TCoarseMeshIndexType) *src++;
|
|
*dstPtr++ = (TCoarseMeshIndexType) *src++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sizeof(TCoarseMeshIndexType) == sizeof(uint32))
|
|
{
|
|
memcpy(dstPtr, (uint32 *) ibaRead.getPtr(), numTris*3*sizeof(uint32));
|
|
dstPtr+= numTris*3;
|
|
}
|
|
else
|
|
{
|
|
const uint32 *src = (const uint32 *) ibaRead.getPtr();
|
|
for(uint k = 0; k < numTris; ++k)
|
|
{
|
|
// 32 -> 16
|
|
nlassert(src[0] <= 0xffff);
|
|
nlassert(src[1] <= 0xffff);
|
|
nlassert(src[2] <= 0xffff);
|
|
*dstPtr++ = (TCoarseMeshIndexType) *src++;
|
|
*dstPtr++ = (TCoarseMeshIndexType) *src++;
|
|
*dstPtr++ = (TCoarseMeshIndexType) *src++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nlassert(totalTris == slotRef.CoarseNumTris);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::compileRunTime()
|
|
{
|
|
/* ***********************************************
|
|
* 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
|
|
* ***********************************************/
|
|
|
|
// **** MultiLod basics
|
|
compileDistMax();
|
|
compileCoarseMeshes();
|
|
|
|
// **** try to build a Visual Collision Mesh
|
|
// clear first
|
|
if(_VisualCollisionMesh)
|
|
{
|
|
delete _VisualCollisionMesh;
|
|
_VisualCollisionMesh= NULL;
|
|
}
|
|
// build only if wanted
|
|
if( (_CollisionMeshGeneration==AutoCameraCol && !_LightInfos.empty()) ||
|
|
_CollisionMeshGeneration==ForceCameraCol )
|
|
{
|
|
// try to retrieve the info from a CMeshGeom only
|
|
if(getNumSlotMesh())
|
|
{
|
|
const CMeshGeom *meshGeom= dynamic_cast<const CMeshGeom*>(&getMeshGeom(0));
|
|
if(meshGeom)
|
|
{
|
|
vector<CVector> vertices;
|
|
vector<uint32> indices;
|
|
if(meshGeom->retrieveVertices(vertices) && meshGeom->retrieveTriangles(indices))
|
|
{
|
|
// ok, can build!
|
|
_VisualCollisionMesh= new CVisualCollisionMesh;
|
|
// if fails to build cause of too many vertices/indices for instance
|
|
if(!_VisualCollisionMesh->build(vertices, indices,const_cast<CVertexBuffer&>(meshGeom->getVertexBuffer())))
|
|
{
|
|
// delete
|
|
delete _VisualCollisionMesh;
|
|
_VisualCollisionMesh= NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CMeshMultiLod::buildSystemGeometry()
|
|
{
|
|
// clear any
|
|
_SystemGeometry.clear();
|
|
|
|
// Use the first lod, for system geometry copy
|
|
if(getNumSlotMesh())
|
|
{
|
|
// the first is a meshGeom?
|
|
const CMeshGeom *meshGeom= dynamic_cast<const CMeshGeom*>(&getMeshGeom(0));
|
|
if(meshGeom)
|
|
{
|
|
// retrieve geometry (if VB/IB not resident)
|
|
if( !meshGeom->retrieveVertices(_SystemGeometry.Vertices) ||
|
|
!meshGeom->retrieveTriangles(_SystemGeometry.Triangles))
|
|
{
|
|
_SystemGeometry.clear();
|
|
}
|
|
}
|
|
// else it is a mrm geom?
|
|
else
|
|
{
|
|
const CMeshMRMGeom *meshMRMGeom= dynamic_cast<const CMeshMRMGeom*>(&getMeshGeom(0));
|
|
if(meshMRMGeom)
|
|
{
|
|
// Choose the best Lod available for system geometry
|
|
if(meshMRMGeom->getNbLodLoaded()==0)
|
|
return;
|
|
uint lodId= meshMRMGeom->getNbLodLoaded()-1;
|
|
|
|
// retrieve geometry (if VB/IB not resident)
|
|
if( !meshMRMGeom->buildGeometryForLod(lodId, _SystemGeometry.Vertices, _SystemGeometry.Triangles) )
|
|
{
|
|
_SystemGeometry.clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestYoyo
|
|
/*static uint32 totalMem= 0;
|
|
totalMem+= _SystemGeometry.Vertices.size()*sizeof(CVector);
|
|
totalMem+= _SystemGeometry.Triangles.size()*sizeof(uint32);
|
|
nlinfo("CMeshMultiLod: TotalMem: %d", totalMem);*/
|
|
}
|
|
|
|
} // NL3D
|
|
|