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-2006 Torus Knot Software Ltd |
---|
8 | Also see acknowledgements in Readme.html |
---|
9 | |
---|
10 | This program is free software; you can redistribute it and/or modify it under |
---|
11 | the terms of the GNU Lesser General Public License as published by the Free Software |
---|
12 | Foundation; either version 2 of the License, or (at your option) any later |
---|
13 | version. |
---|
14 | |
---|
15 | This program is distributed in the hope that it will be useful, but WITHOUT |
---|
16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
---|
17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
---|
18 | |
---|
19 | You should have received a copy of the GNU Lesser General Public License along with |
---|
20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
---|
21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to |
---|
22 | http://www.gnu.org/copyleft/lesser.txt. |
---|
23 | |
---|
24 | You may alternatively use this source under the terms of a specific version of |
---|
25 | the OGRE Unrestricted License provided you have obtained such a license from |
---|
26 | Torus Knot Software Ltd. |
---|
27 | ----------------------------------------------------------------------------- |
---|
28 | */ |
---|
29 | #include "OgreStableHeaders.h" |
---|
30 | #include "OgreLight.h" |
---|
31 | #include "OgreEdgeListBuilder.h" |
---|
32 | #include "OgreOptimisedUtil.h" |
---|
33 | |
---|
34 | namespace Ogre { |
---|
35 | const LightList& ShadowRenderable::getLights(void) const |
---|
36 | { |
---|
37 | // return empty |
---|
38 | static LightList ll; |
---|
39 | return ll; |
---|
40 | } |
---|
41 | // ------------------------------------------------------------------------ |
---|
42 | void ShadowCaster::updateEdgeListLightFacing(EdgeData* edgeData, |
---|
43 | const Vector4& lightPos) |
---|
44 | { |
---|
45 | edgeData->updateTriangleLightFacing(lightPos); |
---|
46 | } |
---|
47 | // ------------------------------------------------------------------------ |
---|
48 | void ShadowCaster::generateShadowVolume(EdgeData* edgeData, |
---|
49 | const HardwareIndexBufferSharedPtr& indexBuffer, const Light* light, |
---|
50 | ShadowRenderableList& shadowRenderables, unsigned long flags) |
---|
51 | { |
---|
52 | // Edge groups should be 1:1 with shadow renderables |
---|
53 | assert(edgeData->edgeGroups.size() == shadowRenderables.size()); |
---|
54 | |
---|
55 | EdgeData::EdgeGroupList::const_iterator egi, egiend; |
---|
56 | ShadowRenderableList::const_iterator si; |
---|
57 | |
---|
58 | Light::LightTypes lightType = light->getType(); |
---|
59 | |
---|
60 | // pre-count the size of index data we need since it makes a big perf difference |
---|
61 | // to GL in particular if we lock a smaller area of the index buffer |
---|
62 | size_t preCountIndexes = 0; |
---|
63 | |
---|
64 | si = shadowRenderables.begin(); |
---|
65 | egiend = edgeData->edgeGroups.end(); |
---|
66 | for (egi = edgeData->edgeGroups.begin(); egi != egiend; ++egi, ++si) |
---|
67 | { |
---|
68 | const EdgeData::EdgeGroup& eg = *egi; |
---|
69 | bool firstDarkCapTri = true; |
---|
70 | |
---|
71 | EdgeData::EdgeList::const_iterator i, iend; |
---|
72 | iend = eg.edges.end(); |
---|
73 | for (i = eg.edges.begin(); i != iend; ++i) |
---|
74 | { |
---|
75 | const EdgeData::Edge& edge = *i; |
---|
76 | |
---|
77 | // Silhouette edge, when two tris has opposite light facing, or |
---|
78 | // degenerate edge where only tri 1 is valid and the tri light facing |
---|
79 | char lightFacing = edgeData->triangleLightFacings[edge.triIndex[0]]; |
---|
80 | if ((edge.degenerate && lightFacing) || |
---|
81 | (!edge.degenerate && (lightFacing != edgeData->triangleLightFacings[edge.triIndex[1]]))) |
---|
82 | { |
---|
83 | |
---|
84 | preCountIndexes += 3; |
---|
85 | |
---|
86 | // Are we extruding to infinity? |
---|
87 | if (!(lightType == Light::LT_DIRECTIONAL && |
---|
88 | flags & SRF_EXTRUDE_TO_INFINITY)) |
---|
89 | { |
---|
90 | preCountIndexes += 3; |
---|
91 | } |
---|
92 | |
---|
93 | // Do dark cap tri |
---|
94 | // Use McGuire et al method, a triangle fan covering all silhouette |
---|
95 | // edges and one point (taken from the initial tri) |
---|
96 | if (flags & SRF_INCLUDE_DARK_CAP) |
---|
97 | { |
---|
98 | if (firstDarkCapTri) |
---|
99 | { |
---|
100 | firstDarkCapTri = false; |
---|
101 | } |
---|
102 | else |
---|
103 | { |
---|
104 | preCountIndexes += 3; |
---|
105 | } |
---|
106 | |
---|
107 | } |
---|
108 | } |
---|
109 | |
---|
110 | } |
---|
111 | |
---|
112 | // Do light cap |
---|
113 | if (flags & SRF_INCLUDE_LIGHT_CAP) |
---|
114 | { |
---|
115 | // Iterate over the triangles which are using this vertex set |
---|
116 | EdgeData::TriangleList::const_iterator ti, tiend; |
---|
117 | EdgeData::TriangleLightFacingList::const_iterator lfi; |
---|
118 | ti = edgeData->triangles.begin() + eg.triStart; |
---|
119 | tiend = ti + eg.triCount; |
---|
120 | lfi = edgeData->triangleLightFacings.begin() + eg.triStart; |
---|
121 | for ( ; ti != tiend; ++ti, ++lfi) |
---|
122 | { |
---|
123 | const EdgeData::Triangle& t = *ti; |
---|
124 | assert(t.vertexSet == eg.vertexSet); |
---|
125 | // Check it's light facing |
---|
126 | if (*lfi) |
---|
127 | { |
---|
128 | preCountIndexes += 3; |
---|
129 | } |
---|
130 | } |
---|
131 | |
---|
132 | } |
---|
133 | |
---|
134 | } |
---|
135 | // End pre-count |
---|
136 | |
---|
137 | |
---|
138 | // Lock index buffer for writing, just enough length as we need |
---|
139 | unsigned short* pIdx = static_cast<unsigned short*>( |
---|
140 | indexBuffer->lock(0, sizeof(unsigned short) * preCountIndexes, |
---|
141 | HardwareBuffer::HBL_DISCARD)); |
---|
142 | size_t numIndices = 0; |
---|
143 | |
---|
144 | // Iterate over the groups and form renderables for each based on their |
---|
145 | // lightFacing |
---|
146 | si = shadowRenderables.begin(); |
---|
147 | egiend = edgeData->edgeGroups.end(); |
---|
148 | for (egi = edgeData->edgeGroups.begin(); egi != egiend; ++egi, ++si) |
---|
149 | { |
---|
150 | const EdgeData::EdgeGroup& eg = *egi; |
---|
151 | // Initialise the index start for this shadow renderable |
---|
152 | IndexData* indexData = (*si)->getRenderOperationForUpdate()->indexData; |
---|
153 | indexData->indexStart = numIndices; |
---|
154 | // original number of verts (without extruded copy) |
---|
155 | size_t originalVertexCount = eg.vertexData->vertexCount; |
---|
156 | bool firstDarkCapTri = true; |
---|
157 | unsigned short darkCapStart; |
---|
158 | |
---|
159 | EdgeData::EdgeList::const_iterator i, iend; |
---|
160 | iend = eg.edges.end(); |
---|
161 | for (i = eg.edges.begin(); i != iend; ++i) |
---|
162 | { |
---|
163 | const EdgeData::Edge& edge = *i; |
---|
164 | |
---|
165 | // Silhouette edge, when two tris has opposite light facing, or |
---|
166 | // degenerate edge where only tri 1 is valid and the tri light facing |
---|
167 | char lightFacing = edgeData->triangleLightFacings[edge.triIndex[0]]; |
---|
168 | if ((edge.degenerate && lightFacing) || |
---|
169 | (!edge.degenerate && (lightFacing != edgeData->triangleLightFacings[edge.triIndex[1]]))) |
---|
170 | { |
---|
171 | size_t v0 = edge.vertIndex[0]; |
---|
172 | size_t v1 = edge.vertIndex[1]; |
---|
173 | if (!lightFacing) |
---|
174 | { |
---|
175 | // Inverse edge indexes when t1 is light away |
---|
176 | std::swap(v0, v1); |
---|
177 | } |
---|
178 | |
---|
179 | /* Note edge(v0, v1) run anticlockwise along the edge from |
---|
180 | the light facing tri so to point shadow volume tris outward, |
---|
181 | light cap indexes have to be backwards |
---|
182 | |
---|
183 | We emit 2 tris if light is a point light, 1 if light |
---|
184 | is directional, because directional lights cause all |
---|
185 | points to converge to a single point at infinity. |
---|
186 | |
---|
187 | First side tri = near1, near0, far0 |
---|
188 | Second tri = far0, far1, near1 |
---|
189 | |
---|
190 | 'far' indexes are 'near' index + originalVertexCount |
---|
191 | because 'far' verts are in the second half of the |
---|
192 | buffer |
---|
193 | */ |
---|
194 | assert(v1 < 65536 && v0 < 65536 && (v0 + originalVertexCount) < 65536 && |
---|
195 | "Vertex count exceeds 16-bit index limit!"); |
---|
196 | *pIdx++ = static_cast<unsigned short>(v1); |
---|
197 | *pIdx++ = static_cast<unsigned short>(v0); |
---|
198 | *pIdx++ = static_cast<unsigned short>(v0 + originalVertexCount); |
---|
199 | numIndices += 3; |
---|
200 | |
---|
201 | // Are we extruding to infinity? |
---|
202 | if (!(lightType == Light::LT_DIRECTIONAL && |
---|
203 | flags & SRF_EXTRUDE_TO_INFINITY)) |
---|
204 | { |
---|
205 | // additional tri to make quad |
---|
206 | *pIdx++ = static_cast<unsigned short>(v0 + originalVertexCount); |
---|
207 | *pIdx++ = static_cast<unsigned short>(v1 + originalVertexCount); |
---|
208 | *pIdx++ = static_cast<unsigned short>(v1); |
---|
209 | numIndices += 3; |
---|
210 | } |
---|
211 | |
---|
212 | // Do dark cap tri |
---|
213 | // Use McGuire et al method, a triangle fan covering all silhouette |
---|
214 | // edges and one point (taken from the initial tri) |
---|
215 | if (flags & SRF_INCLUDE_DARK_CAP) |
---|
216 | { |
---|
217 | if (firstDarkCapTri) |
---|
218 | { |
---|
219 | darkCapStart = static_cast<unsigned short>(v0 + originalVertexCount); |
---|
220 | firstDarkCapTri = false; |
---|
221 | } |
---|
222 | else |
---|
223 | { |
---|
224 | *pIdx++ = darkCapStart; |
---|
225 | *pIdx++ = static_cast<unsigned short>(v1 + originalVertexCount); |
---|
226 | *pIdx++ = static_cast<unsigned short>(v0 + originalVertexCount); |
---|
227 | numIndices += 3; |
---|
228 | } |
---|
229 | |
---|
230 | } |
---|
231 | } |
---|
232 | |
---|
233 | } |
---|
234 | |
---|
235 | // Do light cap |
---|
236 | if (flags & SRF_INCLUDE_LIGHT_CAP) |
---|
237 | { |
---|
238 | // separate light cap? |
---|
239 | if ((*si)->isLightCapSeparate()) |
---|
240 | { |
---|
241 | // update index count for this shadow renderable |
---|
242 | indexData->indexCount = numIndices - indexData->indexStart; |
---|
243 | |
---|
244 | // get light cap index data for update |
---|
245 | indexData = (*si)->getLightCapRenderable()->getRenderOperationForUpdate()->indexData; |
---|
246 | // start indexes after the current total |
---|
247 | indexData->indexStart = numIndices; |
---|
248 | } |
---|
249 | |
---|
250 | // Iterate over the triangles which are using this vertex set |
---|
251 | EdgeData::TriangleList::const_iterator ti, tiend; |
---|
252 | EdgeData::TriangleLightFacingList::const_iterator lfi; |
---|
253 | ti = edgeData->triangles.begin() + eg.triStart; |
---|
254 | tiend = ti + eg.triCount; |
---|
255 | lfi = edgeData->triangleLightFacings.begin() + eg.triStart; |
---|
256 | for ( ; ti != tiend; ++ti, ++lfi) |
---|
257 | { |
---|
258 | const EdgeData::Triangle& t = *ti; |
---|
259 | assert(t.vertexSet == eg.vertexSet); |
---|
260 | // Check it's light facing |
---|
261 | if (*lfi) |
---|
262 | { |
---|
263 | assert(t.vertIndex[0] < 65536 && t.vertIndex[1] < 65536 && |
---|
264 | t.vertIndex[2] < 65536 && |
---|
265 | "16-bit index limit exceeded!"); |
---|
266 | *pIdx++ = static_cast<unsigned short>(t.vertIndex[0]); |
---|
267 | *pIdx++ = static_cast<unsigned short>(t.vertIndex[1]); |
---|
268 | *pIdx++ = static_cast<unsigned short>(t.vertIndex[2]); |
---|
269 | numIndices += 3; |
---|
270 | } |
---|
271 | } |
---|
272 | |
---|
273 | } |
---|
274 | |
---|
275 | // update index count for current index data (either this shadow renderable or its light cap) |
---|
276 | indexData->indexCount = numIndices - indexData->indexStart; |
---|
277 | |
---|
278 | } |
---|
279 | |
---|
280 | |
---|
281 | // Unlock index buffer |
---|
282 | indexBuffer->unlock(); |
---|
283 | |
---|
284 | // In debug mode, check we didn't overrun the index buffer |
---|
285 | assert(numIndices <= indexBuffer->getNumIndexes() && |
---|
286 | "Index buffer overrun while generating shadow volume!! " |
---|
287 | "You must increase the size of the shadow index buffer."); |
---|
288 | |
---|
289 | } |
---|
290 | // ------------------------------------------------------------------------ |
---|
291 | void ShadowCaster::extrudeVertices( |
---|
292 | const HardwareVertexBufferSharedPtr& vertexBuffer, |
---|
293 | size_t originalVertexCount, const Vector4& light, Real extrudeDist) |
---|
294 | { |
---|
295 | assert (vertexBuffer->getVertexSize() == sizeof(float) * 3 |
---|
296 | && "Position buffer should contain only positions!"); |
---|
297 | |
---|
298 | // Extrude the first area of the buffer into the second area |
---|
299 | // Lock the entire buffer for writing, even though we'll only be |
---|
300 | // updating the latter because you can't have 2 locks on the same |
---|
301 | // buffer |
---|
302 | float* pSrc = static_cast<float*>( |
---|
303 | vertexBuffer->lock(HardwareBuffer::HBL_NORMAL)); |
---|
304 | |
---|
305 | // TODO: We should add extra (ununsed) vertices ensure source and |
---|
306 | // destination buffer have same alignment for slight performance gain. |
---|
307 | float* pDest = pSrc + originalVertexCount * 3; |
---|
308 | |
---|
309 | OptimisedUtil::getImplementation()->extrudeVertices( |
---|
310 | light, extrudeDist, |
---|
311 | pSrc, pDest, originalVertexCount); |
---|
312 | |
---|
313 | vertexBuffer->unlock(); |
---|
314 | |
---|
315 | } |
---|
316 | // ------------------------------------------------------------------------ |
---|
317 | void ShadowCaster::extrudeBounds(AxisAlignedBox& box, const Vector4& light, Real extrudeDist) const |
---|
318 | { |
---|
319 | Vector3 extrusionDir; |
---|
320 | |
---|
321 | if (light.w == 0) |
---|
322 | { |
---|
323 | // Parallel projection guarantees min/max relationship remains the same |
---|
324 | extrusionDir.x = -light.x; |
---|
325 | extrusionDir.y = -light.y; |
---|
326 | extrusionDir.z = -light.z; |
---|
327 | extrusionDir.normalise(); |
---|
328 | extrusionDir *= extrudeDist; |
---|
329 | box.setExtents(box.getMinimum() + extrusionDir, |
---|
330 | box.getMaximum() + extrusionDir); |
---|
331 | } |
---|
332 | else |
---|
333 | { |
---|
334 | Vector3 oldMin, oldMax, currentCorner; |
---|
335 | // Getting the original values |
---|
336 | oldMin = box.getMinimum(); |
---|
337 | oldMax = box.getMaximum(); |
---|
338 | // Starting the box again with a null content |
---|
339 | box.setNull(); |
---|
340 | |
---|
341 | // merging all the extruded corners |
---|
342 | |
---|
343 | // 0 : min min min |
---|
344 | currentCorner = oldMin; |
---|
345 | extrusionDir.x = currentCorner.x - light.x; |
---|
346 | extrusionDir.y = currentCorner.y - light.y; |
---|
347 | extrusionDir.z = currentCorner.z - light.z; |
---|
348 | extrusionDir.normalise(); |
---|
349 | extrusionDir *= extrudeDist; |
---|
350 | box.merge(currentCorner + extrusionDir); |
---|
351 | |
---|
352 | // 6 : min min max |
---|
353 | // only z has changed |
---|
354 | currentCorner.z = oldMax.z; |
---|
355 | extrusionDir.z = currentCorner.z - light.z; |
---|
356 | extrusionDir.normalise(); |
---|
357 | extrusionDir *= extrudeDist; |
---|
358 | box.merge(currentCorner + extrusionDir); |
---|
359 | |
---|
360 | // 5 : min max max |
---|
361 | currentCorner.y = oldMax.y; |
---|
362 | extrusionDir.y = currentCorner.y - light.y; |
---|
363 | extrusionDir.normalise(); |
---|
364 | extrusionDir *= extrudeDist; |
---|
365 | box.merge(currentCorner + extrusionDir); |
---|
366 | |
---|
367 | // 1 : min max min |
---|
368 | currentCorner.z = oldMin.z; |
---|
369 | extrusionDir.z = currentCorner.z - light.z; |
---|
370 | extrusionDir.normalise(); |
---|
371 | extrusionDir *= extrudeDist; |
---|
372 | box.merge(currentCorner + extrusionDir); |
---|
373 | |
---|
374 | // 2 : max max min |
---|
375 | currentCorner.x = oldMax.x; |
---|
376 | extrusionDir.x = currentCorner.x - light.x; |
---|
377 | extrusionDir.normalise(); |
---|
378 | extrusionDir *= extrudeDist; |
---|
379 | box.merge(currentCorner + extrusionDir); |
---|
380 | |
---|
381 | // 4 : max max max |
---|
382 | currentCorner.z = oldMax.z; |
---|
383 | extrusionDir.z = currentCorner.z - light.z; |
---|
384 | extrusionDir.normalise(); |
---|
385 | extrusionDir *= extrudeDist; |
---|
386 | box.merge(currentCorner + extrusionDir); |
---|
387 | |
---|
388 | // 7 : max min max |
---|
389 | currentCorner.y = oldMin.y; |
---|
390 | extrusionDir.y = currentCorner.y - light.y; |
---|
391 | extrusionDir.normalise(); |
---|
392 | extrusionDir *= extrudeDist; |
---|
393 | box.merge(currentCorner + extrusionDir); |
---|
394 | |
---|
395 | // 3 : max min min |
---|
396 | currentCorner.z = oldMin.z; |
---|
397 | extrusionDir.z = currentCorner.z - light.z; |
---|
398 | extrusionDir.normalise(); |
---|
399 | extrusionDir *= extrudeDist; |
---|
400 | box.merge(currentCorner + extrusionDir); |
---|
401 | |
---|
402 | } |
---|
403 | |
---|
404 | } |
---|
405 | // ------------------------------------------------------------------------ |
---|
406 | Real ShadowCaster::getExtrusionDistance(const Vector3& objectPos, const Light* light) const |
---|
407 | { |
---|
408 | Vector3 diff = objectPos - light->getDerivedPosition(); |
---|
409 | return light->getAttenuationRange() - diff.length(); |
---|
410 | } |
---|
411 | |
---|
412 | } |
---|