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:
parent
d17e48ad99
commit
82ee484821
264
source/renderer/FixedFunctionModelRenderer.cpp
Normal file
264
source/renderer/FixedFunctionModelRenderer.cpp
Normal 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;
|
||||
}
|
||||
|
60
source/renderer/FixedFunctionModelRenderer.h
Normal file
60
source/renderer/FixedFunctionModelRenderer.h
Normal 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
|
271
source/renderer/HWLightingModelRenderer.cpp
Normal file
271
source/renderer/HWLightingModelRenderer.cpp
Normal 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;
|
||||
}
|
||||
|
75
source/renderer/HWLightingModelRenderer.h
Normal file
75
source/renderer/HWLightingModelRenderer.h
Normal 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
|
@ -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;
|
||||
}
|
@ -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__
|
@ -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;
|
||||
}
|
@ -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
|
424
source/renderer/ModelRenderer.cpp
Normal file
424
source/renderer/ModelRenderer.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
338
source/renderer/ModelRenderer.h
Normal file
338
source/renderer/ModelRenderer.h
Normal 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
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
179
source/renderer/RenderModifiers.cpp
Normal file
179
source/renderer/RenderModifiers.cpp
Normal 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)
|
||||
{
|
||||
}
|
151
source/renderer/RenderModifiers.h
Normal file
151
source/renderer/RenderModifiers.h
Normal 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
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user