Commits

duststorm01 committed cff39f0 Draft

Updated paged crowd sample with dark_sylinc's new instancing component.
This is in early stages yet, but works with the robot.

Comments (0)

Files changed (31)

 
 	# Lock mouse to render window, applies to linux and MAC OS (default=Yes)
 	# (disable eg. when using debugger)
-	Grab Mouse=No
+	Grab Mouse=Yes
 
 	# Implement agent steering demo using a temporary obstacle for agent 
 	# avoidance. No for steering a crowd agent manually (less control).
 	Temp Obstacle Steering=Yes
 	
 	# Show human characters as agents, cylinders if set to No (default=Yes)
-	Human Characters=No
+	Human Characters=Yes
+	
+	# Use the new instancing system in the paged crowds demo to instance
+	# the rendered crowd characters. (default=No)
+	Instanced Crowd Characters=Yes
 	
 	# Place extra obstacles (pots) for building initial navmesh (default=Yes)
 	# Only applicable if Terrain Demo=No
     Samples/src/OgreRecastTerrainApplication.cpp \
     Samples/src/SettingsFileParser.cpp \
     Samples/src/OgreRecastPagedCrowdApplication.cpp \
-    src/OgreRecastNavmeshPruner.cpp
+    src/OgreRecastNavmeshPruner.cpp \
+    src/InstancedCharacter.cpp
 HEADERS += Samples/include/BaseApplication.h \
     Samples/include/OgreRecastApplication.h \
     include/OgreRecastDefinitions.h \
     Samples/include/OgreRecastTerrainApplication.h \
     Samples/include/SettingsFileParser.h \
     Samples/include/OgreRecastPagedCrowdApplication.h \
-    include/OgreRecastNavmeshPruner.h
+    include/OgreRecastNavmeshPruner.h \
+    include/InstancedCharacter.h

Samples/include/OgreRecastPagedCrowdApplication.h

 
     static const bool EXTRACT_WALKABLE_AREAS;
 
+    static bool INSTANCED_CROWD;
+
 protected:
     /**
       * Initialise the scene and everything needed for pathfinding and steering.
 
     std::vector<Ogre::Vector2> mBorderTiles;
 
+    Ogre::InstanceManager* mInstanceManager;
+
 };
 
 #endif // OGRERECASTPAGEDCROWDAPPLICATION_H

Samples/include/SettingsFileParser.h

     bool mComplexObstacles;
     bool mTerrain;
     bool mPaged;
+    bool mInstancedCrowd;
 
     bool mRestoreConfig;
     bool mLockMouse;

Samples/src/OgreRecastApplication.cpp

     mDetourCrowd = new OgreDetourCrowd(mRecast);
     Character *character = createCharacter("Agent0", beginPos);    // create initial agent at start marker
     if(!HUMAN_CHARACTERS)
-        character->getEntity()->setMaterialName("Cylinder/LightBlue");  // Give the first agent a different color
+        ((TestCharacter*)character)->getEntity()->setMaterialName("Cylinder/LightBlue");  // Give the first agent a different color
     setDestinationForAllAgents(endPos, false);  // Move all agents in crowd to destination
 
 
         OgreRecastTerrainApplication::TERRAIN_TILE_SIZE = sfp.mTerrainTileSize;
         OgreRecastTerrainApplication::TERRAIN_HEIGHT_SCALE = sfp.mTerrainHeightScale;
         OgreRecastTerrainApplication::TERRAIN_TILE_RESOLUTION = sfp.mTerrainTileResolution;
+
+        OgreRecastPagedCrowdApplication::INSTANCED_CROWD = sfp.mInstancedCrowd;
     } catch (Ogre::Exception e) {
         std::cout << "WARNING: Could not find file " << configFileName << ". Using default settings." << std::endl;
     }

Samples/src/OgreRecastPagedCrowdApplication.cpp

 #include "TestCharacter.h"
 #include "RecastInputGeom.h"
 #include "OgreRecastNavmeshPruner.h"
+#include "InstancedCharacter.h"
 
 const Ogre::Real OgreRecastPagedCrowdApplication::CROWD_PAGE_UPDATE_DELTA = 1;
 const bool OgreRecastPagedCrowdApplication::EXTRACT_WALKABLE_AREAS = true;
 
+bool OgreRecastPagedCrowdApplication::INSTANCED_CROWD = false;
+
 
 // TODO extract CrowdInstancer class
 
 // TODO incorporate instancing + hardware skinning
 
+// TODO manage sets of character types, and when instancing also manage their fitting instanceManagers
+
 // TODO larger scale demo (and more interesting scene)
 
 // TODO make crowd wandering more purposeful by use of waypoints, maybe make the exact behaviour configurable
 
 
-// TODO make crowd wander more robust (periodically check liveliness of agents so they don't get stuck)
-
-
 // TODO consider this alternative way of placing new agents that walked off the grid: place them on the outer edge of the page, not just on a border tile
 
 
     , mGoingRight(false)
     , mDebugDraw(false)
     , mBorderTiles()
+    , mInstanceManager(0)
 {
     // Number of tiles filled with agents
     if(mPagedAreaDistance == 0) {
                 debugPrint("Init: load "+Ogre::StringConverter::toString(agentsPerTile)+" agents on tile "+tileToStr(x,y)+".");
                 for(int i = 0; i < agentsPerTile; i++) {
                     nbAgents++;
-    // TODO another option is to place agents randomly in a circle around the whole paged area, same for a complete move to another position!
                     //Ogre::Vector3 position = getRandomPositionInNavmeshTileSet(NavmeshTileSet);
                     Ogre::Vector3 position = getRandomPositionInNavmeshTile(x, y);
                     Character *character;
 // TODO make configurable which type of character is exactly instanced (maybe allow keeping sets of different populations)
-                    if(OgreRecastApplication::HUMAN_CHARACTERS)
+                    if(OgreRecastPagedCrowdApplication::INSTANCED_CROWD) {
+                        character = new InstancedCharacter("Character_"+Ogre::StringConverter::toString(nbAgents), mSceneMgr, mDetourCrowd, mInstanceManager, false, position);
+                    } else if(OgreRecastApplication::HUMAN_CHARACTERS) {
                         character = new AnimateableCharacter("Character_"+Ogre::StringConverter::toString(nbAgents), mSceneMgr, mDetourCrowd, false, position);
-                    else
+                    } else {
                         character = new TestCharacter("Character_"+Ogre::StringConverter::toString(nbAgents), mSceneMgr, mDetourCrowd, position);
+                    }
                     mCharacters.push_back(character);
                     mAssignedCharacters.push_back(character);
                     assignAgentDestination(character);
     nbAgents++;
     while(nbAgents < mCrowdSize) {
         Character *character;
-        if(OgreRecastApplication::HUMAN_CHARACTERS)
+        if(OgreRecastPagedCrowdApplication::INSTANCED_CROWD) {
+            character = new InstancedCharacter("Character_"+Ogre::StringConverter::toString(nbAgents), mSceneMgr, mDetourCrowd, mInstanceManager);
+        } else if(OgreRecastApplication::HUMAN_CHARACTERS) {
             character = new AnimateableCharacter("Character_"+Ogre::StringConverter::toString(nbAgents), mSceneMgr, mDetourCrowd);
-        else
+        } else {
             character = new TestCharacter("Character_"+Ogre::StringConverter::toString(nbAgents), mSceneMgr, mDetourCrowd);
+        }
         character->unLoad();
         mCharacters.push_back(character);
         mUnassignedCharacters.push_back(character);
         navMeshPruner->pruneSelected();
     }
 
+    if(INSTANCED_CROWD) {
+        // Most compatible SM2+ technique
+        Ogre::InstanceManager::InstancingTechnique instanceTechnique = Ogre::InstanceManager::ShaderBased;
+
+        // Create instance manager for managing instances of the robot mesh
+        mInstanceManager = mSceneMgr->createInstanceManager(
+                    "RobotInstanceMgr", "robot.mesh",
+                    Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, instanceTechnique,
+                    mCrowdSize); // TODO experiment with batch size
+    }
 
     // DETOUR CROWD PAGING
     setDebugVisibility(true);

Samples/src/OgreRecastTerrainApplication.cpp

 */
 
 #include "OgreRecastTerrainApplication.h"
