Coverage Report

Created: 2025-08-25 06:44

/src/ogre/OgreMain/src/OgreShadowCaster.cpp
Line
Count
Source (jump to first uncovered line)
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-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
#include "OgreStableHeaders.h"
29
#include "OgreEdgeListBuilder.h"
30
#include "OgreOptimisedUtil.h"
31
32
namespace Ogre {
33
    ShadowRenderable::ShadowRenderable(MovableObject* parent, const HardwareIndexBufferSharedPtr& indexBuffer,
34
                                   const VertexData* vertexData, bool createSeparateLightCap,
35
                                   bool isLightCap)
36
0
    : mLightCap(0), mParent(parent)
37
0
    {
38
        // Initialise render op
39
0
        mRenderOp.indexData = OGRE_NEW IndexData();
40
0
        mRenderOp.indexData->indexBuffer = indexBuffer;
41
0
        mRenderOp.indexData->indexStart = 0;
42
        // index start and count are sorted out later
43
44
        // Create vertex data which just references position component (and 2 component)
45
0
        mRenderOp.vertexData = OGRE_NEW VertexData();
46
        // Map in position data
47
0
        mRenderOp.vertexData->vertexDeclaration->addElement(0,0,VET_FLOAT3, VES_POSITION);
48
0
        ushort origPosBind =
49
0
            vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION)->getSource();
50
0
        mPositionBuffer = vertexData->vertexBufferBinding->getBuffer(origPosBind);
51
0
        mRenderOp.vertexData->vertexBufferBinding->setBinding(0, mPositionBuffer);
52
        // Map in w-coord buffer (if present)
53
0
        if(vertexData->hardwareShadowVolWBuffer)
54
0
        {
55
0
            mRenderOp.vertexData->vertexDeclaration->addElement(1,0,VET_FLOAT1, VES_TEXTURE_COORDINATES, 0);
56
0
            mWBuffer = vertexData->hardwareShadowVolWBuffer;
57
0
            mRenderOp.vertexData->vertexBufferBinding->setBinding(1, mWBuffer);
58
0
        }
59
        // Use same vertex start as input
60
0
        mRenderOp.vertexData->vertexStart = vertexData->vertexStart;
61
62
0
        if (isLightCap)
63
0
        {
64
            // Use original vertex count, no extrusion
65
0
            mRenderOp.vertexData->vertexCount = vertexData->vertexCount;
66
0
        }
67
0
        else
68
0
        {
69
            // Vertex count must take into account the doubling of the buffer,
70
            // because second half of the buffer is the extruded copy
71
0
            mRenderOp.vertexData->vertexCount = vertexData->vertexCount * 2;
72
73
0
            if (createSeparateLightCap)
74
0
            {
75
                // Create child light cap
76
0
                mLightCap = OGRE_NEW ShadowRenderable(parent, indexBuffer, vertexData, false, true);
77
0
            }
78
0
        }
79
0
    }
80
    ShadowRenderable::~ShadowRenderable()
81
0
    {
82
0
        delete mLightCap;
83
0
        delete mRenderOp.indexData;
84
0
        delete mRenderOp.vertexData;
85
0
    }
86
    void ShadowRenderable::rebindIndexBuffer(const HardwareIndexBufferSharedPtr& indexBuffer)
87
0
    {
88
0
        mRenderOp.indexData->indexBuffer = indexBuffer;
89
0
        if (mLightCap) mLightCap->rebindIndexBuffer(indexBuffer);
90
0
    }
91
    void ShadowRenderable::getWorldTransforms(Matrix4* xform) const
92
0
    {
93
0
        *xform = mParent->_getParentNodeFullTransform();
94
0
    }
95
    const LightList& ShadowRenderable::getLights(void) const 
96
0
    {
97
        // return empty
98
0
        static LightList ll;
99
0
        return ll;
100
0
    }
101
    //-----------------------------------------------------------------------
102
    const ShadowRenderableList& ShadowCaster::getShadowVolumeRenderableList(
103
        const Light* light, const HardwareIndexBufferPtr& indexBuffer, size_t& indexBufferUsedSize,
104
        float extrusionDist, int flags)
