1
0
forked from 0ad/0ad
0ad/source/renderer/ModelRenderer.cpp
janwas b755ddefda remove all author/modified by tags.
make include guards consistent.

This was SVN commit r5040.
2007-05-07 16:33:24 +00:00

467 lines
12 KiB
C++

/**
* =========================================================================
* File : ModelRenderer.cpp
* Project : Pyrogenesis
* Description : Implementation of ModelRenderer and BatchModelRenderer
* =========================================================================
*/
#include "precompiled.h"
#include "lib/ogl.h"
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "graphics/Color.h"
#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "renderer/ModelRenderer.h"
#include "renderer/ModelVertexRenderer.h"
#include "renderer/Renderer.h"
#include "renderer/RenderModifiers.h"
#include <boost/weak_ptr.hpp>
#define LOG_CATEGORY "graphics"
///////////////////////////////////////////////////////////////////////////////////////////////
// ModelRenderer implementation
// Helper function to copy object-space position and normal vectors into arrays.
void ModelRenderer::CopyPositionAndNormals(
CModelDefPtr mdef,
VertexArrayIterator<CVector3D> Position,
VertexArrayIterator<CVector3D> Normal)
{
size_t numVertices = mdef->GetNumVertices();
SModelVertex* vertices = mdef->GetVertices();
for(size_t j = 0; j < numVertices; ++j)
{
Position[j] = vertices[j].m_Coords;
Normal[j] = vertices[j].m_Norm;
}
}
// 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();
if (model->IsSkinned())
{
// boned model - calculate skinned vertex positions/normals
PROFILE( "skinning bones" );
// Avoid the noisy warnings that occur inside SkinPoint/SkinNormal in
// some broken situations
if (numVertices && vertices[0].m_Blend.m_Bone[0] == 0xff)
{
LOG_ONCE(ERROR, LOG_CATEGORY, "Model %s is boned with unboned animation", mdef->GetName().c_str());
return;
}
for (size_t j=0; j<numVertices; j++)
{
Position[j] = CModelDef::SkinPoint(vertices[j], model->GetAnimatedBoneMatrices(), model->GetInverseBindBoneMatrices());
Normal[j] = CModelDef::SkinNormal(vertices[j], model->GetAnimatedBoneMatrices(), model->GetInverseBindBoneMatrices());
}
}
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,
bool onlyDiffuse)
{
PROFILE( "lighting vertices" );
CModelDefPtr mdef = model->GetModelDef();
size_t numVertices = mdef->GetNumVertices();
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
CColor shadingColor = model->GetShadingColor();
RGBColor tempcolor;
if (onlyDiffuse)
{
for (uint j=0; j<numVertices; j++)
{
lightEnv.EvaluateDirect(Normal[j], tempcolor);
tempcolor.X *= shadingColor.r;
tempcolor.Y *= shadingColor.g;
tempcolor.Z *= shadingColor.b;
*(u32*)&Color[j] = ConvertRGBColorTo4ub(tempcolor);
}
}
else
{
for (uint j=0; j<numVertices; j++)
{
lightEnv.EvaluateUnit(Normal[j], tempcolor);
tempcolor.X *= shadingColor.r;
tempcolor.Y *= shadingColor.g;
tempcolor.Z *= shadingColor.b;
*(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
boost::weak_ptr<CModelDef> 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;
/// ModelVertexRenderer used for vertex transformations
ModelVertexRendererPtr vertexRenderer;
/// 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)
{
vertexRenderer->DestroyModelData(model, data);
}
void RenderAllModels(RenderModifierPtr modifier, u32 filterflags, uint pass, uint streamflags);
};
BMRModelData::~BMRModelData()
{
m_BMRI->ThunkDestroyModelData(GetModel(), m_Data);
}
// Construction/Destruction
BatchModelRenderer::BatchModelRenderer(ModelVertexRendererPtr vertexrenderer)
{
m = new BatchModelRendererInternals(this);
m->vertexRenderer = vertexrenderer;
m->phase = BMRSubmit;
m->submissions = 0;
}
BatchModelRenderer::~BatchModelRenderer()
{
delete m;
}
// Submit one model.
void BatchModelRenderer::Submit(CModel* model)
{
debug_assert(m->phase == BMRSubmit);
ogl_WarnIfError();
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 = m->vertexRenderer->CreateModelData(model);
rdata = bmrdata;
model->SetRenderData(bmrdata);
model->SetDirty(~0u);
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;
ogl_WarnIfError();
}
// 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)
{
CModel* model = bmrdata->GetModel();
debug_assert(model->GetRenderData() == bmrdata);
m->vertexRenderer->UpdateModelData(
model, 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;
}
// Render models, outer loop for multi-passing
void BatchModelRenderer::Render(RenderModifierPtr modifier, u32 flags)
{
debug_assert(m->phase == BMRRender);
if (!HaveSubmissions())
return;
uint pass = 0;
do
{
uint streamflags = modifier->BeginPass(pass);
const CMatrix3D* texturematrix = 0;
if (streamflags & STREAM_TEXGENTOUV1)
texturematrix = modifier->GetTexGenMatrix(pass);
m->vertexRenderer->BeginPass(streamflags, texturematrix);
m->RenderAllModels(modifier, flags, pass, streamflags);
m->vertexRenderer->EndPass(streamflags);
} while(!modifier->EndPass(pass++));
}
// Render one frame worth of models
void BatchModelRendererInternals::RenderAllModels(
RenderModifierPtr modifier, u32 filterflags,
uint pass, uint streamflags)
{
for(BMRModelDefTracker* mdeftracker = submissions; mdeftracker; mdeftracker = mdeftracker->m_Next)
{
vertexRenderer->PrepareModelDef(streamflags, mdeftracker->m_ModelDef.lock());
for(uint idx = 0; idx < mdeftracker->m_Slots; ++idx)
{
BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx];
modifier->PrepareTexture(pass, bmrdata->GetModel()->GetTexture());
for(; bmrdata; bmrdata = bmrdata->m_Next)
{
CModel* model = bmrdata->GetModel();
debug_assert(bmrdata->GetKey() == this);
if (filterflags && !(model->GetFlags()&filterflags))
continue;
modifier->PrepareModel(pass, model);
vertexRenderer->RenderModel(streamflags, model, bmrdata->m_Data);
}
}
}
}