]> git.lizzy.rs Git - irrlicht.git/commitdiff
Add back lighting system
authorLizzy Fleckenstein <eliasfleckenstein@web.de>
Thu, 13 Apr 2023 06:40:18 +0000 (08:40 +0200)
committerLizzy Fleckenstein <eliasfleckenstein@web.de>
Thu, 13 Apr 2023 16:01:09 +0000 (18:01 +0200)
Code is taken from latest irrlicht trunk; this is relevant because there have been fixes to stencil shadows since 1.8.5 (irrlicht SVN revision 5933).

24 files changed:
include/ESceneNodeTypes.h
include/IAnimatedMeshSceneNode.h
include/ILightSceneNode.h [new file with mode: 0644]
include/IMeshSceneNode.h
include/ISceneManager.h
include/IShadowVolumeSceneNode.h [new file with mode: 0644]
include/IVideoDriver.h
include/irrlicht.h
source/Irrlicht/CAnimatedMeshSceneNode.cpp
source/Irrlicht/CAnimatedMeshSceneNode.h
source/Irrlicht/CLightSceneNode.cpp [new file with mode: 0644]
source/Irrlicht/CLightSceneNode.h [new file with mode: 0644]
source/Irrlicht/CMakeLists.txt
source/Irrlicht/CMeshSceneNode.cpp
source/Irrlicht/CMeshSceneNode.h
source/Irrlicht/CNullDriver.cpp
source/Irrlicht/CNullDriver.h
source/Irrlicht/COpenGLDriver.cpp
source/Irrlicht/COpenGLDriver.h
source/Irrlicht/CSceneManager.cpp
source/Irrlicht/CSceneManager.h
source/Irrlicht/CShadowVolumeSceneNode.cpp [new file with mode: 0644]
source/Irrlicht/CShadowVolumeSceneNode.h [new file with mode: 0644]
source/Irrlicht/SLight.h [new file with mode: 0644]

index 2573ca088cdc6a4a432658c3c18e1d6f503900ce..f9442f330984d598b146c5e6de254c9b4fb0adc1 100644 (file)
@@ -21,9 +21,15 @@ namespace scene
                //! of type CSceneManager (note that ISceneManager is not(!) an ISceneNode)\r
                ESNT_SCENE_MANAGER  = MAKE_IRR_ID('s','m','n','g'),\r
 \r
+               //! Shadow Volume Scene Node\r
+               ESNT_SHADOW_VOLUME  = MAKE_IRR_ID('s','h','d','w'),\r
+\r
                //! Mesh Scene Node\r
                ESNT_MESH           = MAKE_IRR_ID('m','e','s','h'),\r
 \r
+               //! Light Scene Node\r
+               ESNT_LIGHT          = MAKE_IRR_ID('l','g','h','t'),\r
+\r
                //! Empty Scene Node\r
                ESNT_EMPTY          = MAKE_IRR_ID('e','m','t','y'),\r
 \r
index ef16dd808da85f6042d59f81bebd88cc5b14bab0..f3d3f7b2aa08c8fec464931196a4e4a1cd922a87 100644 (file)
@@ -13,6 +13,8 @@ namespace irr
 {\r
 namespace scene\r
 {\r
+       class IShadowVolumeSceneNode;\r
+\r
        enum E_JOINT_UPDATE_ON_RENDER\r
        {\r
                //! do nothing\r
@@ -85,6 +87,31 @@ namespace scene
                /** \return Frames per second played. */\r
                virtual f32 getAnimationSpeed() const =0;\r
 \r
+               /** The shadow can be rendered using the ZPass or the zfail\r
+               method. ZPass is a little bit faster because the shadow volume\r
+               creation is easier, but with this method there occur ugly\r
+               looking artifacts when the camera is inside the shadow volume.\r
+               These error do not occur with the ZFail method, but it can\r
+               have trouble with clipping to the far-plane (it usually works\r
+               well in OpenGL and fails with other drivers).\r
+               \param shadowMesh: Optional custom mesh for shadow volume.\r
+               \param id: Id of the shadow scene node. This id can be used to\r
+               identify the node later.\r
+               \param zfailmethod: If set to true, the shadow will use the\r
+               zfail method, if not, zpass is used.\r
+               \param infinity: Value used by the shadow volume algorithm to\r
+               scale the shadow volume. For zfail shadow volumes on some drivers\r
+               only support finite shadows, so camera zfar must be larger than\r
+               shadow back cap,which is depending on the infinity parameter).\r
+               Infinity value also scales by the scaling factors of the model.\r
+               If shadows don't show up with zfail then try reducing infinity.\r
+               If shadows are cut-off then try increasing infinity.\r
+               \return Pointer to the created shadow scene node. This pointer\r
+               should not be dropped. See IReferenceCounted::drop() for more\r
+               information. */\r
+               virtual IShadowVolumeSceneNode* addShadowVolumeSceneNode(const IMesh* shadowMesh=0,\r
+                       s32 id=-1, bool zfailmethod=true, f32 infinity=1000.0f) = 0;\r
+\r
                //! Get a pointer to a joint in the mesh (if the mesh is a bone based mesh).\r
                /** With this method it is possible to attach scene nodes to\r
                joints for example possible to attach a weapon to the left hand\r
diff --git a/include/ILightSceneNode.h b/include/ILightSceneNode.h
new file mode 100644 (file)
index 0000000..ccf7cdb
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt\r
+// This file is part of the "Irrlicht Engine".\r
+// For conditions of distribution and use, see copyright notice in irrlicht.h\r
+\r
+#ifndef IRR_I_LIGHT_SCENE_NODE_H_INCLUDED\r
+#define IRR_I_LIGHT_SCENE_NODE_H_INCLUDED\r
+\r
+#include "ISceneNode.h"\r
+#include "SLight.h"\r
+\r
+namespace irr\r
+{\r
+namespace scene\r
+{\r
+\r
+//! Scene node which is a dynamic light.\r
+/** You can switch the light on and off by making it visible or not. It can be\r
+animated by ordinary scene node animators. If the light type is directional or\r
+spot, the direction of the light source is defined by the rotation of the scene\r
+node (assuming (0,0,1) as the local direction of the light).\r
+*/\r
+class ILightSceneNode : public ISceneNode\r
+{\r
+public:\r
+\r
+       //! constructor\r
+       ILightSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id,\r
+               const core::vector3df& position = core::vector3df(0,0,0))\r
+               : ISceneNode(parent, mgr, id, position) {}\r
+\r
+       //! Sets the light data associated with this ILightSceneNode\r
+       /** \param light The new light data. */\r
+       virtual void setLightData(const video::SLight& light) = 0;\r
+\r
+       //! Gets the light data associated with this ILightSceneNode\r
+       /** \return The light data. */\r
+       virtual const video::SLight& getLightData() const = 0;\r
+\r
+       //! Gets the light data associated with this ILightSceneNode\r
+       /** \return The light data. */\r
+       virtual video::SLight& getLightData() = 0;\r
+\r
+       //! Sets if the node should be visible or not.\r
+       /** All children of this node won't be visible either, when set\r
+       to true.\r
+       \param isVisible If the node shall be visible. */\r
+       virtual void setVisible(bool isVisible) = 0;\r
+\r
+       //! Sets the light's radius of influence.\r
+       /** Outside this radius the light won't lighten geometry and cast no\r
+       shadows. Setting the radius will also influence the attenuation, setting\r
+       it to (0,1/radius,0). If you want to override this behavior, set the\r
+       attenuation after the radius.\r
+       NOTE: On OpenGL only the attenuation is set, there's no hard range.\r
+       \param radius The new radius. */\r
+       virtual void setRadius(f32 radius) = 0;\r
+\r
+       //! Gets the light's radius of influence.\r
+       /** \return The current radius. */\r
+       virtual f32 getRadius() const = 0;\r
+\r
+       //! Sets the light type.\r
+       /** \param type The new type. */\r
+       virtual void setLightType(video::E_LIGHT_TYPE type) = 0;\r
+\r
+       //! Gets the light type.\r
+       /** \return The current light type. */\r
+       virtual video::E_LIGHT_TYPE getLightType() const = 0;\r
+\r
+       //! Sets whether this light casts shadows.\r
+       /** Enabling this flag won't automatically cast shadows, the meshes\r
+       will still need shadow scene nodes attached. But one can enable or\r
+       disable distinct lights for shadow casting for performance reasons.\r
+       \param shadow True if this light shall cast shadows. */\r
+       virtual void enableCastShadow(bool shadow=true) = 0;\r
+\r
+       //! Check whether this light casts shadows.\r
+       /** \return True if light would cast shadows, else false. */\r
+       virtual bool getCastShadow() const = 0;\r
+};\r
+\r
+} // end namespace scene\r
+} // end namespace irr\r
+\r
+\r
+#endif\r
index d4f6fff381481074663a5882e5f636be45a4c862..e4b3b05a29fd6e2555df008e351bda012dcc9b6c 100644 (file)
@@ -12,6 +12,7 @@ namespace irr
 namespace scene\r
 {\r
 \r
+class IShadowVolumeSceneNode;\r
 class IMesh;\r
 \r
 \r
@@ -37,6 +38,31 @@ public:
        /** \return Pointer to mesh which is displayed by this node. */\r
        virtual IMesh* getMesh(void) = 0;\r
 \r
+       /** The shadow can be rendered using the ZPass or the zfail\r
+       method. ZPass is a little bit faster because the shadow volume\r
+       creation is easier, but with this method there occur ugly\r
+       looking artifacts when the camera is inside the shadow volume.\r
+       These error do not occur with the ZFail method, but it can\r
+       have trouble with clipping to the far-plane (it usually works\r
+       well in OpenGL and fails with other drivers).\r
+       \param shadowMesh: Optional custom mesh for shadow volume.\r
+       \param id: Id of the shadow scene node. This id can be used to\r
+       identify the node later.\r
+       \param zfailmethod: If set to true, the shadow will use the\r
+       zfail method, if not, zpass is used.\r
+       \param infinity: Value used by the shadow volume algorithm to\r
+       scale the shadow volume. For zfail shadow volumes on some drivers\r
+       only suppport finite shadows, so camera zfar must be larger than\r
+       shadow back cap,which is depending on the infinity parameter).\r
+       Infinity value also scales by the scaling factors of the model.\r
+       If shadows don't show up with zfail then try reducing infinity.\r
+       If shadows are cut-off then try increasing infinity.\r
+       \return Pointer to the created shadow scene node. This pointer\r
+       should not be dropped. See IReferenceCounted::drop() for more\r
+       information. */\r
+       virtual IShadowVolumeSceneNode* addShadowVolumeSceneNode(const IMesh* shadowMesh=0,\r
+               s32 id=-1, bool zfailmethod=true, f32 infinity=1000.0f) = 0;\r
+\r
        //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.\r
        /** In this way it is possible to change the materials of a mesh\r
        causing all mesh scene nodes referencing this mesh to change, too.\r
index 9379a9ccee7b61e4c7607c60517cc37cc4653dd6..4cd31e2001a9897ad5690df9164580544385df1d 100644 (file)
@@ -102,6 +102,7 @@ namespace scene
        class IBillboardSceneNode;\r
        class ICameraSceneNode;\r
        class IDummyTransformationSceneNode;\r
+       class ILightSceneNode;\r
        class IMesh;\r
        class IMeshBuffer;\r
        class IMeshCache;\r
@@ -112,6 +113,7 @@ namespace scene
        class IMeshWriter;\r
        class ISceneNode;\r
        class ISceneNodeFactory;\r
+       class IShadowVolumeSceneNode;\r
 \r
        //! The Scene Manager manages scene nodes, mesh resources, cameras and all the other stuff.\r
        /** All Scene nodes can be created only here.\r
@@ -392,6 +394,24 @@ namespace scene
                        const core::vector3df& lookat = core::vector3df(0,0,100),\r
                        s32 id=-1, bool makeActive=true) = 0;\r
 \r
+               //! Adds a dynamic light scene node to the scene graph.\r
+               /** The light will cast dynamic light on all\r
+               other scene nodes in the scene, which have the material flag video::MTF_LIGHTING\r
+               turned on. (This is the default setting in most scene nodes).\r
+               \param parent: Parent scene node of the light. Can be null. If the parent moves,\r
+               the light will move too.\r
+               \param position: Position of the space relative to its parent where the light will be placed.\r
+               \param color: Diffuse color of the light. Ambient or Specular colors can be set manually with\r
+               the ILightSceneNode::getLightData() method.\r
+               \param radius: Radius of the light.\r
+               \param id: id of the node. This id can be used to identify the node.\r
+               \return Pointer to the interface of the light if successful, otherwise NULL.\r
+               This pointer should not be dropped. See IReferenceCounted::drop() for more information. */\r
+               virtual ILightSceneNode* addLightSceneNode(ISceneNode* parent = 0,\r
+                       const core::vector3df& position = core::vector3df(0,0,0),\r
+                       video::SColorf color = video::SColorf(1.0f, 1.0f, 1.0f),\r
+                       f32 radius=100.0f, s32 id=-1) = 0;\r
+\r
                //! Adds a billboard scene node to the scene graph.\r
                /** A billboard is like a 3d sprite: A 2d element,\r
                which always looks to the camera. It is usually used for things\r
@@ -493,6 +513,17 @@ namespace scene
                \param camera: The new camera which should be active. */\r
                virtual void setActiveCamera(ICameraSceneNode* camera) = 0;\r
 \r
+               //! Sets the color of stencil buffers shadows drawn by the scene manager.\r
+               virtual void setShadowColor(video::SColor color = video::SColor(150,0,0,0)) = 0;\r
+\r
+               //! Get the current color of shadows.\r
+               virtual video::SColor getShadowColor() const = 0;\r
+\r
+               //! Create a shadow volume scene node to be used with custom nodes\r
+               /** Use this if you implement your own SceneNodes and need shadow volumes in them.\r
+               Otherwise you should generally use addShadowVolumeSceneNode functions from IMeshSceneNode or IAnimatedMeshSceneNode.*/\r
+               virtual IShadowVolumeSceneNode* createShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent, s32 id, bool zfailmethod, f32 infinity) = 0;\r
+\r
                //! Registers a node for rendering it at a specific time.\r
                /** This method should only be used by SceneNodes when they get a\r
                ISceneNode::OnRegisterSceneNode() call.\r
diff --git a/include/IShadowVolumeSceneNode.h b/include/IShadowVolumeSceneNode.h
new file mode 100644 (file)
index 0000000..7f661cd
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt\r
+// This file is part of the "Irrlicht Engine".\r
+// For conditions of distribution and use, see copyright notice in irrlicht.h\r
+\r
+#ifndef IRR_I_SHADOW_VOLUME_SCENE_NODE_H_INCLUDED\r
+#define IRR_I_SHADOW_VOLUME_SCENE_NODE_H_INCLUDED\r
+\r
+#include "ISceneNode.h"\r
+\r
+namespace irr\r
+{\r
+namespace scene\r
+{\r
+       class IMesh;\r
+\r
+       enum ESHADOWVOLUME_OPTIMIZATION\r
+       {\r
+               //! Create volumes around every triangle\r
+               ESV_NONE,\r
+\r
+               //! Create volumes only around the silhouette of the mesh\r
+               /** This can reduce the number of volumes drastically,\r
+               but will have an upfront-cost where it calculates adjacency of\r
+               triangles. Also it will not work with all models. Basically\r
+               if you see strange black shadow lines then you have a model\r
+               for which it won't work.\r
+               We get that information about adjacency by comparing the positions of \r
+               all edges in the mesh (even if they are in different meshbuffers). */\r
+               ESV_SILHOUETTE_BY_POS\r
+       };\r
+\r
+       //! Scene node for rendering a shadow volume into a stencil buffer.\r
+       class IShadowVolumeSceneNode : public ISceneNode\r
+       {\r
+       public:\r
+\r
+               //! constructor\r
+               IShadowVolumeSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id)\r
+                       : ISceneNode(parent, mgr, id) {}\r
+\r
+               //! Sets the mesh from which the shadow volume should be generated.\r
+               /** To optimize shadow rendering, use a simpler mesh for shadows.\r
+               */\r
+               virtual void setShadowMesh(const IMesh* mesh) = 0;\r
+\r
+               //! Updates the shadow volumes for current light positions.\r
+               virtual void updateShadowVolumes() = 0;\r
+\r
+               //! Set optimization used to create shadow volumes\r
+               /** Default is ESV_SILHOUETTE_BY_POS. If the shadow \r
+               looks bad then give ESV_NONE a try (which will be slower). \r
+               Alternatively you can try to fix the model, it's often\r
+               because it's not closed (aka if you'd put water in it then \r
+               that would leak out). */\r
+               virtual void setOptimization(ESHADOWVOLUME_OPTIMIZATION optimization) = 0;\r
+\r
+               //! Get currently active optimization used to create shadow volumes\r
+               virtual ESHADOWVOLUME_OPTIMIZATION getOptimization() const = 0;\r
+       };\r
+\r
+} // end namespace scene\r
+} // end namespace irr\r
+\r
+#endif\r
index ef8e38e1d13e3bf5d00aaa99d399988da7e8c69d..b2d000484095974a2d13edd383491380f86a5d66 100644 (file)
@@ -41,6 +41,7 @@ namespace video
        struct S3DVertex;\r
        struct S3DVertex2TCoords;\r
        struct S3DVertexTangents;\r
