1
0
forked from 0ad/0ad

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.
This commit is contained in:
prefect 2005-10-25 01:43:07 +00:00
parent d17e48ad99
commit 82ee484821
18 changed files with 2750 additions and 1318 deletions

View File

@ -0,0 +1,264 @@
/**
* =========================================================================
* File : FixedFunctionModelRenderer.cpp
* Project : Pyrogenesis
* Description : Implementation of FixedFunctionModelRenderer
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#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<float[2]> UVit = m_UV.GetIterator<float[2]>();
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<CVector3D> 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<CVector3D> Position = ffmodel->m_Position.GetIterator<CVector3D>();
VertexArrayIterator<CVector3D> Normal = VertexArrayIterator<CVector3D>((char*)&m->normals[0], sizeof(CVector3D));
BuildPositionAndNormals(model, Position, Normal);
VertexArrayIterator<SColor4ub> Color = ffmodel->m_Color.GetIterator<SColor4ub>();
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;
}

View File

@ -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 <nicolai@wildfiregames.com>
* =========================================================================
*/
#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

View File

@ -0,0 +1,271 @@
/**
* =========================================================================
* File : HWLightingModelRenderer.cpp
* Project : Pyrogenesis
* Description : Implementation of HWLightingModelRenderer
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#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<float[2]> UVit = hwlmodel->m_UV.GetIterator<float[2]>();
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<CVector3D> Position = hwlmodel->m_Position.GetIterator<CVector3D>();
VertexArrayIterator<CVector3D> Normal = hwlmodel->m_Normal.GetIterator<CVector3D>();
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;
}

View File

@ -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 <nicolai@wildfiregames.com>
* =========================================================================
*/
#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

View File

@ -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<float[2]> UVit = m_UV.GetIterator<float[2]>();
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;
}

View File

@ -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<CModelRData*> m_SubmissionModels;
static CModelDefRData* m_Submissions;
};
#endif // __MODELDEFRDATA_H__

View File

