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