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