+       struct SLight;\r
        class IImageLoader;\r
        class IImageWriter;\r
        class IMaterialRenderer;\r
@@ -844,6 +845,48 @@ namespace video
                                        const core::position2d<s32>& end,\r
                                        SColor color=SColor(255,255,255,255)) =0;\r
 \r
+               //! Draws a shadow volume into the stencil buffer.\r
+               /** To draw a stencil shadow, do this: First, draw all geometry.\r
+               Then use this method, to draw the shadow volume. Then, use\r
+               IVideoDriver::drawStencilShadow() to visualize the shadow.\r
+               Please note that the code for the opengl version of the method\r
+               is based on free code sent in by Philipp Dortmann, lots of\r
+               thanks go to him!\r
+               \param triangles Array of 3d vectors, specifying the shadow\r
+               volume.\r
+               \param zfail If set to true, zfail method is used, otherwise\r
+               zpass.\r
+               \param debugDataVisible The debug data that is enabled for this\r
+               shadow node\r
+               */\r
+               virtual void drawStencilShadowVolume(const core::array<core::vector3df>& triangles, bool zfail=true, u32 debugDataVisible=0) =0;\r
+\r
+               //! Fills the stencil shadow with color.\r
+               /** After the shadow volume has been drawn into the stencil\r
+               buffer using IVideoDriver::drawStencilShadowVolume(), use this\r
+               to draw the color of the shadow.\r
+               Please note that the code for the opengl version of the method\r
+               is based on free code sent in by Philipp Dortmann, lots of\r
+               thanks go to him!\r
+               \param clearStencilBuffer Set this to false, if you want to\r
+               draw every shadow with the same color, and only want to call\r
+               drawStencilShadow() once after all shadow volumes have been\r
+               drawn. Set this to true, if you want to paint every shadow with\r
+               its own color.\r
+               \param leftUpEdge Color of the shadow in the upper left corner\r
+               of screen.\r
+               \param rightUpEdge Color of the shadow in the upper right\r
+               corner of screen.\r
+               \param leftDownEdge Color of the shadow in the lower left\r
+               corner of screen.\r
+               \param rightDownEdge Color of the shadow in the lower right\r
+               corner of screen. */\r
+               virtual void drawStencilShadow(bool clearStencilBuffer=false,\r
+                       video::SColor leftUpEdge = video::SColor(255,0,0,0),\r
+                       video::SColor rightUpEdge = video::SColor(255,0,0,0),\r
+                       video::SColor leftDownEdge = video::SColor(255,0,0,0),\r
+                       video::SColor rightDownEdge = video::SColor(255,0,0,0)) =0;\r
+\r
                //! Draws a mesh buffer\r
                /** \param mb Buffer to draw */\r
                virtual void drawMeshBuffer(const scene::IMeshBuffer* mb) =0;\r
@@ -912,6 +955,33 @@ namespace video
                \return Amount of primitives drawn in the last frame. */\r
                virtual u32 getPrimitiveCountDrawn( u32 mode =0 ) const =0;\r
 \r
+               //! Deletes all dynamic lights which were previously added with addDynamicLight().\r
+               virtual void deleteAllDynamicLights() =0;\r
+\r
+               //! adds a dynamic light, returning an index to the light\r
+               //! \param light: the light data to use to create the light\r
+               //! \return An index to the light, or -1 if an error occurs\r
+               virtual s32 addDynamicLight(const SLight& light) =0;\r
+\r
+               //! Returns the maximal amount of dynamic lights the device can handle\r
+               /** \return Maximal amount of dynamic lights. */\r
+               virtual u32 getMaximalDynamicLightAmount() const =0;\r
+\r
+               //! Returns amount of dynamic lights currently set\r
+               /** \return Amount of dynamic lights currently set */\r
+               virtual u32 getDynamicLightCount() const =0;\r
+\r
+               //! Returns light data which was previously set by IVideoDriver::addDynamicLight().\r
+               /** \param idx Zero based index of the light. Must be 0 or\r
+               greater and smaller than IVideoDriver::getDynamicLightCount.\r
+               \return Light data. */\r
+               virtual const SLight& getDynamicLight(u32 idx) const =0;\r
+\r
+               //! Turns a dynamic light on or off\r
+               //! \param lightIndex: the index returned by addDynamicLight\r
+               //! \param turnOn: true to turn the light on, false to turn it off\r
+               virtual void turnLightOn(s32 lightIndex, bool turnOn) =0;\r
+\r
                //! Gets name of this video driver.\r
                /** \return Returns the name of the video driver, e.g. in case\r
                of the Direct3D8 driver, it would return "Direct3D 8.1". */\r
index 359f810f4aa140956e5d509406f89d6324468f25..37b961d94268100192e4df590711d7f2a53aa8d5 100644 (file)
 #include "ISceneManager.h"\r
 #include "ISceneNode.h"\r
 #include "IShaderConstantSetCallBack.h"\r
+#include "IShadowVolumeSceneNode.h"\r
 #include "ISkinnedMesh.h"\r
 #include "ITexture.h"\r
 #include "ITimer.h"\r
index cea8ef296d661e21302a22e02f95f7f4d0f01947..9108ba6fd3f24be9a02443b7df8c609ce21a27df 100644 (file)
@@ -7,6 +7,11 @@
 #include "ISceneManager.h"\r
 #include "S3DVertex.h"\r
 #include "os.h"\r
+#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+#include "CShadowVolumeSceneNode.h"\r
+#else\r
+#include "IShadowVolumeSceneNode.h"\r
+#endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
 #ifdef _IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_\r
 #include "CSkinnedMesh.h"\r
 #endif\r
@@ -38,7 +43,7 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh* mesh,
        TransitionTime(0), Transiting(0.f), TransitingBlend(0.f),\r
        JointMode(EJUOR_NONE), JointsUsed(false),\r
        Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false),\r
-       LoopCallBack(0), PassCount(0)\r
+       LoopCallBack(0), PassCount(0), Shadow(0)\r
 {\r
        #ifdef _DEBUG\r
        setDebugName("CAnimatedMeshSceneNode");\r
@@ -51,6 +56,9 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh* mesh,
 //! destructor\r
 CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode()\r
 {\r
+       if (Shadow)\r
+               Shadow->drop();\r
+\r
        if (LoopCallBack)\r
                LoopCallBack->drop();\r
 }\r
@@ -274,6 +282,9 @@ void CAnimatedMeshSceneNode::render()
 \r
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
 \r
+       if (Shadow && PassCount==1)\r
+               Shadow->updateShadowVolumes();\r
+\r
        // for debug purposes only:\r
 \r
        bool renderMeshes = true;\r
@@ -493,6 +504,28 @@ u32 CAnimatedMeshSceneNode::getMaterialCount() const
 }\r
 \r
 \r
+//! Creates shadow volume scene node as child of this node\r
+//! and returns a pointer to it.\r
+IShadowVolumeSceneNode* CAnimatedMeshSceneNode::addShadowVolumeSceneNode(\r
+               const IMesh* shadowMesh, s32 id, bool zfailmethod, f32 infinity)\r
+{\r
+#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+       if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))\r
+               return 0;\r
+\r
+       if (!shadowMesh)\r
+               shadowMesh = Mesh; // if null is given, use the mesh of node\r
+\r
+       if (Shadow)\r
+               Shadow->drop();\r
+\r
+       Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id,  zfailmethod, infinity);\r
+       return Shadow;\r
+#else\r
+       return 0;\r
+#endif\r
+}\r
+\r
 //! Returns a pointer to a child node, which has the same transformation as\r
 //! the corresponding joint, if the mesh in this scene node is a skinned mesh.\r
 IBoneSceneNode* CAnimatedMeshSceneNode::getJointNode(const c8* jointName)\r
