Index: ps/trunk/source/graphics/ParticleEmitter.cpp
===================================================================
--- ps/trunk/source/graphics/ParticleEmitter.cpp (revision 15480)
+++ ps/trunk/source/graphics/ParticleEmitter.cpp (revision 15481)
@@ -1,287 +1,288 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "ParticleEmitter.h"
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/ParticleEmitterType.h"
#include "graphics/ParticleManager.h"
+#include "graphics/ShaderProgram.h"
#include "graphics/TextureManager.h"
#include "renderer/Renderer.h"
CParticleEmitter::CParticleEmitter(const CParticleEmitterTypePtr& type) :
m_Type(type), m_Active(true), m_NextParticleIdx(0), m_EmissionRoundingError(0.f),
m_LastUpdateTime(type->m_Manager.GetCurrentTime()),
m_IndexArray(GL_DYNAMIC_DRAW),
m_VertexArray(GL_DYNAMIC_DRAW),
m_LastFrameNumber(-1)
{
// If we should start with particles fully emitted, pretend that we
// were created in the past so the first update will produce lots of
// particles.
// TODO: instead of this, maybe it would make more sense to do a full
// lifetime-length update of all emitters when the game first starts
// (so that e.g. buildings constructed later on won't have fully-started
// emitters, but those at the start will)?
if (m_Type->m_StartFull)
m_LastUpdateTime -= m_Type->m_MaxLifetime;
m_Particles.reserve(m_Type->m_MaxParticles);
m_AttributePos.type = GL_FLOAT;
m_AttributePos.elems = 3;
m_VertexArray.AddAttribute(&m_AttributePos);
m_AttributeAxis.type = GL_FLOAT;
m_AttributeAxis.elems = 2;
m_VertexArray.AddAttribute(&m_AttributeAxis);
m_AttributeUV.type = GL_FLOAT;
m_AttributeUV.elems = 2;
m_VertexArray.AddAttribute(&m_AttributeUV);
m_AttributeColor.type = GL_UNSIGNED_BYTE;
m_AttributeColor.elems = 4;
m_VertexArray.AddAttribute(&m_AttributeColor);
m_VertexArray.SetNumVertices(m_Type->m_MaxParticles * 4);
m_VertexArray.Layout();
m_IndexArray.SetNumVertices(m_Type->m_MaxParticles * 6);
m_IndexArray.Layout();
VertexArrayIterator index = m_IndexArray.GetIterator();
for (size_t i = 0; i < m_Type->m_MaxParticles; ++i)
{
*index++ = i*4 + 0;
*index++ = i*4 + 1;
*index++ = i*4 + 2;
*index++ = i*4 + 2;
*index++ = i*4 + 3;
*index++ = i*4 + 0;
}
m_IndexArray.Upload();
m_IndexArray.FreeBackingStore();
}
void CParticleEmitter::UpdateArrayData(int frameNumber)
{
if (m_LastFrameNumber == frameNumber)
return;
m_LastFrameNumber = frameNumber;
// Update m_Particles
m_Type->UpdateEmitter(*this, m_Type->m_Manager.GetCurrentTime() - m_LastUpdateTime);
m_LastUpdateTime = m_Type->m_Manager.GetCurrentTime();
// Regenerate the vertex array data:
VertexArrayIterator attrPos = m_AttributePos.GetIterator();
VertexArrayIterator attrAxis = m_AttributeAxis.GetIterator();
VertexArrayIterator attrUV = m_AttributeUV.GetIterator();
VertexArrayIterator attrColor = m_AttributeColor.GetIterator();
ENSURE(m_Particles.size() <= m_Type->m_MaxParticles);
CBoundingBoxAligned bounds;
for (size_t i = 0; i < m_Particles.size(); ++i)
{
// TODO: for more efficient rendering, maybe we should replace this with
// a degenerate quad if alpha is 0
bounds += m_Particles[i].pos;
*attrPos++ = m_Particles[i].pos;
*attrPos++ = m_Particles[i].pos;
*attrPos++ = m_Particles[i].pos;
*attrPos++ = m_Particles[i].pos;
// Compute corner offsets, split into sin/cos components so the vertex
// shader can multiply by the camera-right (or left?) and camera-up vectors
// to get rotating billboards:
float s = sin(m_Particles[i].angle) * m_Particles[i].size/2.f;
float c = cos(m_Particles[i].angle) * m_Particles[i].size/2.f;
(*attrAxis)[0] = c;
(*attrAxis)[1] = s;
++attrAxis;
(*attrAxis)[0] = s;
(*attrAxis)[1] = -c;
++attrAxis;
(*attrAxis)[0] = -c;
(*attrAxis)[1] = -s;
++attrAxis;
(*attrAxis)[0] = -s;
(*attrAxis)[1] = c;
++attrAxis;
(*attrUV)[0] = 1;
(*attrUV)[1] = 0;
++attrUV;
(*attrUV)[0] = 0;
(*attrUV)[1] = 0;
++attrUV;
(*attrUV)[0] = 0;
(*attrUV)[1] = 1;
++attrUV;
(*attrUV)[0] = 1;
(*attrUV)[1] = 1;
++attrUV;
SColor4ub color = m_Particles[i].color;
// Special case: If the blending depends on the source colour, not the source alpha,
// then pre-multiply by the alpha. (This is kind of a hack.)
if (m_Type->m_BlendFuncDst == GL_ONE_MINUS_SRC_COLOR)
{
color.R = (color.R * color.A) / 255;
color.G = (color.G * color.A) / 255;
color.B = (color.B * color.A) / 255;
}
*attrColor++ = color;
*attrColor++ = color;
*attrColor++ = color;
*attrColor++ = color;
}
m_ParticleBounds = bounds;
m_VertexArray.Upload();
}
void CParticleEmitter::Bind(const CShaderProgramPtr& shader)
{
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
shader->BindTexture(str_losTex, los.GetTextureSmooth());
shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
shader->Uniform(str_sunColor, lightEnv.m_SunColor);
shader->Uniform(str_fogColor, lightEnv.m_FogColor);
shader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);
shader->BindTexture(str_baseTex, m_Type->m_Texture);
pglBlendEquationEXT(m_Type->m_BlendEquation);
glBlendFunc(m_Type->m_BlendFuncSrc, m_Type->m_BlendFuncDst);
}
void CParticleEmitter::RenderArray(const CShaderProgramPtr& shader)
{
// Some drivers apparently don't like count=0 in glDrawArrays here,
// so skip all drawing in that case
if (m_Particles.empty())
return;
u8* indexBase = m_IndexArray.Bind();
u8* base = m_VertexArray.Bind();
GLsizei stride = (GLsizei)m_VertexArray.GetStride();
shader->VertexPointer(3, GL_FLOAT, stride, base + m_AttributePos.offset);
// Pass the sin/cos axis components as texcoords for no particular reason
// other than that they fit. (Maybe this should be glVertexAttrib* instead?)
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m_AttributeUV.offset);
shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, base + m_AttributeAxis.offset);
shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset);
shader->AssertPointersBound();
glDrawElements(GL_TRIANGLES, (GLsizei)(m_Particles.size() * 6), GL_UNSIGNED_SHORT, indexBase);
g_Renderer.GetStats().m_DrawCalls++;
g_Renderer.GetStats().m_Particles += m_Particles.size();
}
void CParticleEmitter::Unattach(const CParticleEmitterPtr& self)
{
m_Active = false;
m_Type->m_Manager.AddUnattachedEmitter(self);
}
void CParticleEmitter::AddParticle(const SParticle& particle)
{
if (m_NextParticleIdx >= m_Particles.size())
m_Particles.push_back(particle);
else
m_Particles[m_NextParticleIdx] = particle;
m_NextParticleIdx = (m_NextParticleIdx + 1) % m_Type->m_MaxParticles;
}
void CParticleEmitter::SetEntityVariable(const std::string& name, float value)
{
m_EntityVariables[name] = value;
}
CModelParticleEmitter::CModelParticleEmitter(const CParticleEmitterTypePtr& type) :
m_Type(type)
{
m_Emitter = CParticleEmitterPtr(new CParticleEmitter(m_Type));
}
CModelParticleEmitter::~CModelParticleEmitter()
{
m_Emitter->Unattach(m_Emitter);
}
void CModelParticleEmitter::SetEntityVariable(const std::string& name, float value)
{
m_Emitter->SetEntityVariable(name, value);
}
CModelAbstract* CModelParticleEmitter::Clone() const
{
return new CModelParticleEmitter(m_Type);
}
void CModelParticleEmitter::CalcBounds()
{
// TODO: we ought to compute sensible bounds here, probably based on the
// current computed particle positions plus the emitter type's largest
// potential bounding box at the current position
m_WorldBounds = m_Type->CalculateBounds(m_Emitter->GetPosition(), m_Emitter->GetParticleBounds());
}
void CModelParticleEmitter::ValidatePosition()
{
// TODO: do we need to do anything here?
// This is a convenient (though possibly not particularly appropriate) place
// to invalidate bounds so they'll be recomputed from the recent particle data
InvalidateBounds();
}
void CModelParticleEmitter::InvalidatePosition()
{
}
void CModelParticleEmitter::SetTransform(const CMatrix3D& transform)
{
m_Emitter->SetPosition(transform.GetTranslation());
m_Emitter->SetRotation(transform.GetRotation());
}
Index: ps/trunk/source/graphics/ShaderDefines.cpp
===================================================================
--- ps/trunk/source/graphics/ShaderDefines.cpp (revision 15480)
+++ ps/trunk/source/graphics/ShaderDefines.cpp (revision 15481)
@@ -1,280 +1,281 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "ShaderDefines.h"
+#include "graphics/ShaderProgram.h"
#include "maths/Vector4D.h"
#include "ps/ThreadUtil.h"
#include
size_t hash_value(const CVector4D& v)
{
size_t hash = 0;
boost::hash_combine(hash, v.X);
boost::hash_combine(hash, v.Y);
boost::hash_combine(hash, v.Z);
boost::hash_combine(hash, v.W);
return hash;
}
size_t hash_value(const CShaderParams::SItems& items)
{
return items.hash;
}
size_t hash_value(const CShaderParams::SItems& items)
{
return items.hash;
}
bool operator==(const CShaderParams::SItems& a, const CShaderParams::SItems& b)
{
return a.items == b.items;
}
bool operator==(const CShaderParams::SItems& a, const CShaderParams::SItems& b)
{
return a.items == b.items;
}
template
struct ItemNameCmp
{
typedef typename CShaderParams::SItems::Item Item;
typedef Item first_argument_type;
typedef Item second_argument_type;
bool operator()(const Item& a, const Item& b) const
{
return a.first < b.first;
}
};
template
struct ItemNameGeq
{
typedef typename CShaderParams::SItems::Item Item;
bool operator()(const Item& a, const Item& b) const
{
return !(b.first < a.first);
}
};
template
typename CShaderParams::SItems* CShaderParams::GetInterned(const SItems& items)
{
ENSURE(ThreadUtil::IsMainThread()); // s_InternedItems is not thread-safe
typename InternedItems_t::iterator it = s_InternedItems.find(items);
if (it != s_InternedItems.end())
return it->second.get();
// Sanity test: the items list is meant to be sorted by name.
// This is a reasonable place to verify that, since this will be called once per distinct SItems.
typedef ItemNameCmp Cmp;
ENSURE(std::adjacent_find(items.items.begin(), items.items.end(), std::binary_negate(Cmp())) == items.items.end());
shared_ptr ptr(new SItems(items));
s_InternedItems.insert(std::make_pair(items, ptr));
return ptr.get();
}
template
CShaderParams::CShaderParams()
{
*this = s_Empty;
}
template
CShaderParams::CShaderParams(SItems* items) : m_Items(items)
{
}
template
CShaderParams CShaderParams::CreateEmpty()
{
SItems items;
items.RecalcHash();
return CShaderParams(GetInterned(items));
}
template
void CShaderParams::Set(CStrIntern name, const value_t& value)
{
SItems items = *m_Items;
typename SItems::Item addedItem = std::make_pair(name, value);
// Add the new item in a way that preserves the sortedness and uniqueness of item names
for (typename std::vector::iterator it = items.items.begin(); ; ++it)
{
if (it == items.items.end() || addedItem.first < it->first)
{
items.items.insert(it, addedItem);
break;
}
else if (addedItem.first == it->first)
{
it->second = addedItem.second;
break;
}
}
items.RecalcHash();
m_Items = GetInterned(items);
}
template
void CShaderParams::SetMany(const CShaderParams& params)
{
SItems items;
// set_union merges the two sorted lists into a new sorted list;
// if two items are equivalent (i.e. equal names, possibly different values)
// then the one from the first list is kept
std::set_union(
params.m_Items->items.begin(), params.m_Items->items.end(),
m_Items->items.begin(), m_Items->items.end(),
std::inserter(items.items, items.items.begin()),
ItemNameCmp());
items.RecalcHash();
m_Items = GetInterned(items);
}
template
std::map CShaderParams::GetMap() const
{
std::map ret;
for (size_t i = 0; i < m_Items->items.size(); ++i)
ret[m_Items->items[i].first] = m_Items->items[i].second;
return ret;
}
template
size_t CShaderParams::GetHash() const
{
return m_Items->hash;
}
template
void CShaderParams::SItems::RecalcHash()
{
size_t h = 0;
for (size_t i = 0; i < items.size(); ++i)
{
boost::hash_combine(h, items[i].first);
boost::hash_combine(h, items[i].second);
}
hash = h;
}
void CShaderDefines::Add(CStrIntern name, CStrIntern value)
{
Set(name, value);
}
int CShaderDefines::GetInt(const char* name) const
{
CStrIntern nameIntern(name);
for (size_t i = 0; i < m_Items->items.size(); ++i)
{
if (m_Items->items[i].first == nameIntern)
{
int ret;
std::stringstream str(m_Items->items[i].second.c_str());
str >> ret;
return ret;
}
}
return 0;
}
void CShaderUniforms::Add(const char* name, const CVector4D& value)
{
Set(CStrIntern(name), value);
}
CVector4D CShaderUniforms::GetVector(const char* name) const
{
CStrIntern nameIntern(name);
for (size_t i = 0; i < m_Items->items.size(); ++i)
{
if (m_Items->items[i].first == nameIntern)
{
return m_Items->items[i].second;
}
}
return CVector4D();
}
void CShaderUniforms::BindUniforms(const CShaderProgramPtr& shader) const
{
const std::vector& items = m_Items->items;
for (size_t i = 0; i < items.size(); ++i)
{
CShaderProgram::Binding binding = shader->GetUniformBinding(items[i].first);
if (binding.Active())
{
CVector4D v = items[i].second;
shader->Uniform(binding, v.X, v.Y, v.Z, v.W);
}
}
}
void CShaderRenderQueries::Add(const char* name)
{
if (name == CStr("sim_time"))
{
m_Items.push_back(std::make_pair(RQUERY_TIME, CStrIntern(name)));
}
else if (name == CStr("water_tex"))
{
m_Items.push_back(std::make_pair(RQUERY_WATER_TEX, CStrIntern(name)));
}
else if (name == CStr("sky_cube"))
{
m_Items.push_back(std::make_pair(RQUERY_SKY_CUBE, CStrIntern(name)));
}
}
void CShaderConditionalDefines::Add(const char* defname, const char* defvalue, int type, std::vector &args)
{
CondDefine cd;
cd.m_DefName = CStrIntern(defname);
cd.m_DefValue = CStrIntern(defvalue);
cd.m_CondArgs = args;
cd.m_CondType = type;
m_Defines.push_back(cd);
}
// Explicit instantiations:
template<> CShaderParams::InternedItems_t CShaderParams::s_InternedItems = CShaderParams::InternedItems_t();
template<> CShaderParams::InternedItems_t CShaderParams::s_InternedItems = CShaderParams::InternedItems_t();
template<> CShaderParams CShaderParams::s_Empty = CShaderParams::CreateEmpty();
template<> CShaderParams CShaderParams::s_Empty = CShaderParams::CreateEmpty();
template class CShaderParams;
template class CShaderParams;
Index: ps/trunk/source/graphics/ShaderDefines.h
===================================================================
--- ps/trunk/source/graphics/ShaderDefines.h (revision 15480)
+++ ps/trunk/source/graphics/ShaderDefines.h (revision 15481)
@@ -1,224 +1,224 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_SHADERDEFINES
#define INCLUDED_SHADERDEFINES
-#include "graphics/ShaderProgram.h"
+#include "graphics/ShaderProgramPtr.h"
#include "ps/CStr.h"
#include "ps/CStrIntern.h"
#include
class CVector4D;
/**
* Represents a mapping of name strings to value, for use with
* CShaderDefines (values are strings) and CShaderUniforms (values are vec4s).
*
* Stored as interned vectors of name-value pairs, to support high performance
* comparison operators.
*
* Not thread-safe - must only be used from the main thread.
*/
template
class CShaderParams
{
public:
/**
* Create an empty map of defines.
*/
CShaderParams();
/**
* Add a name and associated value to the map of parameters.
* If the name is already defined, its value will be replaced.
*/
void Set(CStrIntern name, const value_t& value);
/**
* Add all the names and values from another set of parameters.
* If any name is already defined in this object, its value will be replaced.
*/
void SetMany(const CShaderParams& params);
/**
* Return a copy of the current name/value mapping.
*/
std::map GetMap() const;
/**
* Return a hash of the current mapping.
*/
size_t GetHash() const;
/**
* Compare with some arbitrary total order.
* The order may be different each time the application is run
* (it is based on interned memory addresses).
*/
bool operator<(const CShaderParams& b) const
{
return m_Items < b.m_Items;
}
/**
* Fast equality comparison.
*/
bool operator==(const CShaderParams& b) const
{
return m_Items == b.m_Items;
}
/**
* Fast inequality comparison.
*/
bool operator!=(const CShaderParams& b) const
{
return m_Items != b.m_Items;
}
struct SItems
{
// Name/value pair
typedef std::pair Item;
// Sorted by name; no duplicated names
std::vector- items;
size_t hash;
void RecalcHash();
};
protected:
SItems* m_Items; // interned value
private:
typedef boost::unordered_map > InternedItems_t;
static InternedItems_t s_InternedItems;
/**
* Returns a pointer to an SItems equal to @p items.
* The pointer will be valid forever, and the same pointer will be returned
* for any subsequent requests for an equal items list.
*/
static SItems* GetInterned(const SItems& items);
CShaderParams(SItems* items);
static CShaderParams CreateEmpty();
static CShaderParams s_Empty;
};
/**
* Represents a mapping of name strings to value strings, for use with
* \#if and \#ifdef and similar conditionals in shaders.
*
* Not thread-safe - must only be used from the main thread.
*/
class CShaderDefines : public CShaderParams
{
public:
/**
* Add a name and associated value to the map of defines.
* If the name is already defined, its value will be replaced.
*/
void Add(CStrIntern name, CStrIntern value);
/**
* Return the value for the given name as an integer, or 0 if not defined.
*/
int GetInt(const char* name) const;
};
/**
* Represents a mapping of name strings to value CVector4Ds, for use with
* uniforms in shaders.
*
* Not thread-safe - must only be used from the main thread.
*/
class CShaderUniforms : public CShaderParams
{
public:
/**
* Add a name and associated value to the map of uniforms.
* If the name is already defined, its value will be replaced.
*/
void Add(const char* name, const CVector4D& value);
/**
* Return the value for the given name, or (0,0,0,0) if not defined.
*/
CVector4D GetVector(const char* name) const;
/**
* Bind the collection of uniforms onto the given shader.
*/
void BindUniforms(const CShaderProgramPtr& shader) const;
};
// Add here the types of queries we can make in the renderer
enum RENDER_QUERIES
{
RQUERY_TIME,
RQUERY_WATER_TEX,
RQUERY_SKY_CUBE
};
/**
* Uniform values that need to be evaluated in the renderer.
*
* Not thread-safe - must only be used from the main thread.
*/
class CShaderRenderQueries
{
public:
typedef std::pair RenderQuery;
void Add(const char* name);
size_t GetSize() const { return m_Items.size(); }
RenderQuery GetItem(size_t i) const { return m_Items[i]; }
private:
std::vector m_Items;
};
enum DEFINE_CONDITION_TYPES
{
DCOND_DISTANCE
};
class CShaderConditionalDefines
{
public:
struct CondDefine
{
CStrIntern m_DefName;
CStrIntern m_DefValue;
int m_CondType;
std::vector m_CondArgs;
};
void Add(const char* defname, const char* defvalue, int type, std::vector &args);
size_t GetSize() const { return m_Defines.size(); }
const CondDefine& GetItem(size_t i) const { return m_Defines[i]; }
private:
std::vector m_Defines;
};
#endif // INCLUDED_SHADERDEFINES
Index: ps/trunk/source/graphics/ShaderProgram.h
===================================================================
--- ps/trunk/source/graphics/ShaderProgram.h (revision 15480)
+++ ps/trunk/source/graphics/ShaderProgram.h (revision 15481)
@@ -1,210 +1,209 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_SHADERPROGRAM
#define INCLUDED_SHADERPROGRAM
#include "graphics/Texture.h"
#include "lib/ogl.h"
#include "lib/file/vfs/vfs_path.h"
#include "lib/res/handle.h"
-#include "ps/CStr.h"
#include