forked from Mirrors/openclonk
GL: Render meshes out of a VBO
While we're still not doing skinning on the GPU, copying the vertex data to a VBO immediately after updating the animation allows us to re-use that data for unanimated meshes. It also allows us to store unanimated data on the GPU, instead of transferring it over the bus for each frame.stable-6.1
parent
5b0759952e
commit
c66833e2db
|
@ -2,7 +2,7 @@
|
|||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2013, The OpenClonk Team and contributors
|
||||
* Copyright (c) 2013-2015, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
|
@ -456,7 +456,10 @@ namespace
|
|||
const StdMeshMaterial& material = instance.GetMaterial();
|
||||
assert(material.BestTechniqueIndex != -1);
|
||||
const StdMeshMaterialTechnique& technique = material.Techniques[material.BestTechniqueIndex];
|
||||
const StdMeshVertex* vertices = instance.GetVertices().empty() ? &mesh_instance.GetSharedVertices()[0] : &instance.GetVertices()[0];
|
||||
|
||||
bool using_shared_vertices = instance.GetVertices().empty();
|
||||
GLuint vbo = mesh_instance.GetVBO();
|
||||
size_t buffer_offset = using_shared_vertices ? 0 : instance.GetOffsetInBuffer();
|
||||
|
||||
// Render each pass
|
||||
for (unsigned int i = 0; i < technique.Passes.size(); ++i)
|
||||
|
@ -525,11 +528,12 @@ namespace
|
|||
glBlendFunc(OgreBlendTypeToGL(pass.SceneBlendFactors[0]), GL_ONE);
|
||||
}
|
||||
|
||||
// TODO: Use vbo if available.
|
||||
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(StdMeshVertex), &vertices->u);
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(StdMeshVertex), &vertices->x);
|
||||
glNormalPointer(GL_FLOAT, sizeof(StdMeshVertex), &vertices->nx);
|
||||
#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);
|
||||
|
||||
|
@ -649,6 +653,7 @@ namespace
|
|||
glMatrixMode(GL_MODELVIEW);
|
||||
glDrawElements(GL_TRIANGLES, instance.GetNumFaces()*3, GL_UNSIGNED_INT, instance.GetFaces());
|
||||
call.Finish();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
if(!pass.DepthCheck)
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* 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
|
||||
* "COPYING" for details.
|
||||
|
@ -552,8 +552,8 @@ void StdMesh::PostInit()
|
|||
}
|
||||
|
||||
StdSubMeshInstance::StdSubMeshInstance(StdMeshInstance& instance, const StdSubMesh& submesh, float completion):
|
||||
Vertices(submesh.GetNumVertices()),
|
||||
Material(NULL), CurrentFaceOrdering(FO_Fixed)
|
||||
Vertices(submesh.GetNumVertices()),
|
||||
Material(NULL), CurrentFaceOrdering(FO_Fixed)
|
||||
{
|
||||
// Copy initial Vertices/Faces
|
||||
for (unsigned int i = 0; i < submesh.GetNumVertices(); ++i)
|
||||
|
@ -961,7 +961,7 @@ void StdMeshInstance::AttachedMesh::MapBonesOfChildToParent(const StdMeshSkeleto
|
|||
}
|
||||
|
||||
StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
|
||||
Mesh(&mesh), SharedVertices(mesh.GetSharedVertices().size()), Completion(completion),
|
||||
Mesh(&mesh), SharedVertices(mesh.GetSharedVertices().size()), vbo(0), Completion(completion),
|
||||
BoneTransforms(Mesh->GetSkeleton().GetNumBones(), StdMeshMatrix::Identity()),
|
||||
SubMeshInstances(Mesh->GetNumSubMeshes()), AttachParent(NULL),
|
||||
BoneTransformsDirty(false)
|
||||
|
@ -979,6 +979,9 @@ StdMeshInstance::StdMeshInstance(const StdMesh& mesh, float completion):
|
|||
|
||||
// copy, order is fine at the moment since only default materials are used.
|
||||
SubMeshInstancesOrdered = SubMeshInstances;
|
||||
|
||||
// Initialize VBOs for non-animated mesh instances
|
||||
UpdateVBO();
|
||||
}
|
||||
|
||||
StdMeshInstance::~StdMeshInstance()
|
||||
|
@ -997,7 +1000,12 @@ StdMeshInstance::~StdMeshInstance()
|
|||
|
||||
// Delete submeshes
|
||||
for (unsigned int i = 0; i < SubMeshInstances.size(); ++i)
|
||||
{
|
||||
delete SubMeshInstances[i];
|
||||
}
|
||||
|
||||
if (vbo)
|
||||
glDeleteBuffers(1, &vbo);
|
||||
}
|
||||
|
||||
void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
|
||||
|
@ -1350,6 +1358,9 @@ bool StdMeshInstance::UpdateBoneTransforms()
|
|||
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.
|
||||
|
@ -1388,6 +1399,58 @@ bool StdMeshInstance::UpdateBoneTransforms()
|
|||
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)
|
||||
{
|
||||
for (unsigned int i = 0; i < SubMeshInstances.size(); ++i)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* 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
|
||||
* "COPYING" for details.
|
||||
|
@ -116,6 +116,7 @@ public:
|
|||
const StdMeshBone* GetBoneByName(const StdStrBuf& name) const;
|
||||
|
||||
const StdMeshAnimation* GetAnimationByName(const StdStrBuf& name) const;
|
||||
bool IsAnimated() const { return !Animations.empty(); }
|
||||
|
||||
// TODO: This code should maybe better be placed in StdMeshLoader...
|
||||
void MirrorAnimation(const StdMeshAnimation& animation);
|
||||
|
@ -233,6 +234,8 @@ public:
|
|||
// 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,
|
||||
// with the exception that they are differently ordered, depending on the
|
||||
|
@ -258,6 +261,7 @@ protected:
|
|||
// 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.
|
||||
size_t buffer_offset;
|
||||
|
||||
const StdMeshMaterial* Material;
|
||||
|
||||
|
@ -281,7 +285,6 @@ protected:
|
|||
FaceOrdering CurrentFaceOrdering; // NoSave
|
||||
|
||||
// TODO: GLuint texenv_list; // NoSave, texture environment setup could be stored in a display list (w/ and w/o ClrMod). What about PlayerColor?
|
||||
// TODO: GLuint vbo; // NoSave, replacing vertices list -- can be mapped into memory for writing. Should be moved to StdMesh once we apply skeletal transformation on the GPU.
|
||||
|
||||
private:
|
||||
StdSubMeshInstance(const StdSubMeshInstance& other); // noncopyable
|
||||
|
@ -592,6 +595,8 @@ public:
|
|||
|
||||
const StdMesh& GetMesh() const { assert(Mesh != NULL); return *Mesh; }
|
||||
|
||||
GLuint GetVBO() const { return vbo; }
|
||||
|
||||
protected:
|
||||
typedef std::vector<AnimationNode*> AnimationNodeList;
|
||||
|
||||
|
@ -606,6 +611,8 @@ protected:
|
|||
float Completion; // NoSave
|
||||
|
||||
std::vector<StdMeshVertex> SharedVertices;
|
||||
GLuint vbo;
|
||||
void UpdateVBO();
|
||||
|
||||
AnimationNodeList AnimationNodes; // for simple lookup of animation nodes by their unique number
|
||||
AnimationNodeList AnimationStack; // contains top level nodes only, ordered by slot number
|
||||
|
|
Loading…
Reference in New Issue