+#include "TestCharacter.h"
 #include <Terrain/OgreTerrain.h>
 
 
     mDetourCrowd = new OgreDetourCrowd(mRecast);
     Character *character = createCharacter("Agent0", beginPos);    // create initial agent at start marker
     if(!HUMAN_CHARACTERS)
-        character->getEntity()->setMaterialName("Cylinder/LightBlue");  // Give the first agent a different color
+        ((TestCharacter*)character)->getEntity()->setMaterialName("Cylinder/LightBlue");  // Give the first agent a different color
     setDestinationForAllAgents(endPos, false);  // Move all agents in crowd to destination
 
 

Samples/src/SettingsFileParser.cpp

     mComplexObstacles = true;
     mTerrain = false;
     mPaged = false;
+    mInstancedCrowd = false;
 
     mTerrainTilesX = 1;
     mTerrainTilesZ = 1;
     } else if(equals(optionName, "Paged Crowds Demo")) {
         mPaged = getBool(optionValue);
         return true;
+    } else if(equals(optionName, "Instanced Crowd Characters")) {
+        mInstancedCrowd = getBool(optionValue);
+        return true;
+
 
 
     } else if(equals(optionName, "Terrain Tiles X")) {
         mTerrainTilesX = (int) getReal(optionValue);
+        return true;
     } else if(equals(optionName, "Terrain Tiles Z")) {
         mTerrainTilesZ = (int) getReal(optionValue);
+        return true;
     } else if(equals(optionName, "Terrain Tile Size")) {
         mTerrainTileSize = getReal(optionValue);
+        return true;
     } else if(equals(optionName, "Terrain Height Scale")) {
         mTerrainHeightScale = getReal(optionValue);
+        return true;
     } else if(equals(optionName, "Terrain Tile Resolution")) {
         mTerrainTileResolution = getReal(optionValue);
+        return true;
     }
 
 

include/AnimateableCharacter.h

     AnimateableCharacter(Ogre::String name, Ogre::SceneManager* sceneMgr, OgreDetourCrowd* detourCrowd, bool debugDraw = false, Ogre::Vector3 position = Ogre::Vector3::ZERO);
 
     /**
+      * The entity that represents this character in the scene
+      **/
+    virtual Ogre::Entity* getEntity(void);
+
+    /**
       * Update one tick in the render loop. Advances animation and character position.
       * In order for the agents to be updated, you first need to call the detourCrowd
       * update function.
       **/
     virtual void setDebugVisibility(bool visible);
 
+    virtual bool getDebugVisibility(void);
+
+    virtual void show(void);
+
 protected:
+    bool mDebugDraw;
+
+    /**
+      * Main entity that represents this character.
+      **/
+    Ogre::Entity *mEnt;
+
     /**
       * Currently active animation state.
       **/

include/Character.h

     virtual Ogre::SceneNode* getNode(void) const;
 
     /**
-      * The entity that represents this character in the scene
-      **/
-    virtual Ogre::Entity* getEntity(void);
-
-    /**
       * The height of the agent for this character.
       **/
     virtual Ogre::Real getAgentHeight(void) const;
     /**
       * Returns true when this character has reached its set destination.
       **/
-    virtual bool destinationReached(void) const;
+    virtual bool destinationReached(void);
 
     /**
       * Request to set a manual velocity for this character, to control it
 
     bool isLoaded(void) const { return mAgentID >= 0; }
 
-    void load(void);
+    virtual void load(void);
 
-    void load(Ogre::Vector3 position);
+    virtual void load(Ogre::Vector3 position);
 
-    void unLoad(void);
+    virtual void unLoad(void);
 
-    void show(void);
+    virtual void show(void);
 
-    void hide(void);
+    virtual void hide(void);
+
+    virtual Ogre::Vector3 getRelativeLookingDirection(void);
+
+    virtual void setRelativeLookingDirection(Ogre::Vector3 direction);
 
 
 protected:
     Ogre::SceneManager *mSceneMgr;
 
     /**
-      * Main entity that represents this character.
-      **/
-    Ogre::Entity *mEnt;
-
-    /**
       * Node in which this character is.
       **/
     Ogre::SceneNode *mNode;
       **/
     Ogre::RaySceneQuery *mRaySceneQuery;
 
+    Ogre::Vector3 mLookingDirection;
+
 
     // Friend the application class to allow setDestinationForAllAgents to update character destination values
     friend class OgreRecastApplication;

include/InstancedCharacter.h

+#ifndef INSTANCEDCHARACTER_H
+#define INSTANCEDCHARACTER_H
+
+#include "Character.h"
+
+class InstancedCharacter : public Character
+{
+public:
+    InstancedCharacter(Ogre::String name, Ogre::SceneManager* sceneMgr, OgreDetourCrowd* detourCrowd, Ogre::InstanceManager* instanceMgr, bool debugDraw = false, Ogre::Vector3 position = Ogre::Vector3::ZERO);
+
+    /**
+      * Update one tick in the render loop. Advances animation and character position.
+      * In order for the agents to be updated, you first need to call the detourCrowd
+      * update function.
+      **/
+    virtual void update(Ogre::Real timeSinceLastFrame);
+
+    /**
+      * The instanced entity that represents this character in the scene
+      **/
+    virtual Ogre::InstancedEntity* getEntity(void);
+
+    /**
+      * @see{Character::setDebugVisibility(bool)}
+      **/
+    virtual void setDebugVisibility(bool visible);
+
+    virtual bool getDebugVisibility(void);
+
+    virtual void show(void);
+
+protected:
+    bool mDebugDraw;
+
+    /**
+      * Main entity that represents this character.
+      **/
+    Ogre::InstancedEntity *mEnt;
+
+    Ogre::InstanceManager* mInstanceManager;
+
+    /**
+      * Currently active animation state.
+      **/
+    Ogre::AnimationState* mAnimState;
+
+    /**
+      * Speed scaling factor of the animation playback.
+      **/
+    Ogre::Real mAnimSpeedScale;
+
+    /**
+      * Scenenode that stores all geometry related to
+      * recast debug drawing. Can be made visible with
+      * setDebugVisibility().
+      **/
+    Ogre::SceneNode *mDebugNode;
+};
+
+#endif // INSTANCEDCHARACTER_H

include/OgreDetourCrowd.h

       **/
     static Ogre::Vector3 calcVel(Ogre::Vector3 position, Ogre::Vector3 target, Ogre::Real speed);
 
+    static float getDistanceToGoal(const dtCrowdAgent* agent, const float maxRange);
+
+    static bool destinationReached(const dtCrowdAgent* agent, const float maxDistanceFromTarget);
+
     /**
       * Update method for the crowd manager. Will calculate new positions for moving agents.
       * Call this method in your frameloop every frame to make your agents move.

include/TestCharacter.h

     TestCharacter(Ogre::String name, Ogre::SceneManager *sceneMgr, OgreDetourCrowd* detourCrowd, Ogre::Vector3 position = Ogre::Vector3::ZERO);
 
     /**
+      * The entity that represents this character in the scene
+      **/
+    virtual Ogre::Entity* getEntity(void);
+
+    /**
       * @see{Character::update(Ogre::Real)}
       **/
     virtual void update(Ogre::Real timeSinceLastFrame);
       * @see{Character::setDebugVisibility(bool)}
       **/
     virtual void setDebugVisibility(bool visible);
+
+protected:
+    /**
+      * Main entity that represents this character.
+      **/
+    Ogre::Entity *mEnt;
 };
 
 #endif // TESTCHARACTER_H
 FileSystem=resources/overlays
 
 FileSystem=resources/materials/terrain
+FileSystem=resources/materials/instancing
 

resources/materials/instancing/Instancing.frag

