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 billboards (note: StaticBillboardSet
14 //does not allow billboards to be moved or deleted individually in real-time)
15 //-------------------------------------------------------------------------------------
16 
17 #include <OgreRoot.h>
18 #include <OgreCamera.h>
19 #include <OgreVector3.h>
20 #include <OgreMeshManager.h>
21 #include <OgreMesh.h>
22 #include <OgreSubMesh.h>
23 #include <OgreMaterialManager.h>
24 #include <OgreMaterial.h>
25 #include <OgreBillboardSet.h>
26 #include <OgreBillboard.h>
27 #include <OgreSceneNode.h>
28 #include <OgreString.h>
29 #include <OgreStringConverter.h>
30 #include <OgreRenderSystem.h>
31 #include <OgreRenderSystemCapabilities.h>
32 #include <OgreHighLevelGpuProgramManager.h>
33 #include <OgreHighLevelGpuProgram.h>
34 #include <OgreHardwareBufferManager.h>
35 #include <OgreHardwareBuffer.h>
36 #include <OgreLogManager.h>
37 #include <OgreEntity.h>
38 
39 #include "StaticBillboardSet.h"
40 
41 using namespace Ogre;
42 using namespace Forests;
43 
44 
45 // Static data initialization
46 StaticBillboardSet::FadedMaterialMap StaticBillboardSet::s_mapFadedMaterial;
47 bool StaticBillboardSet::s_isGLSL                  = false;
48 unsigned int StaticBillboardSet::s_nSelfInstances  = 0;
49 unsigned long StaticBillboardSet::GUID             = 0;
50 
51 
52 //-----------------------------------------------------------------------------
53 /// Constructor
StaticBillboardSet(SceneManager * mgr,SceneNode * rootSceneNode,BillboardMethod method)54 StaticBillboardSet::StaticBillboardSet(SceneManager *mgr, SceneNode *rootSceneNode, BillboardMethod method) :
55 mVisible                (true),
56 mFadeEnabled            (false),
57 mRenderMethod           (method),
58 mpSceneMgr              (mgr),
59 mpSceneNode             (NULL),
60 mpEntity                (NULL),
61 mfUFactor               (1.f),
62 mfVFactor               (1.f),
63 mpFallbackBillboardSet  (NULL),
64 mBBOrigin               (BBO_CENTER),
65 mFadeVisibleDist        (0.f),
66 mFadeInvisibleDist      (0.f)
67 {
68    assert(rootSceneNode);
69 
70    //Fall back to BB_METHOD_COMPATIBLE if vertex shaders are not available
71    if (method == BB_METHOD_ACCELERATED)
72    {
73       const RenderSystemCapabilities *caps = Root::getSingleton().getRenderSystem()->getCapabilities();
74       if (!caps->hasCapability(RSC_VERTEX_PROGRAM))
75          mRenderMethod = BB_METHOD_COMPATIBLE;
76    }
77 
78    mpSceneNode = rootSceneNode->createChildSceneNode();
79    mEntityName = getUniqueID("SBSentity");
80 
81    if (mRenderMethod == BB_METHOD_ACCELERATED)
82    {
83       //Load vertex shader to align billboards to face the camera (if not loaded already)
84       if (s_nSelfInstances == 0)
85       {
86          const Ogre::String &renderName = Root::getSingleton().getRenderSystem()->getName();
87          s_isGLSL = renderName == "OpenGL Rendering Subsystem" ? true : false;
88          Ogre::String shaderLanguage = s_isGLSL ? "glsl" : renderName == "Direct3D9 Rendering Subsystem" ? "hlsl" : "cg";
89 
90          //First shader, simple camera-alignment
91          String vertexProg;
92          if (!s_isGLSL) // DirectX HLSL or nVidia CG
93          {
94             vertexProg =
95                "void Sprite_vp(	\n"
96                "	float4 position : POSITION,	\n"
97                "	float3 normal   : NORMAL,	\n"
98                "	float4 color	: COLOR,	\n"
99                "	float2 uv       : TEXCOORD0,	\n"
100                "	out float4 oPosition : POSITION,	\n"
101                "	out float2 oUv       : TEXCOORD0,	\n"
102                "	out float4 oColor    : COLOR, \n"
103                "	out float oFog       : FOG,	\n"
104                "	uniform float4x4 worldViewProj,	\n"
105                "	uniform float    uScroll, \n"
106                "	uniform float    vScroll, \n"
107                "	uniform float4   preRotatedQuad[4] )	\n"
108                "{	\n"
109                //Face the camera
110                "	float4 vCenter = float4( position.x, position.y, position.z, 1.0f );	\n"
111                "	float4 vScale = float4( normal.x, normal.y, normal.x, 1.0f );	\n"
112                "	oPosition = mul( worldViewProj, vCenter + (preRotatedQuad[normal.z] * vScale) );  \n"
113 
114                //Color
115                "	oColor = color;   \n"
116 
117                //UV Scroll
118                "	oUv = uv;	\n"
119                "	oUv.x += uScroll; \n"
120                "	oUv.y += vScroll; \n"
121 
122                //Fog
123                "	oFog = oPosition.z; \n"
124                "}";
125          }
126          else     // OpenGL GLSL
127          {
128             vertexProg =
129                "uniform float uScroll; \n"
130                "uniform float vScroll; \n"
131                "uniform vec4  preRotatedQuad[4]; \n"
132 
133                "void main() { \n"
134                //Face the camera
135                "	vec4 vCenter = vec4( gl_Vertex.x, gl_Vertex.y, gl_Vertex.z, 1.0 ); \n"
136                "	vec4 vScale = vec4( gl_Normal.x, gl_Normal.y, gl_Normal.x , 1.0 ); \n"
137                "	gl_Position = gl_ModelViewProjectionMatrix * (vCenter + (preRotatedQuad[int(gl_Normal.z)] * vScale) ); \n"
138 
139                //Color
140                "	gl_FrontColor = gl_Color; \n"
141 
142                //UV Scroll
143                "	gl_TexCoord[0] = gl_MultiTexCoord0; \n"
144                "	gl_TexCoord[0].x += uScroll; \n"
145                "	gl_TexCoord[0].y += vScroll; \n"
146 
147                //Fog
148                "	gl_FogFragCoord = gl_Position.z; \n"
149                "}";
150          }
151 
152 		 HighLevelGpuProgramPtr vertexShader = HighLevelGpuProgramManager::getSingleton().getByName("Sprite_vp").staticCast<HighLevelGpuProgram>();
153          assert(vertexShader.isNull() && "Sprite_vp already exist");
154 
155          vertexShader = HighLevelGpuProgramManager::getSingleton().createProgram(
156             "Sprite_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, shaderLanguage, GPT_VERTEX_PROGRAM);
157          vertexShader->setSource(vertexProg);
158 
159          // Set entry point for vertex program. GLSL can only have one entry point "main".
160          if (!s_isGLSL)
161          {
162             if (shaderLanguage == "hlsl")
163             {
164                vertexShader->setParameter("target", "vs_1_1");
165                vertexShader->setParameter("entry_point", "Sprite_vp");
166             }
167             else if(shaderLanguage == "cg")
168             {
169                vertexShader->setParameter("profiles", "vs_1_1 arbvp1");
170                vertexShader->setParameter("entry_point", "Sprite_vp");
171             }
172             else
173             {
174                assert(false && "Unknown shader language");
175             }
176          }
177 
178          // compile vertex shader
179          vertexShader->load();
180 
181 
182          //====================================================================
183          //Second shader, camera alignment and distance based fading
184          String vertexProg2;
185          if (!s_isGLSL) // DirectX HLSL or nVidia CG
186          {
187             vertexProg2 =
188                "void SpriteFade_vp(	\n"
189                "	float4 position : POSITION,	\n"
190                "	float3 normal   : NORMAL,	\n"
191                "	float4 color	: COLOR,	\n"
192                "	float2 uv       : TEXCOORD0,	\n"
193                "	out float4 oPosition : POSITION,	\n"
194                "	out float2 oUv       : TEXCOORD0,	\n"
195                "	out float4 oColor    : COLOR, \n"
196                "	out float oFog       : FOG,	\n"
197                "	uniform float4x4 worldViewProj,	\n"
198 
199                "	uniform float3 camPos, \n"
200                "	uniform float fadeGap, \n"
201                "   uniform float invisibleDist, \n"
202 
203                "	uniform float    uScroll, \n"
204                "	uniform float    vScroll, \n"
205                "	uniform float4   preRotatedQuad[4] )	\n"
206                "{	\n"
207                //Face the camera
208                "	float4 vCenter = float4( position.x, position.y, position.z, 1.0f );	\n"
209                "	float4 vScale = float4( normal.x, normal.y, normal.x, 1.0f );	\n"
210                "	oPosition = mul( worldViewProj, vCenter + (preRotatedQuad[normal.z] * vScale) );  \n"
211 
212                "	oColor.rgb = color.rgb;   \n"
213 
214                //Fade out in the distance
215                "	float dist = distance(camPos.xz, position.xz);	\n"
216                "	oColor.a = (invisibleDist - dist) / fadeGap;   \n"
217 
218                //UV scroll
219                "	oUv = uv;	\n"
220                "	oUv.x += uScroll; \n"
221                "	oUv.y += vScroll; \n"
222 
223                //Fog
224                "	oFog = oPosition.z; \n"
225                "}";
226          }
227          else        // OpenGL GLSL
228          {
229             vertexProg2 =
230                "uniform vec3  camPos; \n"
231                "uniform float fadeGap; \n"
232                "uniform float invisibleDist; \n"
233                "uniform float uScroll; \n"
234                "uniform float vScroll; \n"
235                "uniform vec4  preRotatedQuad[4]; \n"
236 
237                "void main() { \n"
238                //Face the camera
239                "	vec4 vCenter = vec4( gl_Vertex.x, gl_Vertex.y, gl_Vertex.z, 1.0 ); \n"
240                "	vec4 vScale = vec4( gl_Normal.x, gl_Normal.y, gl_Normal.x , 1.0 ); \n"
241                "	gl_Position = gl_ModelViewProjectionMatrix * (vCenter + (preRotatedQuad[int(gl_Normal.z)] * vScale) ); \n"
242 
243                "	gl_FrontColor.xyz = gl_Color.xyz; \n"
244 
245                //Fade out in the distance
246                "	vec4 position = gl_Vertex; \n"
247                "	float dist = distance(camPos.xz, position.xz); \n"
248                "	gl_FrontColor.w = (invisibleDist - dist) / fadeGap; \n"
249 
250                //UV scroll
251                "	gl_TexCoord[0] = gl_MultiTexCoord0; \n"
252                "	gl_TexCoord[0].x += uScroll; \n"
253                "	gl_TexCoord[0].y += vScroll; \n"
254 
255                //Fog
256                "	gl_FogFragCoord = gl_Position.z; \n"
257                "}";
258          }
259 
260 		 HighLevelGpuProgramPtr vertexShader2 = HighLevelGpuProgramManager::getSingleton().getByName("SpriteFade_vp").staticCast<HighLevelGpuProgram>();
261          assert(vertexShader2.isNull() && "SpriteFade_vp already exist");
262          vertexShader2 = HighLevelGpuProgramManager::getSingleton().createProgram("SpriteFade_vp",
263             ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, shaderLanguage, GPT_VERTEX_PROGRAM);
264          vertexShader2->setSource(vertexProg2);
265 
266          // Set entry point. GLSL can only have one entry point "main".
267          if (!s_isGLSL)
268          {
269 
270             if (shaderLanguage == "hlsl")
271             {
272                vertexShader2->setParameter("target", "vs_1_1");
273                vertexShader2->setParameter("entry_point", "SpriteFade_vp");
274             }
275             else if(shaderLanguage == "cg")
276             {
277                vertexShader2->setParameter("profiles", "vs_1_1 arbvp1");
278                vertexShader2->setParameter("entry_point", "SpriteFade_vp");
279             }
280          }
281          // compile it
282          vertexShader2->load();
283       }
284    }
285    else
286    {
287       //Compatible billboard method
288       mpFallbackBillboardSet = mgr->createBillboardSet(getUniqueID("SBS"), 100);
289       mpSceneNode->attachObject(mpFallbackBillboardSet);
290       mfUFactor = mfVFactor = Ogre::Real(0.);
291    }
292 
293 
294    ++s_nSelfInstances;
295 }
296 
297 
298 //-----------------------------------------------------------------------------
299 /// Destructor
~StaticBillboardSet()300 StaticBillboardSet::~StaticBillboardSet()
301 {
302    if (mRenderMethod == BB_METHOD_ACCELERATED)
303    {
304       clear(); // Delete mesh data
305 
306       //Update material reference list
307       if (!mPtrMaterial.isNull())
308          SBMaterialRef::removeMaterialRef(mPtrMaterial);
309       if (!mPtrFadeMaterial.isNull())
310          SBMaterialRef::removeMaterialRef(mPtrFadeMaterial);
311 
312       //Delete vertex shaders and materials if no longer in use
313       if (--s_nSelfInstances == 0)
314          s_mapFadedMaterial.clear();  //Delete fade materials
315    }
316    else
317       //Remove billboard set
318       mpSceneMgr->destroyBillboardSet(mpFallbackBillboardSet);
319 
320    //Delete scene node
321    if (mpSceneNode->getParent())
322       mpSceneNode->getParentSceneNode()->removeAndDestroyChild(mpSceneNode->getName());
323    else
324       mpSceneNode->getCreator()->destroySceneNode(mpSceneNode);
325 }
326 
327 
328 //-----------------------------------------------------------------------------
329 ///
clear()330 void StaticBillboardSet::clear()
331 {
332    if (mRenderMethod == BB_METHOD_ACCELERATED)
333    {
334       //Delete the entity and mesh data
335       if (mpEntity)
336       {
337          //Delete entity
338          mpSceneNode->detachAllObjects();
339          mpEntity->_getManager()->destroyEntity(mpEntity);
340          mpEntity = NULL;
341 
342          //Delete mesh
343          String meshName(mPtrMesh->getName());
344          mPtrMesh.setNull();
345          MeshManager::getSingleton().remove(meshName);
346       }
347 
348       if (!mBillboardBuffer.empty())
349       {
350          //Remove any billboard data which might be left over if the user forgot to call build()
351          for (int i = mBillboardBuffer.size() - 1; i > 0; /* empty */ )
352             delete mBillboardBuffer[--i];
353          mBillboardBuffer.clear();
354       }
355    }
356    else
357       mpFallbackBillboardSet->clear();
358 }
359 
360 
361 //-----------------------------------------------------------------------------
362 /// Performs final steps required for the created billboards to appear in the scene.
build()363 void StaticBillboardSet::build()
364 {
365    if (mRenderMethod == BB_METHOD_ACCELERATED)
366    {
367       //Delete old entity and mesh data
368       if (mpEntity)
369       {
370          //Delete entity
371          mpSceneNode->detachAllObjects();
372          mpEntity->_getManager()->destroyEntity(mpEntity);
373          mpEntity = NULL;
374 
375          //Delete mesh
376          assert(!mPtrMesh.isNull());
377          String meshName(mPtrMesh->getName());
378          mPtrMesh.setNull();
379          MeshManager::getSingleton().remove(meshName);
380       }
381 
382       //If there are no billboards to create, exit
383       if (mBillboardBuffer.empty())
384          return;
385 
386       //Create manual mesh to store billboard quads
387       mPtrMesh = MeshManager::getSingleton().createManual(getUniqueID("SBSmesh"), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
388       Ogre::SubMesh *pSubMesh = mPtrMesh->createSubMesh();
389       pSubMesh->useSharedVertices = false;
390 
391       //Setup vertex format information
392       pSubMesh->vertexData = new VertexData;
393       pSubMesh->vertexData->vertexStart = 0;
394       pSubMesh->vertexData->vertexCount = 4 * mBillboardBuffer.size();
395 
396       VertexDeclaration* dcl = pSubMesh->vertexData->vertexDeclaration;
397       size_t offset = 0;
398       dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
399       offset += VertexElement::getTypeSize(VET_FLOAT3);
400       dcl->addElement(0, offset, VET_FLOAT3, VES_NORMAL);
401       offset += VertexElement::getTypeSize(VET_FLOAT3);
402       dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
403       offset += VertexElement::getTypeSize(VET_COLOUR);
404       dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
405       offset += VertexElement::getTypeSize(VET_FLOAT2);
406 
407       //Populate a new vertex buffer
408       HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
409          offset, pSubMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
410       float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
411 
412       float minX = (float)Math::POS_INFINITY, minY = (float)Math::POS_INFINITY, minZ = (float)Math::POS_INFINITY;
413       float maxX = (float)Math::NEG_INFINITY, maxY = (float)Math::NEG_INFINITY, maxZ = (float)Math::NEG_INFINITY;
414 
415       // For each billboard
416       size_t billboardCount = mBillboardBuffer.size();
417       for (size_t ibb = 0; ibb < billboardCount; ++ibb)
418       {
419          const StaticBillboard *bb = mBillboardBuffer[ibb];
420 
421          // position
422          ////*pReal++ = bb->xPos;
423          ////*pReal++ = bb->yPos;
424          ////*pReal++ = bb->zPos;
425 
426          ////// normals (actually used as scale / translate info for vertex shader)
427          ////*pReal++ = bb->xScaleHalf;
428          ////*pReal++ = bb->yScaleHalf;
429          memcpy(pReal, &(bb->xPos), 5 * sizeof(float));
430          pReal += 5; // move to next float value
431          *pReal++ = 0.0f;
432 
433          // color
434          *(reinterpret_cast< uint32* >(pReal++)) = bb->color;
435          // uv
436          *pReal++ = (bb->texcoordIndexU * mfUFactor);
437          *pReal++ = (bb->texcoordIndexV * mfVFactor);
438 
439 
440          // position
441          //////*pReal++ = bb->xPos;
442          //////*pReal++ = bb->yPos;
443          //////*pReal++ = bb->zPos;
444          //////// normals (actually used as scale / translate info for vertex shader)
445          //////*pReal++ = bb->xScaleHalf;
446          //////*pReal++ = bb->yScaleHalf;
447          memcpy(pReal, &(bb->xPos), 5 * sizeof(float));
448          pReal += 5; // move to next float value
449          *pReal++ = 1.0f;
450          // color
451          *(reinterpret_cast< uint32* >(pReal++)) = bb->color;
452          // uv
453          *pReal++ = ((bb->texcoordIndexU + 1) * mfUFactor);
454          *pReal++ = (bb->texcoordIndexV * mfVFactor);
455 
456 
457          // position
458          //////*pReal++ = bb->xPos;
459          //////*pReal++ = bb->yPos;
460          //////*pReal++ = bb->zPos;
461          //////// normals (actually used as scale / translate info for vertex shader)
462          //////*pReal++ = bb->xScaleHalf;
463          //////*pReal++ = bb->yScaleHalf;
464          memcpy(pReal, &(bb->xPos), 5 * sizeof(float));
465          pReal += 5; // move to next float value
466          *pReal++ = 2.0f;
467          // color
468          *(reinterpret_cast< uint32* > (pReal++)) = bb->color;
469          // uv
470          *pReal++ = (bb->texcoordIndexU * mfUFactor);
471          *pReal++ = ((bb->texcoordIndexV + 1) * mfVFactor);
472 
473 
474          // position
475          //////*pReal++ = bb->xPos;
476          //////*pReal++ = bb->yPos;
477          //////*pReal++ = bb->zPos;
478          //////// normals (actually used as scale / translate info for vertex shader)
479          //////*pReal++ = bb->xScaleHalf;
480          //////*pReal++ = bb->yScaleHalf;
481          memcpy(pReal, &(bb->xPos), 5 * sizeof(float));
482          pReal += 5; // move to next float value
483          *pReal++ = 3.0f;
484          // color
485          *(reinterpret_cast< uint32* >(pReal++)) = bb->color;
486          // uv
487          *pReal++ = ((bb->texcoordIndexU + 1) * mfUFactor);
488          *pReal++ = ((bb->texcoordIndexV + 1) * mfVFactor);
489 
490          //Update bounding box
491          if (bb->xPos - bb->xScaleHalf < minX) minX = bb->xPos - bb->xScaleHalf;
492          if (bb->xPos + bb->xScaleHalf > maxX) maxX = bb->xPos + bb->xScaleHalf;
493          if (bb->yPos - bb->yScaleHalf < minY) minY = bb->yPos - bb->yScaleHalf;
494          if (bb->yPos + bb->yScaleHalf > maxY) maxY = bb->yPos + bb->yScaleHalf;
495          //if (bb->zPos - halfXScale < minZ) minZ = bb->zPos - halfXScale;
496          //if (bb->zPos + halfXScale > maxZ) maxZ = bb->zPos + halfXScale;
497          if (bb->zPos < minZ) minZ = bb->zPos - 0.5f;
498          if (bb->zPos > maxZ) maxZ = bb->zPos + 0.5f;
499 
500          delete bb;
501       }
502       mBillboardBuffer.clear(); //Empty the mBillboardBuffer now, because all billboards have been built
503 
504       vbuf->unlock();
505       pSubMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
506 
507       // Populate index buffer
508       {
509          pSubMesh->indexData->indexStart = 0;
510          pSubMesh->indexData->indexCount = 6 * billboardCount;
511          assert(pSubMesh->indexData->indexCount <= std::numeric_limits<Ogre::uint16>::max() && "To many indices. Use 32 bit indices");
512          Ogre::HardwareIndexBufferSharedPtr &ptrIndBuf = pSubMesh->indexData->indexBuffer;
513          ptrIndBuf = HardwareBufferManager::getSingleton().createIndexBuffer(HardwareIndexBuffer::IT_16BIT,
514             pSubMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
515 
516          Ogre::uint16 *pIBuf = static_cast < Ogre::uint16* > (ptrIndBuf->lock(HardwareBuffer::HBL_DISCARD));
517          for (Ogre::uint16 i = 0; i < billboardCount; ++i)
518          {
519             Ogre::uint16 offset = i * 4;
520 
521             *pIBuf++ = 0 + offset;
522             *pIBuf++ = 2 + offset;
523             *pIBuf++ = 1 + offset;
524 
525             *pIBuf++ = 1 + offset;
526             *pIBuf++ = 2 + offset;
527             *pIBuf++ = 3 + offset;
528          }
529          ptrIndBuf->unlock(); // Unlock buffer and update GPU
530       }
531 
532       // Finish up mesh
533       {
534          AxisAlignedBox bounds(minX, minY, minZ, maxX, maxY, maxZ);
535          mPtrMesh->_setBounds(bounds);
536          Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
537          mPtrMesh->_setBoundingSphereRadius(temp.length() * 0.5f);
538 
539          // Loading mesh
540          Ogre::LoggingLevel logLev = LogManager::getSingleton().getDefaultLog()->getLogDetail();
541          LogManager::getSingleton().setLogDetail(LL_LOW);
542          mPtrMesh->load();
543          LogManager::getSingleton().setLogDetail(logLev);
544       }
545 
546       // Create an entity for the mesh
547       mpEntity = mpSceneMgr->createEntity(mEntityName, mPtrMesh->getName(), mPtrMesh->getGroup());
548       mpEntity->setCastShadows(false);
549       mpEntity->setMaterial(mFadeEnabled ? mPtrFadeMaterial : mPtrMaterial);
550 
551       // Add to scene
552       mpSceneNode->attachObject(mpEntity);
553       mpEntity->setVisible(mVisible);
554    }
555 }
556 
557 
558 //-----------------------------------------------------------------------------
559 ///
setMaterial(const String & materialName,const Ogre::String & resourceGroup)560 void StaticBillboardSet::setMaterial(const String &materialName, const Ogre::String &resourceGroup)
561 {
562    bool needUpdateMat = mPtrMaterial.isNull() || mPtrMaterial->getName() != materialName || mPtrMaterial->getGroup() != resourceGroup;
563    if (!needUpdateMat)
564       return;
565 
566    if (mRenderMethod == BB_METHOD_ACCELERATED)
567    {
568       //Update material reference list
569       if (mFadeEnabled)
570       {
571          assert(!mPtrFadeMaterial.isNull());
572          SBMaterialRef::removeMaterialRef(mPtrFadeMaterial);
573       }
574       else if (!mPtrMaterial.isNull())
575          SBMaterialRef::removeMaterialRef(mPtrMaterial);
576 
577 	  mPtrMaterial = MaterialManager::getSingleton().getByName(materialName, resourceGroup).staticCast<Material>();
578 
579       if (mFadeEnabled)
580       {
581          mPtrFadeMaterial = getFadeMaterial(mPtrMaterial, mFadeVisibleDist, mFadeInvisibleDist);
582          SBMaterialRef::addMaterialRef(mPtrFadeMaterial, mBBOrigin);
583       }
584       else
585          SBMaterialRef::addMaterialRef(mPtrMaterial, mBBOrigin);
586 
587       //Apply material to entity
588       if (mpEntity)
589          mpEntity->setMaterial(mFadeEnabled ? mPtrFadeMaterial : mPtrMaterial);
590    }
591    else  // old GPU compatibility
592    {
593       mPtrMaterial = MaterialManager::getSingleton().getByName(materialName, resourceGroup).staticCast<Material>();
594       mpFallbackBillboardSet->setMaterialName(mPtrMaterial->getName(), mPtrMaterial->getGroup());
595       // SVA. Since Ogre 1.7.3 Ogre::BillboardSet have setMaterial(const MaterialPtr&) method
596    }
597 }
598 
599 
600 //-----------------------------------------------------------------------------
601 ///
setFade(bool enabled,Real visibleDist,Real invisibleDist)602 void StaticBillboardSet::setFade(bool enabled, Real visibleDist, Real invisibleDist)
603 {
604    if (mRenderMethod == BB_METHOD_ACCELERATED)
605    {
606       if (enabled)
607       {
608          if (mPtrMaterial.isNull())
609             OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Billboard fading cannot be enabled without a material applied first", "StaticBillboardSet::setFade()");
610 
611          //Update material reference list
612          if (mFadeEnabled)
613          {
614             assert(!mPtrFadeMaterial.isNull());
615             SBMaterialRef::removeMaterialRef(mPtrFadeMaterial);
616          }
617          else
618          {
619             assert(!mPtrMaterial.isNull());
620             SBMaterialRef::removeMaterialRef(mPtrMaterial);
621          }
622 
623          mPtrFadeMaterial = getFadeMaterial(mPtrMaterial, visibleDist, invisibleDist);
624          SBMaterialRef::addMaterialRef(mPtrFadeMaterial, mBBOrigin);
625 
626          //Apply material to entity
627          if (mpEntity)
628             mpEntity->setMaterial(mPtrFadeMaterial);
629 
630          mFadeEnabled = true;
631          mFadeVisibleDist = visibleDist;
632          mFadeInvisibleDist = invisibleDist;
633       }
634       else  // disable
635       {
636          if (mFadeEnabled)
637          {
638             //Update material reference list
639             assert(!mPtrFadeMaterial.isNull());
640             assert(!mPtrMaterial.isNull());
641             SBMaterialRef::removeMaterialRef(mPtrFadeMaterial);
642             SBMaterialRef::addMaterialRef(mPtrMaterial, mBBOrigin);
643 
644             //Apply material to entity
645             if (mpEntity)
646                mpEntity->setMaterial(mPtrMaterial);
647 
648             mFadeEnabled = false;
649             mFadeVisibleDist = visibleDist;
650             mFadeInvisibleDist = invisibleDist;
651          }
652       }
653    }
654 }
655 
656 
657 //-----------------------------------------------------------------------------
658 ///
setTextureStacksAndSlices(Ogre::uint16 stacks,Ogre::uint16 slices)659 void StaticBillboardSet::setTextureStacksAndSlices(Ogre::uint16 stacks, Ogre::uint16 slices)
660 {
661    assert(stacks != 0 && slices != 0 && "division by zero");
662    mfUFactor = 1.0f / slices;
663    mfVFactor = 1.0f / stacks;
664 }
665 
666 
667 //-----------------------------------------------------------------------------
668 ///
getFadeMaterial(const Ogre::MaterialPtr & protoMaterial,Real visibleDist_,Real invisibleDist_)669 MaterialPtr StaticBillboardSet::getFadeMaterial(const Ogre::MaterialPtr &protoMaterial,
670                                                 Real visibleDist_, Real invisibleDist_)
671 {
672    assert(!protoMaterial.isNull());
673 
674    StringUtil::StrStreamType materialSignature;
675    materialSignature << mEntityName << "|";
676    materialSignature << visibleDist_ << "|";
677    materialSignature << invisibleDist_ << "|";
678    materialSignature << protoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureUScroll() << "|";
679    materialSignature << protoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureVScroll() << "|";
680 
681    FadedMaterialMap::iterator it = s_mapFadedMaterial.find(materialSignature.str());
682    if (it != s_mapFadedMaterial.end())
683       return it->second; //Use the existing fade material
684    else
685    {
686       MaterialPtr fadeMaterial = protoMaterial->clone(getUniqueID("ImpostorFade"));
687 
688       bool isglsl = Root::getSingleton().getRenderSystem()->getName() == "OpenGL Rendering Subsystem" ? true : false;
689 
690       //And apply the fade shader
691       for (unsigned short t = 0; t < fadeMaterial->getNumTechniques(); ++t)
692       {
693          Technique *tech = fadeMaterial->getTechnique(t);
694          for (unsigned short p = 0; p < tech->getNumPasses(); ++p)
695          {
696             Pass *pass = tech->getPass(p);
697 
698             //Setup vertex program
699             pass->setVertexProgram("SpriteFade_vp");
700             GpuProgramParametersSharedPtr params = pass->getVertexProgramParameters();
701 
702             //glsl can use the built in gl_ModelViewProjectionMatrix
703             if (!isglsl)
704                params->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
705 
706             static const Ogre::String uScroll = "uScroll", vScroll = "vScroll",
707                preRotatedQuad0 = "preRotatedQuad[0]", preRotatedQuad1 = "preRotatedQuad[1]",
708                preRotatedQuad2 = "preRotatedQuad[2]", preRotatedQuad3 = "preRotatedQuad[3]",
709                camPos = "camPos", fadeGap = "fadeGap", invisibleDist = "invisibleDist";
710 
711             params->setNamedAutoConstant(uScroll, GpuProgramParameters::ACT_CUSTOM);
712             params->setNamedAutoConstant(vScroll, GpuProgramParameters::ACT_CUSTOM);
713             params->setNamedAutoConstant(preRotatedQuad0, GpuProgramParameters::ACT_CUSTOM);
714             params->setNamedAutoConstant(preRotatedQuad1, GpuProgramParameters::ACT_CUSTOM);
715             params->setNamedAutoConstant(preRotatedQuad2, GpuProgramParameters::ACT_CUSTOM);
716             params->setNamedAutoConstant(preRotatedQuad3, GpuProgramParameters::ACT_CUSTOM);
717 
718             params->setNamedAutoConstant(camPos, GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE);
719             params->setNamedAutoConstant(fadeGap, GpuProgramParameters::ACT_CUSTOM);
720             params->setNamedAutoConstant(invisibleDist, GpuProgramParameters::ACT_CUSTOM);
721 
722             //Set fade ranges
723             params->setNamedConstant(invisibleDist, invisibleDist_);
724             params->setNamedConstant(fadeGap, invisibleDist_ - visibleDist_);
725 
726             pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
727             //pass->setAlphaRejectFunction(CMPF_ALWAYS_PASS);
728             //pass->setDepthWriteEnabled(false);
729 
730          }  // for Pass
731 
732       }  // for Technique
733 
734       //Add it to the list so it can be reused later
735       s_mapFadedMaterial.insert(std::pair<String, MaterialPtr>(materialSignature.str(), fadeMaterial));
736 
737       return fadeMaterial;
738    }
739 }
740 
741 
742 //-----------------------------------------------------------------------------
743 ///
updateAll(const Vector3 & cameraDirection)744 void StaticBillboardSet::updateAll(const Vector3 &cameraDirection)
745 {
746    // s_nSelfInstances will only be greater than 0 if one or more StaticBillboardSet's are using BB_METHOD_ACCELERATED
747    if (s_nSelfInstances == 0)
748       return;
749 
750    //Set shader parameter so material will face camera
751    Vector3 forward = cameraDirection;
752    Vector3 vRight = forward.crossProduct(Vector3::UNIT_Y);
753    Vector3 vUp = forward.crossProduct(vRight);
754    vRight.normalise();
755    vUp.normalise();
756 
757    //Even if camera is upside down, the billboards should remain upright
758    if (vUp.y < 0)
759       vUp *= -1;
760 
761    // Precompute preRotatedQuad for both cases (BBO_CENTER, BBO_BOTTOM_CENTER)
762 
763    Vector3 vPoint0 = (-vRight + vUp);
764    Vector3 vPoint1 = ( vRight + vUp);
765    Vector3 vPoint2 = (-vRight - vUp);
766    Vector3 vPoint3 = ( vRight - vUp);
767 
768    float preRotatedQuad_BBO_CENTER[16] = // single prerotated quad oriented towards the camera
769    {
770       (float)vPoint0.x, (float)vPoint0.y, (float)vPoint0.z, 0.0f,
771       (float)vPoint1.x, (float)vPoint1.y, (float)vPoint1.z, 0.0f,
772       (float)vPoint2.x, (float)vPoint2.y, (float)vPoint2.z, 0.0f,
773       (float)vPoint3.x, (float)vPoint3.y, (float)vPoint3.z, 0.0f
774    };
775 
776    vPoint0 = (-vRight + vUp + vUp);
777    vPoint1 = ( vRight + vUp + vUp);
778    vPoint2 = (-vRight);
779    vPoint3 = ( vRight);
780    float preRotatedQuad_BBO_BOTTOM_CENTER[16] =
781    {
782       (float)vPoint0.x, (float)vPoint0.y, (float)vPoint0.z, 0.0f,
783       (float)vPoint1.x, (float)vPoint1.y, (float)vPoint1.z, 0.0f,
784       (float)vPoint2.x, (float)vPoint2.y, (float)vPoint2.z, 0.0f,
785       (float)vPoint3.x, (float)vPoint3.y, (float)vPoint3.z, 0.0f
786    };
787 
788    // Shaders uniform variables
789    static const Ogre::String uScroll = "uScroll", vScroll = "vScroll", preRotatedQuad0 = "preRotatedQuad[0]",
790       preRotatedQuad1 = "preRotatedQuad[1]", preRotatedQuad2 = "preRotatedQuad[2]", preRotatedQuad3 = "preRotatedQuad[3]";
791 
792    // SVA for Ogre::Material hack
793    const GpuConstantDefinition *pGPU_ConstDef_preRotatedQuad0 = 0,
794       *pGPU_ConstDef_uScroll = 0, *pGPU_ConstDef_vScroll = 0;
795 
796    // For each material in use by the billboard system..
797    SBMaterialRefList::iterator i1 = SBMaterialRef::getList().begin(), iend = SBMaterialRef::getList().end();
798    while (i1 != iend)
799    {
800       Ogre::Material *mat = i1->second->getMaterial();
801 
802       // Ensure material is set up with the vertex shader
803       Pass *p = mat->getTechnique(0)->getPass(0);
804       if (!p->hasVertexProgram())
805       {
806          static const Ogre::String Sprite_vp = "Sprite_vp";
807          p->setVertexProgram(Sprite_vp);
808 
809          // glsl can use the built in gl_ModelViewProjectionMatrix
810          if (!s_isGLSL)
811             p->getVertexProgramParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
812 
813          GpuProgramParametersSharedPtr params = p->getVertexProgramParameters();
814          params->setNamedAutoConstant(uScroll, GpuProgramParameters::ACT_CUSTOM);
815          params->setNamedAutoConstant(vScroll, GpuProgramParameters::ACT_CUSTOM);
816          params->setNamedAutoConstant(preRotatedQuad0, GpuProgramParameters::ACT_CUSTOM);
817          params->setNamedAutoConstant(preRotatedQuad1, GpuProgramParameters::ACT_CUSTOM);
818          params->setNamedAutoConstant(preRotatedQuad2, GpuProgramParameters::ACT_CUSTOM);
819          params->setNamedAutoConstant(preRotatedQuad3, GpuProgramParameters::ACT_CUSTOM);
820       }
821 
822       // Which prerotated quad use
823       const float *pQuad = i1->second->getOrigin() == BBO_CENTER ? preRotatedQuad_BBO_CENTER : preRotatedQuad_BBO_BOTTOM_CENTER;
824 
825       // Update the vertex shader parameters
826       GpuProgramParametersSharedPtr params = p->getVertexProgramParameters();
827       //params->setNamedConstant(preRotatedQuad0, pQuad, 4);
828       //params->setNamedConstant(uScroll, p->getTextureUnitState(0)->getTextureUScroll());
829       //params->setNamedConstant(vScroll, p->getTextureUnitState(0)->getTextureVScroll());
830 
831       // SVA some hack of Ogre::Material.
832       // Since material are cloned and use same vertex shader "Sprite_vp" hardware GPU indices
833       // must be same. I don`t know planes of Ogre Team to change this behaviour.
834       // Therefore this may be unsafe code. Instead of 3 std::map lookups(map::find(const Ogre::String&)) do only 1
835       {
836          const GpuConstantDefinition *def = params->_findNamedConstantDefinition(preRotatedQuad0, true);
837          if (def != pGPU_ConstDef_preRotatedQuad0) // new material, reread
838          {
839             pGPU_ConstDef_preRotatedQuad0 = def;
840             pGPU_ConstDef_uScroll         = params->_findNamedConstantDefinition(uScroll, true);
841             pGPU_ConstDef_vScroll         = params->_findNamedConstantDefinition(vScroll, true);
842          }
843       }
844 
845       float fUScroll = (float)p->getTextureUnitState(0)->getTextureUScroll(),
846          fVScroll = (float)p->getTextureUnitState(0)->getTextureVScroll();
847       params->_writeRawConstants(pGPU_ConstDef_preRotatedQuad0->physicalIndex, pQuad, 16);
848       params->_writeRawConstants(pGPU_ConstDef_uScroll->physicalIndex, &fUScroll, 1);
849       params->_writeRawConstants(pGPU_ConstDef_vScroll->physicalIndex, &fVScroll, 1);
850 
851       ++i1; // next material in billboard system
852    }
853 }
854 
855 
856 //-----------------------------------------------------------------------------
857 ///
setBillboardOrigin(BillboardOrigin origin)858 void StaticBillboardSet::setBillboardOrigin(BillboardOrigin origin)
859 {
860    assert((origin == BBO_CENTER || origin == BBO_BOTTOM_CENTER) && "Invalid origin - only BBO_CENTER and BBO_BOTTOM_CENTER is supported");
861    mBBOrigin = origin;
862    if (mRenderMethod != BB_METHOD_ACCELERATED)
863       mpFallbackBillboardSet->setBillboardOrigin(origin);
864 }
865 
866 
867 //-------------------------------------------------------------------------------------
868 
869 SBMaterialRefList SBMaterialRef::selfList;
870 
addMaterialRef(const MaterialPtr & matP,Ogre::BillboardOrigin o)871 void SBMaterialRef::addMaterialRef(const MaterialPtr &matP, Ogre::BillboardOrigin o)
872 {
873    Material *mat = matP.getPointer();
874 
875    SBMaterialRef *matRef;
876    SBMaterialRefList::iterator it;
877    it = selfList.find(mat);
878 
879    if (it != selfList.end()){
880       //Material already exists in selfList - increment refCount
881       matRef = it->second;
882       ++matRef->refCount;
883    } else {
884       //Material does not exist in selfList - add it
885       matRef = new SBMaterialRef(mat, o);
886       selfList[mat] = matRef;
887       //No need to set refCount to 1 here because the SBMaterialRef
888       //constructor sets refCount to 1.
889    }
890 }
891 
removeMaterialRef(const MaterialPtr & matP)892 void SBMaterialRef::removeMaterialRef(const MaterialPtr &matP)
893 {
894    Material *mat = matP.getPointer();
895 
896    SBMaterialRef *matRef;
897    SBMaterialRefList::iterator it;
898 
899    //Find material in selfList
900    it = selfList.find(mat);
901    if (it != selfList.end()){
902       //Decrease the reference count, and remove the item if refCount == 0
903       matRef = it->second;
904       if (--matRef->refCount == 0){
905          delete matRef;
906          selfList.erase(it);
907       }
908    }
909 }
910 
SBMaterialRef(Material * mat,Ogre::BillboardOrigin o)911 SBMaterialRef::SBMaterialRef(Material *mat, Ogre::BillboardOrigin o)
912 {
913    material = mat;
914    origin = o;
915    refCount = 1;
916 }
917