@ -1,519 +0,0 @@
#include "precompiled.h"
#include <algorithm>
#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; j<mdef->GetNumFaces(); 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; i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff; i++) {
const CMatrix3D& m=matrices[blend.m_Bone[i]];
m.Transform(vertex.m_Coords,tmp);
result+=tmp*blend.m_Weight[i];
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the vertex normal using it's blend data and given bone matrices
static void SkinNormal(const SModelVertex& vertex,const CMatrix3D* invmatrices,CVector3D& result)
{
CVector3D tmp;
const SVertexBlend& blend=vertex.m_Blend;
// must have at least one valid bone if we're using SkinNormal
debug_assert(blend.m_Bone[0]!=0xff);
const CMatrix3D& m=invmatrices[blend.m_Bone[0]];
m.RotateTransposed(vertex.m_Norm,result);
result*=blend.m_Weight[0];
for (u32 i=1; i<SVertexBlend::SIZE && vertex.m_Blend.m_Bone[i]!=0xff; i++) {
const CMatrix3D& m=invmatrices[blend.m_Bone[i]];
m.RotateTransposed(vertex.m_Norm,tmp);
result+=tmp*blend.m_Weight[i];
}
}
void CModelRData::BuildStaticVertices()
{
if (m_UV.type)
{
CModelDefPtr mdef = m_Model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
SModelVertex* vertices = mdef->GetVertices();
VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>();
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<CVector3D> Position = m_Position.GetIterator<CVector3D>();
VertexArrayIterator<CVector3D> Normal;
if (m_Normal.type)
{
Normal = m_Normal.GetIterator<CVector3D>();
}
else
{
if (!m_TempNormals)
m_TempNormals = new CVector3D[numVertices];
Normal = VertexArrayIterator<CVector3D>((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; j<numVertices; j++) {
SkinPoint(vertices[j],bonematrices,Position[j]);
SkinNormal(vertices[j],invbonematrices,Normal[j]);
}
} else {
PROFILE( "software transform" );
// just copy regular positions, transform normals to world space
const CMatrix3D& transform=m_Model->GetTransform();
const CMatrix3D& invtransform=m_Model->GetInvTransform();
for (uint j=0; j<numVertices; j++) {
transform.Transform(vertices[j].m_Coords,Position[j]);
invtransform.RotateTransposed(vertices[j].m_Norm,Normal[j]);
}
}
if (m_Color.type)
{
PROFILE( "lighting vertices" );
// now fill in vertex colour data
VertexArrayIterator<SColor3ub> Color = m_Color.GetIterator<SColor3ub>();
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; j<numVertices; j++) {
shcoeffs.Evaluate(Normal[j], tempcolor, shadingcolor);
*(u32*)&Color[j] = ConvertRGBColorTo4ub(tempcolor);
}
}
// upload everything to vertex buffer
m_DynamicArray.Upload();
}
// prepare for rendering of models
void CModelRData::SetupRender(u32 streamflags)
{
glEnableClientState(GL_VERTEX_ARRAY);
if (streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (streamflags & STREAM_COLOR)
{
if (g_Renderer.GetRenderPath() == CRenderer::RP_VERTEXSHADER)
{
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);
}
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<int,float> IntFloatPair;
static std::vector<IntFloatPair> 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;i<numFaces;i++)
{
osvtx=vtxs[facePtr->m_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 (distsqrd<mindist) mindist=distsqrd;
IndexSorter.push_back(IntFloatPair(i,distsqrd));
facePtr++;
}
std::sort(IndexSorter.begin(),IndexSorter.end(),SortFacesByDist());
// now build index list
u32 indices=0;
for (i=0;i<numFaces;i++) {
SModelFace& face=faces[IndexSorter[i].first];
m_Indices[indices++]=(u16)(face.m_Verts[0]);
m_Indices[indices++]=(u16)(face.m_Verts[1]);
m_Indices[indices++]=(u16)(face.m_Verts[2]);
}
// clear list for next call
IndexSorter.clear();
return mindist;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// RenderModels: render all submitted models; assumes necessary client states already enabled,
// and texture environment already setup as required
// Must be bracketed by calls to CModelRData::SetupRender/FinishRender
void CModelRData::RenderModels(u32 streamflags, u32 flags)
{
for(CModelDefRData* mdefdata = CModelDefRData::m_Submissions;
mdefdata;
mdefdata = mdefdata->m_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;
}

View File

@ -1,77 +0,0 @@
#ifndef _MODELRDATA_H
#define _MODELRDATA_H
#include <vector>
#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

View File

@ -0,0 +1,424 @@
/**
* =========================================================================
* File : ModelRenderer.cpp
* Project : Pyrogenesis
* Description : Implementation of ModelRenderer and BatchModelRenderer
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#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<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff; i++) {
const CMatrix3D& m=matrices[blend.m_Bone[i]];
m.Transform(vertex.m_Coords,tmp);
result+=tmp*blend.m_Weight[i];
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the vertex normal using it's blend data and given bone matrices
static void SkinNormal(const SModelVertex& vertex,const CMatrix3D* invmatrices,CVector3D& result)
{
CVector3D tmp;
const SVertexBlend& blend=vertex.m_Blend;
// must have at least one valid bone if we're using SkinNormal
debug_assert(blend.m_Bone[0]!=0xff);
const CMatrix3D& m=invmatrices[blend.m_Bone[0]];
m.RotateTransposed(vertex.m_Norm,result);
result*=blend.m_Weight[0];
for (u32 i=1; i<SVertexBlend::SIZE && vertex.m_Blend.m_Bone[i]!=0xff; i++) {
const CMatrix3D& m=invmatrices[blend.m_Bone[i]];
m.RotateTransposed(vertex.m_Norm,tmp);
result+=tmp*blend.m_Weight[i];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// ModelRenderer implementation
// Helper function to transform position and normal vectors into world-space.
void ModelRenderer::BuildPositionAndNormals(
CModel* model,
VertexArrayIterator<CVector3D> Position,
VertexArrayIterator<CVector3D> 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; j<numVertices; j++)
{
SkinPoint(vertices[j],bonematrices,Position[j]);
SkinNormal(vertices[j],invbonematrices,Normal[j]);
}
}
else
{
PROFILE( "software transform" );
// just copy regular positions, transform normals to world space
const CMatrix3D& transform = model->GetTransform();
const CMatrix3D& invtransform = model->GetInvTransform();
for (uint j=0; j<numVertices; j++)
{
transform.Transform(vertices[j].m_Coords,Position[j]);
invtransform.RotateTransposed(vertices[j].m_Norm,Normal[j]);
}
}
}
// Helper function for lighting
void ModelRenderer::BuildColor4ub(
CModel* model,
VertexArrayIterator<CVector3D> Normal,
VertexArrayIterator<SColor4ub> 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<numVertices; j++)
{
shcoeffs.Evaluate(Normal[j], tempcolor, shadingcolor);
*(u32*)&Color[j] = ConvertRGBColorTo4ub(tempcolor);
}
}
// Copy UV coordinates
void ModelRenderer::BuildUV(
CModelDefPtr mdef,
VertexArrayIterator<float[2]> 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<BMRModelData*> 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);
}
}
}
}

View File

@ -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 <nicolai@wildfiregames.com>
* =========================================================================
*/
#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<RenderModifier> 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<CVector3D> Position,
VertexArrayIterator<CVector3D> 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<CVector3D> Normal,
VertexArrayIterator<SColor4ub> 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<float[2]> 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

View File

@ -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 <JohnMMena@hotmail.com>
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#include "precompiled.h"
#include <algorithm>
#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;i<m_Objects.size();++i) {
if (!mflags || (m_Objects[i]->GetFlags() & 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);
}
}

View File

@ -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 <JohnMMena@hotmail.com>
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#ifndef __PLAYERRENDERER_H
#define __PLAYERRENDERER_H
#include <vector>
#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<CModel*> 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

View File

@ -0,0 +1,179 @@
/**
* =========================================================================
* File : RenderModifiers.cpp
* Project : Pyrogenesis
* Description : Implementation of common RenderModifiers
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#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)
{
}

View File

@ -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 <nicolai@wildfiregames.com>
* =========================================================================
*/
#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

View File

@ -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;i<MaxTextureUnits;i++) {
m_ActiveTextures[i]=0;
}
// Must query card capabilities before creating renderers that depend
// on card capabilities.
EnumCaps();
m_VertexShader = new RenderPathVertexShader;
if (!m_VertexShader->Init())
{
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()<rhs->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<CRenderer>::ScriptingInit("Renderer");
}

View File

@ -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;
};

View File

@ -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 <rich@wildfiregames.com>
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#include "precompiled.h"
#include <algorithm>
#include "Renderer.h"
#include "TransparencyRenderer.h"
#include "Model.h"
#include "Profile.h"
#include <vector>
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<float[2]> UVit = m_UV.GetIterator<float[2]>();
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<int,float> 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<IntFloatPair> IndexSorter;
CModelDefPtr mdef = GetModel()->GetModelDef();
size_t numFaces = mdef->GetNumFaces();
const SModelFace* faces = mdef->GetFaces();
if (IndexSorter.size() < numFaces)
IndexSorter.resize(numFaces);
VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
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<SObject> objects;
/// Scratch space for normal vector calculation
std::vector<CVector3D> 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<SObject>::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<CVector3D> Position = tmdl->m_Position.GetIterator<CVector3D>();
VertexArrayIterator<CVector3D> Normal = VertexArrayIterator<CVector3D>((char*)&m->normals[0], sizeof(CVector3D));
BuildPositionAndNormals(model, Position, Normal);
VertexArrayIterator<SColor4ub> Color = tmdl->m_Color.GetIterator<SColor4ub>();
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<SObject>::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;i<m_Objects.size();++i) {
if (!mflags || (m_Objects[i].m_Model->GetFlags() & 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
}

View File

@ -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 <rich@wildfiregames.com>
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#ifndef __TRANSPARENCYRENDERER_H
#define __TRANSPARENCYRENDERER_H
#include <vector>
#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<SObject> 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