diff --git a/changes.txt b/changes.txt index c95a869f..10027554 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,7 @@ -------------------------- Changes in 1.9 (not yet released) +- Add IMeshSceneNode::setNodeRegistration to allow registering MeshSceneNodes to the SceneManager per buffer instead of per node - Add SMaterialLayer::hasSetTextureMatrix and SMaterialLayer::resetTextureMatrix - Add IShaderConstantSetCallBack::OnCreate to allow earlier access to IMaterialRendererServices - CIrrDeviceWin32::yield() now uses Sleep(0) instead of Sleep(1). diff --git a/include/IMeshSceneNode.h b/include/IMeshSceneNode.h index 309f46db..25600e76 100644 --- a/include/IMeshSceneNode.h +++ b/include/IMeshSceneNode.h @@ -15,6 +15,20 @@ namespace scene class IShadowVolumeSceneNode; class IMesh; +//! Option for nodes how to register themeselves at the SceneManager +enum ENodeRegistration +{ + //! Each node registers once and renders all it's mesh-buffers + ENR_DEFAULT, + + //! Register a new node per mesh-buffer at the SceneManager + //! It allows the SceneManager to sort in each render stage per buffer instead of per node. + //! This can be useful when having several transparent buffers in a mesh. + //! Depending on the scene (and hardware) this can have a positive or negative effect on performance. + //! It can avoid texture-switches, but adds nodes to sort and more matrix transformations are set. + ENR_PER_MESH_BUFFER +}; + //! A scene node displaying a static mesh class IMeshSceneNode : public ISceneNode @@ -28,9 +42,11 @@ public: const core::vector3df& position = core::vector3df(0,0,0), const core::vector3df& rotation = core::vector3df(0,0,0), const core::vector3df& scale = core::vector3df(1,1,1)) - : ISceneNode(parent, mgr, id, position, rotation, scale) {} + : ISceneNode(parent, mgr, id, position, rotation, scale) + , NodeRegistration(ENR_DEFAULT) + {} - //! Sets a new mesh to display + //! Sets a new mesh to display or update mesh when it changed /** \param mesh Mesh to display. */ virtual void setMesh(IMesh* mesh) = 0; @@ -73,6 +89,23 @@ public: /** This flag can be set by setReadOnlyMaterials(). \return Whether the materials are read-only. */ virtual bool isReadOnlyMaterials() const = 0; + + //! Set how the node registers itself to the SceneManager + /** Note: Derived classes can ignore this flag, so think of it as a hint. */ + virtual void setNodeRegistration(ENodeRegistration nodeRegistration) + { + NodeRegistration = nodeRegistration; + } + + //! How does a node register itself to the SceneManager + /** Note: Derived classes may ignore this flag */ + virtual ENodeRegistration getNodeRegistration() const + { + return NodeRegistration; + } + +protected: + ENodeRegistration NodeRegistration; }; } // end namespace scene diff --git a/include/ISceneManager.h b/include/ISceneManager.h index eae5d49b..64ab0e8c 100644 --- a/include/ISceneManager.h +++ b/include/ISceneManager.h @@ -66,7 +66,7 @@ namespace scene //! In this pass, lights are transformed into camera space and added to the driver ESNRP_LIGHT =2, - //! This is used for sky boxes. + //! This is mostly used for sky boxes. Stage between light and solid. ESNRP_SKY_BOX =4, //! All normal objects can use this for registering themselves. diff --git a/source/Irrlicht/CBufferRenderNode.h b/source/Irrlicht/CBufferRenderNode.h new file mode 100644 index 00000000..923434fa --- /dev/null +++ b/source/Irrlicht/CBufferRenderNode.h @@ -0,0 +1,185 @@ +// 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 "IMeshSceneNode.h" +#include "IVideoDriver.h" +#include "ISceneManager.h" +#include "ISceneNode.h" + +namespace irr +{ +namespace scene +{ + +// A node which helps rendering a single buffer of an IMeshSceneNode +// It solves several problems: +// - Allowing the scene manager to sort meshbuffers. Currently it only sorts nodes, so we have to put each meshbuffer in an extra node to allow that +// The reason we want that is +// a) Better sorting when a node has several transparent buffers (without it they will be just drawn in original order) +// b) It can allow to avoid texture changes in the render-pipeline which can make quite a bit of a performance difference +// - It buffers the RenderPass. Bit of an abuse of this interface maybe? +// Strangely the check for finding out the correct render pass constantly shows up in profilers. +// Not exactly sure why as the check looks pretty cheap. My best guess is that there are some cache misses going on due to several virtual +// function pointers being involved in the transparency check. +// +// For now (added pre Irrlicht 1.9) this interface is still a bit experimental. Maybe could go into public headers later, not sure yet. +// Or maybe the SceneManager shouldn't work with nodes at all but a simplified interface to render buffers from which Nodes can derive? +// CBufferRenderNode isn't really a node - it has to work around nearly all the ISceneNode functions, it only has to be one because +// the SceneManager can't sort anything else but nodes. +class CBufferRenderNode : public ISceneNode +{ +public: + CBufferRenderNode(irr::scene::IMeshSceneNode& parent, irr::scene::ISceneManager* mgr, irr::u32 bufferIdx) + : ISceneNode(0, mgr) // we don't want it in the scenegraph + , MeshNodeParent(parent) + , BufferIdx(bufferIdx) + , RenderPass(ESNRP_NONE) + , ParentDoesRender(true) + { + // While it's not the parent in the SceneGraph, we still want to allow accessing it + // That can be useful p.E. in a light manager + // Arguably if it's a good idea as it's a bit going against the ISceneNode interface - having a parent which doesn't have this node as child. + // But the alternative is adding another member to the ISceneNode or having SceneManager not use the ISceneNode for rendering + // So for now it should be fine, but if that interface ever get's public... we might have to reconsider + Parent = &MeshNodeParent; + } + + u32 prepareRendering(E_SCENE_NODE_RENDER_PASS pass, bool parentDoesRender) + { + RenderPass = pass; + ParentDoesRender = parentDoesRender; + if ( !ParentDoesRender ) + return SceneManager->registerNodeForRendering(this, pass); + return 0; + } + + E_SCENE_NODE_RENDER_PASS getRenderPass() const + { + return RenderPass; + } + + // When true render() this node hasn't registered itself for rendering, but expects it's owner to do the rendering + bool getDoesParentRender() const + { + return ParentDoesRender; + } + + // Render meshbuffer, but don't set transformation + // It's assumed that this function is only called from within the correct render stage + void renderBuffer(video::IVideoDriver* driver) + { + const IMeshBuffer* mb = MeshNodeParent.getMesh()->getMeshBuffer(BufferIdx); + if (mb) + { + const video::SMaterial& material = MeshNodeParent.getMaterial(BufferIdx); + driver->setMaterial(material); + driver->drawMeshBuffer(mb); + } + } + + //! Renders the node. + virtual void render() IRR_OVERRIDE + { + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + driver->setTransform(video::ETS_WORLD, MeshNodeParent.getAbsoluteTransformation()); + renderBuffer(driver); + + // resetting each time so direct calls to render() for parent node continue to work + RenderPass = ESNRP_NONE; + ParentDoesRender = true; + } + + virtual const core::aabbox3d& getBoundingBox() const IRR_OVERRIDE + { + return MeshNodeParent.getMesh()->getMeshBuffer(BufferIdx)->getBoundingBox(); + } + + virtual video::SMaterial& getMaterial(u32 num) IRR_OVERRIDE + { + return MeshNodeParent.getMaterial(BufferIdx); + } + + virtual u32 getMaterialCount() const IRR_OVERRIDE + { + return 1; + } + + virtual const core::matrix4& getAbsoluteTransformation() const IRR_OVERRIDE + { + return MeshNodeParent.getAbsoluteTransformation(); + } + + virtual const core::aabbox3d getTransformedBoundingBox() const IRR_OVERRIDE + { + core::aabbox3d box = getBoundingBox(); + getAbsoluteTransformation().transformBoxEx(box); + return box; + } + + virtual void getTransformedBoundingBoxEdges(core::array< core::vector3d >& edges) const IRR_OVERRIDE + { + edges.set_used(8); + getBoundingBox().getEdges( edges.pointer() ); + for ( u32 i=0; i<8; ++i ) + getAbsoluteTransformation().transformVect( edges[i] ); + } + + virtual core::matrix4 getRelativeTransformation() const IRR_OVERRIDE + { + return MeshNodeParent.getRelativeTransformation(); + } + + virtual s32 getID() const IRR_OVERRIDE + { + return MeshNodeParent.getID(); + } + + virtual const core::vector3df& getScale() const IRR_OVERRIDE + { + return MeshNodeParent.getScale(); + } + + virtual const core::vector3df& getRotation() const IRR_OVERRIDE + { + return MeshNodeParent.getRotation(); + } + + virtual const core::vector3df& getPosition() const IRR_OVERRIDE + { + return MeshNodeParent.getPosition(); + } + + virtual core::vector3df getAbsolutePosition() const IRR_OVERRIDE + { + return MeshNodeParent.getAbsolutePosition(); + } + + virtual ITriangleSelector* getTriangleSelector() const IRR_OVERRIDE + { + return MeshNodeParent.getTriangleSelector(); + } + + // Not allowing any of that stuff + virtual void OnRegisterSceneNode()IRR_OVERRIDE {} + virtual void OnAnimate(u32 timeMs) IRR_OVERRIDE {} + virtual void addChild(ISceneNode* child) IRR_OVERRIDE {} + virtual void addAnimator(ISceneNodeAnimator* animator) IRR_OVERRIDE {} + virtual void setScale(const core::vector3df& scale) IRR_OVERRIDE {} + virtual void setRotation(const core::vector3df& rotation) IRR_OVERRIDE {} + virtual void setPosition(const core::vector3df& newpos) IRR_OVERRIDE {} + virtual void setParent(ISceneNode* newParent) IRR_OVERRIDE {} + virtual void setTriangleSelector(ITriangleSelector* selector) IRR_OVERRIDE {} + virtual void updateAbsolutePosition() IRR_OVERRIDE {} + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const IRR_OVERRIDE {} + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) IRR_OVERRIDE {} + +private: + irr::scene::IMeshSceneNode& MeshNodeParent; + irr::u32 BufferIdx; // Note: Not saving the meshbuffer pointer as meshes can add/remove buffers and we don't want to keep track of that + E_SCENE_NODE_RENDER_PASS RenderPass; + bool ParentDoesRender; +}; + +} // end namespace scene +} // end namespace irr diff --git a/source/Irrlicht/CMeshSceneNode.cpp b/source/Irrlicht/CMeshSceneNode.cpp index 59d1c1cd..7e3a6b9f 100644 --- a/source/Irrlicht/CMeshSceneNode.cpp +++ b/source/Irrlicht/CMeshSceneNode.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CMeshSceneNode.h" +#include "CBufferRenderNode.h" #include "IVideoDriver.h" #include "ISceneManager.h" #include "S3DVertex.h" @@ -22,14 +23,12 @@ namespace irr namespace scene { - - //! constructor CMeshSceneNode::CMeshSceneNode(IMesh* mesh, ISceneNode* parent, ISceneManager* mgr, s32 id, const core::vector3df& position, const core::vector3df& rotation, const core::vector3df& scale) -: IMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0), Shadow(0), - PassCount(0), ReadOnlyMaterials(false) + : IMeshSceneNode(parent, mgr, id, position, rotation, scale) + , Mesh(0), Shadow(0), ReadOnlyMaterials(false) { #ifdef _DEBUG setDebugName("CMeshSceneNode"); @@ -42,6 +41,7 @@ CMeshSceneNode::CMeshSceneNode(IMesh* mesh, ISceneNode* parent, ISceneManager* m //! destructor CMeshSceneNode::~CMeshSceneNode() { + setUsedBufferRenderNodes(0); if (Shadow) Shadow->drop(); if (Mesh) @@ -54,39 +54,60 @@ void CMeshSceneNode::OnRegisterSceneNode() { if (IsVisible && Mesh) { - // because this node supports rendering of mixed mode meshes consisting of + Box = Mesh->getBoundingBox(); // in case mesh was modified, as clipping happens when registering nodes for rendering + + // Because this node supports rendering of mixed mode meshes consisting of // transparent and solid material at the same time, we need to go through all // materials, check of what type they are and register this node for the right // render pass according to that. + // Also some buffers might register into their own render node video::IVideoDriver* driver = SceneManager->getVideoDriver(); - PassCount = 0; int transparentCount = 0; int solidCount = 0; - // count transparent and solid materials in this scene node - const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size(); - for (u32 i=0; igetMeshBuffer(i)->getMaterial() : Materials[i]; + // count transparent and solid materials in this scene node + const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size(); + const bool parentRenders = NodeRegistration == ENR_DEFAULT || numMaterials < 2; + for (u32 i=0; igetMeshBuffer(i)->getMaterial() : Materials[i]; - if ( driver->needsTransparentRenderPass(material) ) - ++transparentCount; - else - ++solidCount; - - if (solidCount && transparentCount) - break; + if ( driver->needsTransparentRenderPass(material) ) + { + BufferRenderNodes[i]->prepareRendering(ESNRP_TRANSPARENT, parentRenders); + if ( parentRenders ) + { + ++transparentCount; + } + } + else + { + BufferRenderNodes[i]->prepareRendering(ESNRP_SOLID, parentRenders); + if ( parentRenders ) + { + ++solidCount; + } + } + } } // register according to material types counted if (solidCount) - SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID); + SceneManager->registerNodeForRendering(this, ESNRP_SOLID); if (transparentCount) - SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); + SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT); + + if (Shadow) // update (not render) shadow node after lights have been set up + SceneManager->registerNodeForRendering(this, ESNRP_SKY_BOX); + + if (DebugDataVisible) // debug data has it's own render-stage between solid and transparence + SceneManager->registerNodeForRendering(this, ESNRP_SHADOW); ISceneNode::OnRegisterSceneNode(); } @@ -96,109 +117,110 @@ void CMeshSceneNode::OnRegisterSceneNode() //! renders the node. void CMeshSceneNode::render() { - video::IVideoDriver* driver = SceneManager->getVideoDriver(); - - if (!Mesh || !driver) + if (!Mesh ) return; - const bool isTransparentPass = - SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; + const E_SCENE_NODE_RENDER_PASS renderPass = SceneManager->getSceneNodeRenderPass(); - ++PassCount; - - driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); - Box = Mesh->getBoundingBox(); - - if (Shadow && PassCount==1) - Shadow->updateShadowVolumes(); - - // for debug purposes only: - - bool renderMeshes = true; - video::SMaterial mat; - if (DebugDataVisible && PassCount==1) + if ( renderPass == ESNRP_SKY_BOX ) { - // overwrite half transparency - if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY) - { - for (u32 g=0; ggetMeshBufferCount(); ++g) - { - mat = Materials[g]; - mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; - driver->setMaterial(mat); - driver->drawMeshBuffer(Mesh->getMeshBuffer(g)); - } - renderMeshes = false; - } + if (Shadow ) + Shadow->updateShadowVolumes(); } - - // render original meshes - if (renderMeshes) + else if ( renderPass == ESNRP_SHADOW ) { - for (u32 i=0; igetMeshBufferCount(); ++i) + // for debug purposes only + if ( DebugDataVisible ) { - scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i); - if (mb) + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); + + // render with half transparency + if (DebugDataVisible & scene::EDS_HALF_TRANSPARENCY) { - const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i]; - - const bool transparent = driver->needsTransparentRenderPass(material); - - // only render transparent buffer if this is the transparent render pass - // and solid only in solid pass - if (transparent == isTransparentPass) + for (u32 g=0; ggetMeshBufferCount(); ++g) { - driver->setMaterial(material); - driver->drawMeshBuffer(mb); + irr::video::SMaterial mat = Materials[g]; + mat.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; + driver->setMaterial(mat); + driver->drawMeshBuffer(Mesh->getMeshBuffer(g)); + } + } + + video::SMaterial m; + m.Lighting = false; + m.AntiAliasing=0; + driver->setMaterial(m); + + if (DebugDataVisible & scene::EDS_BBOX) + { + driver->draw3DBox(Box, video::SColor(255,255,255,255)); + } + if (DebugDataVisible & scene::EDS_BBOX_BUFFERS) + { + for (u32 g=0; ggetMeshBufferCount(); ++g) + { + driver->draw3DBox( + Mesh->getMeshBuffer(g)->getBoundingBox(), + video::SColor(255,190,128,128)); + } + } + + if (DebugDataVisible & scene::EDS_NORMALS) + { + // draw normals + const f32 debugNormalLength = SceneManager->getParameters()->getAttributeAsFloat(DEBUG_NORMAL_LENGTH); + const video::SColor debugNormalColor = SceneManager->getParameters()->getAttributeAsColor(DEBUG_NORMAL_COLOR); + const u32 count = Mesh->getMeshBufferCount(); + + for (u32 i=0; i != count; ++i) + { + driver->drawMeshBufferNormals(Mesh->getMeshBuffer(i), debugNormalLength, debugNormalColor); + } + } + + // show mesh + if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY) + { + m.Wireframe = true; + driver->setMaterial(m); + + for (u32 g=0; ggetMeshBufferCount(); ++g) + { + driver->drawMeshBuffer(Mesh->getMeshBuffer(g)); } } } } - - // for debug purposes only: - if (DebugDataVisible && PassCount==1) + else // solid, transparent or unknown (none when render is called without SceneManager) render stages { - video::SMaterial m; - m.Lighting = false; - m.AntiAliasing=0; - driver->setMaterial(m); + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); - if (DebugDataVisible & scene::EDS_BBOX) + // render buffers, or at least those which don't render in their own node + for (u32 i=0; idraw3DBox(Box, video::SColor(255,255,255,255)); - } - if (DebugDataVisible & scene::EDS_BBOX_BUFFERS) - { - for (u32 g=0; ggetMeshBufferCount(); ++g) + CBufferRenderNode* bufRenderNode = BufferRenderNodes[i]; + if ( bufRenderNode->getDoesParentRender()) { - driver->draw3DBox( - Mesh->getMeshBuffer(g)->getBoundingBox(), - video::SColor(255,190,128,128)); - } - } + E_SCENE_NODE_RENDER_PASS bufferRenderPass = bufRenderNode->getRenderPass(); - if (DebugDataVisible & scene::EDS_NORMALS) - { - // draw normals - const f32 debugNormalLength = SceneManager->getParameters()->getAttributeAsFloat(DEBUG_NORMAL_LENGTH); - const video::SColor debugNormalColor = SceneManager->getParameters()->getAttributeAsColor(DEBUG_NORMAL_COLOR); - const u32 count = Mesh->getMeshBufferCount(); + // render() called without OnRegisterSceneNode, but still wants to render in a specific render stage + // Note: Not checking transparency every time, as check got slightly expensive (I think it's prone to cache-misses) + if ( bufferRenderPass == ESNRP_NONE && renderPass > ESNRP_NONE ) + { + if ( driver->needsTransparentRenderPass(getMaterial(i)) ) + { + bufferRenderPass = ESNRP_TRANSPARENT; + } + else + { + bufferRenderPass = ESNRP_SOLID; + } + } - for (u32 i=0; i != count; ++i) - { - driver->drawMeshBufferNormals(Mesh->getMeshBuffer(i), debugNormalLength, debugNormalColor); - } - } - - // show mesh - if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY) - { - m.Wireframe = true; - driver->setMaterial(m); - - for (u32 g=0; ggetMeshBufferCount(); ++g) - { - driver->drawMeshBuffer(Mesh->getMeshBuffer(g)); + if ( bufRenderNode->getRenderPass() == renderPass ) + bufRenderNode->renderBuffer(driver); } } } @@ -207,7 +229,7 @@ void CMeshSceneNode::render() //! Removes a child from this scene node. //! Implemented here, to be able to remove the shadow properly, if there is one, -//! or to remove attached childs. +//! or to remove attached children. bool CMeshSceneNode::removeChild(ISceneNode* child) { if (child && Shadow == child) @@ -256,6 +278,22 @@ u32 CMeshSceneNode::getMaterialCount() const return Materials.size(); } +void CMeshSceneNode::setUsedBufferRenderNodes(irr::u32 num) +{ + if ( BufferRenderNodes.size() > num ) + { + for ( irr::u32 i=num; idrop(); + BufferRenderNodes.erase(num, BufferRenderNodes.size()-num); + } + else if ( BufferRenderNodes.size() < num ) + { + for ( irr::u32 i=BufferRenderNodes.size(); i < num; ++i ) + { + BufferRenderNodes.push_back( new CBufferRenderNode(*this, SceneManager, i) ); + } + } +} //! Sets a new mesh void CMeshSceneNode::setMesh(IMesh* mesh) @@ -264,10 +302,16 @@ void CMeshSceneNode::setMesh(IMesh* mesh) { mesh->grab(); if (Mesh) + { Mesh->drop(); + } Mesh = mesh; + + // Note: Mesh can change amount of meshbuffers later and we don't handle that so far so that would cause trouble + // For now assuming users call setMesh again in that case copyMaterials(); + setUsedBufferRenderNodes(Mesh ? Mesh->getMeshBufferCount() : 0); } } diff --git a/source/Irrlicht/CMeshSceneNode.h b/source/Irrlicht/CMeshSceneNode.h index 3b1c1b1c..04537035 100644 --- a/source/Irrlicht/CMeshSceneNode.h +++ b/source/Irrlicht/CMeshSceneNode.h @@ -12,6 +12,7 @@ namespace irr { namespace scene { + class CBufferRenderNode; class CMeshSceneNode : public IMeshSceneNode { @@ -83,6 +84,7 @@ namespace scene protected: + void setUsedBufferRenderNodes(irr::u32 num); void copyMaterials(); core::array Materials; @@ -92,8 +94,9 @@ namespace scene IMesh* Mesh; IShadowVolumeSceneNode* Shadow; - s32 PassCount; bool ReadOnlyMaterials; + + core::array BufferRenderNodes; }; } // end namespace scene diff --git a/source/Irrlicht/Irrlicht-gcc.cbp b/source/Irrlicht/Irrlicht-gcc.cbp index 7f58b8ee..315240a9 100644 --- a/source/Irrlicht/Irrlicht-gcc.cbp +++ b/source/Irrlicht/Irrlicht-gcc.cbp @@ -8,8 +8,8 @@ @@ -397,200 +397,200 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -616,6 +616,7 @@ + @@ -1020,23 +1021,23 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -1045,281 +1046,277 @@ - + - + - + - - - + + + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - - + + - + - - - - - + + + + + - + - + - + - + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - - + + - - - + + + - + - - + + - - + + - - - + + + - - - - - - + + diff --git a/source/Irrlicht/Irrlicht10.0.vcxproj b/source/Irrlicht/Irrlicht10.0.vcxproj index db456922..a3d69826 100644 --- a/source/Irrlicht/Irrlicht10.0.vcxproj +++ b/source/Irrlicht/Irrlicht10.0.vcxproj @@ -1038,6 +1038,7 @@ + diff --git a/source/Irrlicht/Irrlicht10.0.vcxproj.filters b/source/Irrlicht/Irrlicht10.0.vcxproj.filters index 1ee79831..bb8e196b 100644 --- a/source/Irrlicht/Irrlicht10.0.vcxproj.filters +++ b/source/Irrlicht/Irrlicht10.0.vcxproj.filters @@ -1444,6 +1444,9 @@ include\scene + + Irrlicht\scene\sceneNodes + diff --git a/source/Irrlicht/Irrlicht17.0.vcxproj b/source/Irrlicht/Irrlicht17.0.vcxproj index 88c4ab76..465cdd31 100644 --- a/source/Irrlicht/Irrlicht17.0.vcxproj +++ b/source/Irrlicht/Irrlicht17.0.vcxproj @@ -776,6 +776,7 @@ + diff --git a/source/Irrlicht/Irrlicht17.0.vcxproj.filters b/source/Irrlicht/Irrlicht17.0.vcxproj.filters index 330b0260..7cb136ed 100644 --- a/source/Irrlicht/Irrlicht17.0.vcxproj.filters +++ b/source/Irrlicht/Irrlicht17.0.vcxproj.filters @@ -1438,6 +1438,9 @@ include + + Irrlicht\scene\sceneNodes + diff --git a/tests/tests-last-passed-at.txt b/tests/tests-last-passed-at.txt index 05ac7099..63ceebb8 100644 --- a/tests/tests-last-passed-at.txt +++ b/tests/tests-last-passed-at.txt @@ -1,4 +1,4 @@ Tests finished. 72 tests of 72 passed. Compiled as DEBUG -Test suite pass at GMT Tue May 02 18:39:02 2023 +Test suite pass at GMT Thu May 04 15:46:30 2023