1 #ifndef __ShaderSystemMultiLight_H__
2 #define __ShaderSystemMultiLight_H__
3 
4 #include "SdkSample.h"
5 #include "SegmentedDynamicLightManager.h"
6 #include "RTShaderSRSSegmentedLights.h"
7 #include "OgreControllerManager.h"
8 
9 /*
10 Part of the original guidelines under which the RTSS was created was to emulate the fixed pipeline mechanism as close as possible.
11 Due to this fact and how it was interpreted using multiple lights in RTSS with the default implementation is problematic. Every light
12 requires it's own line in the shader. Every time an object receives a different amount of lights the shader for is invalidated and  lights
13 recompiled. Amount of is also limited by the amount of const registers a shader supports.
14 
15 The following example shows a different approach to rendering lights in RTSS. A few points on this system
16 	- Only one directional light is supported.
17 	- Point lights and spot lights are handled through the same code.
18 	- Light attenuation is only controlled by range. all other parameters are ignored (to produce more efficient shader programs)
19 	- point light specular effect is not calculated (to produce more faster shader programs). If any one wants to add it feel free.
20 	- Large amount of lights can be supported. Limited currently by the size of the texture used to send the light information to the
21 		shader (currently set to a 9x9 grid. each grid cell can contain 32 lights).
22 	- No need to recompile the shader when the number of lights on an object changes
23 	- Sample requires shader model 3 or higher to run in order
24 	- The world is divided into a grid of 9x9 cells (can be easily increased). Each cell receives it's own list of lights appropriate
25 		only for it. This can be increased depending on your situation.
26 	- The information of the lights in the grid is transferred onto a texture. Which is sent to the shader.
27 	- The list of lights is iterated over in the shader through a dynamic loop.
28 
29 
30 Note:
31 This code was somewhat inspired by Kojack's "Tons of street lights" (http://www.ogre3d.org/forums/viewtopic.php?t=48412) idea. One of
32 the more innovative ideas I've seen of late.
33 
34 */
35 
36 using namespace Ogre;
37 using namespace OgreBites;
38 
39 const uint8 cPriorityMain = 50;
40 const uint8 cPriorityQuery = 51;
41 const uint8 cPriorityLights = 55;
42 const unsigned int cInitialLightCount = 3;
43 const String DEBUG_MODE_CHECKBOX = "DebugMode";
44 const String NUM_OF_LIGHTS_SLIDER = "NumOfLights";
45 const String TWIRL_LIGHTS_CHECKBOX = "TwirlLights";
46 
47 
48 class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample
49 {
50 public:
51 
Sample_ShaderSystemMultiLight()52 	Sample_ShaderSystemMultiLight() :
53         mTwirlLights(false),
54 		mSRSSegLightFactory(NULL),
55         mPathNameGen("RTPath")
56 	{
57 		mInfo["Title"] = "ShaderSystem - Multi Light";
58 		mInfo["Description"] = "Shows a possible way to support a large varying amount of spot lights in the RTSS using a relatively simple system."
59 			"Note in debug mode green and red lines show the light grid. Blue shows the amount of lights processed per grid position.";
60 		mInfo["Thumbnail"] = "thumb_shadersystemmultilight.png";
61 		mInfo["Category"] = "Lighting";
62 
63 	}
64 
~Sample_ShaderSystemMultiLight()65 	~Sample_ShaderSystemMultiLight()
66 	{
67 
68 	}
69 
_shutdown()70 	virtual void _shutdown()
71 	{
72 		delete SegmentedDynamicLightManager::getSingletonPtr();
73 
74 		RTShader::RenderState* pMainRenderState =
75 			RTShader::ShaderGenerator::getSingleton().createOrRetrieveRenderState(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME).first;
76 		pMainRenderState->reset();
77 
78 		if (mSRSSegLightFactory)
79 		{
80 			RTShader::ShaderGenerator::getSingleton().removeAllShaderBasedTechniques();
81 			RTShader::ShaderGenerator::getSingleton().removeSubRenderStateFactory(mSRSSegLightFactory);
82 			delete mSRSSegLightFactory;
83 			mSRSSegLightFactory = NULL;
84 		}
85 
86         while (mLights.size())
87         {
88             removeSpotLight();
89         }
90 
91 		SdkSample::_shutdown();
92 	}
93 
frameRenderingQueued(const FrameEvent & evt)94     bool frameRenderingQueued(const FrameEvent& evt)
95     {
96 		// Move the lights along their paths
97 		for(size_t i = 0 ; i < mLights.size() ; ++i)
98 		{
99 			mLights[i].animState->addTime(evt.timeSinceLastFrame);
100 			if (mTwirlLights)
101 			{
102 				mLights[i].light->setDirection(
103 					Quaternion(Degree(ControllerManager::getSingleton().getElapsedTime() * 150 + 360 * i / (float)mLights.size()), Vector3::UNIT_Y) *
104 					Vector3(0,-1,-1).normalisedCopy());
105 			}
106 			else
107 			{
108 				mLights[i].light->setDirection(Vector3::NEGATIVE_UNIT_Y);
109 			}
110 		}
111 
112 
113 		return SdkSample::frameRenderingQueued(evt);   // don't forget the parent class updates!
114     }
115 
116 protected:
117 
testCapabilities(const RenderSystemCapabilities * caps)118     void testCapabilities( const RenderSystemCapabilities* caps )
119     {
120         if (!Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_3_0") &&
121 			!Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_4_0") &&
122 			!Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_4_1") &&
123 			!Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_5_0") &&
124 			!Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("glsl"))
125 		{
126             OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "This sample uses dynamic loops in Cg or GLSL type shader language, your graphic card must support Shader Profile 3 or above."
127                 " You cannot run this sample. Sorry!", "Sample_ShaderSystemMultiLight::testCapabilities");
128         }
129     }
130 
setupContent()131 	void setupContent()
132 	{
133 		mTrayMgr->createThickSlider(TL_BOTTOM, NUM_OF_LIGHTS_SLIDER, "Num of lights", 240, 80, 0, 64, 65)->setValue(cInitialLightCount, false);
134 		mTrayMgr->createCheckBox(TL_BOTTOM, TWIRL_LIGHTS_CHECKBOX, "Twirl Lights", 240)->setChecked(false, false);
135 		mTrayMgr->createCheckBox(TL_BOTTOM, DEBUG_MODE_CHECKBOX, "Show Grid", 240)->setChecked(false, false);
136 
137 		// Set our camera to orbit around the origin at a suitable distance
138 		mCamera->setPosition(0, 100, 600);
139 
140 		mTrayMgr->showCursor();
141 
142 		// create a floor mesh resource
143 		MeshManager::getSingleton().createPlane("floor", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
144 			Plane(Vector3::UNIT_Y, -30), 1000, 1000, 10, 10, true, 1, 8, 8, Vector3::UNIT_Z);
145 
146 		// create a floor entity, give it a material, and place it at the origin
147         Entity* floor = mSceneMgr->createEntity("Floor", "floor");
148         floor->setMaterialName("Examples/BumpyMetal");
149         mSceneMgr->getRootSceneNode()->attachObject(floor);
150 
151 		// Create an ogre head and place it at the origin
152 		Entity* head = mSceneMgr->createEntity("Head", "ogrehead.mesh");
153 		head->setRenderQueueGroup(cPriorityMain);
154 		mSceneMgr->getRootSceneNode()->attachObject(head);
155 
156 		setupShaderGenerator();
157 
158 		setupLights();
159 	}
160 
setupShaderGenerator()161 	void setupShaderGenerator()
162 	{
163 		new SegmentedDynamicLightManager;
164 
165 		SegmentedDynamicLightManager::getSingleton().setSceneManager(mSceneMgr);
166 
167 		RTShader::ShaderGenerator* mGen = RTShader::ShaderGenerator::getSingletonPtr();
168 
169 		RTShader::RenderState* pMainRenderState =
170             mGen->createOrRetrieveRenderState(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME).first;
171 		pMainRenderState->reset();
172 
173 		// If we are using segmented lighting, no auto light update required. (prevent constant invalidation)
174 		pMainRenderState->setLightCountAutoUpdate(false);
175 
176 		mSRSSegLightFactory = new RTShaderSRSSegmentedLightsFactory;
177 		mGen->addSubRenderStateFactory(mSRSSegLightFactory);
178 		pMainRenderState->addTemplateSubRenderState(
179 			mGen->createSubRenderState(RTShaderSRSSegmentedLights::Type));
180 
181 
182 		mGen->invalidateScheme(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
183 
184 		// Make this viewport work with shader generator scheme.
185 		mViewport->setMaterialScheme(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
186 	}
187 
188 
setupLights()189 	void setupLights()
190 	{
191 		mSceneMgr->setAmbientLight(ColourValue(0.1, 0.1, 0.1));
192 		// set the single directional light
193 		Light* light = mSceneMgr->createLight();
194 		light->setType(Light::LT_DIRECTIONAL);
195 		light->setDirection(Vector3(-1,-1,0).normalisedCopy());
196 		light->setDiffuseColour(ColourValue(0.1, 0.1, 0.1));
197 		light->setCastShadows(false);
198 
199 		for(unsigned int i = 0 ; i < cInitialLightCount ; ++i)
200 		{
201 			addSpotLight();
202 		}
203 	}
204 
addSpotLight()205 	void addSpotLight()
206 	{
207 		LightState state;
208 
209 		// Create a light node
210 		state.node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(50, 30, 0));
211 
212 		String animName = mPathNameGen.generate();
213 		// Create a 14 second animation with spline interpolation
214 
215 		const int animPoints = 5;
216 		const int animTimeBetweenPoints = 4;
217 		state.anim = mSceneMgr->createAnimation(animName, animPoints * animTimeBetweenPoints);
218 		state.anim->setInterpolationMode(Animation::IM_SPLINE);
219 
220 		state.track = state.anim->createNodeTrack(1, state.node);  // Create a node track for our animation
221 
222 		// Enter keyframes for our track to define a path for the light to follow
223 		Vector3 firstFramePos;
224 		for(int i = 0 ; i <= animPoints ; ++i)
225 		{
226 			Vector3 framePos(rand01() * 900 - 500, 10 + rand01() * 100, rand01() * 900 - 500);
227 			if (i == 0)
228 			{
229 				firstFramePos = framePos;
230 			}
231 			if (i == animPoints)
232 			{
233 				framePos = firstFramePos;
234 			}
235 			state.track->createNodeKeyFrame(i * animTimeBetweenPoints)->setTranslate(framePos);
236 		}
237 
238 
239 		ColourValue lightColor(rand01(), rand01(), rand01());
240 		float complement = 1 - std::max<float>(std::max<float>(lightColor.r, lightColor.g), lightColor.b);
241 		lightColor.r += complement;
242 		lightColor.g += complement;
243 		lightColor.b += complement;
244 
245 		// Create an animation state from the animation and enable it
246 		state.animState = mSceneMgr->createAnimationState(animName);
247 		state.animState->setEnabled(true);
248 
249 		// Attach a light with the same colour to the light node
250 		state.light = mSceneMgr->createLight();
251 		state.light->setCastShadows(false);
252 		state.light->setType(mLights.size() % 10 ? Light::LT_SPOTLIGHT : Light::LT_POINT);
253 		state.light->setDirection(Vector3::NEGATIVE_UNIT_Y);
254 		state.light->setAttenuation(200,0,0,0);
255 		state.light->setDiffuseColour(lightColor);
256 		state.node->attachObject(state.light);
257 
258 		// Attach a flare with the same colour to the light node
259 		state.bbs = mSceneMgr->createBillboardSet(1);
260 		Billboard* bb = state.bbs->createBillboard(Vector3::ZERO, lightColor);
261 		bb->setColour(lightColor);
262 		state.bbs->setMaterialName("Examples/Flare");
263 		state.bbs->setRenderQueueGroup(cPriorityLights);
264 		state.node->attachObject(state.bbs);
265 
266 		mLights.push_back(state);
267 	}
268 
rand01()269 	float rand01()
270 	{
271 		return (abs(rand()) % 1000) / 1000.0f;
272 	}
273 
setDebugModeState(bool state)274 	void setDebugModeState(bool state)
275 	{
276 		bool needInvalidate = SegmentedDynamicLightManager::getSingleton().setDebugMode(state);
277 		if (needInvalidate)
278 		{
279 			RTShader::ShaderGenerator::getSingleton().invalidateScheme(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
280 		}
281 	}
282 
283 	//--------------------------------------------------------------------------
sliderMoved(Slider * slider)284 	void sliderMoved(Slider* slider)
285 	{
286 		if (slider->getName() == NUM_OF_LIGHTS_SLIDER)
287 		{
288 			size_t numOfLights = (size_t)slider->getValue();
289 
290 			while (mLights.size() < numOfLights)
291 			{
292 				addSpotLight();
293 			}
294 
295 			while (numOfLights < mLights.size())
296 			{
297 				removeSpotLight();
298 			}
299 		}
300 	}
301 
removeSpotLight()302 	void removeSpotLight()
303 	{
304 		if (!mLights.empty())
305 		{
306 			LightState& state = mLights[mLights.size() - 1];
307 
308 			// Delete the nodes
309 			mSceneMgr->destroyBillboardSet(state.bbs);
310 			mSceneMgr->destroyLight(state.light);
311 			mSceneMgr->destroyAnimationState(state.anim->getName());
312 			mSceneMgr->destroyAnimation(state.anim->getName());
313 			mSceneMgr->destroySceneNode(state.node);
314 
315 
316 			mLights.resize(mLights.size() - 1);
317 		}
318 	}
319 
320 
checkBoxToggled(CheckBox * box)321 	void checkBoxToggled(CheckBox* box)
322 	{
323 		const String& cbName = box->getName();
324 
325 		if (cbName == DEBUG_MODE_CHECKBOX)
326 		{
327 			setDebugModeState(box->isChecked());
328 		}
329 		if (cbName == TWIRL_LIGHTS_CHECKBOX)
330 		{
331 			mTwirlLights = box->isChecked();
332 		}
333 	}
334 
335 	#if (OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS) && (OGRE_PLATFORM != OGRE_PLATFORM_ANDROID)
336 
337 	//-----------------------------------------------------------------------
mousePressed(const OIS::MouseEvent & evt,OIS::MouseButtonID id)338 	bool mousePressed( const OIS::MouseEvent& evt, OIS::MouseButtonID id )
339 	{
340 		if (mTrayMgr->injectMouseDown(evt, id))
341 			return true;
342 		if (id == OIS::MB_Left)
343 			mTrayMgr->hideCursor();  // hide the cursor if user left-clicks in the scene
344 
345 		return true;
346 	}
347 
348 	//-----------------------------------------------------------------------
mouseReleased(const OIS::MouseEvent & evt,OIS::MouseButtonID id)349 	bool mouseReleased( const OIS::MouseEvent& evt, OIS::MouseButtonID id )
350 	{
351 		if (mTrayMgr->injectMouseUp(evt, id))
352 			return true;
353 		if (id == OIS::MB_Left)
354 			mTrayMgr->showCursor();  // unhide the cursor if user lets go of LMB
355 
356 		return true;
357 	}
358 
359 	//-----------------------------------------------------------------------
mouseMoved(const OIS::MouseEvent & evt)360 	bool mouseMoved( const OIS::MouseEvent& evt )
361 	{
362 		// only rotate the camera if cursor is hidden
363 		if (mTrayMgr->isCursorVisible())
364 			mTrayMgr->injectMouseMove(evt);
365 		else
366 			mCameraMan->injectMouseMove(evt);
367 
368 
369 		return true;
370 	}
371 	#endif
372 
373 
374 
375 private:
376 
377 	struct LightState
378 	{
379 		SceneNode* node;
380 		Animation* anim;
381 		NodeAnimationTrack* track;
382 		AnimationState* animState;
383 		Light* light;
384 		BillboardSet* bbs;
385 	};
386 
387 	typedef Ogre::vector<LightState>::type VecLights;
388 	VecLights mLights;
389 	bool mTwirlLights;
390 
391 	RTShaderSRSSegmentedLightsFactory* mSRSSegLightFactory;
392 
393 	NameGenerator mPathNameGen;
394 };
395 
396 #endif
397