@@ -581,6 +614,12 @@ u32 CAnimatedMeshSceneNode::getJointCount() const
 //! or to remove attached childs.\r
 bool CAnimatedMeshSceneNode::removeChild(ISceneNode* child)\r
 {\r
+       if (child && Shadow == child)\r
+       {\r
+               Shadow->drop();\r
+               Shadow = 0;\r
+       }\r
+\r
        if (ISceneNode::removeChild(child))\r
        {\r
                if (JointsUsed) //stop weird bugs caused while changing parents as the joints are being created\r
@@ -902,6 +941,9 @@ ISceneNode* CAnimatedMeshSceneNode::clone(ISceneNode* newParent, ISceneManager*
        if (newNode->LoopCallBack)\r
                newNode->LoopCallBack->grab();\r
        newNode->PassCount = PassCount;\r
+       newNode->Shadow = Shadow;\r
+       if (newNode->Shadow)\r
+               newNode->Shadow->grab();\r
        newNode->JointChildSceneNodes = JointChildSceneNodes;\r
        newNode->PretransitingSave = PretransitingSave;\r
        newNode->RenderFromIdentity = RenderFromIdentity;\r
index a11a19091c4dff1e638b01a373177e1d55760803..8d5e8a6d166f952980c033a70288d06aedf3c5c5 100644 (file)
@@ -78,6 +78,11 @@ namespace scene
                //! returns amount of materials used by this scene node.\r
                u32 getMaterialCount() const override;\r
 \r
+               //! Creates shadow volume scene node as child of this node\r
+               //! and returns a pointer to it.\r
+               virtual IShadowVolumeSceneNode* addShadowVolumeSceneNode(const IMesh* shadowMesh,\r
+                       s32 id, bool zfailmethod=true, f32 infinity=1000.0f) override;\r
+\r
                //! Returns a pointer to a child node, which has the same transformation as\r
                //! the corresponding joint, if the mesh in this scene node is a skinned mesh.\r
                IBoneSceneNode* getJointNode(const c8* jointName) override;\r
@@ -174,6 +179,8 @@ namespace scene
                IAnimationEndCallBack* LoopCallBack;\r
                s32 PassCount;\r
 \r
+               IShadowVolumeSceneNode* Shadow;\r
+\r
                core::array<IBoneSceneNode* > JointChildSceneNodes;\r
                core::array<core::matrix4> PretransitingSave;\r
        };\r
diff --git a/source/Irrlicht/CLightSceneNode.cpp b/source/Irrlicht/CLightSceneNode.cpp
new file mode 100644 (file)
index 0000000..ac94508
--- /dev/null
@@ -0,0 +1,233 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt\r
+// This file is part of the "Irrlicht Engine".\r
+// For conditions of distribution and use, see copyright notice in irrlicht.h\r
+\r
+#include "CLightSceneNode.h"\r
+#include "IVideoDriver.h"\r
+#include "ISceneManager.h"\r
+#include "ICameraSceneNode.h"\r
+\r
+#include "os.h"\r
+\r
+namespace irr\r
+{\r
+namespace scene\r
+{\r
+\r
+//! constructor\r
+CLightSceneNode::CLightSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id,\r
+               const core::vector3df& position, video::SColorf color, f32 radius)\r
+: ILightSceneNode(parent, mgr, id, position), DriverLightIndex(-1), LightIsOn(true)\r
+{\r
+       #ifdef _DEBUG\r
+       setDebugName("CLightSceneNode");\r
+       #endif\r
+\r
+       LightData.DiffuseColor = color;\r
+       // set some useful specular color\r
+       LightData.SpecularColor = color.getInterpolated(video::SColor(255,255,255,255),0.7f);\r
+\r
+       setRadius(radius);\r
+}\r
+\r
+\r
+//! pre render event\r
+void CLightSceneNode::OnRegisterSceneNode()\r
+{\r
+       doLightRecalc();        // TODO: since doLightRecalc has now been added to updateAbsolutePosition it might be possible to remove this one.\r
+\r
+       if (IsVisible)\r
+               SceneManager->registerNodeForRendering(this, ESNRP_LIGHT);\r
+\r
+       ISceneNode::OnRegisterSceneNode();\r
+}\r
+\r
+\r
+//! render\r
+void CLightSceneNode::render()\r
+{\r
+       video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
+       if (!driver)\r
+               return;\r
+\r
+       if ( DebugDataVisible & scene::EDS_BBOX )\r
+       {\r
+               driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
+               video::SMaterial m;\r
+               m.Lighting = false;\r
+               driver->setMaterial(m);\r
+\r
+               switch ( LightData.Type )\r
+               {\r
+                       case video::ELT_POINT:\r
+                       case video::ELT_SPOT:\r
+                               driver->draw3DBox(BBox, LightData.DiffuseColor.toSColor());\r
+                               break;\r
+\r
+                       case video::ELT_DIRECTIONAL:\r
+                               driver->draw3DLine(core::vector3df(0.f, 0.f, 0.f),\r
+                                               LightData.Direction * LightData.Radius,\r
+                                               LightData.DiffuseColor.toSColor());\r
+                               break;\r
+                       default:\r
+                               break;\r
+               }\r
+       }\r
+\r
+       DriverLightIndex = driver->addDynamicLight(LightData);\r
+       setVisible(LightIsOn);\r
+}\r
+\r
+\r
+//! sets the light data\r
+void CLightSceneNode::setLightData(const video::SLight& light)\r
+{\r
+       LightData = light;\r
+}\r
+\r
+\r
+//! \return Returns the light data.\r
+const video::SLight& CLightSceneNode::getLightData() const\r
+{\r
+       return LightData;\r
+}\r
+\r
+\r
+//! \return Returns the light data.\r
+video::SLight& CLightSceneNode::getLightData()\r
+{\r
+       return LightData;\r
+}\r
+\r
+void CLightSceneNode::setVisible(bool isVisible)\r
+{\r
+       ISceneNode::setVisible(isVisible);\r
+\r
+       if(DriverLightIndex < 0)\r
+               return;\r
+       video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
+       if (!driver)\r
+               return;\r
+\r
+       LightIsOn = isVisible;\r
+       driver->turnLightOn((u32)DriverLightIndex, LightIsOn);\r
+}\r
+\r
+//! returns the axis aligned bounding box of this node\r
+const core::aabbox3d<f32>& CLightSceneNode::getBoundingBox() const\r
+{\r
+       return BBox;\r
+}\r
+\r
+\r
+//! Sets the light's radius of influence.\r
+/** Outside this radius the light won't lighten geometry and cast no\r
+shadows. Setting the radius will also influence the attenuation, setting\r
+it to (0,1/radius,0). If you want to override this behavior, set the\r
+attenuation after the radius.\r
+\param radius The new radius. */\r
+void CLightSceneNode::setRadius(f32 radius)\r
+{\r
+       LightData.Radius=radius;\r
+       LightData.Attenuation.set(0.f, 1.f/radius, 0.f);\r
+       doLightRecalc();\r
+}\r
+\r
+\r
+//! Gets the light's radius of influence.\r
+/** \return The current radius. */\r
+f32 CLightSceneNode::getRadius() const\r
+{\r
+       return LightData.Radius;\r
+}\r
+\r
+\r
+//! Sets the light type.\r
+/** \param type The new type. */\r
+void CLightSceneNode::setLightType(video::E_LIGHT_TYPE type)\r
+{\r
+       LightData.Type=type;\r
+}\r
+\r
+\r
+//! Gets the light type.\r
+/** \return The current light type. */\r
+video::E_LIGHT_TYPE CLightSceneNode::getLightType() const\r
+{\r
+       return LightData.Type;\r
+}\r
+\r
+\r
+//! Sets whether this light casts shadows.\r
+/** Enabling this flag won't automatically cast shadows, the meshes\r
+will still need shadow scene nodes attached. But one can enable or\r
+disable distinct lights for shadow casting for performance reasons.\r
+\param shadow True if this light shall cast shadows. */\r
+void CLightSceneNode::enableCastShadow(bool shadow)\r
+{\r
+       LightData.CastShadows=shadow;\r
+}\r
+\r
+\r
+//! Check whether this light casts shadows.\r
+/** \return True if light would cast shadows, else false. */\r
+bool CLightSceneNode::getCastShadow() const\r
+{\r
+       return LightData.CastShadows;\r
+}\r
+\r
+\r
+void CLightSceneNode::doLightRecalc()\r
+{\r
+       if ((LightData.Type == video::ELT_SPOT) || (LightData.Type == video::ELT_DIRECTIONAL))\r
+       {\r
+               LightData.Direction = core::vector3df(.0f,.0f,1.0f);\r
+               getAbsoluteTransformation().rotateVect(LightData.Direction);\r
+               LightData.Direction.normalize();\r
+       }\r
+       if ((LightData.Type == video::ELT_SPOT) || (LightData.Type == video::ELT_POINT))\r
+       {\r
+               const f32 r = LightData.Radius * LightData.Radius * 0.5f;\r
+               BBox.MaxEdge.set( r, r, r );\r
+               BBox.MinEdge.set( -r, -r, -r );\r
+               //setAutomaticCulling( scene::EAC_BOX );\r
+               setAutomaticCulling( scene::EAC_OFF );\r
+               LightData.Position = getAbsolutePosition();\r
+       }\r
+       if (LightData.Type == video::ELT_DIRECTIONAL)\r
+       {\r
+               BBox.reset( 0, 0, 0 );\r
+               setAutomaticCulling( scene::EAC_OFF );\r
+       }\r
+}\r
+\r
+void CLightSceneNode::updateAbsolutePosition()\r
+{\r
+       ILightSceneNode::updateAbsolutePosition();\r
+       doLightRecalc();\r
+}\r
+\r
+\r
+//! Creates a clone of this scene node and its children.\r
+ISceneNode* CLightSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)\r
+{\r
+       if (!newParent)\r
+               newParent = Parent;\r
+       if (!newManager)\r
+               newManager = SceneManager;\r
+\r
+       CLightSceneNode* nb = new CLightSceneNode(newParent,\r
+               newManager, ID, RelativeTranslation, LightData.DiffuseColor, LightData.Radius);\r
+\r
+       nb->cloneMembers(this, newManager);\r
+       nb->LightData = LightData;\r
+       nb->BBox = BBox;\r
+\r
+       if ( newParent )\r
+               nb->drop();\r
+       return nb;\r
+}\r
+\r
+} // end namespace scene\r
+} // end namespace irr\r
+\r
diff --git a/source/Irrlicht/CLightSceneNode.h b/source/Irrlicht/CLightSceneNode.h
new file mode 100644 (file)
index 0000000..8417a5b
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt\r
+// This file is part of the "Irrlicht Engine".\r
+// For conditions of distribution and use, see copyright notice in irrlicht.h\r
+\r
+#ifndef IRR_C_LIGHT_SCENE_NODE_H_INCLUDED\r
+#define IRR_C_LIGHT_SCENE_NODE_H_INCLUDED\r
+\r
+#include "ILightSceneNode.h"\r
+\r
+namespace irr\r
+{\r
+namespace scene\r
+{\r
+\r
+//! Scene node which is a dynamic light. You can switch the light on and off by\r
+//! making it visible or not, and let it be animated by ordinary scene node animators.\r
+class CLightSceneNode : public ILightSceneNode\r
+{\r
+public:\r
+\r
+       //! constructor\r
+       CLightSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id,\r
+               const core::vector3df& position, video::SColorf color, f32 range);\r
+\r
+       //! pre render event\r
+       virtual void OnRegisterSceneNode() override;\r
+\r
+       //! render\r
+       virtual void render() override;\r
+\r
+       //! set node light data from light info\r
+       virtual void setLightData(const video::SLight& light) override;\r
+\r
+       //! \return Returns the light data.\r
+       virtual const video::SLight& getLightData() const override;\r
+\r
+       //! \return Returns the light data.\r
+       virtual video::SLight& getLightData() override;\r
+\r
+       //! Sets if the node should be visible or not.\r
+       /** All children of this node won't be visible either, when set\r
+       to true.\r
+       \param isVisible If the node shall be visible. */\r
+       virtual void setVisible(bool isVisible) override;\r
+\r
+       //! returns the axis aligned bounding box of this node\r
+       virtual const core::aabbox3d<f32>& getBoundingBox() const override;\r
+\r
+       //! Returns type of the scene node\r
+       virtual ESCENE_NODE_TYPE getType() const override { return ESNT_LIGHT; }\r
+\r
+       //! Creates a clone of this scene node and its children.\r
+       virtual ISceneNode* clone(ISceneNode* newParent=0, ISceneManager* newManager=0) override;\r
+\r
+       //! Sets the light's radius of influence.\r
+       /** Outside this radius the light won't lighten geometry and cast no\r
+       shadows. Setting the radius will also influence the attenuation, setting\r
+       it to (0,1/radius,0). If you want to override this behavior, set the\r
+       attenuation after the radius.\r
+       \param radius The new radius. */\r
+       virtual void setRadius(f32 radius) override;\r
+\r
+       //! Gets the light's radius of influence.\r
+       /** \return The current radius. */\r
+       virtual f32 getRadius() const override;\r
+\r
+       //! Sets the light type.\r
+       /** \param type The new type. */\r
+       virtual void setLightType(video::E_LIGHT_TYPE type) override;\r
+\r
+       //! Gets the light type.\r
+       /** \return The current light type. */\r
+       virtual video::E_LIGHT_TYPE getLightType() const override;\r
+\r
+       //! Sets whether this light casts shadows.\r
+       /** Enabling this flag won't automatically cast shadows, the meshes\r
+       will still need shadow scene nodes attached. But one can enable or\r
+       disable distinct lights for shadow casting for performance reasons.\r
+       \param shadow True if this light shall cast shadows. */\r
+       virtual void enableCastShadow(bool shadow=true) override;\r
+\r
+       //! Check whether this light casts shadows.\r
+       /** \return True if light would cast shadows, else false. */\r
+       virtual bool getCastShadow() const override;\r
+\r
+       //! Updates the absolute position based on the relative and the parents position\r
+       virtual void updateAbsolutePosition() override;\r
+\r
+private:\r
+\r
+       video::SLight LightData;\r
+       core::aabbox3d<f32> BBox;\r
+       s32 DriverLightIndex;\r
+       bool LightIsOn;\r
+       void doLightRecalc();\r
+};\r
+\r
+\r
+} // end namespace scene\r
+} // end namespace irr\r
+\r
+#endif\r
index 0bb71e3b075599dbd4f553e7bb1fd7a20d8f783a..23b7c593f007235e64093ef4d7ca9a455cfe6ea0 100644 (file)
@@ -70,6 +70,7 @@ endif()
 add_definitions(
        -DIRR_ENABLE_BUILTIN_FONT
        -D_IRR_COMPILE_WITH_SKINNED_MESH_SUPPORT_
+       -D_IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
 )
 
 # Platform-specific configuration
