diff options
author | Lizzy Fleckenstein <eliasfleckenstein@web.de> | 2023-04-13 08:40:18 +0200 |
---|---|---|
committer | Lizzy Fleckenstein <eliasfleckenstein@web.de> | 2023-04-13 18:01:09 +0200 |
commit | bf90df100e120e272c14c7975a22ed01bf3ad215 (patch) | |
tree | 073f4cf268a7f9407e5ad4a313ae76a619ff1dcc | |
parent | 7a3fc62ada4001d5bb6c97ed26ec19a4e7c9d9ac (diff) | |
download | irrlicht-bf90df100e120e272c14c7975a22ed01bf3ad215.tar.xz |
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).
24 files changed, 1922 insertions, 9 deletions
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<s32>& 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<core::vector3df>& 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<IBoneSceneNode* > JointChildSceneNodes;
core::array<core::matrix4> 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<f32>& 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<f32>& 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<f32> 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<u32>& 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<core::vector3df>& 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 <list>
@@ -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<core::vector3df>& triangles,
- bool zfail=true, u32 debugDataVisible=0) {}
+ virtual void drawStencilShadowVolume(const core::array<core::vector3df>& 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<video::IImageLoader*> SurfaceLoader;
core::array<video::IImageWriter*> SurfaceWriter;
+ core::array<SLight> Lights;
core::array<SMaterialRenderer> MaterialRenderers;
std::list<SHWBufferLink*> 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<MaxLights; ++i)
+ glDisable(GL_LIGHT0 + i);
+
+ RequestedLights.clear();
+
+ CNullDriver::deleteAllDynamicLights();
+}
+
+
+//! adds a dynamic light
+s32 COpenGLDriver::addDynamicLight(const SLight& light)
+{
+ CNullDriver::addDynamicLight(light);
+
+ RequestedLights.push_back(RequestedLight(light));
+
+ u32 newLightIndex = RequestedLights.size() - 1;
+
+ // Try and assign a hardware light just now, but don't worry if I can't
+ assignHardwareLight(newLightIndex);
+
+ return (s32)newLightIndex;
+}
+
+
+void COpenGLDriver::assignHardwareLight(u32 lightIndex)
+{
+ setTransform(ETS_WORLD, core::matrix4());
+
+ s32 lidx;
+ for (lidx=GL_LIGHT0; lidx < GL_LIGHT0 + MaxLights; ++lidx)
+ {
+ if(!glIsEnabled(lidx))
+ {
+ RequestedLights[lightIndex].HardwareLightIndex = lidx;
+ break;
+ }
+ }
+
+ if(lidx == GL_LIGHT0 + MaxLights) // There's no room for it just now
+ return;
+
+ GLfloat data[4];
+ const SLight & light = RequestedLights[lightIndex].LightData;
+
+ switch (light.Type)
+ {
+ case video::ELT_SPOT:
+ data[0] = light.Direction.X;
+ data[1] = light.Direction.Y;
+ data[2] = light.Direction.Z;
+ data[3] = 0.0f;
+ glLightfv(lidx, GL_SPOT_DIRECTION, data);
+
+ // set position
+ data[0] = light.Position.X;
+ data[1] = light.Position.Y;
+ data[2] = light.Position.Z;
+ data[3] = 1.0f; // 1.0f for positional light
+ glLightfv(lidx, GL_POSITION, data);
+
+ glLightf(lidx, GL_SPOT_EXPONENT, light.Falloff);
+ glLightf(lidx, GL_SPOT_CUTOFF, light.OuterCone);
+ break;
+ case video::ELT_POINT:
+ // set position
+ data[0] = light.Position.X;
+ data[1] = light.Position.Y;
+ data[2] = light.Position.Z;
+ data[3] = 1.0f; // 1.0f for positional light
+ glLightfv(lidx, GL_POSITION, data);
+
+ glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);
+ glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);
+ break;
+ case video::ELT_DIRECTIONAL:
+ // set direction
+ data[0] = -light.Direction.X;
+ data[1] = -light.Direction.Y;
+ data[2] = -light.Direction.Z;
+ data[3] = 0.0f; // 0.0f for directional light
+ glLightfv(lidx, GL_POSITION, data);
+
+ glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);
+ glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);
+ break;
+ default:
+ break;
+ }
+
+ // set diffuse color
+ data[0] = light.DiffuseColor.r;
+ data[1] = light.DiffuseColor.g;
+ data[2] = light.DiffuseColor.b;
+ data[3] = light.DiffuseColor.a;
+ glLightfv(lidx, GL_DIFFUSE, data);
+
+ // set specular color
+ data[0] = light.SpecularColor.r;
+ data[1] = light.SpecularColor.g;
+ data[2] = light.SpecularColor.b;
+ data[3] = light.SpecularColor.a;
+ glLightfv(lidx, GL_SPECULAR, data);
+
+ // set ambient color
+ data[0] = light.AmbientColor.r;
+ data[1] = light.AmbientColor.g;
+ data[2] = light.AmbientColor.b;
+ data[3] = light.AmbientColor.a;
+ glLightfv(lidx, GL_AMBIENT, data);
+
+ // 1.0f / (constant + linear * d + quadratic*(d*d);
+
+ // set attenuation
+ glLightf(lidx, GL_CONSTANT_ATTENUATION, light.Attenuation.X);
+ glLightf(lidx, GL_LINEAR_ATTENUATION, light.Attenuation.Y);
+ glLightf(lidx, GL_QUADRATIC_ATTENUATION, light.Attenuation.Z);
+
+ glEnable(lidx);
+}
+
+
+//! 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 COpenGLDriver::turnLightOn(s32 lightIndex, bool turnOn)
+{
+ if(lightIndex < 0 || lightIndex >= (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<RequestedLight> 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<DistanceNodeEntry> 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; i<ShadowNodeList.size(); ++i)
+ ShadowNodeList[i]->render();
+
+ 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<ISceneNode*> CameraList;
+ core::array<ISceneNode*> LightList;
+ core::array<ISceneNode*> ShadowNodeList;
core::array<ISceneNode*> SkyBoxList;
core::array<DefaultNodeEntry> SolidNodeList;
core::array<TransparentNodeEntry> 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<f32>* 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<f32>());
+ 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; i<numEdges; ++i)
+ {
+ const core::vector3df &v1 = Vertices[Edges[2*i+0]];
+ const core::vector3df &v2 = Vertices[Edges[2*i+1]];
+ if ( !isDirectional )
+ {
+ lightDir1 = (v1 - light).normalize()*Infinity;
+ lightDir2 = (v2 - light).normalize()*Infinity;
+ }
+ const core::vector3df v3(v1+lightDir1);
+ const core::vector3df v4(v2+lightDir2);
+
+ // Add a quad (two triangles) to the vertex list
+#ifdef _DEBUG
+ if (svp->size() >= 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<f32>* 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; i<faceCount; ++i)
+ {
+ const core::vector3df v0 = Vertices[Indices[3*i+0]];
+ const core::vector3df v1 = Vertices[Indices[3*i+1]];
+ const core::vector3df v2 = Vertices[Indices[3*i+2]];
+
+ if ( !isDirectional )
+ {
+ lightDir0 = (v0-light).normalize();
+ }
+#ifdef IRR_USE_REVERSE_EXTRUDED
+ FaceData[i]=core::triangle3df(v2,v1,v0).isFrontFacing(lightDir0); // actually the back-facing polygons
+#else
+ FaceData[i]=core::triangle3df(v0,v1,v2).isFrontFacing(lightDir0);
+#endif
+
+#if 0 // Useful for internal debugging & testing. Show all the faces in the light.
+ if ( FaceData[i] )
+ {
+ video::SMaterial m;
+ m.Lighting = false;
+ SceneManager->getVideoDriver()->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; i<faceCount; ++i)
+ {
+ // check all front facing faces
+ if (FaceData[i] == true)
+ {
+ const u16 wFace0 = Indices[3*i+0];
+ const u16 wFace1 = Indices[3*i+1];
+ const u16 wFace2 = Indices[3*i+2];
+
+ if ( Optimization == ESV_NONE )
+ {
+ // add edge v0-v1
+ Edges[2*numEdges+0] = wFace0;
+ Edges[2*numEdges+1] = wFace1;
+ ++numEdges;
+
+ // add edge v1-v2
+ Edges[2*numEdges+0] = wFace1;
+ Edges[2*numEdges+1] = wFace2;
+ ++numEdges;
+
+ // add edge v2-v0
+ Edges[2*numEdges+0] = wFace2;
+ Edges[2*numEdges+1] = wFace0;
+ ++numEdges;
+ }
+ else
+ {
+ const u16 adj0 = Adjacency[3*i+0];
+ const u16 adj1 = Adjacency[3*i+1];
+ const u16 adj2 = Adjacency[3*i+2];
+
+ // add edges if face is adjacent to back-facing face
+ // or if no adjacent face was found
+ if (adj0 == i || FaceData[adj0] == false)
+ {
+ // add edge v0-v1
+ Edges[2*numEdges+0] = wFace0;
+ Edges[2*numEdges+1] = wFace1;
+ ++numEdges;
+ }
+
+ if (adj1 == i || FaceData[adj1] == false)
+ {
+ // add edge v1-v2
+ Edges[2*numEdges+0] = wFace1;
+ Edges[2*numEdges+1] = wFace2;
+ ++numEdges;
+ }
+
+ if (adj2 == i || FaceData[adj2] == false)
+ {
+ // add edge v2-v0
+ Edges[2*numEdges+0] = wFace2;
+ Edges[2*numEdges+1] = wFace0;
+ ++numEdges;
+ }
+ }
+ }
+ }
+ return numEdges;
+}
+
+
+void CShadowVolumeSceneNode::setShadowMesh(const IMesh* mesh)
+{
+ if (ShadowMesh == mesh)
+ return;
+ if (ShadowMesh)
+ ShadowMesh->drop();
+ 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; i<bufcnt; ++i)
+ {
+ const IMeshBuffer* buf = mesh->getMeshBuffer(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; i<bufcnt; ++i)
+ {
+ const IMeshBuffer* buf = mesh->getMeshBuffer(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; j<vtxcnt; ++j)
+ Vertices[VertexCount++] = buf->getPosition(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; i<lightCount; ++i)
+ {
+ const video::SLight& dl = SceneManager->getVideoDriver()->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; i<ShadowVolumesUsed; ++i)
+ {
+ bool drawShadow = true;
+
+ if (checkFarPlaneClipping)
+ {
+ // Disable shadows drawing, when back cap is behind of ZFar plane.
+ // TODO: Using infinite projection matrices instead is said to work better
+ // as then we wouldn't fail when the shadow clip the far plane.
+ // I couldn't get it working (and neither anyone before me it seems).
+ // Anyone who can figure it out is welcome to provide a patch.
+
+ core::vector3df edges[8];
+ ShadowBBox[i].getEdges(edges);
+
+ for(int j = 0; j < 8; ++j)
+ {
+ if (frust.planes[scene::SViewFrustum::VF_FAR_PLANE].classifyPointRelation(edges[j]) == core::ISREL3D_FRONT)
+ {
+ drawShadow = false;
+ break;
+ }
+ }
+ }
+
+ if(drawShadow)
+ driver->drawStencilShadowVolume(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<core::vector3df> triangles;
+ driver->drawStencilShadowVolume(triangles, UseZFailMethod, DebugDataVisible);
+ }
+ }
+}
+
+
+//! returns the axis aligned bounding box of this node
+const core::aabbox3d<f32>& 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<IndexCount; f+=3)
+ {
+ for (u32 edge = 0; edge<3; ++edge)
+ {
+ const core::vector3df& v1 = Vertices[Indices[f+edge]];
+ const core::vector3df& v2 = Vertices[Indices[f+((edge+1)%3)]];
+
+ // now we search an_O_ther _F_ace with these two
+ // vertices, which is not the current face.
+ u32 of;
+
+ for (of=0; of<IndexCount; of+=3)
+ {
+ // only other faces
+ if (of != f)
+ {
+ bool cnt1 = false;
+ bool cnt2 = false;
+
+ for (s32 e=0; e<3; ++e)
+ {
+ if (v1.equals(Vertices[Indices[of+e]]))
+ cnt1=true;
+
+ if (v2.equals(Vertices[Indices[of+e]]))
+ cnt2=true;
+ }
+ // one match for each vertex, i.e. edge is the same
+ if (cnt1 && cnt2)
+ break;
+ }
+ }
+
+ // no adjacent edges -> 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<f32>& getBoundingBox() const override;
+
+ //! Returns type of the scene node
+ virtual ESCENE_NODE_TYPE getType() const override { return ESNT_SHADOW_VOLUME; }
+
+ private:
+
+ typedef core::array<core::vector3df> SShadowVolume;
+
+ void createShadowVolume(const core::vector3df& pos, bool isDirectional);
+ u32 createEdgesAndCaps(const core::vector3df& light, bool isDirectional, SShadowVolume* svp, core::aabbox3d<f32>* bb);
+
+ //! Generates adjacency information based on mesh indices.
+ void calculateAdjacency();
+
+ core::aabbox3d<f32> Box;
+
+ // a shadow volume for every light
+ core::array<SShadowVolume> ShadowVolumes;
+
+ // a back cap bounding box for every light
+ core::array<core::aabbox3d<f32> > ShadowBBox;
+
+ core::array<core::vector3df> Vertices;
+ core::array<u16> Indices;
+ core::array<u16> Adjacency;
+ core::array<u16> Edges;
+ // tells if face is front facing
+ std::vector<bool> 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
|