105
0
    {
106
0
        static ShadowRenderableList dummyList;
107
0
        return dummyList;
108
0
    }
109
    //-----------------------------------------------------------------------
110
    const AxisAlignedBox& ShadowCaster::getDarkCapBounds(const Light& light, Real extrusionDist) const
111
0
    {
112
        // Extrude own light cap bounds
113
0
        mWorldDarkCapBounds = getLightCapBounds();
114
0
        extrudeBounds(mWorldDarkCapBounds, light.getAs4DVector(), extrusionDist);
115
0
        return mWorldDarkCapBounds;
116
0
    }
117
    // ------------------------------------------------------------------------
118
    void ShadowCaster::clearShadowRenderableList(ShadowRenderableList& shadowRenderables)
119
0
    {
120
0
        for(auto & shadowRenderable : shadowRenderables)
121
0
        {
122
0
            OGRE_DELETE shadowRenderable;
123
0
            shadowRenderable = 0;
124
0
        }
125
0
        shadowRenderables.clear();
126
0
    }
127
    // ------------------------------------------------------------------------
128
    void ShadowCaster::updateEdgeListLightFacing(EdgeData* edgeData, 
129
        const Vector4& lightPos)
130
0
    {
131
0
        edgeData->updateTriangleLightFacing(lightPos);
132
0
    }
133
    // ------------------------------------------------------------------------
134
    static bool isBoundOkForMcGuire(const AxisAlignedBox& lightCapBounds, const Ogre::Vector3& lightPosition)
135
0
    {
136
        // If light position is inside light cap bound then extrusion could be in opposite directions
137
        // and McGuire cap could intersect near clip plane of camera frustum without being noticed
138
0
        if(lightCapBounds.contains(lightPosition))
139
0
            return false;
140
141
        // If angular size of object is too high then extrusion could be in almost opposite directions,
142
        // interpolated points would be extruded by shorter distance, and strange geometry of McGuire cap
143
        // could be visible even for well tesselated meshes. As a heuristic we will avoid McGuire cap if
144
        // angular size is larger than 60 degrees - it guarantees that interpolated points would be
145
        // extruded by at least cos(60deg/2) ~ 86% of the original extrusion distance.
146
0
        if(lightCapBounds.getHalfSize().length() / (lightCapBounds.getCenter() - lightPosition).length() > 0.5) // if boundingSphereAngularSize > 60deg
147
0
        {
148
            // Calculate angular size one more time using edge corners angular distance comparision,
149
            // Determine lit sides of the bound, store in mask
150
0
            enum { L = 1, R = 2, B = 4, T = 8, F = 16, N = 32 }; // left, right, bottom, top, far, near
151
0
            unsigned lightSidesMask = 
152
0
                (lightPosition.x < lightCapBounds.getMinimum().x ? L : 0) | // left
153
0
                (lightPosition.x > lightCapBounds.getMaximum().x ? R : 0) | // right
154
0
                (lightPosition.y < lightCapBounds.getMinimum().y ? B : 0) | // bottom
155
0
                (lightPosition.y > lightCapBounds.getMaximum().y ? T : 0) | // top
156
0
                (lightPosition.z < lightCapBounds.getMinimum().z ? F : 0) | // far
157
0
                (lightPosition.z > lightCapBounds.getMaximum().z ? N : 0);  // near
158
            
159
            // find corners on lit/unlit edge (should not be more than 6 simultaneously, but better be safe than sorry)
160
0
            Ogre::Vector3 edgeCorners[8]; 
161
0
            unsigned edgeCornersCount = 0;
162
0
            std::pair<unsigned, AxisAlignedBox::CornerEnum> cornerMap[8] = {
163
0
                { F|L|B, AxisAlignedBox::FAR_LEFT_BOTTOM }, { F|R|B, AxisAlignedBox::FAR_RIGHT_BOTTOM },
164
0
                { F|L|T, AxisAlignedBox::FAR_LEFT_TOP },    { F|R|T, AxisAlignedBox::FAR_RIGHT_TOP },
165
0
                { N|L|B, AxisAlignedBox::NEAR_LEFT_BOTTOM },{ N|R|B, AxisAlignedBox::NEAR_RIGHT_BOTTOM },
166
0
                { N|L|T, AxisAlignedBox::NEAR_LEFT_TOP },   { N|R|T, AxisAlignedBox::NEAR_RIGHT_TOP }};
167
0
            for(auto& c : cornerMap)
168
0
                if((lightSidesMask & c.first) != 0 && (lightSidesMask & c.first) != c.first) // if adjacent sides not all lit or all unlit
169
0
                    edgeCorners[edgeCornersCount++] = lightCapBounds.getCorner(c.second);
170
            
171
            // find max angular size in range [0..pi] by finding min cos of angular size, range [1..-1]
172
0
            Real cosAngle = 1.0;
173
0
            for(unsigned i0 = 0; i0 + 1 < edgeCornersCount; ++i0)
174
0
                for(unsigned i1 = i0 + 1; i1 < edgeCornersCount; ++i1)
175
0
                {
176
                    // 4~6 edge corners, 6~15 angular distance calculations
177
0
                    Vector3 a = (edgeCorners[i0] - lightPosition).normalisedCopy();
178
0
                    Vector3 b = (edgeCorners[i1] - lightPosition).normalisedCopy();
179
0
                    Real cosAB = a.dotProduct(b);
180
0
                    if(cosAngle > cosAB)
181
0
                        cosAngle  = cosAB;
182
0
                }
183
            
184
0
            if(cosAngle < 0.5) // angularSize > 60 degrees
185
0
                return false;
186
0
        }
187
188
0
        return true;
189
0
    }