@@ -349,6 +350,8 @@ add_library(IRROBJ OBJECT
        CSceneCollisionManager.cpp
        CSceneManager.cpp
        CMeshCache.cpp
+       CLightSceneNode.cpp
+       CShadowVolumeSceneNode.cpp
 )
 
 set(IRRDRVROBJ
index 1331ddae7f1873ed70c361754fe2271ecca7c318..0bcd850c9bba22ec8672b20663972b015bf20b63 100644 (file)
 #include "IAnimatedMesh.h"\r
 #include "IMaterialRenderer.h"\r
 #include "IFileSystem.h"\r
+#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+#include "CShadowVolumeSceneNode.h"\r
+#else\r
+#include "IShadowVolumeSceneNode.h"\r
+#endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
 \r
 namespace irr\r
 {\r
@@ -23,7 +28,7 @@ namespace scene
 CMeshSceneNode::CMeshSceneNode(IMesh* mesh, ISceneNode* parent, ISceneManager* mgr, s32 id,\r
                        const core::vector3df& position, const core::vector3df& rotation,\r
                        const core::vector3df& scale)\r
-: IMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0),\r
+: IMeshSceneNode(parent, mgr, id, position, rotation, scale), Mesh(0), Shadow(0),\r
        PassCount(0), ReadOnlyMaterials(false)\r
 {\r
        #ifdef _DEBUG\r
@@ -37,6 +42,8 @@ CMeshSceneNode::CMeshSceneNode(IMesh* mesh, ISceneNode* parent, ISceneManager* m
 //! destructor\r
 CMeshSceneNode::~CMeshSceneNode()\r
 {\r
+       if (Shadow)\r
+               Shadow->drop();\r
        if (Mesh)\r
                Mesh->drop();\r
 }\r
@@ -102,6 +109,9 @@ void CMeshSceneNode::render()
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);\r
        Box = Mesh->getBoundingBox();\r
 \r
+       if (Shadow && PassCount==1)\r
+               Shadow->updateShadowVolumes();\r
+\r
        // for debug purposes only:\r
 \r
        bool renderMeshes = true;\r
@@ -200,6 +210,12 @@ void CMeshSceneNode::render()
 //! or to remove attached childs.\r
 bool CMeshSceneNode::removeChild(ISceneNode* child)\r
 {\r
+       if (child && Shadow == child)\r
+       {\r
+               Shadow->drop();\r
+               Shadow = 0;\r
+       }\r
+\r
        return ISceneNode::removeChild(child);\r
 }\r
 \r
@@ -256,6 +272,29 @@ void CMeshSceneNode::setMesh(IMesh* mesh)
 }\r
 \r
 \r
+//! Creates shadow volume scene node as child of this node\r
+//! and returns a pointer to it.\r
+IShadowVolumeSceneNode* CMeshSceneNode::addShadowVolumeSceneNode(\r
+               const IMesh* shadowMesh, s32 id, bool zfailmethod, f32 infinity)\r
+{\r
+#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+       if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))\r
+               return 0;\r
+\r
+       if (!shadowMesh)\r
+               shadowMesh = Mesh; // if null is given, use the mesh of node\r
+\r
+       if (Shadow)\r
+               Shadow->drop();\r
+\r
+       Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id,  zfailmethod, infinity);\r
+       return Shadow;\r
+#else\r
+       return 0;\r
+#endif\r
+}\r
+\r
+\r
 void CMeshSceneNode::copyMaterials()\r
 {\r
        Materials.clear();\r
@@ -306,6 +345,9 @@ ISceneNode* CMeshSceneNode::clone(ISceneNode* newParent, ISceneManager* newManag
        nb->cloneMembers(this, newManager);\r
        nb->ReadOnlyMaterials = ReadOnlyMaterials;\r
        nb->Materials = Materials;\r
+       nb->Shadow = Shadow;\r
+       if ( nb->Shadow )\r
+               nb->Shadow->grab();\r
 \r
        if (newParent)\r
                nb->drop();\r
index 521744521be5087bf9168d49edb33c1f92b77ff3..209000fe958cf820ee840f38bec427c2823794c0 100644 (file)
@@ -54,6 +54,11 @@ namespace scene
                //! Returns the current mesh\r
                IMesh* getMesh(void) override { return Mesh; }\r
 \r
+               //! Creates shadow volume scene node as child of this node\r
+               //! and returns a pointer to it.\r
+               virtual IShadowVolumeSceneNode* addShadowVolumeSceneNode(const IMesh* shadowMesh,\r
+                       s32 id, bool zfailmethod=true, f32 infinity=10000.0f) override;\r
+\r
                //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.\r
                /* In this way it is possible to change the materials a mesh causing all mesh scene nodes\r
                referencing this mesh to change too. */\r
@@ -79,6 +84,7 @@ namespace scene
                video::SMaterial ReadOnlyMaterial;\r
 \r
                IMesh* Mesh;\r
+               IShadowVolumeSceneNode* Shadow;\r
 \r
                s32 PassCount;\r
                bool ReadOnlyMaterials;\r
index 6ef7c17a7e5d67a84b6531552679d1edceac1fac..e829c56c8c0dd0cbaa78ffb587fa3b477ed87710 100644 (file)
@@ -55,6 +55,7 @@ CNullDriver::CNullDriver(io::IFileSystem* io, const core::dimension2d<u32>& scre
        DriverAttributes = new io::CAttributes();\r
        DriverAttributes->addInt("MaxTextures", MATERIAL_MAX_TEXTURES);\r
        DriverAttributes->addInt("MaxSupportedTextures", MATERIAL_MAX_TEXTURES);\r
+       DriverAttributes->addInt("MaxLights", getMaximalDynamicLightAmount());\r
        DriverAttributes->addInt("MaxAnisotropy", 1);\r
 //     DriverAttributes->addInt("MaxUserClipPlanes", 0);\r
 //     DriverAttributes->addInt("MaxAuxBuffers", 0);\r
@@ -865,6 +866,80 @@ const wchar_t* CNullDriver::getName() const
 }\r
 \r
 \r
+\r
+//! Draws a shadow volume into the stencil buffer. To draw a stencil shadow, do\r
+//! this: First, draw all geometry. Then use this method, to draw the shadow\r
+//! volume. Then, use IVideoDriver::drawStencilShadow() to visualize the shadow.\r
+void CNullDriver::drawStencilShadowVolume(const core::array<core::vector3df>& triangles, bool zfail, u32 debugDataVisible)\r
+{\r
+}\r
+\r
+\r
+//! Fills the stencil shadow with color. After the shadow volume has been drawn\r
+//! into the stencil buffer using IVideoDriver::drawStencilShadowVolume(), use this\r
+//! to draw the color of the shadow.\r
+void CNullDriver::drawStencilShadow(bool clearStencilBuffer,\r
+               video::SColor leftUpEdge, video::SColor rightUpEdge,\r
+               video::SColor leftDownEdge, video::SColor rightDownEdge)\r
+{\r
+}\r
+\r
+\r
+//! deletes all dynamic lights there are\r
+void CNullDriver::deleteAllDynamicLights()\r
+{\r
+       Lights.set_used(0);\r
+}\r
+\r
+\r
+//! adds a dynamic light\r
+s32 CNullDriver::addDynamicLight(const SLight& light)\r
+{\r
+       Lights.push_back(light);\r
+       return Lights.size() - 1;\r
+}\r
+\r
+//! Turns a dynamic light on or off\r
+//! \param lightIndex: the index returned by addDynamicLight\r
+//! \param turnOn: true to turn the light on, false to turn it off\r
+void CNullDriver::turnLightOn(s32 lightIndex, bool turnOn)\r
+{\r
+       // Do nothing\r
+}\r
+\r
+\r
+//! returns the maximal amount of dynamic lights the device can handle\r
+u32 CNullDriver::getMaximalDynamicLightAmount() const\r
+{\r
+       return 0;\r
+}\r
+\r
+\r
+//! Returns current amount of dynamic lights set\r
+//! \return Current amount of dynamic lights set\r
+u32 CNullDriver::getDynamicLightCount() const\r
+{\r
+       return Lights.size();\r
+}\r
+\r
+\r
+//! Returns light data which was previously set by IVideoDriver::addDynamicLight().\r
+//! \param idx: Zero based index of the light. Must be greater than 0 and smaller\r
+//! than IVideoDriver()::getDynamicLightCount.\r
+//! \return Light data.\r
+const SLight& CNullDriver::getDynamicLight(u32 idx) const\r
+{\r
+       if ( idx < Lights.size() )\r
+               return Lights[idx];\r
+       else\r
+       {\r
+               _IRR_DEBUG_BREAK_IF(true)\r
+               static const SLight dummy;\r
+               return dummy;\r
+       }\r
+}\r
+\r
+\r
 //! Creates a boolean alpha channel of the texture based of an color key.\r
 void CNullDriver::makeColorKeyTexture(video::ITexture* texture,\r
                                                                        video::SColor color,\r
index d3d933c6d1be0588da51daeb91eb9f79ff4c7214..da5b4a6f18946c10928a2bda6b8547ae55074703 100644 (file)
@@ -17,6 +17,7 @@
 #include "CFPSCounter.h"\r
 #include "S3DVertex.h"\r
 #include "SVertexIndex.h"\r
+#include "SLight.h"\r
 #include "SExposedVideoData.h"\r
 #include <list>\r
 \r
@@ -246,6 +247,22 @@ namespace video
                //! very useful method for statistics.\r
                u32 getPrimitiveCountDrawn( u32 param = 0 ) const override;\r
 \r
+               //! deletes all dynamic lights there are\r
+               virtual void deleteAllDynamicLights() override;\r
+\r
+               //! adds a dynamic light, returning an index to the light\r
+               //! \param light: the light data to use to create the light\r
+               //! \return An index to the light, or -1 if an error occurs\r
+               virtual s32 addDynamicLight(const SLight& light) override;\r
+\r
+               //! Turns a dynamic light on or off\r
+               //! \param lightIndex: the index returned by addDynamicLight\r
+               //! \param turnOn: true to turn the light on, false to turn it off\r
+               virtual void turnLightOn(s32 lightIndex, bool turnOn) override;\r
+\r
+               //! returns the maximal amount of dynamic lights the device can handle\r
+               virtual u32 getMaximalDynamicLightAmount() const override;\r
+\r
                //! \return Returns the name of the video driver. Example: In case of the DIRECT3D8\r
                //! driver, it would return "Direct3D8.1".\r
                const wchar_t* getName() const override;\r
@@ -267,18 +284,27 @@ namespace video
                //! Draws a shadow volume into the stencil buffer. To draw a stencil shadow, do\r
                //! this: First, draw all geometry. Then use this method, to draw the shadow\r
                //! volume. Then, use IVideoDriver::drawStencilShadow() to visualize the shadow.\r
-               [[deprecated]] virtual void drawStencilShadowVolume(const core::array<core::vector3df>& triangles,\r
-                       bool zfail=true, u32 debugDataVisible=0) {}\r
+               virtual void drawStencilShadowVolume(const core::array<core::vector3df>& triangles,\r
+                       bool zfail=true, u32 debugDataVisible=0) override;\r
 \r
                //! Fills the stencil shadow with color. After the shadow volume has been drawn\r
                //! into the stencil buffer using IVideoDriver::drawStencilShadowVolume(), use this\r
                //! to draw the color of the shadow.\r
-               [[deprecated]] virtual void drawStencilShadow(bool clearStencilBuffer=false,\r
+               virtual void drawStencilShadow(bool clearStencilBuffer=false,\r
                        video::SColor leftUpEdge = video::SColor(0,0,0,0),\r
                        video::SColor rightUpEdge = video::SColor(0,0,0,0),\r
                        video::SColor leftDownEdge = video::SColor(0,0,0,0),\r
-                       video::SColor rightDownEdge = video::SColor(0,0,0,0)) {}\r
+                       video::SColor rightDownEdge = video::SColor(0,0,0,0)) override;\r
+\r
+               //! Returns current amount of dynamic lights set\r
+               //! \return Current amount of dynamic lights set\r
+               virtual u32 getDynamicLightCount() const override;\r
 \r
+               //! Returns light data which was previously set with IVideDriver::addDynamicLight().\r
+               //! \param idx: Zero based index of the light. Must be greater than 0 and smaller\r
+               //! than IVideoDriver()::getDynamicLightCount.\r
+               //! \return Light data.\r
+               virtual const SLight& getDynamicLight(u32 idx) const override;\r
 \r
                //! Removes a texture from the texture cache and deletes it, freeing lot of\r
                //! memory.\r
@@ -793,6 +819,7 @@ namespace video
 \r
                core::array<video::IImageLoader*> SurfaceLoader;\r
                core::array<video::IImageWriter*> SurfaceWriter;\r
+               core::array<SLight> Lights;\r
                core::array<SMaterialRenderer> MaterialRenderers;\r
 \r
                std::list<SHWBufferLink*> HWBufferList;\r
index c213283bbf1651c72ff42f3363eb236ce88949d9..6ae36e3f2f1b7dc941c597e1a33ea86e0750e3c4 100644 (file)
@@ -59,6 +59,8 @@ bool COpenGLDriver::initDriver()
 //! destructor\r
 COpenGLDriver::~COpenGLDriver()\r
 {\r
+       RequestedLights.clear();\r
+\r
        deleteMaterialRenders();\r
 \r
        CacheHandler->getTextureCache().clear();\r
@@ -2985,6 +2987,177 @@ const wchar_t* COpenGLDriver::getName() const
 }\r
 \r
 \r
+//! deletes all dynamic lights there are\r
+void COpenGLDriver::deleteAllDynamicLights()\r
+{\r
+       for (s32 i=0; i<MaxLights; ++i)\r
+               glDisable(GL_LIGHT0 + i);\r
+\r
+       RequestedLights.clear();\r
+\r
+       CNullDriver::deleteAllDynamicLights();\r
+}\r
+\r
+\r
+//! adds a dynamic light\r
+s32 COpenGLDriver::addDynamicLight(const SLight& light)\r
+{\r
+       CNullDriver::addDynamicLight(light);\r
+\r
+       RequestedLights.push_back(RequestedLight(light));\r
+\r
+       u32 newLightIndex = RequestedLights.size() - 1;\r
+\r
+       // Try and assign a hardware light just now, but don't worry if I can't\r
+       assignHardwareLight(newLightIndex);\r
+\r
+       return (s32)newLightIndex;\r
+}\r
+\r
+\r
+void COpenGLDriver::assignHardwareLight(u32 lightIndex)\r
+{\r
+       setTransform(ETS_WORLD, core::matrix4());\r
+\r
+       s32 lidx;\r
+       for (lidx=GL_LIGHT0; lidx < GL_LIGHT0 + MaxLights; ++lidx)\r
+       {\r
+               if(!glIsEnabled(lidx))\r
+               {\r
+                       RequestedLights[lightIndex].HardwareLightIndex = lidx;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if(lidx == GL_LIGHT0 + MaxLights) // There's no room for it just now\r
+               return;\r
+\r
+       GLfloat data[4];\r
+       const SLight & light = RequestedLights[lightIndex].LightData;\r
+\r
+       switch (light.Type)\r
+       {\r
+       case video::ELT_SPOT:\r
+               data[0] = light.Direction.X;\r
+               data[1] = light.Direction.Y;\r
+               data[2] = light.Direction.Z;\r
+               data[3] = 0.0f;\r
+               glLightfv(lidx, GL_SPOT_DIRECTION, data);\r
+\r
+               // set position\r
+               data[0] = light.Position.X;\r
+               data[1] = light.Position.Y;\r
+               data[2] = light.Position.Z;\r
+               data[3] = 1.0f; // 1.0f for positional light\r
+               glLightfv(lidx, GL_POSITION, data);\r
+\r
+               glLightf(lidx, GL_SPOT_EXPONENT, light.Falloff);\r
+               glLightf(lidx, GL_SPOT_CUTOFF, light.OuterCone);\r
+       break;\r
+       case video::ELT_POINT:\r
+               // set position\r
+               data[0] = light.Position.X;\r
+               data[1] = light.Position.Y;\r
+               data[2] = light.Position.Z;\r
+               data[3] = 1.0f; // 1.0f for positional light\r
+               glLightfv(lidx, GL_POSITION, data);\r
+\r
+               glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);\r
+               glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);\r
+       break;\r
+       case video::ELT_DIRECTIONAL:\r
+               // set direction\r
+               data[0] = -light.Direction.X;\r
+               data[1] = -light.Direction.Y;\r
+               data[2] = -light.Direction.Z;\r
+               data[3] = 0.0f; // 0.0f for directional light\r
+               glLightfv(lidx, GL_POSITION, data);\r
+\r
+               glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);\r
+               glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);\r
+       break;\r
+       default:\r
+       break;\r
+       }\r
+\r
+       // set diffuse color\r
+       data[0] = light.DiffuseColor.r;\r
+       data[1] = light.DiffuseColor.g;\r
+       data[2] = light.DiffuseColor.b;\r
+       data[3] = light.DiffuseColor.a;\r
+       glLightfv(lidx, GL_DIFFUSE, data);\r
+\r
+       // set specular color\r
+       data[0] = light.SpecularColor.r;\r
+       data[1] = light.SpecularColor.g;\r
+       data[2] = light.SpecularColor.b;\r
+       data[3] = light.SpecularColor.a;\r
+       glLightfv(lidx, GL_SPECULAR, data);\r
+\r
+       // set ambient color\r
+       data[0] = light.AmbientColor.r;\r
+       data[1] = light.AmbientColor.g;\r
+       data[2] = light.AmbientColor.b;\r
+       data[3] = light.AmbientColor.a;\r
+       glLightfv(lidx, GL_AMBIENT, data);\r
+\r
+       // 1.0f / (constant + linear * d + quadratic*(d*d);\r
+\r
+       // set attenuation\r
+       glLightf(lidx, GL_CONSTANT_ATTENUATION, light.Attenuation.X);\r
+       glLightf(lidx, GL_LINEAR_ATTENUATION, light.Attenuation.Y);\r
+       glLightf(lidx, GL_QUADRATIC_ATTENUATION, light.Attenuation.Z);\r
+\r
+       glEnable(lidx);\r
+}\r
+\r
+\r
+//! Turns a dynamic light on or off\r
+//! \param lightIndex: the index returned by addDynamicLight\r
+//! \param turnOn: true to turn the light on, false to turn it off\r
+void COpenGLDriver::turnLightOn(s32 lightIndex, bool turnOn)\r
+{\r
+       if(lightIndex < 0 || lightIndex >= (s32)RequestedLights.size())\r
+               return;\r
+\r
+       RequestedLight & requestedLight = RequestedLights[lightIndex];\r
+\r
+       requestedLight.DesireToBeOn = turnOn;\r
+\r
+       if(turnOn)\r
+       {\r
+               if(-1 == requestedLight.HardwareLightIndex)\r
+                       assignHardwareLight(lightIndex);\r
+       }\r
+       else\r
+       {\r
+               if(-1 != requestedLight.HardwareLightIndex)\r
+               {\r
+                       // It's currently assigned, so free up the hardware light\r
+                       glDisable(requestedLight.HardwareLightIndex);\r
+                       requestedLight.HardwareLightIndex = -1;\r
+\r
+                       // Now let the first light that's waiting on a free hardware light grab it\r
+                       for(u32 requested = 0; requested < RequestedLights.size(); ++requested)\r
+                               if(RequestedLights[requested].DesireToBeOn\r
+                                       &&\r
+                                       -1 == RequestedLights[requested].HardwareLightIndex)\r
+                               {\r
+                                       assignHardwareLight(requested);\r
+                                       break;\r
+                               }\r
+               }\r
+       }\r
+}\r
+\r
+\r
+//! returns the maximal amount of dynamic lights the device can handle\r
+u32 COpenGLDriver::getMaximalDynamicLightAmount() const\r
+{\r
+       return MaxLights;\r
+}\r
+\r
+\r
 //! Sets the dynamic ambient light color. The default color is\r
 //! (0,0,0,0) which means it is dark.\r
 //! \param color: New color of the ambient light.\r
index c60d2ba88ae491598ca7d1be8ac57e3e2cba9c25..7121a4df65e02cce9fa69648718381a60a9866a9 100644 (file)
@@ -209,6 +209,22 @@ namespace video
                //! driver, it would return "Direct3D8.1".\r
                const wchar_t* getName() const override;\r
 \r
+               //! deletes all dynamic lights there are\r
+               virtual void deleteAllDynamicLights() override;\r
+\r
+               //! adds a dynamic light, returning an index to the light\r
+               //! \param light: the light data to use to create the light\r
+               //! \return An index to the light, or -1 if an error occurs\r
+               virtual s32 addDynamicLight(const SLight& light) override;\r
+\r
+               //! Turns a dynamic light on or off\r
+               //! \param lightIndex: the index returned by addDynamicLight\r
+               //! \param turnOn: true to turn the light on, false to turn it off\r
+               virtual void turnLightOn(s32 lightIndex, bool turnOn) override;\r
+\r
+               //! returns the maximal amount of dynamic lights the device can handle\r
+               virtual u32 getMaximalDynamicLightAmount() const override;\r
+\r
                //! Sets the dynamic ambient light color. The default color is\r
                //! (0,0,0,0) which means it is dark.\r
                //! \param color: New color of the ambient light.\r
@@ -480,6 +496,19 @@ namespace video
 \r
                SIrrlichtCreationParameters Params;\r
 \r
+               //! All the lights that have been requested; a hardware limited\r
+               //! number of them will be used at once.\r
+               struct RequestedLight\r
+               {\r
+                       RequestedLight(SLight const & lightData)\r
+                               : LightData(lightData), HardwareLightIndex(-1), DesireToBeOn(true) { }\r
+\r
+                       SLight  LightData;\r
+                       s32     HardwareLightIndex; // GL_LIGHT0 - GL_LIGHT7\r
+                       bool    DesireToBeOn;\r
+               };\r
+               core::array<RequestedLight> RequestedLights;\r
+\r
                //! Built-in 2D quad for 2D rendering.\r
                S3DVertex Quad2DVertices[4];\r
                static const u16 Quad2DIndices[4];\r
index 8a16dd21a9248b51ccc3ab46d20028c0be362767..6882a5b36790a273c6ec0c0746882bc8cdd340ca 100644 (file)
 #include "CBillboardSceneNode.h"\r
 #include "CAnimatedMeshSceneNode.h"\r
 #include "CCameraSceneNode.h"\r
+#include "CLightSceneNode.h"\r
 #include "CMeshSceneNode.h"\r
+\r
+#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+#include "CShadowVolumeSceneNode.h"\r
+#else\r
+#include "IShadowVolumeSceneNode.h"\r
+#endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+\r
 #include "CDummyTransformationSceneNode.h"\r
 #include "CEmptySceneNode.h"\r
 \r
@@ -234,6 +242,22 @@ ICameraSceneNode* CSceneManager::addCameraSceneNode(ISceneNode* parent,
 }\r
 \r
 \r
+//! Adds a dynamic light scene node. The light will cast dynamic light on all\r
+//! other scene nodes in the scene, which have the material flag video::MTF_LIGHTING\r
+//! turned on. (This is the default setting in most scene nodes).\r
+ILightSceneNode* CSceneManager::addLightSceneNode(ISceneNode* parent,\r
+       const core::vector3df& position, video::SColorf color, f32 range, s32 id)\r
+{\r
+       if (!parent)\r
+               parent = this;\r
+\r
+       ILightSceneNode* node = new CLightSceneNode(parent, this, id, position, color, range);\r
+       node->drop();\r
+\r
+       return node;\r
+}\r
+\r
+\r
 //! Adds a billboard scene node to the scene. A billboard is like a 3d sprite: A 2d element,\r
 //! which always looks to the camera. It is usually used for things like explosions, fire,\r
 //! lensflares and things like that.\r
@@ -432,6 +456,15 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
                        }\r
                }\r
                break;\r
