2023-10-03 20:37:00 +02:00
|
|
|
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
|
|
// This file is part of the "Irrlicht Engine".
|
|
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
|
|
|
|
#include "CSceneManager.h"
|
|
|
|
#include "IVideoDriver.h"
|
|
|
|
#include "IFileSystem.h"
|
|
|
|
#include "SAnimatedMesh.h"
|
|
|
|
#include "CMeshCache.h"
|
|
|
|
#include "IGUIEnvironment.h"
|
|
|
|
#include "IMaterialRenderer.h"
|
|
|
|
#include "IReadFile.h"
|
|
|
|
#include "IWriteFile.h"
|
|
|
|
|
|
|
|
#include "os.h"
|
|
|
|
|
|
|
|
#include "CSkinnedMesh.h"
|
|
|
|
#include "CXMeshFileLoader.h"
|
|
|
|
#include "COBJMeshFileLoader.h"
|
|
|
|
#include "CB3DMeshFileLoader.h"
|
2022-10-17 04:17:21 +02:00
|
|
|
#include "CGLTFMeshFileLoader.h"
|
2023-10-03 20:37:00 +02:00
|
|
|
#include "CBillboardSceneNode.h"
|
|
|
|
#include "CAnimatedMeshSceneNode.h"
|
|
|
|
#include "CCameraSceneNode.h"
|
|
|
|
#include "CMeshSceneNode.h"
|
|
|
|
#include "CDummyTransformationSceneNode.h"
|
|
|
|
#include "CEmptySceneNode.h"
|
|
|
|
|
|
|
|
#include "CSceneCollisionManager.h"
|
|
|
|
|
|
|
|
namespace irr
|
|
|
|
{
|
|
|
|
namespace scene
|
|
|
|
{
|
|
|
|
|
|
|
|
//! constructor
|
2024-03-20 19:35:52 +01:00
|
|
|
CSceneManager::CSceneManager(video::IVideoDriver *driver,
|
|
|
|
gui::ICursorControl *cursorControl, IMeshCache *cache) :
|
|
|
|
ISceneNode(0, 0),
|
|
|
|
Driver(driver),
|
|
|
|
CursorControl(cursorControl),
|
|
|
|
ActiveCamera(0), ShadowColor(150, 0, 0, 0), AmbientLight(0, 0, 0, 0), Parameters(0),
|
|
|
|
MeshCache(cache), CurrentRenderPass(ESNRP_NONE)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG
|
2023-10-03 20:37:00 +02:00
|
|
|
ISceneManager::setDebugName("CSceneManager ISceneManager");
|
|
|
|
ISceneNode::setDebugName("CSceneManager ISceneNode");
|
2024-03-20 19:35:52 +01:00
|
|
|
#endif
|
2023-10-03 20:37:00 +02:00
|
|
|
|
|
|
|
// root node's scene manager
|
|
|
|
SceneManager = this;
|
|
|
|
|
|
|
|
if (Driver)
|
|
|
|
Driver->grab();
|
|
|
|
|
|
|
|
if (CursorControl)
|
|
|
|
CursorControl->grab();
|
|
|
|
|
|
|
|
// create mesh cache if not there already
|
|
|
|
if (!MeshCache)
|
|
|
|
MeshCache = new CMeshCache();
|
|
|
|
else
|
|
|
|
MeshCache->grab();
|
|
|
|
|
|
|
|
// set scene parameters
|
|
|
|
Parameters = new io::CAttributes();
|
|
|
|
|
|
|
|
// create collision manager
|
|
|
|
CollisionManager = new CSceneCollisionManager(this, Driver);
|
|
|
|
|
|
|
|
// add file format loaders. add the least commonly used ones first,
|
|
|
|
// as these are checked last
|
|
|
|
|
|
|
|
// TODO: now that we have multiple scene managers, these should be
|
|
|
|
// shallow copies from the previous manager if there is one.
|
|
|
|
|
|
|
|
MeshLoaderList.push_back(new CXMeshFileLoader(this));
|
|
|
|
MeshLoaderList.push_back(new COBJMeshFileLoader(this));
|
|
|
|
MeshLoaderList.push_back(new CB3DMeshFileLoader(this));
|
2022-10-17 04:17:21 +02:00
|
|
|
MeshLoaderList.push_back(new CGLTFMeshFileLoader());
|
2023-10-03 20:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//! destructor
|
|
|
|
CSceneManager::~CSceneManager()
|
|
|
|
{
|
|
|
|
clearDeletionList();
|
|
|
|
|
|
|
|
//! force to remove hardwareTextures from the driver
|
|
|
|
//! because Scenes may hold internally data bounded to sceneNodes
|
|
|
|
//! which may be destroyed twice
|
|
|
|
if (Driver)
|
|
|
|
Driver->removeAllHardwareBuffers();
|
|
|
|
|
|
|
|
if (CursorControl)
|
|
|
|
CursorControl->drop();
|
|
|
|
|
|
|
|
if (CollisionManager)
|
|
|
|
CollisionManager->drop();
|
|
|
|
|
|
|
|
u32 i;
|
2024-03-20 19:35:52 +01:00
|
|
|
for (i = 0; i < MeshLoaderList.size(); ++i)
|
2023-10-03 20:37:00 +02:00
|
|
|
MeshLoaderList[i]->drop();
|
|
|
|
|
|
|
|
if (ActiveCamera)
|
|
|
|
ActiveCamera->drop();
|
|
|
|
ActiveCamera = 0;
|
|
|
|
|
|
|
|
if (MeshCache)
|
|
|
|
MeshCache->drop();
|
|
|
|
|
|
|
|
if (Parameters)
|
|
|
|
Parameters->drop();
|
|
|
|
|
|
|
|
// remove all nodes before dropping the driver
|
|
|
|
// as render targets may be destroyed twice
|
|
|
|
|
|
|
|
removeAll();
|
|
|
|
|
|
|
|
if (Driver)
|
|
|
|
Driver->drop();
|
|
|
|
}
|
|
|
|
|
2023-04-27 20:44:33 +02:00
|
|
|
//! gets an animatable mesh. loads it if needed. returned pointer must not be dropped.
|
2024-03-20 19:35:52 +01:00
|
|
|
IAnimatedMesh *CSceneManager::getMesh(io::IReadFile *file)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!file)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
io::path name = file->getFileName();
|
2024-03-20 19:35:52 +01:00
|
|
|
IAnimatedMesh *msh = MeshCache->getMeshByName(name);
|
2023-10-03 20:37:00 +02:00
|
|
|
if (msh)
|
|
|
|
return msh;
|
|
|
|
|
|
|
|
msh = getUncachedMesh(file, name, name);
|
|
|
|
|
|
|
|
return msh;
|
|
|
|
}
|
|
|
|
|
|
|
|
// load and create a mesh which we know already isn't in the cache and put it in there
|
2024-03-20 19:35:52 +01:00
|
|
|
IAnimatedMesh *CSceneManager::getUncachedMesh(io::IReadFile *file, const io::path &filename, const io::path &cachename)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
2024-03-20 19:35:52 +01:00
|
|
|
IAnimatedMesh *msh = 0;
|
2023-10-03 20:37:00 +02:00
|
|
|
|
|
|
|
// iterate the list in reverse order so user-added loaders can override the built-in ones
|
|
|
|
s32 count = MeshLoaderList.size();
|
2024-03-20 19:35:52 +01:00
|
|
|
for (s32 i = count - 1; i >= 0; --i) {
|
|
|
|
if (MeshLoaderList[i]->isALoadableFileExtension(filename)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
// reset file to avoid side effects of previous calls to createMesh
|
|
|
|
file->seek(0);
|
|
|
|
msh = MeshLoaderList[i]->createMesh(file);
|
2024-03-20 19:35:52 +01:00
|
|
|
if (msh) {
|
2023-10-03 20:37:00 +02:00
|
|
|
MeshCache->addMesh(cachename, msh);
|
|
|
|
msh->drop();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!msh)
|
|
|
|
os::Printer::log("Could not load mesh, file format seems to be unsupported", filename, ELL_ERROR);
|
|
|
|
else
|
|
|
|
os::Printer::log("Loaded mesh", filename, ELL_DEBUG);
|
|
|
|
|
|
|
|
return msh;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! returns the video driver
|
2024-03-20 19:35:52 +01:00
|
|
|
video::IVideoDriver *CSceneManager::getVideoDriver()
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return Driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! adds a scene node for rendering a static mesh
|
|
|
|
//! the returned pointer must not be dropped.
|
2024-03-20 19:35:52 +01:00
|
|
|
IMeshSceneNode *CSceneManager::addMeshSceneNode(IMesh *mesh, ISceneNode *parent, s32 id,
|
|
|
|
const core::vector3df &position, const core::vector3df &rotation,
|
|
|
|
const core::vector3df &scale, bool alsoAddIfMeshPointerZero)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!alsoAddIfMeshPointerZero && !mesh)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!parent)
|
|
|
|
parent = this;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
IMeshSceneNode *node = new CMeshSceneNode(mesh, parent, this, id, position, rotation, scale);
|
2023-10-03 20:37:00 +02:00
|
|
|
node->drop();
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! adds a scene node for rendering an animated mesh model
|
2024-03-20 19:35:52 +01:00
|
|
|
IAnimatedMeshSceneNode *CSceneManager::addAnimatedMeshSceneNode(IAnimatedMesh *mesh, ISceneNode *parent, s32 id,
|
|
|
|
const core::vector3df &position, const core::vector3df &rotation,
|
|
|
|
const core::vector3df &scale, bool alsoAddIfMeshPointerZero)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!alsoAddIfMeshPointerZero && !mesh)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!parent)
|
|
|
|
parent = this;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
IAnimatedMeshSceneNode *node =
|
|
|
|
new CAnimatedMeshSceneNode(mesh, parent, this, id, position, rotation, scale);
|
2023-10-03 20:37:00 +02:00
|
|
|
node->drop();
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Adds a camera scene node to the tree and sets it as active camera.
|
|
|
|
//! \param position: Position of the space relative to its parent where the camera will be placed.
|
|
|
|
//! \param lookat: Position where the camera will look at. Also known as target.
|
|
|
|
//! \param parent: Parent scene node of the camera. Can be null. If the parent moves,
|
|
|
|
//! the camera will move too.
|
|
|
|
//! \return Returns pointer to interface to camera
|
2024-03-20 19:35:52 +01:00
|
|
|
ICameraSceneNode *CSceneManager::addCameraSceneNode(ISceneNode *parent,
|
|
|
|
const core::vector3df &position, const core::vector3df &lookat, s32 id,
|
|
|
|
bool makeActive)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!parent)
|
|
|
|
parent = this;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
ICameraSceneNode *node = new CCameraSceneNode(parent, this, id, position, lookat);
|
2023-10-03 20:37:00 +02:00
|
|
|
|
|
|
|
if (makeActive)
|
|
|
|
setActiveCamera(node);
|
|
|
|
node->drop();
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Adds a billboard scene node to the scene. A billboard is like a 3d sprite: A 2d element,
|
|
|
|
//! which always looks to the camera. It is usually used for things like explosions, fire,
|
|
|
|
//! lensflares and things like that.
|
2024-03-20 19:35:52 +01:00
|
|
|
IBillboardSceneNode *CSceneManager::addBillboardSceneNode(ISceneNode *parent,
|
|
|
|
const core::dimension2d<f32> &size, const core::vector3df &position, s32 id,
|
|
|
|
video::SColor colorTop, video::SColor colorBottom)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!parent)
|
|
|
|
parent = this;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
IBillboardSceneNode *node = new CBillboardSceneNode(parent, this, id, position, size,
|
|
|
|
colorTop, colorBottom);
|
2023-10-03 20:37:00 +02:00
|
|
|
node->drop();
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Adds an empty scene node.
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *CSceneManager::addEmptySceneNode(ISceneNode *parent, s32 id)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!parent)
|
|
|
|
parent = this;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *node = new CEmptySceneNode(parent, this, id);
|
2023-10-03 20:37:00 +02:00
|
|
|
node->drop();
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Adds a dummy transformation scene node to the scene graph.
|
2024-03-20 19:35:52 +01:00
|
|
|
IDummyTransformationSceneNode *CSceneManager::addDummyTransformationSceneNode(
|
|
|
|
ISceneNode *parent, s32 id)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!parent)
|
|
|
|
parent = this;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
IDummyTransformationSceneNode *node = new CDummyTransformationSceneNode(
|
|
|
|
parent, this, id);
|
2023-10-03 20:37:00 +02:00
|
|
|
node->drop();
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns the root scene node. This is the scene node which is parent
|
|
|
|
//! of all scene nodes. The root scene node is a special scene node which
|
|
|
|
//! only exists to manage all scene nodes. It is not rendered and cannot
|
|
|
|
//! be removed from the scene.
|
|
|
|
//! \return Returns a pointer to the root scene node.
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *CSceneManager::getRootSceneNode()
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns the current active camera.
|
|
|
|
//! \return The active camera is returned. Note that this can be NULL, if there
|
|
|
|
//! was no camera created yet.
|
2024-03-20 19:35:52 +01:00
|
|
|
ICameraSceneNode *CSceneManager::getActiveCamera() const
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return ActiveCamera;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Sets the active camera. The previous active camera will be deactivated.
|
|
|
|
//! \param camera: The new camera which should be active.
|
2024-03-20 19:35:52 +01:00
|
|
|
void CSceneManager::setActiveCamera(ICameraSceneNode *camera)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (camera)
|
|
|
|
camera->grab();
|
|
|
|
if (ActiveCamera)
|
|
|
|
ActiveCamera->drop();
|
|
|
|
|
|
|
|
ActiveCamera = camera;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! renders the node.
|
|
|
|
void CSceneManager::render()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//! returns the axis aligned bounding box of this node
|
2024-03-20 19:35:52 +01:00
|
|
|
const core::aabbox3d<f32> &CSceneManager::getBoundingBox() const
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
_IRR_DEBUG_BREAK_IF(true) // Bounding Box of Scene Manager should never be used.
|
|
|
|
|
|
|
|
static const core::aabbox3d<f32> dummy;
|
|
|
|
return dummy;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! returns if node is culled
|
2024-03-20 19:35:52 +01:00
|
|
|
bool CSceneManager::isCulled(const ISceneNode *node) const
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
2024-03-20 19:35:52 +01:00
|
|
|
const ICameraSceneNode *cam = getActiveCamera();
|
|
|
|
if (!cam) {
|
2023-10-03 20:37:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
// has occlusion query information
|
2024-03-20 19:35:52 +01:00
|
|
|
if (node->getAutomaticCulling() & scene::EAC_OCC_QUERY) {
|
|
|
|
result = (Driver->getOcclusionQueryResult(const_cast<ISceneNode *>(node)) == 0);
|
2023-10-03 20:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// can be seen by a bounding box ?
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!result && (node->getAutomaticCulling() & scene::EAC_BOX)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
core::aabbox3d<f32> tbox = node->getBoundingBox();
|
|
|
|
node->getAbsoluteTransformation().transformBoxEx(tbox);
|
2024-03-20 19:35:52 +01:00
|
|
|
result = !(tbox.intersectsWithBox(cam->getViewFrustum()->getBoundingBox()));
|
2023-10-03 20:37:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// can be seen by a bounding sphere
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!result && (node->getAutomaticCulling() & scene::EAC_FRUSTUM_SPHERE)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
const core::aabbox3df nbox = node->getTransformedBoundingBox();
|
|
|
|
const float rad = nbox.getRadius();
|
|
|
|
const core::vector3df center = nbox.getCenter();
|
|
|
|
|
|
|
|
const float camrad = cam->getViewFrustum()->getBoundingRadius();
|
|
|
|
const core::vector3df camcenter = cam->getViewFrustum()->getBoundingCenter();
|
|
|
|
|
|
|
|
const float dist = (center - camcenter).getLengthSQ();
|
|
|
|
const float maxdist = (rad + camrad) * (rad + camrad);
|
|
|
|
|
|
|
|
result = dist > maxdist;
|
|
|
|
}
|
|
|
|
|
|
|
|
// can be seen by cam pyramid planes ?
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!result && (node->getAutomaticCulling() & scene::EAC_FRUSTUM_BOX)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
SViewFrustum frust = *cam->getViewFrustum();
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
// transform the frustum to the node's current absolute transformation
|
2023-10-03 20:37:00 +02:00
|
|
|
core::matrix4 invTrans(node->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE);
|
2024-03-20 19:35:52 +01:00
|
|
|
// invTrans.makeInverse();
|
2023-10-03 20:37:00 +02:00
|
|
|
frust.transform(invTrans);
|
|
|
|
|
|
|
|
core::vector3df edges[8];
|
|
|
|
node->getBoundingBox().getEdges(edges);
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (s32 i = 0; i < scene::SViewFrustum::VF_PLANE_COUNT; ++i) {
|
|
|
|
bool boxInFrustum = false;
|
|
|
|
for (u32 j = 0; j < 8; ++j) {
|
|
|
|
if (frust.planes[i].classifyPointRelation(edges[j]) != core::ISREL3D_FRONT) {
|
|
|
|
boxInFrustum = true;
|
2023-10-03 20:37:00 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!boxInFrustum) {
|
2023-10-03 20:37:00 +02:00
|
|
|
result = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! registers a node for rendering it at a specific time.
|
2024-03-20 19:35:52 +01:00
|
|
|
u32 CSceneManager::registerNodeForRendering(ISceneNode *node, E_SCENE_NODE_RENDER_PASS pass)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
u32 taken = 0;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
switch (pass) {
|
2023-10-03 20:37:00 +02:00
|
|
|
// take camera if it is not already registered
|
2024-03-20 19:35:52 +01:00
|
|
|
case ESNRP_CAMERA: {
|
|
|
|
taken = 1;
|
|
|
|
for (u32 i = 0; i != CameraList.size(); ++i) {
|
|
|
|
if (CameraList[i] == node) {
|
|
|
|
taken = 0;
|
|
|
|
break;
|
2023-10-03 20:37:00 +02:00
|
|
|
}
|
|
|
|
}
|
2024-03-20 19:35:52 +01:00
|
|
|
if (taken) {
|
|
|
|
CameraList.push_back(node);
|
|
|
|
}
|
|
|
|
} break;
|
2023-10-03 20:37:00 +02:00
|
|
|
case ESNRP_SKY_BOX:
|
|
|
|
SkyBoxList.push_back(node);
|
|
|
|
taken = 1;
|
|
|
|
break;
|
|
|
|
case ESNRP_SOLID:
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!isCulled(node)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
SolidNodeList.push_back(node);
|
|
|
|
taken = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ESNRP_TRANSPARENT:
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!isCulled(node)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
TransparentNodeList.push_back(TransparentNodeEntry(node, camWorldPos));
|
|
|
|
taken = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ESNRP_TRANSPARENT_EFFECT:
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!isCulled(node)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
TransparentEffectNodeList.push_back(TransparentNodeEntry(node, camWorldPos));
|
|
|
|
taken = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ESNRP_AUTOMATIC:
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!isCulled(node)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
const u32 count = node->getMaterialCount();
|
|
|
|
|
|
|
|
taken = 0;
|
2024-03-20 19:35:52 +01:00
|
|
|
for (u32 i = 0; i < count; ++i) {
|
|
|
|
if (Driver->needsTransparentRenderPass(node->getMaterial(i))) {
|
2023-10-03 20:37:00 +02:00
|
|
|
// register as transparent node
|
|
|
|
TransparentNodeEntry e(node, camWorldPos);
|
|
|
|
TransparentNodeList.push_back(e);
|
|
|
|
taken = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// not transparent, register as solid
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!taken) {
|
2023-10-03 20:37:00 +02:00
|
|
|
SolidNodeList.push_back(node);
|
|
|
|
taken = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ESNRP_GUI:
|
2024-03-20 19:35:52 +01:00
|
|
|
if (!isCulled(node)) {
|
2023-10-03 20:37:00 +02:00
|
|
|
GuiNodeList.push_back(node);
|
|
|
|
taken = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// as of yet unused
|
|
|
|
case ESNRP_LIGHT:
|
|
|
|
case ESNRP_SHADOW:
|
|
|
|
case ESNRP_NONE: // ignore this one
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return taken;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSceneManager::clearAllRegisteredNodesForRendering()
|
|
|
|
{
|
|
|
|
CameraList.clear();
|
|
|
|
SkyBoxList.clear();
|
|
|
|
SolidNodeList.clear();
|
|
|
|
TransparentNodeList.clear();
|
|
|
|
TransparentEffectNodeList.clear();
|
|
|
|
GuiNodeList.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
//! This method is called just before the rendering process of the whole scene.
|
|
|
|
//! draws all scene nodes
|
|
|
|
void CSceneManager::drawAll()
|
|
|
|
{
|
|
|
|
if (!Driver)
|
|
|
|
return;
|
|
|
|
|
|
|
|
u32 i; // new ISO for scoping problem in some compilers
|
|
|
|
|
|
|
|
// reset all transforms
|
|
|
|
Driver->setMaterial(video::SMaterial());
|
2024-03-20 19:35:52 +01:00
|
|
|
Driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix);
|
|
|
|
Driver->setTransform(video::ETS_VIEW, core::IdentityMatrix);
|
|
|
|
Driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
|
|
|
|
for (i = video::ETS_COUNT - 1; i >= video::ETS_TEXTURE_0; --i)
|
|
|
|
Driver->setTransform((video::E_TRANSFORMATION_STATE)i, core::IdentityMatrix);
|
2023-10-03 20:37:00 +02:00
|
|
|
// TODO: This should not use an attribute here but a real parameter when necessary (too slow!)
|
|
|
|
Driver->setAllowZWriteOnTransparent(Parameters->getAttributeAsBool(ALLOW_ZWRITE_ON_TRANSPARENT));
|
|
|
|
|
|
|
|
// do animations and other stuff.
|
|
|
|
OnAnimate(os::Timer::getTime());
|
|
|
|
|
|
|
|
/*!
|
|
|
|
First Scene Node for prerendering should be the active camera
|
|
|
|
consistent Camera is needed for culling
|
|
|
|
*/
|
2024-03-20 19:35:52 +01:00
|
|
|
camWorldPos.set(0, 0, 0);
|
|
|
|
if (ActiveCamera) {
|
2023-10-03 20:37:00 +02:00
|
|
|
ActiveCamera->render();
|
|
|
|
camWorldPos = ActiveCamera->getAbsolutePosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
// let all nodes register themselves
|
|
|
|
OnRegisterSceneNode();
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
// render camera scenes
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
CurrentRenderPass = ESNRP_CAMERA;
|
|
|
|
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (i = 0; i < CameraList.size(); ++i)
|
2023-10-03 20:37:00 +02:00
|
|
|
CameraList[i]->render();
|
|
|
|
|
|
|
|
CameraList.set_used(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// render skyboxes
|
|
|
|
{
|
|
|
|
CurrentRenderPass = ESNRP_SKY_BOX;
|
|
|
|
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (i = 0; i < SkyBoxList.size(); ++i)
|
2023-10-03 20:37:00 +02:00
|
|
|
SkyBoxList[i]->render();
|
|
|
|
|
|
|
|
SkyBoxList.set_used(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// render default objects
|
|
|
|
{
|
|
|
|
CurrentRenderPass = ESNRP_SOLID;
|
|
|
|
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
|
|
|
|
|
|
|
|
SolidNodeList.sort(); // sort by textures
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (i = 0; i < SolidNodeList.size(); ++i)
|
2023-10-03 20:37:00 +02:00
|
|
|
SolidNodeList[i].Node->render();
|
|
|
|
|
|
|
|
SolidNodeList.set_used(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// render transparent objects.
|
|
|
|
{
|
|
|
|
CurrentRenderPass = ESNRP_TRANSPARENT;
|
|
|
|
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
|
|
|
|
|
|
|
|
TransparentNodeList.sort(); // sort by distance from camera
|
2024-03-20 19:35:52 +01:00
|
|
|
for (i = 0; i < TransparentNodeList.size(); ++i)
|
2023-10-03 20:37:00 +02:00
|
|
|
TransparentNodeList[i].Node->render();
|
|
|
|
|
|
|
|
TransparentNodeList.set_used(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// render transparent effect objects.
|
|
|
|
{
|
|
|
|
CurrentRenderPass = ESNRP_TRANSPARENT_EFFECT;
|
|
|
|
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
|
|
|
|
|
|
|
|
TransparentEffectNodeList.sort(); // sort by distance from camera
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (i = 0; i < TransparentEffectNodeList.size(); ++i)
|
2023-10-03 20:37:00 +02:00
|
|
|
TransparentEffectNodeList[i].Node->render();
|
|
|
|
|
|
|
|
TransparentEffectNodeList.set_used(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// render custom gui nodes
|
|
|
|
{
|
|
|
|
CurrentRenderPass = ESNRP_GUI;
|
|
|
|
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (i = 0; i < GuiNodeList.size(); ++i)
|
2023-10-03 20:37:00 +02:00
|
|
|
GuiNodeList[i]->render();
|
|
|
|
|
|
|
|
GuiNodeList.set_used(0);
|
|
|
|
}
|
|
|
|
clearDeletionList();
|
|
|
|
|
|
|
|
CurrentRenderPass = ESNRP_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Adds an external mesh loader.
|
2024-03-20 19:35:52 +01:00
|
|
|
void CSceneManager::addExternalMeshLoader(IMeshLoader *externalLoader)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!externalLoader)
|
|
|
|
return;
|
|
|
|
|
|
|
|
externalLoader->grab();
|
|
|
|
MeshLoaderList.push_back(externalLoader);
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns the number of mesh loaders supported by Irrlicht at this time
|
|
|
|
u32 CSceneManager::getMeshLoaderCount() const
|
|
|
|
{
|
|
|
|
return MeshLoaderList.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Retrieve the given mesh loader
|
2024-03-20 19:35:52 +01:00
|
|
|
IMeshLoader *CSceneManager::getMeshLoader(u32 index) const
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (index < MeshLoaderList.size())
|
|
|
|
return MeshLoaderList[index];
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns a pointer to the scene collision manager.
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneCollisionManager *CSceneManager::getSceneCollisionManager()
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return CollisionManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns a pointer to the mesh manipulator.
|
2024-03-20 19:35:52 +01:00
|
|
|
IMeshManipulator *CSceneManager::getMeshManipulator()
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return Driver->getMeshManipulator();
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Adds a scene node to the deletion queue.
|
2024-03-20 19:35:52 +01:00
|
|
|
void CSceneManager::addToDeletionQueue(ISceneNode *node)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (!node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
node->grab();
|
|
|
|
DeletionList.push_back(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
//! clears the deletion list
|
|
|
|
void CSceneManager::clearDeletionList()
|
|
|
|
{
|
|
|
|
if (DeletionList.empty())
|
|
|
|
return;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (u32 i = 0; i < DeletionList.size(); ++i) {
|
2023-10-03 20:37:00 +02:00
|
|
|
DeletionList[i]->remove();
|
|
|
|
DeletionList[i]->drop();
|
|
|
|
}
|
|
|
|
|
|
|
|
DeletionList.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns the first scene node with the specified name.
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *CSceneManager::getSceneNodeFromName(const char *name, ISceneNode *start)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (start == 0)
|
|
|
|
start = getRootSceneNode();
|
|
|
|
|
2024-02-06 20:22:44 +01:00
|
|
|
auto startName = start->getName();
|
|
|
|
if (startName.has_value() && startName == name)
|
2023-10-03 20:37:00 +02:00
|
|
|
return start;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *node = 0;
|
2023-10-03 20:37:00 +02:00
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
const ISceneNodeList &list = start->getChildren();
|
2023-10-03 20:37:00 +02:00
|
|
|
ISceneNodeList::const_iterator it = list.begin();
|
2024-03-20 19:35:52 +01:00
|
|
|
for (; it != list.end(); ++it) {
|
2023-10-03 20:37:00 +02:00
|
|
|
node = getSceneNodeFromName(name, *it);
|
|
|
|
if (node)
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns the first scene node with the specified id.
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *CSceneManager::getSceneNodeFromId(s32 id, ISceneNode *start)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (start == 0)
|
|
|
|
start = getRootSceneNode();
|
|
|
|
|
|
|
|
if (start->getID() == id)
|
|
|
|
return start;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *node = 0;
|
2023-10-03 20:37:00 +02:00
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
const ISceneNodeList &list = start->getChildren();
|
2023-10-03 20:37:00 +02:00
|
|
|
ISceneNodeList::const_iterator it = list.begin();
|
2024-03-20 19:35:52 +01:00
|
|
|
for (; it != list.end(); ++it) {
|
2023-10-03 20:37:00 +02:00
|
|
|
node = getSceneNodeFromId(id, *it);
|
|
|
|
if (node)
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns the first scene node with the specified type.
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *CSceneManager::getSceneNodeFromType(scene::ESCENE_NODE_TYPE type, ISceneNode *start)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (start == 0)
|
|
|
|
start = getRootSceneNode();
|
|
|
|
|
|
|
|
if (start->getType() == type || ESNT_ANY == type)
|
|
|
|
return start;
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneNode *node = 0;
|
2023-10-03 20:37:00 +02:00
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
const ISceneNodeList &list = start->getChildren();
|
2023-10-03 20:37:00 +02:00
|
|
|
ISceneNodeList::const_iterator it = list.begin();
|
2024-03-20 19:35:52 +01:00
|
|
|
for (; it != list.end(); ++it) {
|
2023-10-03 20:37:00 +02:00
|
|
|
node = getSceneNodeFromType(type, *it);
|
|
|
|
if (node)
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! returns scene nodes by type.
|
2024-03-20 19:35:52 +01:00
|
|
|
void CSceneManager::getSceneNodesFromType(ESCENE_NODE_TYPE type, core::array<scene::ISceneNode *> &outNodes, ISceneNode *start)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
if (start == 0)
|
|
|
|
start = getRootSceneNode();
|
|
|
|
|
|
|
|
if (start->getType() == type || ESNT_ANY == type)
|
|
|
|
outNodes.push_back(start);
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
const ISceneNodeList &list = start->getChildren();
|
2023-10-03 20:37:00 +02:00
|
|
|
ISceneNodeList::const_iterator it = list.begin();
|
|
|
|
|
2024-03-20 19:35:52 +01:00
|
|
|
for (; it != list.end(); ++it) {
|
2023-10-03 20:37:00 +02:00
|
|
|
getSceneNodesFromType(type, outNodes, *it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Posts an input event to the environment. Usually you do not have to
|
|
|
|
//! use this method, it is used by the internal engine.
|
2024-03-20 19:35:52 +01:00
|
|
|
bool CSceneManager::postEventFromUser(const SEvent &event)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
bool ret = false;
|
2024-03-20 19:35:52 +01:00
|
|
|
ICameraSceneNode *cam = getActiveCamera();
|
2023-10-03 20:37:00 +02:00
|
|
|
if (cam)
|
|
|
|
ret = cam->OnEvent(event);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Removes all children of this scene node
|
|
|
|
void CSceneManager::removeAll()
|
|
|
|
{
|
|
|
|
ISceneNode::removeAll();
|
|
|
|
setActiveCamera(0);
|
|
|
|
// Make sure the driver is reset, might need a more complex method at some point
|
|
|
|
if (Driver)
|
|
|
|
Driver->setMaterial(video::SMaterial());
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Clears the whole scene. All scene nodes are removed.
|
|
|
|
void CSceneManager::clear()
|
|
|
|
{
|
|
|
|
removeAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns interface to the parameters set in this scene.
|
2024-03-20 19:35:52 +01:00
|
|
|
io::IAttributes *CSceneManager::getParameters()
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return Parameters;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns current render pass.
|
|
|
|
E_SCENE_NODE_RENDER_PASS CSceneManager::getSceneNodeRenderPass() const
|
|
|
|
{
|
|
|
|
return CurrentRenderPass;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns an interface to the mesh cache which is shared between all existing scene managers.
|
2024-03-20 19:35:52 +01:00
|
|
|
IMeshCache *CSceneManager::getMeshCache()
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return MeshCache;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Creates a new scene manager.
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneManager *CSceneManager::createNewSceneManager(bool cloneContent)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
2024-03-20 19:35:52 +01:00
|
|
|
CSceneManager *manager = new CSceneManager(Driver, CursorControl, MeshCache);
|
2023-10-03 20:37:00 +02:00
|
|
|
|
|
|
|
if (cloneContent)
|
|
|
|
manager->cloneMembers(this, manager);
|
|
|
|
|
|
|
|
return manager;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Sets ambient color of the scene
|
|
|
|
void CSceneManager::setAmbientLight(const video::SColorf &ambientColor)
|
|
|
|
{
|
|
|
|
AmbientLight = ambientColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Returns ambient color of the scene
|
2024-03-20 19:35:52 +01:00
|
|
|
const video::SColorf &CSceneManager::getAmbientLight() const
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return AmbientLight;
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Get a skinned mesh, which is not available as header-only code
|
2024-03-20 19:35:52 +01:00
|
|
|
ISkinnedMesh *CSceneManager::createSkinnedMesh()
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return new CSkinnedMesh();
|
|
|
|
}
|
|
|
|
|
|
|
|
// creates a scenemanager
|
2024-03-20 19:35:52 +01:00
|
|
|
ISceneManager *createSceneManager(video::IVideoDriver *driver, gui::ICursorControl *cursorcontrol)
|
2023-10-03 20:37:00 +02:00
|
|
|
{
|
|
|
|
return new CSceneManager(driver, cursorcontrol, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end namespace scene
|
|
|
|
} // end namespace irr
|