190
    // ------------------------------------------------------------------------
191
    void ShadowCaster::generateShadowVolume(EdgeData* edgeData, 
192
        const HardwareIndexBufferSharedPtr& indexBuffer, size_t& indexBufferUsedSize, 
193
        const Light* light, ShadowRenderableList& shadowRenderables, unsigned long flags)
194
0
    {
195
        // Edge groups should be 1:1 with shadow renderables
196
0
        assert(edgeData->edgeGroups.size() == shadowRenderables.size());
197
198
0
        Light::LightTypes lightType = light->getType();
199
200
        // Whether to use the McGuire method, a triangle fan covering all silhouette
201
        // This won't work properly with multiple separate edge groups (should be one fan per group, not implemented)
202
        // or when light position is too close to light cap bound.
203
0
        bool useMcGuire = edgeData->edgeGroups.size() <= 1 && 
204
0
            (lightType == Light::LT_DIRECTIONAL || isBoundOkForMcGuire(getLightCapBounds(), light->getDerivedPosition()));
205
0
        ShadowRenderableList::const_iterator si;
206
207
        // pre-count the size of index data we need since it makes a big perf difference
208
        // to GL in particular if we lock a smaller area of the index buffer
209
0
        uint32 preCountIndexes = 0;
210
211
0
        si = shadowRenderables.begin();
212
0
        for (auto& eg : edgeData->edgeGroups)
213
0
        {
214
0
            bool  firstDarkCapTri = true;
215
0
            for (auto& edge :  eg.edges)
216
0
            {
217
                // Silhouette edge, when two tris has opposite light facing, or
218
                // degenerate edge where only tri 1 is valid and the tri light facing
219
0
                char lightFacing = edgeData->triangleLightFacings[edge.triIndex[0]];
220
0
                if ((edge.degenerate && lightFacing) ||
221
0
                    (!edge.degenerate && (lightFacing != edgeData->triangleLightFacings[edge.triIndex[1]])))
222
0
                {
223
224
0
                    preCountIndexes += 3;
225
226
                    // Are we extruding to infinity?
227
0
                    if (!(lightType == Light::LT_DIRECTIONAL &&
228
0
                        flags & SRF_EXTRUDE_TO_INFINITY))
229
0
                    {
230
0
                        preCountIndexes += 3;
231
0
                    }
232
233
0
                    if(useMcGuire)
234
0
                    {
235
                        // Do dark cap tri
236
                        // Use McGuire et al method, a triangle fan covering all silhouette
237
                        // edges and one point (taken from the initial tri)
238
0
                        if (flags & SRF_INCLUDE_DARK_CAP)
239
0
                        {
240
0
                            if (firstDarkCapTri)
241
0
                            {
242
0
                                firstDarkCapTri = false;
243
0
                            }
244
0
                            else
245
0
                            {
246
0
                                preCountIndexes += 3;
247
0
                            }
248
0
                        }
249
0
                    }
250
0
                }
251
252
0
            }
253
254
0
            if(useMcGuire)
255
0
            {
256
                // Do light cap
257
0
                if (flags & SRF_INCLUDE_LIGHT_CAP) 
258
0
                {
259
                    // Iterate over the triangles which are using this vertex set
260
0
                    EdgeData::TriangleList::const_iterator ti, tiend;
261
0
                    EdgeData::TriangleLightFacingList::const_iterator lfi;
262
0
                    ti = edgeData->triangles.begin() + eg.triStart;
263
0
                    tiend = ti + eg.triCount;
264
0
                    lfi = edgeData->triangleLightFacings.begin() + eg.triStart;
265
0
                    for ( ; ti != tiend; ++ti, ++lfi)
266
0
                    {
267
0
                        assert(ti->vertexSet == eg.vertexSet);
268
                        // Check it's light facing
269
0
                        if (*lfi)
270
0
                        {
271
0
                            preCountIndexes += 3;
272
0
                        }
273
0
                    }
274
275
0
                }
276
0
            }
277
0
            else
278
0
            {
279
                // Do both caps
280
0
                int increment = ((flags & SRF_INCLUDE_DARK_CAP) ? 3 : 0) + ((flags & SRF_INCLUDE_LIGHT_CAP) ? 3 : 0);
281
0
                if(increment != 0)
282
0
                {
283
                    // Iterate over the triangles which are using this vertex set
284
0
                    EdgeData::TriangleList::const_iterator ti, tiend;
285
0
                    EdgeData::TriangleLightFacingList::const_iterator lfi;
286
0
                    ti = edgeData->triangles.begin() + eg.triStart;
287
0
                    tiend = ti + eg.triCount;
288
0
                    lfi = edgeData->triangleLightFacings.begin() + eg.triStart;
289
0
                    for ( ; ti != tiend; ++ti, ++lfi)
290
0
                    {
291
0
                        assert(ti->vertexSet == eg.vertexSet);
292
                        // Check it's light facing
293
0
                        if (*lfi)
294
0
                            preCountIndexes += increment;
295
0
                    }
296
0
                }
297
0
            }
298
0
            ++si;
299
0
        }
300
        // End pre-count
301
        
302
        //Check if index buffer is to small 
303
0
        if (preCountIndexes > indexBuffer->getNumIndexes())
304
0
        {
305
0
            LogManager::getSingleton().logWarning(
306
0
                "shadow index buffer size to small. Auto increasing buffer size to" +
307
0
                StringConverter::toString(sizeof(unsigned short) * preCountIndexes));
308
309
0
            SceneManager* pManager = Root::getSingleton()._getCurrentSceneManager();
310
0
            if (pManager)
311
0
            {
312
0
                pManager->setShadowIndexBufferSize(preCountIndexes);
313
0
            }
314
            
315
            //Check that the index buffer size has actually increased
316
0
            if (preCountIndexes > indexBuffer->getNumIndexes())
317
0
            {
318
                //increasing index buffer size has failed
319
0
                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
320
0
                    "Lock request out of bounds.",
321
0
                    "ShadowCaster::generateShadowVolume");
322
0
            }
323
0
        }
