Coverage Report

Created: 2026-10-28 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreBillboardSet.cpp
Line
Count
Source
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
30
#include "OgreBillboardSet.h"
31
#include "OgreBillboard.h"
32
33
#include <algorithm>
34
#include <memory>
35
36
namespace Ogre {
37
    //-----------------------------------------------------------------------
38
    BillboardSet::BillboardSet() :
39
0
        mBoundingRadius(0.0f), 
40
0
        mOriginType( BBO_CENTER ),
41
0
        mRotationType( BBR_TEXCOORD ),
42
0
        mAutoExtendPool( true ),
43
0
        mSortingEnabled(false),
44
0
        mAccurateFacing(false),
45
0
        mWorldSpace(false),
46
0
        mCullIndividual( false ),
47
0
        mBillboardType(BBT_POINT),
48
0
        mCommonDirection(Ogre::Vector3::UNIT_Z),
49
0
        mCommonUpVector(Vector3::UNIT_Y),
50
0
        mPointRendering(false),
51
0
        mBuffersCreated(false),
52
0
        mPoolSize(0),
53
0
        mExternalData(false),
54
0
        mAutoUpdate(true),
55
0
        mBillboardDataChanged(true)
56
0
    {
57
0
        setDefaultDimensions( 100, 100 );
58
0
        mMaterial = MaterialManager::getSingleton().getDefaultMaterial();
59
0
        mMaterial->load();
60
0
        mCastShadows = false;
61
0
        setTextureStacksAndSlices( 1, 1 );
62
0
    }
63
64
    //-----------------------------------------------------------------------
65
    BillboardSet::BillboardSet(
66
        const String& name,
67
        unsigned int poolSize,
68
        bool externalData) :
69
0
        MovableObject(name),
70
0
        mBoundingRadius(0.0f), 
71
0
        mOriginType( BBO_CENTER ),
72
0
        mRotationType( BBR_TEXCOORD ),
73
0
        mAutoExtendPool( true ),
74
0
        mSortingEnabled(false),
75
0
        mAccurateFacing(false),
76
0
        mWorldSpace(false),
77
0
        mActiveBillboards(0),
78
0
        mCullIndividual( false ),
79
0
        mBillboardType(BBT_POINT),
80
0
        mCommonDirection(Ogre::Vector3::UNIT_Z),
81
0
        mCommonUpVector(Vector3::UNIT_Y),
82
0
        mPointRendering(false),
83
0
        mBuffersCreated(false),
84
0
        mPoolSize(poolSize),
85
0
        mExternalData(externalData),
86
0
        mAutoUpdate(true),
87
0
        mBillboardDataChanged(true)
88
0
    {
89
0
        setDefaultDimensions( 100, 100 );
90
0
        mMaterial = MaterialManager::getSingleton().getDefaultMaterial();
91
0
        mMaterial->load();
92
0
        setPoolSize( poolSize );
93
0
        mCastShadows = false;
94
0
        setTextureStacksAndSlices( 1, 1 );
95
0
    }
96
    //-----------------------------------------------------------------------
97
    BillboardSet::~BillboardSet()
98
0
    {
99
        // Free pool items
100
0
        for (auto *b : mBillboardPool)
101
0
        {
102
0
            OGRE_DELETE b;
103
0
        }
104
105
        // Delete shared buffers
106
0
        _destroyBuffers();
107
0
    }
108
    //-----------------------------------------------------------------------
109
    Billboard* BillboardSet::createBillboard(
110
        const Vector3& position,
111
        const ColourValue& colour )
112
0
    {
113
0
        if( mActiveBillboards == mBillboardPool.size() )
114
0
        {
115
0
            if( mAutoExtendPool )
116
0
            {
117
0
                setPoolSize( getPoolSize() * 2 );
118
0
            }
119
0
            else
120
0
            {
121
0
                return 0;
122
0
            }
123
0
        }
124
125
        // Get a new billboard
126
0
        Billboard* newBill = mBillboardPool[mActiveBillboards++];
127
0
        newBill->setPosition(position);
128
0
        newBill->setColour(colour);
129
0
        newBill->mDirection = Vector3::ZERO;
130
0
        newBill->setRotation(Radian(0));
131
0
        newBill->setTexcoordIndex(0);
132
0
        newBill->resetDimensions();
133
134
        // Merge into bounds
135
0
        Real adjust = std::max(mDefaultWidth, mDefaultHeight);
136
0
        Vector3 vecAdjust(adjust, adjust, adjust);
137
0
        Vector3 newMin = position - vecAdjust;
138
0
        Vector3 newMax = position + vecAdjust;
139
140
0
        mAABB.merge(newMin);
141
0
        mAABB.merge(newMax);
142
143
0
        mBoundingRadius = Math::boundingRadiusFromAABB(mAABB);
144
145
0
        return newBill;
146
0
    }
147
148
    //-----------------------------------------------------------------------
149
    void BillboardSet::clear()
150
0
    {
151
0
        mActiveBillboards = 0;
152
0
    }
153
154
    //-----------------------------------------------------------------------
155
    Billboard* BillboardSet::getBillboard( unsigned int index ) const
156
0
    {
157
0
        assert(index < mActiveBillboards && "Billboard index out of bounds.");
158
0
        return mBillboardPool[index];
159
0
    }
160
161
    //-----------------------------------------------------------------------
162
    void BillboardSet::removeBillboard(unsigned int index)
163
0
    {
164
0
        assert(index < mActiveBillboards && "Billboard isn't in the active list.");
165
0
        std::swap(mBillboardPool[index], mBillboardPool[--mActiveBillboards]);
166
0
    }
167
168
    //-----------------------------------------------------------------------
169
    void BillboardSet::removeBillboard( Billboard* pBill )
170
0
    {
171
0
        auto it = std::find(mBillboardPool.begin(), mBillboardPool.begin() + mActiveBillboards, pBill);
172
0
        removeBillboard(std::distance(mBillboardPool.begin(), it));
173
0
    }
174
    //-----------------------------------------------------------------------
175
    void BillboardSet::setMaterialName( const String& name , const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */ )
176
0
    {
177
0
        mMaterial = MaterialManager::getSingleton().getByName(name, groupName);
178
179
0
        if (!mMaterial)
180
0
            OGRE_EXCEPT( Exception::ERR_ITEM_NOT_FOUND, "Could not find material " + name,
181
0
                "BillboardSet::setMaterialName" );
182
183
        /* Ensure that the new material was loaded (will not load again if
184
           already loaded anyway)
185
        */
186
0
        mMaterial->load();
187
0
    }
188
189
    //-----------------------------------------------------------------------
190
    void BillboardSet::_sortBillboards( Camera* cam)
191
0
    {
192
0
        static RadixSort<BillboardPool, Billboard*, float> mRadixSorter;
193
194
0
        switch (_getSortMode())
195
0
        {
196
0
        case SM_DIRECTION:
197
0
            mRadixSorter.sort(mBillboardPool.begin(), mBillboardPool.begin() + mActiveBillboards,
198
0
                              SortByDirectionFunctor(-mCamDir));
199
0
            break;
200
0
        case SM_DISTANCE:
201
0
            mRadixSorter.sort(mBillboardPool.begin(), mBillboardPool.begin() + mActiveBillboards,
202
0
                              SortByDistanceFunctor(mCamPos));
203
0
            break;
204
0
        }
205
0
    }
206
    BillboardSet::SortByDirectionFunctor::SortByDirectionFunctor(const Vector3& dir)
207
0
        : sortDir(dir)
208
0
    {
209
0
    }
210
    float BillboardSet::SortByDirectionFunctor::operator()(Billboard* bill) const
211
0
    {
212
0
        return sortDir.dotProduct(bill->getPosition());
213
0
    }
214
    BillboardSet::SortByDistanceFunctor::SortByDistanceFunctor(const Vector3& pos)
215
0
        : sortPos(pos)
216
0
    {
217
0
    }
218
    float BillboardSet::SortByDistanceFunctor::operator()(Billboard* bill) const
219
0
    {
220
        // Sort descending by squared distance
221
0
        return - (sortPos - bill->getPosition()).squaredLength();
222
0
    }
223
    //-----------------------------------------------------------------------
224
    SortMode BillboardSet::_getSortMode(void) const
225
0
    {
226
        // Need to sort by distance if we're using accurate facing, or perpendicular billboard type.
227
0
        if (mAccurateFacing ||
228
0
            mBillboardType == BBT_PERPENDICULAR_SELF ||
229
0
            mBillboardType == BBT_PERPENDICULAR_COMMON)
230
0
        {
231
0
            return SM_DISTANCE;
232
0
        }
233
0
        else
234
0
        {
235
0
            return SM_DIRECTION;
236
0
        }
237
0
    }
238
    //-----------------------------------------------------------------------
239
    void BillboardSet::_notifyCurrentCamera( Camera* cam )
240
0
    {
241
0
        MovableObject::_notifyCurrentCamera(cam);
242
243
0
        mCurrentCamera = cam;
244
245
        // Calculate camera orientation and position
246
0
        mCamQ = mCurrentCamera->getDerivedOrientation();
247
0
        mCamPos = mCurrentCamera->getDerivedPosition();
248
0
        if (!mWorldSpace)
249
0
        {
250
            // Default behaviour is that billboards are in local node space
251
            // so orientation of camera (in world space) must be reverse-transformed
252
            // into node space
253
0
            mCamQ = mParentNode->convertWorldToLocalOrientation(mCamQ);
254
0
            mCamPos = mParentNode->convertWorldToLocalPosition(mCamPos);
255
0
        }
256
257
        // Camera direction points down -Z
258
0
        mCamDir = mCamQ * Vector3::NEGATIVE_UNIT_Z;
259
0
    }
260
    //-----------------------------------------------------------------------
261
    void BillboardSet::beginBillboards(size_t numBillboards)
262
0
    {
263
        /* Generate the vertices for all the billboards relative to the camera
264
           Also take the opportunity to update the vertex colours
265
           May as well do it here to save on loops elsewhere
266
        */
267
268
        /* NOTE: most engines generate world coordinates for the billboards
269
           directly, taking the world axes of the camera as offsets to the
270
           center points. I take a different approach, reverse-transforming
271
           the camera world axes into local billboard space.
272
           Why?
273
           Well, it's actually more efficient this way, because I only have to
274
           reverse-transform using the billboardset world matrix (inverse)
275
           once, from then on it's simple additions (assuming identically
276
           sized billboards). If I transformed every billboard center by it's
277
           world transform, that's a matrix multiplication per billboard
278
           instead.
279
           I leave the final transform to the render pipeline since that can
280
           use hardware TnL if it is available.
281
        */
282
283
        // create vertex and index buffers if they haven't already been
284
0
        if(!mBuffersCreated)
285
0
            _createBuffers();
286
287
        // Only calculate vertex offets et al if we're not point rendering
288
0
        if (!mPointRendering)
289
0
        {
290
291
            // Get offsets for origin type
292
0
            getParametricOffsets(mLeftOff, mRightOff, mTopOff, mBottomOff);
293
294
            // Generate axes etc up-front if not oriented per-billboard
295
0
            if (mBillboardType != BBT_ORIENTED_SELF &&
296
0
                mBillboardType != BBT_PERPENDICULAR_SELF && 
297
0
                !(mAccurateFacing && mBillboardType != BBT_PERPENDICULAR_COMMON))
298
0
            {
299
0
                genBillboardAxes(&mCamX, &mCamY);
300
301
                /* If all billboards are the same size we can precalculate the
302
                   offsets and just use '+' instead of '*' for each billboard,
303
                   and it should be faster.
304
                */
305
0
                genVertOffsets(mLeftOff, mRightOff, mTopOff, mBottomOff,
306
0
                    mDefaultWidth, mDefaultHeight, mCamX, mCamY, mVOffset);
307
308
0
            }
309
0
        }
310
311
        // Init num visible
312
0
        mNumVisibleBillboards = 0;
313
314
        // Lock the buffer
315
0
        if (numBillboards) // optimal lock
316
0
        {
317
            // clamp to max
318
0
            numBillboards = std::min(mPoolSize, numBillboards);
319
320
0
            size_t billboardSize;
321
0
            if (mPointRendering)
322
0
            {
323
                // just one vertex per billboard (this also excludes texcoords)
324
0
                billboardSize = mMainBuf->getVertexSize();
325
0
            }
326
0
            else
327
0
            {
328
                // 4 corners
329
0
                billboardSize = mMainBuf->getVertexSize() * 4;
330
0
            }
331
0
            assert (numBillboards * billboardSize <= mMainBuf->getSizeInBytes());
332
333
0
            mLockPtr = static_cast<float*>(
334
0
                mMainBuf->lock(0, numBillboards * billboardSize, 
335
0
                mMainBuf->getUsage() & HardwareBuffer::HBU_DYNAMIC ?
336
0
                HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL) );
337
0
        }
338
0
        else // lock the entire thing
339
0
            mLockPtr = static_cast<float*>(
340
0
            mMainBuf->lock(mMainBuf->getUsage() & HardwareBuffer::HBU_DYNAMIC ?
341
0
            HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL) );
342
343
0
    }
344
    //-----------------------------------------------------------------------
345
    void BillboardSet::injectBillboard(const Billboard& bb)
346
0
    {
347
        // Don't accept injections beyond pool size
348
0
        if (mNumVisibleBillboards == mPoolSize) return;
349
350
        // Skip if not visible (NB always true if not bounds checking individual billboards)
351
0
        if (!billboardVisible(mCurrentCamera, bb)) return;
352
353
        // Increment visibles
354
0
        mNumVisibleBillboards++;
355
356
0
        if(mPointRendering)
357
0
        {
358
0
            genPointVertices(bb);
359
0
            return;
360
0
        }
361
362
0
        if ((mBillboardType == BBT_ORIENTED_SELF || mBillboardType == BBT_PERPENDICULAR_SELF ||
363
0
             (mAccurateFacing && mBillboardType != BBT_PERPENDICULAR_COMMON)))
364
0
        {
365
            // Have to generate axes & offsets per billboard
366
0
            genBillboardAxes(&mCamX, &mCamY, &bb);
367
0
        }
368
369
0
        if ((mBillboardType == BBT_ORIENTED_SELF || mBillboardType == BBT_PERPENDICULAR_SELF ||
370
0
             bb.mOwnDimensions || (mAccurateFacing && mBillboardType != BBT_PERPENDICULAR_COMMON)))
371
0
        {
372
            // If it has own dimensions, or self-oriented, gen offsets
373
0
            Vector3 vOwnOffset[4];
374
0
            Real width = bb.mOwnDimensions ? bb.mWidth : mDefaultWidth;
375
0
            Real height = bb.mOwnDimensions ? bb.mHeight : mDefaultHeight;
376
0
            genVertOffsets(mLeftOff, mRightOff, mTopOff, mBottomOff,
377
0
                width, height, mCamX, mCamY, vOwnOffset);
378
0
            genQuadVertices(vOwnOffset, bb);
379
0
        }
380
0
        else
381
0
        {
382
            // Use default dimension, already computed before the loop, for faster creation
383
0
            genQuadVertices(mVOffset, bb);
384
0
        }
385
0
    }
386
    //-----------------------------------------------------------------------
387
    void BillboardSet::endBillboards(void)
388
0
    {
389
0
        mMainBuf->unlock();
390
0
    }
391
    //-----------------------------------------------------------------------
392
    void BillboardSet::setBounds(const AxisAlignedBox& box, Real radius)
393
0
    {
394
0
        mAABB = box;
395
0
        mBoundingRadius = radius;
396
0
    }
397
    //-----------------------------------------------------------------------
398
    void BillboardSet::_updateBounds(void)
399
0
    {
400
0
        if (mActiveBillboards == 0)
401
0
        {
402
            // No billboards, null bbox
403
0
            mAABB.setNull();
404
0
            mBoundingRadius = 0.0f;
405
0
        }
406
0
        else
407
0
        {
408
0
            mAABB.setNull();
409
0
            auto iend = mBillboardPool.begin() + mActiveBillboards;
410
0
            Affine3 invWorld;
411
0
            bool invert = mWorldSpace && getParentSceneNode();
412
0
            if (invert)
413
0
                invWorld = getParentSceneNode()->_getFullTransform().inverse();
414
415
0
            for (auto i = mBillboardPool.begin(); i != iend; ++i)
416
0
            {
417
0
                Vector3 pos = (*i)->getPosition();
418
                // transform from world space to local space
419
0
                if (invert)
420
0
                    pos = invWorld * pos;
421
422
0
                mAABB.merge(pos);
423
0
            }
424
            // Adjust for billboard size
425
0
            Real adjust = std::max(mDefaultWidth, mDefaultHeight);
426
0
            Vector3 vecAdjust(adjust, adjust, adjust);
427
428
0
            mAABB.setExtents(mAABB.getMinimum() - vecAdjust, mAABB.getMaximum() + vecAdjust);
429
0
            mBoundingRadius = Math::boundingRadiusFromAABB(mAABB);
430
0
        }
431
432
0
        if (mParentNode)
433
0
            mParentNode->needUpdate();
434
435
0
    }
436
437
    //-----------------------------------------------------------------------
438
    void BillboardSet::_updateRenderQueue(RenderQueue* queue)
439
0
    {
440
        // If we're driving this from our own data, update geometry if need to.
441
0
        if (!mExternalData && (mAutoUpdate || mBillboardDataChanged || !mBuffersCreated))
442
0
        {
443
0
            if (mSortingEnabled)
444
0
            {
445
0
                _sortBillboards(mCurrentCamera);
446
0
            }
447
448
0
            beginBillboards(mActiveBillboards);
449
0
            auto iend = mBillboardPool.begin() + mActiveBillboards;
450
0
            for (auto it = mBillboardPool.begin(); it != iend; ++it)
451
0
            {
452
0
                injectBillboard(*(*it));
453
0
            }
454
0
            endBillboards();
455
0
            mBillboardDataChanged = false;
456
0
        }
457
458
        //only set the render queue group if it has been explicitly set.
459
0
        if (mRenderQueuePrioritySet)
460
0
        {
461
0
            assert(mRenderQueueIDSet == true);
462
0
            queue->addRenderable(this, mRenderQueueID, mRenderQueuePriority);
463
0
        }
464
0
        else if( mRenderQueueIDSet )
465
0
        {
466
0
           queue->addRenderable(this, mRenderQueueID);
467
0
        } else {
468
0
           queue->addRenderable(this);
469
0
        }
470
471
0
    }
472
473
    void BillboardSet::setMaterial( const MaterialPtr& material )
474
0
    {
475
0
        OgreAssert(material, "material is NULL");
476
0
        mMaterial = material;
477
478
        // Ensure new material loaded (will not load again if already loaded)
479
0
        mMaterial->load();
480
0
    }
481
482
    //-----------------------------------------------------------------------
483
    void BillboardSet::getRenderOperation(RenderOperation& op)
484
0
    {
485
0
        op.vertexData = mVertexData.get();
486
0
        op.vertexData->vertexStart = 0;
487
488
0
        if (mPointRendering)
489
0
        {
490
0
            op.operationType = RenderOperation::OT_POINT_LIST;
491
0
            op.useIndexes = false;
492
0
            op.indexData = 0;
493
0
            op.vertexData->vertexCount = mNumVisibleBillboards;
494
0
        }
495
0
        else
496
0
        {
497
0
            op.operationType = RenderOperation::OT_TRIANGLE_LIST;
498
0
            op.useIndexes = true;
499
500
0
            op.vertexData->vertexCount = mNumVisibleBillboards * 4;
501
502
0
            op.indexData = mIndexData.get();
503
0
            op.indexData->indexCount = mNumVisibleBillboards * 6;
504
0
            op.indexData->indexStart = 0;
505
0
        }
506
0
    }
507
508
    //-----------------------------------------------------------------------
509
    void BillboardSet::getWorldTransforms( Matrix4* xform ) const
510
0
    {
511
0
        if (mWorldSpace)
512
0
        {
513
0
            *xform = Matrix4::IDENTITY;
514
0
        }
515
0
        else
516
0
        {
517
0
            *xform = _getParentNodeFullTransform();
518
0
        }
519
0
    }
520
521
    //-----------------------------------------------------------------------
522
    void BillboardSet::setPoolSize( size_t size )
523
0
    {
524
        // If we're driving this from our own data, allocate billboards
525
0
        if (!mExternalData)
526
0
        {
527
            // Never shrink below size()
528
0
            size_t currSize = mBillboardPool.size();
529
0
            if (currSize >= size)
530
0
                return;
531
532
0
            this->increasePool(size);
533
0
        }
534
535
0
        mPoolSize = size;
536
537
0
        _destroyBuffers();
538
0
    }
539
540
    //-----------------------------------------------------------------------
541
    void BillboardSet::_createBuffers(void)
542
0
    {
543
        /* Allocate / reallocate vertex data
544
           Note that we allocate enough space for ALL the billboards in the pool, but only issue
545
           rendering operations for the sections relating to the active billboards
546
        */
547
548
        /* Alloc positions   ( 1 or 4 verts per billboard, 3 components )
549
                 colours     ( 1 x RGBA per vertex )
550
                 indices     ( 6 per billboard ( 2 tris ) if not point rendering )
551
                 tex. coords ( 2D coords, 1 or 4 per billboard )
552
        */
553
554
        // Warn if user requested an invalid setup
555
        // Do it here so it only appears once
556
0
        if (mPointRendering && mBillboardType != BBT_POINT)
557
0
        {
558
559
0
            LogManager::getSingleton().logWarning("BillboardSet " +
560
0
                mName + " has point rendering enabled but is using a type "
561
0
                "other than BBT_POINT, this may not give you the results you "
562
0
                "expect.");
563
0
        }
564
565
0
        mVertexData = std::make_unique<VertexData>();
566
0
        if (mPointRendering)
567
0
            mVertexData->vertexCount = mPoolSize;
568
0
        else
569
0
            mVertexData->vertexCount = mPoolSize * 4;
570
571
0
        mVertexData->vertexStart = 0;
572
573
        // Vertex declaration
574
0
        VertexDeclaration* decl = mVertexData->vertexDeclaration;
575
0
        VertexBufferBinding* binding = mVertexData->vertexBufferBinding;
576
577
0
        size_t offset = 0;
578
0
        offset += decl->addElement(0, offset, VET_FLOAT3, VES_POSITION).getSize();
579
0
        offset += decl->addElement(0, offset, VET_UBYTE4_NORM, VES_DIFFUSE).getSize();
580
        // Texture coords irrelevant when enabled point rendering (generated
581
        // in point sprite mode, and unused in standard point mode)
582
0
        if (!mPointRendering)
583
0
        {
584
0
            decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
585
0
        }
586
587
0
        mMainBuf =
588
0
            HardwareBufferManager::getSingleton().createVertexBuffer(
589
0
                decl->getVertexSize(0),
590
0
                mVertexData->vertexCount,
591
0
                mAutoUpdate ? HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE : 
592
0
                HardwareBuffer::HBU_STATIC_WRITE_ONLY);
593
        // bind position and diffuses
594
0
        binding->setBinding(0, mMainBuf);
595
596
0
        if (!mPointRendering)
597
0
        {
598
0
            mIndexData = std::make_unique<IndexData>();
599
0
            mIndexData->indexStart = 0;
600
0
            mIndexData->indexCount = mPoolSize * 6;
601
602
0
            mIndexData->indexBuffer = HardwareBufferManager::getSingleton().
603
0
                createIndexBuffer(HardwareIndexBuffer::IT_16BIT,
604
0
                    mIndexData->indexCount,
605
0
                    HardwareBuffer::HBU_STATIC_WRITE_ONLY);
606
607
            /* Create indexes (will be the same every frame)
608
               Using indexes because it means 1/3 less vertex transforms (4 instead of 6)
609
610
               Billboard layout relative to camera:
611
612
                0-----1
613
                |    /|
614
                |  /  |
615
                |/    |
616
                2-----3
617
            */
618
619
0
            HardwareBufferLockGuard indexLock(mIndexData->indexBuffer, HardwareBuffer::HBL_DISCARD);
620
0
            ushort* pIdx = static_cast<ushort*>(indexLock.pData);
621
622
0
            for(
623
0
                size_t idx, idxOff, bboard = 0;
624
0
                bboard < mPoolSize;
625
0
                ++bboard )
626
0
            {
627
                // Do indexes
628
0
                idx    = bboard * 6;
629
0
                idxOff = bboard * 4;
630
631
0
                pIdx[idx] = static_cast<unsigned short>(idxOff); // + 0;, for clarity
632
0
                pIdx[idx+1] = static_cast<unsigned short>(idxOff + 2);
633
0
                pIdx[idx+2] = static_cast<unsigned short>(idxOff + 1);
634
0
                pIdx[idx+3] = static_cast<unsigned short>(idxOff + 1);
635
0
                pIdx[idx+4] = static_cast<unsigned short>(idxOff + 2);
636
0
                pIdx[idx+5] = static_cast<unsigned short>(idxOff + 3);
637
638
0
            }
639
0
        }
640
0
        mBuffersCreated = true;
641
0
    }
642
    //-----------------------------------------------------------------------
643
    void BillboardSet::_destroyBuffers(void)
644
0
    {
645
0
        mVertexData.reset();
646
0
        mIndexData.reset();
647
0
        mMainBuf.reset();
648
649
0
        mBuffersCreated = false;
650
0
    }
651
652
    //-----------------------------------------------------------------------
653
    void BillboardSet::getParametricOffsets(
654
        Real& left, Real& right, Real& top, Real& bottom )
655
0
    {
656
0
        switch( mOriginType )
657
0
        {
658
0
        case BBO_TOP_LEFT:
659
0
            left = 0.0f;
660
0
            right = 1.0f;
661
0
            top = 0.0f;
662
0
            bottom = -1.0f;
663
0
            break;
664
665
0
        case BBO_TOP_CENTER:
666
0
            left = -0.5f;
667
0
            right = 0.5f;
668
0
            top = 0.0f;
669
0
            bottom = -1.0f;
670
0
            break;
671
672
0
        case BBO_TOP_RIGHT:
673
0
            left = -1.0f;
674
0
            right = 0.0f;
675
0
            top = 0.0f;
676
0
            bottom = -1.0f;
677
0
            break;
678
679
0
        case BBO_CENTER_LEFT:
680
0
            left = 0.0f;
681
0
            right = 1.0f;
682
0
            top = 0.5f;
683
0
            bottom = -0.5f;
684
0
            break;
685
686
0
        case BBO_CENTER:
687
0
            left = -0.5f;
688
0
            right = 0.5f;
689
0
            top = 0.5f;
690
0
            bottom = -0.5f;
691
0
            break;
692
693
0
        case BBO_CENTER_RIGHT:
694
0
            left = -1.0f;
695
0
            right = 0.0f;
696
0
            top = 0.5f;
697
0
            bottom = -0.5f;
698
0
            break;
699
700
0
        case BBO_BOTTOM_LEFT:
701
0
            left = 0.0f;
702
0
            right = 1.0f;
703
0
            top = 1.0f;
704
0
            bottom = 0.0f;
705
0
            break;
706
707
0
        case BBO_BOTTOM_CENTER:
708
0
            left = -0.5f;
709
0
            right = 0.5f;
710
0
            top = 1.0f;
711
0
            bottom = 0.0f;
712
0
            break;
713
714
0
        case BBO_BOTTOM_RIGHT:
715
0
            left = -1.0f;
716
0
            right = 0.0f;
717
0
            top = 1.0f;
718
0
            bottom = 0.0f;
719
0
            break;
720
0
        }
721
0
    }
722
    //-----------------------------------------------------------------------
723
    bool BillboardSet::billboardVisible(Camera* cam, const Billboard& bill)
724
0
    {
725
        // Return always visible if not culling individually
726
0
        if (!mCullIndividual) return true;
727
728
        // Cull based on sphere (have to transform less)
729
0
        Sphere sph;
730
0
        Matrix4 xworld;
731
732
0
        getWorldTransforms(&xworld);
733
734
0
        sph.setCenter(xworld * bill.mPosition);
735
736
0
        if (bill.mOwnDimensions)
737
0
        {
738
0
            sph.setRadius(std::max(bill.mWidth, bill.mHeight));
739
0
        }
740
0
        else
741
0
        {
742
0
            sph.setRadius(std::max(mDefaultWidth, mDefaultHeight));
743
0
        }
744
745
0
        return cam->isVisible(sph);
746
747
0
    }
748
    //-----------------------------------------------------------------------
749
    void BillboardSet::increasePool(size_t size)
750
0
    {
751
0
        size_t oldSize = mBillboardPool.size();
752
753
        // Increase size
754
0
        mBillboardPool.resize(size);
755
756
        // Create new billboards
757
0
        for( size_t i = oldSize; i < size; ++i )
758
0
            mBillboardPool[i] = OGRE_NEW Billboard();
759
760
0
    }
761
    //-----------------------------------------------------------------------
762
    void BillboardSet::genBillboardAxes(Vector3* pX, Vector3 *pY, const Billboard* bb)
763
0
    {
764
        // If we're using accurate facing, recalculate camera direction per BB
765
0
        if (mAccurateFacing && 
766
0
            (mBillboardType == BBT_POINT || 
767
0
            mBillboardType == BBT_ORIENTED_COMMON ||
768
0
            mBillboardType == BBT_ORIENTED_SELF))
769
0
        {
770
            // cam -> bb direction
771
0
            mCamDir = bb->mPosition - mCamPos;
772
0
            mCamDir.normalise();
773
0
        }
774
775
776
0
        switch (mBillboardType)
777
0
        {
778
0
        case BBT_POINT:
779
0
            if (mAccurateFacing)
780
0
            {
781
                // Point billboards will have 'up' based on but not equal to cameras
782
                // Use pY temporarily to avoid allocation
783
0
                *pY = mCamQ * Vector3::UNIT_Y;
784
0
                *pX = mCamDir.crossProduct(*pY);
785
0
                pX->normalise();
786
0
                *pY = pX->crossProduct(mCamDir); // both normalised already
787
0
            }
788
0
            else
789
0
            {
790
                // Get camera axes for X and Y (depth is irrelevant)
791
0
                *pX = mCamQ * Vector3::UNIT_X;
792
0
                *pY = mCamQ * Vector3::UNIT_Y;
793
0
            }
794
0
            break;
795
796
0
        case BBT_ORIENTED_COMMON:
797
            // Y-axis is common direction
798
            // X-axis is cross with camera direction
799
0
            *pY = mCommonDirection;
800
0
            *pX = mCamDir.crossProduct(*pY);
801
0
            pX->normalise();
802
0
            break;
803
804
0
        case BBT_ORIENTED_SELF:
805
            // Y-axis is direction
806
            // X-axis is cross with camera direction
807
            // Scale direction first
808
0
            *pY = bb->mDirection;
809
0
            *pX = mCamDir.crossProduct(*pY);
810
0
            pX->normalise();
811
0
            break;
812
813
0
        case BBT_PERPENDICULAR_COMMON:
814
            // X-axis is up-vector cross common direction
815
            // Y-axis is common direction cross X-axis
816
0
            *pX = mCommonUpVector.crossProduct(mCommonDirection);
817
0
            *pY = mCommonDirection.crossProduct(*pX);
818
0
            break;
819
820
0
        case BBT_PERPENDICULAR_SELF:
821
            // X-axis is up-vector cross own direction
822
            // Y-axis is own direction cross X-axis
823
0
            *pX = mCommonUpVector.crossProduct(bb->mDirection);
824
0
            pX->normalise();
825
0
            *pY = bb->mDirection.crossProduct(*pX); // both should be normalised
826
0
            break;
827
0
        }
828
829
0
    }
830
    //-----------------------------------------------------------------------
831
    uint32 BillboardSet::getTypeFlags(void) const
832
0
    {
833
0
        return SceneManager::FX_TYPE_MASK;
834
0
    }
835
    //-----------------------------------------------------------------------
836
    void BillboardSet::genPointVertices(const Billboard& bb)
837
0
    {
838
0
        RGBA colour = bb.mColour;
839
        // Single vertex per billboard, ignore offsets
840
        // position
841
0
        *mLockPtr++ = bb.mPosition.x;
842
0
        *mLockPtr++ = bb.mPosition.y;
843
0
        *mLockPtr++ = bb.mPosition.z;
844
        // Colour
845
0
        memcpy(mLockPtr++, &colour, sizeof(RGBA));
846
        // No texture coords in point rendering
847
0
    }
848
    void BillboardSet::genQuadVertices(const Vector3* const offsets, const Billboard& bb)
849
0
    {
850
0
        RGBA colour = bb.mColour;
851
852
        // Texcoords
853
0
        assert(bb.mUseTexcoordRect || bb.mTexcoordIndex < mTextureCoords.size());
854
0
        const Ogre::FloatRect& r =
855
0
            bb.mUseTexcoordRect ? bb.mTexcoordRect : mTextureCoords[bb.mTexcoordIndex];
856
857
0
        if (bb.mRotation == Radian(0))
858
0
        {
859
            // Left-top
860
            // Positions
861
0
            *mLockPtr++ = offsets[0].x + bb.mPosition.x;
862
0
            *mLockPtr++ = offsets[0].y + bb.mPosition.y;
863
0
            *mLockPtr++ = offsets[0].z + bb.mPosition.z;
864
            // Colour
865
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
866
            // Texture coords
867
0
            *mLockPtr++ = r.left;
868
0
            *mLockPtr++ = r.top;
869
870
            // Right-top
871
            // Positions
872
0
            *mLockPtr++ = offsets[1].x + bb.mPosition.x;
873
0
            *mLockPtr++ = offsets[1].y + bb.mPosition.y;
874
0
            *mLockPtr++ = offsets[1].z + bb.mPosition.z;
875
            // Colour
876
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
877
            // Texture coords
878
0
            *mLockPtr++ = r.right;
879
0
            *mLockPtr++ = r.top;
880
881
            // Left-bottom
882
            // Positions
883
0
            *mLockPtr++ = offsets[2].x + bb.mPosition.x;
884
0
            *mLockPtr++ = offsets[2].y + bb.mPosition.y;
885
0
            *mLockPtr++ = offsets[2].z + bb.mPosition.z;
886
            // Colour
887
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
888
            // Texture coords
889
0
            *mLockPtr++ = r.left;
890
0
            *mLockPtr++ = r.bottom;
891
892
            // Right-bottom
893
            // Positions
894
0
            *mLockPtr++ = offsets[3].x + bb.mPosition.x;
895
0
            *mLockPtr++ = offsets[3].y + bb.mPosition.y;
896
0
            *mLockPtr++ = offsets[3].z + bb.mPosition.z;
897
            // Colour
898
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
899
            // Texture coords
900
0
            *mLockPtr++ = r.right;
901
0
            *mLockPtr++ = r.bottom;
902
0
        }
903
0
        else if (mRotationType == BBR_VERTEX)
904
0
        {
905
            // TODO: Cache axis when billboard type is BBT_POINT or BBT_PERPENDICULAR_COMMON
906
0
            Vector3 axis = (offsets[3] - offsets[0]).crossProduct(offsets[2] - offsets[1]).normalisedCopy();
907
908
0
            Matrix3 rotation;
909
0
            rotation.FromAngleAxis(axis, bb.mRotation);
910
911
0
            Vector3 pt;
912
913
            // Left-top
914
            // Positions
915
0
            pt = rotation * offsets[0];
916
0
            *mLockPtr++ = pt.x + bb.mPosition.x;
917
0
            *mLockPtr++ = pt.y + bb.mPosition.y;
918
0
            *mLockPtr++ = pt.z + bb.mPosition.z;
919
            // Colour
920
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
921
            // Texture coords
922
0
            *mLockPtr++ = r.left;
923
0
            *mLockPtr++ = r.top;
924
925
            // Right-top
926
            // Positions
927
0
            pt = rotation * offsets[1];
928
0
            *mLockPtr++ = pt.x + bb.mPosition.x;
929
0
            *mLockPtr++ = pt.y + bb.mPosition.y;
930
0
            *mLockPtr++ = pt.z + bb.mPosition.z;
931
            // Colour
932
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
933
            // Texture coords
934
0
            *mLockPtr++ = r.right;
935
0
            *mLockPtr++ = r.top;
936
937
            // Left-bottom
938
            // Positions
939
0
            pt = rotation * offsets[2];
940
0
            *mLockPtr++ = pt.x + bb.mPosition.x;
941
0
            *mLockPtr++ = pt.y + bb.mPosition.y;
942
0
            *mLockPtr++ = pt.z + bb.mPosition.z;
943
            // Colour
944
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
945
            // Texture coords
946
0
            *mLockPtr++ = r.left;
947
0
            *mLockPtr++ = r.bottom;
948
949
            // Right-bottom
950
            // Positions
951
0
            pt = rotation * offsets[3];
952
0
            *mLockPtr++ = pt.x + bb.mPosition.x;
953
0
            *mLockPtr++ = pt.y + bb.mPosition.y;
954
0
            *mLockPtr++ = pt.z + bb.mPosition.z;
955
            // Colour
956
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
957
            // Texture coords
958
0
            *mLockPtr++ = r.right;
959
0
            *mLockPtr++ = r.bottom;
960
0
        }
961
0
        else
962
0
        {
963
0
            const Real      cos_rot  ( Math::Cos(bb.mRotation)   );
964
0
            const Real      sin_rot  ( Math::Sin(bb.mRotation)   );
965
966
0
            float width = (r.right-r.left)/2;
967
0
            float height = (r.bottom-r.top)/2;
968
0
            float mid_u = r.left+width;
969
0
            float mid_v = r.top+height;
970
971
0
            float cos_rot_w = cos_rot * width;
972
0
            float cos_rot_h = cos_rot * height;
973
0
            float sin_rot_w = sin_rot * width;
974
0
            float sin_rot_h = sin_rot * height;
975
976
            // Left-top
977
            // Positions
978
0
            *mLockPtr++ = offsets[0].x + bb.mPosition.x;
979
0
            *mLockPtr++ = offsets[0].y + bb.mPosition.y;
980
0
            *mLockPtr++ = offsets[0].z + bb.mPosition.z;
981
            // Colour
982
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
983
            // Texture coords
984
0
            *mLockPtr++ = mid_u - cos_rot_w + sin_rot_h;
985
0
            *mLockPtr++ = mid_v - sin_rot_w - cos_rot_h;
986
987
            // Right-top
988
            // Positions
989
0
            *mLockPtr++ = offsets[1].x + bb.mPosition.x;
990
0
            *mLockPtr++ = offsets[1].y + bb.mPosition.y;
991
0
            *mLockPtr++ = offsets[1].z + bb.mPosition.z;
992
            // Colour
993
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
994
            // Texture coords
995
0
            *mLockPtr++ = mid_u + cos_rot_w + sin_rot_h;
996
0
            *mLockPtr++ = mid_v + sin_rot_w - cos_rot_h;
997
998
            // Left-bottom
999
            // Positions
1000
0
            *mLockPtr++ = offsets[2].x + bb.mPosition.x;
1001
0
            *mLockPtr++ = offsets[2].y + bb.mPosition.y;
1002
0
            *mLockPtr++ = offsets[2].z + bb.mPosition.z;
1003
            // Colour
1004
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
1005
            // Texture coords
1006
0
            *mLockPtr++ = mid_u - cos_rot_w - sin_rot_h;
1007
0
            *mLockPtr++ = mid_v - sin_rot_w + cos_rot_h;
1008
1009
            // Right-bottom
1010
            // Positions
1011
0
            *mLockPtr++ = offsets[3].x + bb.mPosition.x;
1012
0
            *mLockPtr++ = offsets[3].y + bb.mPosition.y;
1013
0
            *mLockPtr++ = offsets[3].z + bb.mPosition.z;
1014
            // Colour
1015
0
            memcpy(mLockPtr++, &colour, sizeof(RGBA));
1016
            // Texture coords
1017
0
            *mLockPtr++ = mid_u + cos_rot_w - sin_rot_h;
1018
0
            *mLockPtr++ = mid_v + sin_rot_w + cos_rot_h;
1019
0
        }
1020
0
    }
1021
    //-----------------------------------------------------------------------
1022
    void BillboardSet::genVertOffsets(Real inleft, Real inright, Real intop, Real inbottom,
1023
        Real width, Real height, const Vector3& x, const Vector3& y, Vector3* pDestVec)
1024
0
    {
1025
0
        Vector3 vLeftOff, vRightOff, vTopOff, vBottomOff;
1026
        /* Calculate default offsets. Scale the axes by
1027
           parametric offset and dimensions, ready to be added to
1028
           positions.
1029
        */
1030
1031
0
        vLeftOff   = x * ( inleft   * width );
1032
0
        vRightOff  = x * ( inright  * width );
1033
0
        vTopOff    = y * ( intop   * height );
1034
0
        vBottomOff = y * ( inbottom * height );
1035
1036
        // Make final offsets to vertex positions
1037
0
        pDestVec[0] = vLeftOff  + vTopOff;
1038
0
        pDestVec[1] = vRightOff + vTopOff;
1039
0
        pDestVec[2] = vLeftOff  + vBottomOff;
1040
0
        pDestVec[3] = vRightOff + vBottomOff;
1041
1042
0
    }
1043
    //-----------------------------------------------------------------------
1044
    const String& BillboardSet::getMovableType(void) const
1045
0
    {
1046
0
        return MOT_BILLBOARD_SET;
1047
0
    }
1048
    //-----------------------------------------------------------------------
1049
    Real BillboardSet::getSquaredViewDepth(const Camera* const cam) const
1050
0
    {
1051
0
        assert(mParentNode);
1052
0
        return mParentNode->getSquaredViewDepth(cam);
1053
0
    }
1054
    //-----------------------------------------------------------------------
1055
    const LightList& BillboardSet::getLights(void) const
1056
0
    {
1057
        // It's actually quite unlikely that this will be called,
1058
        // because most billboards are unlit, but here we go anyway
1059
0
        return queryLights();
1060
0
    }
1061
    //---------------------------------------------------------------------
1062
    void BillboardSet::visitRenderables(Renderable::Visitor* visitor, 
1063
        bool debugRenderables)
1064
0
    {
1065
        // only one renderable
1066
0
        visitor->visit(this, 0, false);
1067
0
    }
1068
1069
1070
    void BillboardSet::setTextureCoords(const std::vector<FloatRect>& coords)
1071
0
    {
1072
0
        if(coords.empty()) {
1073
0
            setTextureStacksAndSlices(1, 1);
1074
0
            return;
1075
0
        }
1076
0
        mTextureCoords = coords;
1077
0
    }
1078
1079
    void BillboardSet::setTextureStacksAndSlices( uchar stacks, uchar slices )
1080
0
{
1081
0
        if(stacks == 0) stacks = 1;
1082
0
        if(slices == 0) slices = 1;
1083
        //  clear out any previous allocation (as vectors may not shrink)
1084
0
        TextureCoordSets().swap(mTextureCoords);
1085
        //  make room
1086
0
        mTextureCoords.resize((size_t)stacks * slices);
1087
0
        unsigned int coordIndex = 0;
1088
        //  spread the U and V coordinates across the rects
1089
0
        for(uint v = 0; v < stacks; ++v) {
1090
            //  (float)X / X is guaranteed to be == 1.0f for X up to 8 million, so
1091
            //  our range of 1..256 is quite enough to guarantee perfect coverage.
1092
0
            float top = (float)v / (float)stacks;
1093
0
            float bottom = ((float)v + 1) / (float)stacks;
1094
0
            for(uint u = 0; u < slices; ++u) {
1095
0
                Ogre::FloatRect & r = mTextureCoords[coordIndex];
1096
0
                r.left = (float)u / (float)slices;
1097
0
                r.bottom = bottom;
1098
0
                r.right = ((float)u + 1) / (float)slices;
1099
0
                r.top = top;
1100
0
                ++coordIndex;
1101
0
            }
1102
0
        }
1103
0
        assert( coordIndex == (size_t)stacks * slices );
1104
0
    }
1105
    //-----------------------------------------------------------------------
1106
    void BillboardSet::setPointRenderingEnabled(bool enabled)
1107
0
    {
1108
        // Override point rendering if not supported
1109
0
        if (enabled && !Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_POINT_SPRITES))
1110
0
        {
1111
0
            enabled = false;
1112
0
        }
1113
1114
0
        if (enabled != mPointRendering)
1115
0
        {
1116
0
            mPointRendering = enabled;
1117
            // Different buffer structure (1 or 4 verts per billboard)
1118
0
            _destroyBuffers();
1119
0
        }
1120
0
    }
