ember/external/pagedgeometry/source/StaticBillboardSet.cpp
2021-07-08 15:50:16 +02:00

924 lines
31 KiB
C++

/*-------------------------------------------------------------------------------------
Copyright (c) 2006 John Judnich
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.
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:
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.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
-------------------------------------------------------------------------------------*/
//StaticBillboardSet.h
//Provides a method of displaying billboards faster than Ogre's built-in BillboardSet
//functions by taking advantage of the static nature of billboards (note: StaticBillboardSet
//does not allow billboards to be moved or deleted individually in real-time)
//-------------------------------------------------------------------------------------
#include "StaticBillboardSet.h"
#include "ShaderHelper.h"
#include <OgreRoot.h>
#include <OgreCamera.h>
#include <OgreVector.h>
#include <OgreMeshManager.h>
#include <OgreMesh.h>
#include <OgreSubMesh.h>
#include <OgreMaterialManager.h>
#include <OgreMaterial.h>
#include <OgreBillboardSet.h>
#include <OgreBillboard.h>
#include <OgreSceneNode.h>
#include <OgreString.h>
#include <OgreStringConverter.h>
#include <OgreRenderSystem.h>
#include <OgreRenderSystemCapabilities.h>
#include <OgreHighLevelGpuProgramManager.h>
#include <OgreHighLevelGpuProgram.h>
#include <OgreHardwareBufferManager.h>
#include <OgreHardwareBuffer.h>
#include <OgreLogManager.h>
#include <OgreEntity.h>
#include <OgreTechnique.h>
using namespace Ogre;
namespace Forests {
//-------------------------------------------------------------------------------------
unsigned long StaticBillboardSet::GUID = 0;
uint32 StaticBillboardSet::selfInstances = 0;
StaticBillboardSet::FadedMaterialMap StaticBillboardSet::fadedMaterialMap;
StaticBillboardSet::StaticBillboardSet(SceneManager *mgr, SceneNode *rootSceneNode, BillboardMethod method)
{
sceneMgr = mgr;
renderMethod = method;
visible = true;
fadeEnabled = false;
bbOrigin = BBO_CENTER;
subMesh = NULL;
//Fall back to BB_METHOD_COMPATIBLE if vertex shaders are not available
if (renderMethod == BB_METHOD_ACCELERATED){
const RenderSystemCapabilities *caps = Root::getSingleton().getRenderSystem()->getCapabilities();
if (!caps->hasCapability(RSC_VERTEX_PROGRAM))
renderMethod = BB_METHOD_COMPATIBLE;
}
node = rootSceneNode->createChildSceneNode();
entityName = getUniqueID("SBSentity");
if (renderMethod == BB_METHOD_ACCELERATED){
//Accelerated billboard method
entity = NULL;
uFactor = 1.0f;
vFactor = 1.0f;
//Load vertex shader to align billboards to face the camera (if not loaded already)
if (++selfInstances == 1){
String shaderLanguage = ShaderHelper::getShaderLanguage();
const std::string fragmentProgramName("ImposterFragStandard");
//We also need a fragment program to go with our vertex program. Especially on ATI cards on Linux where we can't mix shaders and the fixed function pipeline.
if (!HighLevelGpuProgramManager::getSingleton().getByName(fragmentProgramName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME)){
String fragmentProgSource;
if(shaderLanguage == "hlsl" || shaderLanguage == "cg")
{
fragmentProgSource = "void main \n"
"( \n"
" float2 iTexcoord : TEXCOORD0, \n"
" float iFog : FOG, \n"
" out float4 oColour : COLOR, \n"
" uniform sampler2D diffuseTexture : TEXUNIT0, \n"
" uniform float3 iFogColour, \n"
" uniform float4 iLightAmbient \n"
") \n"
"{ \n"
" oColour = tex2D(diffuseTexture, iTexcoord.xy); \n"
" oColour.xyz = lerp((iLightAmbient * oColour).xyz, iFogColour, iFog);\n"
"}";
} else {
fragmentProgSource =
"#version 330\n"
"uniform sampler2D diffuseMap;\n"
"uniform vec3 iFogColour;\n"
"uniform vec4 iLightAmbient;\n"
"in vec4 oUv0;\n"
"in float fog;\n"
"in float fade;\n"
"out vec4 fragColour;\n"
"void main() {\n"
" fragColour = texture(diffuseMap, oUv0.st);\n"
" if (fragColour.a < 0.5) { discard; }\n"
" fragColour.rgb = mix(iFogColour, iLightAmbient.rgb * fragColour.rgb, fog).rgb;\n"
" fragColour.a = fade;\n"
"}";
}
HighLevelGpuProgramPtr fragShader = HighLevelGpuProgramManager::getSingleton().createProgram(
fragmentProgramName,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
shaderLanguage, GPT_FRAGMENT_PROGRAM);
fragShader->setSource(fragmentProgSource);
if (shaderLanguage == "hlsl") {
fragShader->setParameter("target", "ps_2_0");
fragShader->setParameter("entry_point", "main");
} else if(shaderLanguage == "cg") {
fragShader->setParameter("profiles", "ps_2_0 arbfp1");
fragShader->setParameter("entry_point", "main");
}
//No need with glsl
fragShader->load();
if (fragShader->hasCompileError() || !fragShader->isSupported()) {
Ogre::LogManager::getSingleton().getDefaultLog()->logMessage("Error loading the billboard fragment shader.", Ogre::LML_CRITICAL);
}
}
//First shader, simple camera-alignment
HighLevelGpuProgramPtr vertexShader;
vertexShader = HighLevelGpuProgramManager::getSingleton().getByName("Sprite_vp", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
if (!vertexShader){
String vertexProg;
if(shaderLanguage == "hlsl" || shaderLanguage == "cg")
{
vertexProg =
"void Sprite_vp( \n"
" float4 position : POSITION, \n"
" float3 normal : NORMAL, \n"
" float4 color : COLOR, \n"
" float2 uv : TEXCOORD0, \n"
" out float4 oPosition : POSITION, \n"
" out float2 oUv : TEXCOORD0, \n"
" out float4 oColor : COLOR, \n"
" out float oFog : FOG, \n"
" uniform float4 iFogParams, \n"
" uniform float4x4 worldViewProj, \n"
" uniform float uScroll, \n"
" uniform float vScroll, \n"
" uniform float4 preRotatedQuad[4] ) \n"
"{ \n"
//Face the camera
" float4 vCenter = float4( position.x, position.y, position.z, 1.0f ); \n"
" float4 vScale = float4( normal.x, normal.y, normal.x, 1.0f ); \n"
" oPosition = mul( worldViewProj, vCenter + (preRotatedQuad[normal.z] * vScale) ); \n"
//Color
" oColor = color; \n"
//UV Scroll
" oUv = uv; \n"
" oUv.x += uScroll; \n"
" oUv.y += vScroll; \n";
//Fog
if (mgr->getFogMode() == Ogre::FOG_EXP2) {
vertexProg +=
" oFog = 1 - clamp (pow (2.71828, -oPosition.z * iFogParams.x), 0, 1); \n";
} else {
vertexProg +=
" oFog = oPosition.z; \n";
}
vertexProg += "}";
}
else
{
// Must be GLSL
vertexProg =
"#version 330\n"
"uniform mat4 worldViewProj;\n"
"uniform float uScroll; \n"
"uniform float vScroll; \n"
"uniform vec4 preRotatedQuad[4]; \n"
"uniform vec4 iFogParams; \n" // density, linear start, linear end, 1/(end-start)
"in vec3 normal;\n"
"in vec4 vertex;\n"
"in vec4 uv0;\n"
"out vec4 oUv0;\n"
"out float fog;\n"
"out float fade;\n"
"void main() { \n"
" fade = 1; \n"
//Face the camera
" vec4 vCenter = vec4( vertex.x, vertex.y, vertex.z, 1.0 ); \n"
" vec4 vScale = vec4( normal.x, normal.y, normal.x , 1.0 ); \n"
" gl_Position = worldViewProj * (vCenter + (preRotatedQuad[int(normal.z)] * vScale) ); \n"
//Color
// " gl_FrontColor = gl_Color; \n"
//UV Scroll
" oUv0 = uv0; \n"
" oUv0.x += uScroll; \n"
" oUv0.y += vScroll; \n";
//Fog
if (mgr->getFogMode() == Ogre::FOG_EXP2) {
vertexProg +=
" fog = clamp(exp(- iFogParams.x * iFogParams.x * gl_Position.z * gl_Position.z), 0.0, 1.0);\n";
} else {
vertexProg +=
" fog = gl_Position.z; \n";
}
vertexProg += "}";
}
vertexShader = HighLevelGpuProgramManager::getSingleton()
.createProgram("Sprite_vp",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
shaderLanguage, GPT_VERTEX_PROGRAM);
vertexShader->setSource(vertexProg);
if (shaderLanguage == "hlsl")
{
vertexShader->setParameter("target", "vs_1_1");
vertexShader->setParameter("entry_point", "Sprite_vp");
}
else if(shaderLanguage == "cg")
{
vertexShader->setParameter("profiles", "vs_1_1 arbvp1");
vertexShader->setParameter("entry_point", "Sprite_vp");
}
// GLSL can only have one entry point "main".
vertexShader->load();
if (vertexShader->hasCompileError() || !vertexShader->isSupported()) {
Ogre::LogManager::getSingleton().getDefaultLog()->logMessage("Error loading the billboard vertex shader.", Ogre::LML_CRITICAL);
}
}
//Second shader, camera alignment and distance based fading
HighLevelGpuProgramPtr vertexShader2;
vertexShader2 = HighLevelGpuProgramManager::getSingleton().getByName("SpriteFade_vp", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
if (!vertexShader2){
String vertexProg2;
if(shaderLanguage == "hlsl" || shaderLanguage == "cg")
{
vertexProg2 =
"void SpriteFade_vp( \n"
" float4 position : POSITION, \n"
" float3 normal : NORMAL, \n"
" float4 color : COLOR, \n"
" float2 uv : TEXCOORD0, \n"
" out float4 oPosition : POSITION, \n"
" out float2 oUv : TEXCOORD0, \n"
" out float4 oColor : COLOR, \n"
" out float oFog : FOG, \n"
" uniform float4x4 worldViewProj, \n"
" uniform float4 iFogParams, \n"
" uniform float3 camPos, \n"
" uniform float fadeGap, \n"
" uniform float invisibleDist, \n"
" uniform float uScroll, \n"
" uniform float vScroll, \n"
" uniform float4 preRotatedQuad[4] ) \n"
"{ \n"
//Face the camera
" float4 vCenter = float4( position.x, position.y, position.z, 1.0f ); \n"
" float4 vScale = float4( normal.x, normal.y, normal.x, 1.0f ); \n"
" oPosition = mul( worldViewProj, vCenter + (preRotatedQuad[normal.z] * vScale) ); \n"
" oColor.rgb = color.rgb; \n"
//Fade out in the distance
" float dist = distance(camPos.xz, position.xz); \n"
" oColor.a = (invisibleDist - dist) / fadeGap; \n"
//UV scroll
" oUv = uv; \n"
" oUv.x += uScroll; \n"
" oUv.y += vScroll; \n";
//Fog
if (mgr->getFogMode() == Ogre::FOG_EXP2) {
vertexProg2 +=
" oFog = 1 - clamp (pow (2.71828, -oPosition.z * iFogParams.x), 0, 1); \n";
} else {
vertexProg2 +=
" oFog = oPosition.z; \n";
}
vertexProg2 += "}";
}
else
{
// Must be GLSL
vertexProg2 =
"#version 330\n"
"uniform mat4 worldViewProj;\n"
"uniform vec3 camPos; \n"
"uniform float fadeGap; \n"
"uniform float invisibleDist; \n"
"uniform float uScroll; \n"
"uniform float vScroll; \n"
"uniform vec4 preRotatedQuad[4]; \n"
"uniform vec4 iFogParams; \n" // density, linear start, linear end, 1/(end-start)
"in vec3 normal;\n"
"in vec4 vertex;\n"
"in vec4 uv0;\n"
"out vec4 oUv0;\n"
"out float fog;\n"
"out float fade;\n"
"void main() { \n"
//Face the camera
" vec4 vCenter = vec4( vertex.x, vertex.y, vertex.z, 1.0 ); \n"
" vec4 vScale = vec4( normal.x, normal.y, normal.x , 1.0 ); \n"
" gl_Position = worldViewProj * (vCenter + (preRotatedQuad[int(normal.z)] * vScale) ); \n"
// " gl_FrontColor.xyz = gl_Color.xyz; \n"
//Fade out in the distance
" vec4 position = vertex; \n"
" float dist = distance(camPos.xz, position.xz); \n"
" fade = (invisibleDist - dist) / fadeGap; \n"
//UV scroll
" oUv0 = uv0; \n"
" oUv0.x += uScroll; \n"
" oUv0.y += vScroll; \n";
//Fog
if (mgr->getFogMode() == Ogre::FOG_EXP2) {
vertexProg2 +=
" fog = clamp(exp(- iFogParams.x * iFogParams.x * gl_Position.z * gl_Position.z), 0.0, 1.0);";
} else {
vertexProg2 +=
" fog = gl_Position.z; \n";
}
vertexProg2 += "}";
}
vertexShader2 = HighLevelGpuProgramManager::getSingleton()
.createProgram("SpriteFade_vp",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
shaderLanguage, GPT_VERTEX_PROGRAM);
vertexShader2->setSource(vertexProg2);
if (shaderLanguage == "hlsl")
{
vertexShader2->setParameter("target", "vs_1_1");
vertexShader2->setParameter("entry_point", "SpriteFade_vp");
}
else if(shaderLanguage == "cg")
{
vertexShader2->setParameter("profiles", "vs_1_1 arbvp1");
vertexShader2->setParameter("entry_point", "SpriteFade_vp");
}
// GLSL can only have one entry point "main".
vertexShader2->load();
if (vertexShader2->hasCompileError() || !vertexShader2->isSupported()) {
Ogre::LogManager::getSingleton().getDefaultLog()->logMessage("Error loading the billboard vertex shader.", Ogre::LML_CRITICAL);
}
}
}
} else {
//Compatible billboard method
fallbackSet = sceneMgr->createBillboardSet(getUniqueID("SBS"), 100);
node->attachObject(fallbackSet);
uFactor = 0;
vFactor = 0;
}
}
StaticBillboardSet::~StaticBillboardSet()
{
if (renderMethod == BB_METHOD_ACCELERATED){
//Delete mesh data
clear();
//Delete scene node
sceneMgr->destroySceneNode(node);
//Update material reference list
if (materialPtr) SBMaterialRef::removeMaterialRef(materialPtr);
if (fadeMaterialPtr) SBMaterialRef::removeMaterialRef(fadeMaterialPtr);
//Delete vertex shaders and materials if no longer in use
if (--selfInstances == 0){
//Delete fade materials
fadedMaterialMap.clear();
}
} else {
//Delete scene node
sceneMgr->destroySceneNode(node);
//Remove billboard set
sceneMgr->destroyBillboardSet(fallbackSet);
}
}
void StaticBillboardSet::clear()
{
if (renderMethod == BB_METHOD_ACCELERATED){
//Delete the entity and mesh data
if (entity){
//Delete entity
node->detachAllObjects();
sceneMgr->destroyEntity(entity);
entity = 0;
//Delete mesh
assert(mesh);
String meshName(mesh->getName());
mesh.reset();
if (MeshManager::getSingletonPtr())
MeshManager::getSingleton().remove(meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
}
//Remove any billboard data which might be left over if the user forgot to call build()
billboardBuffer.clear();
} else {
fallbackSet->clear();
}
}
void StaticBillboardSet::build()
{
if (renderMethod == BB_METHOD_ACCELERATED){
//Delete old entity and mesh data
if (entity){
//Delete entity
node->detachAllObjects();
sceneMgr->destroyEntity(entity);
entity = 0;
//Delete mesh
assert(mesh);
String meshName(mesh->getName());
mesh.reset();
if (MeshManager::getSingletonPtr())
MeshManager::getSingleton().remove(meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
}
//If there are no billboards to create, exit
if (billboardBuffer.empty())
return;
//Create manual mesh to store billboard quads
mesh = MeshManager::getSingleton().createManual(getUniqueID("SBSmesh"), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
subMesh = mesh->createSubMesh();
subMesh->useSharedVertices = false;
//Setup vertex format information
subMesh->vertexData = new VertexData;
subMesh->vertexData->vertexStart = 0;
subMesh->vertexData->vertexCount = 4 * billboardBuffer.size();
VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
size_t offset = 0;
dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
offset += VertexElement::getTypeSize(VET_FLOAT3);
dcl->addElement(0, offset, VET_FLOAT3, VES_NORMAL);
offset += VertexElement::getTypeSize(VET_FLOAT3);
dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
offset += VertexElement::getTypeSize(VET_COLOUR);
dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
offset += VertexElement::getTypeSize(VET_FLOAT2);
//Populate a new vertex buffer
HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
.createVertexBuffer(offset, subMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
float minX = Math::POS_INFINITY, minY = Math::POS_INFINITY, minZ = Math::POS_INFINITY;
float maxX = Math::NEG_INFINITY, maxY = Math::NEG_INFINITY, maxZ = Math::NEG_INFINITY;
std::vector<StaticBillboard>::iterator i1, i2;
i1 = billboardBuffer.begin();
i2 = billboardBuffer.end();
while (i1 != i2)
{
StaticBillboard& bb = (*i1);
float halfXScale = bb.xScale * 0.5f;
float halfYScale = bb.yScale * 0.5f;
// position
*pReal++ = bb.position.x;
*pReal++ = bb.position.y;
*pReal++ = bb.position.z;
// normals (actually used as scale / translate info for vertex shader)
*pReal++ = halfXScale;
*pReal++ = halfYScale;
*pReal++ = 0.0f;
// color
*((uint32*)pReal++) = bb.color;
// uv
*pReal++ = (bb.texcoordIndexU * uFactor);
*pReal++ = (bb.texcoordIndexV * vFactor);
// position
*pReal++ = bb.position.x;
*pReal++ = bb.position.y;
*pReal++ = bb.position.z;
// normals (actually used as scale / translate info for vertex shader)
*pReal++ = halfXScale;
*pReal++ = halfYScale;
*pReal++ = 1.0f;
// color
*((uint32*)pReal++) = bb.color;
// uv
*pReal++ = ((bb.texcoordIndexU + 1) * uFactor);
*pReal++ = (bb.texcoordIndexV * vFactor);
// position
*pReal++ = bb.position.x;
*pReal++ = bb.position.y;
*pReal++ = bb.position.z;
// normals (actually used as scale / translate info for vertex shader)
*pReal++ = halfXScale;
*pReal++ = halfYScale;
*pReal++ = 2.0f;
// color
*((uint32*)pReal++) = bb.color;
// uv
*pReal++ = (bb.texcoordIndexU * uFactor);
*pReal++ = ((bb.texcoordIndexV + 1) * vFactor);
// position
*pReal++ = bb.position.x;
*pReal++ = bb.position.y;
*pReal++ = bb.position.z;
// normals (actually used as scale / translate info for vertex shader)
*pReal++ = halfXScale;
*pReal++ = halfYScale;
*pReal++ = 3.0f;
// color
*((uint32*)pReal++) = bb.color;
// uv
*pReal++ = ((bb.texcoordIndexU + 1) * uFactor);
*pReal++ = ((bb.texcoordIndexV + 1) * vFactor);
//Update bounding box
if (bb.position.x - halfXScale < minX) minX = bb.position.x - halfXScale;
if (bb.position.x + halfXScale > maxX) maxX = bb.position.x + halfXScale;
if (bb.position.y - halfYScale < minY) minY = bb.position.y - halfYScale;
if (bb.position.y + halfYScale > maxY) maxY = bb.position.y + halfYScale;
if (bb.position.z - halfXScale < minZ) minZ = bb.position.z - halfXScale;
if (bb.position.z + halfXScale > maxZ) maxZ = bb.position.z + halfXScale;
++i1;
}
AxisAlignedBox bounds(minX, minY, minZ, maxX, maxY, maxZ);
vbuf->unlock();
subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
//Populate index buffer
subMesh->indexData->indexStart = 0;
subMesh->indexData->indexCount = 6 * billboardBuffer.size();
subMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton()
.createIndexBuffer(HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
Ogre::uint16* pI = static_cast<Ogre::uint16*>(subMesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
for (Ogre::uint16 i = 0; i < billboardBuffer.size(); ++i)
{
Ogre::uint16 offset = i * 4;
*pI++ = 0 + offset;
*pI++ = 2 + offset;
*pI++ = 1 + offset;
*pI++ = 1 + offset;
*pI++ = 2 + offset;
*pI++ = 3 + offset;
}
subMesh->indexData->indexBuffer->unlock();
//Finish up mesh
mesh->_setBounds(bounds);
Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
mesh->load();
//Empty the billboardBuffer now, because all billboards have been built
billboardBuffer.clear();
//Create an entity for the mesh
entity = sceneMgr->createEntity(entityName, mesh->getName());
entity->setCastShadows(false);
//Apply texture
if (fadeEnabled) {
assert(fadeMaterialPtr);
entity->setMaterialName(fadeMaterialPtr->getName());
} else {
assert(materialPtr);
entity->setMaterialName(materialPtr->getName());
}
//Add to scene
node->attachObject(entity);
entity->setVisible(visible);
}
}
void StaticBillboardSet::setMaterial(const String &materialName)
{
if (renderMethod == BB_METHOD_ACCELERATED){
if (!materialPtr || materialPtr->getName() != materialName){
//Update material reference list
if (fadeEnabled) {
assert(fadeMaterialPtr);
SBMaterialRef::removeMaterialRef(fadeMaterialPtr);
} else {
if (materialPtr)
SBMaterialRef::removeMaterialRef(materialPtr);
}
materialPtr = MaterialManager::getSingleton().getByName(materialName);
if (fadeEnabled) {
fadeMaterialPtr = getFadeMaterial(fadeVisibleDist, fadeInvisibleDist);
SBMaterialRef::addMaterialRef(fadeMaterialPtr, bbOrigin);
} else {
SBMaterialRef::addMaterialRef(materialPtr, bbOrigin);
}
//Apply material to entity
if (entity){
if (fadeEnabled){
entity->setMaterialName(fadeMaterialPtr->getName());
} else {
entity->setMaterialName(materialPtr->getName());
}
}
}
} else {
if (!materialPtr || materialPtr->getName() != materialName){
materialPtr = MaterialManager::getSingleton().getByName(materialName);
fallbackSet->setMaterialName(materialPtr->getName());
}
}
}
void StaticBillboardSet::setFade(bool enabled, Real visibleDist, Real invisibleDist)
{
if (renderMethod == BB_METHOD_ACCELERATED){
if (enabled){
if (!materialPtr)
OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Billboard fading cannot be enabled without a material applied first", "StaticBillboardSet::setFade()");
//Update material reference list
if (fadeEnabled) {
assert(fadeMaterialPtr);
SBMaterialRef::removeMaterialRef(fadeMaterialPtr);
} else {
assert(materialPtr);
SBMaterialRef::removeMaterialRef(materialPtr);
}
fadeMaterialPtr = getFadeMaterial(visibleDist, invisibleDist);
SBMaterialRef::addMaterialRef(fadeMaterialPtr, bbOrigin);
//Apply material to entity
if (entity)
entity->setMaterialName(fadeMaterialPtr->getName());
fadeEnabled = enabled;
fadeVisibleDist = visibleDist;
fadeInvisibleDist = invisibleDist;
} else {
if (fadeEnabled){
//Update material reference list
assert(fadeMaterialPtr);
assert(materialPtr);
SBMaterialRef::removeMaterialRef(fadeMaterialPtr);
SBMaterialRef::addMaterialRef(materialPtr, bbOrigin);
//Apply material to entity
if (entity)
entity->setMaterialName(materialPtr->getName());
fadeEnabled = enabled;
fadeVisibleDist = visibleDist;
fadeInvisibleDist = invisibleDist;
}
}
}
}
void StaticBillboardSet::setTextureStacksAndSlices(Ogre::uint16 stacks, Ogre::uint16 slices)
{
uFactor = 1.0f / slices;
vFactor = 1.0f / stacks;
}
MaterialPtr StaticBillboardSet::getFadeMaterial(Real visibleDist, Real invisibleDist)
{
Ogre::StringStream materialSignature;
materialSignature << entityName << "|";
materialSignature << visibleDist << "|";
materialSignature << invisibleDist << "|";
materialSignature << materialPtr->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureUScroll() << "|";
materialSignature << materialPtr->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureVScroll() << "|";
FadedMaterialMap::iterator it = fadedMaterialMap.find(materialSignature.str());
MaterialPtr fadeMaterial;
//If a correctly faded version of the material exists...
if (it != fadedMaterialMap.end()){
//Use the existing fade material
fadeMaterial = it->second;
} else {
//Otherwise clone the material
fadeMaterial = materialPtr->clone(getUniqueID("ImpostorFade"));
fadeMaterial->getTechnique(0)->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
fadeMaterial->getTechnique(0)->setDepthWriteEnabled(false);
//And apply the fade shader
for (unsigned short t = 0; t < fadeMaterial->getNumTechniques(); ++t){
Technique *tech = fadeMaterial->getTechnique(t);
for (unsigned short p = 0; p < tech->getNumPasses(); ++p){
Pass *pass = tech->getPass(p);
//Setup vertex program
pass->setVertexProgram("SpriteFade_vp");
pass->setFragmentProgram("ImposterFragStandard");
GpuProgramParametersSharedPtr params = pass->getVertexProgramParameters();
params->setIgnoreMissingParams(true);
params->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
params->setNamedAutoConstant("iFogParams", GpuProgramParameters::ACT_FOG_PARAMS);
params->setNamedAutoConstant("uScroll", GpuProgramParameters::ACT_CUSTOM);
params->setNamedAutoConstant("vScroll", GpuProgramParameters::ACT_CUSTOM);
params->setNamedAutoConstant("preRotatedQuad[0]", GpuProgramParameters::ACT_CUSTOM);
params->setNamedAutoConstant("preRotatedQuad[1]", GpuProgramParameters::ACT_CUSTOM);
params->setNamedAutoConstant("preRotatedQuad[2]", GpuProgramParameters::ACT_CUSTOM);
params->setNamedAutoConstant("preRotatedQuad[3]", GpuProgramParameters::ACT_CUSTOM);
params->setNamedAutoConstant("camPos", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE);
params->setNamedAutoConstant("fadeGap", GpuProgramParameters::ACT_CUSTOM);
params->setNamedAutoConstant("invisibleDist", GpuProgramParameters::ACT_CUSTOM);
//Set fade ranges
params->setNamedConstant("invisibleDist", invisibleDist);
params->setNamedConstant("fadeGap", invisibleDist - visibleDist);
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
//pass->setAlphaRejectFunction(CMPF_ALWAYS_PASS);
//pass->setDepthWriteEnabled(false);
if (pass->hasFragmentProgram()) {
params = pass->getFragmentProgramParameters();
params->setIgnoreMissingParams(true);
params->setNamedAutoConstant("iFogColour", GpuProgramParameters::ACT_FOG_COLOUR);
params->setNamedAutoConstant("iLightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR);
}
}
}
//Add it to the list so it can be reused later
fadedMaterialMap.insert(std::pair<String, MaterialPtr>(materialSignature.str(), fadeMaterial));
}
return fadeMaterial;
}
void StaticBillboardSet::updateAll(const Vector3 &cameraDirection)
{
if (selfInstances > 0){ //selfInstances will only be greater than 0 if one or more StaticBillboardSet's are using BB_METHOD_ACCELERATED
//Set shader parameter so material will face camera
Vector3 forward = cameraDirection;
Vector3 vRight = forward.crossProduct(Vector3::UNIT_Y);
Vector3 vUp = forward.crossProduct(vRight);
vRight.normalise();
vUp.normalise();
//Even if camera is upside down, the billboards should remain upright
if (vUp.y < 0) vUp *= -1;
//For each material in use by the billboard system..
SBMaterialRefList::iterator i1, i2;
i1 = SBMaterialRef::getList().begin();
i2 = SBMaterialRef::getList().end();
while (i1 != i2){
Material *mat = i1->second->getMaterial();
BillboardOrigin bbOrigin = i1->second->getOrigin();
Vector3 vPoint0, vPoint1, vPoint2, vPoint3;
if (bbOrigin == BBO_CENTER){
vPoint0 = (-vRight + vUp);
vPoint1 = ( vRight + vUp);
vPoint2 = (-vRight - vUp);
vPoint3 = ( vRight - vUp);
}
else if (bbOrigin == BBO_BOTTOM_CENTER){
vPoint0 = (-vRight + vUp + vUp);
vPoint1 = ( vRight + vUp + vUp);
vPoint2 = (-vRight);
vPoint3 = ( vRight);
}
//single prerotated quad oriented towards the camera
float preRotatedQuad[16] = {
vPoint0.x, vPoint0.y, vPoint0.z, 0.0f,
vPoint1.x, vPoint1.y, vPoint1.z, 0.0f,
vPoint2.x, vPoint2.y, vPoint2.z, 0.0f,
vPoint3.x, vPoint3.y, vPoint3.z, 0.0f
};
//Ensure material is set up with the vertex shader
Pass *p = mat->getTechnique(0)->getPass(0);
if(!p->hasVertexProgram()){
p->setVertexProgram("Sprite_vp");
p->getVertexProgramParameters()->setIgnoreMissingParams(true);
p->getVertexProgramParameters()->setNamedAutoConstant("iFogParams", GpuProgramParameters::ACT_FOG_PARAMS);
p->getVertexProgramParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
p->getVertexProgramParameters()->setNamedAutoConstant("uScroll", GpuProgramParameters::ACT_CUSTOM);
p->getVertexProgramParameters()->setNamedAutoConstant("vScroll", GpuProgramParameters::ACT_CUSTOM);
p->getVertexProgramParameters()->setNamedAutoConstant("preRotatedQuad[0]", GpuProgramParameters::ACT_CUSTOM);
p->getVertexProgramParameters()->setNamedAutoConstant("preRotatedQuad[1]", GpuProgramParameters::ACT_CUSTOM);
p->getVertexProgramParameters()->setNamedAutoConstant("preRotatedQuad[2]", GpuProgramParameters::ACT_CUSTOM);
p->getVertexProgramParameters()->setNamedAutoConstant("preRotatedQuad[3]", GpuProgramParameters::ACT_CUSTOM);
p->setFragmentProgram("ImposterFragStandard");
if (p->hasFragmentProgram()) {
p->getFragmentProgramParameters()->setIgnoreMissingParams(true);
p->getFragmentProgramParameters()->setNamedAutoConstant("iFogColour", GpuProgramParameters::ACT_FOG_COLOUR);
p->getFragmentProgramParameters()->setNamedAutoConstant("iLightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR);
}
}
//Update the vertex shader parameters
GpuProgramParametersSharedPtr params = p->getVertexProgramParameters();
params->setNamedConstant("preRotatedQuad[0]", preRotatedQuad, 4);
params->setNamedConstant("uScroll", p->getTextureUnitState(0)->getTextureUScroll());
params->setNamedConstant("vScroll", p->getTextureUnitState(0)->getTextureVScroll());
++i1;
}
}
}
void StaticBillboardSet::setBillboardOrigin(BillboardOrigin origin)
{
if (origin != BBO_CENTER && origin != BBO_BOTTOM_CENTER)
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Invalid origin - only BBO_CENTER and BBO_BOTTOM_CENTER is supported", "StaticBillboardSet::setBillboardOrigin()");
if (renderMethod == BB_METHOD_ACCELERATED){
bbOrigin = origin;
} else {
bbOrigin = origin;
fallbackSet->setBillboardOrigin(origin);
}
}
//-------------------------------------------------------------------------------------
SBMaterialRefList SBMaterialRef::selfList;
void SBMaterialRef::addMaterialRef(const MaterialPtr &matP, Ogre::BillboardOrigin o)
{
Material *mat = matP.get();
SBMaterialRef *matRef;
SBMaterialRefList::iterator it;
it = selfList.find(mat);
if (it != selfList.end()){
//Material already exists in selfList - increment refCount
matRef = it->second;
++matRef->refCount;
} else {
//Material does not exist in selfList - add it
matRef = new SBMaterialRef(mat, o);
selfList[mat] = matRef;
//No need to set refCount to 1 here because the SBMaterialRef
//constructor sets refCount to 1.
}
}
void SBMaterialRef::removeMaterialRef(const MaterialPtr &matP)
{
Material *mat = matP.get();
SBMaterialRef *matRef;
SBMaterialRefList::iterator it;
//Find material in selfList
it = selfList.find(mat);
if (it != selfList.end()){
//Decrease the reference count, and remove the item if refCount == 0
matRef = it->second;
if (--matRef->refCount == 0){
delete matRef;
selfList.erase(it);
}
}
}
SBMaterialRef::SBMaterialRef(Material *mat, Ogre::BillboardOrigin o)
{
material = mat;
origin = o;
refCount = 1;
}
}