324
0
        else if(indexBufferUsedSize + preCountIndexes > indexBuffer->getNumIndexes())
325
0
        {
326
0
            indexBufferUsedSize = 0;
327
0
        }
328
329
        // Lock index buffer for writing, just enough length as we need
330
0
        HardwareBufferLockGuard indexLock(indexBuffer,
331
0
            sizeof(unsigned short) * indexBufferUsedSize, sizeof(unsigned short) * preCountIndexes,
332
0
            indexBufferUsedSize == 0 ? HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NO_OVERWRITE);
333
0
        unsigned short* pIdx = static_cast<unsigned short*>(indexLock.pData);
334
0
        uint32 numIndices = indexBufferUsedSize;
335
        
336
        // Iterate over the groups and form renderables for each based on their
337
        // lightFacing
338
0
        si = shadowRenderables.begin();
339
0
        for (auto& eg : edgeData->edgeGroups)
340
0
        {
341
            // Initialise the index start for this shadow renderable
342
0
            IndexData* indexData = (*si)->getRenderOperationForUpdate()->indexData;
343
344
0
            if (indexData->indexBuffer != indexBuffer)
345
0
            {
346
0
                (*si)->rebindIndexBuffer(indexBuffer);
347
0
                indexData = (*si)->getRenderOperationForUpdate()->indexData;
348
0
            }
349
350
0
            indexData->indexStart = numIndices;
351
            // original number of verts (without extruded copy)
352
0
            uint32 originalVertexCount = eg.vertexData->vertexCount;
353
0
            bool  firstDarkCapTri = true;
354
0
            unsigned short darkCapStart = 0;
355
356
0
            for (auto& edge : eg.edges)
357
0
            {
358
                // Silhouette edge, when two tris has opposite light facing, or
359
                // degenerate edge where only tri 1 is valid and the tri light facing
360
0
                char lightFacing = edgeData->triangleLightFacings[edge.triIndex[0]];
361
0
                if ((edge.degenerate && lightFacing) ||
362
0
                    (!edge.degenerate && (lightFacing != edgeData->triangleLightFacings[edge.triIndex[1]])))
363
0
                {
364
0
                    uint32 v0 = edge.vertIndex[0];
365
0
                    uint32 v1 = edge.vertIndex[1];
366
0
                    if (!lightFacing)
367
0
                    {
368
                        // Inverse edge indexes when t1 is light away
369
0
                        std::swap(v0, v1);
370
0
                    }
371
372
                    /* Note edge(v0, v1) run anticlockwise along the edge from
373
                    the light facing tri so to point shadow volume tris outward,
374
                    light cap indexes have to be backwards
375
376
                    We emit 2 tris if light is a point light, 1 if light 
377
                    is directional, because directional lights cause all
378
                    points to converge to a single point at infinity.
379
380
                    First side tri = near1, near0, far0
381
                    Second tri = far0, far1, near1
382
383
                    'far' indexes are 'near' index + originalVertexCount
384
                    because 'far' verts are in the second half of the 
385
                    buffer
386
                    */
387
0
                    assert(v1 < 65536 && v0 < 65536 && (v0 + originalVertexCount) < 65536 &&
388
0
                        "Vertex count exceeds 16-bit index limit!");
389
0
                    *pIdx++ = static_cast<unsigned short>(v1);
390
0
                    *pIdx++ = static_cast<unsigned short>(v0);
391
0
                    *pIdx++ = static_cast<unsigned short>(v0 + originalVertexCount);
392
0
                    numIndices += 3;
393
394
                    // Are we extruding to infinity?
395
0
                    if (!(lightType == Light::LT_DIRECTIONAL &&
396
0
                        flags & SRF_EXTRUDE_TO_INFINITY))
397
0
                    {
398
                        // additional tri to make quad
399
0
                        *pIdx++ = static_cast<unsigned short>(v0 + originalVertexCount);
400
0
                        *pIdx++ = static_cast<unsigned short>(v1 + originalVertexCount);
401
0
                        *pIdx++ = static_cast<unsigned short>(v1);
402
0
                        numIndices += 3;
403
0
                    }
404
405
0
                    if(useMcGuire)
406
0
                    {
407
                        // Do dark cap tri
408
                        // Use McGuire et al method, a triangle fan covering all silhouette
409
                        // edges and one point (taken from the initial tri)
410
0
                        if (flags & SRF_INCLUDE_DARK_CAP)
411
0
                        {
412
0
                            if (firstDarkCapTri)
413
0
                            {
414
0
                                darkCapStart = static_cast<unsigned short>(v0 + originalVertexCount);
415
0
                                firstDarkCapTri = false;
416
0
                            }
417
0
                            else
418
0
                            {
419
0
                                *pIdx++ = darkCapStart;
420
0
                                *pIdx++ = static_cast<unsigned short>(v1 + originalVertexCount);
421
0
                                *pIdx++ = static_cast<unsigned short>(v0 + originalVertexCount);
422
0
                                numIndices += 3;
423
0
                            }
424
425
0
                        }
426
0
                    }
427
0
                }
428
429
0
            }
430
431
0
            if(!useMcGuire)
432
0
            {
433
                // Do dark cap
434
0
                if (flags & SRF_INCLUDE_DARK_CAP) 
435
0
                {
436
                    // Iterate over the triangles which are using this vertex set
437
0
                    EdgeData::TriangleList::const_iterator ti, tiend;
438
0
                    EdgeData::TriangleLightFacingList::const_iterator lfi;
439
0
                    ti = edgeData->triangles.begin() + eg.triStart;
440
0
                    tiend = ti + eg.triCount;
441
0
                    lfi = edgeData->triangleLightFacings.begin() + eg.triStart;
442
0
                    for ( ; ti != tiend; ++ti, ++lfi)
443
0
                    {
444
0
                        const EdgeData::Triangle& t = *ti;
445
0
                        assert(t.vertexSet == eg.vertexSet);
446
                        // Check it's light facing
447
0
                        if (*lfi)
448
0
                        {
449
0
                            assert(t.vertIndex[0] < 65536 && t.vertIndex[1] < 65536 &&
450
0
                                t.vertIndex[2] < 65536 && 
451
0
                                "16-bit index limit exceeded!");
452
0
                            *pIdx++ = static_cast<unsigned short>(t.vertIndex[1] + originalVertexCount);
453
0
                            *pIdx++ = static_cast<unsigned short>(t.vertIndex[0] + originalVertexCount);
454
0
                            *pIdx++ = static_cast<unsigned short>(t.vertIndex[2] + originalVertexCount);
455
0
                            numIndices += 3;
456
0
                        }
457
0
                    }
458
459
0
                }
460
0
            }
461
462
            // Do light cap
463
0
            if (flags & SRF_INCLUDE_LIGHT_CAP) 
464
0
            {
465
                // separate light cap?
466
0
                if ((*si)->isLightCapSeparate())
467
0
                {
468
                    // update index count for this shadow renderable
469
0
                    indexData->indexCount = numIndices - indexData->indexStart;
470
471
                    // get light cap index data for update
472
0
                    indexData = (*si)->getLightCapRenderable()->getRenderOperationForUpdate()->indexData;
473
                    // start indexes after the current total
474
0
                    indexData->indexStart = numIndices;
475
0
                }
476
477
                // Iterate over the triangles which are using this vertex set
478
0
                EdgeData::TriangleList::const_iterator ti, tiend;
479
0
                EdgeData::TriangleLightFacingList::const_iterator lfi;
480
0
                ti = edgeData->triangles.begin() + eg.triStart;
481
0
                tiend = ti + eg.triCount;
482
0
                lfi = edgeData->triangleLightFacings.begin() + eg.triStart;
483
0
                for ( ; ti != tiend; ++ti, ++lfi)
484
0
                {
485
0
                    const EdgeData::Triangle& t = *ti;
486
0
                    assert(t.vertexSet == eg.vertexSet);
487
                    // Check it's light facing
488
0
                    if (*lfi)
489
0
                    {
490
0
                        assert(t.vertIndex[0] < 65536 && t.vertIndex[1] < 65536 &&
491
0
                            t.vertIndex[2] < 65536 && 
492
0
                            "16-bit index limit exceeded!");
493
0
                        *pIdx++ = static_cast<unsigned short>(t.vertIndex[0]);
494
0
                        *pIdx++ = static_cast<unsigned short>(t.vertIndex[1]);
495
0
                        *pIdx++ = static_cast<unsigned short>(t.vertIndex[2]);
496
0
                        numIndices += 3;
497
0
                    }
498
0
                }
499
500
0
            }
501
502
            // update index count for current index data (either this shadow renderable or its light cap)
503
0
            indexData->indexCount = numIndices - indexData->indexStart;
504
505
0
            ++si;
506
0
        }