1121
1122
    //-----------------------------------------------------------------------
1123
    void BillboardSet::setAutoUpdate(bool autoUpdate)
1124
0
    {
1125
        // Case auto update buffers changed we have to destroy the current buffers
1126
        // since their usage will be different.
1127
0
        if (autoUpdate != mAutoUpdate)
1128
0
        {
1129
0
            mAutoUpdate = autoUpdate;
1130
0
            _destroyBuffers();
1131
0
        }
1132
0
    }
1133
1134
    //-----------------------------------------------------------------------
1135
    //-----------------------------------------------------------------------
1136
    const String MOT_BILLBOARD_SET = "BillboardSet";
1137
    //-----------------------------------------------------------------------
1138
    const String& BillboardSetFactory::getType(void) const
1139
0
    {
1140
0
        return MOT_BILLBOARD_SET;
1141
0
    }
1142
    //-----------------------------------------------------------------------
1143
    MovableObject* BillboardSetFactory::createInstanceImpl( const String& name,
1144
        const NameValuePairList* params)
1145
0
    {
1146
        // may have parameters
1147
0
        bool externalData = false;
1148
0
        unsigned int poolSize = 0;
1149
1150
0
        if (params != 0)
1151
0
        {
1152
0
            NameValuePairList::const_iterator ni = params->find("poolSize");
1153
0
            if (ni != params->end())
1154
0
            {
1155
0
                poolSize = StringConverter::parseUnsignedInt(ni->second);
1156
0
            }
1157
0
            ni = params->find("externalData");
1158
0
            if (ni != params->end())
1159
0
            {
1160
0
                externalData = StringConverter::parseBool(ni->second);
1161
0
            }
1162
0
        }
1163
1164
0
        if (poolSize > 0)
1165
0
        {
1166
0
            return OGRE_NEW BillboardSet(name, poolSize, externalData);
1167
0
        }
1168
0
        else
1169
0
        {
1170
            return OGRE_NEW BillboardSet(name);
1171
0
        }
1172
0
    }
1173
}