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 
35 namespace Ogre {
36     // Init statics
37     RadixSort<BillboardSet::ActiveBillboardList, Billboard*, float> BillboardSet::mRadixSorter;
38 
39     //-----------------------------------------------------------------------
BillboardSet()40     BillboardSet::BillboardSet() :
41         mBoundingRadius(0.0f),
42         mOriginType( BBO_CENTER ),
43         mRotationType( BBR_TEXCOORD ),
44         mAllDefaultSize( true ),
45         mAutoExtendPool( true ),
46         mSortingEnabled(false),
47         mAccurateFacing(false),
48         mAllDefaultRotation(true),
49         mWorldSpace(false),
50         mCullIndividual( false ),
51         mBillboardType(BBT_POINT),
52         mCommonDirection(Ogre::Vector3::UNIT_Z),
53         mCommonUpVector(Vector3::UNIT_Y),
54         mPointRendering(false),
55         mBuffersCreated(false),
56         mPoolSize(0),
57         mExternalData(false),
58         mAutoUpdate(true),
59         mBillboardDataChanged(true)
60     {
61         setDefaultDimensions( 100, 100 );
62         mMaterial = MaterialManager::getSingleton().getDefaultMaterial();
63         mMaterial->load();
64         mCastShadows = false;
65         setTextureStacksAndSlices( 1, 1 );
66     }
67 
68     //-----------------------------------------------------------------------
BillboardSet(const String & name,unsigned int poolSize,bool externalData)69     BillboardSet::BillboardSet(
70         const String& name,
71         unsigned int poolSize,
72         bool externalData) :
73         MovableObject(name),
74         mBoundingRadius(0.0f),
75         mOriginType( BBO_CENTER ),
76         mRotationType( BBR_TEXCOORD ),
77         mAllDefaultSize( true ),
78         mAutoExtendPool( true ),
79         mSortingEnabled(false),
80         mAccurateFacing(false),
81         mAllDefaultRotation(true),
82         mWorldSpace(false),
83         mCullIndividual( false ),
84         mBillboardType(BBT_POINT),
85         mCommonDirection(Ogre::Vector3::UNIT_Z),
86         mCommonUpVector(Vector3::UNIT_Y),
87         mPointRendering(false),
88         mBuffersCreated(false),
89         mPoolSize(poolSize),
90         mExternalData(externalData),
91         mAutoUpdate(true),
92         mBillboardDataChanged(true)
93     {
94         setDefaultDimensions( 100, 100 );
95         mMaterial = MaterialManager::getSingleton().getDefaultMaterial();
96         mMaterial->load();
97         setPoolSize( poolSize );
98         mCastShadows = false;
99         setTextureStacksAndSlices( 1, 1 );
100     }
101     //-----------------------------------------------------------------------
~BillboardSet()102     BillboardSet::~BillboardSet()
103     {
104         // Free pool items
105         BillboardPool::iterator i;
106         for (i = mBillboardPool.begin(); i != mBillboardPool.end(); ++i)
107         {
108             OGRE_DELETE *i;
109         }
110 
111         // Delete shared buffers
112         _destroyBuffers();
113     }
114     //-----------------------------------------------------------------------
createBillboard(const Vector3 & position,const ColourValue & colour)115     Billboard* BillboardSet::createBillboard(
116         const Vector3& position,
117         const ColourValue& colour )
118     {
119         if( mFreeBillboards.empty() )
120         {
121             if( mAutoExtendPool )
122             {
123                 setPoolSize( getPoolSize() * 2 );
124             }
125             else
126             {
127                 return 0;
128             }
129         }
130 
131         // Get a new billboard
132         Billboard* newBill = mFreeBillboards.front();
133         mActiveBillboards.splice(
134             mActiveBillboards.end(), mFreeBillboards, mFreeBillboards.begin());
135         newBill->setPosition(position);
136         newBill->setColour(colour);
137         newBill->mDirection = Vector3::ZERO;
138         newBill->setRotation(Radian(0));
139         newBill->setTexcoordIndex(0);
140         newBill->resetDimensions();
141         newBill->_notifyOwner(this);
142 
143         // Merge into bounds
144         Real adjust = std::max(mDefaultWidth, mDefaultHeight);
145         Vector3 vecAdjust(adjust, adjust, adjust);
146         Vector3 newMin = position - vecAdjust;
147         Vector3 newMax = position + vecAdjust;
148 
149         mAABB.merge(newMin);
150         mAABB.merge(newMax);
151 
152         mBoundingRadius = Math::boundingRadiusFromAABB(mAABB);
153 
154         return newBill;
155     }
156 
157     //-----------------------------------------------------------------------
createBillboard(Real x,Real y,Real z,const ColourValue & colour)158     Billboard* BillboardSet::createBillboard(
159         Real x, Real y, Real z,
160         const ColourValue& colour )
161     {
162         return createBillboard( Vector3( x, y, z ), colour );
163     }
164 
165     //-----------------------------------------------------------------------
getNumBillboards(void) const166     int BillboardSet::getNumBillboards(void) const
167     {
168         return static_cast< int >( mActiveBillboards.size() );
169     }
170 
171     //-----------------------------------------------------------------------
clear()172     void BillboardSet::clear()
173     {
174         // Move actives to free list
175         mFreeBillboards.splice(mFreeBillboards.end(), mActiveBillboards);
176     }
177 
178     //-----------------------------------------------------------------------
getBillboard(unsigned int index) const179     Billboard* BillboardSet::getBillboard( unsigned int index ) const
180     {
181         assert(
182             index < mActiveBillboards.size() &&
183             "Billboard index out of bounds." );
184 
185         /* We can't access it directly, so we check whether it's in the first
186            or the second half, then we start either from the beginning or the
187            end of the list
188         */
189         ActiveBillboardList::const_iterator it;
190         if( index >= ( mActiveBillboards.size() >> 1 ) )
191         {
192             index = static_cast<unsigned int>(mActiveBillboards.size()) - index;
193             for( it = mActiveBillboards.end(); index; --index, --it );
194         }
195         else
196         {
197             for( it = mActiveBillboards.begin(); index; --index, ++it );
198         }
199 
200         return *it;
201     }
202 
203     //-----------------------------------------------------------------------
removeBillboard(unsigned int index)204     void BillboardSet::removeBillboard(unsigned int index)
205     {
206         assert(
207             index < mActiveBillboards.size() &&
208             "Billboard index out of bounds." );
209 
210         /* We can't access it directly, so we check whether it's in the first
211            or the second half, then we start either from the beginning or the
212            end of the list.
213            We then remove the billboard form the 'used' list and add it to
214            the 'free' list.
215         */
216         ActiveBillboardList::iterator it;
217         if( index >= ( mActiveBillboards.size() >> 1 ) )
218         {
219             index = static_cast<unsigned int>(mActiveBillboards.size()) - index;
220             for( it = mActiveBillboards.end(); index; --index, --it );
221         }
222         else
223         {
224             for( it = mActiveBillboards.begin(); index; --index, ++it );
225         }
226 
227         mFreeBillboards.splice(mFreeBillboards.end(), mActiveBillboards, it);
228     }
229 
230     //-----------------------------------------------------------------------
removeBillboard(Billboard * pBill)231     void BillboardSet::removeBillboard( Billboard* pBill )
232     {
233         ActiveBillboardList::iterator it =
234             std::find(mActiveBillboards.begin(), mActiveBillboards.end(), pBill);
235         assert(
236             it != mActiveBillboards.end() &&
237             "Billboard isn't in the active list." );
238 
239         mFreeBillboards.splice(mFreeBillboards.end(), mActiveBillboards, it);
240     }
241 
242     //-----------------------------------------------------------------------
setBillboardOrigin(BillboardOrigin origin)243     void BillboardSet::setBillboardOrigin( BillboardOrigin origin )
244     {
245         mOriginType = origin;
246     }
247 
248     //-----------------------------------------------------------------------
getBillboardOrigin(void) const249     BillboardOrigin BillboardSet::getBillboardOrigin(void) const
250     {
251         return mOriginType;
252     }
253 
254     //-----------------------------------------------------------------------
setBillboardRotationType(BillboardRotationType rotationType)255     void BillboardSet::setBillboardRotationType(BillboardRotationType rotationType)
256     {
257         mRotationType = rotationType;
258     }
259     //-----------------------------------------------------------------------
getBillboardRotationType(void) const260     BillboardRotationType BillboardSet::getBillboardRotationType(void) const
261     {
262         return mRotationType;
263     }
264     //-----------------------------------------------------------------------
setDefaultDimensions(Real width,Real height)265     void BillboardSet::setDefaultDimensions( Real width, Real height )
266     {
267         mDefaultWidth = width;
268         mDefaultHeight = height;
269     }
270     //-----------------------------------------------------------------------
setDefaultWidth(Real width)271     void BillboardSet::setDefaultWidth(Real width)
272     {
273         mDefaultWidth = width;
274     }
275     //-----------------------------------------------------------------------
getDefaultWidth(void) const276     Real BillboardSet::getDefaultWidth(void) const
277     {
278         return mDefaultWidth;
279     }
280     //-----------------------------------------------------------------------
setDefaultHeight(Real height)281     void BillboardSet::setDefaultHeight(Real height)
282     {
283         mDefaultHeight = height;
284     }
285     //-----------------------------------------------------------------------
getDefaultHeight(void) const286     Real BillboardSet::getDefaultHeight(void) const
287     {
288         return mDefaultHeight;
289     }
290     //-----------------------------------------------------------------------
setMaterialName(const String & name,const String & groupName)291     void BillboardSet::setMaterialName( const String& name , const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */ )
292     {
293         mMaterial = MaterialManager::getSingleton().getByName(name, groupName);
294 
295         if (!mMaterial)
296             OGRE_EXCEPT( Exception::ERR_ITEM_NOT_FOUND, "Could not find material " + name,
297                 "BillboardSet::setMaterialName" );
298 
299         /* Ensure that the new material was loaded (will not load again if
300            already loaded anyway)
301         */
302         mMaterial->load();
303     }
304 
305     //-----------------------------------------------------------------------
getMaterialName(void) const306     const String& BillboardSet::getMaterialName(void) const
307     {
308         return mMaterial->getName();
309     }
310 
311     //-----------------------------------------------------------------------
_sortBillboards(Camera * cam)312     void BillboardSet::_sortBillboards( Camera* cam)
313     {
314         switch (_getSortMode())
315         {
316         case SM_DIRECTION:
317             mRadixSorter.sort(mActiveBillboards, SortByDirectionFunctor(-mCamDir));
318             break;
319         case SM_DISTANCE:
320             mRadixSorter.sort(mActiveBillboards, SortByDistanceFunctor(mCamPos));
321             break;
322         }
323     }
SortByDirectionFunctor(const Vector3 & dir)324     BillboardSet::SortByDirectionFunctor::SortByDirectionFunctor(const Vector3& dir)
325         : sortDir(dir)
326     {
327     }
operator ()(Billboard * bill) const328     float BillboardSet::SortByDirectionFunctor::operator()(Billboard* bill) const
329     {
330         return sortDir.dotProduct(bill->getPosition());
331     }
SortByDistanceFunctor(const Vector3 & pos)332     BillboardSet::SortByDistanceFunctor::SortByDistanceFunctor(const Vector3& pos)
333         : sortPos(pos)
334     {
335     }
operator ()(Billboard * bill) const336     float BillboardSet::SortByDistanceFunctor::operator()(Billboard* bill) const
337     {
338         // Sort descending by squared distance
339         return - (sortPos - bill->getPosition()).squaredLength();
340     }
341     //-----------------------------------------------------------------------
_getSortMode(void) const342     SortMode BillboardSet::_getSortMode(void) const
343     {
344         // Need to sort by distance if we're using accurate facing, or perpendicular billboard type.
345         if (mAccurateFacing ||
346             mBillboardType == BBT_PERPENDICULAR_SELF ||
347             mBillboardType == BBT_PERPENDICULAR_COMMON)
348         {
349             return SM_DISTANCE;
350         }
351         else
352         {
353             return SM_DIRECTION;
354         }
355     }
356     //-----------------------------------------------------------------------
_notifyCurrentCamera(Camera * cam)357     void BillboardSet::_notifyCurrentCamera( Camera* cam )
358     {
359         MovableObject::_notifyCurrentCamera(cam);
360 
361         mCurrentCamera = cam;
362 
363         // Calculate camera orientation and position
364         mCamQ = mCurrentCamera->getDerivedOrientation();
365         mCamPos = mCurrentCamera->getDerivedPosition();
366         if (!mWorldSpace)
367         {
368             // Default behaviour is that billboards are in local node space
369             // so orientation of camera (in world space) must be reverse-transformed
370             // into node space
371             mCamQ = mParentNode->convertWorldToLocalOrientation(mCamQ);
372             mCamPos = mParentNode->convertWorldToLocalPosition(mCamPos);
373         }
374 
375         // Camera direction points down -Z
376         mCamDir = mCamQ * Vector3::NEGATIVE_UNIT_Z;
377     }
378     //-----------------------------------------------------------------------
beginBillboards(size_t numBillboards)379     void BillboardSet::beginBillboards(size_t numBillboards)
380     {
381         /* Generate the vertices for all the billboards relative to the camera
382            Also take the opportunity to update the vertex colours
383            May as well do it here to save on loops elsewhere
384         */
385 
386         /* NOTE: most engines generate world coordinates for the billboards
387            directly, taking the world axes of the camera as offsets to the
388            center points. I take a different approach, reverse-transforming
389            the camera world axes into local billboard space.
390            Why?
391            Well, it's actually more efficient this way, because I only have to
392            reverse-transform using the billboardset world matrix (inverse)
393            once, from then on it's simple additions (assuming identically
394            sized billboards). If I transformed every billboard center by it's
395            world transform, that's a matrix multiplication per billboard
396            instead.
397            I leave the final transform to the render pipeline since that can
398            use hardware TnL if it is available.
399         */
400 
401         // create vertex and index buffers if they haven't already been
402         if(!mBuffersCreated)
403             _createBuffers();
404 
405         // Only calculate vertex offets et al if we're not point rendering
406         if (!mPointRendering)
407         {
408 
409             // Get offsets for origin type
410             getParametricOffsets(mLeftOff, mRightOff, mTopOff, mBottomOff);
411 
412             // Generate axes etc up-front if not oriented per-billboard
413             if (mBillboardType != BBT_ORIENTED_SELF &&
414                 mBillboardType != BBT_PERPENDICULAR_SELF &&
415                 !(mAccurateFacing && mBillboardType != BBT_PERPENDICULAR_COMMON))
416             {
417                 genBillboardAxes(&mCamX, &mCamY);
418 
419                 /* If all billboards are the same size we can precalculate the
420                    offsets and just use '+' instead of '*' for each billboard,
421                    and it should be faster.
422                 */
423                 genVertOffsets(mLeftOff, mRightOff, mTopOff, mBottomOff,
424                     mDefaultWidth, mDefaultHeight, mCamX, mCamY, mVOffset);
425 
426             }
427         }
428 
429         // Init num visible
430         mNumVisibleBillboards = 0;
431 
432         // Lock the buffer
433         if (numBillboards) // optimal lock
434         {
435             // clamp to max
436             numBillboards = std::min(mPoolSize, numBillboards);
437 
438             size_t billboardSize;
439             if (mPointRendering)
440             {
441                 // just one vertex per billboard (this also excludes texcoords)
442                 billboardSize = mMainBuf->getVertexSize();
443             }
444             else
445             {
446                 // 4 corners
447                 billboardSize = mMainBuf->getVertexSize() * 4;
448             }
449             assert (numBillboards * billboardSize <= mMainBuf->getSizeInBytes());
450 
451             mLockPtr = static_cast<float*>(
452                 mMainBuf->lock(0, numBillboards * billboardSize,
453                 mMainBuf->getUsage() & HardwareBuffer::HBU_DYNAMIC ?
454                 HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL) );
455         }
456         else // lock the entire thing
457             mLockPtr = static_cast<float*>(
458             mMainBuf->lock(mMainBuf->getUsage() & HardwareBuffer::HBU_DYNAMIC ?
459             HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL) );
460 
461     }
462     //-----------------------------------------------------------------------
injectBillboard(const Billboard & bb)463     void BillboardSet::injectBillboard(const Billboard& bb)
464     {
465         // Don't accept injections beyond pool size
466         if (mNumVisibleBillboards == mPoolSize) return;
467 
468         // Skip if not visible (NB always true if not bounds checking individual billboards)
469         if (!billboardVisible(mCurrentCamera, bb)) return;
470 
471         if (!mPointRendering &&
472             (mBillboardType == BBT_ORIENTED_SELF ||
473             mBillboardType == BBT_PERPENDICULAR_SELF ||
474             (mAccurateFacing && mBillboardType != BBT_PERPENDICULAR_COMMON)))
475         {
476             // Have to generate axes & offsets per billboard
477             genBillboardAxes(&mCamX, &mCamY, &bb);
478         }
479 
480         // If they're all the same size or we're point rendering
481         if( mAllDefaultSize || mPointRendering)
482         {
483             /* No per-billboard checking, just blast through.
484             Saves us an if clause every billboard which may
485             make a difference.
486             */
487 
488             if (!mPointRendering &&
489                 (mBillboardType == BBT_ORIENTED_SELF ||
490                 mBillboardType == BBT_PERPENDICULAR_SELF ||
491                 (mAccurateFacing && mBillboardType != BBT_PERPENDICULAR_COMMON)))
492             {
493                 genVertOffsets(mLeftOff, mRightOff, mTopOff, mBottomOff,
494                     mDefaultWidth, mDefaultHeight, mCamX, mCamY, mVOffset);
495             }
496             genVertices(mVOffset, bb);
497         }
498         else // not all default size and not point rendering
499         {
500             Vector3 vOwnOffset[4];
501             // If it has own dimensions, or self-oriented, gen offsets
502             if (mBillboardType == BBT_ORIENTED_SELF ||
503                 mBillboardType == BBT_PERPENDICULAR_SELF ||
504                 bb.mOwnDimensions ||
505                 (mAccurateFacing && mBillboardType != BBT_PERPENDICULAR_COMMON))
506             {
507                 // Generate using own dimensions
508                 genVertOffsets(mLeftOff, mRightOff, mTopOff, mBottomOff,
509                     bb.mWidth, bb.mHeight, mCamX, mCamY, vOwnOffset);
510                 // Create vertex data
511                 genVertices(vOwnOffset, bb);
512             }
513             else // Use default dimension, already computed before the loop, for faster creation
514             {
515                 genVertices(mVOffset, bb);
516             }
517         }
518         // Increment visibles
519         mNumVisibleBillboards++;
520     }
521     //-----------------------------------------------------------------------
endBillboards(void)522     void BillboardSet::endBillboards(void)
523     {
524         mMainBuf->unlock();
525     }
526     //-----------------------------------------------------------------------
setBounds(const AxisAlignedBox & box,Real radius)527     void BillboardSet::setBounds(const AxisAlignedBox& box, Real radius)
528     {
529         mAABB = box;
530         mBoundingRadius = radius;
531     }
532     //-----------------------------------------------------------------------
_updateBounds(void)533     void BillboardSet::_updateBounds(void)
534     {
535         if (mActiveBillboards.empty())
536         {
537             // No billboards, null bbox
538             mAABB.setNull();
539             mBoundingRadius = 0.0f;
540         }
541         else
542         {
543             Real maxSqLen = -1.0f;
544 
545             Vector3 min(Math::POS_INFINITY, Math::POS_INFINITY, Math::POS_INFINITY);
546             Vector3 max(Math::NEG_INFINITY, Math::NEG_INFINITY, Math::NEG_INFINITY);
547             ActiveBillboardList::iterator i, iend;
548 
549             iend = mActiveBillboards.end();
550             Affine3 invWorld;
551             bool invert = mWorldSpace && getParentSceneNode();
552             if (invert)
553                 invWorld = getParentSceneNode()->_getFullTransform().inverse();
554 
555             for (i = mActiveBillboards.begin(); i != iend; ++i)
556             {
557                 Vector3 pos = (*i)->getPosition();
558                 // transform from world space to local space
559                 if (invert)
560                     pos = invWorld * pos;
561                 min.makeFloor(pos);
562                 max.makeCeil(pos);
563 
564                 maxSqLen = std::max(maxSqLen, pos.squaredLength());
565             }
566             // Adjust for billboard size
567             Real adjust = std::max(mDefaultWidth, mDefaultHeight);
568             Vector3 vecAdjust(adjust, adjust, adjust);
569             min -= vecAdjust;
570             max += vecAdjust;
571 
572             mAABB.setExtents(min, max);
573             mBoundingRadius = Math::Sqrt(maxSqLen);
574 
575         }
576 
577         if (mParentNode)
578             mParentNode->needUpdate();
579 
580     }
581     //-----------------------------------------------------------------------
getBoundingBox(void) const582     const AxisAlignedBox& BillboardSet::getBoundingBox(void) const
583     {
584         return mAABB;
585     }
586 
587     //-----------------------------------------------------------------------
_updateRenderQueue(RenderQueue * queue)588     void BillboardSet::_updateRenderQueue(RenderQueue* queue)
589     {
590         // If we're driving this from our own data, update geometry if need to.
591         if (!mExternalData && (mAutoUpdate || mBillboardDataChanged || !mBuffersCreated))
592         {
593             if (mSortingEnabled)
594             {
595                 _sortBillboards(mCurrentCamera);
596             }
597 
598             beginBillboards(mActiveBillboards.size());
599             ActiveBillboardList::iterator it;
600             for(it = mActiveBillboards.begin();
601                 it != mActiveBillboards.end();
602                 ++it )
603             {
604                 injectBillboard(*(*it));
605             }
606             endBillboards();
607             mBillboardDataChanged = false;
608         }
609 
610         //only set the render queue group if it has been explicitly set.
611         if (mRenderQueuePrioritySet)
612         {
613             assert(mRenderQueueIDSet == true);
614             queue->addRenderable(this, mRenderQueueID, mRenderQueuePriority);
615         }
616         else if( mRenderQueueIDSet )
617         {
618            queue->addRenderable(this, mRenderQueueID);
619         } else {
620            queue->addRenderable(this);
621         }
622 
623     }
624 
625     //-----------------------------------------------------------------------
getMaterial(void) const626     const MaterialPtr& BillboardSet::getMaterial(void) const
627     {
628         return mMaterial;
629     }
630 
setMaterial(const MaterialPtr & material)631     void BillboardSet::setMaterial( const MaterialPtr& material )
632     {
633         mMaterial = material;
634 
635         if (!mMaterial)
636         {
637             LogManager::getSingleton().logMessage("Can't assign material " + material->getName()+
638                                                   " to BillboardSet of " + getName() + " because this "
639                                                   "Material does not exist in group "+material->getGroup()+". Have you forgotten to define it in a "
640                                                   ".material script?", LML_CRITICAL);
641 
642             mMaterial = MaterialManager::getSingleton().getDefaultMaterial();
643         }
644 
645         // Ensure new material loaded (will not load again if already loaded)
646         mMaterial->load();
647     }
648 
649     //-----------------------------------------------------------------------
getRenderOperation(RenderOperation & op)650     void BillboardSet::getRenderOperation(RenderOperation& op)
651     {
652         op.vertexData = mVertexData.get();
653         op.vertexData->vertexStart = 0;
654 
655         if (mPointRendering)
656         {
657             op.operationType = RenderOperation::OT_POINT_LIST;
658             op.useIndexes = false;
659             op.useGlobalInstancingVertexBufferIsAvailable = false;
660             op.indexData = 0;
661             op.vertexData->vertexCount = mNumVisibleBillboards;
662         }
663         else
664         {
665             op.operationType = RenderOperation::OT_TRIANGLE_LIST;
666             op.useIndexes = true;
667 
668             op.vertexData->vertexCount = mNumVisibleBillboards * 4;
669 
670             op.indexData = mIndexData.get();
671             op.indexData->indexCount = mNumVisibleBillboards * 6;
672             op.indexData->indexStart = 0;
673         }
674     }
675 
676     //-----------------------------------------------------------------------
getWorldTransforms(Matrix4 * xform) const677     void BillboardSet::getWorldTransforms( Matrix4* xform ) const
678     {
679         if (mWorldSpace)
680         {
681             *xform = Matrix4::IDENTITY;
682         }
683         else
684         {
685             *xform = _getParentNodeFullTransform();
686         }
687     }
688 
689     //-----------------------------------------------------------------------
setAutoextend(bool autoextend)690     void BillboardSet::setAutoextend( bool autoextend )
691     {
692         mAutoExtendPool = autoextend;
693     }
694 
695     //-----------------------------------------------------------------------
getAutoextend(void) const696     bool BillboardSet::getAutoextend(void) const
697     {
698         return mAutoExtendPool;
699     }
700 
701     //-----------------------------------------------------------------------
setSortingEnabled(bool sortenable)702     void BillboardSet::setSortingEnabled( bool sortenable )
703     {
704         mSortingEnabled = sortenable;
705     }
706 
707     //-----------------------------------------------------------------------
getSortingEnabled(void) const708     bool BillboardSet::getSortingEnabled(void) const
709     {
710         return mSortingEnabled;
711     }
712 
713     //-----------------------------------------------------------------------
setPoolSize(size_t size)714     void BillboardSet::setPoolSize( size_t size )
715     {
716         // If we're driving this from our own data, allocate billboards
717         if (!mExternalData)
718         {
719             // Never shrink below size()
720             size_t currSize = mBillboardPool.size();
721             if (currSize >= size)
722                 return;
723 
724             this->increasePool(size);
725 
726             for( size_t i = currSize; i < size; ++i )
727             {
728                 // Add new items to the queue
729                 mFreeBillboards.push_back( mBillboardPool[i] );
730             }
731         }
732 
733         mPoolSize = size;
734 
735         _destroyBuffers();
736     }
737 
738     //-----------------------------------------------------------------------
_createBuffers(void)739     void BillboardSet::_createBuffers(void)
740     {
741         /* Allocate / reallocate vertex data
742            Note that we allocate enough space for ALL the billboards in the pool, but only issue
743            rendering operations for the sections relating to the active billboards
744         */
745 
746         /* Alloc positions   ( 1 or 4 verts per billboard, 3 components )
747                  colours     ( 1 x RGBA per vertex )
748                  indices     ( 6 per billboard ( 2 tris ) if not point rendering )
749                  tex. coords ( 2D coords, 1 or 4 per billboard )
750         */
751 
752         // Warn if user requested an invalid setup
753         // Do it here so it only appears once
754         if (mPointRendering && mBillboardType != BBT_POINT)
755         {
756 
757             LogManager::getSingleton().logWarning("BillboardSet " +
758                 mName + " has point rendering enabled but is using a type "
759                 "other than BBT_POINT, this may not give you the results you "
760                 "expect.");
761         }
762 
763         mVertexData.reset(new VertexData());
764         if (mPointRendering)
765             mVertexData->vertexCount = mPoolSize;
766         else
767             mVertexData->vertexCount = mPoolSize * 4;
768 
769         mVertexData->vertexStart = 0;
770 
771         // Vertex declaration
772         VertexDeclaration* decl = mVertexData->vertexDeclaration;
773         VertexBufferBinding* binding = mVertexData->vertexBufferBinding;
774 
775         size_t offset = 0;
776         decl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
777         offset += VertexElement::getTypeSize(VET_FLOAT3);
778         decl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
779         offset += VertexElement::getTypeSize(VET_COLOUR);
780         // Texture coords irrelevant when enabled point rendering (generated
781         // in point sprite mode, and unused in standard point mode)
782         if (!mPointRendering)
783         {
784             decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
785         }
786 
787         mMainBuf =
788             HardwareBufferManager::getSingleton().createVertexBuffer(
789                 decl->getVertexSize(0),
790                 mVertexData->vertexCount,
791                 mAutoUpdate ? HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE :
792                 HardwareBuffer::HBU_STATIC_WRITE_ONLY);
793         // bind position and diffuses
794         binding->setBinding(0, mMainBuf);
795 
796         if (!mPointRendering)
797         {
798             mIndexData.reset(new IndexData());
799             mIndexData->indexStart = 0;
800             mIndexData->indexCount = mPoolSize * 6;
801 
802             mIndexData->indexBuffer = HardwareBufferManager::getSingleton().
803                 createIndexBuffer(HardwareIndexBuffer::IT_16BIT,
804                     mIndexData->indexCount,
805                     HardwareBuffer::HBU_STATIC_WRITE_ONLY);
806 
807             /* Create indexes (will be the same every frame)
808                Using indexes because it means 1/3 less vertex transforms (4 instead of 6)
809 
810                Billboard layout relative to camera:
811 
812                 0-----1
813                 |    /|
814                 |  /  |
815                 |/    |
816                 2-----3
817             */
818 
819             HardwareBufferLockGuard indexLock(mIndexData->indexBuffer, HardwareBuffer::HBL_DISCARD);
820             ushort* pIdx = static_cast<ushort*>(indexLock.pData);
821 
822             for(
823                 size_t idx, idxOff, bboard = 0;
824                 bboard < mPoolSize;
825                 ++bboard )
826             {
827                 // Do indexes
828                 idx    = bboard * 6;
829                 idxOff = bboard * 4;
830 
831                 pIdx[idx] = static_cast<unsigned short>(idxOff); // + 0;, for clarity
832                 pIdx[idx+1] = static_cast<unsigned short>(idxOff + 2);
833                 pIdx[idx+2] = static_cast<unsigned short>(idxOff + 1);
834                 pIdx[idx+3] = static_cast<unsigned short>(idxOff + 1);
835                 pIdx[idx+4] = static_cast<unsigned short>(idxOff + 2);
836                 pIdx[idx+5] = static_cast<unsigned short>(idxOff + 3);
837 
838             }
839         }
840         mBuffersCreated = true;
841     }
842     //-----------------------------------------------------------------------
_destroyBuffers(void)843     void BillboardSet::_destroyBuffers(void)
844     {
845         mVertexData.reset();
846         mIndexData.reset();
847         mMainBuf.reset();
848 
849         mBuffersCreated = false;
850     }
851     //-----------------------------------------------------------------------
getPoolSize(void) const852     unsigned int BillboardSet::getPoolSize(void) const
853     {
854         return static_cast< unsigned int >( mBillboardPool.size() );
855     }
856 
857     //-----------------------------------------------------------------------
_notifyBillboardResized(void)858     void BillboardSet::_notifyBillboardResized(void)
859     {
860         mAllDefaultSize = false;
861     }
862 
863     //-----------------------------------------------------------------------
_notifyBillboardRotated(void)864     void BillboardSet::_notifyBillboardRotated(void)
865     {
866         mAllDefaultRotation = false;
867     }
868 
869     //-----------------------------------------------------------------------
getParametricOffsets(Real & left,Real & right,Real & top,Real & bottom)870     void BillboardSet::getParametricOffsets(
871         Real& left, Real& right, Real& top, Real& bottom )
872     {
873         switch( mOriginType )
874         {
875         case BBO_TOP_LEFT:
876             left = 0.0f;
877             right = 1.0f;
878             top = 0.0f;
879             bottom = -1.0f;
880             break;
881 
882         case BBO_TOP_CENTER:
883             left = -0.5f;
884             right = 0.5f;
885             top = 0.0f;
886             bottom = -1.0f;
887             break;
888 
889         case BBO_TOP_RIGHT:
890             left = -1.0f;
891             right = 0.0f;
892             top = 0.0f;
893             bottom = -1.0f;
894             break;
895 
896         case BBO_CENTER_LEFT:
897             left = 0.0f;
898             right = 1.0f;
899             top = 0.5f;
900             bottom = -0.5f;
901             break;
902 
903         case BBO_CENTER:
904             left = -0.5f;
905             right = 0.5f;
906             top = 0.5f;
907             bottom = -0.5f;
908             break;
909 
910         case BBO_CENTER_RIGHT:
911             left = -1.0f;
912             right = 0.0f;
913             top = 0.5f;
914             bottom = -0.5f;
915             break;
916 
917         case BBO_BOTTOM_LEFT:
918             left = 0.0f;
919             right = 1.0f;
920             top = 1.0f;
921             bottom = 0.0f;
922             break;
923 
924         case BBO_BOTTOM_CENTER:
925             left = -0.5f;
926             right = 0.5f;
927             top = 1.0f;
928             bottom = 0.0f;
929             break;
930 
931         case BBO_BOTTOM_RIGHT:
932             left = -1.0f;
933             right = 0.0f;
934             top = 1.0f;
935             bottom = 0.0f;
936             break;
937         }
938     }
939     //-----------------------------------------------------------------------
getCullIndividually(void) const940     bool BillboardSet::getCullIndividually(void) const
941     {
942         return mCullIndividual;
943     }
944     //-----------------------------------------------------------------------
setCullIndividually(bool cullIndividual)945     void BillboardSet::setCullIndividually(bool cullIndividual)
946     {
947         mCullIndividual = cullIndividual;
948     }
949     //-----------------------------------------------------------------------
billboardVisible(Camera * cam,const Billboard & bill)950     bool BillboardSet::billboardVisible(Camera* cam, const Billboard& bill)
951     {
952         // Return always visible if not culling individually
953         if (!mCullIndividual) return true;
954 
955         // Cull based on sphere (have to transform less)
956         Sphere sph;
957         Matrix4 xworld;
958 
959         getWorldTransforms(&xworld);
960 
961         sph.setCenter(xworld * bill.mPosition);
962 
963         if (bill.mOwnDimensions)
964         {
965             sph.setRadius(std::max(bill.mWidth, bill.mHeight));
966         }
967         else
968         {
969             sph.setRadius(std::max(mDefaultWidth, mDefaultHeight));
970         }
971 
972         return cam->isVisible(sph);
973 
974     }
975     //-----------------------------------------------------------------------
increasePool(size_t size)976     void BillboardSet::increasePool(size_t size)
977     {
978         size_t oldSize = mBillboardPool.size();
979 
980         // Increase size
981         mBillboardPool.reserve(size);
982         mBillboardPool.resize(size);
983 
984         // Create new billboards
985         for( size_t i = oldSize; i < size; ++i )
986             mBillboardPool[i] = OGRE_NEW Billboard();
987 
988     }
989     //-----------------------------------------------------------------------
genBillboardAxes(Vector3 * pX,Vector3 * pY,const Billboard * bb)990     void BillboardSet::genBillboardAxes(Vector3* pX, Vector3 *pY, const Billboard* bb)
991     {
992         // If we're using accurate facing, recalculate camera direction per BB
993         if (mAccurateFacing &&
994             (mBillboardType == BBT_POINT ||
995             mBillboardType == BBT_ORIENTED_COMMON ||
996             mBillboardType == BBT_ORIENTED_SELF))
997         {
998             // cam -> bb direction
999             mCamDir = bb->mPosition - mCamPos;
1000             mCamDir.normalise();
1001         }
1002 
1003 
1004         switch (mBillboardType)
1005         {
1006         case BBT_POINT:
1007             if (mAccurateFacing)
1008             {
1009                 // Point billboards will have 'up' based on but not equal to cameras
1010                 // Use pY temporarily to avoid allocation
1011                 *pY = mCamQ * Vector3::UNIT_Y;
1012                 *pX = mCamDir.crossProduct(*pY);
1013                 pX->normalise();
1014                 *pY = pX->crossProduct(mCamDir); // both normalised already
1015             }
1016             else
1017             {
1018                 // Get camera axes for X and Y (depth is irrelevant)
1019                 *pX = mCamQ * Vector3::UNIT_X;
1020                 *pY = mCamQ * Vector3::UNIT_Y;
1021             }
1022             break;
1023 
1024         case BBT_ORIENTED_COMMON:
1025             // Y-axis is common direction
1026             // X-axis is cross with camera direction
1027             *pY = mCommonDirection;
1028             *pX = mCamDir.crossProduct(*pY);
1029             pX->normalise();
1030             break;
1031 
1032         case BBT_ORIENTED_SELF:
1033             // Y-axis is direction
1034             // X-axis is cross with camera direction
1035             // Scale direction first
1036             *pY = bb->mDirection;
1037             *pX = mCamDir.crossProduct(*pY);
1038             pX->normalise();
1039             break;
1040 
1041         case BBT_PERPENDICULAR_COMMON:
1042             // X-axis is up-vector cross common direction
1043             // Y-axis is common direction cross X-axis
1044             *pX = mCommonUpVector.crossProduct(mCommonDirection);
1045             *pY = mCommonDirection.crossProduct(*pX);
1046             break;
1047 
1048         case BBT_PERPENDICULAR_SELF:
1049             // X-axis is up-vector cross own direction
1050             // Y-axis is own direction cross X-axis
1051             *pX = mCommonUpVector.crossProduct(bb->mDirection);
1052             pX->normalise();
1053             *pY = bb->mDirection.crossProduct(*pX); // both should be normalised
1054             break;
1055         }
1056 
1057     }
1058     //-----------------------------------------------------------------------
setBillboardType(BillboardType bbt)1059     void BillboardSet::setBillboardType(BillboardType bbt)
1060     {
1061         mBillboardType = bbt;
1062     }
1063     //-----------------------------------------------------------------------
getBillboardType(void) const1064     BillboardType BillboardSet::getBillboardType(void) const
1065     {
1066         return mBillboardType;
1067     }
1068     //-----------------------------------------------------------------------
setCommonDirection(const Vector3 & vec)1069     void BillboardSet::setCommonDirection(const Vector3& vec)
1070     {
1071         mCommonDirection = vec;
1072     }
1073     //-----------------------------------------------------------------------
getCommonDirection(void) const1074     const Vector3& BillboardSet::getCommonDirection(void) const
1075     {
1076         return mCommonDirection;
1077     }
1078     //-----------------------------------------------------------------------
setCommonUpVector(const Vector3 & vec)1079     void BillboardSet::setCommonUpVector(const Vector3& vec)
1080     {
1081         mCommonUpVector = vec;
1082     }
1083     //-----------------------------------------------------------------------
getCommonUpVector(void) const1084     const Vector3& BillboardSet::getCommonUpVector(void) const
1085     {
1086         return mCommonUpVector;
1087     }
1088     //-----------------------------------------------------------------------
getTypeFlags(void) const1089     uint32 BillboardSet::getTypeFlags(void) const
1090     {
1091         return SceneManager::FX_TYPE_MASK;
1092     }
1093     //-----------------------------------------------------------------------
genVertices(const Vector3 * const offsets,const Billboard & bb)1094     void BillboardSet::genVertices(
1095         const Vector3* const offsets, const Billboard& bb)
1096     {
1097         RGBA colour;
1098         Root::getSingleton().convertColourValue(bb.mColour, &colour);
1099         RGBA* pCol;
1100 
1101         // Texcoords
1102         assert( bb.mUseTexcoordRect || bb.mTexcoordIndex < mTextureCoords.size() );
1103         const Ogre::FloatRect & r =
1104             bb.mUseTexcoordRect ? bb.mTexcoordRect : mTextureCoords[bb.mTexcoordIndex];
1105 
1106         if (mPointRendering)
1107         {
1108             // Single vertex per billboard, ignore offsets
1109             // position
1110             *mLockPtr++ = bb.mPosition.x;
1111             *mLockPtr++ = bb.mPosition.y;
1112             *mLockPtr++ = bb.mPosition.z;
1113             // Colour
1114             // Convert float* to RGBA*
1115             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1116             *pCol++ = colour;
1117             // Update lock pointer
1118             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1119             // No texture coords in point rendering
1120         }
1121         else if (mAllDefaultRotation || bb.mRotation == Radian(0))
1122         {
1123             // Left-top
1124             // Positions
1125             *mLockPtr++ = offsets[0].x + bb.mPosition.x;
1126             *mLockPtr++ = offsets[0].y + bb.mPosition.y;
1127             *mLockPtr++ = offsets[0].z + bb.mPosition.z;
1128             // Colour
1129             // Convert float* to RGBA*
1130             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1131             *pCol++ = colour;
1132             // Update lock pointer
1133             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1134             // Texture coords
1135             *mLockPtr++ = r.left;
1136             *mLockPtr++ = r.top;
1137 
1138             // Right-top
1139             // Positions
1140             *mLockPtr++ = offsets[1].x + bb.mPosition.x;
1141             *mLockPtr++ = offsets[1].y + bb.mPosition.y;
1142             *mLockPtr++ = offsets[1].z + bb.mPosition.z;
1143             // Colour
1144             // Convert float* to RGBA*
1145             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1146             *pCol++ = colour;
1147             // Update lock pointer
1148             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1149             // Texture coords
1150             *mLockPtr++ = r.right;
1151             *mLockPtr++ = r.top;
1152 
1153             // Left-bottom
1154             // Positions
1155             *mLockPtr++ = offsets[2].x + bb.mPosition.x;
1156             *mLockPtr++ = offsets[2].y + bb.mPosition.y;
1157             *mLockPtr++ = offsets[2].z + bb.mPosition.z;
1158             // Colour
1159             // Convert float* to RGBA*
1160             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1161             *pCol++ = colour;
1162             // Update lock pointer
1163             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1164             // Texture coords
1165             *mLockPtr++ = r.left;
1166             *mLockPtr++ = r.bottom;
1167 
1168             // Right-bottom
1169             // Positions
1170             *mLockPtr++ = offsets[3].x + bb.mPosition.x;
1171             *mLockPtr++ = offsets[3].y + bb.mPosition.y;
1172             *mLockPtr++ = offsets[3].z + bb.mPosition.z;
1173             // Colour
1174             // Convert float* to RGBA*
1175             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1176             *pCol++ = colour;
1177             // Update lock pointer
1178             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1179             // Texture coords
1180             *mLockPtr++ = r.right;
1181             *mLockPtr++ = r.bottom;
1182         }
1183         else if (mRotationType == BBR_VERTEX)
1184         {
1185             // TODO: Cache axis when billboard type is BBT_POINT or BBT_PERPENDICULAR_COMMON
1186             Vector3 axis = (offsets[3] - offsets[0]).crossProduct(offsets[2] - offsets[1]).normalisedCopy();
1187 
1188             Matrix3 rotation;
1189             rotation.FromAngleAxis(axis, bb.mRotation);
1190 
1191             Vector3 pt;
1192 
1193             // Left-top
1194             // Positions
1195             pt = rotation * offsets[0];
1196             *mLockPtr++ = pt.x + bb.mPosition.x;
1197             *mLockPtr++ = pt.y + bb.mPosition.y;
1198             *mLockPtr++ = pt.z + bb.mPosition.z;
1199             // Colour
1200             // Convert float* to RGBA*
1201             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1202             *pCol++ = colour;
1203             // Update lock pointer
1204             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1205             // Texture coords
1206             *mLockPtr++ = r.left;
1207             *mLockPtr++ = r.top;
1208 
1209             // Right-top
1210             // Positions
1211             pt = rotation * offsets[1];
1212             *mLockPtr++ = pt.x + bb.mPosition.x;
1213             *mLockPtr++ = pt.y + bb.mPosition.y;
1214             *mLockPtr++ = pt.z + bb.mPosition.z;
1215             // Colour
1216             // Convert float* to RGBA*
1217             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1218             *pCol++ = colour;
1219             // Update lock pointer
1220             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1221             // Texture coords
1222             *mLockPtr++ = r.right;
1223             *mLockPtr++ = r.top;
1224 
1225             // Left-bottom
1226             // Positions
1227             pt = rotation * offsets[2];
1228             *mLockPtr++ = pt.x + bb.mPosition.x;
1229             *mLockPtr++ = pt.y + bb.mPosition.y;
1230             *mLockPtr++ = pt.z + bb.mPosition.z;
1231             // Colour
1232             // Convert float* to RGBA*
1233             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1234             *pCol++ = colour;
1235             // Update lock pointer
1236             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1237             // Texture coords
1238             *mLockPtr++ = r.left;
1239             *mLockPtr++ = r.bottom;
1240 
1241             // Right-bottom
1242             // Positions
1243             pt = rotation * offsets[3];
1244             *mLockPtr++ = pt.x + bb.mPosition.x;
1245             *mLockPtr++ = pt.y + bb.mPosition.y;
1246             *mLockPtr++ = pt.z + bb.mPosition.z;
1247             // Colour
1248             // Convert float* to RGBA*
1249             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1250             *pCol++ = colour;
1251             // Update lock pointer
1252             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1253             // Texture coords
1254             *mLockPtr++ = r.right;
1255             *mLockPtr++ = r.bottom;
1256         }
1257         else
1258         {
1259             const Real      cos_rot  ( Math::Cos(bb.mRotation)   );
1260             const Real      sin_rot  ( Math::Sin(bb.mRotation)   );
1261 
1262             float width = (r.right-r.left)/2;
1263             float height = (r.bottom-r.top)/2;
1264             float mid_u = r.left+width;
1265             float mid_v = r.top+height;
1266 
1267             float cos_rot_w = cos_rot * width;
1268             float cos_rot_h = cos_rot * height;
1269             float sin_rot_w = sin_rot * width;
1270             float sin_rot_h = sin_rot * height;
1271 
1272             // Left-top
1273             // Positions
1274             *mLockPtr++ = offsets[0].x + bb.mPosition.x;
1275             *mLockPtr++ = offsets[0].y + bb.mPosition.y;
1276             *mLockPtr++ = offsets[0].z + bb.mPosition.z;
1277             // Colour
1278             // Convert float* to RGBA*
1279             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1280             *pCol++ = colour;
1281             // Update lock pointer
1282             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1283             // Texture coords
1284             *mLockPtr++ = mid_u - cos_rot_w + sin_rot_h;
1285             *mLockPtr++ = mid_v - sin_rot_w - cos_rot_h;
1286 
1287             // Right-top
1288             // Positions
1289             *mLockPtr++ = offsets[1].x + bb.mPosition.x;
1290             *mLockPtr++ = offsets[1].y + bb.mPosition.y;
1291             *mLockPtr++ = offsets[1].z + bb.mPosition.z;
1292             // Colour
1293             // Convert float* to RGBA*
1294             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1295             *pCol++ = colour;
1296             // Update lock pointer
1297             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1298             // Texture coords
1299             *mLockPtr++ = mid_u + cos_rot_w + sin_rot_h;
1300             *mLockPtr++ = mid_v + sin_rot_w - cos_rot_h;
1301 
1302             // Left-bottom
1303             // Positions
1304             *mLockPtr++ = offsets[2].x + bb.mPosition.x;
1305             *mLockPtr++ = offsets[2].y + bb.mPosition.y;
1306             *mLockPtr++ = offsets[2].z + bb.mPosition.z;
1307             // Colour
1308             // Convert float* to RGBA*
1309             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1310             *pCol++ = colour;
1311             // Update lock pointer
1312             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1313             // Texture coords
1314             *mLockPtr++ = mid_u - cos_rot_w - sin_rot_h;
1315             *mLockPtr++ = mid_v - sin_rot_w + cos_rot_h;
1316 
1317             // Right-bottom
1318             // Positions
1319             *mLockPtr++ = offsets[3].x + bb.mPosition.x;
1320             *mLockPtr++ = offsets[3].y + bb.mPosition.y;
1321             *mLockPtr++ = offsets[3].z + bb.mPosition.z;
1322             // Colour
1323             // Convert float* to RGBA*
1324             pCol = static_cast<RGBA*>(static_cast<void*>(mLockPtr));
1325             *pCol++ = colour;
1326             // Update lock pointer
1327             mLockPtr = static_cast<float*>(static_cast<void*>(pCol));
1328             // Texture coords
1329             *mLockPtr++ = mid_u + cos_rot_w - sin_rot_h;
1330             *mLockPtr++ = mid_v + sin_rot_w + cos_rot_h;
1331         }
1332 
1333     }
1334     //-----------------------------------------------------------------------
genVertOffsets(Real inleft,Real inright,Real intop,Real inbottom,Real width,Real height,const Vector3 & x,const Vector3 & y,Vector3 * pDestVec)1335     void BillboardSet::genVertOffsets(Real inleft, Real inright, Real intop, Real inbottom,
1336         Real width, Real height, const Vector3& x, const Vector3& y, Vector3* pDestVec)
1337     {
1338         Vector3 vLeftOff, vRightOff, vTopOff, vBottomOff;
1339         /* Calculate default offsets. Scale the axes by
1340            parametric offset and dimensions, ready to be added to
1341            positions.
1342         */
1343 
1344         vLeftOff   = x * ( inleft   * width );
1345         vRightOff  = x * ( inright  * width );
1346         vTopOff    = y * ( intop   * height );
1347         vBottomOff = y * ( inbottom * height );
1348 
1349         // Make final offsets to vertex positions
1350         pDestVec[0] = vLeftOff  + vTopOff;
1351         pDestVec[1] = vRightOff + vTopOff;
1352         pDestVec[2] = vLeftOff  + vBottomOff;
1353         pDestVec[3] = vRightOff + vBottomOff;
1354 
1355     }
1356     //-----------------------------------------------------------------------
getMovableType(void) const1357     const String& BillboardSet::getMovableType(void) const
1358     {
1359         return BillboardSetFactory::FACTORY_TYPE_NAME;
1360     }
1361     //-----------------------------------------------------------------------
getSquaredViewDepth(const Camera * const cam) const1362     Real BillboardSet::getSquaredViewDepth(const Camera* const cam) const
1363     {
1364         assert(mParentNode);
1365         return mParentNode->getSquaredViewDepth(cam);
1366     }
1367     //-----------------------------------------------------------------------
getBoundingRadius(void) const1368     Real BillboardSet::getBoundingRadius(void) const
1369     {
1370         return mBoundingRadius;
1371     }
1372     //-----------------------------------------------------------------------
getLights(void) const1373     const LightList& BillboardSet::getLights(void) const
1374     {
1375         // It's actually quite unlikely that this will be called,
1376         // because most billboards are unlit, but here we go anyway
1377         return queryLights();
1378     }
1379     //---------------------------------------------------------------------
visitRenderables(Renderable::Visitor * visitor,bool debugRenderables)1380     void BillboardSet::visitRenderables(Renderable::Visitor* visitor,
1381         bool debugRenderables)
1382     {
1383         // only one renderable
1384         visitor->visit(this, 0, false);
1385     }
1386 
1387 
setTextureCoords(Ogre::FloatRect const * coords,uint16 numCoords)1388     void BillboardSet::setTextureCoords( Ogre::FloatRect const * coords, uint16 numCoords )
1389     {
1390       if( !numCoords || !coords ) {
1391         setTextureStacksAndSlices( 1, 1 );
1392         return;
1393       }
1394       //  clear out any previous allocation (as vectors may not shrink)
1395       TextureCoordSets().swap( mTextureCoords );
1396       //  make room
1397       mTextureCoords.resize( numCoords );
1398       //  copy in data
1399       std::copy( coords, coords+numCoords, &mTextureCoords.front() );
1400     }
1401 
setTextureStacksAndSlices(uchar stacks,uchar slices)1402     void BillboardSet::setTextureStacksAndSlices( uchar stacks, uchar slices )
1403     {
1404       if( stacks == 0 ) stacks = 1;
1405       if( slices == 0 ) slices = 1;
1406       //  clear out any previous allocation (as vectors may not shrink)
1407       TextureCoordSets().swap( mTextureCoords );
1408       //  make room
1409       mTextureCoords.resize( (size_t)stacks * slices );
1410       unsigned int coordIndex = 0;
1411       //  spread the U and V coordinates across the rects
1412       for( uint v = 0; v < stacks; ++v ) {
1413         //  (float)X / X is guaranteed to be == 1.0f for X up to 8 million, so
1414         //  our range of 1..256 is quite enough to guarantee perfect coverage.
1415         float top = (float)v / (float)stacks;
1416         float bottom = ((float)v + 1) / (float)stacks;
1417         for( uint u = 0; u < slices; ++u ) {
1418           Ogre::FloatRect & r = mTextureCoords[coordIndex];
1419           r.left = (float)u / (float)slices;
1420           r.bottom = bottom;
1421           r.right = ((float)u + 1) / (float)slices;
1422           r.top = top;
1423           ++coordIndex;
1424         }
1425       }
1426       assert( coordIndex == (size_t)stacks * slices );
1427     }
1428     //-----------------------------------------------------------------------
getTextureCoords(uint16 * oNumCoords)1429     Ogre::FloatRect const * BillboardSet::getTextureCoords( uint16 * oNumCoords )
1430     {
1431       *oNumCoords = (uint16)mTextureCoords.size();
1432       //  std::vector<> is guaranteed to be contiguous
1433       return &mTextureCoords.front();
1434     }
1435     //-----------------------------------------------------------------------
setPointRenderingEnabled(bool enabled)1436     void BillboardSet::setPointRenderingEnabled(bool enabled)
1437     {
1438         // Override point rendering if not supported
1439         if (enabled && !Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_POINT_SPRITES))
1440         {
1441             enabled = false;
1442         }
1443 
1444         if (enabled != mPointRendering)
1445         {
1446             mPointRendering = enabled;
1447             // Different buffer structure (1 or 4 verts per billboard)
1448             _destroyBuffers();
1449         }
1450     }
1451 
1452     //-----------------------------------------------------------------------
setAutoUpdate(bool autoUpdate)1453     void BillboardSet::setAutoUpdate(bool autoUpdate)
1454     {
1455         // Case auto update buffers changed we have to destroy the current buffers
1456         // since their usage will be different.
1457         if (autoUpdate != mAutoUpdate)
1458         {
1459             mAutoUpdate = autoUpdate;
1460             _destroyBuffers();
1461         }
1462     }
1463 
1464     //-----------------------------------------------------------------------
1465     //-----------------------------------------------------------------------
1466     String BillboardSetFactory::FACTORY_TYPE_NAME = "BillboardSet";
1467     //-----------------------------------------------------------------------
getType(void) const1468     const String& BillboardSetFactory::getType(void) const
1469     {
1470         return FACTORY_TYPE_NAME;
1471     }
1472     //-----------------------------------------------------------------------
createInstanceImpl(const String & name,const NameValuePairList * params)1473     MovableObject* BillboardSetFactory::createInstanceImpl( const String& name,
1474         const NameValuePairList* params)
1475     {
1476         // may have parameters
1477         bool externalData = false;
1478         unsigned int poolSize = 0;
1479 
1480         if (params != 0)
1481         {
1482             NameValuePairList::const_iterator ni = params->find("poolSize");
1483             if (ni != params->end())
1484             {
1485                 poolSize = StringConverter::parseUnsignedInt(ni->second);
1486             }
1487             ni = params->find("externalData");
1488             if (ni != params->end())
1489             {
1490                 externalData = StringConverter::parseBool(ni->second);
1491             }
1492 
1493         }
1494 
1495         if (poolSize > 0)
1496         {
1497             return OGRE_NEW BillboardSet(name, poolSize, externalData);
1498         }
1499         else
1500         {
1501             return OGRE_NEW BillboardSet(name);
1502         }
1503 
1504     }
1505     //-----------------------------------------------------------------------
destroyInstance(MovableObject * obj)1506     void BillboardSetFactory::destroyInstance( MovableObject* obj)
1507     {
1508         OGRE_DELETE obj;
1509     }
1510 
1511 
1512 }
1513