+       case ESNRP_LIGHT:\r
+               // TODO: Point Light culling..\r
+               // Lighting model in irrlicht has to be redone..\r
+               //if (!isCulled(node))\r
+               {\r
+                       LightList.push_back(node);\r
+                       taken = 1;\r
+               }\r
+               break;\r
        case ESNRP_SKY_BOX:\r
                SkyBoxList.push_back(node);\r
                taken = 1;\r
@@ -483,6 +516,13 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
                        }\r
                }\r
                break;\r
+       case ESNRP_SHADOW:\r
+               if (!isCulled(node))\r
+               {\r
+                       ShadowNodeList.push_back(node);\r
+                       taken = 1;\r
+               }\r
+               break;\r
        case ESNRP_GUI:\r
                if (!isCulled(node))\r
                {\r
@@ -490,9 +530,6 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
                        taken = 1;\r
                }\r
 \r
-       // as of yet unused\r
-       case ESNRP_LIGHT:\r
-       case ESNRP_SHADOW:\r
        case ESNRP_NONE: // ignore this one\r
                break;\r
        }\r
@@ -503,10 +540,12 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
 void CSceneManager::clearAllRegisteredNodesForRendering()\r
 {\r
        CameraList.clear();\r
+       LightList.clear();\r
        SkyBoxList.clear();\r
        SolidNodeList.clear();\r
        TransparentNodeList.clear();\r
        TransparentEffectNodeList.clear();\r
+       ShadowNodeList.clear();\r
        GuiNodeList.clear();\r
 }\r
 \r
