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 struct CColor; class CMatrix3D; class CVector3D; class CShaderDefines; class CStrIntern; // Vertex data stream flags enum { STREAM_POS = (1 << 0), STREAM_NORMAL = (1 << 1), STREAM_COLOR = (1 << 2), STREAM_UV0 = (1 << 3), STREAM_UV1 = (1 << 4), STREAM_UV2 = (1 << 5), STREAM_UV3 = (1 << 6), STREAM_POSTOUV0 = (1 << 7), STREAM_POSTOUV1 = (1 << 8), STREAM_POSTOUV2 = (1 << 9), STREAM_POSTOUV3 = (1 << 10) }; /** * A compiled vertex+fragment shader program. * The implementation may use GL_ARB_{vertex,fragment}_program (ARB assembly syntax) * or GL_ARB_{vertex,fragment}_shader (GLSL), or may use hard-coded fixed-function * multitexturing setup code; the difference is hidden from the caller. * * Texture/uniform IDs are typically strings, corresponding to the names defined in * the shader .xml file. Alternatively (and more efficiently, if used very frequently), * call GetTextureBinding/GetUniformBinding and pass its return value as the ID. * Setting uniforms that the shader .xml doesn't support is harmless. * * For a high-level overview of shaders and materials, see * http://trac.wildfiregames.com/wiki/MaterialSystem */ class CShaderProgram { NONCOPYABLE(CShaderProgram); public: typedef CStrIntern attrib_id_t; typedef CStrIntern texture_id_t; typedef CStrIntern uniform_id_t; typedef std::pair frag_index_pair_t; /** * Construct based on ARB vertex/fragment program files. */ static CShaderProgram* ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile, const CShaderDefines& defines, const std::map& vertexIndexes, const std::map& fragmentIndexes, int streamflags); /** * Construct based on GLSL vertex/fragment shader files. */ static CShaderProgram* ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile, const CShaderDefines& defines, const std::map& vertexAttribs, int streamflags); /** * Construct an instance of a pre-defined fixed-function pipeline setup. */ static CShaderProgram* ConstructFFP(const std::string& id, const CShaderDefines& defines); /** * Represents a uniform attribute or texture binding. * For uniforms: * - ARB shaders store vertex location in 'first', fragment location in 'second'. * - GLSL shaders store uniform location in 'first', data type in 'second'. * - FFP shaders store -1 in 'first', index in 'second'. * For textures, all store texture target (e.g. GL_TEXTURE_2D) in 'first', texture unit in 'second'. * Non-existent bindings must store -1 in both. */ struct Binding { Binding(int a, int b) : first(a), second(b) { } Binding() : first(-1), second(-1) { } /** * Returns whether this uniform attribute is active in the shader. * If not then there's no point calling Uniform() to set its value. */ bool Active() { return first != -1 || second != -1; } int first; int second; }; virtual ~CShaderProgram() { } virtual void Reload() = 0; /** * Returns whether this shader was successfully loaded. */ bool IsValid() const; /** * Binds the shader into the GL context. Call this before calling Uniform() * or trying to render with it. */ virtual void Bind() = 0; /** * Unbinds the shader from the GL context. Call this after rendering with it. */ virtual void Unbind() = 0; /** * Returns bitset of STREAM_* value, indicating what vertex data streams the * vertex shader needs (e.g. position, color, UV, ...). */ int GetStreamFlags() const; virtual Binding GetTextureBinding(texture_id_t id) = 0; // Variants of texture binding: void BindTexture(texture_id_t id, CTexturePtr tex); virtual void BindTexture(texture_id_t id, Handle tex) = 0; virtual void BindTexture(texture_id_t id, GLuint tex) = 0; virtual void BindTexture(Binding id, Handle tex) = 0; virtual Binding GetUniformBinding(uniform_id_t id) = 0; // Uniform-setting methods that subclasses must define: virtual void Uniform(Binding id, float v0, float v1, float v2, float v3) = 0; virtual void Uniform(Binding id, const CMatrix3D& v) = 0; virtual void Uniform(Binding id, size_t count, const CMatrix3D* v) = 0; // Convenient uniform-setting wrappers: void Uniform(Binding id, int v); void Uniform(Binding id, float v); void Uniform(Binding id, float v0, float v1); void Uniform(Binding id, const CVector3D& v); void Uniform(Binding id, const CColor& v); void Uniform(uniform_id_t id, int v); void Uniform(uniform_id_t id, float v); void Uniform(uniform_id_t id, float v0, float v1); void Uniform(uniform_id_t id, const CVector3D& v); void Uniform(uniform_id_t id, const CColor& v); void Uniform(uniform_id_t id, float v0, float v1, float v2, float v3); void Uniform(uniform_id_t id, const CMatrix3D& v); void Uniform(uniform_id_t id, size_t count, const CMatrix3D* v); // Vertex attribute pointers (equivalent to glVertexPointer etc): virtual void VertexPointer(GLint size, GLenum type, GLsizei stride, void* pointer); virtual void NormalPointer(GLenum type, GLsizei stride, void* pointer); virtual void ColorPointer(GLint size, GLenum type, GLsizei stride, void* pointer); virtual void TexCoordPointer(GLenum texture, GLint size, GLenum type, GLsizei stride, void* pointer); virtual void VertexAttribPointer(attrib_id_t id, GLint size, GLenum type, GLboolean normalized, GLsizei stride, void* pointer); virtual void VertexAttribIPointer(attrib_id_t id, GLint size, GLenum type, GLsizei stride, void* pointer); /** * Checks that all the required vertex attributes have been set. * Call this before calling glDrawArrays/glDrawElements etc to avoid potential crashes. */ void AssertPointersBound(); protected: CShaderProgram(int streamflags); bool m_IsValid; int m_StreamFlags; // Non-GLSL client state handling: void BindClientStates(); void UnbindClientStates(); int m_ValidStreams; // which streams have been specified via VertexPointer etc since the last Bind }; typedef shared_ptr CShaderProgramPtr; #endif // INCLUDED_SHADERPROGRAM Index: ps/trunk/source/graphics/ShaderProgramPtr.h =================================================================== --- ps/trunk/source/graphics/ShaderProgramPtr.h (nonexistent) +++ ps/trunk/source/graphics/ShaderProgramPtr.h (revision 15481) @@ -0,0 +1,28 @@ +/* Copyright (C) 2014 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_SHADERPROGRAMPTR +#define INCLUDED_SHADERPROGRAMPTR + +/* + * Forward declaration, to reduce the number of header files that have to pull + * in the whole of ShaderProgram.h + */ +class CShaderProgram; +typedef shared_ptr CShaderProgramPtr; + +#endif // INCLUDED_SHADERPROGRAMPTR Property changes on: ps/trunk/source/graphics/ShaderProgramPtr.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/graphics/ShaderTechnique.cpp =================================================================== --- ps/trunk/source/graphics/ShaderTechnique.cpp (revision 15480) +++ ps/trunk/source/graphics/ShaderTechnique.cpp (revision 15481) @@ -1,162 +1,164 @@ /* 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 "ShaderTechnique.h" +#include "graphics/ShaderProgram.h" + CShaderPass::CShaderPass() : m_HasAlpha(false), m_HasBlend(false), m_HasColorMask(false), m_HasDepthMask(false), m_HasDepthFunc(false) { } void CShaderPass::Bind() { m_Shader->Bind(); #if !CONFIG2_GLES if (m_HasAlpha) { glEnable(GL_ALPHA_TEST); glAlphaFunc(m_AlphaFunc, m_AlphaRef); } #endif // TODO: maybe emit some warning if GLSL shaders try to use alpha test; // the test should be done inside the shader itself if (m_HasBlend) { glEnable(GL_BLEND); glBlendFunc(m_BlendSrc, m_BlendDst); } if (m_HasColorMask) glColorMask(m_ColorMaskR, m_ColorMaskG, m_ColorMaskB, m_ColorMaskA); if (m_HasDepthMask) glDepthMask(m_DepthMask); if (m_HasDepthFunc) glDepthFunc(m_DepthFunc); } void CShaderPass::Unbind() { m_Shader->Unbind(); #if !CONFIG2_GLES if (m_HasAlpha) glDisable(GL_ALPHA_TEST); #endif if (m_HasBlend) glDisable(GL_BLEND); if (m_HasColorMask) glColorMask(1, 1, 1, 1); if (m_HasDepthMask) glDepthMask(1); if (m_HasDepthFunc) glDepthFunc(GL_LEQUAL); } void CShaderPass::AlphaFunc(GLenum func, GLclampf ref) { m_HasAlpha = true; m_AlphaFunc = func; m_AlphaRef = ref; } void CShaderPass::BlendFunc(GLenum src, GLenum dst) { m_HasBlend = true; m_BlendSrc = src; m_BlendDst = dst; } void CShaderPass::ColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { m_HasColorMask = true; m_ColorMaskR = r; m_ColorMaskG = g; m_ColorMaskB = b; m_ColorMaskA = a; } void CShaderPass::DepthMask(GLboolean mask) { m_HasDepthMask = true; m_DepthMask = mask; } void CShaderPass::DepthFunc(GLenum func) { m_HasDepthFunc = true; m_DepthFunc = func; } CShaderTechnique::CShaderTechnique() : m_SortByDistance(false) { } void CShaderTechnique::AddPass(const CShaderPass& pass) { m_Passes.push_back(pass); } int CShaderTechnique::GetNumPasses() const { return m_Passes.size(); } void CShaderTechnique::BeginPass(int pass) { ENSURE(0 <= pass && pass < (int)m_Passes.size()); m_Passes[pass].Bind(); } void CShaderTechnique::EndPass(int pass) { ENSURE(0 <= pass && pass < (int)m_Passes.size()); m_Passes[pass].Unbind(); } const CShaderProgramPtr& CShaderTechnique::GetShader(int pass) const { ENSURE(0 <= pass && pass < (int)m_Passes.size()); return m_Passes[pass].GetShader(); } bool CShaderTechnique::GetSortByDistance() const { return m_SortByDistance; } void CShaderTechnique::SetSortByDistance(bool enable) { m_SortByDistance = enable; } void CShaderTechnique::Reset() { m_SortByDistance = false; m_Passes.clear(); } Index: ps/trunk/source/graphics/ShaderTechnique.h =================================================================== --- ps/trunk/source/graphics/ShaderTechnique.h (revision 15480) +++ ps/trunk/source/graphics/ShaderTechnique.h (revision 15481) @@ -1,114 +1,115 @@ /* 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_SHADERTECHNIQUE #define INCLUDED_SHADERTECHNIQUE -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" +#include "lib/ogl.h" /** * Implements a render pass consisting of various GL state changes and a shader, * used by CShaderTechnique. */ class CShaderPass { public: CShaderPass(); /** * Set the shader program used for rendering with this pass. */ void SetShader(const CShaderProgramPtr& shader) { m_Shader = shader; } // Add various bits of GL state to the pass: void AlphaFunc(GLenum func, GLclampf ref); void BlendFunc(GLenum src, GLenum dst); void ColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a); void DepthMask(GLboolean mask); void DepthFunc(GLenum func); /** * Set up all the GL state that was previously specified on this pass. */ void Bind(); /** * Reset the GL state to the default. */ void Unbind(); const CShaderProgramPtr& GetShader() const { return m_Shader; } private: CShaderProgramPtr m_Shader; bool m_HasAlpha; GLenum m_AlphaFunc; GLclampf m_AlphaRef; bool m_HasBlend; GLenum m_BlendSrc; GLenum m_BlendDst; bool m_HasColorMask; GLboolean m_ColorMaskR; GLboolean m_ColorMaskG; GLboolean m_ColorMaskB; GLboolean m_ColorMaskA; bool m_HasDepthMask; GLboolean m_DepthMask; bool m_HasDepthFunc; GLenum m_DepthFunc; }; /** * Implements a render technique consisting of a sequence of passes. * CShaderManager loads these from shader effect XML files. */ class CShaderTechnique { public: CShaderTechnique(); void AddPass(const CShaderPass& pass); int GetNumPasses() const; void BeginPass(int pass = 0); void EndPass(int pass = 0); const CShaderProgramPtr& GetShader(int pass = 0) const; /** * Whether this technique uses alpha blending that requires objects to be * drawn from furthest to nearest. */ bool GetSortByDistance() const; void SetSortByDistance(bool enable); void Reset(); private: std::vector m_Passes; bool m_SortByDistance; }; typedef shared_ptr CShaderTechniquePtr; #endif // INCLUDED_SHADERTECHNIQUE Index: ps/trunk/source/graphics/TextRenderer.cpp =================================================================== --- ps/trunk/source/graphics/TextRenderer.cpp (revision 15480) +++ ps/trunk/source/graphics/TextRenderer.cpp (revision 15481) @@ -1,312 +1,313 @@ /* 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 "TextRenderer.h" #include "graphics/Font.h" #include "graphics/FontManager.h" +#include "graphics/ShaderProgram.h" #include "lib/ogl.h" #include "ps/CStrIntern.h" #include "renderer/Renderer.h" extern int g_xres, g_yres; CTextRenderer::CTextRenderer(const CShaderProgramPtr& shader) : m_Shader(shader) { ResetTransform(); Color(CColor(1.0f, 1.0f, 1.0f, 1.0f)); Font(str_sans_10); } void CTextRenderer::ResetTransform() { m_Transform.SetIdentity(); m_Transform.Scale(1.0f, -1.f, 1.0f); m_Transform.Translate(0.0f, (float)g_yres, -1000.0f); CMatrix3D proj; proj.SetOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); m_Transform = proj * m_Transform; m_Dirty = true; } CMatrix3D CTextRenderer::GetTransform() { return m_Transform; } void CTextRenderer::SetTransform(const CMatrix3D& transform) { m_Transform = transform; m_Dirty = true; } void CTextRenderer::Translate(float x, float y, float z) { CMatrix3D m; m.SetTranslation(x, y, z); m_Transform = m_Transform * m; m_Dirty = true; } void CTextRenderer::SetClippingRect(const CRect& rect) { m_Clipping = rect; } void CTextRenderer::Color(const CColor& color) { if (m_Color != color) { m_Color = color; m_Dirty = true; } } void CTextRenderer::Color(float r, float g, float b, float a) { Color(CColor(r, g, b, a)); } void CTextRenderer::Font(CStrIntern font) { if (font != m_FontName) { m_FontName = font; m_Font = g_Renderer.GetFontManager().LoadFont(font); m_Dirty = true; } } void CTextRenderer::PrintfAdvance(const wchar_t* fmt, ...) { wchar_t buf[1024] = {0}; va_list args; va_start(args, fmt); int ret = vswprintf(buf, ARRAY_SIZE(buf)-1, fmt, args); va_end(args); if (ret < 0) debug_printf(L"CTextRenderer::Printf vswprintf failed (buffer size exceeded?) - return value %d, errno %d\n", ret, errno); PutAdvance(buf); } void CTextRenderer::PrintfAt(float x, float y, const wchar_t* fmt, ...) { wchar_t buf[1024] = {0}; va_list args; va_start(args, fmt); int ret = vswprintf(buf, ARRAY_SIZE(buf)-1, fmt, args); va_end(args); if (ret < 0) debug_printf(L"CTextRenderer::PrintfAt vswprintf failed (buffer size exceeded?) - return value %d, errno %d\n", ret, errno); Put(x, y, buf); } void CTextRenderer::PutAdvance(const wchar_t* buf) { Put(0.0f, 0.0f, buf); int w, h; m_Font->CalculateStringSize(buf, w, h); Translate((float)w, 0.0f, 0.0f); } void CTextRenderer::Put(float x, float y, const wchar_t* buf) { if (buf[0] == 0) return; // empty string; don't bother storing PutString(x, y, new std::wstring(buf), true); } void CTextRenderer::Put(float x, float y, const std::wstring* buf) { if (buf->empty()) return; // empty string; don't bother storing PutString(x, y, buf, false); } void CTextRenderer::PutString(float x, float y, const std::wstring* buf, bool owned) { if (!m_Font) return; // invalid font; can't render if (m_Clipping != CRect()) { float x0, y0, x1, y1; m_Font->GetGlyphBounds(x0, y0, x1, y1); if (y + y1 < m_Clipping.top) return; if (y + y0 > m_Clipping.bottom) return; } // If any state has changed since the last batch, start a new batch if (m_Dirty) { SBatch batch; batch.chars = 0; batch.transform = m_Transform; batch.color = m_Color; batch.font = m_Font; m_Batches.push_back(batch); m_Dirty = false; } // Push a new run onto the latest batch SBatchRun run; run.x = x; run.y = y; m_Batches.back().runs.push_back(run); m_Batches.back().runs.back().text = buf; m_Batches.back().runs.back().owned = owned; m_Batches.back().chars += buf->size(); } struct t2f_v2i { t2f_v2i() : u(0), v(0), x(0), y(0) { } float u, v; i16 x, y; }; struct SBatchCompare { bool operator()(const CTextRenderer::SBatch& a, const CTextRenderer::SBatch& b) { if (a.font < b.font) return true; if (b.font < a.font) return false; // TODO: is it worth sorting by color/transform too? return false; } }; void CTextRenderer::Render() { std::vector indexes; std::vector vertexes; // Try to merge non-consecutive batches that share the same font/color/transform: // sort the batch list by font, then merge the runs of adjacent compatible batches m_Batches.sort(SBatchCompare()); for (std::list::iterator it = m_Batches.begin(); it != m_Batches.end(); ) { std::list::iterator next = it; ++next; if (next != m_Batches.end() && it->font == next->font && it->color == next->color && it->transform == next->transform) { it->chars += next->chars; it->runs.splice(it->runs.end(), next->runs); m_Batches.erase(next); } else ++it; } for (std::list::iterator it = m_Batches.begin(); it != m_Batches.end(); ++it) { SBatch& batch = *it; const CFont::GlyphMap& glyphs = batch.font->GetGlyphs(); m_Shader->BindTexture(str_tex, batch.font->GetTexture()); m_Shader->Uniform(str_transform, batch.transform); // ALPHA-only textures will have .rgb sampled as 0, so we need to // replace it with white (but not affect RGBA textures) if (batch.font->HasRGB()) m_Shader->Uniform(str_colorAdd, CColor(0.0f, 0.0f, 0.0f, 0.0f)); else m_Shader->Uniform(str_colorAdd, CColor(1.0f, 1.0f, 1.0f, 0.0f)); m_Shader->Uniform(str_colorMul, batch.color); vertexes.resize(batch.chars*4); indexes.resize(batch.chars*6); size_t idx = 0; for (std::list::iterator runit = batch.runs.begin(); runit != batch.runs.end(); ++runit) { SBatchRun& run = *runit; i16 x = run.x; i16 y = run.y; for (size_t i = 0; i < run.text->size(); ++i) { const CFont::GlyphData* g = glyphs.get((*run.text)[i]); if (!g) g = glyphs.get(0xFFFD); // Use the missing glyph symbol if (!g) // Missing the missing glyph symbol - give up continue; vertexes[idx*4].u = g->u1; vertexes[idx*4].v = g->v0; vertexes[idx*4].x = g->x1 + x; vertexes[idx*4].y = g->y0 + y; vertexes[idx*4+1].u = g->u0; vertexes[idx*4+1].v = g->v0; vertexes[idx*4+1].x = g->x0 + x; vertexes[idx*4+1].y = g->y0 + y; vertexes[idx*4+2].u = g->u0; vertexes[idx*4+2].v = g->v1; vertexes[idx*4+2].x = g->x0 + x; vertexes[idx*4+2].y = g->y1 + y; vertexes[idx*4+3].u = g->u1; vertexes[idx*4+3].v = g->v1; vertexes[idx*4+3].x = g->x1 + x; vertexes[idx*4+3].y = g->y1 + y; indexes[idx*6+0] = idx*4+0; indexes[idx*6+1] = idx*4+1; indexes[idx*6+2] = idx*4+2; indexes[idx*6+3] = idx*4+2; indexes[idx*6+4] = idx*4+3; indexes[idx*6+5] = idx*4+0; x += g->xadvance; idx++; } } m_Shader->VertexPointer(2, GL_SHORT, sizeof(t2f_v2i), &vertexes[0].x); m_Shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, sizeof(t2f_v2i), &vertexes[0].u); glDrawElements(GL_TRIANGLES, indexes.size(), GL_UNSIGNED_SHORT, &indexes[0]); } m_Batches.clear(); } Index: ps/trunk/source/graphics/TextRenderer.h =================================================================== --- ps/trunk/source/graphics/TextRenderer.h (revision 15480) +++ ps/trunk/source/graphics/TextRenderer.h (revision 15481) @@ -1,171 +1,171 @@ /* 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_TEXTRENDERER #define INCLUDED_TEXTRENDERER -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" #include "maths/Matrix3D.h" #include "ps/CStrIntern.h" #include "ps/Overlay.h" class CFont; class CTextRenderer { public: CTextRenderer(const CShaderProgramPtr& shader); /** * Reset the text transform to the default, with (0,0) in the top-left of the screen. */ void ResetTransform(); CMatrix3D GetTransform(); void SetTransform(const CMatrix3D& transform); void Translate(float x, float y, float z); /** * Set clipping rectangle, in pre-transform coordinates (i.e. text is clipped against * this rect based purely on the x,y values passed into Put()). Text fully outside the * clipping rectangle may not be rendered. Should be used in conjunction with glScissor * for precise clipping - this is just an optimisation. */ void SetClippingRect(const CRect& rect); /** * Set the color for subsequent print calls. */ void Color(const CColor& color); /** * Set the color for subsequent print calls. */ void Color(float r, float g, float b, float a = 1.0); /** * Set the font for subsequent print calls. */ void Font(CStrIntern font); /** * Print formatted text at (0,0) under the current transform, * and advance the transform by the width of the text. */ void PrintfAdvance(const wchar_t* fmt, ...); /** * Print formatted text at (x,y) under the current transform. * Does not alter the current transform. */ void PrintfAt(float x, float y, const wchar_t* fmt, ...); /** * Print text at (0,0) under the current transform, * and advance the transform by the width of the text. */ void PutAdvance(const wchar_t* buf); /** * Print text at (x,y) under the current transform. * Does not alter the current transform. */ void Put(float x, float y, const wchar_t* buf); /** * Print text at (x,y) under the current transform. * Does not alter the current transform. * @p buf must remain valid until Render() is called. * (This should be used to minimise memory copies when possible.) */ void Put(float x, float y, const std::wstring* buf); /** * Render all of the previously printed text calls. */ void Render(); private: friend struct SBatchCompare; /** * A string (optionally owned by this object, or else pointing to an * externally-owned string) with a position. */ struct SBatchRun { private: SBatchRun& operator=(const SBatchRun&); public: SBatchRun() : text(NULL), owned(false) { } SBatchRun(const SBatchRun& str) : x(str.x), y(str.y), owned(str.owned) { if (owned) text = new std::wstring(*str.text); else text = str.text; } ~SBatchRun() { if (owned) delete text; } float x, y; const std::wstring* text; bool owned; }; /** * A list of SBatchRuns, with a single font/color/transform, * to be rendered in a single GL call. */ struct SBatch { size_t chars; // sum of runs[i].text->size() CMatrix3D transform; CColor color; shared_ptr font; std::list runs; }; void PutString(float x, float y, const std::wstring* buf, bool owned); CShaderProgramPtr m_Shader; CMatrix3D m_Transform; CRect m_Clipping; CColor m_Color; CStrIntern m_FontName; shared_ptr m_Font; bool m_Dirty; std::list m_Batches; }; #endif // INCLUDED_TEXTRENDERER Index: ps/trunk/source/maths/BoundingBoxAligned.cpp =================================================================== --- ps/trunk/source/maths/BoundingBoxAligned.cpp (revision 15480) +++ ps/trunk/source/maths/BoundingBoxAligned.cpp (revision 15481) @@ -1,321 +1,322 @@ /* 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 . */ /* * Axis-aligned bounding box */ #include "precompiled.h" #include "BoundingBoxAligned.h" #include "lib/ogl.h" #include #include "graphics/Frustum.h" +#include "graphics/ShaderProgram.h" #include "maths/BoundingBoxOriented.h" #include "maths/Brush.h" #include "maths/Matrix3D.h" const CBoundingBoxAligned CBoundingBoxAligned::EMPTY = CBoundingBoxAligned(); // initializes to an empty bound /////////////////////////////////////////////////////////////////////////////// // RayIntersect: intersect ray with this bound; return true // if ray hits (and store entry and exit times), or false // otherwise // note: incoming ray direction must be normalised bool CBoundingBoxAligned::RayIntersect(const CVector3D& origin,const CVector3D& dir, float& tmin,float& tmax) const { float t1,t2; float tnear,tfar; if (dir[0]==0) { if (origin[0]m_Data[1][0]) return false; else { tnear=(float) -FLT_MAX; tfar=(float) FLT_MAX; } } else { t1=(m_Data[0][0]-origin[0])/dir[0]; t2=(m_Data[1][0]-origin[0])/dir[0]; if (dir[0]<0) { tnear = t2; tfar = t1; } else { tnear = t1; tfar = t2; } if (tfar<0) return false; } if (dir[1]==0 && (origin[1]m_Data[1][1])) return false; else { t1=(m_Data[0][1]-origin[1])/dir[1]; t2=(m_Data[1][1]-origin[1])/dir[1]; if (dir[1]<0) { if (t2>tnear) tnear = t2; if (t1tnear) tnear = t1; if (t2tfar || tfar<0) return false; } if (dir[2]==0 && (origin[2]m_Data[1][2])) return false; else { t1=(m_Data[0][2]-origin[2])/dir[2]; t2=(m_Data[1][2]-origin[2])/dir[2]; if (dir[2]<0) { if (t2>tnear) tnear = t2; if (t1tnear) tnear = t1; if (t2tfar || tfar<0) return false; } tmin=tnear; tmax=tfar; return true; } /////////////////////////////////////////////////////////////////////////////// // SetEmpty: initialise this bound as empty void CBoundingBoxAligned::SetEmpty() { m_Data[0]=CVector3D( FLT_MAX, FLT_MAX, FLT_MAX); m_Data[1]=CVector3D(-FLT_MAX,-FLT_MAX,-FLT_MAX); } /////////////////////////////////////////////////////////////////////////////// // IsEmpty: tests whether this bound is empty bool CBoundingBoxAligned::IsEmpty() const { return (m_Data[0].X == FLT_MAX && m_Data[0].Y == FLT_MAX && m_Data[0].Z == FLT_MAX && m_Data[1].X == -FLT_MAX && m_Data[1].Y == -FLT_MAX && m_Data[1].Z == -FLT_MAX); } /////////////////////////////////////////////////////////////////////////////// // Transform: transform this bound by given matrix; return transformed bound // in 'result' parameter - slightly modified version of code in Graphic Gems // (can't remember which one it was, though) void CBoundingBoxAligned::Transform(const CMatrix3D& m, CBoundingBoxAligned& result) const { ENSURE(this!=&result); for (int i=0;i<3;++i) { // handle translation result[0][i]=result[1][i]=m(i,3); // Now find the extreme points by considering the product of the // min and max with each component of matrix for(int j=0;j<3;j++) { float a=m(i,j)*m_Data[0][j]; float b=m(i,j)*m_Data[1][j]; if (a data; #define ADD_FACE(x, y, z) \ ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \ ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z); #define ADD_PT(u_, v_, x, y, z) \ STMT(int u = u_; int v = v_; \ data.push_back(u); \ data.push_back(v); \ data.push_back(m_Data[x].X); \ data.push_back(m_Data[y].Y); \ data.push_back(m_Data[z].Z); \ ) ADD_FACE(u, v, 0); ADD_FACE(0, u, v); ADD_FACE(u, 0, 1-v); ADD_FACE(u, 1-v, 1); ADD_FACE(1, u, 1-v); ADD_FACE(u, 1, v); #undef ADD_FACE shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_TRIANGLES, 0, 6*6); } void CBoundingBoxAligned::RenderOutline(CShaderProgramPtr& shader) const { std::vector data; #define ADD_FACE(x, y, z) \ ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); \ ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \ ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); \ ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z); #define ADD_PT(u_, v_, x, y, z) \ STMT(int u = u_; int v = v_; \ data.push_back(u); \ data.push_back(v); \ data.push_back(m_Data[x].X); \ data.push_back(m_Data[y].Y); \ data.push_back(m_Data[z].Z); \ ) ADD_FACE(u, v, 0); ADD_FACE(0, u, v); ADD_FACE(u, 0, 1-v); ADD_FACE(u, 1-v, 1); ADD_FACE(1, u, 1-v); ADD_FACE(u, 1, v); #undef ADD_FACE shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_LINES, 0, 6*8); } Index: ps/trunk/source/maths/Brush.cpp =================================================================== --- ps/trunk/source/maths/Brush.cpp (revision 15480) +++ ps/trunk/source/maths/Brush.cpp (revision 15481) @@ -1,490 +1,491 @@ /* 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 . */ /* * Implementation of CBrush, a class representing a convex object */ #include "precompiled.h" #include "lib/ogl.h" #include #include "Brush.h" #include "BoundingBoxAligned.h" #include "graphics/Frustum.h" +#include "graphics/ShaderProgram.h" /////////////////////////////////////////////////////////////////////////////// // Convert the given bounds into a brush CBrush::CBrush(const CBoundingBoxAligned& bounds) { m_Vertices.resize(8); for(size_t i = 0; i < 8; ++i) { m_Vertices[i][0] = bounds[(i & 1) ? 1 : 0][0]; // X m_Vertices[i][1] = bounds[(i & 2) ? 1 : 0][1]; // Y m_Vertices[i][2] = bounds[(i & 4) ? 1 : 0][2]; // Z } // construct cube face indices, 5 vertex indices per face (start vertex included twice) m_Faces.resize(30); m_Faces[0] = 0; m_Faces[1] = 1; m_Faces[2] = 3; m_Faces[3] = 2; m_Faces[4] = 0; // Z = min m_Faces[5] = 4; m_Faces[6] = 5; m_Faces[7] = 7; m_Faces[8] = 6; m_Faces[9] = 4; // Z = max m_Faces[10] = 0; m_Faces[11] = 2; m_Faces[12] = 6; m_Faces[13] = 4; m_Faces[14] = 0; // X = min m_Faces[15] = 1; m_Faces[16] = 3; m_Faces[17] = 7; m_Faces[18] = 5; m_Faces[19] = 1; // X = max m_Faces[20] = 0; m_Faces[21] = 1; m_Faces[22] = 5; m_Faces[23] = 4; m_Faces[24] = 0; // Y = min m_Faces[25] = 2; m_Faces[26] = 3; m_Faces[27] = 7; m_Faces[28] = 6; m_Faces[29] = 2; // Y = max } /////////////////////////////////////////////////////////////////////////////// // Calculate bounds of this brush void CBrush::Bounds(CBoundingBoxAligned& result) const { result.SetEmpty(); for(size_t i = 0; i < m_Vertices.size(); ++i) result += m_Vertices[i]; } /////////////////////////////////////////////////////////////////////////////// // Cut the brush according to a given plane /// Holds information about what happens to a single vertex in a brush during a slicing operation. struct SliceOpVertexInfo { float planeDist; ///< Signed distance from this vertex to the slicing plane. size_t resIdx; ///< Index of this vertex in the resulting brush (or NO_VERTEX if cut away) }; /// Holds information about a newly introduced vertex on an edge in a brush as the result of a slicing operation. struct SliceOpNewVertexInfo { /// Indices of adjacent edge vertices in original brush size_t edgeIdx1, edgeIdx2; /// Index of newly introduced vertex in resulting brush size_t resIdx; /** * Index into SliceOpInfo.nvInfo; hold the indices of this new vertex's direct neighbours in the slicing plane face, * with no consistent winding direction around the face for either field (e.g., the neighb1 of X can point back to * X with either its neighb1 or neighb2). */ size_t neighbIdx1, neighbIdx2; }; /// Holds support information during a CBrush/CPlane slicing operation. struct SliceOpInfo { CBrush* result; const CBrush* original; /** * Holds information about what happens to each vertex in the original brush after the slice operation. * Same size as m_Vertices of the brush getting sliced. */ std::vector ovInfo; /// Holds information about newly inserted vertices during a slice operation. std::vector nvInfo; /** * Indices into nvInfo; during the execution of the slicing algorithm, holds the previously inserted new vertex on * one of the edges of the face that's currently being evaluated for slice points, or NO_VERTEX if no such vertex * exists. */ size_t thisFaceNewVertexIdx; }; struct CBrush::Helper { /** * Creates a new vertex between the given two vertices (indexed into the original brush). * Returns the index of the new vertex in the resulting brush. */ static size_t SliceNewVertex(SliceOpInfo& sliceInfo, size_t v1, size_t v2); }; size_t CBrush::Helper::SliceNewVertex(SliceOpInfo& sliceOp, size_t edgeIdx1, size_t edgeIdx2) { // check if a new vertex has already been inserted on this edge size_t idx; for(idx = 0; idx < sliceOp.nvInfo.size(); ++idx) { if ((sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx1 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx2) || (sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx2 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx1)) break; } if (idx >= sliceOp.nvInfo.size()) { // no previously inserted new vertex found on this edge; insert a new one SliceOpNewVertexInfo nvi; CVector3D newPos; // interpolate between the two vertices based on their distance from the plane float inv = 1.0 / (sliceOp.ovInfo[edgeIdx1].planeDist - sliceOp.ovInfo[edgeIdx2].planeDist); newPos = sliceOp.original->m_Vertices[edgeIdx2] * ( sliceOp.ovInfo[edgeIdx1].planeDist * inv) + sliceOp.original->m_Vertices[edgeIdx1] * (-sliceOp.ovInfo[edgeIdx2].planeDist * inv); nvi.edgeIdx1 = edgeIdx1; nvi.edgeIdx2 = edgeIdx2; nvi.resIdx = sliceOp.result->m_Vertices.size(); nvi.neighbIdx1 = NO_VERTEX; nvi.neighbIdx2 = NO_VERTEX; sliceOp.result->m_Vertices.push_back(newPos); sliceOp.nvInfo.push_back(nvi); } // at this point, 'idx' is the index into nvInfo of the vertex inserted onto the edge if (sliceOp.thisFaceNewVertexIdx != NO_VERTEX) { // a vertex has been previously inserted onto another edge of this face; link them together as neighbours // (using whichever one of the neighbIdx1 or -2 links is still available) if (sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 == NO_VERTEX) sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 = idx; else sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx2 = idx; if (sliceOp.nvInfo[idx].neighbIdx1 == NO_VERTEX) sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.thisFaceNewVertexIdx; else sliceOp.nvInfo[idx].neighbIdx2 = sliceOp.thisFaceNewVertexIdx; // a plane should slice a face only in two locations, so reset for the next face sliceOp.thisFaceNewVertexIdx = NO_VERTEX; } else { // store the index of the inserted vertex on this edge, so that we can retrieve it when the plane slices // this face again in another edge sliceOp.thisFaceNewVertexIdx = idx; } return sliceOp.nvInfo[idx].resIdx; } void CBrush::Slice(const CPlane& plane, CBrush& result) const { ENSURE(&result != this); SliceOpInfo sliceOp; sliceOp.original = this; sliceOp.result = &result; sliceOp.thisFaceNewVertexIdx = NO_VERTEX; sliceOp.ovInfo.resize(m_Vertices.size()); sliceOp.nvInfo.reserve(m_Vertices.size() / 2); result.m_Vertices.resize(0); // clear any left-overs result.m_Faces.resize(0); result.m_Vertices.reserve(m_Vertices.size() + 2); result.m_Faces.reserve(m_Faces.size() + 5); // Copy vertices that weren't sliced away by the plane to the resulting brush. for(size_t i = 0; i < m_Vertices.size(); ++i) { const CVector3D& vtx = m_Vertices[i]; // current vertex SliceOpVertexInfo& vtxInfo = sliceOp.ovInfo[i]; // slicing operation info about current vertex vtxInfo.planeDist = plane.DistanceToPlane(vtx); if (vtxInfo.planeDist >= 0.0) { // positive side of the plane; not sliced away vtxInfo.resIdx = result.m_Vertices.size(); result.m_Vertices.push_back(vtx); } else { // other side of the plane; sliced away vtxInfo.resIdx = NO_VERTEX; } } // Transfer faces. (Recall how faces are specified; see CBrush::m_Faces). The idea is to examine each face separately, // and see where its edges cross the slicing plane (meaning that exactly one of the vertices of that edge was cut away). // On those edges, new vertices are introduced where the edge intersects the plane, and the resulting brush's m_Faces // array is updated to refer to the newly inserted vertices instead of the original one that got cut away. size_t currentFaceStartIdx = NO_VERTEX; // index of the first vertex of the current face in the original brush size_t resultFaceStartIdx = NO_VERTEX; // index of the first vertex of the current face in the resulting brush for(size_t i = 0; i < m_Faces.size(); ++i) { if (currentFaceStartIdx == NO_VERTEX) { // starting a new face ENSURE(sliceOp.thisFaceNewVertexIdx == NO_VERTEX); currentFaceStartIdx = m_Faces[i]; resultFaceStartIdx = result.m_Faces.size(); continue; } size_t prevIdx = m_Faces[i-1]; // index of previous vertex in this face list size_t curIdx = m_Faces[i]; // index of current vertex in this face list if (sliceOp.ovInfo[prevIdx].resIdx == NO_VERTEX) { // previous face vertex got sliced away by the plane; see if the edge (prev,current) crosses the slicing plane if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX) { // re-entering the front side of the plane; insert vertex on intersection of plane and (prev,current) edge result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx)); result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx); } } else { // previous face vertex didn't get sliced away; see if the edge (prev,current) crosses the slicing plane if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX) { // perfectly normal edge; doesn't cross the plane result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx); } else { // leaving the front side of the plane; insert vertex on intersection of plane and edge (prev, current) result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx)); } } // if we're back at the first vertex of the current face, then we've completed the face if (curIdx == currentFaceStartIdx) { // close the index loop if (result.m_Faces.size() > resultFaceStartIdx) result.m_Faces.push_back(result.m_Faces[resultFaceStartIdx]); currentFaceStartIdx = NO_VERTEX; // start a new face } } ENSURE(currentFaceStartIdx == NO_VERTEX); // Create the face that lies in the slicing plane. Remember, all the intersections of the slicing plane with face // edges of the brush have been stored in sliceOp.nvInfo by the SliceNewVertex function, and refer to their direct // neighbours in the slicing plane face using the neighbIdx1 and neighbIdx2 fields (in no consistent winding order). if (sliceOp.nvInfo.size()) { // push the starting vertex result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx); // At this point, there is no consistent winding order in the neighbX fields, so at each vertex we need to figure // out whether neighb1 or neighb2 points 'onwards' along the face, according to an initially chosen winding direction. // (or, equivalently, which one points back to the one we were just at). At each vertex, we then set neighb1 to be the // one to point onwards, deleting any pointers which we no longer need to complete the trace. size_t idx; size_t prev = 0; idx = sliceOp.nvInfo[0].neighbIdx2; // pick arbitrary starting direction sliceOp.nvInfo[0].neighbIdx2 = NO_VERTEX; while(idx != 0) { ENSURE(idx < sliceOp.nvInfo.size()); if (idx >= sliceOp.nvInfo.size()) break; if (sliceOp.nvInfo[idx].neighbIdx1 == prev) { // neighb1 is pointing the wrong way; we want to normalize it to point onwards in the direction // we initially chose, so swap it with neighb2 and delete neighb2 (no longer needed) sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.nvInfo[idx].neighbIdx2; sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX; } else { // neighb1 isn't pointing to the previous vertex, so neighb2 must be (otherwise a pair of vertices failed to // get paired properly during face/plane slicing). ENSURE(sliceOp.nvInfo[idx].neighbIdx2 == prev); sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX; } result.m_Faces.push_back(sliceOp.nvInfo[idx].resIdx); // move to next vertex; neighb1 has been normalized to point onward prev = idx; idx = sliceOp.nvInfo[idx].neighbIdx1; sliceOp.nvInfo[prev].neighbIdx1 = NO_VERTEX; // no longer needed, we've moved on } // push starting vertex again to close the shape result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx); } } /////////////////////////////////////////////////////////////////////////////// // Intersect with frustum by repeated slicing void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const { ENSURE(&result != this); if (!frustum.GetNumPlanes()) { result = *this; return; } CBrush buf; const CBrush* prev = this; CBrush* next; // Repeatedly slice this brush with each plane of the frustum, alternating between 'result' and 'buf' to // save intermediate results. Set up the starting brush so that the final version always ends up in 'result'. if (frustum.GetNumPlanes() & 1) next = &result; else next = &buf; for(size_t i = 0; i < frustum.GetNumPlanes(); ++i) { prev->Slice(frustum[i], *next); prev = next; if (prev == &buf) next = &result; else next = &buf; } ENSURE(prev == &result); } std::vector CBrush::GetVertices() const { return m_Vertices; } void CBrush::GetFaces(std::vector >& out) const { // split the back-to-back faces into separate face vectors, so that they're in a // user-friendlier format than the back-to-back vertex index array // i.e. split 'x--xy------yz----z' into 'x--x', 'y-------y', 'z---z' size_t faceStartIdx = 0; while (faceStartIdx < m_Faces.size()) { // start new face std::vector singleFace; singleFace.push_back(m_Faces[faceStartIdx]); // step over all the values in the face until we hit the starting value again (which closes the face) size_t j = faceStartIdx + 1; while (j < m_Faces.size() && m_Faces[j] != m_Faces[faceStartIdx]) { singleFace.push_back(m_Faces[j]); j++; } // each face must be closed by the same value that started it ENSURE(m_Faces[faceStartIdx] == m_Faces[j]); singleFace.push_back(m_Faces[j]); out.push_back(singleFace); faceStartIdx = j + 1; } } void CBrush::Render(CShaderProgramPtr& shader) const { std::vector data; std::vector > faces; GetFaces(faces); #define ADD_VERT(a) \ STMT( \ data.push_back(u); \ data.push_back(v); \ data.push_back(m_Vertices[faces[i][a]].X); \ data.push_back(m_Vertices[faces[i][a]].Y); \ data.push_back(m_Vertices[faces[i][a]].Z); \ ) for (size_t i = 0; i < faces.size(); ++i) { // Triangulate into (0,1,2), (0,2,3), ... for (size_t j = 1; j < faces[i].size() - 2; ++j) { float u = 0; float v = 0; ADD_VERT(0); ADD_VERT(j); ADD_VERT(j+1); } } #undef ADD_VERT shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_TRIANGLES, 0, data.size() / 5); } void CBrush::RenderOutline(CShaderProgramPtr& shader) const { std::vector data; std::vector > faces; GetFaces(faces); #define ADD_VERT(a) \ STMT( \ data.push_back(u); \ data.push_back(v); \ data.push_back(m_Vertices[faces[i][a]].X); \ data.push_back(m_Vertices[faces[i][a]].Y); \ data.push_back(m_Vertices[faces[i][a]].Z); \ ) for (size_t i = 0; i < faces.size(); ++i) { for (size_t j = 0; j < faces[i].size() - 1; ++j) { float u = 0; float v = 0; ADD_VERT(j); ADD_VERT(j+1); } } #undef ADD_VERT shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_LINES, 0, data.size() / 5); } Index: ps/trunk/source/maths/Brush.h =================================================================== --- ps/trunk/source/maths/Brush.h (revision 15480) +++ ps/trunk/source/maths/Brush.h (revision 15481) @@ -1,127 +1,127 @@ /* 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 . */ /* * CBrush, a class representing a convex object */ #ifndef maths_brush_h #define maths_brush_h #include "Vector3D.h" -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" class CBoundingBoxAligned; class CFrustum; class CPlane; /** * Class CBrush: Represents a convex object, supports some CSG operations. */ class CBrush { friend class TestBrush; public: CBrush() { } /** * CBrush: Construct a brush from a bounds object. * * @param bounds the CBoundingBoxAligned object to construct the brush from. */ CBrush(const CBoundingBoxAligned& bounds); /** * IsEmpty: Returns whether the brush is empty. * * @return @c true if the brush is empty, @c false otherwise */ bool IsEmpty() const { return m_Vertices.size() == 0; } /** * Bounds: Calculate the axis-aligned bounding box for this brush. * * @param result the resulting bounding box is stored here */ void Bounds(CBoundingBoxAligned& result) const; /** * Slice: Cut the object along the given plane, resulting in a smaller (or even empty) brush representing * the part of the object that lies in front of the plane (as defined by the positive direction of its * normal vector). * * @param plane the slicing plane * @param result the resulting brush is stored here */ void Slice(const CPlane& plane, CBrush& result) const; /** * Intersect: Intersect the brush with the given frustum. * * @param frustum the frustum to intersect with * @param result the resulting brush is stored here */ void Intersect(const CFrustum& frustum, CBrush& result) const; /** * Render the surfaces of the brush as triangles. */ void Render(CShaderProgramPtr& shader) const; /** * Render the outline of the brush as lines. */ void RenderOutline(CShaderProgramPtr& shader) const; private: /** * Returns a copy of the vertices in this brush. Intended for testing purposes; you should not need to use * this method directly. */ std::vector GetVertices() const; /** * Writes a vector of the faces in this brush to @p out. Each face is itself a vector, listing the vertex indices * that make up the face, starting and ending with the same index. Intended for testing purposes; you should not * need to use this method directly. */ void GetFaces(std::vector >& out) const; private: static const size_t NO_VERTEX = ~0u; typedef std::vector Vertices; typedef std::vector FaceIndices; /// Collection of unique vertices that make up this shape. Vertices m_Vertices; /** * Holds the face definitions of this brush. Each face is a sequence of indices into m_Vertices that starts and ends with * the same vertex index, completing a loop through all the vertices that make up the face. This vector holds all the face * sequences back-to-back, thus looking something like 'x---xy--------yz--z' in the general case. */ FaceIndices m_Faces; struct Helper; }; #endif // maths_brush_h Index: ps/trunk/source/renderer/DecalRData.h =================================================================== --- ps/trunk/source/renderer/DecalRData.h (revision 15480) +++ ps/trunk/source/renderer/DecalRData.h (revision 15481) @@ -1,58 +1,59 @@ /* 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_DECALRDATA #define INCLUDED_DECALRDATA #include "graphics/Camera.h" #include "graphics/RenderableObject.h" -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" #include "renderer/VertexArray.h" class CModelDecal; +class CShaderDefines; class CSimulation2; class ShadowMap; class CDecalRData : public CRenderData { public: CDecalRData(CModelDecal* decal, CSimulation2* simulation); ~CDecalRData(); void Update(CSimulation2* simulation); static void RenderDecals(std::vector& decals, const CShaderDefines& context, ShadowMap* shadow, bool isDummyShader=false, const CShaderProgramPtr& dummy=CShaderProgramPtr()); CModelDecal* GetDecal() { return m_Decal; } private: void BuildArrays(); VertexIndexArray m_IndexArray; VertexArray m_Array; VertexArray::Attribute m_Position; VertexArray::Attribute m_DiffuseColor; VertexArray::Attribute m_UV; CModelDecal* m_Decal; CSimulation2* m_Simulation; }; #endif // INCLUDED_DECALRDATA Index: ps/trunk/source/renderer/HWLightingModelRenderer.cpp =================================================================== --- ps/trunk/source/renderer/HWLightingModelRenderer.cpp (revision 15480) +++ ps/trunk/source/renderer/HWLightingModelRenderer.cpp (revision 15481) @@ -1,304 +1,305 @@ /* 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 "lib/bits.h" #include "lib/ogl.h" #include "lib/sysdep/rtl.h" #include "maths/Vector3D.h" #include "graphics/Color.h" #include "graphics/LightEnv.h" #include "graphics/Model.h" #include "graphics/ModelDef.h" +#include "graphics/ShaderProgram.h" #include "renderer/HWLightingModelRenderer.h" #include "renderer/Renderer.h" #include "renderer/RenderModifiers.h" #include "renderer/VertexArray.h" struct ShaderModelDef : public CModelDefRPrivate { /// Indices are the same for all models, so share them VertexIndexArray m_IndexArray; /// Static per-CModelDef vertex array VertexArray m_Array; /// UV coordinates are stored in the static array VertexArray::Attribute m_UV; ShaderModelDef(const CModelDefPtr& mdef); }; ShaderModelDef::ShaderModelDef(const CModelDefPtr& mdef) : m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW) { size_t numVertices = mdef->GetNumVertices(); m_UV.type = GL_FLOAT; m_UV.elems = 2; m_Array.AddAttribute(&m_UV); m_Array.SetNumVertices(numVertices); m_Array.Layout(); VertexArrayIterator UVit = m_UV.GetIterator(); ModelRenderer::BuildUV(mdef, UVit, 0); m_Array.Upload(); m_Array.FreeBackingStore(); m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3); m_IndexArray.Layout(); ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator()); m_IndexArray.Upload(); m_IndexArray.FreeBackingStore(); } struct ShaderModel : public CModelRData { /// Dynamic per-CModel vertex array VertexArray m_Array; /// Position and normals/lighting are recalculated on CPU every frame VertexArray::Attribute m_Position; VertexArray::Attribute m_Normal; // valid iff cpuLighting == false VertexArray::Attribute m_Color; // valid iff cpuLighting == true ShaderModel(const void* key) : CModelRData(key), m_Array(GL_DYNAMIC_DRAW) { } }; struct ShaderModelRendererInternals { bool cpuLighting; /** * Scratch space for normal vector calculation. * Only used if cpuLighting == true. * Space is reserved so we don't have to do frequent reallocations. * Allocated with rtl_AllocateAligned(normalsNumVertices*16, 16) for SSE writes. */ char* normals; size_t normalsNumVertices; /// Previously prepared modeldef ShaderModelDef* shadermodeldef; }; // Construction and Destruction ShaderModelVertexRenderer::ShaderModelVertexRenderer(bool cpuLighting) { m = new ShaderModelRendererInternals; m->cpuLighting = cpuLighting; m->normals = NULL; m->normalsNumVertices = 0; m->shadermodeldef = NULL; } ShaderModelVertexRenderer::~ShaderModelVertexRenderer() { rtl_FreeAligned(m->normals); delete m; } // Build model data (and modeldef data if necessary) CModelRData* ShaderModelVertexRenderer::CreateModelData(const void* key, CModel* model) { CModelDefPtr mdef = model->GetModelDef(); ShaderModelDef* shadermodeldef = (ShaderModelDef*)mdef->GetRenderData(m); if (!shadermodeldef) { shadermodeldef = new ShaderModelDef(mdef); mdef->SetRenderData(m, shadermodeldef); } // Build the per-model data ShaderModel* shadermodel = new ShaderModel(key); if (m->cpuLighting) { // Positions must be 16-byte aligned for SSE writes. // We can pack the color after the position; it will be corrupted by // BuildPositionAndNormals, but that's okay since we'll recompute the // colors afterwards. shadermodel->m_Color.type = GL_UNSIGNED_BYTE; shadermodel->m_Color.elems = 4; shadermodel->m_Array.AddAttribute(&shadermodel->m_Color); shadermodel->m_Position.type = GL_FLOAT; shadermodel->m_Position.elems = 3; shadermodel->m_Array.AddAttribute(&shadermodel->m_Position); } else { // Positions and normals must be 16-byte aligned for SSE writes. shadermodel->m_Position.type = GL_FLOAT; shadermodel->m_Position.elems = 4; shadermodel->m_Array.AddAttribute(&shadermodel->m_Position); shadermodel->m_Normal.type = GL_FLOAT; shadermodel->m_Normal.elems = 4; shadermodel->m_Array.AddAttribute(&shadermodel->m_Normal); } shadermodel->m_Array.SetNumVertices(mdef->GetNumVertices()); shadermodel->m_Array.Layout(); // Verify alignment ENSURE(shadermodel->m_Position.offset % 16 == 0); if (!m->cpuLighting) ENSURE(shadermodel->m_Normal.offset % 16 == 0); ENSURE(shadermodel->m_Array.GetStride() % 16 == 0); return shadermodel; } // Fill in and upload dynamic vertex array void ShaderModelVertexRenderer::UpdateModelData(CModel* model, CModelRData* data, int updateflags) { ShaderModel* shadermodel = static_cast(data); if (!m->cpuLighting && (updateflags & RENDERDATA_UPDATE_VERTICES)) { // build vertices VertexArrayIterator Position = shadermodel->m_Position.GetIterator(); VertexArrayIterator Normal = shadermodel->m_Normal.GetIterator(); ModelRenderer::BuildPositionAndNormals(model, Position, Normal); // upload everything to vertex buffer shadermodel->m_Array.Upload(); } if (m->cpuLighting && (updateflags & (RENDERDATA_UPDATE_VERTICES|RENDERDATA_UPDATE_COLOR))) { CModelDefPtr mdef = model->GetModelDef(); size_t numVertices = mdef->GetNumVertices(); // allocate working space for computing normals if (numVertices > m->normalsNumVertices) { rtl_FreeAligned(m->normals); size_t newSize = round_up_to_pow2(numVertices); m->normals = (char*)rtl_AllocateAligned(newSize*16, 16); m->normalsNumVertices = newSize; } VertexArrayIterator Position = shadermodel->m_Position.GetIterator(); VertexArrayIterator Normal = VertexArrayIterator(m->normals, 16); ModelRenderer::BuildPositionAndNormals(model, Position, Normal); VertexArrayIterator Color = shadermodel->m_Color.GetIterator(); ModelRenderer::BuildColor4ub(model, Normal, Color); // upload everything to vertex buffer shadermodel->m_Array.Upload(); } } // Setup one rendering pass void ShaderModelVertexRenderer::BeginPass(int streamflags) { if (m->cpuLighting) ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_UV1 | STREAM_COLOR))); else ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_UV1 | STREAM_NORMAL))); } // Cleanup one rendering pass void ShaderModelVertexRenderer::EndPass(int UNUSED(streamflags)) { CVertexBuffer::Unbind(); } // Prepare UV coordinates for this modeldef void ShaderModelVertexRenderer::PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def) { m->shadermodeldef = (ShaderModelDef*)def.GetRenderData(m); ENSURE(m->shadermodeldef); if (streamflags & STREAM_UV0) { u8* base = m->shadermodeldef->m_Array.Bind(); GLsizei stride = (GLsizei)m->shadermodeldef->m_Array.GetStride(); shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->shadermodeldef->m_UV.offset); } } // Render one model void ShaderModelVertexRenderer::RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data) { CModelDefPtr mdldef = model->GetModelDef(); ShaderModel* shadermodel = static_cast(data); u8* base = shadermodel->m_Array.Bind(); GLsizei stride = (GLsizei)shadermodel->m_Array.GetStride(); u8* indexBase = m->shadermodeldef->m_IndexArray.Bind(); if (streamflags & STREAM_POS) shader->VertexPointer(3, GL_FLOAT, stride, base + shadermodel->m_Position.offset); if (streamflags & STREAM_NORMAL) shader->NormalPointer(GL_FLOAT, stride, base + shadermodel->m_Normal.offset); if (streamflags & STREAM_COLOR) shader->ColorPointer(3, GL_UNSIGNED_BYTE, stride, base + shadermodel->m_Color.offset); shader->AssertPointersBound(); // render the lot size_t numFaces = mdldef->GetNumFaces(); if (!g_Renderer.m_SkipSubmit) { // Draw with DrawRangeElements where available, since it might be more efficient #if CONFIG2_GLES glDrawElements(GL_TRIANGLES, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase); #else pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, indexBase); #endif } // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris += numFaces; } Index: ps/trunk/source/renderer/ModelVertexRenderer.h =================================================================== --- ps/trunk/source/renderer/ModelVertexRenderer.h (revision 15480) +++ ps/trunk/source/renderer/ModelVertexRenderer.h (revision 15481) @@ -1,160 +1,160 @@ /* 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 . */ /* * Definition of ModelVertexRenderer, the abstract base class for model * vertex transformation implementations. */ #ifndef INCLUDED_MODELVERTEXRENDERER #define INCLUDED_MODELVERTEXRENDERER #include "graphics/MeshManager.h" -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" class CModel; class CModelRData; /** * Class ModelVertexRenderer: Normal ModelRenderer implementations delegate * vertex array management and vertex transformation to an implementation of * ModelVertexRenderer. * * ModelVertexRenderer implementations should be designed so that one * instance of the implementation can be used with more than one ModelRenderer * simultaneously. */ class ModelVertexRenderer { public: virtual ~ModelVertexRenderer() { } /** * CreateModelData: Create internal data for one model. * * ModelRenderer implementations must call this once for every * model that will later be rendered, with @p key set to a value * that's unique to that ModelRenderer. * * ModelVertexRenderer implementations should use this function to * create per-CModel and per-CModelDef data like vertex arrays. * * @param key An opaque pointer to pass to the CModelRData constructor * @param model The model. * * @return A new CModelRData that will be passed into other * ModelVertexRenderer functions whenever the same CModel is used again. */ virtual CModelRData* CreateModelData(const void* key, CModel* model) = 0; /** * UpdateModelData: Calculate per-model data for each frame. * * ModelRenderer implementations must call this once per frame for * every model that is to be rendered in this frame, even if the * value of updateflags will be zero. * This implies that this function will also be called at least once * between a call to CreateModelData and a call to RenderModel. * * ModelVertexRenderer implementations should use this function to * perform software vertex transforms and potentially other per-frame * calculations. * * @param model The model. * @param data Private data as returned by CreateModelData. * @param updateflags Flags indicating which data has changed during * the frame. The value is the same as the value of the model's * CRenderData::m_UpdateFlags. */ virtual void UpdateModelData(CModel* model, CModelRData* data, int updateflags) = 0; /** * BeginPass: Setup global OpenGL state for this ModelVertexRenderer. * * ModelVertexRenderer implementations should prepare "heavy" OpenGL * state such as vertex shader state to prepare for rendering models * and delivering vertex data to the fragment stage as described by * streamflags. * * ModelRenderer implementations must call this function before any * calls to other rendering related functions. * * Recursive calls to BeginPass are not allowed, and every BeginPass * is matched by a corresponding call to EndPass. * * @param streamflags Vertex streams required by the fragment stage. */ virtual void BeginPass(int streamflags) = 0; /** * EndPass: Cleanup OpenGL state set up by BeginPass. * * ModelRenderer implementations must call this function after * rendering related functions for one pass have been called. * * @param streamflags Vertex streams required by the fragment stage. * This equals the streamflags parameter passed on the last call to * BeginPass. */ virtual void EndPass(int streamflags) = 0; /** * PrepareModelDef: Setup OpenGL state for rendering of models that * use the given CModelDef object as base. * * ModelRenderer implementations must call this function before * rendering a sequence of models based on the given CModelDef. * When a ModelRenderer switches back and forth between CModelDefs, * it must call PrepareModelDef for every switch. * * @param streamflags Vertex streams required by the fragment stage. * This equals the streamflags parameter passed on the last call to * BeginPass. * @param def The model definition. */ virtual void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def) = 0; /** * RenderModel: Invoke the rendering commands for the given model. * * ModelRenderer implementations must call this function to perform * the actual rendering. * * preconditions : The most recent call to PrepareModelDef since * BeginPass has been for model->GetModelDef(). * * @param streamflags Vertex streams required by the fragment stage. * This equals the streamflags parameter passed on the last call to * BeginPass. * @param model The model that should be rendered. * @param data Private data for the model as returned by CreateModelData. * * postconditions : Subsequent calls to RenderModel for models * that use the same CModelDef object and the same texture must * succeed. */ virtual void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data) = 0; }; #endif // INCLUDED_MODELVERTEXRENDERER Index: ps/trunk/source/renderer/ParticleRenderer.cpp =================================================================== --- ps/trunk/source/renderer/ParticleRenderer.cpp (revision 15480) +++ ps/trunk/source/renderer/ParticleRenderer.cpp (revision 15481) @@ -1,165 +1,166 @@ /* Copyright (C) 2011 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 "ParticleRenderer.h" #include "graphics/ParticleEmitter.h" +#include "graphics/ShaderDefines.h" #include "graphics/ShaderManager.h" #include "graphics/TextureManager.h" #include "ps/Profile.h" #include "renderer/Renderer.h" struct ParticleRendererInternals { int frameNumber; CShaderTechniquePtr shader; CShaderTechniquePtr shaderSolid; std::vector emitters[CRenderer::CULL_MAX]; }; ParticleRenderer::ParticleRenderer() { m = new ParticleRendererInternals(); m->frameNumber = 0; } ParticleRenderer::~ParticleRenderer() { delete m; } void ParticleRenderer::Submit(int cullGroup, CParticleEmitter* emitter) { m->emitters[cullGroup].push_back(emitter); } void ParticleRenderer::EndFrame() { for (int cullGroup = 0; cullGroup < CRenderer::CULL_MAX; ++cullGroup) m->emitters[cullGroup].clear(); // this should leave the capacity unchanged, which is okay since it // won't be very large or very variable } struct SortEmitterDistance { SortEmitterDistance(const CMatrix3D& m) : worldToCam(m) { } // TODO: if this is slow, we should pre-compute the distance for each emitter bool operator()(CParticleEmitter* const& a, CParticleEmitter* const& b) { CVector3D posa = a->GetPosition(); CVector3D posb = b->GetPosition(); if (posa == posb) return false; float dista = worldToCam.Transform(posa).LengthSquared(); float distb = worldToCam.Transform(posb).LengthSquared(); return distb < dista; } CMatrix3D worldToCam; }; void ParticleRenderer::PrepareForRendering(const CShaderDefines& context) { PROFILE3("prepare particles"); // Can't load the shader in the constructor because it's called before the // renderer initialisation is complete, so load it the first time through here if (!m->shader) { // Only construct the shaders when shaders are supported and enabled; otherwise // RenderParticles will never be called so it's safe to leave the shaders as null if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) { m->shader = g_Renderer.GetShaderManager().LoadEffect(str_particle, context, CShaderDefines()); m->shaderSolid = g_Renderer.GetShaderManager().LoadEffect(str_particle_solid, context, CShaderDefines()); } } ++m->frameNumber; for (int cullGroup = 0; cullGroup < CRenderer::CULL_MAX; ++cullGroup) { PROFILE("update emitters"); for (size_t i = 0; i < m->emitters[cullGroup].size(); ++i) { CParticleEmitter* emitter = m->emitters[cullGroup][i]; emitter->UpdateArrayData(m->frameNumber); } } for (int cullGroup = 0; cullGroup < CRenderer::CULL_MAX; ++cullGroup) { // Sort back-to-front by distance from camera PROFILE("sort emitters"); CMatrix3D worldToCam; g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam); std::stable_sort(m->emitters[cullGroup].begin(), m->emitters[cullGroup].end(), SortEmitterDistance(worldToCam)); } // TODO: should batch by texture here when possible, maybe } void ParticleRenderer::RenderParticles(int cullGroup, bool solidColor) { CShaderTechniquePtr shader = solidColor ? m->shaderSolid : m->shader; std::vector& emitters = m->emitters[cullGroup]; shader->BeginPass(); shader->GetShader()->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); if (!solidColor) glEnable(GL_BLEND); glDepthMask(0); for (size_t i = 0; i < emitters.size(); ++i) { CParticleEmitter* emitter = emitters[i]; emitter->Bind(shader->GetShader()); emitter->RenderArray(shader->GetShader()); } CVertexBuffer::Unbind(); pglBlendEquationEXT(GL_FUNC_ADD); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glDepthMask(1); shader->EndPass(); } void ParticleRenderer::RenderBounds(int cullGroup, CShaderProgramPtr& shader) { std::vector& emitters = m->emitters[cullGroup]; for (size_t i = 0; i < emitters.size(); ++i) { CParticleEmitter* emitter = emitters[i]; CBoundingBoxAligned bounds = emitter->m_Type->CalculateBounds(emitter->GetPosition(), emitter->GetParticleBounds()); bounds.Render(shader); } } Index: ps/trunk/source/renderer/ParticleRenderer.h =================================================================== --- ps/trunk/source/renderer/ParticleRenderer.h (revision 15480) +++ ps/trunk/source/renderer/ParticleRenderer.h (revision 15481) @@ -1,69 +1,69 @@ /* Copyright (C) 2013 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_PARTICLERENDERER #define INCLUDED_PARTICLERENDERER class CParticleEmitter; - +class CShaderDefines; struct ParticleRendererInternals; -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" /** * Render particles. */ class ParticleRenderer { NONCOPYABLE(ParticleRenderer); public: ParticleRenderer(); ~ParticleRenderer(); /** * Add an emitter for rendering in this frame. */ void Submit(int cullGroup, CParticleEmitter* emitter); /** * Prepare internal data structures for rendering. * Must be called after all Submit calls for a frame, and before * any rendering calls. */ void PrepareForRendering(const CShaderDefines& context); /** * Reset the list of submitted overlays. */ void EndFrame(); /** * Render all the submitted particles. */ void RenderParticles(int cullGroup, bool solidColor = false); /** * Render bounding boxes for all the submitted emitters. */ void RenderBounds(int cullGroup, CShaderProgramPtr& shader); private: ParticleRendererInternals* m; }; #endif // INCLUDED_PARTICLERENDERER Index: ps/trunk/source/renderer/PatchRData.h =================================================================== --- ps/trunk/source/renderer/PatchRData.h (revision 15480) +++ ps/trunk/source/renderer/PatchRData.h (revision 15481) @@ -1,174 +1,174 @@ /* 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_PATCHRDATA #define INCLUDED_PATCHRDATA #include #include "graphics/SColor.h" #include "maths/Vector3D.h" #include "graphics/RenderableObject.h" -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" #include "VertexBufferManager.h" class CPatch; class CSimulation2; class CTerrainTextureEntry; class CTextRenderer; class ShadowMap; ////////////////////////////////////////////////////////////////////////////////////////////////// // CPatchRData: class encapsulating logic for rendering terrain patches; holds per // patch data, plus some supporting static functions for batching, etc class CPatchRData : public CRenderData { public: CPatchRData(CPatch* patch, CSimulation2* simulation); ~CPatchRData(); void Update(CSimulation2* simulation); void RenderOutline(); void RenderSides(CShaderProgramPtr& shader); void RenderPriorities(CTextRenderer& textRenderer); void RenderWater(CShaderProgramPtr& shader); static void RenderBases(const std::vector& patches, const CShaderDefines& context, ShadowMap* shadow, bool isDummyShader=false, const CShaderProgramPtr& dummy=CShaderProgramPtr()); static void RenderBlends(const std::vector& patches, const CShaderDefines& context, ShadowMap* shadow, bool isDummyShader=false, const CShaderProgramPtr& dummy=CShaderProgramPtr()); static void RenderStreams(const std::vector& patches, const CShaderProgramPtr& shader, int streamflags); CPatch* GetPatch() { return m_Patch; } static void PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow); const CBoundingBoxAligned& GetWaterBounds() const { return m_WaterBounds; } private: friend struct SBlendStackItem; struct SSplat { SSplat() : m_Texture(0), m_IndexCount(0) {} // texture to apply during splat CTerrainTextureEntry* m_Texture; // offset into the index array for this patch where splat starts size_t m_IndexStart; // number of indices used by splat size_t m_IndexCount; }; struct SBaseVertex { // vertex position CVector3D m_Position; // diffuse color from sunlight SColor4ub m_DiffuseColor; CVector3D m_Normal; }; cassert(sizeof(SBaseVertex) == 28); struct SSideVertex { // vertex position CVector3D m_Position; // add some padding u32 m_Padding[1]; }; cassert(sizeof(SSideVertex) == 16); struct SBlendVertex { // vertex position CVector3D m_Position; // diffuse color from sunlight SColor4ub m_DiffuseColor; // vertex uvs for alpha texture float m_AlphaUVs[2]; CVector3D m_Normal; }; cassert(sizeof(SBlendVertex) == 36); // Mixed Fancy/Simple water vertex description data structure struct SWaterVertex { // vertex position CVector3D m_Position; // (p,q,r, a) where // p*255 + q*-255 + r = depth of water // a = depth-dependent alpha SColor4ub m_DepthData; CVector4D m_WaterData; }; cassert(sizeof(SWaterVertex) == 32); // build this renderdata object void Build(); void AddBlend(std::vector& blendVertices, std::vector& blendIndices, u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture); void BuildBlends(); void BuildIndices(); void BuildVertices(); void BuildSides(); void BuildSide(std::vector& vertices, CPatchSideFlags side); // owner patch CPatch* m_Patch; // vertex buffer handle for side vertices CVertexBuffer::VBChunk* m_VBSides; // vertex buffer handle for base vertices CVertexBuffer::VBChunk* m_VBBase; // vertex buffer handle for base vertex indices CVertexBuffer::VBChunk* m_VBBaseIndices; // vertex buffer handle for blend vertices CVertexBuffer::VBChunk* m_VBBlends; // vertex buffer handle for blend vertex indices CVertexBuffer::VBChunk* m_VBBlendIndices; // list of base splats to apply to this patch std::vector m_Splats; // splats used in blend pass std::vector m_BlendSplats; // boundary of water in this patch CBoundingBoxAligned m_WaterBounds; // Water vertex buffer CVertexBuffer::VBChunk* m_VBWater; // Water indices buffer CVertexBuffer::VBChunk* m_VBWaterIndices; CSimulation2* m_Simulation; // Build water vertices and indices (vertex buffer and data vector) void BuildWater(); // parameter allowing a varying number of triangles per patch for LOD // MUST be an exact divisor of PATCH_SIZE // compiled const for the moment until/if dynamic water LOD is offered // savings would be mostly beneficial for GPU or simple water static const ssize_t water_cell_size = 1; }; #endif Index: ps/trunk/source/renderer/RenderModifiers.h =================================================================== --- ps/trunk/source/renderer/RenderModifiers.h (revision 15480) +++ ps/trunk/source/renderer/RenderModifiers.h (revision 15481) @@ -1,133 +1,134 @@ /* 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 . */ /* * RenderModifiers can affect the fragment stage behaviour of some * ModelRenderers. This file defines some common RenderModifiers in * addition to the base class. * * TODO: See comment in CRendererInternals::Models - we no longer use multiple * subclasses of RenderModifier, so most of the stuff here is unnecessary * abstraction which should probably be cleaned up. */ #ifndef INCLUDED_RENDERMODIFIERS #define INCLUDED_RENDERMODIFIERS #include "ModelRenderer.h" +#include "graphics/ShaderProgram.h" #include "graphics/ShaderTechnique.h" #include "graphics/Texture.h" class CLightEnv; class CMatrix3D; class CModel; class ShadowMap; /** * Class RenderModifier: Some ModelRenderer implementations provide vertex * management behaviour but allow fragment stages to be modified by a plugged in * RenderModifier. * * You should use RenderModifierPtr when referencing RenderModifiers. */ class RenderModifier { public: RenderModifier() { } virtual ~RenderModifier() { } /** * BeginPass: Setup OpenGL for the given rendering pass. * * Must be implemented by derived classes. * * @param pass The current pass number (pass == 0 is the first pass) * * @return The streamflags that indicate which vertex components * are required by the fragment stages (see STREAM_XYZ constants). */ virtual void BeginPass(const CShaderProgramPtr& shader) = 0; /** * PrepareModel: Called before rendering the given model. * * Default behaviour does nothing. * * @param pass The current pass number (pass == 0 is the first pass) * @param model The model that is about to be rendered. */ virtual void PrepareModel(const CShaderProgramPtr& shader, CModel* model) = 0; }; /** * Class LitRenderModifier: Abstract base class for RenderModifiers that apply * a shadow map. * LitRenderModifiers expect the diffuse brightness in the primary color (instead of ambient + diffuse). */ class LitRenderModifier : public RenderModifier { public: LitRenderModifier(); ~LitRenderModifier(); /** * SetShadowMap: Set the shadow map that will be used for rendering. * Must be called by the user of the RenderModifier. * * The shadow map must be non-null and use depth texturing, or subsequent rendering * using this RenderModifier will fail. * * @param shadow the shadow map */ void SetShadowMap(const ShadowMap* shadow); /** * SetLightEnv: Set the light environment that will be used for rendering. * Must be called by the user of the RenderModifier. * * @param lightenv the light environment (must be non-null) */ void SetLightEnv(const CLightEnv* lightenv); const ShadowMap* GetShadowMap() const { return m_Shadow; } const CLightEnv* GetLightEnv() const { return m_LightEnv; } private: const ShadowMap* m_Shadow; const CLightEnv* m_LightEnv; }; /** * A RenderModifier that sets uniforms and textures appropriately for rendering models. */ class ShaderRenderModifier : public LitRenderModifier { public: ShaderRenderModifier(); // Implementation void BeginPass(const CShaderProgramPtr& shader); void PrepareModel(const CShaderProgramPtr& shader, CModel* model); private: CShaderProgram::Binding m_BindingInstancingTransform; CShaderProgram::Binding m_BindingShadingColor; CShaderProgram::Binding m_BindingPlayerColor; }; #endif // INCLUDED_RENDERMODIFIERS Index: ps/trunk/source/renderer/Renderer.h =================================================================== --- ps/trunk/source/renderer/Renderer.h (revision 15480) +++ ps/trunk/source/renderer/Renderer.h (revision 15481) @@ -1,499 +1,499 @@ /* Copyright (C) 2014 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 . */ /* * higher level interface on top of OpenGL to render basic objects: * terrain, models, sprites, particles etc. */ #ifndef INCLUDED_RENDERER #define INCLUDED_RENDERER #include "graphics/Camera.h" #include "graphics/SColor.h" -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" #include "lib/res/handle.h" #include "ps/Singleton.h" #include "graphics/ShaderDefines.h" #include "renderer/PostprocManager.h" #include "renderer/Scene.h" #include "renderer/TimeManager.h" #include "scriptinterface/ScriptInterface.h" // necessary declarations class CFontManager; class CLightEnv; class CMaterial; class CMaterialManager; class CModel; class CParticleManager; class CPatch; class CShaderManager; class CSimulation2; class CTextureManager; class CTimeManager; class RenderPathVertexShader; class SkyManager; class TerrainRenderer; class WaterManager; // rendering modes enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES }; // transparency modes enum ETransparentMode { TRANSPARENT, TRANSPARENT_OPAQUE, TRANSPARENT_BLEND }; // access to sole renderer object #define g_Renderer CRenderer::GetSingleton() struct SScreenRect { GLint x1, y1, x2, y2; }; /////////////////////////////////////////////////////////////////////////////////////////// // CRenderer: base renderer class - primary interface to the rendering engine struct CRendererInternals; class CRenderer : public Singleton, private SceneCollector { public: // various enumerations and renderer related constants enum { NumAlphaMaps=14 }; enum Option { OPT_NOVBO, OPT_SHADOWS, OPT_WATERUGLY, OPT_WATERFANCYEFFECTS, OPT_WATERREALDEPTH, OPT_WATERREFLECTION, OPT_WATERREFRACTION, OPT_SHADOWSONWATER, OPT_SHADOWPCF, OPT_PARTICLES, OPT_GENTANGENTS, OPT_PREFERGLSL, OPT_SILHOUETTES, OPT_SHOWSKY, OPT_SMOOTHLOS, OPT_POSTPROC, OPT_DISPLAYFRUSTUM, }; enum CullGroup { CULL_DEFAULT, CULL_SHADOWS, CULL_REFLECTIONS, CULL_REFRACTIONS, CULL_MAX }; enum RenderPath { // If no rendering path is configured explicitly, the renderer // will choose the path when Open() is called. RP_DEFAULT, // Classic fixed function. RP_FIXED, // Use new ARB/GLSL system RP_SHADER }; // stats class - per frame counts of number of draw calls, poly counts etc struct Stats { // set all stats to zero void Reset() { memset(this, 0, sizeof(*this)); } // number of draw calls per frame - total DrawElements + Begin/End immediate mode loops size_t m_DrawCalls; // number of terrain triangles drawn size_t m_TerrainTris; // number of water triangles drawn size_t m_WaterTris; // number of (non-transparent) model triangles drawn size_t m_ModelTris; // number of overlay triangles drawn size_t m_OverlayTris; // number of splat passes for alphamapping size_t m_BlendSplats; // number of particles size_t m_Particles; }; // renderer options struct Options { bool m_NoVBO; bool m_Shadows; bool m_WaterUgly; bool m_WaterFancyEffects; bool m_WaterRealDepth; bool m_WaterRefraction; bool m_WaterReflection; bool m_WaterShadows; RenderPath m_RenderPath; bool m_ShadowAlphaFix; bool m_ARBProgramShadow; bool m_ShadowPCF; bool m_Particles; bool m_PreferGLSL; bool m_ForceAlphaTest; bool m_GPUSkinning; bool m_Silhouettes; bool m_GenTangents; bool m_SmoothLOS; bool m_ShowSky; bool m_Postproc; bool m_DisplayFrustum; } m_Options; struct Caps { bool m_VBO; bool m_ARBProgram; bool m_ARBProgramShadow; bool m_VertexShader; bool m_FragmentShader; bool m_Shadows; }; public: // constructor, destructor CRenderer(); ~CRenderer(); // open up the renderer: performs any necessary initialisation bool Open(int width,int height); // resize renderer view void Resize(int width,int height); // set/get boolean renderer option void SetOptionBool(enum Option opt, bool value); bool GetOptionBool(enum Option opt) const; void SetRenderPath(RenderPath rp); RenderPath GetRenderPath() const { return m_Options.m_RenderPath; } static CStr GetRenderPathName(RenderPath rp); static RenderPath GetRenderPathByName(const CStr& name); // return view width int GetWidth() const { return m_Width; } // return view height int GetHeight() const { return m_Height; } // return view aspect ratio float GetAspect() const { return float(m_Width)/float(m_Height); } // signal frame start void BeginFrame(); // signal frame end void EndFrame(); /** * Set simulation context for rendering purposes. * Must be called at least once when the game has started and before * frames are rendered. */ void SetSimulation(CSimulation2* simulation); // set color used to clear screen in BeginFrame() void SetClearColor(SColor4ub color); // trigger a reload of shaders (when parameters they depend on have changed) void MakeShadersDirty(); /** * Set up the camera used for rendering the next scene; this includes * setting OpenGL state like viewport, projection and modelview matrices. * * @param viewCamera this camera determines the eye position for rendering * @param cullCamera this camera determines the frustum for culling in the renderer and * for shadow calculations */ void SetSceneCamera(const CCamera& viewCamera, const CCamera& cullCamera); // set the viewport void SetViewport(const SViewPort &); // get the last viewport SViewPort GetViewport(); /** * Render the given scene immediately. * @param scene a Scene object describing what should be rendered. */ void RenderScene(Scene& scene); /** * Return the scene that is currently being rendered. * Only valid when the renderer is in a RenderScene call. */ Scene& GetScene(); /** * Render text overlays on top of the scene. * Assumes the caller has set up the GL environment for orthographic rendering * with texturing and blending. */ void RenderTextOverlays(); // set the current lighting environment; (note: the passed pointer is just copied to a variable within the renderer, // so the lightenv passed must be scoped such that it is not destructed until after the renderer is no longer rendering) void SetLightEnv(CLightEnv* lightenv) { m_LightEnv=lightenv; } // set the mode to render subsequent terrain patches void SetTerrainRenderMode(ERenderMode mode) { m_TerrainRenderMode=mode; } // get the mode to render subsequent terrain patches ERenderMode GetTerrainRenderMode() const { return m_TerrainRenderMode; } // set the mode to render subsequent models void SetModelRenderMode(ERenderMode mode) { m_ModelRenderMode=mode; } // get the mode to render subsequent models ERenderMode GetModelRenderMode() const { return m_ModelRenderMode; } // debugging void SetDisplayTerrainPriorities(bool enabled) { m_DisplayTerrainPriorities = enabled; } // bind a GL texture object to active unit void BindTexture(int unit, unsigned int tex); // load the default set of alphamaps. // return a negative error code if anything along the way fails. // called via delay-load mechanism. int LoadAlphaMaps(); void UnloadAlphaMaps(); // return stats accumulated for current frame Stats& GetStats() { return m_Stats; } // return the current light environment const CLightEnv &GetLightEnv() { return *m_LightEnv; } // return the current view camera const CCamera& GetViewCamera() const { return m_ViewCamera; } // replace the current view camera void SetViewCamera(const CCamera& camera) { m_ViewCamera = camera; } // return the current cull camera const CCamera& GetCullCamera() const { return m_CullCamera; } /** * GetWaterManager: Return the renderer's water manager. * * @return the WaterManager object used by the renderer */ WaterManager* GetWaterManager() { return m_WaterManager; } /** * GetSkyManager: Return the renderer's sky manager. * * @return the SkyManager object used by the renderer */ SkyManager* GetSkyManager() { return m_SkyManager; } CTextureManager& GetTextureManager(); CShaderManager& GetShaderManager(); CParticleManager& GetParticleManager(); TerrainRenderer& GetTerrainRenderer(); CMaterialManager& GetMaterialManager(); CFontManager& GetFontManager(); CShaderDefines GetSystemShaderDefines() { return m_SystemShaderDefines; } CTimeManager& GetTimeManager(); CPostprocManager& GetPostprocManager(); /** * GetCapabilities: Return which OpenGL capabilities are available and enabled. * * @return capabilities structure */ const Caps& GetCapabilities() const { return m_Caps; } static void RegisterScriptFunctions(ScriptInterface& scriptInterface); protected: friend struct CRendererInternals; friend class CVertexBuffer; friend class CPatchRData; friend class CDecalRData; friend class FixedFunctionModelRenderer; friend class ModelRenderer; friend class PolygonSortModelRenderer; friend class SortModelRenderer; friend class RenderPathVertexShader; friend class HWLightingModelRenderer; friend class ShaderModelVertexRenderer; friend class InstancingModelRenderer; friend class ShaderInstancingModelRenderer; friend class TerrainRenderer; //BEGIN: Implementation of SceneCollector void Submit(CPatch* patch); void Submit(SOverlayLine* overlay); void Submit(SOverlayTexturedLine* overlay); void Submit(SOverlaySprite* overlay); void Submit(SOverlayQuad* overlay); void Submit(CModelDecal* decal); void Submit(CParticleEmitter* emitter); void Submit(SOverlaySphere* overlay); void SubmitNonRecursive(CModel* model); //END: Implementation of SceneCollector // render any batched objects void RenderSubmissions(const CBoundingBoxAligned& waterScissor); // patch rendering stuff void RenderPatches(const CShaderDefines& context, int cullGroup); // model rendering stuff void RenderModels(const CShaderDefines& context, int cullGroup); void RenderTransparentModels(const CShaderDefines& context, int cullGroup, ETransparentMode transparentMode, bool disableFaceCulling); void RenderSilhouettes(const CShaderDefines& context); void RenderParticles(int cullGroup); // shadow rendering stuff void RenderShadowMap(const CShaderDefines& context); // render water reflection and refraction textures SScreenRect RenderReflections(const CShaderDefines& context, const CBoundingBoxAligned& scissor); SScreenRect RenderRefractions(const CShaderDefines& context, const CBoundingBoxAligned& scissor); void ComputeReflectionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const; void ComputeRefractionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const; // debugging void DisplayFrustum(); // enable oblique frustum clipping with the given clip plane void SetObliqueFrustumClipping(CCamera& camera, const CVector4D& clipPlane) const; void ReloadShaders(); void RecomputeSystemShaderDefines(); // hotloading static Status ReloadChangedFileCB(void* param, const VfsPath& path); // RENDERER DATA: /// Private data that is not needed by inline functions CRendererInternals* m; // view width int m_Width; // view height int m_Height; // current terrain rendering mode ERenderMode m_TerrainRenderMode; // current model rendering mode ERenderMode m_ModelRenderMode; CShaderDefines m_SystemShaderDefines; SViewPort m_Viewport; /** * m_ViewCamera: determines the eye position for rendering * * @see CGameView::m_ViewCamera */ CCamera m_ViewCamera; /** * m_CullCamera: determines the frustum for culling and shadowmap calculations * * @see CGameView::m_ViewCamera */ CCamera m_CullCamera; // only valid inside a call to RenderScene Scene* m_CurrentScene; int m_CurrentCullGroup; // color used to clear screen in BeginFrame float m_ClearColor[4]; // current lighting setup CLightEnv* m_LightEnv; // ogl_tex handle of composite alpha map (all the alpha maps packed into one texture) Handle m_hCompositeAlphaMap; // coordinates of each (untransformed) alpha map within the packed texture struct { float u0,u1,v0,v1; } m_AlphaMapCoords[NumAlphaMaps]; // card capabilities Caps m_Caps; // build card cap bits void EnumCaps(); // per-frame renderer stats Stats m_Stats; /** * m_WaterManager: the WaterManager object used for water textures and settings * (e.g. water color, water height) */ WaterManager* m_WaterManager; /** * m_SkyManager: the SkyManager object used for sky textures and settings */ SkyManager* m_SkyManager; /** * Enable rendering of terrain tile priority text overlay, for debugging. */ bool m_DisplayTerrainPriorities; public: /** * m_ShadowZBias: Z bias used when rendering shadows into a depth texture. * This can be used to control shadowing artifacts. * * Can be accessed via JS as renderer.shadowZBias * ShadowMap uses this for matrix calculation. */ float m_ShadowZBias; /** * m_ShadowMapSize: Size of shadow map, or 0 for default. Typically slow but useful * for high-quality rendering. Changes don't take effect until the shadow map * is regenerated. * * Can be accessed via JS as renderer.shadowMapSize */ int m_ShadowMapSize; /** * m_SkipSubmit: Disable the actual submission of rendering commands to OpenGL. * All state setup is still performed as usual. * * Can be accessed via JS as renderer.skipSubmit */ bool m_SkipSubmit; }; #endif Index: ps/trunk/source/renderer/TexturedLineRData.h =================================================================== --- ps/trunk/source/renderer/TexturedLineRData.h (revision 15480) +++ ps/trunk/source/renderer/TexturedLineRData.h (revision 15481) @@ -1,95 +1,95 @@ /* 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_TEXTUREDLINERDATA #define INCLUDED_TEXTUREDLINERDATA #include "graphics/Overlay.h" #include "graphics/RenderableObject.h" -#include "graphics/ShaderProgram.h" +#include "graphics/ShaderProgramPtr.h" #include "graphics/TextureManager.h" #include "renderer/VertexBufferManager.h" /** * Rendering data for an STexturedOverlayLine. * * Note that instances may be shared amongst multiple copies of the same STexturedOverlayLine instance. * The reason is that this rendering data is non-copyable, but we do wish to maintain copyability of * SOverlayTexturedLineData to not limit its usage patterns too much (particularly the practice of storing * them into containers). * * For this reason, instead of storing a reverse pointer back to any single SOverlayTexturedLine, the methods * in this class accept references to STexturedOverlayLines to work with. It is up to client code to pass in * SOverlayTexturedLines to all methods that are consistently the same instance or non-modified copies of it. */ class CTexturedLineRData : public CRenderData { // we hold raw pointers to vertex buffer chunks that are handed out by the vertex buffer manager // and can not be safely duplicated by us. NONCOPYABLE(CTexturedLineRData); public: CTexturedLineRData() : m_VB(NULL), m_VBIndices(NULL) { } ~CTexturedLineRData() { if (m_VB) g_VBMan.Release(m_VB); if (m_VBIndices) g_VBMan.Release(m_VBIndices); } void Update(const SOverlayTexturedLine& line); void Render(const SOverlayTexturedLine& line, const CShaderProgramPtr& shader); protected: struct SVertex { SVertex(CVector3D pos, float u, float v) : m_Position(pos) { m_UVs[0] = u; m_UVs[1] = v; } CVector3D m_Position; GLfloat m_UVs[2]; float _padding[3]; // get a pow2 struct size }; cassert(sizeof(SVertex) == 32); /** * Creates a line cap of the specified type @p endCapType at the end of the segment going in direction @p normal, and appends * the vertices to @p verticesOut in GL_TRIANGLES order. * * @param corner1 One of the two butt-end corner points of the line to which the cap should be attached. * @param corner2 One of the two butt-end corner points of the line to which the cap should be attached. * @param normal Normal vector indicating the direction of the segment to which the cap should be attached. * @param endCapType The type of end cap to produce. * @param verticesOut Output vector of vertices for passing to the renderer. * @param indicesOut Output vector of vertex indices for passing to the renderer. */ void CreateLineCap(const SOverlayTexturedLine& line, const CVector3D& corner1, const CVector3D& corner2, const CVector3D& normal, SOverlayTexturedLine::LineCapType endCapType, std::vector& verticesOut, std::vector& indicesOut); /// Small utility function; grabs the centroid of the positions of two vertices inline CVector3D Centroid(const SVertex& v1, const SVertex& v2) { return (v1.m_Position + v2.m_Position) * 0.5; } CVertexBuffer::VBChunk* m_VB; CVertexBuffer::VBChunk* m_VBIndices; }; #endif // INCLUDED_TEXTUREDLINERDATA