forked from Mirrors/openclonk
Merge GPU skinning
Doing skinning on the GPU shows a noticeable performance improvement in pretty much any situation, but especially so in scenes with lots of animated objects with high polygon counts.stable-6.1
commit
4639ce1675
|
@ -1,8 +1,42 @@
|
||||||
varying vec3 normalDir;
|
varying vec3 normalDir;
|
||||||
|
uniform mat4 bones[128];
|
||||||
|
|
||||||
|
// For more performance, this should be set by the engine, and this shader
|
||||||
|
// should be compiled three times: with BONE_COUNT set to 0, 4, and 8,
|
||||||
|
// respectively. (Or we could split it even further.)
|
||||||
|
#define BONE_COUNT 8
|
||||||
|
|
||||||
|
attribute vec4 oc_BoneIndices0;
|
||||||
|
attribute vec4 oc_BoneWeights0;
|
||||||
|
|
||||||
|
#if BONE_COUNT > 4
|
||||||
|
attribute vec4 oc_BoneIndices1;
|
||||||
|
attribute vec4 oc_BoneWeights1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vec4 merge_bone(vec4 vertex, vec4 original, mat4 bone, float weight)
|
||||||
|
{
|
||||||
|
return (bone * original) * weight + vertex;
|
||||||
|
}
|
||||||
|
|
||||||
slice(position)
|
slice(position)
|
||||||
{
|
{
|
||||||
|
#if BONE_COUNT == 0
|
||||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||||
|
#else
|
||||||
|
vec4 vertex = vec4(0, 0, 0, 0);
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices0.x)], oc_BoneWeights0.x);
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices0.y)], oc_BoneWeights0.y);
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices0.z)], oc_BoneWeights0.z);
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices0.w)], oc_BoneWeights0.w);
|
||||||
|
#if BONE_COUNT > 4
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices1.x)], oc_BoneWeights1.x);
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices1.y)], oc_BoneWeights1.y);
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices1.z)], oc_BoneWeights1.z);
|
||||||
|
vertex = merge_bone(vertex, gl_Vertex, bones[int(oc_BoneIndices1.w)], oc_BoneWeights1.w);
|
||||||
|
#endif
|
||||||
|
gl_Position = gl_ModelViewProjectionMatrix * vertex;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
slice(texcoord)
|
slice(texcoord)
|
||||||
|
@ -12,5 +46,21 @@ slice(texcoord)
|
||||||
|
|
||||||
slice(normal)
|
slice(normal)
|
||||||
{
|
{
|
||||||
|
#if BONE_COUNT == 0
|
||||||
normalDir = normalize(gl_NormalMatrix * gl_Normal);
|
normalDir = normalize(gl_NormalMatrix * gl_Normal);
|
||||||
|
#else
|
||||||
|
vec4 base_normal = vec4(gl_Normal, 0.0);
|
||||||
|
vec4 normal = base_normal;
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices0.x)], oc_BoneWeights0.x);
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices0.y)], oc_BoneWeights0.y);
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices0.z)], oc_BoneWeights0.z);
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices0.w)], oc_BoneWeights0.w);
|
||||||
|
#if BONE_COUNT > 4
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices1.x)], oc_BoneWeights1.x);
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices1.y)], oc_BoneWeights1.y);
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices1.z)], oc_BoneWeights1.z);
|
||||||
|
normal = merge_bone(normal, base_normal, bones[int(oc_BoneIndices1.w)], oc_BoneWeights1.w);
|
||||||
|
#endif
|
||||||
|
normalDir = normalize(gl_NormalMatrix * normal.xyz);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -570,6 +570,7 @@ bool CStdGL::CreateSpriteShader(C4Shader& shader, const char* name, int ssc, C4G
|
||||||
uniformNames[C4SSU_AmbientTex] = "ambientTex";
|
uniformNames[C4SSU_AmbientTex] = "ambientTex";
|
||||||
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
|
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
|
||||||
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
|
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
|
||||||
|
uniformNames[C4SSU_Bones] = "bones";
|
||||||
uniformNames[C4SSU_Count] = NULL;
|
uniformNames[C4SSU_Count] = NULL;
|
||||||
|
|
||||||
// Clear previous content
|
// Clear previous content
|
||||||
|
|
|
@ -68,6 +68,8 @@ enum C4SS_Uniforms
|
||||||
C4SSU_AmbientTransform, // C4SSC_LIGHT
|
C4SSU_AmbientTransform, // C4SSC_LIGHT
|
||||||
C4SSU_AmbientBrightness, // C4SSC_LIGHT
|
C4SSU_AmbientBrightness, // C4SSC_LIGHT
|
||||||
|
|
||||||
|
C4SSU_Bones, // for meshes
|
||||||
|
|
||||||
C4SSU_Count
|
C4SSU_Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -461,9 +461,29 @@ namespace
|
||||||
assert(material.BestTechniqueIndex != -1);
|
assert(material.BestTechniqueIndex != -1);
|
||||||
const StdMeshMaterialTechnique& technique = material.Techniques[material.BestTechniqueIndex];
|
const StdMeshMaterialTechnique& technique = material.Techniques[material.BestTechniqueIndex];
|
||||||
|
|
||||||
bool using_shared_vertices = instance.GetVertices().empty();
|
bool using_shared_vertices = instance.GetSubMesh().GetVertices().empty();
|
||||||
GLuint vbo = mesh_instance.GetVBO();
|
GLuint vbo = mesh_instance.GetMesh().GetVBO();
|
||||||
size_t buffer_offset = using_shared_vertices ? 0 : instance.GetOffsetInBuffer();
|
size_t buffer_offset = using_shared_vertices ? 0 : instance.GetSubMesh().GetOffsetInBuffer();
|
||||||
|
|
||||||
|
// Cook the bone transform matrixes into something that OpenGL can use. This could be moved into RenderMeshImpl.
|
||||||
|
// Or, even better, we could upload them into a UBO, but Intel doesn't support them prior to Sandy Bridge.
|
||||||
|
struct BoneTransform
|
||||||
|
{
|
||||||
|
float m[4][4];
|
||||||
|
};
|
||||||
|
std::vector<BoneTransform> bones;
|
||||||
|
bones.reserve(mesh_instance.GetBoneCount());
|
||||||
|
for (size_t bone_index = 0; bone_index < mesh_instance.GetBoneCount(); ++bone_index)
|
||||||
|
{
|
||||||
|
const StdMeshMatrix &bone = mesh_instance.GetBoneTransform(bone_index);
|
||||||
|
BoneTransform cooked_bone = {
|
||||||
|
bone(0, 0), bone(0, 1), bone(0, 2), bone(0, 3),
|
||||||
|
bone(1, 0), bone(1, 1), bone(1, 2), bone(1, 3),
|
||||||
|
bone(2, 0), bone(2, 1), bone(2, 2), bone(2, 3),
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
bones.push_back(cooked_bone);
|
||||||
|
}
|
||||||
|
|
||||||
// Render each pass
|
// Render each pass
|
||||||
for (unsigned int i = 0; i < technique.Passes.size(); ++i)
|
for (unsigned int i = 0; i < technique.Passes.size(); ++i)
|
||||||
|
@ -532,13 +552,6 @@ namespace
|
||||||
glBlendFunc(OgreBlendTypeToGL(pass.SceneBlendFactors[0]), GL_ONE);
|
glBlendFunc(OgreBlendTypeToGL(pass.SceneBlendFactors[0]), GL_ONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VERTEX_OFFSET(field) reinterpret_cast<const uint8_t *>(offsetof(StdMeshVertex, field))
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
||||||
glTexCoordPointer(2, GL_FLOAT, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(u));
|
|
||||||
glVertexPointer(3, GL_FLOAT, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(x));
|
|
||||||
glNormalPointer(GL_FLOAT, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(nx));
|
|
||||||
#undef VERTEX_OFFSET
|
|
||||||
|
|
||||||
glMatrixMode(GL_TEXTURE);
|
glMatrixMode(GL_TEXTURE);
|
||||||
|
|
||||||
assert(pass.Program.get() != NULL);
|
assert(pass.Program.get() != NULL);
|
||||||
|
@ -551,6 +564,27 @@ namespace
|
||||||
C4ShaderCall call(shader);
|
C4ShaderCall call(shader);
|
||||||
call.Start();
|
call.Start();
|
||||||
|
|
||||||
|
// Upload the current bone transformation matrixes (if there are any)
|
||||||
|
if (!bones.empty())
|
||||||
|
call.SetUniformMatrix4x4fv(C4SSU_Bones, bones.size(), &bones[0].m[0][0]);
|
||||||
|
|
||||||
|
// Bind the vertex data of the mesh
|
||||||
|
#define VERTEX_OFFSET(field) reinterpret_cast<const uint8_t *>(offsetof(StdMeshVertex, field))
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glTexCoordPointer(2, GL_FLOAT, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(u));
|
||||||
|
glVertexPointer(3, GL_FLOAT, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(x));
|
||||||
|
glNormalPointer(GL_FLOAT, sizeof(StdMeshVertex), buffer_offset + VERTEX_OFFSET(nx));
|
||||||
|
for (int attrib_index = 0; attrib_index <= C4Shader::VAI_BoneIndicesMax - C4Shader::VAI_BoneIndices; ++attrib_index)
|
||||||
|
{
|
||||||
|
glVertexAttribPointer(C4Shader::VAI_BoneWeights + attrib_index, 4, GL_FLOAT, GL_FALSE, sizeof(StdMeshVertex),
|
||||||
|
buffer_offset + VERTEX_OFFSET(bone_weight) + sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_weight)>::type) * 4 * attrib_index);
|
||||||
|
glEnableVertexAttribArray(C4Shader::VAI_BoneWeights + attrib_index);
|
||||||
|
glVertexAttribPointer(C4Shader::VAI_BoneIndices + attrib_index, 4, GL_SHORT, GL_FALSE, sizeof(StdMeshVertex),
|
||||||
|
buffer_offset + VERTEX_OFFSET(bone_index) + sizeof(std::remove_all_extents<decltype(StdMeshVertex::bone_index)>::type) * 4 * attrib_index);
|
||||||
|
glEnableVertexAttribArray(C4Shader::VAI_BoneIndices + attrib_index);
|
||||||
|
}
|
||||||
|
#undef VERTEX_OFFSET
|
||||||
|
|
||||||
for (unsigned int j = 0; j < pass.TextureUnits.size(); ++j)
|
for (unsigned int j = 0; j < pass.TextureUnits.size(); ++j)
|
||||||
{
|
{
|
||||||
const StdMeshMaterialTextureUnit& texunit = pass.TextureUnits[j];
|
const StdMeshMaterialTextureUnit& texunit = pass.TextureUnits[j];
|
||||||
|
@ -655,9 +689,15 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glDrawElements(GL_TRIANGLES, instance.GetNumFaces()*3, GL_UNSIGNED_INT, instance.GetFaces());
|
size_t vertex_count = 3 * instance.GetNumFaces();
|
||||||
call.Finish();
|
glDrawElements(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, instance.GetFaces());
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
for (int attrib_index = 0; attrib_index <= C4Shader::VAI_BoneIndicesMax - C4Shader::VAI_BoneIndices; ++attrib_index)
|
||||||
|
{
|
||||||
|
glDisableVertexAttribArray(C4Shader::VAI_BoneIndices + attrib_index);
|
||||||
|
glDisableVertexAttribArray(C4Shader::VAI_BoneWeights + attrib_index);
|
||||||
|
}
|
||||||
|
call.Finish();
|
||||||
|
|
||||||
if(!pass.DepthCheck)
|
if(!pass.DepthCheck)
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
|
@ -336,6 +336,12 @@ bool C4Shader::Init(const char *szWhat, const char **szUniforms)
|
||||||
hProg = glCreateProgramObjectARB();
|
hProg = glCreateProgramObjectARB();
|
||||||
glAttachObjectARB(hProg, hVert);
|
glAttachObjectARB(hProg, hVert);
|
||||||
glAttachObjectARB(hProg, hFrag);
|
glAttachObjectARB(hProg, hFrag);
|
||||||
|
// Bind all input variables
|
||||||
|
for (int i = 0; i <= VAI_BoneWeightsMax - VAI_BoneWeights; ++i)
|
||||||
|
{
|
||||||
|
glBindAttribLocation(hProg, VAI_BoneWeights + i, FormatString("oc_BoneWeights%d", i).getData());
|
||||||
|
glBindAttribLocation(hProg, VAI_BoneIndices + i, FormatString("oc_BoneIndices%d", i).getData());
|
||||||
|
}
|
||||||
glLinkProgramARB(hProg);
|
glLinkProgramARB(hProg);
|
||||||
|
|
||||||
// Link successful?
|
// Link successful?
|
||||||
|
|
|
@ -75,6 +75,23 @@ private:
|
||||||
GLint *pUniforms;
|
GLint *pUniforms;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum VertexAttribIndex
|
||||||
|
{
|
||||||
|
// These correspond to the locations nVidia uses for the
|
||||||
|
// respective gl_* attributes, so make sure whatever you
|
||||||
|
// use for custom ones doesn't conflict with these UNLESS
|
||||||
|
// you're not using the pre-defined ones in your shader
|
||||||
|
VAI_Vertex = 0,
|
||||||
|
VAI_Normal = 2,
|
||||||
|
VAI_Color = 3,
|
||||||
|
VAI_TexCoord0 = 8, // and upwards through TexCoord7 = 15
|
||||||
|
|
||||||
|
// Make sure you move these if we implement multitexturing
|
||||||
|
VAI_BoneWeights,
|
||||||
|
VAI_BoneWeightsMax = VAI_BoneWeights + 1,
|
||||||
|
VAI_BoneIndices,
|
||||||
|
VAI_BoneIndicesMax = VAI_BoneIndices + VAI_BoneWeightsMax - VAI_BoneWeights
|
||||||
|
};
|
||||||
|
|
||||||
bool Initialised() const { return hVert != 0; }
|
bool Initialised() const { return hVert != 0; }
|
||||||
|
|
||||||
|
|
|
@ -529,12 +529,12 @@ std::vector<int> StdMeshSkeleton::GetMatchingBones(const StdMeshSkeleton& child_
|
||||||
return MatchedBoneInParentSkeleton;
|
return MatchedBoneInParentSkeleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
StdSubMesh::StdSubMesh():
|
StdSubMesh::StdSubMesh() :
|
||||||
Material(NULL)
|
Material(NULL), buffer_offset(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StdMesh::StdMesh() : Skeleton(new StdMeshSkeleton)
|
StdMesh::StdMesh() : Skeleton(new StdMeshSkeleton), vbo(0)
|
||||||
{
|
{
|
||||||
BoundingBox.x1 = BoundingBox.y1 = BoundingBox.z1 = 0.0f;
|
BoundingBox.x1 = BoundingBox.y1 = BoundingBox.z1 = 0.0f;
|
||||||
BoundingBox.x2 = BoundingBox.y2 = BoundingBox.z2 = 0.0f;
|
BoundingBox.x2 = BoundingBox.y2 = BoundingBox.z2 = 0.0f;
|
||||||
|
@ -543,21 +543,71 @@ StdMesh::StdMesh() : Skeleton(new StdMeshSkeleton)
|
||||||
|
|
||||||
StdMesh::~StdMesh()
|
StdMesh::~StdMesh()
|
||||||
{
|
{
|
||||||
|
if (vbo)
|
||||||
|
glDeleteBuffers(1, &vbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdMesh::PostInit()
|
void StdMesh::PostInit()
|
||||||
{
|
{
|
||||||
// Order submeshes so that opaque submeshes come before non-opaque ones
|
// Order submeshes so that opaque submeshes come before non-opaque ones
|
||||||
std::sort(SubMeshes.begin(), SubMeshes.end(), StdMeshSubMeshVisibilityCmpPred());
|
std::sort(SubMeshes.begin(), SubMeshes.end(), StdMeshSubMeshVisibilityCmpPred());
|
||||||
|
UpdateVBO();
|
||||||
}
|
}
|
||||||
|
|
||||||
StdSubMeshInstance::StdSubMeshInstance(StdMeshInstance& instance, const StdSubMesh& submesh, float completion):
|
void StdMesh::UpdateVBO()
|
||||||
Vertices(submesh.GetNumVertices()),
|
{
|
||||||
Material(NULL), CurrentFaceOrdering(FO_Fixed)
|
// We're only uploading vertices once, so there shouldn't be a VBO so far
|
||||||
|
assert(vbo == 0);
|
||||||
|
if (vbo != 0)
|
||||||
|
glDeleteBuffers(1, &vbo);
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
|
||||||
|
// Calculate total number of vertices
|
||||||
|
size_t total_vertices = SharedVertices.size();
|
||||||
|
for (auto &submesh : SubMeshes)
|
||||||
|
{
|
||||||
|
total_vertices += submesh.GetNumVertices();
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
// Unmapping the buffer may fail for certain reasons, in which case we need to try again.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Allocate VBO backing memory. If this mesh's skeleton has no animations
|
||||||
|
// defined, we assume that the VBO will not change frequently.
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, total_vertices * sizeof(StdMeshVertex), NULL, GL_STATIC_DRAW);
|
||||||
|
void *map = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
||||||
|
uint8_t *buffer = static_cast<uint8_t*>(map);
|
||||||
|
uint8_t *cursor = buffer;
|
||||||
|
|
||||||
|
// Add shared vertices to buffer
|
||||||
|
if (!SharedVertices.empty())
|
||||||
|
{
|
||||||
|
size_t shared_vertices_size = SharedVertices.size() * sizeof(SharedVertices[0]);
|
||||||
|
std::memcpy(cursor, &SharedVertices[0], shared_vertices_size);
|
||||||
|
cursor += shared_vertices_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all submeshes to buffer
|
||||||
|
for (auto &submesh : SubMeshes)
|
||||||
|
{
|
||||||
|
// Store the offset, so the render code can use it later
|
||||||
|
submesh.buffer_offset = cursor - buffer;
|
||||||
|
|
||||||
|
if (submesh.Vertices.empty()) continue;
|
||||||
|
size_t vertices_size = sizeof(submesh.Vertices[0]) * submesh.Vertices.size();
|
||||||
|
std::memcpy(cursor, &submesh.Vertices[0], vertices_size);
|
||||||
|
cursor += vertices_size;
|
||||||
|
}
|
||||||
|
} while (glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE);
|
||||||
|
// Unbind the buffer so following rendering calls do not use it
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StdSubMeshInstance::StdSubMeshInstance(StdMeshInstance& instance, const StdSubMesh& submesh, float completion):
|
||||||
|
base(&submesh), Material(NULL), CurrentFaceOrdering(FO_Fixed)
|
||||||
{
|
{
|
||||||
// Copy initial Vertices/Faces
|
|
||||||
for (unsigned int i = 0; i < submesh.GetNumVertices(); ++i)
|
|
||||||
Vertices[i] = submesh.GetVertex(i);
|
|
||||||
LoadFacesForCompletion(instance, submesh, completion);
|
LoadFacesForCompletion(instance, submesh, completion);
|
||||||
|
|
||||||
SetMaterial(submesh.GetMaterial());
|
SetMaterial(submesh.GetMaterial());
|
||||||
|
@ -577,8 +627,8 @@ void StdSubMeshInstance::LoadFacesForCompletion(StdMeshInstance& instance, const
|
||||||
// At this point, all vertices are in the OGRE coordinate frame, and Z in OGRE equals
|
// At this point, all vertices are in the OGRE coordinate frame, and Z in OGRE equals
|
||||||
// Y in Clonk, so we are fine without additional transformation.
|
// Y in Clonk, so we are fine without additional transformation.
|
||||||
const StdMeshVertex* vertices;
|
const StdMeshVertex* vertices;
|
||||||
if(GetNumVertices() > 0)
|
if(submesh.GetNumVertices() > 0)
|
||||||
vertices = &GetVertices()[0];
|
vertices = &submesh.GetVertices()[0];
|
||||||
else
|
else
|
||||||
vertices = &instance.GetSharedVertices()[0];
|
vertices = &instance.GetSharedVertices()[0];
|
||||||
SortFacesArray(vertices, Faces, FO_FarthestToNearest, StdMeshMatrix::Identity());
|
SortFacesArray(vertices, Faces, FO_FarthestToNearest, StdMeshMatrix::Identity());
|
||||||
|
@ -753,7 +803,7 @@ bool StdMeshInstance::AnimationNode::GetBoneTransform(unsigned int bone, StdMesh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdMeshInstance::AnimationNode::CompileFunc(StdCompiler* pComp, const StdMesh* Mesh)
|
void StdMeshInstance::AnimationNode::CompileFunc(StdCompiler* pComp, const StdMesh *Mesh)
|
||||||
{
|
{
|
||||||
static const StdEnumEntry<NodeType> NodeTypes[] =
|
static const StdEnumEntry<NodeType> NodeTypes[] =
|
||||||
{
|
{
|
||||||
|
@ -961,15 +1011,11 @@ void StdMeshInstance::AttachedMesh::MapBonesOfChildToParent(const StdMeshSkeleto
|
||||||
}
|
}
|
||||||
|
|
||||||
StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
|
StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
|
||||||
Mesh(&mesh), SharedVertices(mesh.GetSharedVertices().size()), vbo(0), Completion(completion),
|
Mesh(&mesh), Completion(completion),
|
||||||
BoneTransforms(Mesh->GetSkeleton().GetNumBones(), StdMeshMatrix::Identity()),
|
BoneTransforms(Mesh->GetSkeleton().GetNumBones(), StdMeshMatrix::Identity()),
|
||||||
SubMeshInstances(Mesh->GetNumSubMeshes()), AttachParent(NULL),
|
SubMeshInstances(Mesh->GetNumSubMeshes()), AttachParent(NULL),
|
||||||
BoneTransformsDirty(false)
|
BoneTransformsDirty(false)
|
||||||
{
|
{
|
||||||
// Copy initial shared vertices
|
|
||||||
for (unsigned int i = 0; i < SharedVertices.size(); ++i)
|
|
||||||
SharedVertices[i] = mesh.GetSharedVertices()[i];
|
|
||||||
|
|
||||||
// Create submesh instances
|
// Create submesh instances
|
||||||
for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
|
for (unsigned int i = 0; i < Mesh->GetNumSubMeshes(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -979,9 +1025,6 @@ StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
|
||||||
|
|
||||||
// copy, order is fine at the moment since only default materials are used.
|
// copy, order is fine at the moment since only default materials are used.
|
||||||
SubMeshInstancesOrdered = SubMeshInstances;
|
SubMeshInstancesOrdered = SubMeshInstances;
|
||||||
|
|
||||||
// Initialize VBOs for non-animated mesh instances
|
|
||||||
UpdateVBO();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StdMeshInstance::~StdMeshInstance()
|
StdMeshInstance::~StdMeshInstance()
|
||||||
|
@ -1003,9 +1046,6 @@ StdMeshInstance::~StdMeshInstance()
|
||||||
{
|
{
|
||||||
delete SubMeshInstances[i];
|
delete SubMeshInstances[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vbo)
|
|
||||||
glDeleteBuffers(1, &vbo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
|
void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
|
||||||
|
@ -1301,6 +1341,14 @@ const StdMeshMatrix& StdMeshInstance::GetBoneTransform(size_t i) const
|
||||||
return BoneTransforms[i];
|
return BoneTransforms[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t StdMeshInstance::GetBoneCount() const
|
||||||
|
{
|
||||||
|
if ((AttachParent != NULL) && (AttachParent->GetFlags() & AM_MatchSkeleton))
|
||||||
|
return AttachParent->MatchedBoneInParentSkeleton.size();
|
||||||
|
else
|
||||||
|
return BoneTransforms.size();
|
||||||
|
}
|
||||||
|
|
||||||
bool StdMeshInstance::UpdateBoneTransforms()
|
bool StdMeshInstance::UpdateBoneTransforms()
|
||||||
{
|
{
|
||||||
bool was_dirty = BoneTransformsDirty;
|
bool was_dirty = BoneTransformsDirty;
|
||||||
|
@ -1345,22 +1393,6 @@ bool StdMeshInstance::UpdateBoneTransforms()
|
||||||
if (parent) BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
|
if (parent) BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute transformation for each vertex. We could later think about
|
|
||||||
// doing this on the GPU using a vertex shader. This would then probably
|
|
||||||
// need to go to CStdGL::PerformMesh.
|
|
||||||
// But first, we need to move vertex data to the GPU.
|
|
||||||
if(!Mesh->GetSharedVertices().empty())
|
|
||||||
ApplyBoneTransformToVertices(Mesh->GetSharedVertices(), SharedVertices);
|
|
||||||
for (unsigned int i = 0; i < SubMeshInstances.size(); ++i)
|
|
||||||
{
|
|
||||||
const StdSubMesh& submesh = Mesh->GetSubMesh(i);
|
|
||||||
if(!submesh.GetVertices().empty())
|
|
||||||
ApplyBoneTransformToVertices(submesh.GetVertices(), SubMeshInstances[i]->Vertices);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertexes have moved, update VBO
|
|
||||||
UpdateVBO();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update attachment's attach transformations. Note this is done recursively.
|
// Update attachment's attach transformations. Note this is done recursively.
|
||||||
|
@ -1399,58 +1431,6 @@ bool StdMeshInstance::UpdateBoneTransforms()
|
||||||
return was_dirty;
|
return was_dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdMeshInstance::UpdateVBO()
|
|
||||||
{
|
|
||||||
bool has_animation = Mesh->GetSkeleton().IsAnimated();
|
|
||||||
|
|
||||||
// Create VBO on demand
|
|
||||||
if (vbo == 0)
|
|
||||||
{
|
|
||||||
glGenBuffers(1, &vbo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate total number of vertices
|
|
||||||
size_t total_vertices = GetNumSharedVertices();
|
|
||||||
for (auto &submesh : SubMeshInstances)
|
|
||||||
{
|
|
||||||
total_vertices += submesh->GetNumVertices();
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
||||||
// Unmapping the buffer may fail for certain reasons, in which case we need to try again.
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Allocate VBO backing memory. If this mesh's skeleton has no animations
|
|
||||||
// defined, we assume that the VBO will not change frequently.
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, total_vertices * sizeof(StdMeshVertex), NULL, has_animation ? GL_STREAM_DRAW : GL_STATIC_DRAW);
|
|
||||||
void *map = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
|
||||||
uint8_t *buffer = static_cast<uint8_t*>(map);
|
|
||||||
uint8_t *cursor = buffer;
|
|
||||||
|
|
||||||
// Add shared vertices to buffer
|
|
||||||
if (!SharedVertices.empty())
|
|
||||||
{
|
|
||||||
size_t shared_vertices_size = GetNumSharedVertices() * sizeof(SharedVertices[0]);
|
|
||||||
std::memcpy(cursor, &SharedVertices[0], shared_vertices_size);
|
|
||||||
cursor += shared_vertices_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all submeshes to buffer
|
|
||||||
for (auto &submesh : SubMeshInstances)
|
|
||||||
{
|
|
||||||
// Store the offset, so the render code can use it later
|
|
||||||
submesh->buffer_offset = cursor - buffer;
|
|
||||||
|
|
||||||
if (submesh->Vertices.empty()) continue;
|
|
||||||
size_t vertices_size = sizeof(submesh->Vertices[0]) * submesh->Vertices.size();
|
|
||||||
std::memcpy(cursor, &submesh->Vertices[0], vertices_size);
|
|
||||||
cursor += vertices_size;
|
|
||||||
}
|
|
||||||
} while (glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE);
|
|
||||||
// Unbind the buffer so following rendering calls do not use it
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StdMeshInstance::ReorderFaces(StdMeshMatrix* global_trans)
|
void StdMeshInstance::ReorderFaces(StdMeshMatrix* global_trans)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < SubMeshInstances.size(); ++i)
|
for (unsigned int i = 0; i < SubMeshInstances.size(); ++i)
|
||||||
|
@ -1461,8 +1441,8 @@ void StdMeshInstance::ReorderFaces(StdMeshMatrix* global_trans)
|
||||||
if(inst.Faces.size() > 0 && inst.CurrentFaceOrdering != StdSubMeshInstance::FO_Fixed)
|
if(inst.Faces.size() > 0 && inst.CurrentFaceOrdering != StdSubMeshInstance::FO_Fixed)
|
||||||
{
|
{
|
||||||
const StdMeshVertex* vertices;
|
const StdMeshVertex* vertices;
|
||||||
if(inst.GetNumVertices() > 0)
|
if(inst.GetSubMesh().GetNumVertices() > 0)
|
||||||
vertices = &inst.GetVertices()[0];
|
vertices = &inst.GetSubMesh().GetVertices()[0];
|
||||||
else
|
else
|
||||||
vertices = &GetSharedVertices()[0];
|
vertices = &GetSharedVertices()[0];
|
||||||
SortFacesArray(vertices, inst.Faces, inst.CurrentFaceOrdering, global_trans ? *global_trans : StdMeshMatrix::Identity());
|
SortFacesArray(vertices, inst.Faces, inst.CurrentFaceOrdering, global_trans ? *global_trans : StdMeshMatrix::Identity());
|
||||||
|
@ -1759,32 +1739,6 @@ bool StdMeshInstance::ExecuteAnimationNode(AnimationNode* node)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdMeshInstance::ApplyBoneTransformToVertices(const std::vector<StdSubMesh::Vertex>& mesh_vertices, std::vector<StdMeshVertex>& instance_vertices)
|
|
||||||
{
|
|
||||||
assert(mesh_vertices.size() == instance_vertices.size());
|
|
||||||
for (unsigned int j = 0; j < instance_vertices.size(); ++j)
|
|
||||||
{
|
|
||||||
const StdSubMesh::Vertex& vertex = mesh_vertices[j];
|
|
||||||
StdMeshVertex& instance_vertex = instance_vertices[j];
|
|
||||||
if (!vertex.BoneAssignments.empty())
|
|
||||||
{
|
|
||||||
instance_vertex.x = instance_vertex.y = instance_vertex.z = 0.0f;
|
|
||||||
instance_vertex.nx = instance_vertex.ny = instance_vertex.nz = 0.0f;
|
|
||||||
instance_vertex.u = vertex.u; instance_vertex.v = vertex.v;
|
|
||||||
|
|
||||||
for (unsigned int k = 0; k < vertex.BoneAssignments.size(); ++k)
|
|
||||||
{
|
|
||||||
const StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments[k];
|
|
||||||
instance_vertex += assignment.Weight * (GetBoneTransform(assignment.BoneIndex) * vertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
instance_vertex = vertex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StdMeshInstance::SetBoneTransformsDirty(bool value)
|
void StdMeshInstance::SetBoneTransformsDirty(bool value)
|
||||||
{
|
{
|
||||||
BoneTransformsDirty = value;
|
BoneTransformsDirty = value;
|
||||||
|
|
|
@ -149,12 +149,7 @@ class StdSubMesh
|
||||||
friend class StdMeshLoader;
|
friend class StdMeshLoader;
|
||||||
friend class StdMeshMaterialUpdate;
|
friend class StdMeshMaterialUpdate;
|
||||||
public:
|
public:
|
||||||
// Remember bone assignments for vertices
|
typedef StdMeshVertex Vertex;
|
||||||
class Vertex: public StdMeshVertex
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::vector<StdMeshVertexBoneAssignment> BoneAssignments;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::vector<Vertex>& GetVertices() const { return Vertices; }
|
const std::vector<Vertex>& GetVertices() const { return Vertices; }
|
||||||
const Vertex& GetVertex(size_t i) const { return Vertices[i]; }
|
const Vertex& GetVertex(size_t i) const { return Vertices[i]; }
|
||||||
|
@ -165,11 +160,15 @@ public:
|
||||||
|
|
||||||
const StdMeshMaterial& GetMaterial() const { return *Material; }
|
const StdMeshMaterial& GetMaterial() const { return *Material; }
|
||||||
|
|
||||||
|
// Return the offset into the backing vertex buffer where this SubMesh's data starts
|
||||||
|
size_t GetOffsetInBuffer() const { return buffer_offset; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StdSubMesh();
|
StdSubMesh();
|
||||||
|
|
||||||
std::vector<Vertex> Vertices; // Empty if we use shared vertices
|
std::vector<Vertex> Vertices; // Empty if we use shared vertices
|
||||||
std::vector<StdMeshFace> Faces;
|
std::vector<StdMeshFace> Faces;
|
||||||
|
size_t buffer_offset;
|
||||||
|
|
||||||
const StdMeshMaterial* Material;
|
const StdMeshMaterial* Material;
|
||||||
};
|
};
|
||||||
|
@ -198,7 +197,11 @@ public:
|
||||||
|
|
||||||
void PostInit();
|
void PostInit();
|
||||||
|
|
||||||
|
const GLuint GetVBO() const { return vbo; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
GLuint vbo;
|
||||||
|
void UpdateVBO();
|
||||||
|
|
||||||
StdMesh(const StdMesh& other); // non-copyable
|
StdMesh(const StdMesh& other); // non-copyable
|
||||||
StdMesh& operator=(const StdMesh& other); // non-assignable
|
StdMesh& operator=(const StdMesh& other); // non-assignable
|
||||||
|
@ -230,18 +233,12 @@ public:
|
||||||
|
|
||||||
void CompileFunc(StdCompiler* pComp);
|
void CompileFunc(StdCompiler* pComp);
|
||||||
|
|
||||||
// Get vertex of instance, with current animation applied. This needs to
|
|
||||||
// go elsewhere if/when we want to calculate this on the hardware.
|
|
||||||
const std::vector<StdMeshVertex>& GetVertices() const { return Vertices; }
|
|
||||||
size_t GetNumVertices() const { return Vertices.size(); }
|
|
||||||
// Return the offset into the backing vertex buffer where this SubMesh's data starts
|
|
||||||
size_t GetOffsetInBuffer() const { return buffer_offset; }
|
|
||||||
|
|
||||||
// Get face of instance. The instance faces are the same as the mesh faces,
|
// Get face of instance. The instance faces are the same as the mesh faces,
|
||||||
// with the exception that they are differently ordered, depending on the
|
// with the exception that they are differently ordered, depending on the
|
||||||
// current FaceOrdering. See FaceOrdering in StdMeshInstance.
|
// current FaceOrdering. See FaceOrdering in StdMeshInstance.
|
||||||
const StdMeshFace* GetFaces() const { return Faces.size() > 0 ? &Faces[0] : 0; }
|
const StdMeshFace* GetFaces() const { return Faces.size() > 0 ? &Faces[0] : 0; }
|
||||||
size_t GetNumFaces() const { return Faces.size(); }
|
size_t GetNumFaces() const { return Faces.size(); }
|
||||||
|
const StdSubMesh &GetSubMesh() const { return *base; }
|
||||||
|
|
||||||
unsigned int GetTexturePhase(size_t pass, size_t texunit) const { return PassData[pass].TexUnits[texunit].Phase; }
|
unsigned int GetTexturePhase(size_t pass, size_t texunit) const { return PassData[pass].TexUnits[texunit].Phase; }
|
||||||
double GetTexturePosition(size_t pass, size_t texunit) const { return PassData[pass].TexUnits[texunit].Position; }
|
double GetTexturePosition(size_t pass, size_t texunit) const { return PassData[pass].TexUnits[texunit].Position; }
|
||||||
|
@ -254,14 +251,9 @@ protected:
|
||||||
void SetFaceOrdering(const StdSubMesh& submesh, FaceOrdering ordering);
|
void SetFaceOrdering(const StdSubMesh& submesh, FaceOrdering ordering);
|
||||||
void SetFaceOrderingForClrModulation(const StdSubMesh& submesh, uint32_t clrmod);
|
void SetFaceOrderingForClrModulation(const StdSubMesh& submesh, uint32_t clrmod);
|
||||||
|
|
||||||
// Vertices transformed according to current animation
|
const StdSubMesh *base;
|
||||||
// Faces sorted according to current face ordering
|
// Faces sorted according to current face ordering
|
||||||
// TODO: We can skip these if we decide to either
|
|
||||||
// a) recompute Vertex positions each frame or
|
|
||||||
// b) compute them on the GPU
|
|
||||||
std::vector<StdMeshVertex> Vertices;
|
|
||||||
std::vector<StdMeshFace> Faces; // TODO: Indices could also be stored on GPU in a vbo (element index array). Should be done in a next step if at all.
|
std::vector<StdMeshFace> Faces; // TODO: Indices could also be stored on GPU in a vbo (element index array). Should be done in a next step if at all.
|
||||||
size_t buffer_offset;
|
|
||||||
|
|
||||||
const StdMeshMaterial* Material;
|
const StdMeshMaterial* Material;
|
||||||
|
|
||||||
|
@ -424,7 +416,7 @@ public:
|
||||||
ValueProvider* GetWeightProvider() { assert(Type == LinearInterpolationNode); return LinearInterpolation.Weight; }
|
ValueProvider* GetWeightProvider() { assert(Type == LinearInterpolationNode); return LinearInterpolation.Weight; }
|
||||||
C4Real GetWeight() const { assert(Type == LinearInterpolationNode); return LinearInterpolation.Weight->Value; }
|
C4Real GetWeight() const { assert(Type == LinearInterpolationNode); return LinearInterpolation.Weight->Value; }
|
||||||
|
|
||||||
void CompileFunc(StdCompiler* pComp, const StdMesh* Mesh);
|
void CompileFunc(StdCompiler* pComp, const StdMesh *Mesh);
|
||||||
void DenumeratePointers();
|
void DenumeratePointers();
|
||||||
void ClearPointers(class C4Object* pObj);
|
void ClearPointers(class C4Object* pObj);
|
||||||
|
|
||||||
|
@ -499,6 +491,9 @@ public:
|
||||||
void DenumeratePointers();
|
void DenumeratePointers();
|
||||||
bool ClearPointers(class C4Object* pObj);
|
bool ClearPointers(class C4Object* pObj);
|
||||||
|
|
||||||
|
unsigned int GetParentBone() const { return ParentBone; }
|
||||||
|
unsigned int GetChildBone() const { return ChildBone; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int ParentBone;
|
unsigned int ParentBone;
|
||||||
unsigned int ChildBone;
|
unsigned int ChildBone;
|
||||||
|
@ -521,8 +516,8 @@ public:
|
||||||
void SetFaceOrdering(FaceOrdering ordering);
|
void SetFaceOrdering(FaceOrdering ordering);
|
||||||
void SetFaceOrderingForClrModulation(uint32_t clrmod);
|
void SetFaceOrderingForClrModulation(uint32_t clrmod);
|
||||||
|
|
||||||
const std::vector<StdMeshVertex>& GetSharedVertices() const { return SharedVertices; }
|
const std::vector<StdMeshVertex>& GetSharedVertices() const { return Mesh->GetSharedVertices(); }
|
||||||
size_t GetNumSharedVertices() const { return SharedVertices.size(); }
|
size_t GetNumSharedVertices() const { return GetSharedVertices().size(); }
|
||||||
|
|
||||||
// Set completion of the mesh. For incompleted meshes not all faces will be available.
|
// Set completion of the mesh. For incompleted meshes not all faces will be available.
|
||||||
void SetCompletion(float completion);
|
void SetCompletion(float completion);
|
||||||
|
@ -569,6 +564,7 @@ public:
|
||||||
void SetMaterial(size_t i, const StdMeshMaterial& material);
|
void SetMaterial(size_t i, const StdMeshMaterial& material);
|
||||||
|
|
||||||
const StdMeshMatrix& GetBoneTransform(size_t i) const;
|
const StdMeshMatrix& GetBoneTransform(size_t i) const;
|
||||||
|
size_t GetBoneCount() const;
|
||||||
|
|
||||||
// Update bone transformation matrices, vertex positions and final attach transformations of attached children.
|
// Update bone transformation matrices, vertex positions and final attach transformations of attached children.
|
||||||
// This is called recursively for attached children, so there is no need to call it on attached children only
|
// This is called recursively for attached children, so there is no need to call it on attached children only
|
||||||
|
@ -593,9 +589,7 @@ public:
|
||||||
void DenumeratePointers();
|
void DenumeratePointers();
|
||||||
void ClearPointers(class C4Object* pObj);
|
void ClearPointers(class C4Object* pObj);
|
||||||
|
|
||||||
const StdMesh& GetMesh() const { assert(Mesh != NULL); return *Mesh; }
|
const StdMesh& GetMesh() const { return *Mesh; }
|
||||||
|
|
||||||
GLuint GetVBO() const { return vbo; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::vector<AnimationNode*> AnimationNodeList;
|
typedef std::vector<AnimationNode*> AnimationNodeList;
|
||||||
|
@ -606,14 +600,10 @@ protected:
|
||||||
void ApplyBoneTransformToVertices(const std::vector<StdSubMesh::Vertex>& mesh_vertices, std::vector<StdMeshVertex>& instance_vertices);
|
void ApplyBoneTransformToVertices(const std::vector<StdSubMesh::Vertex>& mesh_vertices, std::vector<StdMeshVertex>& instance_vertices);
|
||||||
void SetBoneTransformsDirty(bool value);
|
void SetBoneTransformsDirty(bool value);
|
||||||
|
|
||||||
const StdMesh* Mesh;
|
const StdMesh *Mesh;
|
||||||
|
|
||||||
float Completion; // NoSave
|
float Completion; // NoSave
|
||||||
|
|
||||||
std::vector<StdMeshVertex> SharedVertices;
|
|
||||||
GLuint vbo;
|
|
||||||
void UpdateVBO();
|
|
||||||
|
|
||||||
AnimationNodeList AnimationNodes; // for simple lookup of animation nodes by their unique number
|
AnimationNodeList AnimationNodes; // for simple lookup of animation nodes by their unique number
|
||||||
AnimationNodeList AnimationStack; // contains top level nodes only, ordered by slot number
|
AnimationNodeList AnimationStack; // contains top level nodes only, ordered by slot number
|
||||||
std::vector<StdMeshMatrix> BoneTransforms;
|
std::vector<StdMeshMatrix> BoneTransforms;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* OpenClonk, http://www.openclonk.org
|
* OpenClonk, http://www.openclonk.org
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010-2013, The OpenClonk Team and contributors
|
* Copyright (c) 2010-2015, The OpenClonk Team and contributors
|
||||||
*
|
*
|
||||||
* Distributed under the terms of the ISC license; see accompanying file
|
* Distributed under the terms of the ISC license; see accompanying file
|
||||||
* "COPYING" for details.
|
* "COPYING" for details.
|
||||||
|
@ -518,26 +518,39 @@ StdMesh *StdMeshLoader::LoadMeshBinary(const char *sourcefile, size_t length, co
|
||||||
// Read bone assignments
|
// Read bone assignments
|
||||||
std::vector<Ogre::Mesh::BoneAssignment> &boneAssignments = (csm.hasSharedVertices ? cmesh.boneAssignments : csm.boneAssignments);
|
std::vector<Ogre::Mesh::BoneAssignment> &boneAssignments = (csm.hasSharedVertices ? cmesh.boneAssignments : csm.boneAssignments);
|
||||||
assert(!csm.hasSharedVertices || csm.boneAssignments.empty());
|
assert(!csm.hasSharedVertices || csm.boneAssignments.empty());
|
||||||
BOOST_FOREACH(const Ogre::Mesh::BoneAssignment &ba, boneAssignments)
|
for(const auto &ba : boneAssignments)
|
||||||
{
|
{
|
||||||
if (ba.vertex >= sm.GetNumVertices())
|
if (ba.vertex >= sm.GetNumVertices())
|
||||||
throw Ogre::Mesh::VertexNotFound();
|
throw Ogre::Mesh::VertexNotFound();
|
||||||
if (bone_lookup.find(ba.bone) == bone_lookup.end())
|
if (bone_lookup.find(ba.bone) == bone_lookup.end())
|
||||||
throw Ogre::Skeleton::BoneNotFound();
|
throw Ogre::Skeleton::BoneNotFound();
|
||||||
StdMeshVertexBoneAssignment assignment;
|
size_t bone_index = bone_lookup[ba.bone];
|
||||||
assignment.BoneIndex = bone_lookup[ba.bone];
|
// Check quickly if all weight slots are used
|
||||||
assignment.Weight = ba.weight;
|
StdSubMesh::Vertex &vertex = sm.Vertices[ba.vertex];
|
||||||
sm.Vertices[ba.vertex].BoneAssignments.push_back(assignment);
|
if (vertex.bone_weight[StdMeshVertex::MaxBoneWeightCount - 1] != 0)
|
||||||
|
{
|
||||||
|
throw Ogre::Mesh::NotImplemented("Vertex is influenced by too many bones");
|
||||||
|
}
|
||||||
|
for (size_t weight_index = 0; weight_index < StdMeshVertex::MaxBoneWeightCount; ++weight_index)
|
||||||
|
{
|
||||||
|
if (vertex.bone_weight[weight_index] == 0)
|
||||||
|
{
|
||||||
|
vertex.bone_weight[weight_index] = ba.weight;
|
||||||
|
vertex.bone_index[weight_index] = bone_index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize bone assignments
|
// Normalize bone assignments
|
||||||
BOOST_FOREACH(StdSubMesh::Vertex &vertex, sm.Vertices)
|
for(StdSubMesh::Vertex &vertex : sm.Vertices)
|
||||||
{
|
{
|
||||||
float sum = 0;
|
float sum = 0;
|
||||||
BOOST_FOREACH(StdMeshVertexBoneAssignment &ba, vertex.BoneAssignments)
|
for (float weight : vertex.bone_weight)
|
||||||
sum += ba.Weight;
|
sum += weight;
|
||||||
BOOST_FOREACH(StdMeshVertexBoneAssignment &ba, vertex.BoneAssignments)
|
if (sum != 0)
|
||||||
ba.Weight /= sum;
|
for (float &weight : vertex.bone_weight)
|
||||||
|
weight /= sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mesh.release();
|
return mesh.release();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* OpenClonk, http://www.openclonk.org
|
* OpenClonk, http://www.openclonk.org
|
||||||
*
|
*
|
||||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||||
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
|
* Copyright (c) 2009-2015, The OpenClonk Team and contributors
|
||||||
*
|
*
|
||||||
* Distributed under the terms of the ISC license; see accompanying file
|
* Distributed under the terms of the ISC license; see accompanying file
|
||||||
* "COPYING" for details.
|
* "COPYING" for details.
|
||||||
|
@ -228,11 +228,22 @@ void StdMeshLoader::StdMeshXML::LoadBoneAssignments(StdMesh& mesh, std::vector<S
|
||||||
// bone_lookup[mesh->GetSkeleton().GetBone(i).ID] = i;
|
// bone_lookup[mesh->GetSkeleton().GetBone(i).ID] = i;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
// Find first bone assignment with a zero weight (i.e. is unused)
|
||||||
StdSubMesh::Vertex& vertex = vertices[VertexIndex];
|
StdSubMesh::Vertex& vertex = vertices[VertexIndex];
|
||||||
vertex.BoneAssignments.push_back(StdMeshVertexBoneAssignment());
|
// Check quickly if all weight slots are used
|
||||||
StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments.back();
|
if (vertex.bone_weight[StdMeshVertex::MaxBoneWeightCount - 1] != 0)
|
||||||
assignment.BoneIndex = bone->Index;
|
{
|
||||||
assignment.Weight = weight;
|
Error(FormatString("Vertex %d is influenced by more than %d bones", VertexIndex, StdMeshVertex::MaxBoneWeightCount), vertexboneassignment_elem);
|
||||||
|
}
|
||||||
|
for (size_t weight_index = 0; weight_index < StdMeshVertex::MaxBoneWeightCount; ++weight_index)
|
||||||
|
{
|
||||||
|
if (vertex.bone_weight[weight_index] == 0)
|
||||||
|
{
|
||||||
|
vertex.bone_weight[weight_index] = weight;
|
||||||
|
vertex.bone_index[weight_index] = bone->Index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize vertex bone assignment weights (this is not guaranteed in the
|
// Normalize vertex bone assignment weights (this is not guaranteed in the
|
||||||
|
@ -240,11 +251,12 @@ void StdMeshLoader::StdMeshXML::LoadBoneAssignments(StdMesh& mesh, std::vector<S
|
||||||
for (unsigned int i = 0; i < vertices.size(); ++i)
|
for (unsigned int i = 0; i < vertices.size(); ++i)
|
||||||
{
|
{
|
||||||
StdSubMesh::Vertex& vertex = vertices[i];
|
StdSubMesh::Vertex& vertex = vertices[i];
|
||||||
float sum = 0.0f;
|
float sum = 0.0;
|
||||||
for (unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j)
|
for (float weight : vertex.bone_weight)
|
||||||
sum += vertex.BoneAssignments[j].Weight;
|
sum += weight;
|
||||||
for (unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j)
|
if (sum != 0)
|
||||||
vertex.BoneAssignments[j].Weight /= sum;
|
for (float &weight : vertex.bone_weight)
|
||||||
|
weight /= sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -860,6 +860,7 @@ bool StdMeshMaterialProgram::CompileShader(StdMeshMaterialLoader& loader, C4Shad
|
||||||
uniformNames[C4SSU_AmbientTex] = "ambientTex";
|
uniformNames[C4SSU_AmbientTex] = "ambientTex";
|
||||||
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
|
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
|
||||||
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
|
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
|
||||||
|
uniformNames[C4SSU_Bones] = "bones";
|
||||||
for (unsigned int i = 0; i < ParameterNames.size(); ++i)
|
for (unsigned int i = 0; i < ParameterNames.size(); ++i)
|
||||||
uniformNames[C4SSU_Count + i] = ParameterNames[i].getData();
|
uniformNames[C4SSU_Count + i] = ParameterNames[i].getData();
|
||||||
uniformNames[C4SSU_Count + ParameterNames.size()] = NULL;
|
uniformNames[C4SSU_Count + ParameterNames.size()] = NULL;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* OpenClonk, http://www.openclonk.org
|
* OpenClonk, http://www.openclonk.org
|
||||||
*
|
*
|
||||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||||
* Copyright (c) 2011-2013, The OpenClonk Team and contributors
|
* Copyright (c) 2011-2015, The OpenClonk Team and contributors
|
||||||
*
|
*
|
||||||
* Distributed under the terms of the ISC license; see accompanying file
|
* Distributed under the terms of the ISC license; see accompanying file
|
||||||
* "COPYING" for details.
|
* "COPYING" for details.
|
||||||
|
@ -29,15 +29,30 @@ struct StdMeshVector
|
||||||
static StdMeshVector Cross(const StdMeshVector& lhs, const StdMeshVector& rhs);
|
static StdMeshVector Cross(const StdMeshVector& lhs, const StdMeshVector& rhs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct StdMeshVertex
|
struct StdMeshVertex
|
||||||
{
|
{
|
||||||
|
static const size_t MaxBoneWeightCount = 8;
|
||||||
|
|
||||||
// Match GL_T2F_N3F_V3F
|
// Match GL_T2F_N3F_V3F
|
||||||
float u, v;
|
float u, v;
|
||||||
float nx, ny, nz;
|
float nx, ny, nz;
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
|
|
||||||
|
float bone_weight[MaxBoneWeightCount];
|
||||||
|
uint16_t bone_index[MaxBoneWeightCount];
|
||||||
|
|
||||||
|
char _padding[16];
|
||||||
|
|
||||||
|
StdMeshVertex() : u(0), v(0), nx(0), ny(0), nz(0), x(0), y(0), z(0)
|
||||||
|
{
|
||||||
|
std::uninitialized_fill(std::begin(bone_weight), std::end(bone_weight), 0);
|
||||||
|
std::uninitialized_fill(std::begin(bone_index), std::end(bone_index), 0);
|
||||||
|
std::uninitialized_fill(std::begin(_padding), std::end(_padding), 0);
|
||||||
|
}
|
||||||
//void Normalize();
|
//void Normalize();
|
||||||
};
|
};
|
||||||
|
static_assert((sizeof(StdMeshVertex) & 31) == 0, "StdMeshVertex should be a multiple of 32 bytes");
|
||||||
|
|
||||||
struct StdMeshQuaternion
|
struct StdMeshQuaternion
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue