1
0
forked from 0ad/0ad

Experimental GPU skinning.

Share inverse bind pose matrices between models.

This was SVN commit r11490.
This commit is contained in:
Ykkrosh 2012-04-12 15:43:59 +00:00
parent ce215cace3
commit 227f9e403f
15 changed files with 247 additions and 96 deletions

View File

@ -61,6 +61,9 @@ preferglsl = false
; Replace alpha-blending with alpha-testing, for performance experiments
forcealphatest = false
; Experimental probably-non-working GPU skinning support; requires preferglsl; use at own risk
gpuskinning = false
; Opt-in online user reporting system
userreport.url = "http://feedback.wildfiregames.com/report/upload/v1/"

View File

@ -1,4 +1,9 @@
#if USE_GPU_SKINNING
// Skinning requires GLSL 1.30 for ivec4 vertex attributes
#version 130
#else
#version 120
#endif
uniform mat4 transform;
uniform vec3 cameraPos;
@ -22,8 +27,32 @@ attribute vec3 a_vertex;
attribute vec3 a_normal;
attribute vec2 a_uv0;
#if USE_GPU_SKINNING
const int MAX_INFLUENCES = 4;
const int MAX_BONES = 64;
uniform mat4 skinBlendMatrices[MAX_BONES];
attribute ivec4 a_skinJoints;
attribute vec4 a_skinWeights;
#endif
varying vec4 debugx;
void main()
{
#if USE_GPU_SKINNING
vec3 p = vec3(0.0);
vec3 n = vec3(0.0);
for (int i = 0; i < MAX_INFLUENCES; ++i) {
int joint = a_skinJoints[i];
if (joint != 0xff) {
mat4 m = skinBlendMatrices[joint];
p += vec3(m * vec4(a_vertex, 1.0)) * a_skinWeights[i];
n += vec3(m * vec4(a_normal, 0.0)) * a_skinWeights[i];
}
}
vec4 position = instancingTransform * vec4(p, 1.0);
vec3 normal = mat3(instancingTransform) * normalize(n);
#else
#if USE_INSTANCING
vec4 position = instancingTransform * vec4(a_vertex, 1.0);
vec3 normal = mat3(instancingTransform) * a_normal;
@ -31,6 +60,7 @@ void main()
vec4 position = vec4(a_vertex, 1.0);
vec3 normal = a_normal;
#endif
#endif
gl_Position = transform * position;

View File

@ -8,6 +8,8 @@
<attrib name="a_vertex" semantics="gl_Vertex"/>
<attrib name="a_normal" semantics="gl_Normal"/>
<attrib name="a_uv0" semantics="gl_MultiTexCoord0"/>
<attrib name="a_skinJoints" semantics="CustomAttribute0" if="USE_GPU_SKINNING"/>
<attrib name="a_skinWeights" semantics="CustomAttribute1" if="USE_GPU_SKINNING"/>
</vertex>
<fragment file="glsl/model_common.fs"/>

View File

@ -4,6 +4,8 @@
<vertex file="glsl/model_common.vs">
<stream name="pos"/>
<attrib name="a_vertex" semantics="gl_Vertex"/>
<attrib name="a_skinJoints" semantics="CustomAttribute0" if="USE_GPU_SKINNING"/>
<attrib name="a_skinWeights" semantics="CustomAttribute1" if="USE_GPU_SKINNING"/>
</vertex>
<fragment file="glsl/solid.fs"/>

View File

@ -4,6 +4,8 @@
<vertex file="glsl/model_common.vs">
<stream name="pos"/>
<attrib name="a_vertex" semantics="gl_Vertex"/>
<attrib name="a_skinJoints" semantics="CustomAttribute0" if="USE_GPU_SKINNING"/>
<attrib name="a_skinWeights" semantics="CustomAttribute1" if="USE_GPU_SKINNING"/>
</vertex>
<fragment file="glsl/solid_player.fs"/>

View File

@ -6,6 +6,8 @@
<stream name="uv0"/>
<attrib name="a_vertex" semantics="gl_Vertex"/>
<attrib name="a_uv0" semantics="gl_MultiTexCoord0"/>
<attrib name="a_skinJoints" semantics="CustomAttribute0" if="USE_GPU_SKINNING"/>
<attrib name="a_skinWeights" semantics="CustomAttribute1" if="USE_GPU_SKINNING"/>
</vertex>
<fragment file="glsl/solid_tex.fs"/>

View File

@ -35,12 +35,13 @@
#include "lib/sysdep/rtl.h"
#include "ps/Profile.h"
#include "ps/CLogger.h"
#include "renderer/Renderer.h"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor
CModel::CModel(CSkeletonAnimManager& skeletonAnimManager)
: m_Flags(0), m_Anim(NULL), m_AnimTime(0),
m_BoneMatrices(NULL), m_InverseBindBoneMatrices(NULL),
m_BoneMatrices(NULL),
m_AmmoPropPoint(NULL), m_AmmoLoadedProp(0),
m_SkeletonAnimManager(skeletonAnimManager)
{
@ -58,7 +59,6 @@ CModel::~CModel()
void CModel::ReleaseData()
{
rtl_FreeAligned(m_BoneMatrices);
delete[] m_InverseBindBoneMatrices;
for (size_t i = 0; i < m_Props.size(); ++i)
delete m_Props[i].m_Model;
@ -84,21 +84,10 @@ bool CModel::InitModel(const CModelDefPtr& modeldef)
// allocate matrices for bone transformations
// (one extra matrix is used for the special case of bind-shape relative weighting)
m_BoneMatrices = (CMatrix3D*)rtl_AllocateAligned(sizeof(CMatrix3D) * (numBones + 1 + numBlends), 16);
for (size_t i = 0; i < numBones + numBlends; ++i)
for (size_t i = 0; i < numBones + 1 + numBlends; ++i)
{
m_BoneMatrices[i].SetIdentity();
}
m_InverseBindBoneMatrices = new CMatrix3D[numBones];
// store default pose until animation assigned
CBoneState* defpose = modeldef->GetBones();
for (size_t i = 0; i < numBones; ++i)
{
m_InverseBindBoneMatrices[i].SetIdentity();
m_InverseBindBoneMatrices[i].Translate(-defpose[i].m_Translation);
m_InverseBindBoneMatrices[i].Rotate(defpose[i].m_Rotation.GetInverse());
}
}
m_PositionValid = true;
@ -344,27 +333,39 @@ void CModel::ValidatePosition()
ENSURE(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());
m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime, m_BoneMatrices, !(m_Flags & MODELFLAG_NOLOOPANIMATION));
// add world-space transformation to m_BoneMatrices
const CMatrix3D& transform = GetTransform();
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
m_BoneMatrices[i].Concatenate(transform);
}
else if (m_BoneMatrices)
{
// Bones but no animation - probably a buggy actor forgot to set up the animation,
// so just render it in its bind pose
const CMatrix3D& transform = GetTransform();
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
{
m_BoneMatrices[i].SetIdentity();
m_BoneMatrices[i].Rotate(m_pModelDef->GetBones()[i].m_Rotation);
m_BoneMatrices[i].Translate(m_pModelDef->GetBones()[i].m_Translation);
m_BoneMatrices[i].Concatenate(transform);
}
}
// For CPU skinning, we precompute as much as possible so that the only
// per-vertex work is a single matrix*vec multiplication.
// For GPU skinning, we try to minimise CPU work by doing most computation
// in the vertex shader instead.
// Using g_Renderer.m_Options to detect CPU vs GPU is a bit hacky,
// and this doesn't allow the setting to change at runtime, but there isn't
// an obvious cleaner way to determine what data needs to be computed,
// and GPU skinning is a rarely-used experimental feature anyway.
bool worldSpaceBoneMatrices = !g_Renderer.m_Options.m_GPUSkinning;
bool computeBlendMatrices = !g_Renderer.m_Options.m_GPUSkinning;
if (m_BoneMatrices && worldSpaceBoneMatrices)
{
// add world-space transformation to m_BoneMatrices
const CMatrix3D transform = GetTransform();
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
m_BoneMatrices[i].Concatenate(transform);
}
// our own position is now valid; now we can safely update our props' positions without fearing
// that doing so will cause a revalidation of this model (see recursion above).
m_PositionValid = true;
@ -377,8 +378,10 @@ void CModel::ValidatePosition()
CMatrix3D proptransform = prop.m_Point->m_Transform;;
if (prop.m_Point->m_BoneIndex != 0xff)
{
// m_BoneMatrices[i] already have world transform pre-applied (see above)
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
CMatrix3D boneMatrix = m_BoneMatrices[prop.m_Point->m_BoneIndex];
if (!worldSpaceBoneMatrices)
boneMatrix.Concatenate(GetTransform());
proptransform.Concatenate(boneMatrix);
}
else
{
@ -394,7 +397,7 @@ void CModel::ValidatePosition()
{
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
{
m_BoneMatrices[i] = m_BoneMatrices[i] * m_InverseBindBoneMatrices[i];
m_BoneMatrices[i] = m_BoneMatrices[i] * m_pModelDef->GetInverseBindBoneMatrices()[i];
}
// Note: there is a special case of joint influence, in which the vertex
@ -405,6 +408,7 @@ void CModel::ValidatePosition()
// (see http://trac.wildfiregames.com/ticket/1012)
m_BoneMatrices[m_pModelDef->GetNumBones()] = m_Transform;
if (computeBlendMatrices)
m_pModelDef->BlendBoneMatrices(m_BoneMatrices);
}
}

