1 /*-------------------------------------------------------------------------------------
2 Copyright (c) 2006 John Judnich
3 
4 This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
5 Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
6 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
7 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
8 3. This notice may not be removed or altered from any source distribution.
9 -------------------------------------------------------------------------------------*/
10 
11 //StaticBillboardSet.h
12 //Provides a method of displaying billboards faster than Ogre's built-in BillboardSet
13 //functions by taking advantage of the static nature of tree billboards (note:
14 //StaticBillboardSet does not allow billboards to be moved or deleted individually in
15 //real-time)
16 //-------------------------------------------------------------------------------------
17 #pragma once
18 #include <OgrePrerequisites.h>
19 #include <OgreRoot.h>
20 #include <OgreRenderSystem.h>
21 #include <OgreVector3.h>
22 #include <OgreMesh.h>
23 #include <OgreMaterial.h>
24 #include <OgreBillboard.h>
25 #include <OgreBillboardSet.h>
26 #include <OgreMaterialManager.h>
27 #include <OgreSceneNode.h>
28 #include <OgreStringConverter.h>
29 
30 namespace Forests
31 {
32 
33    class SBMaterialRef;
34    typedef std::map<Ogre::Material*, SBMaterialRef*> SBMaterialRefList;
35 
36    /** Different methods used to render billboards. This can be supplied as a parameter
37    to the StaticBillboardSet constructor to manually select how you want billboards
38    rendered (although in almost all cases BB_METHOD_ACCELERATED is the best choice).*/
39    enum BillboardMethod
40    {
41       /** This mode accelerates the performance of billboards by using vertex shaders
42       to keep billboards facing the camera. Note: If the computer's hardware is not
43       capable	of vertex shaders, it will automatically fall back to BB_METHOD_COMPATIBLE
44       mode.*/
45       BB_METHOD_ACCELERATED = 1,
46 
47       /** Unlike BB_METHOD_ACCELERATED, this does not use vertex shaders to align
48       billboards to the camera. This is more compatible with old video cards,
49       although it can result in poor performance with high amounts of billboards.*/
50       BB_METHOD_COMPATIBLE = 0,
51    };
52 
53 
54    //--------------------------------------------------------------------------
55    /// A faster alternative to Ogre's built-in BillboardSet class.
56    ///
57    /// This class provides a method of displaying billboards faster than Ogre's built-in
58    /// BillboardSet functions by taking advantage of the static nature of tree billboards.
59    /// However, if your video card does not support vertex shaders, using this class over
60    /// Ogre's built-in Billboard class will have no performance benefit.
61    ///
62    /// @note StaticBillboardSet does not allow billboards to be moved or deleted individually in real-time
63    class StaticBillboardSet
64    {
65       //-----------------------------------------------------------------------------
66       //Internal class - do not use
67       class StaticBillboard
68       {
69       public:
70          // Constructor
StaticBillboard(const Ogre::Vector3 & pos,float xScale,float yScale,const Ogre::ColourValue & clr,Ogre::uint16 texcrdIndexU,Ogre::uint16 texcrdIndexV)71          StaticBillboard(const Ogre::Vector3 &pos, float xScale, float yScale,
72             const Ogre::ColourValue &clr, Ogre::uint16 texcrdIndexU, Ogre::uint16 texcrdIndexV) :
73          xPos((float)pos.x), yPos((float)pos.y), zPos((float)pos.z), xScaleHalf(0.5f * xScale), yScaleHalf(0.5f * yScale),
74             texcoordIndexU(texcrdIndexU), texcoordIndexV(texcrdIndexV)
75          {
76             Ogre::Root::getSingletonPtr()->getRenderSystem()->convertColourValue(clr, &color);
77          }
78 
79 
80          // SVA mem friendly aligment
81          float xPos, yPos, zPos;
82          //float xScale, yScale;
83          float xScaleHalf, yScaleHalf;
84          Ogre::uint32 color;
85          Ogre::uint16 texcoordIndexU, texcoordIndexV;
86       };
87 
88    public:
89       /**
90       \brief Initializes a StaticBillboardSet object.
91       \param mgr The SceneManager to be used to display the billboards.
92       \param method The method used when rendering billboards. See the BillboardMethod
93       documentation for more information. In almost all cases, this should be set to
94       BB_METHOD_ACCELERATED for optimal speed and efficiency.
95       */
96       StaticBillboardSet(Ogre::SceneManager *mgr, Ogre::SceneNode *rootSceneNode, BillboardMethod method = BB_METHOD_ACCELERATED);
97       ~StaticBillboardSet();
98 
99       /**
100       \brief Adds a billboard to the StaticBillboardSet at the specified position.
101       \param position The desired position of the billboard.
102       \param xScale The width scale of the billboard.
103       \param yScale The height scale of the billboard.
104       \param texcoordIndex The texture tile this billboard will use. This value shoud be
105       0..n, where n is the number of slices set with setTextureSlices()
106 
107       The texcoordIndex option is only applicable if you have used setTextureSlices()
108       to divide the applied material into a number of horizontal segments. texcoordIndex selects
109       which segment is applied to the billboard as a texture.
110 
111       \note Any billboards created will not appear in the scene until you call build()
112       */
113       void createBillboard(const Ogre::Vector3 &position, float xScale = 1.0f, float yScale = 1.0f,
114          const Ogre::ColourValue &color = Ogre::ColourValue::White, Ogre::uint16 texcoordIndexU = 0, Ogre::uint16 texcoordIndexV = 0)
115       {
116          if (mRenderMethod == BB_METHOD_ACCELERATED)
117          {
118             mBillboardBuffer.push_back(new StaticBillboard(position, xScale, yScale, color, texcoordIndexU, texcoordIndexV));
119 
120             //mBillboardBuffer.push_back(bb);
121 
122             ////bb->position = position;
123             //bb->xPos = (float)position.x;
124             //bb->yPos = (float)position.y;
125             //bb->zPos = (float)position.z;
126             //bb->xScaleHalf = xScale * 0.5f;
127             //bb->yScaleHalf = yScale * 0.5f;
128 
129             //bb->texcoordIndexU = texcoordIndexU;
130             //bb->texcoordIndexV = texcoordIndexV;
131 
132             //Ogre::uint32 packedColor;
133             //Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &packedColor);
134             //bb->color = packedColor;
135          }
136          else
137          {
138             Ogre::Billboard *bb = mpFallbackBillboardSet->createBillboard(position, color);
139             bb->setDimensions(xScale, yScale);
140             bb->setTexcoordRect(texcoordIndexU * mfUFactor, texcoordIndexV * mfVFactor,
141                (texcoordIndexU + 1) * mfUFactor, (texcoordIndexV + 1) * mfVFactor);
142          }
143       }
144 
145       /**
146       \brief Sets the billboard's origin (pivotal point)
147 
148       This function can be used to set what part of the billboard image is considered the
149       origin, or "center". By default, the center of the image is used, so billboards will
150       pivot around the center and positioning a billboard will place it's center at the desired
151       location. Other origins, like BBO_BOTTOM_CENTER are good for trees, etc. BBO_CENTER is
152       used by default.
153       */
154       void setBillboardOrigin(Ogre::BillboardOrigin origin);
155 
156       /// Returns the current billboard origin.
157       /// This returns the current billboard origin as set by setBillboardOrigin().
getBillboardOrigin()158       Ogre::BillboardOrigin getBillboardOrigin() const   { return mBBOrigin; }
159 
160       /// Returns the method used to render billboards.
161       /// The billboard render method is set in the constructor. See the BillboardMethod enum
162       /// documentation for more information on billboard render methods.
getRenderMethod()163       BillboardMethod getRenderMethod() const            { return mRenderMethod; }
164 
165       ///Sets whether or not this StaticBillboardSet will be rendered.
166       /// \param visible The desired visibility state of the StaticBillboardSet (true/false)
setVisible(bool visible)167       void setVisible(bool visible)
168       {
169          if (mVisible != visible)
170          {
171             mVisible = visible;
172             mpSceneNode->setVisible(visible);
173          }
174       }
175 
176       /**
177       \brief Enables/disables distance fade-out for this billboard set
178       \param enabled Whether or not to enable fading
179       \param visibleDist The distance where billboards will be fully opaque (alpha 1)
180       \param invisibleDist The distance where billboards will be invisible (alpha 0)
181 
182       You can use this function to enable distance based alpha fading, so billboards will
183       smoothly fade out into the distance. Note that the fading is performed 2-dimensionally,
184       which means height is not taken into account when fading - only xz distances. This works
185       well with flat terrain maps, but may not be well suited for spherical worlds.
186 
187       The distance ranges given specify how the final alpha values should be calculated -
188       billboards at visibleDist will have alpha values of 1, and geometry at invisibleDist
189       will have alpha values of 0.
190 
191       \note invisibleDist may be greater than or less than visibleDist, depending on
192       whether the geometry is fading out or in to the distance.
193 
194       \note setFade() only works in BB_MODE_ACCELERATED mode.
195       */
196       void setFade(bool enabled, Ogre::Real visibleDist, Ogre::Real invisibleDist);
197 
198       /// Performs final steps required for the created billboards to appear in the scene.
199       /// Until this is called, any billboards created with createBillboard() will not appear.
200       void build();
201 
202       /// Deletes all billboards from the scene.
203       /// The only way to delete a billboard in a StaticBillboardSet is to delete them all,
204       /// which this function does.
205       void clear();
206 
207       /**
208       \brief Applies a material to the billboards in this set.
209       \param materialName The name of the material to apply to this StaticBillboardSet.
210 
211       This may actually modify the material to include a vertex shader (which
212       is used to keep the billboards facing the camera).
213       */
214       void setMaterial(const Ogre::String &materialName, const Ogre::String &resourceGroup =
215          Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
216 
217       /**
218       \brief Sets how many horizontal slices and vertical stacks the currently applied material is using.
219       \param stacks The number of vertical stacks.
220       \param slices The number of horizontal slices.
221 
222       If the applied material contains multiple images all merged together into a grid
223       you can use this function to gain access to the individual images. Simply set how
224       many tiles are contained within the material horizontally and vertically, and use
225       the texcoordIndexU and texcoordIndexV parameters of createBillboard() to specify
226       which image is to be used for the billboard.
227       */
228       void setTextureStacksAndSlices(Ogre::uint16 stacks, Ogre::uint16 slices);
229 
230       /**
231       \brief Manually updates all StaticBillboardSet objects for a frame.
232 
233       \note If you are using root->startRendering() or root->renderOneFrame() to update
234       your scene, there is no need to use this function at all. Doing so would be redundant
235       and ineffient, as it will be called	automatically in this case.
236 
237       However, if you	update all your render targets yourself, you will have to call this
238       manually per frame from your program loop. If updateAll() doesn't get called one way
239       or another, your billboards will not be updated to face the camera.
240       */
241       static void updateAll(const Ogre::Vector3 &cameraDirection);
242 
243    private:
244       ///
245       Ogre::MaterialPtr getFadeMaterial(const Ogre::MaterialPtr &protoMaterial, Ogre::Real visibleDist, Ogre::Real invisibleDist);
246 
247    private:
248 
249       typedef std::vector < StaticBillboard* >  TBillBuf;
250 
251       bool                    mVisible;                  ///<
252       bool                    mFadeEnabled;              ///<
253       BillboardMethod         mRenderMethod;             ///<
254 
255       Ogre::SceneManager*     mpSceneMgr;                ///<
256       Ogre::SceneNode*        mpSceneNode;               ///<
257       Ogre::Entity*           mpEntity;                  ///<
258       Ogre::MeshPtr           mPtrMesh;                  ///<
259       Ogre::String            mEntityName;               ///<
260       Ogre::MaterialPtr       mPtrMaterial;              ///<
261       Ogre::MaterialPtr       mPtrFadeMaterial;          ///<
262       float                   mfUFactor, mfVFactor;      ///<
263 
264       Ogre::BillboardSet*     mpFallbackBillboardSet;    ///<
265       Ogre::BillboardOrigin   mBBOrigin;                 ///<
266       Ogre::Real              mFadeVisibleDist;          ///<
267       Ogre::Real              mFadeInvisibleDist;        ///<
268       TBillBuf                mBillboardBuffer;          ///<
269 
270 
271       // static data
272    private:
273 
274       typedef std::map<Ogre::String, Ogre::MaterialPtr> FadedMaterialMap;
275 
276       static bool             s_isGLSL;                  ///< OpenGL
277       //static bool             s_shadersGenerated;        ///< First instance generate shaders for billboard rendering
278       static unsigned int     s_nSelfInstances;          ///< Instances counter
279       static FadedMaterialMap s_mapFadedMaterial;        ///<
280 
281       //static Ogre::uint32 selfInstances;
282       static unsigned long GUID;
getUniqueID(const Ogre::String & prefix)283       static Ogre::String getUniqueID(const Ogre::String &prefix) { return prefix + Ogre::StringConverter::toString(++GUID); }
284    };
285 
286 
287 
288    //-------------------------------------------------------------------------------------
289 
290    //SBMaterialRef::addMaterialRef() and ::removeMaterialRef() are used to keep track
291    //of all the materials in use by the billboard system. This is necessary when keeping
292    //the materials' vertex shaders up-to-date. To get the list of all materials in use,
293    //use getList().
294    class SBMaterialRef
295    {
296    public:
297       static void addMaterialRef(const Ogre::MaterialPtr &matP, Ogre::BillboardOrigin o);
298       static void removeMaterialRef(const Ogre::MaterialPtr &matP);
299 
getList()300       inline static SBMaterialRefList &getList() { return selfList; }
301 
getMaterial()302       inline Ogre::Material *getMaterial() { return material; }
getOrigin()303       inline Ogre::BillboardOrigin getOrigin() { return origin; }
304 
305    private:
306       SBMaterialRef(Ogre::Material *mat, Ogre::BillboardOrigin o);
307 
308       static SBMaterialRefList selfList;
309 
310       Ogre::uint32 refCount;
311       Ogre::Material *material;
312       Ogre::BillboardOrigin origin;
313    };
314 }
315