Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/OgreMain/src/OgreShadowCaster.cpp @ 1

Last change on this file since 1 was 1, checked in by landauf, 17 years ago
File size: 16.2 KB
Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4(Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23
24You may alternatively use this source under the terms of a specific version of
25the OGRE Unrestricted License provided you have obtained such a license from
26Torus Knot Software Ltd.
27-----------------------------------------------------------------------------
28*/
29#include "OgreStableHeaders.h"
30#include "OgreLight.h"
31#include "OgreEdgeListBuilder.h"
32#include "OgreOptimisedUtil.h"
33
34namespace 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}
Note: See TracBrowser for help on using the repository browser.