+//---------------------------------------------------------------------------
+//These materials/shaders are part of the NEW InstanceManager implementation
+//Written by Matias N. Goldberg ("dark_sylinc")
+//---------------------------------------------------------------------------
+
+uniform sampler2D diffuseMap;
+
+uniform vec4	lightPosition;
+uniform vec3	cameraPosition;
+uniform vec3 	lightAmbient;
+uniform vec3	lightDiffuse;
+uniform vec3	lightSpecular;
+uniform vec4	lightAttenuation;
+uniform float	lightGloss;
+
+#if DEPTH_SHADOWRECEIVER
+uniform float invShadowMapSize;
+uniform sampler2DShadow shadowMap;
+
+//declare external function
+//vec4 calcDepthShadow(in vec4 inColour, in float lum);
+float calcDepthShadow(sampler2DShadow shadowMap, vec4 uv, float invShadowMapSize);
+#endif
+
+varying vec2 _uv0;
+varying vec3 oNormal;
+varying vec3 oVPos;
+#if DEPTH_SHADOWRECEIVER
+	varying vec4 oLightSpacePos;
+#endif
+
+//---------------------------------------------
+//Main Pixel Shader
+//---------------------------------------------
+void main(void)
+{
+	vec4 color = texture2D( diffuseMap, _uv0 );
+
+	float fShadow = 1.0;
+#if DEPTH_SHADOWRECEIVER
+	fShadow = calcDepthShadow( shadowMap, oLightSpacePos, invShadowMapSize );
+#endif
+
+	vec4 baseColour = texture2D( diffuseMap, _uv0 );
+
+	//Blinn-Phong lighting
+	vec3 normal	= normalize( oNormal );
+	vec3 lightDir		= lightPosition.xyz - oVPos * lightPosition.w;
+	vec3 eyeDir			= normalize( cameraPosition - oVPos );
+
+	float fLength	= length( lightDir );
+	lightDir			= normalize( lightDir );
+
+	float NdotL	= max( 0.0, dot( normal, lightDir ) );
+	vec3 halfVector		= normalize(lightDir + eyeDir);
+	float HdotN	= max( 0.0, dot( halfVector, normal ) );
+	
+	vec3 ambient  = lightAmbient * baseColour.xyz;
+	vec3 diffuse  = lightDiffuse * NdotL * baseColour.xyz;
+	vec3 specular = lightSpecular * pow( HdotN, lightGloss );
+	
+	vec3 directLighting = (diffuse + specular) * fShadow;
+	
+	gl_FragColor = vec4( directLighting + ambient, baseColour.a );
+	//gl_FragColor = baseColour;
+}

resources/materials/instancing/Instancing.material

+//---------------------------------------------------------------------------
+//These materials/shaders are part of the NEW InstanceManager implementation
+//Written by Matias N. Goldberg ("dark_sylinc")
+//---------------------------------------------------------------------------
+
+
+//--------------------------------------------------------------
+// GLSL Programs
+//--------------------------------------------------------------
+vertex_program Ogre/Instancing/ShaderBased_glsl_vs glsl
+{
+	source ShaderInstancing.vert
+	
+	preprocessor_defines DEPTH_SHADOWRECEIVER=1
+
+	includes_skeletal_animation true
+}
+
+vertex_program Ogre/Instancing/shadow_caster_glsl_vs glsl
+{
+	source ShaderInstancing.vert
+	preprocessor_defines DEPTH_SHADOWCASTER=1
+
+	includes_skeletal_animation true
+}
+
+
+fragment_program Ogre/Shadow/Utils glsl
+{
+	source shadows.glsl
+}
+
+fragment_program Ogre/Instancing_glsl_ps glsl
+{
+	source Instancing.frag
+	attach Ogre/Shadow/Utils
+
+	preprocessor_defines DEPTH_SHADOWRECEIVER=1
+
+	default_params
+	{
+		param_named			diffuseMap				int 0
+		param_named			shadowMap				int 1
+	}
+}
+
+fragment_program Ogre/Instancing/shadow_caster_glsl_ps glsl
+{
+    source DepthShadowmapCasterFp.glsl
+
+	preprocessor_defines LINEAR_RANGE=1
+}
+
+//--------------------------------------------------------------
+// CG Programs
+//--------------------------------------------------------------
+vertex_program Ogre/Instancing/ShaderBased_cg_vs cg
+{
+	source ShaderInstancing.cg
+	entry_point main_vs
+	profiles vs_3_0 vs_2_0 vp40
+
+	compile_arguments -DDEPTH_SHADOWRECEIVER
+
+	includes_skeletal_animation true
+}
+
+vertex_program Ogre/Instancing/shadow_caster_cg_vs cg
+{
+	source ShaderInstancing.cg
+	entry_point main_vs
+	profiles vs_3_0 vs_2_0 vp40
+	
+	compile_arguments -DDEPTH_SHADOWCASTER
+
+	includes_skeletal_animation true
+}
+
+
+fragment_program Ogre/Instancing_cg_ps cg
+{
+	source Instancing_ps.cg
+	entry_point main_ps
+	profiles ps_3_0 ps_2_x fp40
+
+	compile_arguments -DDEPTH_SHADOWRECEIVER
+}
+
+fragment_program Ogre/Instancing/shadow_caster_cg_ps cg
+{
+	source depthshadowobject.cg
+	profiles ps_3_0 ps_2_x fp40
+	entry_point main_fp
+	compile_arguments -DSHADOWCASTER=1 -DDEPTH_SHADOWCASTER=1 -DDEPTH_SHADOWRECEIVER=0
+
+	default_params
+	{
+	}
+}
+
+//--------------------------------------------------------------
+// Unified CG/GLSL Programs
+//--------------------------------------------------------------
+vertex_program Ogre/Instancing/ShaderBased_vs unified
+{
+	delegate Ogre/Instancing/ShaderBased_glsl_vs
+	delegate Ogre/Instancing/ShaderBased_cg_vs
+
+	default_params
+	{
+		param_named_auto	viewProjMatrix				viewproj_matrix
+		param_named_auto	worldMatrix3x4Array			world_matrix_array_3x4
+		
+		param_named_auto	depthRange					shadow_scene_depth_range 0
+		param_named_auto	texViewProjMatrix			texture_viewproj_matrix 0
+	}
+}
+
+vertex_program Ogre/Instancing/shadow_caster_vs unified
+{
+	delegate Ogre/Instancing/shadow_caster_glsl_vs
+	delegate Ogre/Instancing/shadow_caster_cg_vs
+
+	default_params
+	{
+		param_named_auto	viewProjMatrix				viewproj_matrix
+		param_named_auto	worldMatrix3x4Array			world_matrix_array_3x4
+		param_named_auto	depthRange					scene_depth_range
+	}
+}
+
+
+
+fragment_program Ogre/Instancing_ps unified
+{
+	delegate Ogre/Instancing_glsl_ps
+	delegate Ogre/Instancing_cg_ps
+	
+	default_params
+	{
+		param_named_auto	lightPosition		light_position			0
+		param_named_auto	cameraPosition		camera_position			0
+		param_named_auto	lightAmbient		ambient_light_colour
+		param_named_auto	lightDiffuse		light_diffuse_colour	0
+		param_named_auto	lightSpecular		light_specular_colour	0
+		param_named_auto	lightGloss			surface_shininess
+
+		param_named_auto	invShadowMapSize	inverse_texture_size	1
+	}
+}
+
+fragment_program Ogre/Instancing/shadow_caster_ps unified
+{
+	delegate Ogre/Instancing/shadow_caster_glsl_ps
+	delegate Ogre/Instancing/shadow_caster_cg_ps
+}
+
+//--------------------------------------------------------------
+// Material definitions
+//--------------------------------------------------------------
+material Examples/Instancing/ShaderBased/shadow_caster
+{
+	technique
+	{
+		pass
+		{
+			vertex_program_ref Ogre/Instancing/shadow_caster_vs
+			{
+			}
+			fragment_program_ref Ogre/Instancing/shadow_caster_ps
+			{
+			}
+		}
+	}
+}
+
+abstract material Examples/Instancing/ShaderBased
+{
+	technique
+	{
+		shadow_caster_material Examples/Instancing/ShaderBased/shadow_caster
+
+		pass
+		{
+			specular	1 1 1 1 12.5
+			vertex_program_ref Ogre/Instancing/ShaderBased_vs
+			{
+			}
+
+			fragment_program_ref Ogre/Instancing_ps
+			{
+			}
+
+			texture_unit Diffuse
+			{
+				texture_alias		DiffuseMap
+			}
+
+			texture_unit shadow0
+			{
+				content_type shadow
+				tex_address_mode border
+				tex_border_colour 1 1 1 1
+			}
+		}
+	}
+}
+
+//--------------------------------------------------------------
+// Materials
+//--------------------------------------------------------------
+material Examples/Instancing/ShaderBased/Robot : Examples/Instancing/ShaderBased
+{
+	set_texture_alias	DiffuseMap	r2skin.jpg
+}

