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