View File

@ -189,10 +189,6 @@ public:
return m_BoneMatrices;
}
const CMatrix3D* GetInverseBindBoneMatrices() {
return m_InverseBindBoneMatrices;
}
/**
* Load raw animation frame animation from given file, and build an
* animation specific to this model.
@ -278,8 +274,6 @@ private:
* @see SPropPoint
*/
CMatrix3D* m_BoneMatrices;
// inverse matrices for the bind pose's bones; null if not skeletal
CMatrix3D* m_InverseBindBoneMatrices;
// list of current props on model
std::vector<Prop> m_Props;

View File

@ -219,17 +219,15 @@ void CModelDef::BlendBoneMatrices(
// (see http://trac.wildfiregames.com/ticket/1012)
boneMatrix.Blend(boneMatrices[blend.m_Bone[0]], blend.m_Weight[0]);
boneMatrix.AddBlend(boneMatrices[blend.m_Bone[1]], blend.m_Weight[1]);
for (size_t j = 2; j < SVertexBlend::SIZE && blend.m_Bone[j] != 0xFF; ++j)
{
for (size_t j = 1; j < SVertexBlend::SIZE && blend.m_Bone[j] != 0xFF; ++j)
boneMatrix.AddBlend(boneMatrices[blend.m_Bone[j]], blend.m_Weight[j]);
}
}
}
// CModelDef Constructor
CModelDef::CModelDef() :
m_NumVertices(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0), m_NumBones(0), m_Bones(0),
m_NumVertices(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0),
m_NumBones(0), m_Bones(0), m_InverseBindBoneMatrices(NULL),
m_NumBlends(0), m_pBlends(0), m_pBlendIndices(0),
m_Name(L"[not loaded]")
{
@ -243,6 +241,7 @@ CModelDef::~CModelDef()
delete[] m_pVertices;
delete[] m_pFaces;
delete[] m_Bones;
delete[] m_InverseBindBoneMatrices;
delete[] m_pBlends;
delete[] m_pBlendIndices;
}
@ -381,6 +380,16 @@ CModelDef* CModelDef::Load(const VfsPath& filename, const VfsPath& name)
}
}
// Compute the inverse bind-pose matrices, needed by the skinning code
mdef->m_InverseBindBoneMatrices = new CMatrix3D[mdef->m_NumBones];
CBoneState* defpose = mdef->GetBones();
for (size_t i = 0; i < mdef->m_NumBones; ++i)
{
mdef->m_InverseBindBoneMatrices[i].SetIdentity();
mdef->m_InverseBindBoneMatrices[i].Translate(-defpose[i].m_Translation);
mdef->m_InverseBindBoneMatrices[i].Rotate(defpose[i].m_Rotation.GetInverse());
}
return mdef.release();
}