@@ -557,6 +596,36 @@ void CSceneManager::drawAll()
                CameraList.set_used(0);\r
        }\r
 \r
+       //render lights scenes\r
+       {\r
+               CurrentRenderPass = ESNRP_LIGHT;\r
+               Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);\r
+\r
+               core::vector3df camWorldPos(0, 0, 0);\r
+               if (ActiveCamera)\r
+                       camWorldPos = ActiveCamera->getAbsolutePosition();\r
+\r
+               core::array<DistanceNodeEntry> SortedLights;\r
+               SortedLights.set_used(LightList.size());\r
+               for (s32 light = (s32)LightList.size() - 1; light >= 0; --light)\r
+                       SortedLights[light].setNodeAndDistanceFromPosition(LightList[light], camWorldPos);\r
+\r
+               SortedLights.set_sorted(false);\r
+               SortedLights.sort();\r
+\r
+               for(s32 light = (s32)LightList.size() - 1; light >= 0; --light)\r
+                       LightList[light] = SortedLights[light].Node;\r
+\r
+               Driver->deleteAllDynamicLights();\r
+\r
+               Driver->setAmbientLight(AmbientLight);\r
+\r
+               u32 maxLights = LightList.size();\r
+\r
+               for (i=0; i< maxLights; ++i)\r
+                       LightList[i]->render();\r
+       }\r
+\r
        // render skyboxes\r
        {\r
                CurrentRenderPass = ESNRP_SKY_BOX;\r
@@ -581,6 +650,21 @@ void CSceneManager::drawAll()
                SolidNodeList.set_used(0);\r
        }\r
 \r
+       // render shadows\r
+       {\r
+               CurrentRenderPass = ESNRP_SHADOW;\r
+               Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0);\r
+\r
+               for (i=0; i<ShadowNodeList.size(); ++i)\r
+                       ShadowNodeList[i]->render();\r
+\r
+               if (!ShadowNodeList.empty())\r
+                       Driver->drawStencilShadow(true,ShadowColor, ShadowColor,\r
+                               ShadowColor, ShadowColor);\r
+\r
+               ShadowNodeList.set_used(0);\r
+       }\r
+\r
        // render transparent objects.\r
        {\r
                CurrentRenderPass = ESNRP_TRANSPARENT;\r
@@ -616,12 +700,38 @@ void CSceneManager::drawAll()
 \r
                GuiNodeList.set_used(0);\r
        }\r
+\r
+       LightList.set_used(0);\r
        clearDeletionList();\r
 \r
        CurrentRenderPass = ESNRP_NONE;\r
 }\r
 \r
 \r
+//! Sets the color of stencil buffers shadows drawn by the scene manager.\r
+void CSceneManager::setShadowColor(video::SColor color)\r
+{\r
+       ShadowColor = color;\r
+}\r
+\r
+\r
+//! Returns the current color of shadows.\r
+video::SColor CSceneManager::getShadowColor() const\r
+{\r
+       return ShadowColor;\r
+}\r
+\r
+IShadowVolumeSceneNode* CSceneManager::createShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent, s32 id, bool zfailmethod, f32 infinity)\r
+{\r
+#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+       return new CShadowVolumeSceneNode(shadowMesh, parent, this, id, zfailmethod, infinity);\r
+#else\r
+       return 0;\r
+#endif\r
+}\r
+\r
+\r
+\r
 //! Adds an external mesh loader.\r
 void CSceneManager::addExternalMeshLoader(IMeshLoader* externalLoader)\r
 {\r
index 4b1beec8a4a94b9919560b03af0b9877540c323e..fded7f85f36995121efe91a1837c3612d9058451 100644 (file)
@@ -86,6 +86,14 @@ namespace scene
                        const core::vector3df& lookat = core::vector3df(0,0,100),\r
                        s32 id=-1, bool makeActive=true) override;\r
 \r
+               //! Adds a dynamic light scene node. The light will cast dynamic light on all\r
+               //! other scene nodes in the scene, which have the material flag video::MTF_LIGHTING\r
+               //! turned on. (This is the default setting in most scene nodes).\r
+               virtual ILightSceneNode* addLightSceneNode(ISceneNode* parent = 0,\r
+                       const core::vector3df& position = core::vector3df(0,0,0),\r
+                       video::SColorf color = video::SColorf(1.0f, 1.0f, 1.0f),\r
+                       f32 range=100.0f, s32 id=-1) override;\r
+\r
                //! Adds a billboard scene node to the scene. A billboard is like a 3d sprite: A 2d element,\r
                //! which always looks to the camera. It is usually used for things like explosions, fire,\r
                //! lensflares and things like that.\r
@@ -132,6 +140,15 @@ namespace scene
                //! Returns a pointer to the mesh manipulator.\r
                IMeshManipulator* getMeshManipulator() override;\r
 \r
+               //! Sets the color of stencil buffers shadows drawn by the scene manager.\r
+               virtual void setShadowColor(video::SColor color) override;\r
+\r
+               //! Returns the current color of shadows.\r
+               virtual video::SColor getShadowColor() const override;\r
+\r
+               //! Create a shadow volume scene node to be used with custom nodes\r
+               virtual IShadowVolumeSceneNode* createShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent, s32 id, bool zfailmethod, f32 infinity) override;\r
+\r
                //! Adds a scene node to the deletion queue.\r
                void addToDeletionQueue(ISceneNode* node) override;\r
 \r