resources/materials/instancing/InstancingVertexInterpolators.cg

+//---------------------------------------------------------------------------
+//These materials/shaders are part of the NEW InstanceManager implementation
+//Written by Matias N. Goldberg ("dark_sylinc")
+//---------------------------------------------------------------------------
+
+//---------------------------------------------
+//Pixel Shader Input
+//---------------------------------------------
+struct PS_INPUT
+{
+#ifdef DEPTH_SHADOWCASTER
+	float3 unused	:	TEXCOORD0;
+	float depth		:	TEXCOORD1;
+#else
+	float2 uv0		:	TEXCOORD0;
+	float3 Normal	:	TEXCOORD1;
+	float3 vPos		:	TEXCOORD2;
+	
+	#ifdef DEPTH_SHADOWRECEIVER
+		float4 lightSpacePos	:	TEXCOORD3;
+	#endif
+#endif
+};
+
+#define SHADOW_BIAS 0
+
+//---------------------------------------------
+//Vertex Shader Output
+//---------------------------------------------
+struct VS_OUTPUT
+{
+	float4 Position	:	POSITION;
+	PS_INPUT	ps;
+};

resources/materials/instancing/Instancing_ps.cg

+//---------------------------------------------------------------------------
+//These materials/shaders are part of the NEW InstanceManager implementation
+//Written by Matias N. Goldberg ("dark_sylinc")
+//---------------------------------------------------------------------------
+
+#include "InstancingVertexInterpolators.cg"
+
+#include "shadows.cg"
+
+//---------------------------------------------
+//Main Pixel Shader
+//---------------------------------------------
+half4 main_ps( PS_INPUT input ,
+				uniform float4	lightPosition,
+				uniform float3	cameraPosition,
+				uniform half3 	lightAmbient,
+				uniform half3	lightDiffuse,
+				uniform half3	lightSpecular,
+				uniform half4	lightAttenuation,
+				uniform half	lightGloss,
+
+				//Textures
+				uniform sampler2D diffuseMap : register(s0)
+
+#ifdef DEPTH_SHADOWRECEIVER
+			,	uniform float invShadowMapSize,
+				uniform sampler2D shadowMap : register(s1)
+#endif
+			) : COLOR0
+{
+	float fShadow = 1.0f;
+#ifdef DEPTH_SHADOWRECEIVER
+	fShadow = calcDepthShadow( shadowMap, input.lightSpacePos, invShadowMapSize );
+#endif
+
+	const half4 baseColour = tex2D( diffuseMap, input.uv0 );
+	
+	//Blinn-Phong lighting
+	const half3 normal		= normalize( input.Normal );
+	half3 lightDir			= lightPosition.xyz - input.vPos * lightPosition.w;
+	half3 eyeDir			= normalize( cameraPosition - input.vPos  );
+
+	const half fLength		= length( lightDir );
+	lightDir				= normalize( lightDir );
+
+	const half NdotL = max( 0.0f, dot( normal, lightDir ) );
+	half3 halfVector = normalize(lightDir + eyeDir);
+	const half HdotN = max( 0.0f, dot( halfVector, normal ) );
+	
+	const half3 ambient  = lightAmbient * baseColour.xyz;
+	const half3 diffuse  = lightDiffuse * NdotL * baseColour.xyz;
+	const half3 specular = lightSpecular * pow( HdotN, lightGloss );
+	
+	const half3 directLighting = (diffuse + specular) * fShadow;
+	
+	return half4( directLighting + ambient, baseColour.a );
+}

resources/materials/instancing/ShaderInstancing.cg

+//---------------------------------------------------------------------------
+//These materials/shaders are part of the NEW InstanceManager implementation
+//Written by Matias N. Goldberg ("dark_sylinc")
+//---------------------------------------------------------------------------
+
+//---------------------------------------------
+//Vertex Shader Input
+//---------------------------------------------
+struct VS_INPUT
+{
+	float4 Position	:	POSITION;
+	float3 Normal	:	NORMAL;
+	float3 Tangent	:	TANGENT;
+	float2 uv0	:	TEXCOORD0;
+
+	float4 BlendIdx	:	BLENDINDICES;
+	float4 BlendWgt	:	BLENDWEIGHT;
+};
+
+#include "InstancingVertexInterpolators.cg"
+#ifdef ST_DUAL_QUATERNION
+#include "DualQuaternion_Common.cg"
+#endif
+
+//---------------------------------------------
+//Main Vertex Shader
+//---------------------------------------------
+VS_OUTPUT main_vs( in VS_INPUT input,
+				   uniform float4x4 viewProjMatrix,
+#ifdef ST_DUAL_QUATERNION
+				   uniform float2x4 worldDualQuaternion2x4Array[120]
+#else
+				   uniform float3x4 worldMatrix3x4Array[80]
+#endif
+#if defined( DEPTH_SHADOWCASTER ) || defined( DEPTH_SHADOWRECEIVER )
+				,  uniform float4 depthRange
+#endif
+#ifdef DEPTH_SHADOWRECEIVER
+				,  uniform float4x4 texViewProjMatrix
+#endif
+				   )
+{
+	VS_OUTPUT output;
+
+	float4 worldPos	 = 0;
+	float3 worldNorm = 0;
+
+	int idx = int(input.BlendIdx[0]);
+#ifdef ST_DUAL_QUATERNION
+	//Only dealing with one weight so normalization of the dual quaternion and weighting are unnecessary
+	float2x4 blendDQ = worldDualQuaternion2x4Array[idx];
+#ifdef BONE_TWO_WEIGHTS
+	float2x4 blendDQ2 = worldDualQuaternion2x4Array[int(input.BlendIdx[1])];
+	
+	//Accurate antipodality handling. For speed increase, remove the following line
+	if (dot(blendDQ[0], blendDQ2[0]) < 0.0) blendDQ2 *= -1.0;
+	
+	//Blend the dual quaternions based on the weights
+	blendDQ *= input.BlendWgt.x;
+	blendDQ += input.BlendWgt.y*blendDQ2;
+	//Normalize the resultant dual quaternion
+	blendDQ /= length(blendDQ[0]);
+#endif
+	worldPos = float4(calculateBlendPosition(input.Position.xyz, blendDQ), 1.0);
+	worldNorm = calculateBlendNormal(input.Normal, blendDQ);
+#else
+	worldPos  = float4( mul( worldMatrix3x4Array[idx], input.Position ).xyz, 1.0f );
+	worldNorm = mul( (float3x3)(worldMatrix3x4Array[idx]), input.Normal );
+#endif
+
+	/*int i;
+	for( i=0; i<4; i++ )
+	{
+		int idx = int(input.BlendIdx[0]);
+		worldPos += float4( mul( worldMatrix3x4Array[idx], input.Position ).xyz, 1.0f ) * input.BlendWgt[i];
+		worldNorm += mul( (float3x3)(worldMatrix3x4Array[idx]), input.Normal ) * input.BlendWgt[i];
+	}*/
+
+	//Transform the position
+	output.Position		= mul( viewProjMatrix, worldPos );
+	
+#ifdef DEPTH_SHADOWCASTER
+	output.ps.unused	= float3( 0 );
+	output.ps.depth		= (output.Position.z - depthRange.x + SHADOW_BIAS) * depthRange.w;
+#else
+	output.ps.uv0		= input.uv0;
+	
+	//Pass Normal and position for Blinn Phong lighting
+	output.ps.Normal	= normalize(worldNorm);
+	output.ps.vPos		= worldPos.xyz;
+
+	#ifdef DEPTH_SHADOWRECEIVER
+		// Calculate the position of vertex in light space to do shadows
+		output.ps.lightSpacePos = mul( texViewProjMatrix, worldPos );
+		// make linear
+		output.ps.lightSpacePos.z = (output.ps.lightSpacePos.z - depthRange.x) * depthRange.w;
+	#endif
+#endif
+
+	return output;
+}