View File

@ -134,6 +134,8 @@ public:
// information of a model
class CModelDef
{
NONCOPYABLE(CModelDef);
public:
// current file version given to saved animations
enum { FILE_VERSION = 3 };
@ -170,6 +172,7 @@ public:
// accessor: get bone data
size_t GetNumBones() const { return m_NumBones; }
CBoneState* GetBones() const { return m_Bones; }
CMatrix3D* GetInverseBindBoneMatrices() { return m_InverseBindBoneMatrices; }
// accessor: get blend data
size_t GetNumBlends() const { return m_NumBlends; }
@ -259,6 +262,7 @@ public:
// bone data - default model pose
size_t m_NumBones;
CBoneState* m_Bones;
CMatrix3D* m_InverseBindBoneMatrices;
// blend data
size_t m_NumBlends;
SVertexBlend *m_pBlends;

View File

@ -50,15 +50,17 @@ struct IModelDef : public CModelDefRPrivate
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Normal;
VertexArray::Attribute m_UV;
VertexArray::Attribute m_BlendJoints; // valid iff gpuSkinning == true
VertexArray::Attribute m_BlendWeights; // valid iff gpuSkinning == true
/// Indices are the same for all models, so share them
VertexIndexArray m_IndexArray;
IModelDef(const CModelDefPtr& mdef);
IModelDef(const CModelDefPtr& mdef, bool gpuSkinning);
};
IModelDef::IModelDef(const CModelDefPtr& mdef)
IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning)
: m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
{
size_t numVertices = mdef->GetNumVertices();
@ -75,6 +77,17 @@ IModelDef::IModelDef(const CModelDefPtr& mdef)
m_UV.elems = 2;
m_Array.AddAttribute(&m_UV);
if (gpuSkinning)
{
m_BlendJoints.type = GL_UNSIGNED_BYTE;
m_BlendJoints.elems = 4;
m_Array.AddAttribute(&m_BlendJoints);
m_BlendWeights.type = GL_UNSIGNED_BYTE;
m_BlendWeights.elems = 4;
m_Array.AddAttribute(&m_BlendWeights);
}
m_Array.SetNumVertices(numVertices);
m_Array.Layout();
@ -85,6 +98,21 @@ IModelDef::IModelDef(const CModelDefPtr& mdef)
ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);
ModelRenderer::BuildUV(mdef, UVit);
if (gpuSkinning)
{
VertexArrayIterator<u8[4]> BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
VertexArrayIterator<u8[4]> BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
for (size_t i = 0; i < numVertices; ++i)
{
const SModelVertex& vtx = mdef->GetVertices()[i];
for (size_t j = 0; j < 4; ++j)
{
BlendJoints[i][j] = vtx.m_Blend.m_Bone[j];
BlendWeights[i][j] = (u8)(255.f * vtx.m_Blend.m_Weight[j]);
}
}
}
m_Array.Upload();
m_Array.FreeBackingStore();
@ -98,6 +126,8 @@ IModelDef::IModelDef(const CModelDefPtr& mdef)
struct InstancingModelRendererInternals
{
bool gpuSkinning;
/// Previously prepared modeldef
IModelDef* imodeldef;
@ -107,9 +137,10 @@ struct InstancingModelRendererInternals
// Construction and Destruction
InstancingModelRenderer::InstancingModelRenderer()
InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning)
{
m = new InstancingModelRendererInternals;
m->gpuSkinning = gpuSkinning;
m->imodeldef = 0;
}
@ -125,11 +156,14 @@ CModelRData* InstancingModelRenderer::CreateModelData(const void* key, CModel* m
CModelDefPtr mdef = model->GetModelDef();
IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
if (m->gpuSkinning)
ENSURE(model->IsSkinned());
else
ENSURE(!model->IsSkinned());
if (!imodeldef)
{
imodeldef = new IModelDef(mdef);
imodeldef = new IModelDef(mdef, m->gpuSkinning);
mdef->SetRenderData(m, imodeldef);
}
@ -177,15 +211,33 @@ void InstancingModelRenderer::PrepareModelDef(const CShaderProgramPtr& shader, i
if (streamflags & STREAM_UV0)
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->imodeldef->m_UV.offset);
// GPU skinning requires extra attributes to compute positions/normals
if (m->gpuSkinning)
{
shader->VertexAttribIPointer("a_skinJoints", 4, GL_UNSIGNED_BYTE, stride, base + m->imodeldef->m_BlendJoints.offset);
shader->VertexAttribPointer("a_skinWeights", 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, base + m->imodeldef->m_BlendWeights.offset);
}
shader->AssertPointersBound();
}
// Render one model
void InstancingModelRenderer::RenderModel(const CShaderProgramPtr& UNUSED(shader), int UNUSED(streamflags), CModel* model, CModelRData* UNUSED(data))
void InstancingModelRenderer::RenderModel(const CShaderProgramPtr& shader, int UNUSED(streamflags), CModel* model, CModelRData* UNUSED(data))
{
CModelDefPtr mdldef = model->GetModelDef();
if (m->gpuSkinning)
{
// Bind matrices for current animation state.
// Add 1 to NumBones because of the special 'root' bone.
// HACK: NVIDIA drivers return uniform name with "[0]", Intel Windows drivers without;
// try uploading both names since one of them should work, and this is easier than
// canonicalising the uniform names in CShaderProgramGLSL
shader->Uniform("skinBlendMatrices[0]", mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices());
shader->Uniform("skinBlendMatrices", mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices());
}
// render the lot
size_t numFaces = mdldef->GetNumFaces();

View File

@ -35,7 +35,7 @@ struct InstancingModelRendererInternals;
class InstancingModelRenderer : public ModelVertexRenderer
{
public:
InstancingModelRenderer();
InstancingModelRenderer(bool gpuSkinning);
~InstancingModelRenderer();
// Implementations

View File

@ -288,17 +288,18 @@ public:
// Submitted models are split on two axes:
// - Normal vs Transp[arent] - alpha-blended models are stored in a separate
// list so we can draw them above/below the alpha-blended water plane correctly
// - Instancing vs [not instancing] - with hardware lighting we don't need to
// - Skinned vs Unskinned - with hardware lighting we don't need to
// duplicate mesh data per model instance (except for skinned models),
// so non-skinned models get different ModelVertexRenderers
ModelRendererPtr Normal;
ModelRendererPtr NormalInstancing;
ModelRendererPtr Transp;
ModelRendererPtr TranspInstancing;
ModelRendererPtr NormalSkinned;
ModelRendererPtr NormalUnskinned; // == NormalSkinned if unskinned shader instancing not supported
ModelRendererPtr TranspSkinned;
ModelRendererPtr TranspUnskinned; // == TranspSkinned if unskinned shader instancing not supported
ModelVertexRendererPtr VertexRendererShader;
ModelVertexRendererPtr VertexInstancingShader;
ModelVertexRendererPtr VertexGPUSkinningShader;
LitRenderModifierPtr ModShader;
} Model;
@ -339,12 +340,20 @@ public:
*/
void CallModelRenderers(const CShaderDefines& context, int flags)
{
CShaderDefines contextInstancing = context;
contextInstancing.Add("USE_INSTANCING", "1");
CShaderDefines contextSkinned = context;
if (g_Renderer.m_Options.m_GPUSkinning)
{
contextSkinned.Add("USE_INSTANCING", "1");
contextSkinned.Add("USE_GPU_SKINNING", "1");
}
Model.NormalSkinned->Render(Model.ModShader, contextSkinned, flags);
Model.Normal->Render(Model.ModShader, context, flags);
if (Model.NormalInstancing)
Model.NormalInstancing->Render(Model.ModShader, contextInstancing, flags);
if (Model.NormalUnskinned != Model.NormalSkinned)
{
CShaderDefines contextUnskinned = context;
contextUnskinned.Add("USE_INSTANCING", "1");
Model.NormalUnskinned->Render(Model.ModShader, contextUnskinned, flags);
}
}
/**
@ -352,12 +361,20 @@ public:
*/
void CallTranspModelRenderers(const CShaderDefines& context, int flags)
{
CShaderDefines contextInstancing = context;
contextInstancing.Add("USE_INSTANCING", "1");
CShaderDefines contextSkinned = context;
if (g_Renderer.m_Options.m_GPUSkinning)
{
contextSkinned.Add("USE_INSTANCING", "1");
contextSkinned.Add("USE_GPU_SKINNING", "1");
}
Model.TranspSkinned->Render(Model.ModShader, contextSkinned, flags);
Model.Transp->Render(Model.ModShader, context, flags);
if (Model.TranspInstancing)
Model.TranspInstancing->Render(Model.ModShader, contextInstancing, flags);
if (Model.TranspUnskinned != Model.TranspSkinned)
{
CShaderDefines contextUnskinned = context;
contextUnskinned.Add("USE_INSTANCING", "1");
Model.TranspUnskinned->Render(Model.ModShader, contextUnskinned, flags);
}
}
/**
@ -365,9 +382,9 @@ public:
*/
void FilterModels(CModelFilter& filter, int passed, int flags = 0)
{
Model.Normal->Filter(filter, passed, flags);
if (Model.NormalInstancing)
Model.NormalInstancing->Filter(filter, passed, flags);
Model.NormalSkinned->Filter(filter, passed, flags);
if (Model.NormalUnskinned != Model.NormalSkinned)
Model.NormalUnskinned->Filter(filter, passed, flags);
}
/**
@ -375,9 +392,9 @@ public:
*/
void FilterTranspModels(CModelFilter& filter, int passed, int flags = 0)
{
Model.Transp->Filter(filter, passed, flags);
if (Model.TranspInstancing)
Model.TranspInstancing->Filter(filter, passed, flags);
Model.TranspSkinned->Filter(filter, passed, flags);
if (Model.TranspUnskinned != Model.TranspSkinned)
Model.TranspUnskinned->Filter(filter, passed, flags);
}
};
@ -410,10 +427,12 @@ CRenderer::CRenderer()
m_Options.m_ShadowPCF = false;
m_Options.m_PreferGLSL = false;
m_Options.m_ForceAlphaTest = false;
m_Options.m_GPUSkinning = false;
// TODO: be more consistent in use of the config system
CFG_GET_USER_VAL("preferglsl", Bool, m_Options.m_PreferGLSL);
CFG_GET_USER_VAL("forcealphatest", Bool, m_Options.m_ForceAlphaTest);
CFG_GET_USER_VAL("gpuskinning", Bool, m_Options.m_GPUSkinning);
#if CONFIG2_GLES
// Override config option since GLES only supports GLSL
@ -535,21 +554,31 @@ void CRenderer::ReloadShaders()
bool cpuLighting = (GetRenderPath() == RP_FIXED);
m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelVertexRenderer(cpuLighting));
m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer);
m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer(false));
m->Model.Normal = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
m->Model.Transp = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
if (GetRenderPath() == RP_SHADER && m_Options.m_GPUSkinning) // TODO: should check caps and GLSL etc too
{
m->Model.VertexGPUSkinningShader = ModelVertexRendererPtr(new InstancingModelRenderer(true));
m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
}
else
{
m->Model.VertexGPUSkinningShader.reset();
m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
}
// Use instancing renderers in shader mode
if (GetRenderPath() == RP_SHADER)
{
m->Model.NormalInstancing = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
m->Model.TranspInstancing = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
m->Model.NormalUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
m->Model.TranspUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
}
else
{
m->Model.NormalInstancing.reset();
m->Model.TranspInstancing.reset();
m->Model.NormalUnskinned = m->Model.NormalSkinned;
m->Model.TranspUnskinned = m->Model.TranspSkinned;
}
m->ShadersDirty = false;
@ -1310,12 +1339,12 @@ void CRenderer::RenderSubmissions()
// Prepare model renderers
{
PROFILE3("prepare models");
m->Model.Normal->PrepareModels();
m->Model.Transp->PrepareModels();
if (m->Model.NormalInstancing)
m->Model.NormalInstancing->PrepareModels();
if (m->Model.TranspInstancing)
m->Model.TranspInstancing->PrepareModels();
m->Model.NormalSkinned->PrepareModels();
m->Model.TranspSkinned->PrepareModels();
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
m->Model.NormalUnskinned->PrepareModels();
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
m->Model.TranspUnskinned->PrepareModels();
}
m->terrainRenderer.PrepareForRendering();
@ -1441,12 +1470,12 @@ void CRenderer::EndFrame()
m->particleRenderer.EndFrame();
// Finish model renderers
m->Model.Normal->EndFrame();
m->Model.Transp->EndFrame();
if (m->Model.NormalInstancing)
m->Model.NormalInstancing->EndFrame();
if (m->Model.TranspInstancing)
m->Model.TranspInstancing->EndFrame();
m->Model.NormalSkinned->EndFrame();
m->Model.TranspSkinned->EndFrame();
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
m->Model.NormalUnskinned->EndFrame();
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
m->Model.TranspUnskinned->EndFrame();
ogl_tex_bind(0, 0);
@ -1560,24 +1589,21 @@ void CRenderer::SubmitNonRecursive(CModel* model)
// Tricky: The call to GetWorldBounds() above can invalidate the position
model->ValidatePosition();
bool canUseInstancing = false;
if (model->GetModelDef()->GetNumBones() == 0)
canUseInstancing = true;
bool requiresSkinning = (model->GetModelDef()->GetNumBones() != 0);
if (model->GetMaterial().UsesAlphaBlending())
{
if (canUseInstancing && m->Model.TranspInstancing)
m->Model.TranspInstancing->Submit(model);
if (requiresSkinning)
m->Model.TranspSkinned->Submit(model);
else
m->Model.Transp->Submit(model);
m->Model.TranspUnskinned->Submit(model);
}
else
{
if (canUseInstancing && m->Model.NormalInstancing)
m->Model.NormalInstancing->Submit(model);
if (requiresSkinning)
m->Model.NormalSkinned->Submit(model);
else
m->Model.Normal->Submit(model);
m->Model.NormalUnskinned->Submit(model);
}
}

View File

@ -123,6 +123,7 @@ public:
bool m_ShadowPCF;
bool m_PreferGLSL;
bool m_ForceAlphaTest;
bool m_GPUSkinning;
} m_Options;
struct Caps {

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -146,6 +146,26 @@ VertexArrayIterator<u16> VertexArray::Attribute::GetIterator<u16>() const
return vertexArray->MakeIterator<u16>(this);
}
template<>
VertexArrayIterator<u8> VertexArray::Attribute::GetIterator<u8>() const
{
ENSURE(vertexArray);
ENSURE(type == GL_UNSIGNED_BYTE);
ENSURE(elems >= 1);
return vertexArray->MakeIterator<u8>(this);
}
template<>
VertexArrayIterator<u8[4]> VertexArray::Attribute::GetIterator<u8[4]>() const
{
ENSURE(vertexArray);
ENSURE(type == GL_UNSIGNED_BYTE);
ENSURE(elems >= 4);
return vertexArray->MakeIterator<u8[4]>(this);
}
static size_t RoundStride(size_t stride)