From 82ee48482184dd8280bd30849de2cb862f313b47 Mon Sep 17 00:00:00 2001 From: prefect Date: Tue, 25 Oct 2005 01:43:07 +0000 Subject: [PATCH] Massive rewrite/refactoring of the model rendering system. Performance impact: + Player color rendered models are batched like normal models - Transparent sorted models never use the vertex shader path This was SVN commit r3009. --- .../renderer/FixedFunctionModelRenderer.cpp | 264 ++++++++ source/renderer/FixedFunctionModelRenderer.h | 60 ++ source/renderer/HWLightingModelRenderer.cpp | 271 ++++++++ source/renderer/HWLightingModelRenderer.h | 75 +++ source/renderer/ModelDefRData.cpp | 132 ---- source/renderer/ModelDefRData.h | 46 -- source/renderer/ModelRData.cpp | 519 --------------- source/renderer/ModelRData.h | 77 --- source/renderer/ModelRenderer.cpp | 424 ++++++++++++ source/renderer/ModelRenderer.h | 338 ++++++++++ source/renderer/PlayerRenderer.cpp | 332 ++++------ source/renderer/PlayerRenderer.h | 88 ++- source/renderer/RenderModifiers.cpp | 179 +++++ source/renderer/RenderModifiers.h | 151 +++++ source/renderer/Renderer.cpp | 315 +++++---- source/renderer/Renderer.h | 68 +- source/renderer/TransparencyRenderer.cpp | 609 ++++++++++++++---- source/renderer/TransparencyRenderer.h | 120 +++- 18 files changed, 2750 insertions(+), 1318 deletions(-) create mode 100644 source/renderer/FixedFunctionModelRenderer.cpp create mode 100644 source/renderer/FixedFunctionModelRenderer.h create mode 100644 source/renderer/HWLightingModelRenderer.cpp create mode 100644 source/renderer/HWLightingModelRenderer.h delete mode 100644 source/renderer/ModelDefRData.cpp delete mode 100644 source/renderer/ModelDefRData.h delete mode 100755 source/renderer/ModelRData.cpp delete mode 100755 source/renderer/ModelRData.h create mode 100644 source/renderer/ModelRenderer.cpp create mode 100644 source/renderer/ModelRenderer.h create mode 100644 source/renderer/RenderModifiers.cpp create mode 100644 source/renderer/RenderModifiers.h diff --git a/source/renderer/FixedFunctionModelRenderer.cpp b/source/renderer/FixedFunctionModelRenderer.cpp new file mode 100644 index 0000000000..c75e3563d0 --- /dev/null +++ b/source/renderer/FixedFunctionModelRenderer.cpp @@ -0,0 +1,264 @@ +/** + * ========================================================================= + * File : FixedFunctionModelRenderer.cpp + * Project : Pyrogenesis + * Description : Implementation of FixedFunctionModelRenderer + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#include "precompiled.h" + +#include "ogl.h" +#include "Vector3D.h" +#include "Vector4D.h" + +#include "ps/CLogger.h" + +#include "graphics/Color.h" +#include "graphics/Model.h" +#include "graphics/ModelDef.h" + +#include "renderer/FixedFunctionModelRenderer.h" +#include "renderer/Renderer.h" +#include "renderer/RenderModifiers.h" +#include "renderer/VertexArray.h" + + +#define LOG_CATEGORY "graphics" + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// FixedFunctionModelRenderer implementation + +struct FFModelDef : public CModelDefRPrivate +{ + /// Indices are the same for all models, so share them + u16* m_Indices; + + /// Static per-CModelDef vertex array + VertexArray m_Array; + + /// UV coordinates are stored in the static array + VertexArray::Attribute m_UV; + + FFModelDef(CModelDefPtr mdef); + ~FFModelDef() { delete m_Indices; } +}; + + +FFModelDef::FFModelDef(CModelDefPtr mdef) + : m_Array(false) +{ + 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); + + m_Array.Upload(); + m_Array.FreeBackingStore(); + + m_Indices = new u16[mdef->GetNumFaces()*3]; + ModelRenderer::BuildIndices(mdef, m_Indices); +} + + +struct FFModel +{ + /// Dynamic per-CModel vertex array + VertexArray m_Array; + + /// Position and lighting are recalculated on CPU every frame + VertexArray::Attribute m_Position; + VertexArray::Attribute m_Color; + + FFModel() : m_Array(true) { } +}; + + +struct FixedFunctionModelRendererInternals +{ + /// Transformed vertex normals - required for recalculating lighting on skinned models + std::vector normals; + + /// Currently used RenderModifier + RenderModifierPtr modifier; + + /// Current rendering pass + uint pass; + + /// Streamflags required in this pass + uint streamflags; + + /// Previously prepared modeldef + FFModelDef* ffmodeldef; +}; + + +// Construction and Destruction +FixedFunctionModelRenderer::FixedFunctionModelRenderer() +{ + m = new FixedFunctionModelRendererInternals; +} + +FixedFunctionModelRenderer::~FixedFunctionModelRenderer() +{ + delete m; +} + +// Render submitted models. +void FixedFunctionModelRenderer::Render(RenderModifierPtr modifier, u32 flags) +{ + if (!BatchModelRenderer::HaveSubmissions()) + return; + + // Save for later + m->modifier = modifier; + + glEnableClientState(GL_VERTEX_ARRAY); + + m->pass = 0; + do + { + m->streamflags = modifier->BeginPass(m->pass); + + if (m->streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if (m->streamflags & STREAM_COLOR) glEnableClientState(GL_COLOR_ARRAY); + + RenderAllModels(flags); + + if (m->streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (m->streamflags & STREAM_COLOR) glDisableClientState(GL_COLOR_ARRAY); + } while(!modifier->EndPass(m->pass++)); + + glDisableClientState(GL_VERTEX_ARRAY); +} + + +// Build model data (and modeldef data if necessary) +void* FixedFunctionModelRenderer::CreateModelData(CModel* model) +{ + CModelDefPtr mdef = model->GetModelDef(); + FFModelDef* ffmodeldef = (FFModelDef*)mdef->GetRenderData(m); + + if (!ffmodeldef) + { + ffmodeldef = new FFModelDef(mdef); + mdef->SetRenderData(m, ffmodeldef); + } + + // Build the per-model data + FFModel* ffmodel = new FFModel; + + ffmodel->m_Position.type = GL_FLOAT; + ffmodel->m_Position.elems = 3; + ffmodel->m_Array.AddAttribute(&ffmodel->m_Position); + + ffmodel->m_Color.type = GL_UNSIGNED_BYTE; + ffmodel->m_Color.elems = 4; + ffmodel->m_Array.AddAttribute(&ffmodel->m_Color); + + ffmodel->m_Array.SetNumVertices(mdef->GetNumVertices()); + ffmodel->m_Array.Layout(); + + return ffmodel; +} + + +// Fill in and upload dynamic vertex array +void FixedFunctionModelRenderer::UpdateModelData(CModel* model, void* data, u32 updateflags) +{ + FFModel* ffmodel = (FFModel*)data; + + if (updateflags & RENDERDATA_UPDATE_VERTICES) + { + CModelDefPtr mdef = model->GetModelDef(); + size_t numVertices = mdef->GetNumVertices(); + + // build vertices + if (m->normals.size() < numVertices) + m->normals.resize(numVertices); + + VertexArrayIterator Position = ffmodel->m_Position.GetIterator(); + VertexArrayIterator Normal = VertexArrayIterator((char*)&m->normals[0], sizeof(CVector3D)); + + BuildPositionAndNormals(model, Position, Normal); + + VertexArrayIterator Color = ffmodel->m_Color.GetIterator(); + + BuildColor4ub(model, Normal, Color); + + // upload everything to vertex buffer + ffmodel->m_Array.Upload(); + } +} + + +// Cleanup per-model data. +// Note that per-CModelDef data is deleted by the CModelDef itself. +void FixedFunctionModelRenderer::DestroyModelData(CModel* model, void* data) +{ + FFModel* ffmodel = (FFModel*)data; + + delete ffmodel; +} + + +// Prepare UV coordinates for this modeldef +void FixedFunctionModelRenderer::PrepareModelDef(CModelDefPtr def) +{ + m->ffmodeldef = (FFModelDef*)def->GetRenderData(m); + + debug_assert(m->ffmodeldef); + + if (m->streamflags & STREAM_UV0) + { + u8* base = m->ffmodeldef->m_Array.Bind(); + GLsizei stride = (GLsizei)m->ffmodeldef->m_Array.GetStride(); + + glTexCoordPointer(2, GL_FLOAT, stride, base + m->ffmodeldef->m_UV.offset); + } +} + + +// Call the modifier to prepare the given texture +void FixedFunctionModelRenderer::PrepareTexture(CTexture* texture) +{ + m->modifier->PrepareTexture(m->pass, texture); +} + + +// Render one model +void FixedFunctionModelRenderer::RenderModel(CModel* model, void* data) +{ + m->modifier->PrepareModel(m->pass, model); + + CModelDefPtr mdldef = model->GetModelDef(); + FFModel* ffmodel = (FFModel*)data; + + u8* base = ffmodel->m_Array.Bind(); + GLsizei stride = (GLsizei)ffmodel->m_Array.GetStride(); + + glVertexPointer(3, GL_FLOAT, stride, base + ffmodel->m_Position.offset); + if (m->streamflags & STREAM_COLOR) + glColorPointer(3, ffmodel->m_Color.type, stride, base + ffmodel->m_Color.offset); + + // render the lot + size_t numFaces = mdldef->GetNumFaces(); + glDrawRangeElementsEXT(GL_TRIANGLES, 0, mdldef->GetNumVertices(), + numFaces*3, GL_UNSIGNED_SHORT, m->ffmodeldef->m_Indices); + + // bump stats + g_Renderer.m_Stats.m_DrawCalls++; + g_Renderer.m_Stats.m_ModelTris += numFaces; +} + diff --git a/source/renderer/FixedFunctionModelRenderer.h b/source/renderer/FixedFunctionModelRenderer.h new file mode 100644 index 0000000000..0d6ae2e984 --- /dev/null +++ b/source/renderer/FixedFunctionModelRenderer.h @@ -0,0 +1,60 @@ +/** + * ========================================================================= + * File : FixedFunctionModelRenderer.h + * Project : Pyrogenesis + * Description : BatchModelRenderer that uses only fixed function pipeline + * : to render animated models, capable of using RenderModifier. + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#ifndef FIXEDFUNCTIONMODELRENDERER_H +#define FIXEDFUNCTIONMODELRENDERER_H + +#include "renderer/ModelRenderer.h" + +struct FixedFunctionModelRendererInternals; + +/** + * Class FixedFunctionModelRenderer: Render animated models using only + * OpenGL fixed function. + * + * Use the RenderModifier to enable normal model rendering as well + * as player colour rendering using this model renderer. + */ +class FixedFunctionModelRenderer : public BatchModelRenderer +{ +public: + FixedFunctionModelRenderer(); + ~FixedFunctionModelRenderer(); + + /** + * Render: Render submitted models using the given RenderModifier + * for fragment stages. + * + * preconditions : PrepareModels must be called before Render. + * + * @param modifier The RenderModifier that specifies the fragment stage. + * @param flags If flags is 0, all submitted models are rendered. + * If flags is non-zero, only models that contain flags in their + * CModel::GetFlags() are rendered. + */ + void Render(RenderModifierPtr modifier, u32 flags); + +protected: + // Implementations + void* CreateModelData(CModel* model); + void UpdateModelData(CModel* model, void* data, u32 updateflags); + void DestroyModelData(CModel* model, void* data); + + void PrepareModelDef(CModelDefPtr def); + void PrepareTexture(CTexture* texture); + void RenderModel(CModel* model, void* data); + +private: + FixedFunctionModelRendererInternals* m; +}; + + +#endif // FIXEDFUNCTIONMODELRENDERER_H diff --git a/source/renderer/HWLightingModelRenderer.cpp b/source/renderer/HWLightingModelRenderer.cpp new file mode 100644 index 0000000000..1bd87505df --- /dev/null +++ b/source/renderer/HWLightingModelRenderer.cpp @@ -0,0 +1,271 @@ +/** + * ========================================================================= + * File : HWLightingModelRenderer.cpp + * Project : Pyrogenesis + * Description : Implementation of HWLightingModelRenderer + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#include "precompiled.h" + +#include "ogl.h" +#include "lib/res/graphics/ogl_shader.h" +#include "Vector3D.h" +#include "Vector4D.h" + +#include "ps/CLogger.h" + +#include "graphics/Color.h" +#include "graphics/Model.h" +#include "graphics/ModelDef.h" + +#include "renderer/HWLightingModelRenderer.h" +#include "renderer/Renderer.h" +#include "renderer/RenderModifiers.h" +#include "renderer/RenderPathVertexShader.h" +#include "renderer/SHCoeffs.h" +#include "renderer/VertexArray.h" + + +#define LOG_CATEGORY "graphics" + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// HWLightingModelRenderer implementation + +struct HWLModelDef : public CModelDefRPrivate +{ + /// Indices are the same for all models, so share them + u16* m_Indices; + + + HWLModelDef(CModelDefPtr mdef); + ~HWLModelDef() { delete m_Indices; } +}; + + +HWLModelDef::HWLModelDef(CModelDefPtr mdef) +{ + m_Indices = new u16[mdef->GetNumFaces()*3]; + ModelRenderer::BuildIndices(mdef, m_Indices); +} + + +struct HWLModel +{ + /// Dynamic per-CModel vertex array + VertexArray m_Array; + + /// Position and normals are recalculated on CPU every frame + VertexArray::Attribute m_Position; + VertexArray::Attribute m_Normal; + + /// UV is stored per-CModel in order to avoid space wastage due to alignment + VertexArray::Attribute m_UV; + + HWLModel() : m_Array(true) { } +}; + + +struct HWLightingModelRendererInternals +{ + /// Currently used RenderModifier + RenderModifierPtr modifier; + + /// Current rendering pass + uint pass; + + /// Streamflags required in this pass + uint streamflags; + + /// Previously prepared modeldef + HWLModelDef* hwlmodeldef; +}; + + +// Construction and Destruction +HWLightingModelRenderer::HWLightingModelRenderer() +{ + m = new HWLightingModelRendererInternals; +} + +HWLightingModelRenderer::~HWLightingModelRenderer() +{ + delete m; +} + + +// Check hardware support +bool HWLightingModelRenderer::IsAvailable() +{ + return g_Renderer.m_VertexShader != 0; +} + + +// Render submitted models. +void HWLightingModelRenderer::Render(RenderModifierPtr modifier, u32 flags) +{ + if (!HaveSubmissions()) + return; + + // Save for later + m->modifier = modifier; + + glEnableClientState(GL_VERTEX_ARRAY); + + m->pass = 0; + do + { + m->streamflags = modifier->BeginPass(m->pass); + + if (m->streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if (m->streamflags & STREAM_COLOR) + { + const RGBColor* coeffs = g_Renderer.m_SHCoeffsUnits.GetCoefficients(); + int idx; + + ogl_program_use(g_Renderer.m_VertexShader->m_ModelLight); + idx = g_Renderer.m_VertexShader->m_ModelLight_SHCoefficients; + glUniform3fvARB(idx, 9, (float*)coeffs); + + glEnableClientState(GL_NORMAL_ARRAY); + } + + RenderAllModels(flags); + + if (m->streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (m->streamflags & STREAM_COLOR) + { + glUseProgramObjectARB(0); + + glDisableClientState(GL_NORMAL_ARRAY); + } + } while(!modifier->EndPass(m->pass++)); + + glDisableClientState(GL_VERTEX_ARRAY); +} + + +// Build model data (and modeldef data if necessary) +void* HWLightingModelRenderer::CreateModelData(CModel* model) +{ + CModelDefPtr mdef = model->GetModelDef(); + HWLModelDef* hwlmodeldef = (HWLModelDef*)mdef->GetRenderData(m); + + if (!hwlmodeldef) + { + hwlmodeldef = new HWLModelDef(mdef); + mdef->SetRenderData(m, hwlmodeldef); + } + + // Build the per-model data + HWLModel* hwlmodel = new HWLModel; + + hwlmodel->m_Position.type = GL_FLOAT; + hwlmodel->m_Position.elems = 3; + hwlmodel->m_Array.AddAttribute(&hwlmodel->m_Position); + + hwlmodel->m_UV.type = GL_FLOAT; + hwlmodel->m_UV.elems = 2; + hwlmodel->m_Array.AddAttribute(&hwlmodel->m_UV); + + hwlmodel->m_Normal.type = GL_FLOAT; + hwlmodel->m_Normal.elems = 3; + hwlmodel->m_Array.AddAttribute(&hwlmodel->m_Normal); + + hwlmodel->m_Array.SetNumVertices(mdef->GetNumVertices()); + hwlmodel->m_Array.Layout(); + + // Fill in static UV coordinates + VertexArrayIterator UVit = hwlmodel->m_UV.GetIterator(); + + ModelRenderer::BuildUV(mdef, UVit); + + return hwlmodel; +} + + +// Fill in and upload dynamic vertex array +void HWLightingModelRenderer::UpdateModelData(CModel* model, void* data, u32 updateflags) +{ + HWLModel* hwlmodel = (HWLModel*)data; + + if (updateflags & RENDERDATA_UPDATE_VERTICES) + { + CModelDefPtr mdef = model->GetModelDef(); + size_t numVertices = mdef->GetNumVertices(); + + // build vertices + VertexArrayIterator Position = hwlmodel->m_Position.GetIterator(); + VertexArrayIterator Normal = hwlmodel->m_Normal.GetIterator(); + + BuildPositionAndNormals(model, Position, Normal); + + // upload everything to vertex buffer + hwlmodel->m_Array.Upload(); + } +} + + +// Cleanup per-model data. +// Note that per-CModelDef data is deleted by the CModelDef itself. +void HWLightingModelRenderer::DestroyModelData(CModel* model, void* data) +{ + HWLModel* hwlmodel = (HWLModel*)data; + + delete hwlmodel; +} + + +// Prepare UV coordinates for this modeldef +void HWLightingModelRenderer::PrepareModelDef(CModelDefPtr def) +{ + m->hwlmodeldef = (HWLModelDef*)def->GetRenderData(m); + + debug_assert(m->hwlmodeldef); +} + + +// Call the modifier to prepare the given texture +void HWLightingModelRenderer::PrepareTexture(CTexture* texture) +{ + m->modifier->PrepareTexture(m->pass, texture); +} + + +// Render one model +void HWLightingModelRenderer::RenderModel(CModel* model, void* data) +{ + m->modifier->PrepareModel(m->pass, model); + + CModelDefPtr mdldef = model->GetModelDef(); + HWLModel* hwlmodel = (HWLModel*)data; + + u8* base = hwlmodel->m_Array.Bind(); + GLsizei stride = (GLsizei)hwlmodel->m_Array.GetStride(); + + glVertexPointer(3, GL_FLOAT, stride, base + hwlmodel->m_Position.offset); + if (m->streamflags & STREAM_COLOR) + { + CColor sc = model->GetShadingColor(); + glColor3f(sc.r, sc.g, sc.b); + + glNormalPointer(GL_FLOAT, stride, base + hwlmodel->m_Normal.offset); + } + if (m->streamflags & STREAM_UV0) + { + glTexCoordPointer(2, GL_FLOAT, stride, base + hwlmodel->m_UV.offset); + } + + // render the lot + size_t numFaces = mdldef->GetNumFaces(); + glDrawRangeElementsEXT(GL_TRIANGLES, 0, mdldef->GetNumVertices(), + numFaces*3, GL_UNSIGNED_SHORT, m->hwlmodeldef->m_Indices); + + // bump stats + g_Renderer.m_Stats.m_DrawCalls++; + g_Renderer.m_Stats.m_ModelTris += numFaces; +} + diff --git a/source/renderer/HWLightingModelRenderer.h b/source/renderer/HWLightingModelRenderer.h new file mode 100644 index 0000000000..ced8106e90 --- /dev/null +++ b/source/renderer/HWLightingModelRenderer.h @@ -0,0 +1,75 @@ +/** + * ========================================================================= + * File : HWLightingModelRenderer.h + * Project : Pyrogenesis + * Description : BatchModelRenderer that transforms models on the CPU + * : but performs lighting in a vertex shader. + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#ifndef HWLIGHTINGMODELRENDERER_H +#define HWLIGHTINGMODELRENDERER_H + +#include "renderer/ModelRenderer.h" + +struct HWLightingModelRendererInternals; + +/** + * Class HWLightingModelRenderer: Render animated models using only + * OpenGL fixed function. + * + * Use the RenderModifier to enable normal model rendering as well + * as player colour rendering using this model renderer. + * + * @note You should verify hardware capabilities using IsAvailable + * before creating this model renderer. + */ +class HWLightingModelRenderer : public BatchModelRenderer +{ +public: + HWLightingModelRenderer(); + ~HWLightingModelRenderer(); + + /** + * Render: Render submitted models using the given RenderModifier + * for fragment stages. + * + * preconditions : PrepareModels must be called before Render. + * + * @param modifier The RenderModifier that specifies the fragment stage. + * @param flags If flags is 0, all submitted models are rendered. + * If flags is non-zero, only models that contain flags in their + * CModel::GetFlags() are rendered. + */ + void Render(RenderModifierPtr modifier, u32 flags); + + /** + * IsAvailable: Determines whether this model renderer can be used + * given the OpenGL implementation specific limits. + * + * @note Do not attempt to construct a HWLightingModelRenderer object + * when IsAvailable returns false. + * + * @return true if the OpenGL implementation can support this + * model renderer. + */ + static bool IsAvailable(); + +protected: + // Implementations + void* CreateModelData(CModel* model); + void UpdateModelData(CModel* model, void* data, u32 updateflags); + void DestroyModelData(CModel* model, void* data); + + void PrepareModelDef(CModelDefPtr def); + void PrepareTexture(CTexture* texture); + void RenderModel(CModel* model, void* data); + +private: + HWLightingModelRendererInternals* m; +}; + + +#endif // HWLIGHTINGMODELRENDERER_H diff --git a/source/renderer/ModelDefRData.cpp b/source/renderer/ModelDefRData.cpp deleted file mode 100644 index f6e52e469e..0000000000 --- a/source/renderer/ModelDefRData.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "precompiled.h" - -#include "ogl.h" -#include "Vector3D.h" -#include "Vector4D.h" -#include "ps/CLogger.h" -#include "graphics/Color.h" -#include "graphics/Model.h" -#include "renderer/ModelRData.h" -#include "renderer/ModelDefRData.h" -#include "renderer/Renderer.h" - - -#define LOG_CATEGORY "graphics" - - -// Shared list of all submitted models this frame, sorted by CModelDef -CModelDefRData* CModelDefRData::m_Submissions = 0; - -CModelDefRData::CModelDefRData(CModelDef* mdef) - : m_ModelDef(mdef), m_Array(false) -{ - m_SubmissionNext = 0; - m_SubmissionSlots = 0; - - Build(); -} - -CModelDefRData::~CModelDefRData() -{ -} - - -// Create and upload shared vertex arrays -// -// UV is only shared for fixed function at the moment. -// Rationale: The non-shared vertex structure has enough space left for UV -// coordinates due to alignment, so we slightly *reduce* vertex buffer space by -// not sharing UV. -void CModelDefRData::Build() -{ - if (g_Renderer.GetRenderPath() == CRenderer::RP_FIXED) - { - size_t numVertices = m_ModelDef->GetNumVertices(); - - m_UV.type = GL_FLOAT; - m_UV.elems = 2; - m_Array.AddAttribute(&m_UV); - - m_Array.SetNumVertices(numVertices); - m_Array.Layout(); - - SModelVertex* vertices = m_ModelDef->GetVertices(); - VertexArrayIterator UVit = m_UV.GetIterator(); - - for (uint j=0; j < numVertices; ++j, ++UVit) { - (*UVit)[0] = vertices[j].m_U; - (*UVit)[1] = 1.0-vertices[j].m_V; - } - - m_Array.Upload(); - m_Array.FreeBackingStore(); - } -} - - -// Setup shared vertex arrays as needed. -void CModelDefRData::PrepareStream(uint streamflags) -{ - if (!(streamflags & STREAM_UV0) || !m_UV.type) - return; - - u8* base = m_Array.Bind(); - GLsizei stride = (GLsizei)m_Array.GetStride(); - - glTexCoordPointer(2, GL_FLOAT, stride, base + m_UV.offset); -} - - -// Submit one model. -// Models are sorted into a hash-table to avoid ping-ponging between -// different render states later on. -void CModelDefRData::Submit(CModelRData* data) -{ - debug_assert(data->GetModel()->GetModelDef()->GetRenderData() == this); - - if (!m_SubmissionSlots) - { - m_SubmissionNext = m_Submissions; - m_Submissions = this; - } - - Handle htex = data->GetModel()->GetTexture()->GetHandle(); - uint idx; - - for(idx = 0; idx < m_SubmissionSlots; ++idx) - { - CModelRData* in = m_SubmissionModels[idx]; - - if (in->GetModel()->GetTexture()->GetHandle() == htex) - break; - } - - if (idx >= m_SubmissionSlots) - { - ++m_SubmissionSlots; - if (m_SubmissionSlots > m_SubmissionModels.size()) - { - m_SubmissionModels.push_back(0); - debug_assert(m_SubmissionModels.size() == m_SubmissionSlots); - } - m_SubmissionModels[idx] = 0; - } - - data->m_SubmissionNext = m_SubmissionModels[idx]; - m_SubmissionModels[idx] = data; -} - - -// Clear all submissions for this CModelDef -// Do not shrink the submissions array immediately to avoid unnecessary -// allocate/free roundtrips through memory management. -void CModelDefRData::ClearSubmissions() -{ - static uint mostslots = 1; - if (m_SubmissionSlots > mostslots) - { - mostslots = m_SubmissionSlots; - debug_printf("CModelDefRData: SubmissionSlots maximum: %u\n", mostslots); - } - m_SubmissionSlots = 0; -} diff --git a/source/renderer/ModelDefRData.h b/source/renderer/ModelDefRData.h deleted file mode 100644 index dd1c50ea55..0000000000 --- a/source/renderer/ModelDefRData.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef __MODELDEFRDATA_H__ -#define __MODELDEFRDATA_H__ - -#include "graphics/ModelDef.h" -#include "renderer/ModelRData.h" -#include "renderer/VertexArray.h" - - -class CModelRData; - -// Maintain rendering data that can be shared across models -// that are based on the same model definition. -class CModelDefRData : public CSharedRenderData -{ - friend class CModelRData; - -public: - CModelDefRData(CModelDef* mdef); - virtual ~CModelDefRData(); - - // Setup shared vertex arrays required by streamflags - void PrepareStream(uint streamflags); - - // Submit one model - void Submit(CModelRData* data); - // Clear all submissions for this CModelDef - void ClearSubmissions(); - -private: - // Build and upload vertex arrays - void Build(); - -private: - CModelDef* m_ModelDef; - - VertexArray m_Array; - VertexArray::Attribute m_UV; // only used in RP_FIXED - - CModelDefRData* m_SubmissionNext; - uint m_SubmissionSlots; - std::vector m_SubmissionModels; - static CModelDefRData* m_Submissions; -}; - - -#endif // __MODELDEFRDATA_H__ diff --git a/source/renderer/ModelRData.cpp b/source/renderer/ModelRData.cpp deleted file mode 100755 index 1b5c25162f..0000000000 --- a/source/renderer/ModelRData.cpp +++ /dev/null @@ -1,519 +0,0 @@ -#include "precompiled.h" - - -#include -#include "MathUtil.h" -#include "lib/ogl.h" -#include "lib/res/graphics/ogl_tex.h" -#include "lib/res/graphics/ogl_shader.h" -#include "Renderer.h" -#include "TransparencyRenderer.h" -#include "PlayerRenderer.h" -#include "ModelRData.h" -#include "Model.h" -#include "ModelDef.h" -#include "MaterialManager.h" -#include "Profile.h" -#include "renderer/ModelDefRData.h" -#include "renderer/RenderPathVertexShader.h" - - - -/////////////////////////////////////////////////////////////////// -// CModelRData constructor -CModelRData::CModelRData(CModel* model) - : m_Model(model), m_TempNormals(0), m_DynamicArray(true), m_Indices(0), m_Flags(0) -{ - debug_assert(model); - // build all data now - Build(); -} - -/////////////////////////////////////////////////////////////////// -// CModelRData destructor -CModelRData::~CModelRData() -{ - // clean up system copies of data - delete[] m_Indices; - delete[] m_TempNormals; -} - -void CModelRData::Build() -{ - CModelDefPtr mdef = m_Model->GetModelDef(); - - if (!mdef->GetRenderData()) - { - mdef->SetRenderData(new CModelDefRData(&*mdef)); - } - - m_Position.type = GL_FLOAT; - m_Position.elems = 3; - m_DynamicArray.AddAttribute(&m_Position); - - if (g_Renderer.GetRenderPath() == CRenderer::RP_VERTEXSHADER) - { - m_UV.type = GL_FLOAT; - m_UV.elems = 2; - m_DynamicArray.AddAttribute(&m_UV); - - m_Normal.type = GL_FLOAT; - m_Normal.elems = 3; - m_DynamicArray.AddAttribute(&m_Normal); - } - else - { - m_Color.type = GL_UNSIGNED_BYTE; - m_Color.elems = 3; - m_DynamicArray.AddAttribute(&m_Color); - } - - m_DynamicArray.SetNumVertices(mdef->GetNumVertices()); - m_DynamicArray.Layout(); - - // build data - BuildStaticVertices(); - BuildVertices(); - BuildIndices(); - // force a texture load on model's texture - g_Renderer.LoadTexture(m_Model->GetTexture(),GL_CLAMP_TO_EDGE); - // setup model render flags - /*if (g_Renderer.IsTextureTransparent(m_Model->GetTexture())) { - m_Flags|=MODELRDATA_FLAG_TRANSPARENT; - }*/ - if(m_Model->GetMaterial().IsPlayer()) - { - m_Flags |= MODELRDATA_FLAG_PLAYERCOLOR; - } - else if(m_Model->GetMaterial().UsesAlpha()) - { - m_Flags |= MODELRDATA_FLAG_TRANSPARENT; - } -} - -void CModelRData::BuildIndices() -{ - CModelDefPtr mdef=m_Model->GetModelDef(); - debug_assert(mdef); - - // allocate indices if we haven't got any already - if (!m_Indices) { - m_Indices=new u16[mdef->GetNumFaces()*3]; - } - - // build indices - u32 indices=0; - SModelFace* faces=mdef->GetFaces(); - for (size_t j=0; jGetNumFaces(); j++) { - SModelFace& face=faces[j]; - m_Indices[indices++]=face.m_Verts[0]; - m_Indices[indices++]=face.m_Verts[1]; - m_Indices[indices++]=face.m_Verts[2]; - } -} - - - -///////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SkinPoint: skin the vertex position using it's blend data and given bone matrices -static void SkinPoint(const SModelVertex& vertex,const CMatrix3D* matrices,CVector3D& result) -{ - CVector3D tmp; - const SVertexBlend& blend=vertex.m_Blend; - - // must have at least one valid bone if we're using SkinPoint - debug_assert(blend.m_Bone[0]!=0xff); - - const CMatrix3D& m=matrices[blend.m_Bone[0]]; - m.Transform(vertex.m_Coords,result); - result*=blend.m_Weight[0]; - - for (u32 i=1; iGetModelDef(); - size_t numVertices = mdef->GetNumVertices(); - SModelVertex* vertices = mdef->GetVertices(); - VertexArrayIterator UVit = m_UV.GetIterator(); - - for (uint j=0; j < numVertices; ++j, ++UVit) { - (*UVit)[0] = vertices[j].m_U; - (*UVit)[1] = 1.0-vertices[j].m_V; - } - } -} - -void CModelRData::BuildVertices() -{ - CModelDefPtr mdef=m_Model->GetModelDef(); - size_t numVertices=mdef->GetNumVertices(); - SModelVertex* vertices=mdef->GetVertices(); - - // build vertices - VertexArrayIterator Position = m_Position.GetIterator(); - VertexArrayIterator Normal; - - if (m_Normal.type) - { - Normal = m_Normal.GetIterator(); - } - else - { - if (!m_TempNormals) - m_TempNormals = new CVector3D[numVertices]; - Normal = VertexArrayIterator((char*)m_TempNormals, sizeof(CVector3D)); - } - - const CMatrix3D* bonematrices=m_Model->GetBoneMatrices(); - if (bonematrices) { - // boned model - calculate skinned vertex positions/normals - PROFILE( "skinning bones" ); - const CMatrix3D* invbonematrices=m_Model->GetInvBoneMatrices(); - for (size_t j=0; jGetTransform(); - const CMatrix3D& invtransform=m_Model->GetInvTransform(); - for (uint j=0; j Color = m_Color.GetIterator(); - CSHCoeffs& shcoeffs = g_Renderer.m_SHCoeffsUnits; - CColor sc = m_Model->GetShadingColor(); - RGBColor shadingcolor(sc.r, sc.g, sc.b); - RGBColor tempcolor; - for (uint j=0; jm_ModelLight); - idx = g_Renderer.m_VertexShader->m_ModelLight_SHCoefficients; - glUniform3fvARB(idx, 9, (float*)coeffs); - - glEnableClientState(GL_NORMAL_ARRAY); - } - else - { - glEnableClientState(GL_COLOR_ARRAY); - } - } -} - -// reset state prepared by SetupRender -void CModelRData::FinishRender(u32 streamflags) -{ - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - if (streamflags & STREAM_COLOR) - { - if (g_Renderer.GetRenderPath() == CRenderer::RP_VERTEXSHADER) - { - glUseProgramObjectARB(0); - - glDisableClientState(GL_NORMAL_ARRAY); - } - else - { - glDisableClientState(GL_COLOR_ARRAY); - } - } -} - - -// Render one indiviual model. -// Try to use RenderModels instead wherever possible. -// Must be bracketed by calls to CModelRData::SetupRender/FinishRender -void CModelRData::RenderStreams(u32 streamflags, int tmus) -{ - CModelDefPtr mdldef=m_Model->GetModelDef(); - - if (streamflags & STREAM_UV0) - { - if (tmus > 1) - { - for(int i = 1; i < tmus; ++i) - g_Renderer.SetTexture(i, m_Model->GetTexture()); - } - - g_Renderer.SetTexture(0, m_Model->GetTexture()); - } - - u8* base = m_DynamicArray.Bind(); - GLsizei stride = (GLsizei)m_DynamicArray.GetStride(); - - glVertexPointer(3, GL_FLOAT, stride, base + m_Position.offset); - if (streamflags & STREAM_COLOR) - { - if (m_Normal.type) - { - CColor sc = m_Model->GetShadingColor(); - glColor3f(sc.r, sc.g, sc.b); - - glNormalPointer(GL_FLOAT, stride, base + m_Normal.offset); - } - else - glColorPointer(3, m_Color.type, stride, base + m_Color.offset); - } - if (streamflags & STREAM_UV0) - { - if (m_UV.type) - glTexCoordPointer(2, GL_FLOAT, stride, base + m_UV.offset); - else - ((CModelDefRData*)mdldef->GetRenderData())->PrepareStream(streamflags); - } - - // render the lot - size_t numFaces=mdldef->GetNumFaces(); - glDrawRangeElementsEXT(GL_TRIANGLES,0,mdldef->GetNumVertices(),numFaces*3,GL_UNSIGNED_SHORT,m_Indices); - - // bump stats - g_Renderer.m_Stats.m_DrawCalls++; - g_Renderer.m_Stats.m_ModelTris+=numFaces; -} - - -void CModelRData::Update() -{ - if (m_UpdateFlags!=0) { - // renderdata changed : rebuild necessary portions - if (m_UpdateFlags & RENDERDATA_UPDATE_VERTICES) { - BuildVertices(); - } - if (m_UpdateFlags & RENDERDATA_UPDATE_INDICES) { - BuildIndices(); - } - - m_UpdateFlags=0; - } -} - -typedef std::pair IntFloatPair; -static std::vector IndexSorter; - -struct SortFacesByDist { - bool operator()(const IntFloatPair& lhs,const IntFloatPair& rhs) { - return lhs.second>rhs.second ? true : false; - } -}; - -float CModelRData::BackToFrontIndexSort(CMatrix3D& objToCam) -{ - float mindist=1.0e30f; - CVector3D osvtx,csvtx; - - CModelDefPtr mdldef=m_Model->GetModelDef(); - - SModelVertex* vtxs=mdldef->GetVertices(); - - size_t numFaces=mdldef->GetNumFaces(); - SModelFace* faces=mdldef->GetFaces(); - - IndexSorter.reserve(numFaces); - - SModelFace* facePtr=faces; - u32 i; - for (i=0;im_Verts[0]].m_Coords; - osvtx+=vtxs[facePtr->m_Verts[1]].m_Coords; - osvtx+=vtxs[facePtr->m_Verts[2]].m_Coords; - osvtx*=1.0f/3.0f; - - csvtx=objToCam.Transform(osvtx); - float distsqrd=SQR(csvtx.X)+SQR(csvtx.Y)+SQR(csvtx.Z); - if (distsqrdm_SubmissionNext) - { - mdefdata->PrepareStream(streamflags); - - for(uint idx = 0; idx < mdefdata->m_SubmissionSlots; ++idx) - { - CModelRData* modeldata = mdefdata->m_SubmissionModels[idx]; - - if (streamflags & STREAM_UV0) - g_Renderer.SetTexture(0, modeldata->GetModel()->GetTexture()); - - for(; modeldata; modeldata = modeldata->m_SubmissionNext) - { - if (flags && !(modeldata->GetModel()->GetFlags()&flags)) - continue; - - CModelDefPtr mdldef = modeldata->GetModel()->GetModelDef(); - - u8* base = modeldata->m_DynamicArray.Bind(); - GLsizei stride = (GLsizei)modeldata->m_DynamicArray.GetStride(); - - glVertexPointer(3, GL_FLOAT, stride, - base + modeldata->m_Position.offset); - if (streamflags & STREAM_COLOR) - { - if (modeldata->m_Normal.type) - { - CColor sc = modeldata->GetModel()->GetShadingColor(); - - glColor3f(sc.r, sc.g, sc.b); - - glNormalPointer(GL_FLOAT, stride, base + modeldata->m_Normal.offset); - } - else - { - glColorPointer(3, modeldata->m_Color.type, stride, - base + modeldata->m_Color.offset); - } - } - if (streamflags & STREAM_UV0 && modeldata->m_UV.type) - { - glTexCoordPointer(2, GL_FLOAT, stride, - base + modeldata->m_UV.offset); - } - - // render the lot - size_t numFaces=mdldef->GetNumFaces(); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, mdldef->GetNumVertices(), - numFaces*3, GL_UNSIGNED_SHORT, modeldata->m_Indices); - - // bump stats - g_Renderer.m_Stats.m_DrawCalls++; - g_Renderer.m_Stats.m_ModelTris+=numFaces; - } - } - } -} - - -///////////////////////////////////////////////////////////////////////////////////////////// -// Submit: submit a model to render this frame -void CModelRData::Submit(CModel* model) -{ - CModelRData* data=(CModelRData*) model->GetRenderData(); - if (data==0) { - // no renderdata for model, create it now - PROFILE( "create render data" ); - data=new CModelRData(model); - model->SetRenderData(data); - } else { - PROFILE( "update render data" ); - data->Update(); - } - - if (data->GetFlags() & MODELRDATA_FLAG_TRANSPARENT) { - // add this mode to the transparency renderer for later processing - calculate - // transform matrix - g_TransparencyRenderer.Add(model); - } else if (data->GetFlags() & MODELRDATA_FLAG_PLAYERCOLOR) { - // add this model to the player renderer - g_PlayerRenderer.Add(model); - } else { - CModelDefPtr mdldef = model->GetModelDef(); - CModelDefRData* mdefdata = (CModelDefRData*)mdldef->GetRenderData(); - - debug_assert(mdefdata != 0); - - mdefdata->Submit(data); - } -} - - -///////////////////////////////////////////////////////////////////////////////////////////// -// ClearSubmissions: Clear the submissions list -// TODO: This is asymmetrical: It only clears CModelRData lists, but no player/transparency renderer lists -void CModelRData::ClearSubmissions() -{ - for(CModelDefRData* mdefdata = CModelDefRData::m_Submissions; mdefdata; mdefdata = mdefdata->m_SubmissionNext) - { - mdefdata->ClearSubmissions(); - } - CModelDefRData::m_Submissions = 0; -} diff --git a/source/renderer/ModelRData.h b/source/renderer/ModelRData.h deleted file mode 100755 index 5f0d79009a..0000000000 --- a/source/renderer/ModelRData.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _MODELRDATA_H -#define _MODELRDATA_H - -#include -#include "Vector3D.h" -#include "Color.h" -#include "RenderableObject.h" -#include "renderer/VertexArray.h" - -#define MODELRDATA_FLAG_TRANSPARENT (1<<0) -#define MODELRDATA_FLAG_PLAYERCOLOR (1<<1) - -class CModel; -class CModelDefRData; - -class CModelRData : public CRenderData -{ - friend class CModelDefRData; - -public: - CModelRData(CModel* model); - ~CModelRData(); - - void Update(); - void RenderStreams(u32 streamflags, int tmus = 1); - - // return render flags for this model - u32 GetFlags() const { return m_Flags; } - // accessor: model we're based on - CModel* GetModel() const { return m_Model; } - - // sort indices of this object from back to front according to given - // object to camera space transform; return sqrd distance to centre of nearest triangle - float BackToFrontIndexSort(CMatrix3D& objToCam); - - // submit a model to render this frame - static void Submit(CModel* model); - // clear per frame model list - static void ClearSubmissions(); - - // prepare for rendering of models - static void SetupRender(u32 streamflags); - // reset state prepared by SetupRender - static void FinishRender(u32 streamflags); - - // render all submitted models - static void RenderModels(u32 streamflags,u32 flags=0); - -private: - // build this renderdata object - void Build(); - - void BuildStaticVertices(); - void BuildVertices(); - void BuildIndices(); - - // owner model - CModel* m_Model; - // transformed vertex normals - required for recalculating lighting on skinned models - // only used in render path RP_FIXED - CVector3D* m_TempNormals; - // vertex array - VertexArray m_DynamicArray; - VertexArray::Attribute m_Position; - VertexArray::Attribute m_UV; // only used in RP_VERTEXSHADER (shared otherwise) - VertexArray::Attribute m_Color; // only used in RP_FIXED - VertexArray::Attribute m_Normal; // only used in RP_VERTEXSHADER - // model render indices - u16* m_Indices; - // model render flags - u32 m_Flags; - // linked list of submitted models per CModelDefRData - CModelRData* m_SubmissionNext; -}; - - -#endif diff --git a/source/renderer/ModelRenderer.cpp b/source/renderer/ModelRenderer.cpp new file mode 100644 index 0000000000..0d1f5fe9c3 --- /dev/null +++ b/source/renderer/ModelRenderer.cpp @@ -0,0 +1,424 @@ +/** + * ========================================================================= + * File : ModelRenderer.cpp + * Project : Pyrogenesis + * Description : Implementation of ModelRenderer and BatchModelRenderer + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#include "precompiled.h" + +#include "ogl.h" +#include "Vector3D.h" +#include "Vector4D.h" + +#include "ps/CLogger.h" +#include "ps/Profile.h" + +#include "graphics/Color.h" +#include "graphics/Model.h" +#include "graphics/ModelDef.h" + +#include "renderer/ModelRenderer.h" +#include "renderer/Renderer.h" +#include "renderer/SHCoeffs.h" + + +#define LOG_CATEGORY "graphics" + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SkinPoint: skin the vertex position using it's blend data and given bone matrices +static void SkinPoint(const SModelVertex& vertex,const CMatrix3D* matrices,CVector3D& result) +{ + CVector3D tmp; + const SVertexBlend& blend=vertex.m_Blend; + + // must have at least one valid bone if we're using SkinPoint + debug_assert(blend.m_Bone[0]!=0xff); + + const CMatrix3D& m=matrices[blend.m_Bone[0]]; + m.Transform(vertex.m_Coords,result); + result*=blend.m_Weight[0]; + + for (u32 i=1; i Position, + VertexArrayIterator Normal) +{ + CModelDefPtr mdef = model->GetModelDef(); + size_t numVertices = mdef->GetNumVertices(); + SModelVertex* vertices=mdef->GetVertices(); + + const CMatrix3D* bonematrices = model->GetBoneMatrices(); + if (bonematrices) + { + // boned model - calculate skinned vertex positions/normals + PROFILE( "skinning bones" ); + const CMatrix3D* invbonematrices = model->GetInvBoneMatrices(); + for (size_t j=0; jGetTransform(); + const CMatrix3D& invtransform = model->GetInvTransform(); + for (uint j=0; j Normal, + VertexArrayIterator Color) +{ + PROFILE( "lighting vertices" ); + + CModelDefPtr mdef = model->GetModelDef(); + size_t numVertices = mdef->GetNumVertices(); + CSHCoeffs& shcoeffs = g_Renderer.m_SHCoeffsUnits; + CColor sc = model->GetShadingColor(); + RGBColor shadingcolor(sc.r, sc.g, sc.b); + RGBColor tempcolor; + + for (uint j=0; j UV) +{ + size_t numVertices = mdef->GetNumVertices(); + SModelVertex* vertices = mdef->GetVertices(); + + for (uint j=0; j < numVertices; ++j, ++UV) + { + (*UV)[0] = vertices[j].m_U; + (*UV)[1] = 1.0-vertices[j].m_V; + } +} + + +// Build default indices array. +void ModelRenderer::BuildIndices( + CModelDefPtr mdef, + u16* Indices) +{ + u32 idxidx = 0; + SModelFace* faces = mdef->GetFaces(); + + for (size_t j = 0; j < mdef->GetNumFaces(); ++j) { + SModelFace& face=faces[j]; + Indices[idxidx++]=face.m_Verts[0]; + Indices[idxidx++]=face.m_Verts[1]; + Indices[idxidx++]=face.m_Verts[2]; + } +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// BatchModelRenderer implementation + + +/// See BatchModelRendererInternals::phase +enum BMRPhase { + /// Currently allow calls to Submit and PrepareModels + BMRSubmit, + + /// Allow calls to rendering and EndFrame + BMRRender +}; + + +/** + * Struct BMRModelData: Per-CModel render data used by the BatchModelRenderer. + */ +struct BMRModelData : public CModelRData +{ + BMRModelData(BatchModelRendererInternals* bmri, CModel* model) + : CModelRData(bmri, model), m_BMRI(bmri), m_Data(0), m_Next(0) { } + virtual ~BMRModelData(); + + /// Back-link to "our" modelrenderer + BatchModelRendererInternals* m_BMRI; + + /// Private data created by derived class' CreateModelData + void* m_Data; + + /// Next model in the per-ModelDefTracker-slot linked list. + BMRModelData* m_Next; +}; + + +/** + * Class BMRModelDefTracker: Per-CModelDef data used by the BatchModelRenderer. + * + * Note that classes that derive from BatchModelRenderer should use + * their own per-CModelDef data if necessary. + */ +struct BMRModelDefTracker : public CModelDefRPrivate +{ + BMRModelDefTracker(CModelDefPtr mdef) + : m_ModelDef(mdef), m_Next(0), m_Slots(0) { } + + /// Back-link to the CModelDef object + CModelDefPtr m_ModelDef; + + /// Pointer to the next ModelDefTracker that has submitted models. + BMRModelDefTracker* m_Next; + + /// Number of slots used in m_ModelSlots + uint m_Slots; + + /// Each slot contains a linked list of model data objects, up to m_Slots-1 + // At the end of the frame, m_Slots is reset to 0, but m_ModelSlots stays + // the same size (we assume the same number of slots is going to be used + // next frame) + std::vector m_ModelSlots; +}; + + + +/** + * Struct BatchModelRendererInternals: Internal data of the BatchModelRenderer + * + * Separated into the source file to increase implementation hiding (and to + * avoid some causes of recompiles). + */ +struct BatchModelRendererInternals +{ + BatchModelRendererInternals(BatchModelRenderer* r) : m_Renderer(r) { } + + /// Back-link to "our" renderer + BatchModelRenderer* m_Renderer; + + /// Track the current "phase" of the frame (only for debugging purposes) + BMRPhase phase; + + /// Linked list of ModelDefTrackers that have submitted models + BMRModelDefTracker* submissions; + + /// Helper functions + void ThunkDestroyModelData(CModel* model, void* data) + { + m_Renderer->DestroyModelData(model, data); + } +}; + +BMRModelData::~BMRModelData() +{ + m_BMRI->ThunkDestroyModelData(GetModel(), m_Data); +} + + +// Construction/Destruction +BatchModelRenderer::BatchModelRenderer() +{ + m = new BatchModelRendererInternals(this); + m->phase = BMRSubmit; + m->submissions = 0; +} + +BatchModelRenderer::~BatchModelRenderer() +{ + delete m; +} + +// Submit one model. +void BatchModelRenderer::Submit(CModel* model) +{ + debug_assert(m->phase == BMRSubmit); + + CModelDefPtr mdef = model->GetModelDef(); + BMRModelDefTracker* mdeftracker = (BMRModelDefTracker*)mdef->GetRenderData(m); + CModelRData* rdata = (CModelRData*)model->GetRenderData(); + BMRModelData* bmrdata = 0; + + // Ensure model def data and model data exist + if (!mdeftracker) + { + mdeftracker = new BMRModelDefTracker(mdef); + mdef->SetRenderData(m, mdeftracker); + } + + if (rdata && rdata->GetKey() == m) + { + bmrdata = (BMRModelData*)rdata; + } + else + { + bmrdata = new BMRModelData(m, model); + bmrdata->m_Data = CreateModelData(model); + rdata = bmrdata; + model->SetRenderData(bmrdata); + model->SetDirty(~0); + g_Renderer.LoadTexture(model->GetTexture(), GL_CLAMP_TO_EDGE); + } + + // Add the model def tracker to the submission list if necessary + if (!mdeftracker->m_Slots) + { + mdeftracker->m_Next = m->submissions; + m->submissions = mdeftracker; + } + + // Add the bmrdata to the modeldef list + Handle htex = model->GetTexture()->GetHandle(); + uint idx; + + for(idx = 0; idx < mdeftracker->m_Slots; ++idx) + { + BMRModelData* in = mdeftracker->m_ModelSlots[idx]; + + if (in->GetModel()->GetTexture()->GetHandle() == htex) + break; + } + + if (idx >= mdeftracker->m_Slots) + { + ++mdeftracker->m_Slots; + if (mdeftracker->m_Slots > mdeftracker->m_ModelSlots.size()) + { + mdeftracker->m_ModelSlots.push_back(0); + debug_assert(mdeftracker->m_ModelSlots.size() == mdeftracker->m_Slots); + } + mdeftracker->m_ModelSlots[idx] = 0; + } + + bmrdata->m_Next = mdeftracker->m_ModelSlots[idx]; + mdeftracker->m_ModelSlots[idx] = bmrdata; +} + + +// Call update for all submitted models and enter the rendering phase +void BatchModelRenderer::PrepareModels() +{ + debug_assert(m->phase == BMRSubmit); + + for(BMRModelDefTracker* mdeftracker = m->submissions; mdeftracker; mdeftracker = mdeftracker->m_Next) + { + for(uint idx = 0; idx < mdeftracker->m_Slots; ++idx) + { + for(BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx]; bmrdata; bmrdata = bmrdata->m_Next) + { + debug_assert(bmrdata->GetModel()->GetRenderData() == bmrdata); + + UpdateModelData(bmrdata->GetModel(), bmrdata->m_Data, bmrdata->m_UpdateFlags); + bmrdata->m_UpdateFlags = 0; + } + } + } + + m->phase = BMRRender; +} + + +// Clear the submissions list +void BatchModelRenderer::EndFrame() +{ + static uint mostslots = 1; + + for(BMRModelDefTracker* mdeftracker = m->submissions; mdeftracker; mdeftracker = mdeftracker->m_Next) + { + if (mdeftracker->m_Slots > mostslots) + { + mostslots = mdeftracker->m_Slots; + debug_printf("BatchModelRenderer: SubmissionSlots maximum: %u\n", mostslots); + } + mdeftracker->m_Slots = 0; + } + m->submissions = 0; + + m->phase = BMRSubmit; +} + + +// Return whether we models have been submitted this frame +bool BatchModelRenderer::HaveSubmissions() +{ + return m->submissions != 0; +} + + +// Walk through the submissions list and call PrepareXYZ and RenderModel as necessary +void BatchModelRenderer::RenderAllModels(u32 flags) +{ + debug_assert(m->phase == BMRRender); + + for(BMRModelDefTracker* mdeftracker = m->submissions; mdeftracker; mdeftracker = mdeftracker->m_Next) + { + PrepareModelDef(mdeftracker->m_ModelDef); + + for(uint idx = 0; idx < mdeftracker->m_Slots; ++idx) + { + BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx]; + + PrepareTexture(bmrdata->GetModel()->GetTexture()); + + for(; bmrdata; bmrdata = bmrdata->m_Next) + { + CModel* model = bmrdata->GetModel(); + + debug_assert(bmrdata->GetKey() == m); + + if (flags && !(model->GetFlags()&flags)) + continue; + + RenderModel(model, bmrdata->m_Data); + } + } + } +} + diff --git a/source/renderer/ModelRenderer.h b/source/renderer/ModelRenderer.h new file mode 100644 index 0000000000..cc44ab6884 --- /dev/null +++ b/source/renderer/ModelRenderer.h @@ -0,0 +1,338 @@ +/** + * ========================================================================= + * File : ModelRenderer.h + * Project : Pyrogenesis + * Description : Home to the ModelRenderer class, an abstract base class + * : that manages a per-frame list of submitted models, + * : as well as simple helper classes. + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#ifndef MODELRENDERER_H +#define MODELRENDERER_H + +#include "boost/shared_ptr.hpp" + +#include "graphics/MeshManager.h" +#include "graphics/RenderableObject.h" +#include "renderer/VertexArray.h" + +class RenderModifier; +typedef boost::shared_ptr RenderModifierPtr; + +class CModel; + + +/** + * Class CModelRData: Render data that is maintained per CModel. + * ModelRenderer implementations may derive from this class to store + * per-CModel data. + * + * The main purpose of this class over CRenderData is to track which + * ModelRenderer the render data belongs to (via the key that is passed + * to the constructor). When a model changes the renderer it uses + * (e.g. via run-time modification of the renderpath configuration), + * the old ModelRenderer's render data is supposed to be replaced by + * the new data. + */ +class CModelRData : public CRenderData +{ +public: + CModelRData(const void* key, CModel* model) : m_Key(key), m_Model(model) { } + + /** + * GetKey: Retrieve the key that can be used to identify the + * ModelRenderer that created this data. + * + * @return The opaque key that was passed to the constructor. + */ + const void* GetKey() const { return m_Key; } + + /** + * GetModel: Retrieve the model that this render data object + * belongs to. + * + * @return The model pointer that was passed to the constructor. + */ + CModel* GetModel() const { return m_Model; } + +private: + /// The key for model renderer identification + const void* m_Key; + + /// The model this object was created for + CModel* m_Model; +}; + + +/** + * Class ModelRenderer: Abstract base class for all model renders. + * + * A ModelRenderer manages a per-frame list of models. + * + * It is supposed to be derived in order to create a new render path + * for models (e.g. for models using special effects). + * However, consider deriving from BatchModelRenderer or another + * specialized base class to use their features. + * + * It is suggested that a derived class implement the provided generic + * Render function, however in some cases it may be necessary to supply + * a Render function with a different prototype. + * + * ModelRenderer also contains a number of static helper functions + * for building vertex arrays. + */ +class ModelRenderer +{ +public: + ModelRenderer() { } + virtual ~ModelRenderer() { } + + /** + * Submit: Submit a model for rendering this frame. + * + * preconditions : The model must not have been submitted to any + * ModelRenderer in this frame. Submit may only be called + * after EndFrame and before PrepareModels. + * + * @param model The model that will be added to the list of models + * submitted this frame. + */ + virtual void Submit(CModel* model) = 0; + + /** + * PrepareModels: Calculate renderer data for all previously + * submitted models. + * + * Must be called before any rendering calls and after all models + * for this frame have been submitted. + */ + virtual void PrepareModels() = 0; + + /** + * EndFrame: Remove all models from the list of submitted + * models. + */ + virtual void EndFrame() = 0; + + /** + * HaveSubmissions: Return whether any models have been submitted this frame. + * + * @return true if models have been submitted, false otherwise. + */ + virtual bool HaveSubmissions() = 0; + + /** + * Render: Render submitted models, using the given RenderModifier to setup + * the fragment stage. + * + * @note It is suggested that derived model renderers implement and use + * this Render functions. However, a highly specialized model renderer + * may need to "disable" this function and provide its own Render function + * with a different prototype. + * + * preconditions : PrepareModels must be called after all models have been + * submitted and before calling Render. + * + * @param modifier The RenderModifier that specifies the fragment stage. + * @param flags If flags is 0, all submitted models are rendered. + * If flags is non-zero, only models that contain flags in their + * CModel::GetFlags() are rendered. + */ + virtual void Render(RenderModifierPtr modifier, u32 flags) = 0; + + /** + * BuildPositionAndNormals: Build animated vertices and normals, + * transformed into world space. + * + * @param model The model that is to be transformed. + * @param Position Points to the array that will receive + * transformed position vectors. The array behind the iterator + * must be large enough to hold model->GetModelDef()->GetNumVertices() + * vertices. + * @param Normal Points to the array that will receive transformed + * normal vectors. The array behind the iterator must be as large as + * the Position array. + */ + static void BuildPositionAndNormals( + CModel* model, + VertexArrayIterator Position, + VertexArrayIterator Normal); + + /** + * BuildColor4ub: Build lighting colors for the given model, + * based on previously calculated world space normals. + * + * @param model The model that is to be lit. + * @param Normal Array of the model's normal vectors, animated and + * transformed into world space. + * @param Color Points to the array that will receive the lit vertex color. + * The array behind the iterator must large enough to hold + * model->GetModelDef()->GetNumVertices() vertices. + */ + static void BuildColor4ub( + CModel* model, + VertexArrayIterator Normal, + VertexArrayIterator Color); + + /** + * BuildUV: Copy UV coordinates into the given vertex array. + * + * @param mdef The model def. + * @param UV Points to the array that will receive UV coordinates. + * The array behind the iterator must large enough to hold + * mdef->GetNumVertices() vertices. + */ + static void BuildUV( + CModelDefPtr mdef, + VertexArrayIterator UV); + + /** + * BuildIndices: Create the indices array for the given CModelDef. + * + * @param mdef The model definition object. + * @param Indices The index array, must be able to hold + * mdef->GetNumFaces()*3 elements. + */ + static void BuildIndices( + CModelDefPtr mdef, + u16* Indices); +}; + + +struct BatchModelRendererInternals; + +/** + * Class BatchModelRenderer: Base class for model renderers that sorts + * submitted models by CModelDef and texture for batching. + * + * A derived class must provide its own Render function. Depending on + * the ModelRenderer's purpose, this function may take renderer-dependent + * arguments and prepare OpenGL state or private data. It should then + * call RenderAllModels, which will call back into the various PrepareXYZ + * functions for the actual rendering. + * + * Thus a derived class must also provide implementations for + * RenderModel and PrepareXYZ. + * + * @note Model renderers that derive from this class MUST NOT set their + * own per-CModel render data. Use the CreateModelData facility instead. + */ +class BatchModelRenderer : public ModelRenderer +{ + friend struct BatchModelRendererInternals; + +public: + BatchModelRenderer(); + virtual ~BatchModelRenderer(); + + // Batching implementations + virtual void Submit(CModel* model); + virtual void PrepareModels(); + virtual void EndFrame(); + virtual bool HaveSubmissions(); + +protected: + /** + * CreateModelData: Create renderer's internal data for one model. + * + * This function must be implemented by derived classes. It will be + * called once by Submit for every model, and allows derived classes + * to allocate per-model vertex buffers and other data. + * + * @param model The model. + * + * @return A data pointer that is opaque to this class, but will be + * passed to other functions whenever the CModel is passed again. + */ + virtual void* CreateModelData(CModel* model) = 0; + + /** + * UpdateModelData: Calculate per-model data for each frame. + * + * This function must be implemented by derived classes. It will be + * called once per frame by PrepareModels, regardless of the value + * of CRenderData::m_UpdateFlags. + * + * This implies that UpdateModelData will be called at least once + * between the call to CreateModelData and the first call to RenderModel + * for this model. + * + * @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, void* data, u32 updateflags) = 0; + + /** + * DestroyModelData: Release all per-model data that has been allocated + * by CreateModelData or UpdateModelData. + * + * @param model The model. + * @param data Private data as returned by CreateModelData. + */ + virtual void DestroyModelData(CModel* model, void* data) = 0; + + /** + * RenderAllModels: Loop through submitted models and render them + * by calling the PrepareXYZ functions and RenderModel as appropriate. + * + * @param flags Filter models based on CModel::GetFlags(). If flags + * is 0, all models are rendered. Otherwise, only models are rendered + * for which CModel::GetFlags() & flags returns true. + * + * postconditions : RenderAllModels does not affect OpenGL state + * itself. However, it changes OpenGL state indirectly by calling + * the PrepareXYZ and RenderModel functions. + */ + void RenderAllModels(u32 flags); + + /** + * PrepareModelDef: Setup OpenGL state for rendering of models that + * use the given CModelDef object as base. + * + * This function is called by RenderAllModels before rendering any + * models using def as base model definition. + * + * @param def The model definition. + */ + virtual void PrepareModelDef(CModelDefPtr def) = 0; + + /** + * PrepareTexture: Setup OpenGL state for rendering of models that + * use the given texture. + * + * This function is called by RenderAllModels before rendering any + * models using the given texture. + * + * @param texture The texture. + */ + virtual void PrepareTexture(CTexture* texture) = 0; + + /** + * RenderModel: Render the given model. + * + * This function is called by RenderAllModels. + * + * preconditions : PrepareModelDef and PrepareTexture are called + * before calling RenderModel. + * + * @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(CModel* model, void* data) = 0; + +private: + BatchModelRendererInternals* m; +}; + + +#endif // MODELRENDERER_H diff --git a/source/renderer/PlayerRenderer.cpp b/source/renderer/PlayerRenderer.cpp index 3c4d395d68..449e157731 100644 --- a/source/renderer/PlayerRenderer.cpp +++ b/source/renderer/PlayerRenderer.cpp @@ -1,34 +1,46 @@ -/*************************************************************************************** - AUTHOR: John M. Mena - EMAIL: JohnMMena@hotmail.com - FILE: PlayerRenderer.cpp - CREATED: 1/23/05 - COMPLETED: NULL - - DESCRIPTION: Handles rendering all of the player objects. - The structure and overall design was inherited from Rich Cross' Transparency Renderer. -****************************************************************************************/ +/** + * ========================================================================= + * File : PlayerRenderer.cpp + * Project : Pyrogenesis + * Description : Implementation of player colour RenderModifiers. + * + * @author John M. Mena + * @author Nicolai Hähnle + * ========================================================================= + */ #include "precompiled.h" -#include -#include "Renderer.h" -#include "PlayerRenderer.h" -#include "Model.h" -#include "Game.h" -#include "Profile.h" +#include "renderer/Renderer.h" +#include "renderer/PlayerRenderer.h" +#include "graphics/Model.h" #include "ps/CLogger.h" #define LOG_CATEGORY "graphics" -CPlayerRenderer g_PlayerRenderer; - /////////////////////////////////////////////////////////////////////////////////////////////////// -// RenderFast: Use single-pass, three-TMU rendering technique -void CPlayerRenderer::RenderFast() +// FastPlayerColorRender + +FastPlayerColorRender::FastPlayerColorRender() { + debug_assert(ogl_max_tex_units >= 3); +} + +FastPlayerColorRender::~FastPlayerColorRender() +{ +} + +bool FastPlayerColorRender::IsAvailable() +{ + return (ogl_max_tex_units >= 3); +} + +u32 FastPlayerColorRender::BeginPass(uint pass) +{ + debug_assert(pass == 0); + // Nice player color uses a single pass with three texture environments // Note: This uses ARB_texture_env_crossbar (which is checked in GameSetup) // @@ -82,84 +94,121 @@ void CPlayerRenderer::RenderFast() glActiveTextureARB(GL_TEXTURE0); - // Render it! - RenderObjectsStreams(STREAM_POS|STREAM_COLOR|STREAM_UV0, true); + return STREAM_POS|STREAM_COLOR|STREAM_UV0; +} + +bool FastPlayerColorRender::EndPass(uint pass) +{ // Restore state glActiveTextureARB(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); glActiveTextureARB(GL_TEXTURE2); glDisable(GL_TEXTURE_2D); glActiveTextureARB(GL_TEXTURE0); + + return true; +} + +void FastPlayerColorRender::PrepareTexture(uint pass, CTexture* texture) +{ + g_Renderer.SetTexture(2, texture); + g_Renderer.SetTexture(1, texture); + g_Renderer.SetTexture(0, texture); +} + +void FastPlayerColorRender::PrepareModel(uint pass, CModel* model) +{ + // Get the player color + SMaterialColor colour = model->GetMaterial().GetPlayerColor(); + float* color = &colour.r; // because it's stored RGBA + + // Set the texture environment color the player color + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); } /////////////////////////////////////////////////////////////////////////////////////////////////// -// RenderSlow: Use multi-pass rendering technique, using two TMUs -void CPlayerRenderer::RenderSlow() +// SlowPlayerColorRender + +SlowPlayerColorRender::SlowPlayerColorRender() +{ +} + +SlowPlayerColorRender::~SlowPlayerColorRender() +{ +} + +u32 SlowPlayerColorRender::BeginPass(uint pass) { // We calculate: Result = (Color*Texture)*Texture.a + (Color*Texture*PlayerColor)*(1-Texture.a) // Modulation is done via texture environments, the final interpolation is done via blending - // FIRST PASS + if (pass == 0) + { + // TexEnv #0 + glActiveTextureARB(GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + + // Don't care about alpha; set it to something harmless + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + + // Render it! + return STREAM_POS|STREAM_COLOR|STREAM_UV0; + } + else + { + // TexEnv #0 + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + + // Alpha = Opacity of non-player colored layer + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + + // TexEnv #1 + glActiveTextureARB(GL_TEXTURE1); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - // TexEnv #0 - glActiveTextureARB(GL_TEXTURE0); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - - // Don't care about alpha; set it to something harmless - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - - // Render it! - RenderObjectsStreams(STREAM_POS|STREAM_COLOR|STREAM_UV0, false); - - - // SECOND PASS - - // TexEnv #0 - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - - // Alpha = Opacity of non-player colored layer - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - - // TexEnv #1 - glActiveTextureARB(GL_TEXTURE1); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + // Pass alpha unchanged + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + glActiveTextureARB(GL_TEXTURE0); + + // Setup blending + glEnable(GL_BLEND); + glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_LESS, 1.0); + glDepthMask(0); + + // Render it! + return STREAM_POS|STREAM_COLOR|STREAM_UV0; + } +} - // Pass alpha unchanged - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - glActiveTextureARB(GL_TEXTURE0); - - // Setup blending - glEnable(GL_BLEND); - glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_LESS, 1.0); - glDepthMask(0); - - // Render it! - RenderObjectsStreams(STREAM_POS|STREAM_COLOR|STREAM_UV0, true); +bool SlowPlayerColorRender::EndPass(uint pass) +{ + if (pass == 0) + return false; // need two passes // Restore state glActiveTextureARB(GL_TEXTURE1); @@ -168,123 +217,28 @@ void CPlayerRenderer::RenderSlow() glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glDepthMask(1); -} - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Render: render all deferred passes; call Sort before using to ensure passes -// are drawn in correct order -void CPlayerRenderer::Render() -{ - PROFILE( "render player models" ); - - if (m_Objects.size()==0) return; - - if (g_Renderer.m_FastPlayerColor && ogl_max_tex_units < 3) - { - g_Renderer.m_FastPlayerColor = false; - LOG(WARNING, LOG_CATEGORY, "Using fallback player color rendering (not enough TMUs)."); - } - // switch on wireframe if we need it - if (g_Renderer.m_ModelRenderMode==WIREFRAME) { - glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); - } - - CModelRData::SetupRender(STREAM_POS|STREAM_COLOR|STREAM_UV0); - - if (g_Renderer.m_FastPlayerColor) - { - RenderFast(); - } - else - { - RenderSlow(); - } - - CModelRData::FinishRender(STREAM_POS|STREAM_COLOR|STREAM_UV0); - - if (g_Renderer.m_ModelRenderMode==WIREFRAME) { - // switch wireframe off again - glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); - } else if (g_Renderer.m_ModelRenderMode==EDGED_FACES) { - // edged faces: need to make a second pass over the data: - // first switch on wireframe - glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); - - // setup some renderstate .. - glDepthMask(0); - g_Renderer.SetTexture(0,0); - glColor4f(1,1,1,0.75f); - glLineWidth(1.0f); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - - // render each model - CModelRData::SetupRender(STREAM_POS); - RenderObjectsStreams(STREAM_POS); - CModelRData::FinishRender(STREAM_POS); - - // .. and restore the renderstates - glDisable(GL_BLEND); - glDepthMask(1); - - // restore fill mode, and we're done - glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); - } + return true; } -void CPlayerRenderer::Clear() +void SlowPlayerColorRender::PrepareTexture(uint pass, CTexture* texture) { - // all transparent objects rendered; release them - m_Objects.clear(); + if (pass == 1) + g_Renderer.SetTexture(1, texture); + g_Renderer.SetTexture(0, texture); } -void CPlayerRenderer::Add(CModel* model) -{ - m_Objects.push_back(model); -} -//TODO: Correctly implement shadows for the players -void CPlayerRenderer::RenderShadows() +void SlowPlayerColorRender::PrepareModel(uint pass, CModel* model) { - if (m_Objects.size()==0) return; - - RenderObjectsStreams(STREAM_POS|STREAM_UV0, false, MODELFLAG_CASTSHADOWS); -} - -/////////////////////////////////////////////////////////////////////////////////////////////// -// RenderObjectsStreams: render given streams on all objects -void CPlayerRenderer::RenderObjectsStreams(u32 streamflags, bool iscolorpass, u32 mflags) -{ - int tmus = 1; - - if (iscolorpass) + if (pass == 1) { - if (g_Renderer.m_FastPlayerColor) - tmus = 3; - else - tmus = 2; - } - - for (uint i=0;iGetFlags() & mflags)) { - CModelRData* modeldata=(CModelRData*) m_Objects[i]->GetRenderData(); + // Get the player color + SMaterialColor colour = model->GetMaterial().GetPlayerColor(); + float* color = &colour.r; // because it's stored RGBA - // Pass the player color as a TexEnv constant when applicable - if (iscolorpass) - { - // Get the player color - SMaterialColor colour = m_Objects[i]->GetMaterial().GetPlayerColor(); - float* color = &colour.r; // because it's stored RGBA - - // Set the texture environment color the player color - glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); - } - - // Render the model - modeldata->RenderStreams(streamflags, tmus); - } + // Set the texture environment color the player color + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); } } + diff --git a/source/renderer/PlayerRenderer.h b/source/renderer/PlayerRenderer.h index 22ccf88bb3..a31d117bfc 100644 --- a/source/renderer/PlayerRenderer.h +++ b/source/renderer/PlayerRenderer.h @@ -1,44 +1,70 @@ -/*************************************************************************************** - AUTHOR: John M. Mena - EMAIL: JohnMMena@hotmail.com - FILE: CConsole.h - CREATED: 1/23/05 - COMPLETED: NULL - - DESCRIPTION: Handles rendering all of the player objects. - The structure was inherited from Rich Cross' Transparency Renderer. -****************************************************************************************/ +/** + * ========================================================================= + * File : PlayerRenderer + * Project : Pyrogenesis + * Description : RenderModifier for player color rendering, to be used + * : with e.g. FixedFunctionModelRenderer + * + * @author John M. Mena + * @author Nicolai Hähnle + * ========================================================================= + */ #ifndef __PLAYERRENDERER_H #define __PLAYERRENDERER_H -#include +#include "RenderModifiers.h" -class CModel; -class CPlayerRenderer +/** + * Class FastPlayerColorRender: Render models fully textured and lit + * plus player color in a single pass using multi-texturing (at least 3 TMUs + * required). + */ +class FastPlayerColorRender : public RenderModifier { public: - // add object to render in deferred transparency pass - void Add(CModel* model); - // render all deferred objects - void Render(); - // render shadows from all deferred objects - void RenderShadows(); - // empty object list - void Clear(); + FastPlayerColorRender(); + ~FastPlayerColorRender(); -private: - // fast render path - void RenderFast(); - // slow render path - void RenderSlow(); - // render given streams on all objects - void RenderObjectsStreams(u32 streamflags, bool iscolorpass=false, u32 mflags=0); - // list of objects to render - std::vector m_Objects; + // Implementation + u32 BeginPass(uint pass); + bool EndPass(uint pass); + void PrepareTexture(uint pass, CTexture* texture); + void PrepareModel(uint pass, CModel* model); + + /** + * IsAvailable: Determines whether this RenderModifier can be used + * given the OpenGL implementation specific limits. + * + * @note Do not attempt to construct a FastPlayerColorRender object + * when IsAvailable returns false. + * + * @return true if the OpenGL implementation can support this + * RenderModifier. + */ + static bool IsAvailable(); }; -extern CPlayerRenderer g_PlayerRenderer; + +/** + * Class SlowPlayerColorRender: Render models fully textured and lit + * plus player color using multi-pass. + * + * It has the same visual result as FastPlayerColorRender (except for + * potential precision issues due to the multi-passing). + */ +class SlowPlayerColorRender : public RenderModifier +{ +public: + SlowPlayerColorRender(); + ~SlowPlayerColorRender(); + + // Implementation + u32 BeginPass(uint pass); + bool EndPass(uint pass); + void PrepareTexture(uint pass, CTexture* texture); + void PrepareModel(uint pass, CModel* model); +}; #endif diff --git a/source/renderer/RenderModifiers.cpp b/source/renderer/RenderModifiers.cpp new file mode 100644 index 0000000000..5fa4960fb4 --- /dev/null +++ b/source/renderer/RenderModifiers.cpp @@ -0,0 +1,179 @@ +/** + * ========================================================================= + * File : RenderModifiers.cpp + * Project : Pyrogenesis + * Description : Implementation of common RenderModifiers + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#include "precompiled.h" + +#include "ogl.h" +#include "Vector3D.h" +#include "Vector4D.h" + +#include "ps/CLogger.h" + +#include "graphics/Color.h" +#include "graphics/Model.h" + +#include "renderer/RenderModifiers.h" +#include "renderer/Renderer.h" + +#define LOG_CATEGORY "graphics" + + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// RenderModifier implementation + +void RenderModifier::PrepareModel(uint pass, CModel* model) +{ +} + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// PlainRenderModifier implementation + +PlainRenderModifier::PlainRenderModifier() +{ +} + +PlainRenderModifier::~PlainRenderModifier() +{ +} + +u32 PlainRenderModifier::BeginPass(uint pass) +{ + debug_assert(pass == 0); + + // set up texture environment for base pass - modulate texture and primary color + glActiveTextureARB(GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + + // Set the proper LOD bias + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); + + // pass one through as alpha; transparent textures handled specially by TransparencyRenderer + // (gl_constant means the colour comes from the gl_texture_env_color) + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + + float color[] = { 1.0, 1.0, 1.0, 1.0 }; + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); + + return STREAM_POS|STREAM_COLOR|STREAM_UV0; +} + +bool PlainRenderModifier::EndPass(uint pass) +{ + // We didn't modify blend state or higher texenvs, so we don't have + // to reset OpenGL state here. + + return true; +} + +void PlainRenderModifier::PrepareTexture(uint pass, CTexture* texture) +{ + g_Renderer.SetTexture(0, texture); +} + +void PlainRenderModifier::PrepareModel(uint pass, CModel* model) +{ +} + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// WireframeRenderModifier implementation + + +WireframeRenderModifier::WireframeRenderModifier() +{ +} + +WireframeRenderModifier::~WireframeRenderModifier() +{ +} + +u32 WireframeRenderModifier::BeginPass(uint pass) +{ + debug_assert(pass == 0); + + // first switch on wireframe + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + // setup some renderstate .. + glDepthMask(0); + g_Renderer.SetTexture(0,0); + glColor4f(1,1,1,0.75f); + glLineWidth(1.0f); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + return STREAM_POS; +} + + +bool WireframeRenderModifier::EndPass(uint pass) +{ + // .. restore the renderstates + glDisable(GL_BLEND); + glDepthMask(1); + + // restore fill mode, and we're done + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + + return true; +} + + +void WireframeRenderModifier::PrepareTexture(uint pass, CTexture* texture) +{ +} + + +void WireframeRenderModifier::PrepareModel(uint pass, CModel* model) +{ +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// SolidColorRenderModifier implementation + +SolidColorRenderModifier::SolidColorRenderModifier() +{ +} + +SolidColorRenderModifier::~SolidColorRenderModifier() +{ +} + +u32 SolidColorRenderModifier::BeginPass(uint pass) +{ + g_Renderer.SetTexture(0,0); + + return STREAM_POS; +} + +bool SolidColorRenderModifier::EndPass(uint pass) +{ + return true; +} + +void SolidColorRenderModifier::PrepareTexture(uint pass, CTexture* texture) +{ +} + +void SolidColorRenderModifier::PrepareModel(uint pass, CModel* model) +{ +} diff --git a/source/renderer/RenderModifiers.h b/source/renderer/RenderModifiers.h new file mode 100644 index 0000000000..e749fbce9b --- /dev/null +++ b/source/renderer/RenderModifiers.h @@ -0,0 +1,151 @@ +/** + * ========================================================================= + * File : RenderModifiers.h + * Project : Pyrogenesis + * Description : RenderModifiers can affect the fragment stage behaviour + * : of some ModelRenderers. This file defines some common + * : RenderModifiers in addition to the base class. + * + * @author Nicolai Hähnle + * ========================================================================= + */ + +#ifndef RENDERMODIFIERS_H +#define RENDERMODIFIERS_H + +#include "ModelRenderer.h" + + +class CModel; +class CTexture; + + +/** + * 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 u32 BeginPass(uint pass) = 0; + + /** + * EndPass: Cleanup OpenGL state after the given pass. This function + * may indicate that additional passes are needed. + * + * Must be implemented by derived classes. + * + * @param pass The current pass number (pass == 0 is the first pass) + * + * @return true if rendering is complete, false if an additional pass + * is needed. If false is returned, BeginPass is then called with an + * increased pass number. + */ + virtual bool EndPass(uint pass) = 0; + + /** + * PrepareTexture: Called before rendering models that use the given + * texture. + * + * Must be implemented by derived classes. + * + * @param pass The current pass number (pass == 0 is the first pass) + * @param texture The texture used by subsequent models + */ + virtual void PrepareTexture(uint pass, CTexture* texture) = 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(uint pass, CModel* model); +}; + + +/** + * Class RenderModifierRenderer: Interface to a model renderer that can render + * its models via a RenderModifier that sets up fragment stages. + */ +class RenderModifierRenderer : public ModelRenderer +{ +public: + RenderModifierRenderer() { } + virtual ~RenderModifierRenderer(); + +}; + + +/** + * Class PlainRenderModifier: RenderModifier that simply uses opaque textures + * modulated by primary color. It is used for normal, no-frills models. + */ +class PlainRenderModifier : public RenderModifier +{ +public: + PlainRenderModifier(); + ~PlainRenderModifier(); + + // Implementation + u32 BeginPass(uint pass); + bool EndPass(uint pass); + void PrepareTexture(uint pass, CTexture* texture); + void PrepareModel(uint pass, CModel* model); +}; + +/** + * Class WireframeRenderModifier: RenderModifier that renders wireframe models. + */ +class WireframeRenderModifier : public RenderModifier +{ +public: + WireframeRenderModifier(); + ~WireframeRenderModifier(); + + // Implementation + u32 BeginPass(uint pass); + bool EndPass(uint pass); + void PrepareTexture(uint pass, CTexture* texture); + void PrepareModel(uint pass, CModel* model); +}; + + +/** + * Class SolidColorRenderModifier: Render all models using the same + * solid color without lighting. + * + * You have to specify the color using a glColor*() calls before rendering. + */ +class SolidColorRenderModifier : public RenderModifier +{ +public: + SolidColorRenderModifier(); + ~SolidColorRenderModifier(); + + // Implementation + u32 BeginPass(uint pass); + bool EndPass(uint pass); + void PrepareTexture(uint pass, CTexture* texture); + void PrepareModel(uint pass, CModel* model); +}; + +#endif // RENDERMODIFIERS_H diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 9f382b51ee..8d74c68bf9 100755 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -46,7 +46,13 @@ #include "lib/res/graphics/ogl_tex.h" #include "ps/Loader.h" +#include "renderer/FixedFunctionModelRenderer.h" +#include "renderer/HWLightingModelRenderer.h" +#include "renderer/ModelRenderer.h" +#include "renderer/PlayerRenderer.h" +#include "renderer/RenderModifiers.h" #include "renderer/RenderPathVertexShader.h" +#include "renderer/TransparencyRenderer.h" #define LOG_CATEGORY "graphics" @@ -69,12 +75,39 @@ CRenderer::CRenderer() m_Options.m_Shadows=true; m_Options.m_ShadowColor=RGBAColor(0.4f,0.4f,0.4f,1.0f); m_Options.m_RenderPath = RP_DEFAULT; - m_FastPlayerColor = true; for (uint i=0;iInit()) + { + delete m_VertexShader; + m_VertexShader = 0; + } + + // model rendering + m_Models.NormalFF = new FixedFunctionModelRenderer; + m_Models.PlayerFF = new FixedFunctionModelRenderer; + if (HWLightingModelRenderer::IsAvailable()) + { + m_Models.NormalHWLit = new HWLightingModelRenderer; + m_Models.PlayerHWLit = new HWLightingModelRenderer; + } + m_Models.Transparency = new TransparencyRenderer; + + m_Models.ModWireframe = RenderModifierPtr(new WireframeRenderModifier); + m_Models.ModPlain = RenderModifierPtr(new PlainRenderModifier); + SetFastPlayerColor(true); + m_Models.ModSolidColor = RenderModifierPtr(new SolidColorRenderModifier); + m_Models.ModTransparent = RenderModifierPtr(new TransparentRenderModifier); + m_Models.ModTransparentShadow = RenderModifierPtr(new TransparentShadowRenderModifier); + // water m_RenderWater = true; m_WaterHeight = 5.0f; @@ -99,6 +132,14 @@ CRenderer::CRenderer() // CRenderer destructor CRenderer::~CRenderer() { + // model rendering + delete m_Models.NormalFF; + delete m_Models.PlayerFF; + delete m_Models.NormalHWLit; + delete m_Models.PlayerHWLit; + delete m_Models.Transparency; + + // general delete m_VertexShader; m_VertexShader = 0; @@ -137,31 +178,6 @@ void CRenderer::EnumCaps() } -// Select the render path we're going to use based on config preferrences and -// on available extensions. -void CRenderer::InitRenderPath() -{ - RenderPath desired = m_Options.m_RenderPath; - - if (m_Options.m_RenderPath == RP_DEFAULT) - m_Options.m_RenderPath = RP_VERTEXSHADER; - - if (m_Options.m_RenderPath == RP_VERTEXSHADER) - { - m_VertexShader = new RenderPathVertexShader; - if (!m_VertexShader->Init()) - { - delete m_VertexShader; - m_VertexShader = 0; - m_Options.m_RenderPath = RP_FIXED; - } - } - - LOG(NORMAL, LOG_CATEGORY, "Selected render path: %hs (configuration value: %hs)", - GetRenderPathName(m_Options.m_RenderPath).c_str(), - GetRenderPathName(desired).c_str()); -} - bool CRenderer::Open(int width, int height, int depth) { m_Width = width; @@ -179,9 +195,6 @@ bool CRenderer::Open(int width, int height, int depth) glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); - // query card capabilities - EnumCaps(); - GLint bits; glGetIntegerv(GL_DEPTH_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: depth bits %d",bits); @@ -190,7 +203,8 @@ bool CRenderer::Open(int width, int height, int depth) glGetIntegerv(GL_ALPHA_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: alpha bits %d",bits); - InitRenderPath(); + if (m_Options.m_RenderPath == RP_DEFAULT) + SetRenderPath(m_Options.m_RenderPath); return true; } @@ -294,10 +308,21 @@ const RGBAColor& CRenderer::GetOptionColor(enum Option opt) const // data may depend on the chosen render path. void CRenderer::SetRenderPath(RenderPath rp) { - if (m_Options.m_RenderPath != RP_DEFAULT && rp != m_Options.m_RenderPath) + if (rp == RP_DEFAULT) { - debug_warn("Cannot change RenderPath after the fact"); - return; + if (m_Models.NormalHWLit && m_Models.PlayerHWLit) + rp = RP_VERTEXSHADER; + else + rp = RP_FIXED; + } + + if (rp == RP_VERTEXSHADER) + { + if (!m_Models.NormalHWLit || !m_Models.PlayerHWLit) + { + LOG(WARNING, LOG_CATEGORY, "Falling back to fixed function\n"); + rp = RP_FIXED; + } } m_Options.m_RenderPath = rp; @@ -328,6 +353,27 @@ CRenderer::RenderPath CRenderer::GetRenderPathByName(CStr name) } +////////////////////////////////////////////////////////////////////////////////////////// +// SetFastPlayerColor +void CRenderer::SetFastPlayerColor(bool fast) +{ + m_FastPlayerColor = fast; + + if (m_FastPlayerColor) + { + if (!FastPlayerColorRender::IsAvailable()) + { + LOG(WARNING, LOG_CATEGORY, "Falling back to slower player color rendering."); + m_FastPlayerColor = false; + } + } + + if (m_FastPlayerColor) + m_Models.ModPlayer = RenderModifierPtr(new FastPlayerColorRender); + else + m_Models.ModPlayer = RenderModifierPtr(new SlowPlayerColorRender); +} + ////////////////////////////////////////////////////////////////////////////////////////// // BeginFrame: signal frame start void CRenderer::BeginFrame() @@ -721,36 +767,18 @@ void CRenderer::RenderShadowMap() glEnable(GL_SCISSOR_TEST); glScissor(1,1,m_Width-2,m_Height-2); - glActiveTextureARB(GL_TEXTURE0); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - oglSquelchError(GL_INVALID_ENUM); - - // Set the proper LOD bias - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_Options.m_LodBias); - glColor4fv(m_Options.m_ShadowColor); glDisable(GL_CULL_FACE); - CModelRData::SetupRender(STREAM_POS); + m_Models.NormalFF->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); + m_Models.PlayerFF->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); + if (m_Models.NormalHWLit) + m_Models.NormalHWLit->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); + if (m_Models.PlayerHWLit) + m_Models.PlayerHWLit->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); + m_Models.Transparency->Render(m_Models.ModTransparentShadow, MODELFLAG_CASTSHADOWS); - // render models - CModelRData::RenderModels(STREAM_POS,MODELFLAG_CASTSHADOWS); - - // call on the player renderer to render all of the player shadows. - g_PlayerRenderer.RenderShadows(); - - // call on the transparency renderer to render all the transparent stuff - g_TransparencyRenderer.RenderShadows(); - - CModelRData::FinishRender(STREAM_POS); - glEnable(GL_CULL_FACE); glColor3f(1.0f,1.0f,1.0f); @@ -993,35 +1021,6 @@ void CRenderer::RenderWater() glDisable(GL_TEXTURE_2D); } -void CRenderer::RenderModelSubmissions() -{ - // set up texture environment for base pass - modulate texture and primary color - glActiveTextureARB(GL_TEXTURE0); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - - // Set the proper LOD bias - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_Options.m_LodBias); - - // pass one through as alpha; transparent textures handled specially by CTransparencyRenderer - // (gl_constant means the colour comes from the gl_texture_env_color) - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - - float color[] = { 1.0, 1.0, 1.0, 1.0 }; - glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color); - - // render models - CModelRData::SetupRender(STREAM_POS|STREAM_COLOR|STREAM_UV0); - CModelRData::RenderModels(STREAM_POS|STREAM_COLOR|STREAM_UV0); - CModelRData::FinishRender(STREAM_POS|STREAM_COLOR|STREAM_UV0); -} - void CRenderer::RenderModels() { PROFILE( "render models "); @@ -1031,49 +1030,44 @@ void CRenderer::RenderModels() glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } - // render all the models - RenderModelSubmissions(); + m_Models.NormalFF->Render(m_Models.ModPlain, 0); + m_Models.PlayerFF->Render(m_Models.ModPlayer, 0); + if (m_Models.NormalHWLit) + m_Models.NormalHWLit->Render(m_Models.ModPlain, 0); + if (m_Models.PlayerHWLit) + m_Models.PlayerHWLit->Render(m_Models.ModPlayer, 0); if (m_ModelRenderMode==WIREFRAME) { // switch wireframe off again glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } else if (m_ModelRenderMode==EDGED_FACES) { - // edged faces: need to make a second pass over the data: - // first switch on wireframe - glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); - - // setup some renderstate .. - glDepthMask(0); - SetTexture(0,0); - glColor4f(1,1,1,0.75f); - glLineWidth(1.0f); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - - // render all non-transparent, non-player models - CModelRData::SetupRender(STREAM_POS); - CModelRData::RenderModels(STREAM_POS); - CModelRData::FinishRender(STREAM_POS); - - // .. and restore the renderstates - glDisable(GL_BLEND); - glDepthMask(1); - - // restore fill mode, and we're done - glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + m_Models.NormalFF->Render(m_Models.ModWireframe, 0); + m_Models.PlayerFF->Render(m_Models.ModWireframe, 0); + if (m_Models.NormalHWLit) + m_Models.NormalHWLit->Render(m_Models.ModWireframe, 0); + if (m_Models.PlayerHWLit) + m_Models.PlayerHWLit->Render(m_Models.ModWireframe, 0); } } -/////////////////////////////////////////////////////////////////////////////////////////////////// -// SortModelsByTexture: sorting class used for batching models with identical textures -struct SortModelsByTexture { - typedef CModel* SortObj; +void CRenderer::RenderTransparentModels() +{ + PROFILE( "render transparent models "); - bool operator()(const SortObj& lhs,const SortObj& rhs) { - return lhs->GetTexture()GetTexture() ? true : false; + // switch on wireframe if we need it + if (m_ModelRenderMode==WIREFRAME) { + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } -}; + + m_Models.Transparency->Render(m_Models.ModTransparent, 0); + + if (m_ModelRenderMode==WIREFRAME) { + // switch wireframe off again + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + } else if (m_ModelRenderMode==EDGED_FACES) { + m_Models.Transparency->Render(m_Models.ModWireframe, 0); + } +} /////////////////////////////////////////////////////////////////////////////////////////////////// // FlushFrame: force rendering of any batched objects @@ -1086,8 +1080,14 @@ void CRenderer::FlushFrame() oglCheck(); - // sort all the transparent stuff - g_TransparencyRenderer.Sort(); + // Prepare model renderers + m_Models.NormalFF->PrepareModels(); + m_Models.PlayerFF->PrepareModels(); + if (m_Models.NormalHWLit) + m_Models.NormalHWLit->PrepareModels(); + if (m_Models.PlayerHWLit) + m_Models.PlayerHWLit->PrepareModels(); + m_Models.Transparency->PrepareModels(); if (!m_ShadowRendered) { if (m_Options.m_Shadows) { @@ -1106,7 +1106,6 @@ void CRenderer::FlushFrame() RenderPatches(); oglCheck(); - MICROLOG(L"render models"); RenderModels(); oglCheck(); @@ -1118,13 +1117,9 @@ void CRenderer::FlushFrame() } m_ShadowRendered=true; - MICROLOG(L"render player models"); - g_PlayerRenderer.Render(); - oglCheck(); - // call on the transparency renderer to render all the transparent stuff MICROLOG(L"render transparent"); - g_TransparencyRenderer.Render(); + RenderTransparentModels(); oglCheck(); // render water (note: we're assuming there's no transparent stuff over water... @@ -1136,11 +1131,17 @@ void CRenderer::FlushFrame() // empty lists MICROLOG(L"empty lists"); - g_TransparencyRenderer.Clear(); - g_PlayerRenderer.Clear(); CPatchRData::ClearSubmissions(); - CModelRData::ClearSubmissions(); m_VisiblePatches.clear(); + + // Finish model renderers + m_Models.NormalFF->EndFrame(); + m_Models.PlayerFF->EndFrame(); + if (m_Models.NormalHWLit) + m_Models.NormalHWLit->EndFrame(); + if (m_Models.PlayerHWLit) + m_Models.PlayerHWLit->EndFrame(); + m_Models.Transparency->EndFrame(); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1199,7 +1200,24 @@ void CRenderer::Submit(CModel* model) m_ShadowBound+=model->GetBounds(); } - CModelRData::Submit(model); + if (model->GetMaterial().IsPlayer()) + { + if (m_Options.m_RenderPath == RP_VERTEXSHADER) + m_Models.PlayerHWLit->Submit(model); + else + m_Models.PlayerFF->Submit(model); + } + else if(model->GetMaterial().UsesAlpha()) + { + m_Models.Transparency->Submit(model); + } + else + { + if (m_Options.m_RenderPath == RP_VERTEXSHADER) + m_Models.NormalHWLit->Submit(model); + else + m_Models.NormalFF->Submit(model); + } } void CRenderer::Submit(CSprite* UNUSED(sprite)) @@ -1524,9 +1542,40 @@ void CRenderer::UnloadWaterTextures() /////////////////////////////////////////////////////////////////////////////////////////////////// // Scripting Interface +jsval CRenderer::JSI_GetFastPlayerColor(JSContext*) +{ + return ToJSVal(m_FastPlayerColor); +} + +void CRenderer::JSI_SetFastPlayerColor(JSContext* ctx, jsval newval) +{ + bool fast; + + if (!ToPrimitive(ctx, newval, fast)) + return; + + SetFastPlayerColor(fast); +} + +jsval CRenderer::JSI_GetRenderPath(JSContext*) +{ + return ToJSVal(GetRenderPathName(m_Options.m_RenderPath)); +} + +void CRenderer::JSI_SetRenderPath(JSContext* ctx, jsval newval) +{ + CStr name; + + if (!ToPrimitive(ctx, newval, name)) + return; + + SetRenderPath(GetRenderPathByName(name)); +} + void CRenderer::ScriptingInit() { - AddProperty(L"fastPlayerColor", &CRenderer::m_FastPlayerColor); + AddProperty(L"fastPlayerColor", &CRenderer::JSI_GetFastPlayerColor, &CRenderer::JSI_SetFastPlayerColor); + AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath); CJSObject::ScriptingInit("Renderer"); } diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h index 84bc5e1d44..c1631de757 100755 --- a/source/renderer/Renderer.h +++ b/source/renderer/Renderer.h @@ -18,7 +18,6 @@ #include "Camera.h" #include "Frustum.h" #include "PatchRData.h" -#include "ModelRData.h" #include "SHCoeffs.h" #include "Terrain.h" #include "Singleton.h" @@ -26,6 +25,8 @@ #include "scripting/ScriptableObject.h" +#include "renderer/ModelRenderer.h" + // necessary declarations class CCamera; class CPatch; @@ -33,9 +34,11 @@ class CSprite; class CParticleSys; class COverlay; class CMaterial; +class CModel; class CLightEnv; class CTexture; class CTerrain; + class RenderPathVertexShader; @@ -148,6 +151,15 @@ public: size_t m_BlendSplats; }; + // renderer options + struct Options { + bool m_NoVBO; + bool m_Shadows; + RGBAColor m_ShadowColor; + float m_LodBias; + RenderPath m_RenderPath; + } m_Options; + public: // constructor, destructor CRenderer(); @@ -263,15 +275,32 @@ public: // return the current light environment const CLightEnv &GetLightEnv() { return *m_LightEnv; } + + /** + * SetFastPlayerColor: Tell the renderer which path to take for + * player colored models. Both paths should provide the same visual + * quality, however the slow path runs on older hardware using multi-pass. + * + * @param fast true if the fast path should be used from now on. If fast + * is true but the OpenGL implementation does not support it, a warning + * is printed and the slow path is used instead. + */ + void SetFastPlayerColor(bool fast); + protected: friend class CVertexBuffer; friend class CPatchRData; - friend class CModelRData; - friend class CTransparencyRenderer; - friend class CPlayerRenderer; + friend class FixedFunctionModelRenderer; + friend class ModelRenderer; + friend class TransparencyRenderer; friend class RenderPathVertexShader; + friend class HWLightingModelRenderer; // scripting + jsval JSI_GetFastPlayerColor(JSContext*); + void JSI_SetFastPlayerColor(JSContext* ctx, jsval newval); + jsval JSI_GetRenderPath(JSContext*); + void JSI_SetRenderPath(JSContext* ctx, jsval newval); static void ScriptingInit(); // patch rendering stuff @@ -280,8 +309,8 @@ protected: void RenderWater(); // model rendering stuff - void RenderModelSubmissions(); void RenderModels(); + void RenderTransparentModels(); // shadow rendering stuff void CreateShadowMap(); @@ -293,11 +322,6 @@ protected: void CalcShadowMatrices(); void CalcShadowBounds(CBound& bounds); - // render path stuff - bool InitRenderPathVertexShader(); - void ShutdownRenderPathVertexShader(); - void InitRenderPath(); - // RENDERER DATA: // view width int m_Width; @@ -350,14 +374,6 @@ protected: bool m_GenerateMipmaps; bool m_VertexShader; } m_Caps; - // renderer options - struct Options { - bool m_NoVBO; - bool m_Shadows; - RGBAColor m_ShadowColor; - float m_LodBias; - RenderPath m_RenderPath; - } m_Options; // build card cap bits void EnumCaps(); // per-frame renderer stats @@ -374,6 +390,22 @@ protected: // State used by LoadWaterTextures with progressive loading uint cur_loading_water_tex; + + // Various model renderers + struct Models { + ModelRenderer* NormalFF; + ModelRenderer* PlayerFF; + ModelRenderer* NormalHWLit; + ModelRenderer* PlayerHWLit; + ModelRenderer* Transparency; + + RenderModifierPtr ModWireframe; + RenderModifierPtr ModPlain; + RenderModifierPtr ModPlayer; + RenderModifierPtr ModSolidColor; + RenderModifierPtr ModTransparent; + RenderModifierPtr ModTransparentShadow; + } m_Models; }; diff --git a/source/renderer/TransparencyRenderer.cpp b/source/renderer/TransparencyRenderer.cpp index 7849059bf3..e8f1f96857 100755 --- a/source/renderer/TransparencyRenderer.cpp +++ b/source/renderer/TransparencyRenderer.cpp @@ -1,166 +1,493 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Name: TransparencyRenderer.cpp -// Author: Rich Cross -// Contact: rich@wildfiregames.com -// -/////////////////////////////////////////////////////////////////////////////// +/** + * ========================================================================= + * File : TransparencyRenderer.h + * Project : Pyrogenesis + * Description : ModelRenderer implementation that sorts polygons based + * : on distance from viewer, for transparency rendering. + * + * @author Rich Cross + * @author Nicolai Hähnle + * ========================================================================= + */ #include "precompiled.h" #include -#include "Renderer.h" -#include "TransparencyRenderer.h" -#include "Model.h" -#include "Profile.h" +#include -CTransparencyRenderer g_TransparencyRenderer; +#include "ogl.h" +#include "MathUtil.h" +#include "Vector3D.h" +#include "Vector4D.h" + +#include "graphics/Model.h" +#include "graphics/ModelDef.h" + +#include "ps/Profile.h" + +#include "renderer/Renderer.h" +#include "renderer/TransparencyRenderer.h" +#include "renderer/VertexArray.h" /////////////////////////////////////////////////////////////////////////////////////////////////// -// SortObjectsByDist: sorting class used for back-to-front sort of transparent passes -struct SortObjectsByDist { - typedef CTransparencyRenderer::SObject SortObj; +// TransparencyRenderer implementation + +/** + * Struct TModelDef: Per-CModelDef data for the transparency renderer + */ +struct TModelDef : public CModelDefRPrivate +{ + TModelDef(CModelDefPtr mdef); - bool operator()(const SortObj& lhs,const SortObj& rhs) { + /// Static vertex array + VertexArray m_Array; + + /// UV is static + VertexArray::Attribute m_UV; +}; + +TModelDef::TModelDef(CModelDefPtr mdef) + : m_Array(false) +{ + m_UV.type = GL_FLOAT; + m_UV.elems = 2; + m_Array.AddAttribute(&m_UV); + + m_Array.SetNumVertices(mdef->GetNumVertices()); + m_Array.Layout(); + + VertexArrayIterator UVit = m_UV.GetIterator(); + + ModelRenderer::BuildUV(mdef, UVit); + + m_Array.Upload(); + m_Array.FreeBackingStore(); +} + + +/** + * Struct TModel: Per-CModel data for the transparency renderer + */ +struct TModel : public CModelRData +{ + TModel(TransparencyRendererInternals* tri, CModel* model); + + /** + * BackToFrontIndexSort: Sort polygons by distance to camera for + * transparency rendering and fill the indices array appropriately. + * + * @param worldToCam World to camera coordinate space transform + * + * @return Square of the estimated distance to the nearest triangle. + */ + float BackToFrontIndexSort(const CMatrix3D& objToCam); + + /// Dynamic per-CModel vertex array + VertexArray m_Array; + + /// Position and lighting are recalculated on CPU every frame + VertexArray::Attribute m_Position; + VertexArray::Attribute m_Color; + + /// Indices array (sorted on CPU based on distance to camera) + u16* m_Indices; +}; + +TModel::TModel(TransparencyRendererInternals* tri, CModel* model) + : CModelRData(tri, model), m_Array(true) +{ + CModelDefPtr mdef = model->GetModelDef(); + + m_Position.type = GL_FLOAT; + m_Position.elems = 3; + m_Array.AddAttribute(&m_Position); + + m_Color.type = GL_UNSIGNED_BYTE; + m_Color.elems = 4; + m_Array.AddAttribute(&m_Color); + + m_Array.SetNumVertices(mdef->GetNumVertices()); + m_Array.Layout(); + + m_Indices = new u16[mdef->GetNumFaces()*3]; +} + + +typedef std::pair IntFloatPair; + +struct SortFacesByDist { + bool operator()(const IntFloatPair& lhs,const IntFloatPair& rhs) { + return lhs.second>rhs.second ? true : false; + } +}; + +float TModel::BackToFrontIndexSort(const CMatrix3D& worldToCam) +{ + static std::vector IndexSorter; + + CModelDefPtr mdef = GetModel()->GetModelDef(); + size_t numFaces = mdef->GetNumFaces(); + const SModelFace* faces = mdef->GetFaces(); + + if (IndexSorter.size() < numFaces) + IndexSorter.resize(numFaces); + + VertexArrayIterator Position = m_Position.GetIterator(); + CVector3D tmpvtx; + + for(size_t i = 0; i < numFaces; ++i) + { + tmpvtx = Position[faces[i].m_Verts[0]]; + tmpvtx += Position[faces[i].m_Verts[1]]; + tmpvtx += Position[faces[i].m_Verts[2]]; + tmpvtx *= 1.0f/3.0f; + + tmpvtx = worldToCam.Transform(tmpvtx); + float distsqrd = SQR(tmpvtx.X)+SQR(tmpvtx.Y)+SQR(tmpvtx.Z); + + IndexSorter[i].first = i; + IndexSorter[i].second = distsqrd; + } + + std::sort(IndexSorter.begin(),IndexSorter.begin()+numFaces,SortFacesByDist()); + + // now build index list + u32 idxidx = 0; + for (size_t i = 0; i < numFaces; ++i) { + const SModelFace& face = faces[IndexSorter[i].first]; + m_Indices[idxidx++] = (u16)(face.m_Verts[0]); + m_Indices[idxidx++] = (u16)(face.m_Verts[1]); + m_Indices[idxidx++] = (u16)(face.m_Verts[2]); + } + + return IndexSorter[0].second; +} + + +/** + * Struct SObject: Pair of model and camera distance. + */ +struct SObject +{ + /// the transparent model + TModel* m_Model; + + /// sqrd distance from camera to centre of nearest triangle + float m_Dist; + + SObject(TModel* tmdl) : m_Model(tmdl), m_Dist(0) { } +}; + + +/** + * Struct TransparencyRendererInternals: Internal data structure of TransparencyRenderer + */ +struct TransparencyRendererInternals +{ + /// List of submitted models. + std::vector objects; + + /// Scratch space for normal vector calculation + std::vector normals; +}; + + +// Construction / Destruction +TransparencyRenderer::TransparencyRenderer() +{ + m = new TransparencyRendererInternals; +} + +TransparencyRenderer::~TransparencyRenderer() +{ + delete m; +} + +// Submit a model: Create, but don't fill in, our own Model and ModelDef structures +void TransparencyRenderer::Submit(CModel* model) +{ + CModelRData* rdata = (CModelRData*)model->GetRenderData(); + TModel* tmdl; + + if (rdata && rdata->GetKey() == m) + { + tmdl = (TModel*)rdata; + } + else + { + CModelDefPtr mdef = model->GetModelDef(); + TModelDef* tmdef = (TModelDef*)mdef->GetRenderData(m); + + if (!tmdef) + { + tmdef = new TModelDef(mdef); + mdef->SetRenderData(m, tmdef); + } + + tmdl = new TModel(m, model); + rdata = tmdl; + model->SetRenderData(rdata); + model->SetDirty(~0); + g_Renderer.LoadTexture(model->GetTexture(), GL_CLAMP_TO_EDGE); + } + + m->objects.push_back(tmdl); +} + + +// Transform and sort all models +struct SortObjectsByDist { + bool operator()(const SObject& lhs, const SObject& rhs) { return lhs.m_Dist>rhs.m_Dist? true : false; } }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Sort: coarsely sort submitted objects in back to front manner -void CTransparencyRenderer::Sort() +void TransparencyRenderer::PrepareModels() { - PROFILE( "sorting transparent" ); - std::sort(m_Objects.begin(),m_Objects.end(),SortObjectsByDist()); + CMatrix3D worldToCam; + + g_Renderer.m_Camera.m_Orientation.GetInverse(worldToCam); + + for(std::vector::iterator it = m->objects.begin(); it != m->objects.end(); ++it) + { + TModel* tmdl = it->m_Model; + CModel* model = tmdl->GetModel(); + + debug_assert(model->GetRenderData() == tmdl); + + if (tmdl->m_UpdateFlags & RENDERDATA_UPDATE_VERTICES) + { + CModelDefPtr mdef = model->GetModelDef(); + size_t numVertices = mdef->GetNumVertices(); + + // build vertices + if (m->normals.size() < numVertices) + m->normals.resize(numVertices); + + VertexArrayIterator Position = tmdl->m_Position.GetIterator(); + VertexArrayIterator Normal = VertexArrayIterator((char*)&m->normals[0], sizeof(CVector3D)); + + BuildPositionAndNormals(model, Position, Normal); + + VertexArrayIterator Color = tmdl->m_Color.GetIterator(); + + BuildColor4ub(model, Normal, Color); + + // upload everything to vertex buffer + tmdl->m_Array.Upload(); + } + + // resort model indices from back to front, according to camera position - and store + // the returned sqrd distance to the centre of the nearest triangle + PROFILE_START( "sorting transparent" ); + it->m_Dist = tmdl->BackToFrontIndexSort(worldToCam); + PROFILE_END( "sorting transparent" ); + } + + PROFILE_START( "sorting transparent" ); + std::sort(m->objects.begin(), m->objects.end(), SortObjectsByDist()); + PROFILE_END( "sorting transparent" ); } -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Render: render all deferred passes; call Sort before using to ensure passes -// are drawn in correct order -void CTransparencyRenderer::Render() + +// Render all models in order +void TransparencyRenderer::EndFrame() { - PROFILE( "render transparent models" ); + m->objects.clear(); +} - if (m_Objects.size()==0) return; - // switch on wireframe if we need it - if (g_Renderer.m_ModelRenderMode==WIREFRAME) { - glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); - } +// Return whether models have been submitted this frame +bool TransparencyRenderer::HaveSubmissions() +{ + return m->objects.size() != 0; +} - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - // just pass through texture's alpha - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER,0.975f); - - // render everything with color writes off to setup depth buffer correctly - glColorMask(0,0,0,0); - CModelRData::SetupRender(STREAM_POS|STREAM_UV0); - RenderObjectsStreams(STREAM_POS|STREAM_UV0); - CModelRData::FinishRender(STREAM_POS|STREAM_UV0); - glColorMask(1,1,1,1); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - - glDepthMask(0); - glAlphaFunc(GL_GREATER,0); - - // setup texture environment to modulate diffuse color with texture color - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - - // Set the proper LOD bias - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); - - CModelRData::SetupRender(STREAM_POS|STREAM_COLOR|STREAM_UV0); - RenderObjectsStreams(STREAM_POS|STREAM_COLOR|STREAM_UV0); - CModelRData::FinishRender(STREAM_POS|STREAM_COLOR|STREAM_UV0); - - glDisable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glDepthMask(1); - - // switch off client states - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - if (g_Renderer.m_ModelRenderMode==WIREFRAME) { - // switch wireframe off again - glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); - } else if (g_Renderer.m_ModelRenderMode==EDGED_FACES) { - // edged faces: need to make a second pass over the data: - // first switch on wireframe - glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); +// Render submitted models (filtered by flags) using the given modifier +void TransparencyRenderer::Render(RenderModifierPtr modifier, u32 flags) +{ + uint pass = 0; + + if (m->objects.size() == 0) + return; + + glEnableClientState(GL_VERTEX_ARRAY); + + do + { + u32 streamflags = modifier->BeginPass(pass); + CModelDefPtr lastmdef; + CTexture* lasttex = 0; - // setup some renderstate .. + if (streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if (streamflags & STREAM_COLOR) glEnableClientState(GL_COLOR_ARRAY); + + for(std::vector::iterator it = m->objects.begin(); it != m->objects.end(); ++it) + { + if (flags & !(it->m_Model->GetModel()->GetFlags()&flags)) + continue; + + TModel* tmdl = it->m_Model; + CModel* mdl = tmdl->GetModel(); + CModelDefPtr mdef = mdl->GetModelDef(); + CTexture* tex = mdl->GetTexture(); + + // Prepare per-CModelDef data if changed + if (mdef != lastmdef) + { + TModelDef* tmdef = (TModelDef*)mdef->GetRenderData(m); + + if (streamflags & STREAM_UV0) + { + u8* base = tmdef->m_Array.Bind(); + GLsizei stride = (GLsizei)tmdef->m_Array.GetStride(); + + glTexCoordPointer(2, GL_FLOAT, stride, base + tmdef->m_UV.offset); + } + + lastmdef = mdef; + } + + // Prepare necessary RenderModifier stuff + if (tex != lasttex) + { + modifier->PrepareTexture(pass, tex); + lasttex = tex; + } + + modifier->PrepareModel(pass, mdl); + + // Setup per-CModel arrays + u8* base = tmdl->m_Array.Bind(); + GLsizei stride = (GLsizei)tmdl->m_Array.GetStride(); + + glVertexPointer(3, GL_FLOAT, stride, base + tmdl->m_Position.offset); + if (streamflags & STREAM_COLOR) + glColorPointer(3, tmdl->m_Color.type, stride, base + tmdl->m_Color.offset); + + // render the lot + size_t numFaces = mdef->GetNumFaces(); + glDrawRangeElementsEXT(GL_TRIANGLES, 0, mdef->GetNumVertices(), + numFaces*3, GL_UNSIGNED_SHORT, tmdl->m_Indices); + + // bump stats + g_Renderer.m_Stats.m_DrawCalls++; + g_Renderer.m_Stats.m_ModelTris += numFaces; + } + + if (streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (streamflags & STREAM_COLOR) glDisableClientState(GL_COLOR_ARRAY); + } while(!modifier->EndPass(pass++)); + + glDisableClientState(GL_VERTEX_ARRAY); +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// TransparentRenderModifier implementation + +TransparentRenderModifier::TransparentRenderModifier() +{ +} + +TransparentRenderModifier::~TransparentRenderModifier() +{ +} + +u32 TransparentRenderModifier::BeginPass(uint pass) +{ + if (pass == 0) + { + // First pass: Put down Z for opaque parts of the model, + // don't touch the color buffer. + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + + // just pass through texture's alpha + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + + // Set the proper LOD bias + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER,0.975f); + + // render everything with color writes off to setup depth buffer correctly + glColorMask(0,0,0,0); + + return STREAM_POS|STREAM_UV0; + } + else + { + // Second pass: Put down color, disable Z write + glColorMask(1,1,1,1); + glDepthMask(0); - g_Renderer.SetTexture(0,0); - glColor4f(1,1,1,0.75f); - glLineWidth(1.0f); + + // setup texture environment to modulate diffuse color with texture color + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + + glAlphaFunc(GL_GREATER,0); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - - // render each model - CModelRData::SetupRender(STREAM_POS); - RenderObjectsStreams(STREAM_POS); - CModelRData::FinishRender(STREAM_POS); - - // .. and switch off the client states - glDisableClientState(GL_VERTEX_ARRAY); - - // .. and restore the renderstates - glDisable(GL_BLEND); - glDepthMask(1); - - // restore fill mode, and we're done - glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + + // Set the proper LOD bias + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); + + return STREAM_POS|STREAM_COLOR|STREAM_UV0; } } -void CTransparencyRenderer::Clear() +bool TransparentRenderModifier::EndPass(uint pass) { - // all transparent objects rendered; release them - m_Objects.clear(); -} - -void CTransparencyRenderer::Add(CModel* model) -{ - // resize array, get last object in list - m_Objects.resize(m_Objects.size()+1); + if (pass == 0) + return false; // multi-pass - SObject& obj=m_Objects.back(); - obj.m_Model=model; - - // build transform from object to camera space - CMatrix3D objToCam,invcam; - g_Renderer.m_Camera.m_Orientation.GetInverse(objToCam); - objToCam*=model->GetTransform(); - - // resort model indices from back to front, according to camera position - and store - // the returned sqrd distance to the centre of the nearest triangle - CModelRData* modeldata=(CModelRData*) model->GetRenderData(); - obj.m_Dist=modeldata->BackToFrontIndexSort(objToCam); + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDepthMask(1); + + return true; } -void CTransparencyRenderer::RenderShadows() +void TransparentRenderModifier::PrepareTexture(uint pass, CTexture* texture) { - if (m_Objects.size()==0) return; + g_Renderer.SetTexture(0, texture); +} +void TransparentRenderModifier::PrepareModel(uint pass, CModel* model) +{ + // No per-model setup nececssary +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// TransparentShadowRenderModifier implementation + +TransparentShadowRenderModifier::TransparentShadowRenderModifier() +{ +} + +TransparentShadowRenderModifier::~TransparentShadowRenderModifier() +{ +} + +u32 TransparentShadowRenderModifier::BeginPass(uint pass) +{ + debug_assert(pass == 0); + glDepthMask(0); glEnable(GL_BLEND); @@ -177,22 +504,24 @@ void CTransparencyRenderer::RenderShadows() // Set the proper LOD bias glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); - CModelRData::SetupRender(STREAM_POS|STREAM_UV0); - RenderObjectsStreams(STREAM_POS|STREAM_UV0,MODELFLAG_CASTSHADOWS); - CModelRData::FinishRender(STREAM_POS|STREAM_UV0); + return STREAM_POS|STREAM_UV0; +} +bool TransparentShadowRenderModifier::EndPass(uint pass) +{ glDepthMask(1); glDisable(GL_BLEND); + + return true; } -/////////////////////////////////////////////////////////////////////////////////////////////// -// RenderObjectsStreams: render given streams on all objects -void CTransparencyRenderer::RenderObjectsStreams(u32 streamflags,u32 mflags) +void TransparentShadowRenderModifier::PrepareTexture(uint pass, CTexture* texture) { - for (uint i=0;iGetFlags() & mflags)) { - CModelRData* modeldata=(CModelRData*) m_Objects[i].m_Model->GetRenderData(); - modeldata->RenderStreams(streamflags); - } - } + g_Renderer.SetTexture(0, texture); } + +void TransparentShadowRenderModifier::PrepareModel(uint pass, CModel* model) +{ + // No per-model setup nececssary +} + diff --git a/source/renderer/TransparencyRenderer.h b/source/renderer/TransparencyRenderer.h index cb6991c142..b31b61e0b1 100755 --- a/source/renderer/TransparencyRenderer.h +++ b/source/renderer/TransparencyRenderer.h @@ -1,47 +1,101 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Name: TransparencyRenderer.h -// Author: Rich Cross -// Contact: rich@wildfiregames.com -// -/////////////////////////////////////////////////////////////////////////////// +/** + * ========================================================================= + * File : TransparencyRenderer.h + * Project : Pyrogenesis + * Description : ModelRenderer implementation that sorts polygons based + * : on distance from viewer, for transparency rendering. + * + * @author Rich Cross + * @author Nicolai Hähnle + * ========================================================================= + */ #ifndef __TRANSPARENCYRENDERER_H #define __TRANSPARENCYRENDERER_H -#include +#include "renderer/ModelRenderer.h" +#include "renderer/RenderModifiers.h" -class CModel; -class CTransparencyRenderer +struct TransparencyRendererInternals; + +/** + * Class TransparencyRenderer: Render transparent models that require + * Z-based sorting. + * + * This is a lot less efficient than batched model + * renderers, so it should be used only when really necessary for + * translucent models. Models that use the alpha channel for masking + * should probably use a batched model renderer and an appropriate + * RenderModifier. + * + * Use this renderer together with TransparentRenderModifier and + * TransparentShadowRenderModifier. + */ +class TransparencyRenderer : public ModelRenderer { public: - struct SObject { - // the transparent model - CModel* m_Model; - // sqrd distance from camera to centre of nearest triangle - float m_Dist; - }; - -public: - // add object to render in deferred transparency pass - void Add(CModel* model); - // render all deferred objects - void Render(); - // render shadows from all deferred objects - void RenderShadows(); - // coarsely sort submitted objects in back to front manner - void Sort(); - // empty object list - void Clear(); + TransparencyRenderer(); + ~TransparencyRenderer(); + + // Transparency renderer implementation + void Submit(CModel* model); + void PrepareModels(); + void EndFrame(); + bool HaveSubmissions(); + + /** + * Render: Render submitted models, using the given RenderModifier to setup + * the fragment stage. + * + * preconditions : PrepareModels must be called after all models have been + * submitted and before calling Render. + * + * @param modifier The RenderModifier that specifies the fragment stage. + * @param flags If flags is 0, all submitted models are rendered. + * If flags is non-zero, only models that contain flags in their + * CModel::GetFlags() are rendered. + */ + void Render(RenderModifierPtr modifier, u32 flags); private: - // render given streams on all objects - void RenderObjectsStreams(u32 streamflags,u32 mflags=0); - // list of transparent objects to render - std::vector m_Objects; + TransparencyRendererInternals* m; }; -extern CTransparencyRenderer g_TransparencyRenderer; + +/** + * Class TransparentRenderModifier: Modifier for transparent models, + * including alpha blending and lighting. + */ +class TransparentRenderModifier : public RenderModifier +{ +public: + TransparentRenderModifier(); + ~TransparentRenderModifier(); + + // Implementation + u32 BeginPass(uint pass); + bool EndPass(uint pass); + void PrepareTexture(uint pass, CTexture* texture); + void PrepareModel(uint pass, CModel* model); + +}; + +/** + * Class TransparentShadowRenderModifier: Use for shadow rendering of + * transparent models. + */ +class TransparentShadowRenderModifier : public RenderModifier +{ +public: + TransparentShadowRenderModifier(); + ~TransparentShadowRenderModifier(); + + // Implementation + u32 BeginPass(uint pass); + bool EndPass(uint pass); + void PrepareTexture(uint pass, CTexture* texture); + void PrepareModel(uint pass, CModel* model); +}; #endif