507
508
        // In debug mode, check we didn't overrun the index buffer
509
0
        assert(numIndices == indexBufferUsedSize + preCountIndexes);
510
0
        assert(numIndices <= indexBuffer->getNumIndexes() &&
511
0
            "Index buffer overrun while generating shadow volume!! "
512
0
            "You must increase the size of the shadow index buffer.");
513
514
0
        indexBufferUsedSize = numIndices;
515
0
    }
516
    // ------------------------------------------------------------------------
517
    void ShadowCaster::extrudeVertices(
518
        const HardwareVertexBufferSharedPtr& vertexBuffer, 
519
        size_t originalVertexCount, const Vector4& light, Real extrudeDist)
520
0
    {
521
0
        assert (vertexBuffer->getVertexSize() == sizeof(float) * 3
522
0
            && "Position buffer should contain only positions!");
523
524
        // Extrude the first area of the buffer into the second area
525
        // Lock the entire buffer for writing, even though we'll only be
526
        // updating the latter because you can't have 2 locks on the same
527
        // buffer
528
0
        HardwareBufferLockGuard vertexLock(vertexBuffer, HardwareBuffer::HBL_NORMAL);
529
0
        float* pSrc = static_cast<float*>(vertexLock.pData);
530
531
        // TODO: We should add extra (ununsed) vertices ensure source and
532
        // destination buffer have same alignment for slight performance gain.
533
0
        float* pDest = pSrc + originalVertexCount * 3;
534
535
0
        OptimisedUtil::getImplementation()->extrudeVertices(
536
0
            light, extrudeDist,
537
0
            pSrc, pDest, originalVertexCount);
538
0
    }