resources/materials/instancing/ShaderInstancing.vert

+//---------------------------------------------------------------------------
+//These materials/shaders are part of the NEW InstanceManager implementation
+//Written by Matias N. Goldberg ("dark_sylinc")
+//---------------------------------------------------------------------------
+#version 120
+
+//Vertex input
+attribute vec4 vertex;
+attribute vec3 normal;
+attribute vec3 tangent;
+attribute vec4 uv0;
+attribute vec4 blendIndices;
+attribute vec4 blendWeights;
+
+//Parameters
+uniform mat4 viewProjMatrix;
+//uniform mat4x3 worldMatrix3x4Array[80];
+#ifdef ST_DUAL_QUATERNION
+uniform vec4 worldDualQuaternion2x4Array[240];
+#else
+uniform vec4 worldMatrix3x4Array[240]; //240 = 80*3
+#endif
+
+
+#if (DEPTH_SHADOWCASTER || DEPTH_SHADOWRECEIVER)
+uniform vec4 depthRange;
+#endif
+
+#if DEPTH_SHADOWRECEIVER
+uniform mat4 texViewProjMatrix;
+#endif
+
+//Output
+#if DEPTH_SHADOWCASTER
+	varying vec2 depth;
+#else
+	varying vec2 _uv0;
+	varying vec3 oNormal;
+	varying vec3 oVPos;
+	#if DEPTH_SHADOWRECEIVER
+		varying vec4 oLightSpacePos;
+	#endif
+#endif
+
+vec3 calculateBlendPosition(vec3 position, mat2x4 blendDQ)
+{
+	vec3 blendPosition = position + 2.0*cross(blendDQ[0].yzw, cross(blendDQ[0].yzw, position) + blendDQ[0].x*position);
+	vec3 trans = 2.0*(blendDQ[0].x*blendDQ[1].yzw - blendDQ[1].x*blendDQ[0].yzw + cross(blendDQ[0].yzw, blendDQ[1].yzw));
+	blendPosition += trans;
+
+	return blendPosition;
+}
+
+vec3 calculateBlendNormal(vec3 normal, mat2x4 blendDQ)
+{
+	return normal + 2.0*cross(blendDQ[0].yzw, cross(blendDQ[0].yzw, normal) + blendDQ[0].x*normal);
+}
+
+//---------------------------------------------
+//Main Vertex Shader
+//---------------------------------------------
+void main(void)
+{
+vec4 worldPos;
+vec3 worldNorm;
+
+#ifdef ST_DUAL_QUATERNION
+	int idx = int(blendIndices[0]) * 2;
+	mat2x4 blendDQ;
+	blendDQ[0] = worldDualQuaternion2x4Array[idx];
+	blendDQ[1] = worldDualQuaternion2x4Array[idx + 1];
+#ifdef BONE_TWO_WEIGHTS
+	int idx2 = int(blendIndices[1]) * 2;
+	mat2x4 blendDQ2;
+ 	blendDQ2[0] = worldDualQuaternion2x4Array[idx2];
+	blendDQ2[1] = worldDualQuaternion2x4Array[idx2 + 1];
+
+	//Accurate antipodality handling. For speed increase, remove the following line
+	if (dot(blendDQ[0], blendDQ2[0]) < 0.0) blendDQ2 *= -1.0;
+	
+	//Blend the dual quaternions based on the weights
+	blendDQ *= blendWeights.x;
+	blendDQ += blendWeights.y*blendDQ2;
+	//Normalize the resultant dual quaternion
+	blendDQ /= length(blendDQ[0]);
+#endif
+	worldPos = vec4(calculateBlendPosition(vertex.xyz, blendDQ), 1.0);
+	worldNorm = calculateBlendNormal(normal, blendDQ);
+#else
+	mat4 worldMatrix;
+	int idx = int(blendIndices[0]) * 3;
+	worldMatrix[0] = worldMatrix3x4Array[idx];
+	worldMatrix[1] = worldMatrix3x4Array[idx + 1];
+	worldMatrix[2] = worldMatrix3x4Array[idx + 2];
+	worldMatrix[3] = vec4( 0, 0, 0, 1 );
+
+	worldPos		= vertex * worldMatrix;
+	worldNorm		= normal * mat3(worldMatrix);
+#endif
+
+	//Transform the position
+	gl_Position			= viewProjMatrix * worldPos;
+
+#if DEPTH_SHADOWCASTER
+	depth.x				= (gl_Position.z - depthRange.x) * depthRange.w;
+	depth.y				= depthRange.w;
+#else
+	_uv0		= uv0.xy;
+	oNormal		= worldNorm;
+	oVPos		= worldPos.xyz;
+
+	#if DEPTH_SHADOWRECEIVER
+		oLightSpacePos		= texViewProjMatrix * worldPos;
+		oLightSpacePos.z	= (oLightSpacePos.z - depthRange.x) * depthRange.w;
+	#endif
+#endif
+}

resources/materials/instancing/depthshadowobject.cg

+/* Copyright Torus Knot Software Ltd 2000-2012
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "shadows.cg"
+
+#define BIAS 0
+
+struct VS_OUT
+{
+    float4  pos			  : POSITION;   
+    float3  diffuseUV     : TEXCOORD0;  // xy = UV, z = fog/depth
+#if !SHADOWCASTER
+	float3	col			  : COLOR;
+#endif
+#if DEPTH_SHADOWCASTER
+	float	depth		  : TEXCOORD1;
+#endif
+#if DEPTH_SHADOWRECEIVER
+	float4 lightSpacePos0	: TEXCOORD2;
+	float4 lightSpacePos1	: TEXCOORD3;
+	float4 lightSpacePos2	: TEXCOORD4;
+#endif
+};
+
+VS_OUT main_vp(
+	float4   pos	: POSITION,     
+    float3   normal	: NORMAL,    
+    float4   uv0	: TEXCOORD0,
+
+   	uniform float4x4 worldViewProj,
+	uniform float4 lightPosition,
+	uniform float3 lightDiffuse
+#if FOG
+	, uniform float2 fogParams		// x = fog start, y = fog distance
+#endif
+#if DEPTH_SHADOWCASTER
+	, uniform float4 depthRange // x = min, y = max, z = range, w = 1/range
+#elif DEPTH_SHADOWRECEIVER
+	, uniform float4 depthRange0 // x = min, y = max, z = range, w = 1/range
+	, uniform float4 depthRange1 // x = min, y = max, z = range, w = 1/range
+	, uniform float4 depthRange2 // x = min, y = max, z = range, w = 1/range
+#endif
+#if DEPTH_SHADOWRECEIVER
+	, uniform float4x4 texWorldViewProjMatrix0
+	, uniform float4x4 texWorldViewProjMatrix1
+	, uniform float4x4 texWorldViewProjMatrix2
+#endif
+
+	)
+{
+    VS_OUT outp;
+
+    // project position to the screen
+    outp.pos = mul(worldViewProj, pos);
+
+#if !SHADOWCASTER
+	// Get object space light direction
+	float3 lightDir = normalize(lightPosition.xyz - (pos.xyz * lightPosition.w).xyz);
+	outp.col = lightDiffuse.xyz * max(dot(lightDir, normal.xyz), 0.0);
+#  if FOG
+    outp.diffuseUV.z = linearFog(outp.pos.z, fogParams.x, fogParams.y);
+#  endif
+
+#endif
+
+    // pass through other texcoords exactly as they were received
+    outp.diffuseUV.xy = uv0.xy;
+    
+
+#if DEPTH_SHADOWCASTER
+	outp.depth = (BIAS + outp.pos.z - depthRange.x) * depthRange.w;
+#endif
+
+#if DEPTH_SHADOWRECEIVER
+	// Calculate the position of vertex in light space
+	outp.lightSpacePos0 = mul(texWorldViewProjMatrix0, pos);
+	outp.lightSpacePos1 = mul(texWorldViewProjMatrix1, pos);
+	outp.lightSpacePos2 = mul(texWorldViewProjMatrix2, pos);
+
+	// make linear
+	outp.lightSpacePos0.z = (outp.lightSpacePos0.z - depthRange0.x) * depthRange0.w;
+	outp.lightSpacePos1.z = (outp.lightSpacePos1.z - depthRange1.x) * depthRange1.w;
+	outp.lightSpacePos2.z = (outp.lightSpacePos2.z - depthRange2.x) * depthRange2.w;
+
+	// pass cam depth
+	outp.diffuseUV.z = outp.pos.z;
+
+#endif
+
+    return outp;
+}
+
+
+float4 main_fp(
+	VS_OUT In,
+
+	uniform sampler2D diffuseMap	: register(s0),
+#if DEPTH_SHADOWRECEIVER
+	uniform sampler2D shadowMap0 : register(s1),
+	uniform sampler2D shadowMap1 : register(s2),
+	uniform sampler2D shadowMap2 : register(s3),
+#endif
+
+	uniform float3 materialAmbient
+	
+#if SHADOWCASTER
+	, uniform float3 shadowColour
+#endif
+#if FOG
+	, uniform float3 fogColour
+#endif
+#if DEPTH_SHADOWRECEIVER
+	, uniform float inverseShadowmapSize0
+	, uniform float inverseShadowmapSize1
+	, uniform float inverseShadowmapSize2
+	, uniform float4 pssmSplitPoints
+#endif
+
+	) : COLOR
+{
+
+    // look up the diffuse map layer
+    float4 texDiffuse = tex2D(diffuseMap, In.diffuseUV.xy);
+    
+#if SHADOWCASTER
+#  if DEPTH_SHADOWCASTER
+	// early-out with depth (we still include alpha for those cards that support it)
+	return float4(In.depth.xxx, 1);
+#  else
+	return float4(shadowColour.xyz, texDiffuse.a);
+#  endif
+
+#else
+    // compute the ambient contribution (pulled from the diffuse map)
+    float3 vAmbient = texDiffuse.xyz * materialAmbient.xyz;
+    float3 vColor3 = texDiffuse.rgb * In.col.rgb;
+
+#  if DEPTH_SHADOWRECEIVER
+	float camDepth = In.diffuseUV.z;
+	float shadow = calcPSSMDepthShadow(shadowMap0, shadowMap1, shadowMap2, 
+		In.lightSpacePos0, In.lightSpacePos1, In.lightSpacePos2,
+		inverseShadowmapSize0, inverseShadowmapSize1, inverseShadowmapSize2,
+		pssmSplitPoints, camDepth);
+	vColor3 *= shadow;
+	/*
+	vAmbient = float3(0,0,0);
+	vColor3 = calcPSSMDebugShadow(shadowMap0, shadowMap1, shadowMap2, 
+		In.lightSpacePos0, In.lightSpacePos1, In.lightSpacePos2,
+		inverseShadowmapSize0, inverseShadowmapSize1, inverseShadowmapSize2,
+		pssmSplitPoints, camDepth);
+	*/
+#  endif    
+
+	float4 vColor;
+    vColor = float4(vColor3 + vAmbient, texDiffuse.a);
+    
+#  if FOG
+    // if fog is active, interpolate between the unfogged color and the fog color
+    // based on vertex shader fog value
+    vColor.rgb = lerp(vColor.rgb, fogColour, In.diffuseUV.z).rgb;
+#  endif
+
+    return vColor;
+#endif
+
+}
+
+

