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