1 /*-------------------------------------------------------------------------------------
2 Copyright (c) 2006 John Judnich
3 Modified 2008 by Erik Hjortsberg (erik.hjortsberg@iteam.se)
4
5 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.
6 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:
7 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.
8 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
9 3. This notice may not be removed or altered from any source distribution.
10 -------------------------------------------------------------------------------------*/
11
12 //ImpostorPage.h
13 //ImposterPage is an extension to PagedGeometry which displays entities as imposters.
14 //-------------------------------------------------------------------------------------
15 #pragma once
16 #include "PagedGeometry.h"
17 #include "StaticBillboardSet.h"
18
19 #include <OgrePrerequisites.h>
20 #include <OgreTextureManager.h>
21 #include <OgreRenderTexture.h>
22
23 #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
24 // linux memory fix
25 #include <memory>
26 #endif
27
28 //The number of angle increments around the yaw axis to render impostor "snapshots" of trees
29 #define IMPOSTOR_YAW_ANGLES 4 ///T
30
31 //The number of angle increments around the pitch axis to render impostor "snapshots" of trees
32 #define IMPOSTOR_PITCH_ANGLES 4
33
34 //When IMPOSTOR_RENDER_ABOVE_ONLY is defined, impostor images will only be rendered from angles around and
35 //above entities. If this is disabled, bottom views of the entities will be rendered to the impostor atlas
36 //and therefore allow those angles to be viewed from a distance. However, this requires the IMPOSTOR_PITCH_ANGLES
37 //to be doubled to maintain an equal level of impostor angle correctness compared to when impostors are rendered
38 //from above only.
39 #define IMPOSTOR_RENDER_ABOVE_ONLY
40
41 //When IMPOSTOR_FILE_SAVE is defined, impostor textures will be read and saved to disc; if not, they will stay
42 //in memory and need to be regenerated each time the application is run (remove or comment out the line below if this
43 //is desired)
44 #define IMPOSTOR_FILE_SAVE
45
46 namespace Forests
47 {
48
49 // Forward declaration
50 class ImpostorBatch;
51 class ImpostorTexture;
52
53 /// Blend modes used by ImpostorPage::setBlendMode()
54 enum ImpostorBlendMode
55 {
56 ALPHA_REJECT_IMPOSTOR,
57 ALPHA_BLEND_IMPOSTOR
58 };
59
60 //--------------------------------------------------------------------------
61 /// \brief The ImpostorPage class renders entities as impostors (billboard images that look just like the real entity).
62 ///
63 /// This is one of the geometry page types included in the StaticGeometry engine. These
64 /// page types should be added to a PagedGeometry object with PagedGeometry::addDetailLevel()
65 /// so the PagedGeometry will know how you want your geometry displayed.
66 ///
67 /// To use this page type, use:
68 /// \code
69 /// PagedGeometry::addDetailLevel<ImpostorPage>(farRange);
70 /// \endcode TexturePtr renderTexture;
71 ///
72 /// Of all the page types included in the PagedGeometry engine, this one is the fastest. It
73 /// uses impostors (billboards that look just like the real entity) to represent entities.
74 /// This way, your video card only has to render a bunch of 2D images, instead of a full 3D
75 /// mesh. Imposters are generally used off in the distance slightly, since they don't always
76 /// look exactly like the real thing, especially up close (since they are flat, and sometimes
77 /// slightly pixelated).
78 ///
79 /// \note Impostors are generated only once for each entity. If you make any changes to your
80 /// entities, you'll have to force impostor regeneration by deleting the prerender files
81 /// located in your executable's working directory (named "Impostor.[ResourceGroup].[Entity].png")
82 class ImpostorPage: public GeometryPage
83 {
84 typedef std::map<Ogre::String, ImpostorBatch*> TImpostorBatchs;
85
86 public:
87 ImpostorPage();
88 ~ImpostorPage();
89
90 ///T fake, just render impostors, dont use
ImpostorPage(Ogre::SceneManager * s,PagedGeometry * p)91 ImpostorPage(Ogre::SceneManager* s, PagedGeometry* p)
92 : m_pSceneMgr(s), m_pPagedGeom(p), m_blendMode(ALPHA_REJECT_IMPOSTOR)
93 { }
94
95 void init(PagedGeometry *geom, const Ogre::Any &data);
96
97 void setRegion(Ogre::Real left, Ogre::Real top, Ogre::Real right, Ogre::Real bottom);
98 void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
99 const Ogre::Vector3 &scale, const Ogre::ColourValue &color);
100
101 void build();
102 void removeEntities();
103 void setVisible(bool visible);
104 void setFade(bool enabled, Ogre::Real visibleDist, Ogre::Real invisibleDist);
105 void update();
106
getParentPagedGeometry()107 PagedGeometry* getParentPagedGeometry() const { return m_pPagedGeom; }
setBlendMode(ImpostorBlendMode blendMode)108 void setBlendMode(ImpostorBlendMode blendMode) { m_blendMode = blendMode; }
getBlendMode()109 ImpostorBlendMode getBlendMode() const { return m_blendMode; }
110
111 ///
getImpostorBatch(const Ogre::String & key)112 ImpostorBatch* getImpostorBatch(const Ogre::String &key) const
113 {
114 TImpostorBatchs::const_iterator it = m_mapImpostorBatches.find(key);
115 return it != m_mapImpostorBatches.end() ? it->second : NULL;
116 }
117
118 ///
injectImpostorBatch(const Ogre::String & key,ImpostorBatch * injectedBatch)119 bool injectImpostorBatch(const Ogre::String &key, ImpostorBatch *injectedBatch)
120 {
121 return injectedBatch ?
122 m_mapImpostorBatches.insert(TImpostorBatchs::value_type(key, injectedBatch)).second : false;
123 }
124
125
126 // Static functions
127
128 /**
129 \brief Sets the resolution for single impostor images.
130 \param pixels The width/height of one square impostor render
131
132 The default impostor resolution is 128x128. Note that 32 impostor images
133 will be stored in a single texture (8 x 4), so a impostor resolution of 128,
134 for example, results in final texture size of 1024 x 512.
135
136 \warning Calling this function will have no effect unless it is done before
137 any entities are added to any page.
138 */
setImpostorResolution(Ogre::uint pixels)139 static void setImpostorResolution(Ogre::uint pixels) { s_nImpostorResolution = pixels; }
getImpostorResolution()140 static Ogre::uint getImpostorResolution() { return s_nImpostorResolution; }
141
142 /**
143 \brief Sets the background color used when rendering impostor images.
144 \param color The background color
145
146 Choosing an impostor color that closely matches the main color of your objects
147 is important to reduce mipmap artifacts. When distant objects are displayed as
148 impostors, hardware mipmapping can cause the color surrounding your object (the
149 background color) to "bleed" into the main image, which can result in incorrectly
150 tinted distant objects.
151
152 The default background color is ColourValue(0.0f, 0.3f, 0.0f, 0.0f), or dark green
153 (this color was chosen because the main use of ImpostorPage is for trees, bushes, etc.)
154
155 \warning Calling this function will have no effect unless it is done before
156 any entities are added to any page. Also remember that you may have to
157 delete the old impostor renders (located in your exe's directory) in
158 order for the new ones to be generated.
159 */
setImpostorBackgroundColor(const Ogre::ColourValue & color)160 static void setImpostorBackgroundColor(const Ogre::ColourValue &color)
161 {
162 s_clrImpostorBackground = color;
163 s_clrImpostorBackground.a = 0.0f;
164 }
165
166 ///
getImpostorBackgroundColor()167 static const Ogre::ColourValue& getImpostorBackgroundColor() { return s_clrImpostorBackground; }
168
169 /**
170 \brief Sets the billboard pivot point used when rendering camera-facing impostors
171
172 This function can be used to set how impostors should rotate to face the camera. By default,
173 impostors are pointed towards the camera by rotating around the impostor billboard's center.
174 By choosing an alternate pivot point with this function, you can acheive better results under
175 certain conditions. For example, when looking up or down very steep hills, you'll probably want
176 to set BBO_BOTTOM_CENTER as the pivot point. For most other cases, however, the default pivot
177 point of BBO_CENTER works best.
178
179 \note Only BBO_CENTER and BBO_BOTTOM_CENTER is supported by this function currently.
180
181 \warning Calling this function will have no effect unless it is done before
182 any entities are added to any page.
183 */
setImpostorPivot(Ogre::BillboardOrigin origin)184 static void setImpostorPivot(Ogre::BillboardOrigin origin)
185 {
186 if (origin != Ogre::BBO_CENTER && origin != Ogre::BBO_BOTTOM_CENTER)
187 {
188 OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS,
189 "Invalid origin - only BBO_CENTER and BBO_BOTTOM_CENTER is supported",
190 "ImpostorPage::setImpostorPivot()");
191 }
192
193 s_impostorPivot = origin;
194 }
195
196 ///
getImpostorPivot()197 static Ogre::BillboardOrigin getImpostorPivot() { return s_impostorPivot; }
198
199 /**
200 \brief Regenerates the impostor texture for the specified entity
201 \param ent The entity which will have it's impostor texture regenerated
202
203 This function can be called to force the regeneration of a specific impostor.
204 Normally, impostors are generated once (saved to a file), and simply preloaded
205 from the file afterwards (unless you delete the file). Calling this will
206 instantly regenerate the impostor and update it's saved image file.
207
208 \note This function cannot regenerate an impostor unless it's already being used
209 in the scene.
210
211 \warning This is NOT a real-time operation - it may take a few seconds to complete.
212 */
213 static void regenerate(Ogre::Entity *ent);
214
215 /**
216 \brief Regenerates all impostor textures currently being used in the scene
217
218 This function can be called to force the regeneration of all impostors currently being
219 used in your scene. Normally, impostors are generated once (saved to a file), and simply
220 preloaded from the files afterwards (unless you delete the files). Calling this will
221 instantly regenerate the impostors and update their saved image files.
222
223 \warning This is NOT a real-time operation - it may take a few seconds to complete.
224 */
225 static void regenerateAll();
226
227
228 protected:
229 Ogre::SceneManager* m_pSceneMgr; ///<
230 PagedGeometry* m_pPagedGeom; ///<
231 ImpostorBlendMode m_blendMode; ///<
232 Ogre::uint m_nInstanceID; ///<
233 Ogre::uint m_nAveCount; ///<
234 Ogre::Vector3 m_vecCenter; ///<
235 Ogre::Timer m_timerUpdate; ///<
236 TImpostorBatchs m_mapImpostorBatches; ///<
237
238 private:
239
240 static Ogre::uint s_nImpostorResolution; ///<
241 static Ogre::uint s_nSelfInstances; ///<
242 static Ogre::uint s_nUpdateInstanceID; ///<
243 static Ogre::ColourValue s_clrImpostorBackground; ///<
244 static Ogre::BillboardOrigin s_impostorPivot; ///<
245 }; // end class ImpostorPage
246
247
248 //-------------------------------------------------------------------------------------
249 //This is used internally by ImpostorPage to store a "batch" of impostors. Similar
250 //impostors are all batched into ImpostorBatch'es, which contain a BillboardSet (where
251 //the actual billboards are), and a pointer to an existing ImpostorTexture.
252 class ImpostorBatch
253 {
254 public:
255 static ImpostorBatch *getBatch(ImpostorPage *group, Ogre::Entity *entity);
256 ~ImpostorBatch();
257
build()258 inline void build()
259 {
260 bbset->build();
261 }
262
clear()263 inline void clear()
264 {
265 bbset->clear();
266 }
267
setVisible(bool visible)268 inline void setVisible(bool visible)
269 {
270 bbset->setVisible(visible);
271 }
272
setFade(bool enabled,Ogre::Real visibleDist,Ogre::Real invisibleDist)273 inline void setFade(bool enabled, Ogre::Real visibleDist, Ogre::Real invisibleDist)
274 {
275 bbset->setFade(enabled, visibleDist, invisibleDist);
276 }
277
278 ///
279 void setBillboardOrigin(Ogre::BillboardOrigin origin);
280 ///
281 void addBillboard(const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
282 const Ogre::Vector3 &scale, const Ogre::ColourValue &color = Ogre::ColourValue::White);
283 ///
284 void setAngle(Ogre::Real pitchDeg, Ogre::Real yawDeg);
285
286 static Ogre::String generateEntityKey(Ogre::Entity *entity);
287
288 protected:
289 ImpostorBatch(ImpostorPage *group, Ogre::Entity *entity);
290
291 ImpostorTexture* m_pTexture;
292 StaticBillboardSet *bbset;
293
294 Ogre::Vector3 entityBBCenter;
295
296 ImpostorPage *igroup;
297
298 Ogre::uint16 pitchIndex, yawIndex;
299
300 static unsigned long GUID;
getUniqueID(const Ogre::String & prefix)301 static inline Ogre::String getUniqueID(const Ogre::String &prefix)
302 {
303 return prefix + Ogre::StringConverter::toString(++GUID);
304 }
305 };
306
307 //--------------------------------------------------------------------------
308 /// Responsible for making sure that the texture is rerendered when the
309 /// texture resource needs to be reloaded.
310 class ImpostorTextureResourceLoader : public Ogre::ManualResourceLoader
311 {
312 public:
313 /**
314 * Ctor.
315 * @param renderContext The ImpostorTexture to which this instance belongs.
316 */
317 ImpostorTextureResourceLoader(ImpostorTexture& impostorTexture);
318
319
320 /**
321 * At load time the texture will be rerendered.
322 * @param resource
323 */
324 virtual void loadResource (Ogre::Resource *resource);
325 protected:
326 ImpostorTexture& texture;
327 };
328
329 //-------------------------------------------------------------------------------------
330 //This is used internally by ImpostorPage. An ImpostorTexture is actually multiple
331 //images of an entity from various rotations. ImpostorTextures are applied
332 //to billboards to create the effect of 3D shapes, when in reality they are simply
333 //flat billboards.
334 class ImpostorTexture
335 {
336 friend class ImpostorBatch;
337 friend class ImpostorTextureResourceLoader;
338
339 public:
340 /** Returns a pointer to an ImpostorTexture for the specified entity. If one does not
341 already exist, one will automatically be created.
342 */
343 static ImpostorTexture *getTexture(ImpostorPage *group, Ogre::Entity *entity);
344
345 /** remove created texture, note that all of the ImposterTextures
346 must be deleted at once, because there is no track if a texture is still
347 being used by something else
348 */
349 static void removeTexture(ImpostorTexture* Texture);
350
351 void regenerate();
352 static void regenerateAll();
353
354 ~ImpostorTexture();
355
356 ImpostorTexture(ImpostorPage *group, Ogre::Entity *entity, bool onlyToRender=false);
357 bool bOnlyToRender;
358 protected:
359
360 void renderTextures(bool force); // Renders the impostor texture grid
361 void updateMaterials(); // Updates the materials to use the latest rendered impostor texture grid
362
363 Ogre::String removeInvalidCharacters(Ogre::String s);
364
365 static std::map<Ogre::String, ImpostorTexture *> selfList;
366 Ogre::SceneManager *sceneMgr;
367 Ogre::Entity *entity;
368 Ogre::String entityKey;
369 ImpostorPage *group;
370
371 Ogre::MaterialPtr material[IMPOSTOR_PITCH_ANGLES][IMPOSTOR_YAW_ANGLES];
372 Ogre::TexturePtr texture;
373
374 Ogre::ResourceHandle sourceMesh;
375 Ogre::AxisAlignedBox boundingBox;
376 Ogre::Real entityDiameter, entityRadius;
377 Ogre::Vector3 entityCenter;
378
379 static unsigned long GUID;
getUniqueID(const Ogre::String & prefix)380 static inline Ogre::String getUniqueID(const Ogre::String &prefix)
381 {
382 return prefix + Ogre::StringConverter::toString(++GUID);
383 }
384
385 //This will only be used when IMPOSTOR_FILE_SAVE is set to 0
386 std::auto_ptr<ImpostorTextureResourceLoader> loader;
387 };
388
389
390
391 //-------------------------------------------------------------------------------------
392 //This is an inline function from ImposterBatch that had to be defined down below the
393 //ImpostorTexture class, because it uses it.
addBillboard(const Ogre::Vector3 & position,const Ogre::Quaternion & rotation,const Ogre::Vector3 & scale,const Ogre::ColourValue & color)394 inline void ImpostorBatch::addBillboard(const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
395 const Ogre::Vector3 &scale, const Ogre::ColourValue &color)
396 {
397 //float degrees = (Math::ACos(rotation.w)*2.0f).valueDegrees();
398 const Ogre::Vector3 zVector = rotation * Ogre::Vector3::UNIT_Z;
399 float degrees = (float)Ogre::Math::ATan2(zVector.x, zVector.z).valueDegrees();
400 if (degrees < 0.f)
401 degrees += 360.f;
402
403 int n = int(IMPOSTOR_YAW_ANGLES * (degrees / 360.0f) + 0.5f);
404 Ogre::uint16 texCoordIndx = (IMPOSTOR_YAW_ANGLES - n) % IMPOSTOR_YAW_ANGLES;
405
406 bbset->createBillboard(position + (rotation * entityBBCenter) * scale,
407 float(m_pTexture->entityDiameter * 0.5f * (scale.x + scale.z)),
408 float(m_pTexture->entityDiameter * scale.y),
409 color, texCoordIndx);
410 }
411
412 } // end namespace
413