539
    // ------------------------------------------------------------------------
540
    void ShadowCaster::extrudeBounds(AxisAlignedBox& box, const Vector4& light, Real extrudeDist) const
541
0
    {
542
0
        Vector3 extrusionDir;
543
544
0
        if (light.w == 0)
545
0
        {
546
            // Parallel projection guarantees min/max relationship remains the same
547
0
            extrusionDir.x = -light.x;
548
0
            extrusionDir.y = -light.y;
549
0
            extrusionDir.z = -light.z;
550
0
            extrusionDir.normalise();
551
0
            extrusionDir *= extrudeDist;
552
0
            box.setExtents(box.getMinimum() + extrusionDir, 
553
0
                box.getMaximum() + extrusionDir);
554
0
        }
555
0
        else
556
0
        {
557
0
            Vector3 oldMin, oldMax, currentCorner;
558
            // Getting the original values
559
0
            oldMin = box.getMinimum();
560
0
            oldMax = box.getMaximum();
561
            // Starting the box again with a null content
562
0
            box.setNull();
563
564
            // merging all the extruded corners
565
566
            // 0 : min min min
567
0
            currentCorner = oldMin;
568
0
            extrusionDir.x = currentCorner.x - light.x;
569
0
            extrusionDir.y = currentCorner.y - light.y;
570
0
            extrusionDir.z = currentCorner.z - light.z;
571
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
572
573
            // 6 : min min max
574
            // only z has changed
575
0
            currentCorner.z = oldMax.z;
576
0
            extrusionDir.z = currentCorner.z - light.z;
577
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
578
579
            // 5 : min max max
580
0
            currentCorner.y = oldMax.y;
581
0
            extrusionDir.y = currentCorner.y - light.y;
582
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
583
584
            // 1 : min max min
585
0
            currentCorner.z = oldMin.z;
586
0
            extrusionDir.z = currentCorner.z - light.z;
587
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
588
589
            // 2 : max max min
590
0
            currentCorner.x = oldMax.x;
591
0
            extrusionDir.x = currentCorner.x - light.x;
592
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
593
594
            // 4 : max max max
595
0
            currentCorner.z = oldMax.z;
596
0
            extrusionDir.z = currentCorner.z - light.z;
597
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
598
599
            // 7 : max min max
600
0
            currentCorner.y = oldMin.y;
601
0
            extrusionDir.y = currentCorner.y - light.y;
602
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
603
604
            // 3 : max min min
605
0
            currentCorner.z = oldMin.z;
606
0
            extrusionDir.z = currentCorner.z - light.z;
607
0
            box.merge(currentCorner + extrudeDist * extrusionDir.normalisedCopy());
608
609
0
        }
610
611
0
    }
612
}