forked from 0ad/0ad
Experimental GPU skinning.
Share inverse bind pose matrices between models. This was SVN commit r11490.
This commit is contained in:
parent
ce215cace3
commit
227f9e403f
@ -61,6 +61,9 @@ preferglsl = false
|
|||||||
; Replace alpha-blending with alpha-testing, for performance experiments
|
; Replace alpha-blending with alpha-testing, for performance experiments
|
||||||
forcealphatest = false
|
forcealphatest = false
|
||||||
|
|
||||||
|
; Experimental probably-non-working GPU skinning support; requires preferglsl; use at own risk
|
||||||
|
gpuskinning = false
|
||||||
|
|
||||||
; Opt-in online user reporting system
|
; Opt-in online user reporting system
|
||||||
userreport.url = "http://feedback.wildfiregames.com/report/upload/v1/"
|
userreport.url = "http://feedback.wildfiregames.com/report/upload/v1/"
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
#if USE_GPU_SKINNING
|
||||||
|
// Skinning requires GLSL 1.30 for ivec4 vertex attributes
|
||||||
|
#version 130
|
||||||
|
#else
|
||||||
#version 120
|
#version 120
|
||||||
|
#endif
|
||||||
|
|
||||||
uniform mat4 transform;
|
uniform mat4 transform;
|
||||||
uniform vec3 cameraPos;
|
uniform vec3 cameraPos;
|
||||||
@ -22,8 +27,32 @@ attribute vec3 a_vertex;
|
|||||||
attribute vec3 a_normal;
|
attribute vec3 a_normal;
|
||||||
attribute vec2 a_uv0;
|
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()
|
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
|
#if USE_INSTANCING
|
||||||
vec4 position = instancingTransform * vec4(a_vertex, 1.0);
|
vec4 position = instancingTransform * vec4(a_vertex, 1.0);
|
||||||
vec3 normal = mat3(instancingTransform) * a_normal;
|
vec3 normal = mat3(instancingTransform) * a_normal;
|
||||||
@ -31,6 +60,7 @@ void main()
|
|||||||
vec4 position = vec4(a_vertex, 1.0);
|
vec4 position = vec4(a_vertex, 1.0);
|
||||||
vec3 normal = a_normal;
|
vec3 normal = a_normal;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
gl_Position = transform * position;
|
gl_Position = transform * position;
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
<attrib name="a_vertex" semantics="gl_Vertex"/>
|
<attrib name="a_vertex" semantics="gl_Vertex"/>
|
||||||
<attrib name="a_normal" semantics="gl_Normal"/>
|
<attrib name="a_normal" semantics="gl_Normal"/>
|
||||||
<attrib name="a_uv0" semantics="gl_MultiTexCoord0"/>
|
<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>
|
</vertex>
|
||||||
|
|
||||||
<fragment file="glsl/model_common.fs"/>
|
<fragment file="glsl/model_common.fs"/>
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
<vertex file="glsl/model_common.vs">
|
<vertex file="glsl/model_common.vs">
|
||||||
<stream name="pos"/>
|
<stream name="pos"/>
|
||||||
<attrib name="a_vertex" semantics="gl_Vertex"/>
|
<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>
|
</vertex>
|
||||||
|
|
||||||
<fragment file="glsl/solid.fs"/>
|
<fragment file="glsl/solid.fs"/>
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
<vertex file="glsl/model_common.vs">
|
<vertex file="glsl/model_common.vs">
|
||||||
<stream name="pos"/>
|
<stream name="pos"/>
|
||||||
<attrib name="a_vertex" semantics="gl_Vertex"/>
|
<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>
|
</vertex>
|
||||||
|
|
||||||
<fragment file="glsl/solid_player.fs"/>
|
<fragment file="glsl/solid_player.fs"/>
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
<stream name="uv0"/>
|
<stream name="uv0"/>
|
||||||
<attrib name="a_vertex" semantics="gl_Vertex"/>
|
<attrib name="a_vertex" semantics="gl_Vertex"/>
|
||||||
<attrib name="a_uv0" semantics="gl_MultiTexCoord0"/>
|
<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>
|
</vertex>
|
||||||
|
|
||||||
<fragment file="glsl/solid_tex.fs"/>
|
<fragment file="glsl/solid_tex.fs"/>
|
||||||
|
@ -35,12 +35,13 @@
|
|||||||
#include "lib/sysdep/rtl.h"
|
#include "lib/sysdep/rtl.h"
|
||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
|
#include "renderer/Renderer.h"
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
CModel::CModel(CSkeletonAnimManager& skeletonAnimManager)
|
CModel::CModel(CSkeletonAnimManager& skeletonAnimManager)
|
||||||
: m_Flags(0), m_Anim(NULL), m_AnimTime(0),
|
: 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_AmmoPropPoint(NULL), m_AmmoLoadedProp(0),
|
||||||
m_SkeletonAnimManager(skeletonAnimManager)
|
m_SkeletonAnimManager(skeletonAnimManager)
|
||||||
{
|
{
|
||||||
@ -58,7 +59,6 @@ CModel::~CModel()
|
|||||||
void CModel::ReleaseData()
|
void CModel::ReleaseData()
|
||||||
{
|
{
|
||||||
rtl_FreeAligned(m_BoneMatrices);
|
rtl_FreeAligned(m_BoneMatrices);
|
||||||
delete[] m_InverseBindBoneMatrices;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||||
delete m_Props[i].m_Model;
|
delete m_Props[i].m_Model;
|
||||||
@ -84,21 +84,10 @@ bool CModel::InitModel(const CModelDefPtr& modeldef)
|
|||||||
// allocate matrices for bone transformations
|
// allocate matrices for bone transformations
|
||||||
// (one extra matrix is used for the special case of bind-shape relative weighting)
|
// (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);
|
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_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;
|
m_PositionValid = true;
|
||||||
@ -344,27 +333,39 @@ void CModel::ValidatePosition()
|
|||||||
ENSURE(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());
|
ENSURE(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());
|
||||||
|
|
||||||
m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime, m_BoneMatrices, !(m_Flags & MODELFLAG_NOLOOPANIMATION));
|
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)
|
else if (m_BoneMatrices)
|
||||||
{
|
{
|
||||||
// Bones but no animation - probably a buggy actor forgot to set up the animation,
|
// Bones but no animation - probably a buggy actor forgot to set up the animation,
|
||||||
// so just render it in its bind pose
|
// so just render it in its bind pose
|
||||||
|
|
||||||
const CMatrix3D& transform = GetTransform();
|
|
||||||
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
|
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
|
||||||
{
|
{
|
||||||
m_BoneMatrices[i].SetIdentity();
|
m_BoneMatrices[i].SetIdentity();
|
||||||
m_BoneMatrices[i].Rotate(m_pModelDef->GetBones()[i].m_Rotation);
|
m_BoneMatrices[i].Rotate(m_pModelDef->GetBones()[i].m_Rotation);
|
||||||
m_BoneMatrices[i].Translate(m_pModelDef->GetBones()[i].m_Translation);
|
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
|
// 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).
|
// that doing so will cause a revalidation of this model (see recursion above).
|
||||||
m_PositionValid = true;
|
m_PositionValid = true;
|
||||||
@ -377,8 +378,10 @@ void CModel::ValidatePosition()
|
|||||||
CMatrix3D proptransform = prop.m_Point->m_Transform;;
|
CMatrix3D proptransform = prop.m_Point->m_Transform;;
|
||||||
if (prop.m_Point->m_BoneIndex != 0xff)
|
if (prop.m_Point->m_BoneIndex != 0xff)
|
||||||
{
|
{
|
||||||
// m_BoneMatrices[i] already have world transform pre-applied (see above)
|
CMatrix3D boneMatrix = m_BoneMatrices[prop.m_Point->m_BoneIndex];
|
||||||
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
|
if (!worldSpaceBoneMatrices)
|
||||||
|
boneMatrix.Concatenate(GetTransform());
|
||||||
|
proptransform.Concatenate(boneMatrix);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -394,7 +397,7 @@ void CModel::ValidatePosition()
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
|
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
|
// Note: there is a special case of joint influence, in which the vertex
|
||||||
@ -405,7 +408,8 @@ void CModel::ValidatePosition()
|
|||||||
// (see http://trac.wildfiregames.com/ticket/1012)
|
// (see http://trac.wildfiregames.com/ticket/1012)
|
||||||
m_BoneMatrices[m_pModelDef->GetNumBones()] = m_Transform;
|
m_BoneMatrices[m_pModelDef->GetNumBones()] = m_Transform;
|
||||||
|
|
||||||
m_pModelDef->BlendBoneMatrices(m_BoneMatrices);
|
if (computeBlendMatrices)
|
||||||
|
m_pModelDef->BlendBoneMatrices(m_BoneMatrices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,10 +189,6 @@ public:
|
|||||||
return m_BoneMatrices;
|
return m_BoneMatrices;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CMatrix3D* GetInverseBindBoneMatrices() {
|
|
||||||
return m_InverseBindBoneMatrices;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load raw animation frame animation from given file, and build an
|
* Load raw animation frame animation from given file, and build an
|
||||||
* animation specific to this model.
|
* animation specific to this model.
|
||||||
@ -278,8 +274,6 @@ private:
|
|||||||
* @see SPropPoint
|
* @see SPropPoint
|
||||||
*/
|
*/
|
||||||
CMatrix3D* m_BoneMatrices;
|
CMatrix3D* m_BoneMatrices;
|
||||||
// inverse matrices for the bind pose's bones; null if not skeletal
|
|
||||||
CMatrix3D* m_InverseBindBoneMatrices;
|
|
||||||
// list of current props on model
|
// list of current props on model
|
||||||
std::vector<Prop> m_Props;
|
std::vector<Prop> m_Props;
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ void CModelDef::SkinPointsAndNormals_SSE(
|
|||||||
ASSERT((intptr_t)newPoseMatrices % 16 == 0);
|
ASSERT((intptr_t)newPoseMatrices % 16 == 0);
|
||||||
ASSERT((intptr_t)PositionData % 16 == 0);
|
ASSERT((intptr_t)PositionData % 16 == 0);
|
||||||
ASSERT((intptr_t)PositionStride % 16 == 0);
|
ASSERT((intptr_t)PositionStride % 16 == 0);
|
||||||
ASSERT((intptr_t)NormalData % 16 == 0);
|
ASSERT((intptr_t)NormalData % 16 == 0);
|
||||||
ASSERT((intptr_t)NormalStride % 16 == 0);
|
ASSERT((intptr_t)NormalStride % 16 == 0);
|
||||||
|
|
||||||
__m128 col0, col1, col2, col3, vec0, vec1, vec2;
|
__m128 col0, col1, col2, col3, vec0, vec1, vec2;
|
||||||
@ -219,17 +219,15 @@ void CModelDef::BlendBoneMatrices(
|
|||||||
// (see http://trac.wildfiregames.com/ticket/1012)
|
// (see http://trac.wildfiregames.com/ticket/1012)
|
||||||
|
|
||||||
boneMatrix.Blend(boneMatrices[blend.m_Bone[0]], blend.m_Weight[0]);
|
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 = 1; j < SVertexBlend::SIZE && blend.m_Bone[j] != 0xFF; ++j)
|
||||||
for (size_t j = 2; j < SVertexBlend::SIZE && blend.m_Bone[j] != 0xFF; ++j)
|
|
||||||
{
|
|
||||||
boneMatrix.AddBlend(boneMatrices[blend.m_Bone[j]], blend.m_Weight[j]);
|
boneMatrix.AddBlend(boneMatrices[blend.m_Bone[j]], blend.m_Weight[j]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CModelDef Constructor
|
// CModelDef Constructor
|
||||||
CModelDef::CModelDef() :
|
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_NumBlends(0), m_pBlends(0), m_pBlendIndices(0),
|
||||||
m_Name(L"[not loaded]")
|
m_Name(L"[not loaded]")
|
||||||
{
|
{
|
||||||
@ -243,6 +241,7 @@ CModelDef::~CModelDef()
|
|||||||
delete[] m_pVertices;
|
delete[] m_pVertices;
|
||||||
delete[] m_pFaces;
|
delete[] m_pFaces;
|
||||||
delete[] m_Bones;
|
delete[] m_Bones;
|
||||||
|
delete[] m_InverseBindBoneMatrices;
|
||||||
delete[] m_pBlends;
|
delete[] m_pBlends;
|
||||||
delete[] m_pBlendIndices;
|
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();
|
return mdef.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +134,8 @@ public:
|
|||||||
// information of a model
|
// information of a model
|
||||||
class CModelDef
|
class CModelDef
|
||||||
{
|
{
|
||||||
|
NONCOPYABLE(CModelDef);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// current file version given to saved animations
|
// current file version given to saved animations
|
||||||
enum { FILE_VERSION = 3 };
|
enum { FILE_VERSION = 3 };
|
||||||
@ -170,6 +172,7 @@ public:
|
|||||||
// accessor: get bone data
|
// accessor: get bone data
|
||||||
size_t GetNumBones() const { return m_NumBones; }
|
size_t GetNumBones() const { return m_NumBones; }
|
||||||
CBoneState* GetBones() const { return m_Bones; }
|
CBoneState* GetBones() const { return m_Bones; }
|
||||||
|
CMatrix3D* GetInverseBindBoneMatrices() { return m_InverseBindBoneMatrices; }
|
||||||
|
|
||||||
// accessor: get blend data
|
// accessor: get blend data
|
||||||
size_t GetNumBlends() const { return m_NumBlends; }
|
size_t GetNumBlends() const { return m_NumBlends; }
|
||||||
@ -259,6 +262,7 @@ public:
|
|||||||
// bone data - default model pose
|
// bone data - default model pose
|
||||||
size_t m_NumBones;
|
size_t m_NumBones;
|
||||||
CBoneState* m_Bones;
|
CBoneState* m_Bones;
|
||||||
|
CMatrix3D* m_InverseBindBoneMatrices;
|
||||||
// blend data
|
// blend data
|
||||||
size_t m_NumBlends;
|
size_t m_NumBlends;
|
||||||
SVertexBlend *m_pBlends;
|
SVertexBlend *m_pBlends;
|
||||||
|
@ -50,15 +50,17 @@ struct IModelDef : public CModelDefRPrivate
|
|||||||
VertexArray::Attribute m_Position;
|
VertexArray::Attribute m_Position;
|
||||||
VertexArray::Attribute m_Normal;
|
VertexArray::Attribute m_Normal;
|
||||||
VertexArray::Attribute m_UV;
|
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
|
/// Indices are the same for all models, so share them
|
||||||
VertexIndexArray m_IndexArray;
|
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)
|
: m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
|
||||||
{
|
{
|
||||||
size_t numVertices = mdef->GetNumVertices();
|
size_t numVertices = mdef->GetNumVertices();
|
||||||
@ -75,6 +77,17 @@ IModelDef::IModelDef(const CModelDefPtr& mdef)
|
|||||||
m_UV.elems = 2;
|
m_UV.elems = 2;
|
||||||
m_Array.AddAttribute(&m_UV);
|
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.SetNumVertices(numVertices);
|
||||||
m_Array.Layout();
|
m_Array.Layout();
|
||||||
|
|
||||||
@ -85,6 +98,21 @@ IModelDef::IModelDef(const CModelDefPtr& mdef)
|
|||||||
ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);
|
ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);
|
||||||
ModelRenderer::BuildUV(mdef, UVit);
|
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.Upload();
|
||||||
m_Array.FreeBackingStore();
|
m_Array.FreeBackingStore();
|
||||||
|
|
||||||
@ -98,6 +126,8 @@ IModelDef::IModelDef(const CModelDefPtr& mdef)
|
|||||||
|
|
||||||
struct InstancingModelRendererInternals
|
struct InstancingModelRendererInternals
|
||||||
{
|
{
|
||||||
|
bool gpuSkinning;
|
||||||
|
|
||||||
/// Previously prepared modeldef
|
/// Previously prepared modeldef
|
||||||
IModelDef* imodeldef;
|
IModelDef* imodeldef;
|
||||||
|
|
||||||
@ -107,9 +137,10 @@ struct InstancingModelRendererInternals
|
|||||||
|
|
||||||
|
|
||||||
// Construction and Destruction
|
// Construction and Destruction
|
||||||
InstancingModelRenderer::InstancingModelRenderer()
|
InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning)
|
||||||
{
|
{
|
||||||
m = new InstancingModelRendererInternals;
|
m = new InstancingModelRendererInternals;
|
||||||
|
m->gpuSkinning = gpuSkinning;
|
||||||
m->imodeldef = 0;
|
m->imodeldef = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,11 +156,14 @@ CModelRData* InstancingModelRenderer::CreateModelData(const void* key, CModel* m
|
|||||||
CModelDefPtr mdef = model->GetModelDef();
|
CModelDefPtr mdef = model->GetModelDef();
|
||||||
IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
|
IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m);
|
||||||
|
|
||||||
ENSURE(!model->IsSkinned());
|
if (m->gpuSkinning)
|
||||||
|
ENSURE(model->IsSkinned());
|
||||||
|
else
|
||||||
|
ENSURE(!model->IsSkinned());
|
||||||
|
|
||||||
if (!imodeldef)
|
if (!imodeldef)
|
||||||
{
|
{
|
||||||
imodeldef = new IModelDef(mdef);
|
imodeldef = new IModelDef(mdef, m->gpuSkinning);
|
||||||
mdef->SetRenderData(m, imodeldef);
|
mdef->SetRenderData(m, imodeldef);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,15 +211,33 @@ void InstancingModelRenderer::PrepareModelDef(const CShaderProgramPtr& shader, i
|
|||||||
if (streamflags & STREAM_UV0)
|
if (streamflags & STREAM_UV0)
|
||||||
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->imodeldef->m_UV.offset);
|
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();
|
shader->AssertPointersBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Render one model
|
// 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();
|
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
|
// render the lot
|
||||||
size_t numFaces = mdldef->GetNumFaces();
|
size_t numFaces = mdldef->GetNumFaces();
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ struct InstancingModelRendererInternals;
|
|||||||
class InstancingModelRenderer : public ModelVertexRenderer
|
class InstancingModelRenderer : public ModelVertexRenderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InstancingModelRenderer();
|
InstancingModelRenderer(bool gpuSkinning);
|
||||||
~InstancingModelRenderer();
|
~InstancingModelRenderer();
|
||||||
|
|
||||||
// Implementations
|
// Implementations
|
||||||
|
@ -288,17 +288,18 @@ public:
|
|||||||
// Submitted models are split on two axes:
|
// Submitted models are split on two axes:
|
||||||
// - Normal vs Transp[arent] - alpha-blended models are stored in a separate
|
// - 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
|
// 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),
|
// duplicate mesh data per model instance (except for skinned models),
|
||||||
// so non-skinned models get different ModelVertexRenderers
|
// so non-skinned models get different ModelVertexRenderers
|
||||||
|
|
||||||
ModelRendererPtr Normal;
|
ModelRendererPtr NormalSkinned;
|
||||||
ModelRendererPtr NormalInstancing;
|
ModelRendererPtr NormalUnskinned; // == NormalSkinned if unskinned shader instancing not supported
|
||||||
ModelRendererPtr Transp;
|
ModelRendererPtr TranspSkinned;
|
||||||
ModelRendererPtr TranspInstancing;
|
ModelRendererPtr TranspUnskinned; // == TranspSkinned if unskinned shader instancing not supported
|
||||||
|
|
||||||
ModelVertexRendererPtr VertexRendererShader;
|
ModelVertexRendererPtr VertexRendererShader;
|
||||||
ModelVertexRendererPtr VertexInstancingShader;
|
ModelVertexRendererPtr VertexInstancingShader;
|
||||||
|
ModelVertexRendererPtr VertexGPUSkinningShader;
|
||||||
|
|
||||||
LitRenderModifierPtr ModShader;
|
LitRenderModifierPtr ModShader;
|
||||||
} Model;
|
} Model;
|
||||||
@ -339,12 +340,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
void CallModelRenderers(const CShaderDefines& context, int flags)
|
void CallModelRenderers(const CShaderDefines& context, int flags)
|
||||||
{
|
{
|
||||||
CShaderDefines contextInstancing = context;
|
CShaderDefines contextSkinned = context;
|
||||||
contextInstancing.Add("USE_INSTANCING", "1");
|
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.NormalUnskinned != Model.NormalSkinned)
|
||||||
if (Model.NormalInstancing)
|
{
|
||||||
Model.NormalInstancing->Render(Model.ModShader, contextInstancing, flags);
|
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)
|
void CallTranspModelRenderers(const CShaderDefines& context, int flags)
|
||||||
{
|
{
|
||||||
CShaderDefines contextInstancing = context;
|
CShaderDefines contextSkinned = context;
|
||||||
contextInstancing.Add("USE_INSTANCING", "1");
|
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.TranspUnskinned != Model.TranspSkinned)
|
||||||
if (Model.TranspInstancing)
|
{
|
||||||
Model.TranspInstancing->Render(Model.ModShader, contextInstancing, flags);
|
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)
|
void FilterModels(CModelFilter& filter, int passed, int flags = 0)
|
||||||
{
|
{
|
||||||
Model.Normal->Filter(filter, passed, flags);
|
Model.NormalSkinned->Filter(filter, passed, flags);
|
||||||
if (Model.NormalInstancing)
|
if (Model.NormalUnskinned != Model.NormalSkinned)
|
||||||
Model.NormalInstancing->Filter(filter, passed, flags);
|
Model.NormalUnskinned->Filter(filter, passed, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -375,9 +392,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
void FilterTranspModels(CModelFilter& filter, int passed, int flags = 0)
|
void FilterTranspModels(CModelFilter& filter, int passed, int flags = 0)
|
||||||
{
|
{
|
||||||
Model.Transp->Filter(filter, passed, flags);
|
Model.TranspSkinned->Filter(filter, passed, flags);
|
||||||
if (Model.TranspInstancing)
|
if (Model.TranspUnskinned != Model.TranspSkinned)
|
||||||
Model.TranspInstancing->Filter(filter, passed, flags);
|
Model.TranspUnskinned->Filter(filter, passed, flags);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -410,10 +427,12 @@ CRenderer::CRenderer()
|
|||||||
m_Options.m_ShadowPCF = false;
|
m_Options.m_ShadowPCF = false;
|
||||||
m_Options.m_PreferGLSL = false;
|
m_Options.m_PreferGLSL = false;
|
||||||
m_Options.m_ForceAlphaTest = false;
|
m_Options.m_ForceAlphaTest = false;
|
||||||
|
m_Options.m_GPUSkinning = false;
|
||||||
|
|
||||||
// TODO: be more consistent in use of the config system
|
// TODO: be more consistent in use of the config system
|
||||||
CFG_GET_USER_VAL("preferglsl", Bool, m_Options.m_PreferGLSL);
|
CFG_GET_USER_VAL("preferglsl", Bool, m_Options.m_PreferGLSL);
|
||||||
CFG_GET_USER_VAL("forcealphatest", Bool, m_Options.m_ForceAlphaTest);
|
CFG_GET_USER_VAL("forcealphatest", Bool, m_Options.m_ForceAlphaTest);
|
||||||
|
CFG_GET_USER_VAL("gpuskinning", Bool, m_Options.m_GPUSkinning);
|
||||||
|
|
||||||
#if CONFIG2_GLES
|
#if CONFIG2_GLES
|
||||||
// Override config option since GLES only supports GLSL
|
// Override config option since GLES only supports GLSL
|
||||||
@ -535,21 +554,31 @@ void CRenderer::ReloadShaders()
|
|||||||
|
|
||||||
bool cpuLighting = (GetRenderPath() == RP_FIXED);
|
bool cpuLighting = (GetRenderPath() == RP_FIXED);
|
||||||
m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelVertexRenderer(cpuLighting));
|
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));
|
if (GetRenderPath() == RP_SHADER && m_Options.m_GPUSkinning) // TODO: should check caps and GLSL etc too
|
||||||
m->Model.Transp = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
|
{
|
||||||
|
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
|
// Use instancing renderers in shader mode
|
||||||
if (GetRenderPath() == RP_SHADER)
|
if (GetRenderPath() == RP_SHADER)
|
||||||
{
|
{
|
||||||
m->Model.NormalInstancing = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
m->Model.NormalUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
||||||
m->Model.TranspInstancing = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
m->Model.TranspUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m->Model.NormalInstancing.reset();
|
m->Model.NormalUnskinned = m->Model.NormalSkinned;
|
||||||
m->Model.TranspInstancing.reset();
|
m->Model.TranspUnskinned = m->Model.TranspSkinned;
|
||||||
}
|
}
|
||||||
|
|
||||||
m->ShadersDirty = false;
|
m->ShadersDirty = false;
|
||||||
@ -1310,12 +1339,12 @@ void CRenderer::RenderSubmissions()
|
|||||||
// Prepare model renderers
|
// Prepare model renderers
|
||||||
{
|
{
|
||||||
PROFILE3("prepare models");
|
PROFILE3("prepare models");
|
||||||
m->Model.Normal->PrepareModels();
|
m->Model.NormalSkinned->PrepareModels();
|
||||||
m->Model.Transp->PrepareModels();
|
m->Model.TranspSkinned->PrepareModels();
|
||||||
if (m->Model.NormalInstancing)
|
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
|
||||||
m->Model.NormalInstancing->PrepareModels();
|
m->Model.NormalUnskinned->PrepareModels();
|
||||||
if (m->Model.TranspInstancing)
|
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
|
||||||
m->Model.TranspInstancing->PrepareModels();
|
m->Model.TranspUnskinned->PrepareModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
m->terrainRenderer.PrepareForRendering();
|
m->terrainRenderer.PrepareForRendering();
|
||||||
@ -1441,12 +1470,12 @@ void CRenderer::EndFrame()
|
|||||||
m->particleRenderer.EndFrame();
|
m->particleRenderer.EndFrame();
|
||||||
|
|
||||||
// Finish model renderers
|
// Finish model renderers
|
||||||
m->Model.Normal->EndFrame();
|
m->Model.NormalSkinned->EndFrame();
|
||||||
m->Model.Transp->EndFrame();
|
m->Model.TranspSkinned->EndFrame();
|
||||||
if (m->Model.NormalInstancing)
|
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
|
||||||
m->Model.NormalInstancing->EndFrame();
|
m->Model.NormalUnskinned->EndFrame();
|
||||||
if (m->Model.TranspInstancing)
|
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
|
||||||
m->Model.TranspInstancing->EndFrame();
|
m->Model.TranspUnskinned->EndFrame();
|
||||||
|
|
||||||
ogl_tex_bind(0, 0);
|
ogl_tex_bind(0, 0);
|
||||||
|
|
||||||
@ -1560,24 +1589,21 @@ void CRenderer::SubmitNonRecursive(CModel* model)
|
|||||||
// Tricky: The call to GetWorldBounds() above can invalidate the position
|
// Tricky: The call to GetWorldBounds() above can invalidate the position
|
||||||
model->ValidatePosition();
|
model->ValidatePosition();
|
||||||
|
|
||||||
bool canUseInstancing = false;
|
bool requiresSkinning = (model->GetModelDef()->GetNumBones() != 0);
|
||||||
|
|
||||||
if (model->GetModelDef()->GetNumBones() == 0)
|
|
||||||
canUseInstancing = true;
|
|
||||||
|
|
||||||
if (model->GetMaterial().UsesAlphaBlending())
|
if (model->GetMaterial().UsesAlphaBlending())
|
||||||
{
|
{
|
||||||
if (canUseInstancing && m->Model.TranspInstancing)
|
if (requiresSkinning)
|
||||||
m->Model.TranspInstancing->Submit(model);
|
m->Model.TranspSkinned->Submit(model);
|
||||||
else
|
else
|
||||||
m->Model.Transp->Submit(model);
|
m->Model.TranspUnskinned->Submit(model);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (canUseInstancing && m->Model.NormalInstancing)
|
if (requiresSkinning)
|
||||||
m->Model.NormalInstancing->Submit(model);
|
m->Model.NormalSkinned->Submit(model);
|
||||||
else
|
else
|
||||||
m->Model.Normal->Submit(model);
|
m->Model.NormalUnskinned->Submit(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +123,7 @@ public:
|
|||||||
bool m_ShadowPCF;
|
bool m_ShadowPCF;
|
||||||
bool m_PreferGLSL;
|
bool m_PreferGLSL;
|
||||||
bool m_ForceAlphaTest;
|
bool m_ForceAlphaTest;
|
||||||
|
bool m_GPUSkinning;
|
||||||
} m_Options;
|
} m_Options;
|
||||||
|
|
||||||
struct Caps {
|
struct Caps {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2011 Wildfire Games.
|
/* Copyright (C) 2012 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 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);
|
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)
|
static size_t RoundStride(size_t stride)
|
||||||
|
Loading…
Reference in New Issue
Block a user