resources/materials/instancing/shadows.cg

+/* Copyright Torus Knot Software Ltd 2000-2012
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+// Simple PCF 
+// Number of samples in one dimension (square for total samples)
+#define NUM_SHADOW_SAMPLES_1D 2.0
+#define SHADOW_FILTER_SCALE 1
+
+#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D
+
+float4 offsetSample(float4 uv, float2 offset, float invMapSize)
+{
+	return float4(uv.xy + offset * invMapSize * uv.w, uv.z, uv.w);
+}
+
+float calcDepthShadow(sampler2D shadowMap, float4 uv, float invShadowMapSize)
+{
+	// 4-sample PCF
+	
+	float shadow = 0.0;
+	float offset = (NUM_SHADOW_SAMPLES_1D/2 - 0.5) * SHADOW_FILTER_SCALE;
+	for (float y = -offset; y <= offset; y += SHADOW_FILTER_SCALE)
+		for (float x = -offset; x <= offset; x += SHADOW_FILTER_SCALE)
+		{
+			float depth = tex2Dproj(shadowMap, offsetSample(uv, float2(x, y), invShadowMapSize)).x;
+			if (depth >= 1 || depth >= uv.z)
+				shadow += 1.0;
+		}
+
+	shadow /= SHADOW_SAMPLES;
+
+	return shadow;
+}
+
+
+float calcSimpleShadow(sampler2D shadowMap, float4 shadowMapPos)
+{
+	return tex2Dproj(shadowMap, shadowMapPos).x;
+}
+
+float calcPSSMDepthShadow(sampler2D shadowMap0, sampler2D shadowMap1, sampler2D shadowMap2, 
+						   float4 lsPos0, float4 lsPos1, float4 lsPos2,
+						   float invShadowmapSize0, float invShadowmapSize1, float invShadowmapSize2,
+						   float4 pssmSplitPoints, float camDepth)
+{
+
+	float shadow;
+	float4 splitColour;
+	// calculate shadow
+	if (camDepth <= pssmSplitPoints.y)
+	{
+		splitColour = float4(0.3, 0.0, 0, 0);
+		shadow = calcDepthShadow(shadowMap0, lsPos0, invShadowmapSize0);
+	}
+	else if (camDepth <= pssmSplitPoints.z)
+	{
+		splitColour = float4(0, 0.3, 0, 0);
+		shadow = calcDepthShadow(shadowMap1, lsPos1, invShadowmapSize1);
+	}
+	else
+	{
+		splitColour = float4(0.0, 0.0, 0.3, 0);
+		shadow = calcDepthShadow(shadowMap2, lsPos2, invShadowmapSize2);
+	}
+
+	return shadow;
+}
+
+float calcPSSMSimpleShadow(sampler2D shadowMap0, sampler2D shadowMap1, sampler2D shadowMap2, 
+						   float4 lsPos0, float4 lsPos1, float4 lsPos2,
+						   float4 pssmSplitPoints, float camDepth)
+{
+
+	float shadow;
+	float4 splitColour;
+	// calculate shadow
+	if (camDepth <= pssmSplitPoints.y)
+	{
+		splitColour = float4(0.3, 0.0, 0, 0);
+		shadow = calcSimpleShadow(shadowMap0, lsPos0);
+	}
+	else if (camDepth <= pssmSplitPoints.z)
+	{
+		splitColour = float4(0, 0.3, 0, 0);
+		shadow = calcSimpleShadow(shadowMap1, lsPos1);
+	}
+	else
+	{
+		splitColour = float4(0.0, 0.0, 0.3, 0);
+		shadow = calcSimpleShadow(shadowMap2, lsPos2);
+	}
+
+	return shadow;
+}
+
+
+
+float3 calcPSSMDebugShadow(sampler2D shadowMap0, sampler2D shadowMap1, sampler2D shadowMap2, 
+						   float4 lsPos0, float4 lsPos1, float4 lsPos2,
+						   float invShadowmapSize0, float invShadowmapSize1, float invShadowmapSize2,
+						   float4 pssmSplitPoints, float camDepth)
+{
+
+	float4 splitColour;
+	// calculate shadow
+	if (camDepth <= pssmSplitPoints.y)
+	{
+		//splitColour = float4(0.3, 0.0, 0, 0);
+		//splitColour = lsPos0 / lsPos0.w;
+		splitColour.rgb = tex2Dproj(shadowMap0, lsPos0).x;
+	}
+	else if (camDepth <= pssmSplitPoints.z)
+	{
+		//splitColour = float4(0, 0.3, 0, 0);
+		//splitColour = lsPos1 / lsPos1.w;
+		splitColour.rgb = tex2Dproj(shadowMap1, lsPos1).x;
+	}
+	else
+	{
+		//splitColour = float4(0.0, 0.0, 0.3, 0);
+		//splitColour = lsPos2 / lsPos2.w;
+		splitColour.rgb = tex2Dproj(shadowMap2, lsPos2).x;
+	}
+
+	return splitColour.rgb;
+}

resources/materials/instancing/shadows.glsl

+/* Copyright Torus Knot Software Ltd 2012
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Adapted by Matias N. Goldberg (Dark Sylinc) to GLSL based on the Cg file shadows.cg
+*/
+
+// Simple PCF 
+// Number of samples in one dimension (square for total samples)
+#define NUM_SHADOW_SAMPLES_1D 2.0
+#define SHADOW_FILTER_SCALE 1.0
+
+#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D
+
+vec4 offsetSample(vec4 uv, vec2 offset, float invMapSize)
+{
+	return vec4(uv.xy + offset * invMapSize * uv.w, uv.z, uv.w);
+}
+
+float calcDepthShadow(sampler2DShadow shadowMap, vec4 uv, float invShadowMapSize)
+{
+	// 4-sample PCF
+	
+	float shadow = 0.0;
+	float offset = (NUM_SHADOW_SAMPLES_1D/2.0 - 0.5) * SHADOW_FILTER_SCALE;
+	for (float y = -offset; y <= offset; y += SHADOW_FILTER_SCALE)
+		for (float x = -offset; x <= offset; x += SHADOW_FILTER_SCALE)
+		{
+			float depth = shadow2DProj(shadowMap, offsetSample(uv, vec2(x, y), invShadowMapSize)).x;
+			if (depth >= 1.0 || depth >= uv.z)
+				shadow += 1.0;
+		}
+
+	shadow /= SHADOW_SAMPLES;
+
+	return shadow;
+}
+
+
+float calcSimpleShadow(sampler2DShadow shadowMap, vec4 shadowMapPos)
+{
+	return shadow2DProj(shadowMap, shadowMapPos).x;
+}
+
+float calcPSSMDepthShadow(sampler2DShadow shadowMap0, sampler2DShadow shadowMap1, sampler2DShadow shadowMap2, 
+						   vec4 lsPos0, vec4 lsPos1, vec4 lsPos2,
+						   float invShadowmapSize0, float invShadowmapSize1, float invShadowmapSize2,
+						   vec4 pssmSplitPoints, float camDepth)
+{
+
+	float shadow;
+	vec4 splitColour;
+	// calculate shadow
+	if (camDepth <= pssmSplitPoints.y)
+	{
+		splitColour = vec4(0.3, 0.0, 0, 0);
+		shadow = calcDepthShadow(shadowMap0, lsPos0, invShadowmapSize0);
+	}
+	else if (camDepth <= pssmSplitPoints.z)
+	{
+		splitColour = vec4(0, 0.3, 0, 0);
+		shadow = calcDepthShadow(shadowMap1, lsPos1, invShadowmapSize1);
+	}
+	else
+	{
+		splitColour = vec4(0.0, 0.0, 0.3, 0);
+		shadow = calcDepthShadow(shadowMap2, lsPos2, invShadowmapSize2);
+	}
+
+	return shadow;
+}
+
+float calcPSSMSimpleShadow(sampler2DShadow shadowMap0, sampler2DShadow shadowMap1, sampler2DShadow shadowMap2, 
+						   vec4 lsPos0, vec4 lsPos1, vec4 lsPos2,
+						   vec4 pssmSplitPoints, float camDepth)
+{
+
+	float shadow;
+	vec4 splitColour;
+	// calculate shadow
+	if (camDepth <= pssmSplitPoints.y)
+	{
+		splitColour = vec4(0.3, 0.0, 0, 0);
+		shadow = calcSimpleShadow(shadowMap0, lsPos0);
+	}
+	else if (camDepth <= pssmSplitPoints.z)
+	{
+		splitColour = vec4(0, 0.3, 0, 0);
+		shadow = calcSimpleShadow(shadowMap1, lsPos1);
+	}
+	else
+	{
+		splitColour = vec4(0.0, 0.0, 0.3, 0);
+		shadow = calcSimpleShadow(shadowMap2, lsPos2);
+	}
+
+	return shadow;
+}
+
+
+
+vec3 calcPSSMDebugShadow(sampler2DShadow shadowMap0, sampler2DShadow shadowMap1, sampler2DShadow shadowMap2, 
+						   vec4 lsPos0, vec4 lsPos1, vec4 lsPos2,
+						   float invShadowmapSize0, float invShadowmapSize1, float invShadowmapSize2,
+						   vec4 pssmSplitPoints, float camDepth)
+{
+
+	vec4 splitColour;
+	// calculate shadow
+	if (camDepth <= pssmSplitPoints.y)
+	{
+		//splitColour = vec4(0.3, 0.0, 0, 0);
+		//splitColour = lsPos0 / lsPos0.w;
+		splitColour.rgb = shadow2DProj(shadowMap0, lsPos0).xxx;
+	}
+	else if (camDepth <= pssmSplitPoints.z)
+	{
+		//splitColour = vec4(0, 0.3, 0, 0);
+		//splitColour = lsPos1 / lsPos1.w;
+		splitColour.rgb = shadow2DProj(shadowMap1, lsPos1).xxx;
+	}
+	else
+	{
+		//splitColour = vec4(0.0, 0.0, 0.3, 0);
+		//splitColour = lsPos2 / lsPos2.w;
+		splitColour.rgb = shadow2DProj(shadowMap2, lsPos2).xxx;
+	}
+
+	return splitColour.rgb;
+}

