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