@@ -245,6 +262,9 @@ namespace scene
                //! sort on distance (sphere) to camera\r
                struct DistanceNodeEntry\r
                {\r
+                       DistanceNodeEntry()\r
+                       { }\r
+\r
                        DistanceNodeEntry(ISceneNode* n, const core::vector3df& cameraPos)\r
                                : Node(n)\r
                        {\r
@@ -279,6 +299,8 @@ namespace scene
 \r
                //! render pass lists\r
                core::array<ISceneNode*> CameraList;\r
+               core::array<ISceneNode*> LightList;\r
+               core::array<ISceneNode*> ShadowNodeList;\r
                core::array<ISceneNode*> SkyBoxList;\r
                core::array<DefaultNodeEntry> SolidNodeList;\r
                core::array<TransparentNodeEntry> TransparentNodeList;\r
diff --git a/source/Irrlicht/CShadowVolumeSceneNode.cpp b/source/Irrlicht/CShadowVolumeSceneNode.cpp
new file mode 100644 (file)
index 0000000..da38c22
--- /dev/null
@@ -0,0 +1,531 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt\r
+// This file is part of the "Irrlicht Engine".\r
+// For conditions of distribution and use, see copyright notice in irrlicht.h\r
+\r
+#include "IrrCompileConfig.h"\r
+\r
+#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
+\r
+#include "CShadowVolumeSceneNode.h"\r
+#include "ISceneManager.h"\r
+#include "IMesh.h"\r
+#include "IVideoDriver.h"\r
+#include "ICameraSceneNode.h"\r
+#include "SViewFrustum.h"\r
+#include "SLight.h"\r
+#include "os.h"\r
+\r
+namespace irr\r
+{\r
+namespace scene\r
+{\r
+\r
+\r
+//! constructor\r
+CShadowVolumeSceneNode::CShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent,\r
+               ISceneManager* mgr, s32 id, bool zfailmethod, f32 infinity)\r
+: IShadowVolumeSceneNode(parent, mgr, id),\r
+       AdjacencyDirtyFlag(true),\r
+       ShadowMesh(0), IndexCount(0), VertexCount(0), ShadowVolumesUsed(0),\r
+       Infinity(infinity), UseZFailMethod(zfailmethod), Optimization(ESV_SILHOUETTE_BY_POS)\r
+{\r
+       #ifdef _DEBUG\r
+       setDebugName("CShadowVolumeSceneNode");\r
+       #endif\r
+       setShadowMesh(shadowMesh);\r
+       setAutomaticCulling(scene::EAC_OFF);\r
+}\r
+\r
+\r
+//! destructor\r
+CShadowVolumeSceneNode::~CShadowVolumeSceneNode()\r
+{\r
+       if (ShadowMesh)\r
+               ShadowMesh->drop();\r
+}\r
+\r
+\r
+void CShadowVolumeSceneNode::createShadowVolume(const core::vector3df& light, bool isDirectional)\r
+{\r
+       SShadowVolume* svp = 0;\r
+       core::aabbox3d<f32>* bb = 0;\r
+\r
+       // builds the shadow volume and adds it to the shadow volume list.\r
+\r
+       if (ShadowVolumes.size() > ShadowVolumesUsed)\r
+       {\r
+               // get the next unused buffer\r
+\r
+               svp = &ShadowVolumes[ShadowVolumesUsed];\r
+               svp->set_used(0);\r
+\r
+               bb = &ShadowBBox[ShadowVolumesUsed];\r
+       }\r
+       else\r
+       {\r
+               ShadowVolumes.push_back(SShadowVolume());\r
+               svp = &ShadowVolumes.getLast();\r
+\r
+               ShadowBBox.push_back(core::aabbox3d<f32>());\r
+               bb = &ShadowBBox.getLast();\r
+       }\r
+       svp->reallocate(IndexCount*5);\r
+       ++ShadowVolumesUsed;\r
+\r
+       // We use triangle lists\r
+       Edges.set_used(IndexCount*2);\r
+       u32 numEdges = 0;\r
+\r
+       numEdges=createEdgesAndCaps(light, isDirectional, svp, bb);\r
+\r
+       // for all edges add the near->far quads\r
+       core::vector3df lightDir1(light*Infinity);\r
+       core::vector3df lightDir2(light*Infinity);\r
+       for (u32 i=0; i<numEdges; ++i)\r
+       {\r
+               const core::vector3df &v1 = Vertices[Edges[2*i+0]];\r
+               const core::vector3df &v2 = Vertices[Edges[2*i+1]];\r
+               if ( !isDirectional )\r
+               {\r
+                       lightDir1 = (v1 - light).normalize()*Infinity;\r
+                       lightDir2 = (v2 - light).normalize()*Infinity;\r
+               }\r
+               const core::vector3df v3(v1+lightDir1);\r
+               const core::vector3df v4(v2+lightDir2);\r
+\r
+               // Add a quad (two triangles) to the vertex list\r
+#ifdef _DEBUG\r
+               if (svp->size() >= svp->allocated_size()-5)\r
+                       os::Printer::log("Allocation too small.", ELL_DEBUG);\r
+#endif\r
+               svp->push_back(v1);\r
+               svp->push_back(v2);\r
+               svp->push_back(v3);\r
+\r
+               svp->push_back(v2);\r
+               svp->push_back(v4);\r
+               svp->push_back(v3);\r
+       }\r
+}\r
+\r
+// TODO.\r
+// Not sure what's going on. Either FaceData should mean the opposite and true should mean facing away from light\r
+// or I'm missing something else. Anyway - when not setting this then Shadows will look wrong on Burnings driver\r
+// while they seem to look OK on first view either way on other drivers. Only tested with z-fail so far.\r
+// Maybe errors only show up close to near/far plane on other drivers as otherwise the stencil-buffer-count \r
+// is probably ending up with same value anyway \r
+#define IRR_USE_REVERSE_EXTRUDED\r
+\r
+u32 CShadowVolumeSceneNode::createEdgesAndCaps(const core::vector3df& light, bool isDirectional,\r
+                                       SShadowVolume* svp, core::aabbox3d<f32>* bb)\r
+{\r
+       u32 numEdges=0;\r
+       const u32 faceCount = IndexCount / 3;\r
+\r
+       if(faceCount >= 1)\r
+               bb->reset(Vertices[Indices[0]]);\r
+       else\r
+               bb->reset(0,0,0);\r
+\r
+       // Check every face if it is front or back facing the light.\r
+       core::vector3df lightDir0(light);\r
+       core::vector3df lightDir1(light);\r
+       core::vector3df lightDir2(light);\r
+       for (u32 i=0; i<faceCount; ++i)\r
+       {\r
+               const core::vector3df v0 = Vertices[Indices[3*i+0]];\r
+               const core::vector3df v1 = Vertices[Indices[3*i+1]];\r
+               const core::vector3df v2 = Vertices[Indices[3*i+2]];\r
+\r
+               if ( !isDirectional )\r
+               {\r
+                       lightDir0 = (v0-light).normalize();\r
+               }\r
+#ifdef IRR_USE_REVERSE_EXTRUDED\r
+               FaceData[i]=core::triangle3df(v2,v1,v0).isFrontFacing(lightDir0);       // actually the back-facing polygons\r
+#else\r
+               FaceData[i]=core::triangle3df(v0,v1,v2).isFrontFacing(lightDir0);\r
+#endif\r
+\r
+#if 0  // Useful for internal debugging & testing. Show all the faces in the light.\r
+               if ( FaceData[i] )\r
+               {\r
+                       video::SMaterial m;\r
+                       m.Lighting = false;\r
+                       SceneManager->getVideoDriver()->setMaterial(m);\r
+#ifdef IRR_USE_REVERSE_EXTRUDED\r
+                       SceneManager->getVideoDriver()->draw3DTriangle(core::triangle3df(v0+lightDir0,v1+lightDir0,v2+lightDir0), irr::video::SColor(255,255, 0, 0));\r
+#else\r
+                       SceneManager->getVideoDriver()->draw3DTriangle(core::triangle3df(v0-lightDir0,v1-lightDir0,v2-lightDir0), irr::video::SColor(255,255, 0, 0));\r
+#endif\r
+               }\r
+#endif\r
+\r
+               if (UseZFailMethod && FaceData[i])\r
+               {\r
+#ifdef _DEBUG\r
+                       if (svp->size() >= svp->allocated_size()-5)\r
+                               os::Printer::log("Allocation too small.", ELL_DEBUG);\r
+#endif\r
+                       // add front cap from light-facing faces\r
+                       svp->push_back(v2);\r
+                       svp->push_back(v1);\r
+                       svp->push_back(v0);\r
+\r
+                       // add back cap\r
+                       if ( !isDirectional )\r
+                       {\r
+                               lightDir1 = (v1-light).normalize();\r
+                               lightDir2 = (v2-light).normalize();\r
+                       }\r
+                       const core::vector3df i0 = v0+lightDir0*Infinity;\r
+                       const core::vector3df i1 = v1+lightDir1*Infinity;\r
+                       const core::vector3df i2 = v2+lightDir2*Infinity;\r
+\r
+                       svp->push_back(i0);\r
+                       svp->push_back(i1);\r
+                       svp->push_back(i2);\r
+\r
+                       bb->addInternalPoint(i0);\r
+                       bb->addInternalPoint(i1);\r
+                       bb->addInternalPoint(i2);\r
+               }\r
+       }\r
+\r
+       // Create edges\r
+       for (u32 i=0; i<faceCount; ++i)\r
+       {\r
+               // check all front facing faces\r
+               if (FaceData[i] == true)\r
+               {\r
+                       const u16 wFace0 = Indices[3*i+0];\r
+                       const u16 wFace1 = Indices[3*i+1];\r
+                       const u16 wFace2 = Indices[3*i+2];\r
+\r
+                       if ( Optimization == ESV_NONE )\r
+                       {\r
+                               // add edge v0-v1\r
+                               Edges[2*numEdges+0] = wFace0;\r
+                               Edges[2*numEdges+1] = wFace1;\r
+                               ++numEdges;\r
+\r
+                               // add edge v1-v2\r
+                               Edges[2*numEdges+0] = wFace1;\r
+                               Edges[2*numEdges+1] = wFace2;\r
+                               ++numEdges;\r
+\r
+                               // add edge v2-v0\r
+                               Edges[2*numEdges+0] = wFace2;\r
+                               Edges[2*numEdges+1] = wFace0;\r
+                               ++numEdges;\r
+                       }\r
+                       else\r
+                       {\r
+                               const u16 adj0 = Adjacency[3*i+0];\r
+                               const u16 adj1 = Adjacency[3*i+1];\r
+                               const u16 adj2 = Adjacency[3*i+2];\r
+\r
+                               // add edges if face is adjacent to back-facing face\r
+                               // or if no adjacent face was found\r
+                               if (adj0 == i || FaceData[adj0] == false)\r
+                               {\r
+                                       // add edge v0-v1\r
+                                       Edges[2*numEdges+0] = wFace0;\r
+                                       Edges[2*numEdges+1] = wFace1;\r
+                                       ++numEdges;\r
+                               }\r
+\r
+                               if (adj1 == i || FaceData[adj1] == false)\r
+                               {\r
+                                       // add edge v1-v2\r
+                                       Edges[2*numEdges+0] = wFace1;\r
+                                       Edges[2*numEdges+1] = wFace2;\r
+                                       ++numEdges;\r
+                               }\r
+\r
+                               if (adj2 == i || FaceData[adj2] == false)\r
+                               {\r
+                                       // add edge v2-v0\r
+                                       Edges[2*numEdges+0] = wFace2;\r
+                                       Edges[2*numEdges+1] = wFace0;\r
+                                       ++numEdges;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       return numEdges;\r
+}\r
+\r
+\r
+void CShadowVolumeSceneNode::setShadowMesh(const IMesh* mesh)\r
+{\r
+       if (ShadowMesh == mesh)\r
+               return;\r
+       if (ShadowMesh)\r
+               ShadowMesh->drop();\r
+       ShadowMesh = mesh;\r
+       if (ShadowMesh)\r
+       {\r
+               ShadowMesh->grab();\r
+               Box = ShadowMesh->getBoundingBox();\r
+       }\r
+}\r
+\r
+\r
+void CShadowVolumeSceneNode::updateShadowVolumes()\r
+{\r
+       const u32 oldIndexCount = IndexCount;\r
+       const u32 oldVertexCount = VertexCount;\r
+\r
+       VertexCount = 0;\r
+       IndexCount = 0;\r
+       ShadowVolumesUsed = 0;\r
+\r
+       const IMesh* const mesh = ShadowMesh;\r
+       if (!mesh)\r
+               return;\r
+\r
+       // create as much shadow volumes as there are lights but\r
+       // do not ignore the max light settings.\r
+       const u32 lightCount = SceneManager->getVideoDriver()->getDynamicLightCount();\r
+       if (!lightCount)\r
+               return;\r
+\r
+       // calculate total amount of vertices and indices\r
+\r
+       u32 i;\r
+       u32 totalVertices = 0;\r
+       u32 totalIndices = 0;\r
+       const u32 bufcnt = mesh->getMeshBufferCount();\r
+\r
+       for (i=0; i<bufcnt; ++i)\r
+       {\r
+               const IMeshBuffer* buf = mesh->getMeshBuffer(i);\r
+               if (    buf->getIndexType() == video::EIT_16BIT \r
+                       && buf->getPrimitiveType() == scene::EPT_TRIANGLES )\r
+               {\r
+                       totalIndices += buf->getIndexCount();\r
+                       totalVertices += buf->getVertexCount();\r
+               }\r
+               else\r
+               {\r
+                       os::Printer::log("ShadowVolumeSceneNode only supports meshbuffers with 16 bit indices and triangles", ELL_WARNING);\r
+                       return;\r
+               }\r
+       }\r
+       if ( totalIndices != (u32)(u16)totalIndices)\r
+       {\r
+               // We could switch to 32-bit indices, not much work and just bit of extra memory (< 192k) per shadow volume.\r
+               // If anyone ever complains and really needs that just switch it. But huge shadows are usually a bad idea as they will be slow.\r
+               os::Printer::log("ShadowVolumeSceneNode does not yet support shadowvolumes which need more than 16 bit indices", ELL_WARNING);\r
+               return;\r
+       }\r
+\r
+       // allocate memory if necessary\r
+\r
+       Vertices.set_used(totalVertices);\r
+       Indices.set_used(totalIndices);\r
+       FaceData.resize(totalIndices / 3);\r
+\r
+       // copy mesh \r
+       // (could speed this up for static meshes by adding some user flag to prevents copying)\r
+       for (i=0; i<bufcnt; ++i)\r
+       {\r
+               const IMeshBuffer* buf = mesh->getMeshBuffer(i);\r
+\r
+               const u16* idxp = buf->getIndices();\r
+               const u16* idxpend = idxp + buf->getIndexCount();\r
+               for (; idxp!=idxpend; ++idxp)\r
+                       Indices[IndexCount++] = *idxp + VertexCount;\r
+\r
+               const u32 vtxcnt = buf->getVertexCount();\r
+               for (u32 j=0; j<vtxcnt; ++j)\r
+                       Vertices[VertexCount++] = buf->getPosition(j);\r
+       }\r
+\r
+       // recalculate adjacency if necessary\r
+       if (oldVertexCount != VertexCount || oldIndexCount != IndexCount || AdjacencyDirtyFlag)\r
+               calculateAdjacency();\r
+\r
+       core::matrix4 matInv(Parent->getAbsoluteTransformation());\r
+       matInv.makeInverse();\r
+       core::matrix4 matTransp(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_TRANSPOSED);\r
+       const core::vector3df parentpos = Parent->getAbsolutePosition();\r
+\r
+       for (i=0; i<lightCount; ++i)\r
+       {\r
+               const video::SLight& dl = SceneManager->getVideoDriver()->getDynamicLight(i);\r
+\r
+               if ( dl.Type == video::ELT_DIRECTIONAL )\r
+               {\r
+                       core::vector3df ldir(dl.Direction);\r
+                       matTransp.transformVect(ldir);\r
+                       createShadowVolume(ldir, true);\r
+               }\r
+               else\r
+               {\r
+                       core::vector3df lpos(dl.Position);\r
+                       if (dl.CastShadows &&\r
+                               fabs((lpos - parentpos).getLengthSQ()) <= (dl.Radius*dl.Radius*4.0f))\r
+                       {\r
+                               matInv.transformVect(lpos);\r
+                               createShadowVolume(lpos, false);\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+void CShadowVolumeSceneNode::setOptimization(ESHADOWVOLUME_OPTIMIZATION optimization)\r
+{\r
+       if ( Optimization != optimization )\r
+       {\r
+               Optimization = optimization;\r
+               AdjacencyDirtyFlag = true;\r
+       }\r
+}\r
+\r
+//! pre render method\r
+void CShadowVolumeSceneNode::OnRegisterSceneNode()\r
+{\r
+       if (IsVisible)\r
+       {\r
+               SceneManager->registerNodeForRendering(this, scene::ESNRP_SHADOW);\r
+               ISceneNode::OnRegisterSceneNode();\r
+       }\r
+}\r
+\r
+//! renders the node.\r
+void CShadowVolumeSceneNode::render()\r
+{\r
+       video::IVideoDriver* driver = SceneManager->getVideoDriver();\r
+\r
+       if (!ShadowVolumesUsed || !driver)\r
+               return;\r
+\r
+       driver->setTransform(video::ETS_WORLD, Parent->getAbsoluteTransformation());\r
+\r
+       bool checkFarPlaneClipping = UseZFailMethod && !driver->queryFeature(video::EVDF_DEPTH_CLAMP);\r
+\r
+       // get camera frustum converted to local coordinates when we have to check for far plane clipping\r
+       SViewFrustum frust;\r
+       if ( checkFarPlaneClipping )\r
+       {\r
+               const irr::scene::ICameraSceneNode* camera = SceneManager->getActiveCamera();\r
+               if ( camera )\r
+               {\r
+                       frust = *camera->getViewFrustum();\r
+                       core::matrix4 invTrans(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE);\r
+                       frust.transform(invTrans);\r
+               }\r
+               else\r
+                       checkFarPlaneClipping = false;\r
+       }\r
+\r
+       for (u32 i=0; i<ShadowVolumesUsed; ++i)\r
+       {\r
+               bool drawShadow = true;\r
+\r
+               if (checkFarPlaneClipping)\r
+               {\r
+                       // Disable shadows drawing, when back cap is behind of ZFar plane.\r
+                       // TODO: Using infinite projection matrices instead is said to work better\r
+                       //       as then we wouldn't fail when the shadow clip the far plane.\r
+                       //       I couldn't get it working (and neither anyone before me it seems).\r
+                       //       Anyone who can figure it out is welcome to provide a patch.\r
+\r
+                       core::vector3df edges[8];\r
+                       ShadowBBox[i].getEdges(edges);\r
+\r
+                       for(int j = 0; j < 8; ++j)\r
+                       {\r
+                               if (frust.planes[scene::SViewFrustum::VF_FAR_PLANE].classifyPointRelation(edges[j]) == core::ISREL3D_FRONT)\r
+                               {\r
+                                       drawShadow = false;\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if(drawShadow)\r
+                       driver->drawStencilShadowVolume(ShadowVolumes[i], UseZFailMethod, DebugDataVisible);\r
+               else\r
+               {\r
+                       // TODO: For some reason (not yet further investigated), Direct3D needs a call to drawStencilShadowVolume\r
+                       //       even if we have nothing to draw here to set the renderstate into a StencilShadowMode.\r
+                       //       If that's not done it has effect on further render calls.\r
+                       core::array<core::vector3df> triangles;\r
+                       driver->drawStencilShadowVolume(triangles, UseZFailMethod, DebugDataVisible);\r
+               }\r
+       }\r
+}\r
+\r
+\r
+//! returns the axis aligned bounding box of this node\r
+const core::aabbox3d<f32>& CShadowVolumeSceneNode::getBoundingBox() const\r
+{\r
+       return Box;\r
+}\r
+\r
+\r
+//! Generates adjacency information based on mesh indices.\r
+void CShadowVolumeSceneNode::calculateAdjacency()\r
+{\r
+       AdjacencyDirtyFlag = false;\r
+\r
+       if ( Optimization == ESV_NONE )\r
+       {\r
+               Adjacency.clear();\r
+       }\r
+       else if ( Optimization == ESV_SILHOUETTE_BY_POS )\r
+       {\r
+               Adjacency.set_used(IndexCount);\r
+\r
+               // go through all faces and fetch their three neighbours\r
+               for (u32 f=0; f<IndexCount; f+=3)\r
+               {\r
+                       for (u32 edge = 0; edge<3; ++edge)\r
+                       {\r
+                               const core::vector3df& v1 = Vertices[Indices[f+edge]];\r
+                               const core::vector3df& v2 = Vertices[Indices[f+((edge+1)%3)]];\r
+\r
+                               // now we search an_O_ther _F_ace with these two\r
+                               // vertices, which is not the current face.\r
+                               u32 of;\r
+\r
+                               for (of=0; of<IndexCount; of+=3)\r
+                               {\r
+                                       // only other faces\r
+                                       if (of != f)\r
+                                       {\r
+                                               bool cnt1 = false;\r
+                                               bool cnt2 = false;\r
+\r
+                                               for (s32 e=0; e<3; ++e)\r
+                                               {\r
+                                                       if (v1.equals(Vertices[Indices[of+e]]))\r
+                                                               cnt1=true;\r
+\r
+                                                       if (v2.equals(Vertices[Indices[of+e]]))\r
+                                                               cnt2=true;\r
+                                               }\r
+                                               // one match for each vertex, i.e. edge is the same\r
+                                               if (cnt1 && cnt2)\r
+                                                       break;\r
+                                       }\r
+                               }\r
+\r
+                               // no adjacent edges -> store face number, else store adjacent face\r
+                               if (of >= IndexCount)\r
+                                       Adjacency[f + edge] = f/3;\r
+                               else\r
+                                       Adjacency[f + edge] = of/3;\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+\r
+} // end namespace scene\r
+} // end namespace irr\r
+\r
+#endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_\r
diff --git a/source/Irrlicht/CShadowVolumeSceneNode.h b/source/Irrlicht/CShadowVolumeSceneNode.h
new file mode 100644 (file)
index 0000000..89f5fa9
--- /dev/null
@@ -0,0 +1,99 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt\r
+// This file is part of the "Irrlicht Engine".\r
+// For conditions of distribution and use, see copyright notice in irrlicht.h\r
+\r
+#ifndef IRR_C_SHADOW_VOLUME_SCENE_NODE_H_INCLUDED\r
+#define IRR_C_SHADOW_VOLUME_SCENE_NODE_H_INCLUDED\r
+\r
+#include "IShadowVolumeSceneNode.h"\r
+\r
+namespace irr\r
+{\r
+namespace scene\r
+{\r
+\r
+       //! Scene node for rendering a shadow volume into a stencil buffer.\r
+       class CShadowVolumeSceneNode : public IShadowVolumeSceneNode\r
+       {\r
+       public:\r
+\r
+               //! constructor\r
+               CShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent, ISceneManager* mgr,\r
+                       s32 id, bool zfailmethod=true, f32 infinity=10000.0f);\r
+\r
+               //! destructor\r
+               virtual ~CShadowVolumeSceneNode();\r
+\r
+               //! Sets the mesh from which the shadow volume should be generated.\r
+               /** To optimize shadow rendering, use a simpler mesh for shadows.\r
+               */\r
+               virtual void setShadowMesh(const IMesh* mesh) override;\r
+\r
+               //! Updates the shadow volumes for current light positions.\r
+               /** Called each render cycle from Animated Mesh SceneNode render method. */\r
+               virtual void updateShadowVolumes() override;\r
+\r
+               //! Set optimization used to create shadow volumes\r
+               /** Default is ESV_SILHOUETTE_BY_POS. If the shadow \r
+               looks bad then give ESV_NONE a try (which will be slower). */\r
+               virtual void setOptimization(ESHADOWVOLUME_OPTIMIZATION optimization) override;\r
+\r
+               //! Get currently active optimization used to create shadow volumes\r
+               virtual ESHADOWVOLUME_OPTIMIZATION getOptimization() const override\r
+               {\r
+                       return Optimization;\r
+               }\r
+\r
+               //! pre render method\r
+               virtual void OnRegisterSceneNode() override;\r
+\r
+               //! renders the node.\r
+               virtual void render() override;\r
+\r
+               //! returns the axis aligned bounding box of this node\r
+               virtual const core::aabbox3d<f32>& getBoundingBox() const override;\r
+\r
+               //! Returns type of the scene node\r
+               virtual ESCENE_NODE_TYPE getType() const override { return ESNT_SHADOW_VOLUME; }\r
+\r
+       private:\r
+\r
+               typedef core::array<core::vector3df> SShadowVolume;\r
+\r
+               void createShadowVolume(const core::vector3df& pos, bool isDirectional);\r
+               u32 createEdgesAndCaps(const core::vector3df& light, bool isDirectional, SShadowVolume* svp, core::aabbox3d<f32>* bb);\r
+\r
+               //! Generates adjacency information based on mesh indices.\r
+               void calculateAdjacency();\r
+\r
+               core::aabbox3d<f32> Box;\r
+\r
+               // a shadow volume for every light\r
+               core::array<SShadowVolume> ShadowVolumes;\r
+\r
+               // a back cap bounding box for every light\r
+               core::array<core::aabbox3d<f32> > ShadowBBox;\r
+\r
+               core::array<core::vector3df> Vertices;\r
+               core::array<u16> Indices;\r
+               core::array<u16> Adjacency;\r
+               core::array<u16> Edges;\r
+               // tells if face is front facing\r
+               std::vector<bool> FaceData;\r
+               bool AdjacencyDirtyFlag;\r
+\r
+               const scene::IMesh* ShadowMesh;\r
+\r
+               u32 IndexCount;\r
+               u32 VertexCount;\r
+               u32 ShadowVolumesUsed;\r
+\r
+               f32 Infinity;\r
+               bool UseZFailMethod;\r
+               ESHADOWVOLUME_OPTIMIZATION Optimization;\r
+       };\r
+\r
+} // end namespace scene\r
+} // end namespace irr\r
+\r
+#endif\r
diff --git a/source/Irrlicht/SLight.h b/source/Irrlicht/SLight.h
new file mode 100644 (file)
index 0000000..f23b375
--- /dev/null
@@ -0,0 +1,101 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt\r
+// This file is part of the "Irrlicht Engine".\r
+// For conditions of distribution and use, see copyright notice in irrlicht.h\r
+\r
+#ifndef S_LIGHT_H_INCLUDED\r
+#define S_LIGHT_H_INCLUDED\r
+\r
+#include "SColor.h"\r
+#include "vector3d.h"\r
+\r
+namespace irr\r
+{\r
+namespace video\r
+{\r
+\r
+//! Enumeration for different types of lights\r
+enum E_LIGHT_TYPE\r
+{\r
+       //! point light, it has a position in space and radiates light in all directions\r
+       ELT_POINT,\r
+       //! spot light, it has a position in space, a direction, and a limited cone of influence\r
+       ELT_SPOT,\r
+       //! directional light, coming from a direction from an infinite distance\r
+       ELT_DIRECTIONAL,\r
+\r
+       //! Only used for counting the elements of this enum\r
+       ELT_COUNT\r
+};\r
+\r
+//! Names for light types\r
+const c8* const LightTypeNames[] =\r
+{\r
+       "Point",\r
+       "Spot",\r
+       "Directional",\r
+       0\r
+};\r
+\r
+//! structure for holding data describing a dynamic point light.\r
+/** Irrlicht supports point lights, spot lights, and directional lights.\r
+*/\r
+struct SLight\r
+{\r
+       SLight() : AmbientColor(0.f,0.f,0.f), DiffuseColor(1.f,1.f,1.f),\r
+               SpecularColor(1.f,1.f,1.f), Attenuation(1.f,0.f,0.f),\r
+               OuterCone(45.f), InnerCone(0.f), Falloff(2.f),\r
+               Position(0.f,0.f,0.f), Direction(0.f,0.f,1.f),\r
+               Radius(100.f), Type(ELT_POINT), CastShadows(true)\r
+               {}\r
+\r
+       //! Ambient color emitted by the light\r
+       SColorf AmbientColor;\r
+\r
+       //! Diffuse color emitted by the light.\r
+       /** This is the primary color you want to set. */\r
+       SColorf DiffuseColor;\r
+\r
+       //! Specular color emitted by the light.\r
+       /** For details how to use specular highlights, see SMaterial::Shininess */\r
+       SColorf SpecularColor;\r
+\r
+       //! Attenuation factors (constant, linear, quadratic)\r
+       /** Changes the light strength fading over distance.\r
+       Can also be altered by setting the radius, Attenuation will change to\r
+       (0,1.f/radius,0). Can be overridden after radius was set. */\r
+       core::vector3df Attenuation;\r
+\r
+       //! The angle of the spot's outer cone. Ignored for other lights.\r
+       f32 OuterCone;\r
+\r
+       //! The angle of the spot's inner cone. Ignored for other lights.\r
+       f32 InnerCone;\r
+\r
+       //! The light strength's decrease between Outer and Inner cone. Only for spot lights\r
+       f32 Falloff;\r
+\r
+       //! Read-ONLY! Position of the light.\r
+       /** If Type is ELT_DIRECTIONAL, it is ignored. Changed via light scene node's position. */\r
+       core::vector3df Position;\r
+\r
+       //! Read-ONLY! Direction of the light.\r
+       /** If Type is ELT_POINT, it is ignored. Changed via light scene node's rotation. */\r
+       core::vector3df Direction;\r
+\r
+       //! Read-ONLY! Radius of light. Everything within this radius will be lighted.\r
+       /** On OpenGL light doesn't stop at radius. To get same light as in OpenGL in other drivers\r
+       do set the radius to a large value like sqrt(FLT_MAX) and then set the Attenuation afterwards.\r
+       */\r
+       f32 Radius;\r
+\r
+       //! Read-ONLY! Type of the light. Default: ELT_POINT\r
+       E_LIGHT_TYPE Type;\r
+\r
+       //! Read-ONLY! Does the light cast shadows?\r
+       bool CastShadows:1;\r
+};\r
+\r
+} // end namespace video\r
+} // end namespace irr\r
+\r
+#endif\r