resources/materials/r2skin.jpg

Added
New image

resources/meshes/robot.mesh

Binary file added.

resources/meshes/robot.skeleton

Binary file added.

src/AnimateableCharacter.cpp

 AnimateableCharacter::AnimateableCharacter(Ogre::String name, Ogre::SceneManager *sceneMgr, OgreDetourCrowd* detourCrowd, bool debugDraw, Ogre::Vector3 position)
     : Character(name, sceneMgr, detourCrowd, position),
     mAnimState(NULL),
+    mEnt(NULL),
     mAnimSpeedScale(1),
-    mDebugNode(NULL)
+    mDebugNode(NULL),
+    mDebugDraw(debugDraw)
 {
     mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(name+"Node");
     mEnt = sceneMgr->createEntity(name, "Gamechar-male.mesh");
 
+    // Set looking direction for this model
+    setRelativeLookingDirection( -Ogre::Vector3::UNIT_Z );
+
     // Assign random texture
     int i = (int)Ogre::Math::RangeRandom(0,14);
     if (i > 13)
     mDebugNode->setInheritScale(false);
     mDebugNode->setScale(agentRadius*2, agentHeight, agentRadius*2);
     debugEnt->setQueryFlags(OgreRecastApplication::DEFAULT_MASK);   // Exclude from ray queries
-    mDebugNode->setVisible(debugDraw);
+    mDebugNode->setVisible(mDebugDraw);
 }
 
 void AnimateableCharacter::update(Ogre::Real timeSinceLastFrame)
 
         if(speed > 0/*0.8*/) {  // Avoid jitter (TODO keep this?)
             // Rotate to look in walking direction
-            Ogre::Vector3 src = mNode->getOrientation() * -(Ogre::Vector3::UNIT_Z);    // Character looks in negative Z direction
+            Ogre::Vector3 src = getLookingDirection();
             src.y = 0;  // Ignore y direction
 
             velocity.y = 0;
     }
 }
 
+Ogre::Entity* AnimateableCharacter::getEntity()
+{
+    return mEnt;
+}
+
 void AnimateableCharacter::setDebugVisibility(bool visible)
 {
-    mDebugNode->setVisible(visible);
+    mDebugDraw = visible;
+    mDebugNode->setVisible(mDebugDraw);
 }
+
+bool AnimateableCharacter::getDebugVisibility()
+{
+    return mDebugDraw;
+}
+
+void AnimateableCharacter::show()
+{
+    Character::show();
+
+    mDebugNode->setVisible(getDebugVisibility());
+}

src/Character.cpp

 #include "OgreRecastApplication.h"
 
 // Remember: this value should be squared and should be strictly >0 !
-const Ogre::Real Character::DESTINATION_RADIUS = 0.1 * 0.1;
+const Ogre::Real Character::DESTINATION_RADIUS = 1 * 1;
     // TODO it's also possible to calculate this relative to the agent radius
 
 
     : mName(name),
     mSceneMgr(sceneMgr),
     mDetourCrowd(detourCrowd),
