1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 Also see acknowledgements in Readme.html
9
10 You may use this sample code for anything you like, it is not covered by the
11 same license as the rest of the engine.
12 -----------------------------------------------------------------------------
13 */
14
15 #include "DLight.h"
16
17 #include "OgreHardwareBufferManager.h"
18 #include "OgreCamera.h"
19 #include "OgreSceneNode.h"
20 #include "OgreLight.h"
21 #include "GeomUtils.h"
22 #include "LightMaterialGenerator.h"
23 #include "OgreTechnique.h"
24 #include "OgreSceneManager.h"
25
26 #define ENABLE_BIT(mask, flag) (mask) |= (flag)
27 #define DISABLE_BIT(mask, flag) (mask) &= ~(flag)
28
29 using namespace Ogre;
30 //-----------------------------------------------------------------------
DLight(MaterialGenerator * sys,Ogre::Light * parentLight)31 DLight::DLight(MaterialGenerator *sys, Ogre::Light* parentLight):
32 mParentLight(parentLight), bIgnoreWorld(false), mGenerator(sys), mPermutation(0)
33 {
34 // Set up geometry
35 // Allocate render operation
36 mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST;
37 mRenderOp.indexData = 0;
38 mRenderOp.vertexData = 0;
39 mRenderOp.useIndexes = true;
40
41 updateFromParent();
42 }
43 //-----------------------------------------------------------------------
~DLight()44 DLight::~DLight()
45 {
46 // need to release IndexData and vertexData created for renderable
47 delete mRenderOp.indexData;
48 delete mRenderOp.vertexData;
49 }
50 //-----------------------------------------------------------------------
setAttenuation(float c,float b,float a)51 void DLight::setAttenuation(float c, float b, float a)
52 {
53 // Set Attenuation parameter to shader
54 //setCustomParameter(3, Vector4(c, b, a, 0));
55 float outerRadius = mParentLight->getAttenuationRange();
56 /// There is attenuation? Set material accordingly
57 if(c != 1.0f || b != 0.0f || a != 0.0f)
58 {
59 ENABLE_BIT(mPermutation, LightMaterialGenerator::MI_ATTENUATED);
60 if (mParentLight->getType() == Light::LT_POINT)
61 {
62 //// Calculate radius from Attenuation
63 int threshold_level = 10;// difference of 10-15 levels deemed unnoticeable
64 float threshold = 1.0f/((float)threshold_level/256.0f);
65
66 //// Use quadratic formula to determine outer radius
67 c = c-threshold;
68 float d=sqrt(b*b-4*a*c);
69 outerRadius = (-2*c)/(b+d);
70 outerRadius *= 1.2;
71 }
72 }
73 else
74 {
75 DISABLE_BIT(mPermutation,LightMaterialGenerator::MI_ATTENUATED);
76 }
77
78 rebuildGeometry(outerRadius);
79 }
80 //-----------------------------------------------------------------------
setSpecularColour(const ColourValue & col)81 void DLight::setSpecularColour(const ColourValue &col)
82 {
83 //setCustomParameter(2, Vector4(col.r, col.g, col.b, col.a));
84 /// There is a specular component? Set material accordingly
85
86 if(col.r != 0.0f || col.g != 0.0f || col.b != 0.0f)
87 ENABLE_BIT(mPermutation,LightMaterialGenerator::MI_SPECULAR);
88 else
89 DISABLE_BIT(mPermutation,LightMaterialGenerator::MI_SPECULAR);
90
91 }
92 //-----------------------------------------------------------------------
rebuildGeometry(float radius)93 void DLight::rebuildGeometry(float radius)
94 {
95 //Disable all 3 bits
96 DISABLE_BIT(mPermutation, LightMaterialGenerator::MI_POINT);
97 DISABLE_BIT(mPermutation, LightMaterialGenerator::MI_SPOTLIGHT);
98 DISABLE_BIT(mPermutation, LightMaterialGenerator::MI_DIRECTIONAL);
99
100 switch (mParentLight->getType())
101 {
102 case Light::LT_DIRECTIONAL:
103 createRectangle2D();
104 ENABLE_BIT(mPermutation,LightMaterialGenerator::MI_DIRECTIONAL);
105 break;
106 case Light::LT_POINT:
107 /// XXX some more intelligent expression for rings and segments
108 createSphere(radius, 10, 10);
109 ENABLE_BIT(mPermutation,LightMaterialGenerator::MI_POINT);
110 break;
111 case Light::LT_SPOTLIGHT:
112 Real height = mParentLight->getAttenuationRange();
113 Radian coneRadiusAngle = mParentLight->getSpotlightOuterAngle() / 2;
114 Real rad = Math::Tan(coneRadiusAngle) * height;
115 createCone(rad, height, 20);
116 ENABLE_BIT(mPermutation,LightMaterialGenerator::MI_SPOTLIGHT);
117 break;
118 }
119 }
120 //-----------------------------------------------------------------------
createRectangle2D()121 void DLight::createRectangle2D()
122 {
123 /// XXX this RenderOp should really be re-used between DLight objects,
124 /// not generated every time
125 delete mRenderOp.vertexData;
126 delete mRenderOp.indexData;
127
128 mRenderOp.vertexData = new VertexData();
129 mRenderOp.indexData = 0;
130
131 GeomUtils::createQuad(mRenderOp.vertexData);
132
133 mRenderOp.operationType = RenderOperation::OT_TRIANGLE_STRIP;
134 mRenderOp.useIndexes = false;
135
136 // Set bounding
137 setBoundingBox(AxisAlignedBox(-10000,-10000,-10000,10000,10000,10000));
138 mRadius = 15000;
139 bIgnoreWorld = true;
140 }
141 //-----------------------------------------------------------------------
createSphere(float radius,int nRings,int nSegments)142 void DLight::createSphere(float radius, int nRings, int nSegments)
143 {
144 delete mRenderOp.vertexData;
145 delete mRenderOp.indexData;
146 mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST;
147 mRenderOp.indexData = new IndexData();
148 mRenderOp.vertexData = new VertexData();
149 mRenderOp.useIndexes = true;
150
151 GeomUtils::createSphere(mRenderOp.vertexData, mRenderOp.indexData
152 , radius
153 , nRings, nSegments
154 , false // no normals
155 , false // no texture coordinates
156 );
157
158 // Set bounding box and sphere
159 setBoundingBox( AxisAlignedBox( Vector3(-radius, -radius, -radius), Vector3(radius, radius, radius) ) );
160 mRadius = radius;
161 bIgnoreWorld = false;
162 }
163 //-----------------------------------------------------------------------
createCone(float radius,float height,int nVerticesInBase)164 void DLight::createCone(float radius, float height, int nVerticesInBase)
165 {
166 delete mRenderOp.vertexData;
167 delete mRenderOp.indexData;
168 mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST;
169 mRenderOp.indexData = new IndexData();
170 mRenderOp.vertexData = new VertexData();
171 mRenderOp.useIndexes = true;
172
173 GeomUtils::createCone(mRenderOp.vertexData, mRenderOp.indexData
174 , radius
175 , height, nVerticesInBase);
176
177 // Set bounding box and sphere
178 setBoundingBox( AxisAlignedBox(
179 Vector3(-radius, 0, -radius),
180 Vector3(radius, height, radius) ) );
181
182 mRadius = radius;
183 bIgnoreWorld = false;
184 }
185 //-----------------------------------------------------------------------
getBoundingRadius(void) const186 Real DLight::getBoundingRadius(void) const
187 {
188 return mRadius;
189 }
190 //-----------------------------------------------------------------------
getSquaredViewDepth(const Camera * cam) const191 Real DLight::getSquaredViewDepth(const Camera* cam) const
192 {
193 if(bIgnoreWorld)
194 {
195 return 0.0f;
196 }
197 else
198 {
199 Vector3 dist = cam->getDerivedPosition() - getParentSceneNode()->_getDerivedPosition();
200 return dist.squaredLength();
201 }
202 }
203 //-----------------------------------------------------------------------
getMaterial(void) const204 const MaterialPtr& DLight::getMaterial(void) const
205 {
206 return mGenerator->getMaterial(mPermutation);
207 }
208 //-----------------------------------------------------------------------
getWorldTransforms(Matrix4 * xform) const209 void DLight::getWorldTransforms(Matrix4* xform) const
210 {
211 if (mParentLight->getType() == Light::LT_SPOTLIGHT)
212 {
213 Quaternion quat = Vector3::UNIT_Y.getRotationTo(mParentLight->getDerivedDirection());
214 xform->makeTransform(mParentLight->getDerivedPosition(),
215 Vector3::UNIT_SCALE, quat);
216 }
217 else
218 {
219 xform->makeTransform(mParentLight->getDerivedPosition(),
220 Vector3::UNIT_SCALE, Quaternion::IDENTITY);
221 }
222
223 }
224 //-----------------------------------------------------------------------
updateFromParent()225 void DLight::updateFromParent()
226 {
227 //TODO : Don't do this unless something changed
228 setAttenuation(mParentLight->getAttenuationConstant(),
229 mParentLight->getAttenuationLinear(), mParentLight->getAttenuationQuadric());
230 setSpecularColour(mParentLight->getSpecularColour());
231
232 if (getCastChadows())
233 {
234 ENABLE_BIT(mPermutation,LightMaterialGenerator::MI_SHADOW_CASTER);
235 }
236 else
237 {
238 DISABLE_BIT(mPermutation, LightMaterialGenerator::MI_SHADOW_CASTER);
239 }
240 }
241 //-----------------------------------------------------------------------
isCameraInsideLight(Ogre::Camera * camera)242 bool DLight::isCameraInsideLight(Ogre::Camera* camera)
243 {
244 switch (mParentLight->getType())
245 {
246 case Ogre::Light::LT_DIRECTIONAL:
247 return false;
248 case Ogre::Light::LT_POINT:
249 {
250 Ogre::Real distanceFromLight = camera->getDerivedPosition()
251 .distance(mParentLight->getDerivedPosition());
252 //Small epsilon fix to account for the fact that we aren't a true sphere.
253 return distanceFromLight <= mRadius + camera->getNearClipDistance() + 0.1;
254 }
255 case Ogre::Light::LT_SPOTLIGHT:
256 {
257 Ogre::Vector3 lightPos = mParentLight->getDerivedPosition();
258 Ogre::Vector3 lightDir = mParentLight->getDerivedDirection();
259 Ogre::Radian attAngle = mParentLight->getSpotlightOuterAngle();
260
261 //Extend the analytic cone's radius by the near clip range by moving its tip accordingly.
262 //Some trigonometry needed here.
263 Ogre::Vector3 clipRangeFix = -lightDir * (camera->getNearClipDistance() / Ogre::Math::Tan(attAngle/2));
264 lightPos = lightPos + clipRangeFix;
265
266 Ogre::Vector3 lightToCamDir = camera->getDerivedPosition() - lightPos;
267 Ogre::Real distanceFromLight = lightToCamDir.normalise();
268
269 Ogre::Real cosAngle = lightToCamDir.dotProduct(lightDir);
270 Ogre::Radian angle = Ogre::Math::ACos(cosAngle);
271 //Check whether we will see the cone from our current POV.
272 return (distanceFromLight <= (mParentLight->getAttenuationRange() / cosAngle + clipRangeFix.length()))
273 && (angle <= attAngle);
274 }
275 default:
276 //Please the compiler
277 return false;
278 }
279 }
280 //-----------------------------------------------------------------------
getCastChadows() const281 bool DLight::getCastChadows() const
282 {
283 return
284 mParentLight->_getManager()->isShadowTechniqueInUse() &&
285 mParentLight->getCastShadows() &&
286 (mParentLight->getType() == Light::LT_DIRECTIONAL || mParentLight->getType() == Light::LT_SPOTLIGHT);
287 }
288 //-----------------------------------------------------------------------
updateFromCamera(Ogre::Camera * camera)289 void DLight::updateFromCamera(Ogre::Camera* camera)
290 {
291 //Set shader params
292 const Ogre::MaterialPtr& mat = getMaterial();
293 if (!mat->isLoaded())
294 {
295 mat->load();
296 }
297 Ogre::Technique* tech = mat->getBestTechnique();
298 Ogre::Vector3 farCorner = camera->getViewMatrix(true) * camera->getWorldSpaceCorners()[4];
299
300 for (unsigned short i=0; i<tech->getNumPasses(); i++)
301 {
302 Ogre::Pass* pass = tech->getPass(i);
303 // get the vertex shader parameters
304 Ogre::GpuProgramParametersSharedPtr params = pass->getVertexProgramParameters();
305 // set the camera's far-top-right corner
306 if (params->_findNamedConstantDefinition("farCorner"))
307 params->setNamedConstant("farCorner", farCorner);
308
309 params = pass->getFragmentProgramParameters();
310 if (params->_findNamedConstantDefinition("farCorner"))
311 params->setNamedConstant("farCorner", farCorner);
312
313 //If inside light geometry, render back faces with CMPF_GREATER, otherwise normally
314 if (mParentLight->getType() == Ogre::Light::LT_DIRECTIONAL)
315 {
316 pass->setCullingMode(Ogre::CULL_CLOCKWISE);
317 pass->setDepthCheckEnabled(false);
318 }
319 else
320 {
321 pass->setDepthCheckEnabled(true);
322 if (isCameraInsideLight(camera))
323 {
324 pass->setCullingMode(Ogre::CULL_ANTICLOCKWISE);
325 pass->setDepthFunction(Ogre::CMPF_GREATER_EQUAL);
326 }
327 else
328 {
329 pass->setCullingMode(Ogre::CULL_CLOCKWISE);
330 pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL);
331 }
332 }
333
334 Camera shadowCam("ShadowCameraSetupCam", 0);
335 shadowCam._notifyViewport(camera->getViewport());
336 SceneManager* sm = mParentLight->_getManager();
337 sm->getShadowCameraSetup()->getShadowCamera(sm,
338 camera, camera->getViewport(), mParentLight, &shadowCam, 0);
339
340 //Get the shadow camera position
341 if (params->_findNamedConstantDefinition("shadowCamPos"))
342 {
343 params->setNamedConstant("shadowCamPos", shadowCam.getPosition());
344 }
345 if (params->_findNamedConstantDefinition("shadowFarClip"))
346 {
347 params->setNamedConstant("shadowFarClip", shadowCam.getFarClipDistance());
348 }
349
350 }
351 }
352