-    mEnt(NULL),
     mNode(NULL),
     mAgent(NULL),
     mAgentID(-1),
     mDetourTileCache(NULL),
     mTempObstacle(0),
     mClipTo(0),
-    mRaySceneQuery(0)
+    mRaySceneQuery(0),
+    mLookingDirection(Ogre::Vector3::UNIT_X)
 {
 // TODO maybe create mNode in this consructor instead of in subclasses
     load(position);
     return mAgent;
 }
 
-Ogre::Entity* Character::getEntity()
-{
-    return mEnt;
-}
-
 Ogre::SceneNode* Character::getNode(void) const
 {
     return mNode;
 }
 
 
-bool Character::destinationReached() const
+bool Character::destinationReached()
 {
-    Ogre::Vector3 pos = getPosition();
-    Ogre::Vector3 dest = getDestination();
-    Ogre::Real dist = pos.squaredDistance(dest);
-    Ogre::Real rad = DESTINATION_RADIUS;
-    bool res = dist < DESTINATION_RADIUS;
-    return (getPosition().squaredDistance(getDestination()) <= Character::DESTINATION_RADIUS);
+    if(!isLoaded())
+        return false;
+
+    return mDetourCrowd->destinationReached(getAgent(), Character::DESTINATION_RADIUS);
 }
 
 void Character::setDestination(Ogre::Vector3 destination)
 
 Ogre::Vector3 Character::getLookingDirection()
 {
-    return mNode->getOrientation() * Ogre::Vector3::NEGATIVE_UNIT_Z;    // Character looks in negative Z direction
+    return mNode->getOrientation() * getRelativeLookingDirection();
 }
 
 void Character::moveForward()
 
 void Character::show()
 {
-    if(getNode())
+    if(getNode()) {
         getNode()->setVisible(true);
+    }
 }
 
 void Character::hide()
     if(getNode())
         getNode()->setVisible(false);
 }
+
+Ogre::Vector3 Character::getRelativeLookingDirection()
+{
+    return mLookingDirection;
+}
+
+void Character::setRelativeLookingDirection(Ogre::Vector3 direction)
+{
+    mLookingDirection = direction;
+}

src/InstancedCharacter.cpp

+#include "InstancedCharacter.h"
+#include "OgreRecastApplication.h"  // TODO remove this dependency
+
+InstancedCharacter::InstancedCharacter(Ogre::String name, Ogre::SceneManager* sceneMgr, OgreDetourCrowd* detourCrowd, Ogre::InstanceManager* instanceMgr, bool debugDraw, Ogre::Vector3 position)
+    : Character(name, sceneMgr, detourCrowd, position),
+    mAnimState(NULL),
+    mAnimSpeedScale(1),
+    mDebugNode(NULL),
+    mEnt(NULL),
+    mDebugDraw(debugDraw),
+    mInstanceManager(instanceMgr)
+{
+    mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(name+"Node");
+    mEnt = mInstanceManager->createInstancedEntity("Examples/Instancing/ShaderBased/Robot");
+
+    // Set looking direction for robot model
+    setRelativeLookingDirection( Ogre::Vector3::UNIT_X );
+
+    mEnt->setQueryFlags(OgreRecastApplication::DEFAULT_MASK);   // Exclude from ray queries
+
+    mNode->attachObject(mEnt);
+    mNode->setPosition(position);
+
+    // Assign animation
+    mAnimState= mEnt->getAnimationState("Walk");
+    mAnimState->setEnabled(true);
+    mAnimState->setLoop(true);
+
+    Ogre::Vector3 bBoxSize = mEnt->getBoundingBox().getSize();
+
+    Ogre::Real agentRadius = mDetourCrowd->getAgentRadius();
+    Ogre::Real agentHeight = mDetourCrowd->getAgentHeight();
+
+    // Set Height to match that of agent
+    Ogre::Real scale = agentHeight/bBoxSize.y;
+    mNode->setScale(scale, scale, scale);
+
+    // Set animation speed scaling
+    mAnimSpeedScale = 1;
+
+
+    // Debug draw agent
+    mDebugNode = mNode->createChildSceneNode(name+"AgentDebugNode");
+    mDebugNode->setPosition(0, mDetourCrowd->m_recast->getNavmeshOffsetFromGround(), 0);
+    Ogre::Entity* debugEnt = sceneMgr->createEntity(name+"AgentDebug", "Cylinder.mesh");
+    debugEnt->setMaterialName("Cylinder/Wires/LightBlue");
+    mDebugNode->attachObject(debugEnt);
+    // Set marker scale to size of agent
+    mDebugNode->setInheritScale(false);
+    mDebugNode->setScale(agentRadius*2, agentHeight, agentRadius*2);
+    debugEnt->setQueryFlags(OgreRecastApplication::DEFAULT_MASK);   // Exclude from ray queries
+    mDebugNode->setVisible(mDebugDraw);
+}
+
+void InstancedCharacter::update(Ogre::Real timeSinceLastFrame)
+{
+    updatePosition(timeSinceLastFrame);
+
+    if (mClipTo)
+        clipToTerrainHeight();
+
+    Ogre::Vector3 velocity = getVelocity(); // Current velocity of agent
+    Ogre::Real speed = velocity.length();
+
+    if(speed > 0.15) {
+        mAnimState->setEnabled(true);
+        mAnimState->addTime(mAnimSpeedScale * speed * timeSinceLastFrame);
+
+        if(speed > 0/*0.8*/) {  // Avoid jitter (TODO keep this?)
+            // Rotate to look in walking direction
+            Ogre::Vector3 src = getLookingDirection();
+            src.y = 0;  // Ignore y direction
+
+            velocity.y = 0;
+            velocity.normalise();
+            mNode->rotate(src.getRotationTo(velocity));
+        }
+    } else {    // Assume character has stopped
+        mAnimState->setEnabled(false);
+        mAnimState->setTimePosition(0);
+    }
+}
+
+Ogre::InstancedEntity* InstancedCharacter::getEntity()
+{
+    return mEnt;
+}
+
+void InstancedCharacter ::setDebugVisibility(bool visible)
+{
+    mDebugNode->setVisible(visible);
+}
+
+bool InstancedCharacter::getDebugVisibility()
+{
+    return mDebugDraw;
+}
+
+void InstancedCharacter::show()
+{
+    Character::show();
+
+    mDebugNode->setVisible(getDebugVisibility());
+}

src/OgreDetourCrowd.cpp

     float zeroVel[] = {0,0,0};
     return m_crowd->resetMoveTarget(agentId) && m_crowd->requestMoveVelocity(agentId, zeroVel);
 }
+
+float OgreDetourCrowd::getDistanceToGoal(const dtCrowdAgent* agent, const float maxRange)
+{
+    if (!agent->ncorners)
+        return maxRange;
+
+    const bool endOfPath = (agent->cornerFlags[agent->ncorners-1] & DT_STRAIGHTPATH_END) ? true : false;
+    if (endOfPath)
+        return dtMin(dtVdist2D(agent->npos, &agent->cornerVerts[(agent->ncorners-1)*3]), maxRange);
+
+    return maxRange;
+}
+
+bool OgreDetourCrowd::destinationReached(const dtCrowdAgent* agent, const float maxDistanceFromTarget)
+{
+    return getDistanceToGoal(agent, maxDistanceFromTarget) < maxDistanceFromTarget;
+}

src/TestCharacter.cpp

 #include "OgreRecastApplication.h"
 
 TestCharacter::TestCharacter(Ogre::String name, Ogre::SceneManager *sceneMgr, OgreDetourCrowd* detourCrowd, Ogre::Vector3 position)
-    : Character(name, sceneMgr, detourCrowd, position)
+    : Character(name, sceneMgr, detourCrowd, position),
+    mEnt(NULL)
 {
     // Depict agent as blue cylinder
     mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(name+"Node");
     mEnt->setQueryFlags(OgreRecastApplication::DEFAULT_MASK);   // Exclude from ray queries
 }
 
+Ogre::Entity* TestCharacter::getEntity()
+{
+    return mEnt;
+}
+
 void TestCharacter::update(Ogre::Real timeSinceLastFrame)
 {
     updatePosition(timeSinceLastFrame);