Add loading of sk. meshes, skeletons and animations
This commit is contained in:
parent
cb7f76c8f0
commit
e34fb2a900
147
src/assets/animation.cpp
Normal file
147
src/assets/animation.cpp
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#include "animation.hpp"
|
||||||
|
#include "skeleton.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
std::shared_ptr<assets::Animation> assets::Animation::LoadFromFile(const std::string& filename, const Skeleton* skeleton)
|
||||||
|
{
|
||||||
|
std::ifstream ifs(filename, std::ios::binary);
|
||||||
|
|
||||||
|
if (!ifs.is_open())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to open animation file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Animation> anim = std::make_shared<Animation>();
|
||||||
|
|
||||||
|
int last_frame = 0;
|
||||||
|
std::vector<size_t> frame_indices;
|
||||||
|
|
||||||
|
auto FillFrameRefs = [&](int end_frame)
|
||||||
|
{
|
||||||
|
int channel_start = ((int)anim->channels_.size() - 1) * (int)anim->num_frames_;
|
||||||
|
int target_size = channel_start + end_frame;
|
||||||
|
size_t num_frame_refs = frame_indices.size();
|
||||||
|
|
||||||
|
if (num_frame_refs >= target_size)
|
||||||
|
{
|
||||||
|
return; // Already filled
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_frame_refs % anim->num_frames_ == 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Cannot fill frames of channel that has 0 frames: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t last_frame_idx = frame_indices.back();
|
||||||
|
frame_indices.resize(target_size, last_frame_idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(ifs, line))
|
||||||
|
{
|
||||||
|
if (line.empty() || line[0] == '#') // Skip empty lines and comments
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::istringstream iss(line);
|
||||||
|
|
||||||
|
std::string command;
|
||||||
|
iss >> command;
|
||||||
|
|
||||||
|
if (command == "f")
|
||||||
|
{
|
||||||
|
if (anim->num_frames_ == 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Frame data specified before number of frames in animation file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anim->channels_.empty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Frame data specified before any channels in animation file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
int frame_index;
|
||||||
|
iss >> frame_index;
|
||||||
|
|
||||||
|
if (frame_index < 0 || frame_index >= (int)anim->num_frames_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Frame index out of bounds in animation file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_index < last_frame)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Frame indices must be in ascending order in animation file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_frame = frame_index;
|
||||||
|
|
||||||
|
Transform t;
|
||||||
|
iss >> t.position.x >> t.position.y >> t.position.z;
|
||||||
|
glm::vec3 angles_deg;
|
||||||
|
iss >> angles_deg.x >> angles_deg.y >> angles_deg.z;
|
||||||
|
t.SetAngles(angles_deg);
|
||||||
|
iss >> t.scale;
|
||||||
|
|
||||||
|
size_t idx = anim->frames_.size();
|
||||||
|
anim->frames_.push_back(t);
|
||||||
|
|
||||||
|
FillFrameRefs(frame_index); // Fill to current frame
|
||||||
|
frame_indices.push_back(idx);
|
||||||
|
}
|
||||||
|
else if (command == "ch")
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
iss >> name;
|
||||||
|
|
||||||
|
int bone_index = skeleton->GetBoneIndex(name);
|
||||||
|
|
||||||
|
if (bone_index < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Bone referenced in animation not found in provided skeleton: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
FillFrameRefs(anim->num_frames_); // Fill to end for last channel
|
||||||
|
|
||||||
|
AnimationChannel& channel = anim->channels_.emplace_back();
|
||||||
|
channel.bone_index = bone_index;
|
||||||
|
channel.frames = nullptr; // Will be set up later
|
||||||
|
|
||||||
|
last_frame = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (command == "frames")
|
||||||
|
{
|
||||||
|
iss >> anim->num_frames_;
|
||||||
|
}
|
||||||
|
else if (command == "fps")
|
||||||
|
{
|
||||||
|
iss >> anim->tps_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anim->channels_.empty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("No channels found in animation file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
FillFrameRefs(anim->num_frames_); // Fill to end for last channel
|
||||||
|
|
||||||
|
// Set up frame pointers
|
||||||
|
anim->frame_refs_.resize(frame_indices.size());
|
||||||
|
for (size_t i = 0; i < frame_indices.size(); ++i)
|
||||||
|
{
|
||||||
|
anim->frame_refs_[i] = &anim->frames_[frame_indices[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up channel frame pointers
|
||||||
|
for (size_t i = 0; i < anim->channels_.size(); ++i)
|
||||||
|
{
|
||||||
|
AnimationChannel& channel = anim->channels_[i];
|
||||||
|
channel.frames = &anim->frame_refs_[i * anim->num_frames_];
|
||||||
|
}
|
||||||
|
|
||||||
|
return anim;
|
||||||
|
}
|
||||||
42
src/assets/animation.hpp
Normal file
42
src/assets/animation.hpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "utils/transform.hpp"
|
||||||
|
|
||||||
|
namespace assets
|
||||||
|
{
|
||||||
|
class Skeleton;
|
||||||
|
|
||||||
|
struct AnimationChannel
|
||||||
|
{
|
||||||
|
int bone_index;
|
||||||
|
const Transform* const* frames;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Animation
|
||||||
|
{
|
||||||
|
size_t num_frames_ = 0;
|
||||||
|
float tps_ = 24.0f;
|
||||||
|
|
||||||
|
std::vector<AnimationChannel> channels_;
|
||||||
|
std::vector<const Transform*> frame_refs_;
|
||||||
|
std::vector<Transform> frames_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Animation() = default;
|
||||||
|
|
||||||
|
size_t GetNumFrames() const { return num_frames_; }
|
||||||
|
float GetTPS() const { return tps_; }
|
||||||
|
|
||||||
|
size_t GetNumChannels() const { return channels_.size(); }
|
||||||
|
const AnimationChannel& GetChannel(int index) const { return channels_[index]; }
|
||||||
|
|
||||||
|
static std::shared_ptr<Animation> LoadFromFile(const std::string& filename, const Skeleton* skeleton);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -7,18 +7,72 @@
|
|||||||
|
|
||||||
std::map<std::string, std::weak_ptr<gfx::Texture>> assets::Mesh::s_texture_cache;
|
std::map<std::string, std::weak_ptr<gfx::Texture>> assets::Mesh::s_texture_cache;
|
||||||
|
|
||||||
assets::Mesh::Mesh(std::span<MeshVertex> verts, std::span<MeshTriangle> tris, std::span<MeshMaterial> materials) :
|
static int GetVertexAttrFlags(int mesh_flags)
|
||||||
va_(gfx::VA_POSITION | gfx::VA_NORMAL | gfx::VA_UV | gfx::VA_LIGHTMAP_UV, gfx::VF_CREATE_EBO)
|
{
|
||||||
|
int attrs = gfx::VA_POSITION | gfx::VA_NORMAL | gfx::VA_UV;
|
||||||
|
|
||||||
|
if (mesh_flags & assets::MF_HAS_LIGHTMAP_UV)
|
||||||
|
{
|
||||||
|
attrs |= gfx::VA_LIGHTMAP_UV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh_flags & assets::MF_IS_SKELETAL)
|
||||||
|
{
|
||||||
|
attrs |= gfx::VA_BONE_INDICES | gfx::VA_BONE_WEIGHTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static void BufferPut(std::vector<char>& buffer, const T& val)
|
||||||
|
{
|
||||||
|
const char* data = reinterpret_cast<const char*>(&val);
|
||||||
|
buffer.insert(buffer.end(), data, data + sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
assets::Mesh::Mesh(std::span<MeshVertex> verts, std::span<MeshTriangle> tris, std::span<MeshMaterial> materials, int flags) :
|
||||||
|
va_(GetVertexAttrFlags(flags), gfx::VF_CREATE_EBO)
|
||||||
{
|
{
|
||||||
verts_.assign(verts.begin(), verts.end());
|
verts_.assign(verts.begin(), verts.end());
|
||||||
tris_.assign(tris.begin(), tris.end());
|
tris_.assign(tris.begin(), tris.end());
|
||||||
materials_.assign(materials.begin(), materials.end());
|
materials_.assign(materials.begin(), materials.end());
|
||||||
|
|
||||||
va_.SetVBOData(verts.data(), verts.size_bytes());
|
std::vector<char> buffer;
|
||||||
|
|
||||||
|
for (const MeshVertex& vert : verts)
|
||||||
|
{
|
||||||
|
BufferPut(buffer, vert.pos);
|
||||||
|
BufferPut(buffer, vert.normal);
|
||||||
|
BufferPut(buffer, vert.uv);
|
||||||
|
|
||||||
|
if (flags & MF_HAS_LIGHTMAP_UV)
|
||||||
|
{
|
||||||
|
BufferPut(buffer, vert.lightmap_uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & MF_IS_SKELETAL)
|
||||||
|
{
|
||||||
|
// Bone indices as 4 ints
|
||||||
|
int32_t bone_indices[4] = { 0, 0, 0, 0 };
|
||||||
|
float bone_weights[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
bone_indices[i] = static_cast<int32_t>(vert.bones[i].bone_index);
|
||||||
|
bone_weights[i] = vert.bones[i].weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferPut(buffer, bone_indices);
|
||||||
|
BufferPut(buffer, bone_weights);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
va_.SetVBOData(buffer.data(), buffer.size());
|
||||||
va_.SetIndices(reinterpret_cast<const GLuint*>(tris.data()), tris.size() * 3);
|
va_.SetIndices(reinterpret_cast<const GLuint*>(tris.data()), tris.size() * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<assets::Mesh> assets::Mesh::LoadFromFile(const std::string& filename, bool load_collision)
|
std::shared_ptr<assets::Mesh> assets::Mesh::LoadFromFile(const std::string& filename, bool load_collision, const Skeleton* skeleton)
|
||||||
{
|
{
|
||||||
std::shared_ptr<collision::TriangleMesh> collision_mesh;
|
std::shared_ptr<collision::TriangleMesh> collision_mesh;
|
||||||
|
|
||||||
@ -32,9 +86,11 @@ std::shared_ptr<assets::Mesh> assets::Mesh::LoadFromFile(const std::string& file
|
|||||||
throw std::runtime_error("Failed to open mesh file: " + filename);
|
throw std::runtime_error("Failed to open mesh file: " + filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int flags = 0;
|
||||||
std::vector<MeshVertex> verts;
|
std::vector<MeshVertex> verts;
|
||||||
std::vector<MeshTriangle> tris;
|
std::vector<MeshTriangle> tris;
|
||||||
std::vector<MeshMaterial> materials;
|
std::vector<MeshMaterial> materials;
|
||||||
|
std::vector<int> bone_skel_ids;
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
|
|
||||||
@ -54,7 +110,42 @@ std::shared_ptr<assets::Mesh> assets::Mesh::LoadFromFile(const std::string& file
|
|||||||
iss >> vertex.pos.x >> vertex.pos.y >> vertex.pos.z;
|
iss >> vertex.pos.x >> vertex.pos.y >> vertex.pos.z;
|
||||||
iss >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z;
|
iss >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z;
|
||||||
iss >> vertex.uv.x >> vertex.uv.y;
|
iss >> vertex.uv.x >> vertex.uv.y;
|
||||||
iss >> vertex.lightmap_uv.x >> vertex.lightmap_uv.y;
|
|
||||||
|
if (flags & MF_HAS_LIGHTMAP_UV)
|
||||||
|
{
|
||||||
|
iss >> vertex.lightmap_uv.x >> vertex.lightmap_uv.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & MF_IS_SKELETAL)
|
||||||
|
{
|
||||||
|
int num_bones;
|
||||||
|
iss >> num_bones;
|
||||||
|
|
||||||
|
if (num_bones > 4)
|
||||||
|
num_bones = 4;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_bones; ++i)
|
||||||
|
{
|
||||||
|
int bone_idx;
|
||||||
|
float weight;
|
||||||
|
iss >> bone_idx >> weight;
|
||||||
|
|
||||||
|
if (bone_idx < 0 || bone_idx >= static_cast<int>(bone_skel_ids.size()))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Bone index out of bounds in mesh file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.bones[i].bone_index = bone_skel_ids[bone_idx];
|
||||||
|
vertex.bones[i].weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining influences with zeroes
|
||||||
|
for (int i = num_bones; i < 4; ++i)
|
||||||
|
{
|
||||||
|
vertex.bones[i].bone_index = -1;
|
||||||
|
vertex.bones[i].weight = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (command == "m") // Material switch
|
else if (command == "m") // Material switch
|
||||||
{
|
{
|
||||||
@ -91,6 +182,46 @@ std::shared_ptr<assets::Mesh> assets::Mesh::LoadFromFile(const std::string& file
|
|||||||
collision_mesh->AddTriangle(tri_verts[0], tri_verts[1], tri_verts[2]);
|
collision_mesh->AddTriangle(tri_verts[0], tri_verts[1], tri_verts[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (command == "d")
|
||||||
|
{
|
||||||
|
std::string bone_name;
|
||||||
|
iss >> bone_name;
|
||||||
|
int idx = skeleton->GetBoneIndex(bone_name);
|
||||||
|
|
||||||
|
if (idx < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Bone referenced in mesh not found in provided skeleton: " + bone_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bone_skel_ids.push_back(idx);
|
||||||
|
}
|
||||||
|
else if (command == "luv")
|
||||||
|
{
|
||||||
|
flags |= MF_HAS_LIGHTMAP_UV;
|
||||||
|
|
||||||
|
if (verts.size() > 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Lightmap UV flag must be specified before vertex definitions in mesh file: " + filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (command == "skeletal")
|
||||||
|
{
|
||||||
|
flags |= MF_IS_SKELETAL;
|
||||||
|
|
||||||
|
if (!skeleton)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Loading a skeletal mesh with no skeleton provided: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verts.size() > 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Skeletal flag must be specified before vertex definitions in mesh file: " + filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unknown command in mesh file: " + command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (materials.size() > 0)
|
if (materials.size() > 0)
|
||||||
@ -120,7 +251,7 @@ std::shared_ptr<assets::Mesh> assets::Mesh::LoadFromFile(const std::string& file
|
|||||||
}
|
}
|
||||||
// Create the mesh object
|
// Create the mesh object
|
||||||
|
|
||||||
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>(verts, tris, materials);
|
std::shared_ptr<Mesh> mesh = std::make_shared<Mesh>(verts, tris, materials, flags);
|
||||||
|
|
||||||
if (load_collision)
|
if (load_collision)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -9,15 +9,23 @@
|
|||||||
#include "gfx/vertex_array.hpp"
|
#include "gfx/vertex_array.hpp"
|
||||||
#include "gfx/texture.hpp"
|
#include "gfx/texture.hpp"
|
||||||
#include "collision/trianglemesh.hpp"
|
#include "collision/trianglemesh.hpp"
|
||||||
|
#include "skeleton.hpp"
|
||||||
|
|
||||||
namespace assets
|
namespace assets
|
||||||
{
|
{
|
||||||
|
struct MeshVertexBoneInfluence
|
||||||
|
{
|
||||||
|
int bone_index;
|
||||||
|
float weight;
|
||||||
|
};
|
||||||
|
|
||||||
struct MeshVertex
|
struct MeshVertex
|
||||||
{
|
{
|
||||||
glm::vec3 pos;
|
glm::vec3 pos;
|
||||||
glm::vec3 normal;
|
glm::vec3 normal;
|
||||||
glm::vec2 uv;
|
glm::vec2 uv;
|
||||||
glm::vec2 lightmap_uv;
|
glm::vec2 lightmap_uv;
|
||||||
|
MeshVertexBoneInfluence bones[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MeshTriangle
|
struct MeshTriangle
|
||||||
@ -33,6 +41,13 @@ namespace assets
|
|||||||
size_t num_tris = 0;
|
size_t num_tris = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum MeshFlags
|
||||||
|
{
|
||||||
|
MF_NONE = 0,
|
||||||
|
MF_HAS_LIGHTMAP_UV = 1 << 0,
|
||||||
|
MF_IS_SKELETAL = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
class Mesh
|
class Mesh
|
||||||
{
|
{
|
||||||
std::vector<MeshVertex> verts_;
|
std::vector<MeshVertex> verts_;
|
||||||
@ -43,7 +58,7 @@ namespace assets
|
|||||||
std::shared_ptr<collision::TriangleMesh> collision_mesh_;
|
std::shared_ptr<collision::TriangleMesh> collision_mesh_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Mesh(std::span<MeshVertex> verts, std::span<MeshTriangle> tris, std::span<MeshMaterial> materials);
|
Mesh(std::span<MeshVertex> verts, std::span<MeshTriangle> tris, std::span<MeshMaterial> materials, int flags);
|
||||||
|
|
||||||
void SetCollisionMesh(std::shared_ptr<collision::TriangleMesh> mesh) {
|
void SetCollisionMesh(std::shared_ptr<collision::TriangleMesh> mesh) {
|
||||||
collision_mesh_ = std::move(mesh);
|
collision_mesh_ = std::move(mesh);
|
||||||
@ -59,7 +74,7 @@ namespace assets
|
|||||||
std::span<const MeshTriangle> GetTriangles() const { return tris_; }
|
std::span<const MeshTriangle> GetTriangles() const { return tris_; }
|
||||||
std::span<const MeshMaterial> GetMaterials() const { return materials_; }
|
std::span<const MeshMaterial> GetMaterials() const { return materials_; }
|
||||||
|
|
||||||
static std::shared_ptr<Mesh> LoadFromFile(const std::string& filename, bool load_collision);
|
static std::shared_ptr<Mesh> LoadFromFile(const std::string& filename, bool load_collision, const Skeleton* skeleton = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::map<std::string, std::weak_ptr<gfx::Texture>> s_texture_cache;
|
static std::map<std::string, std::weak_ptr<gfx::Texture>> s_texture_cache;
|
||||||
|
|||||||
74
src/assets/skeleton.cpp
Normal file
74
src/assets/skeleton.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "skeleton.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
void assets::Skeleton::AddBone(const std::string& name, const std::string& parent_name, const Transform& transform)
|
||||||
|
{
|
||||||
|
int index = static_cast<int>(bones_.size());
|
||||||
|
|
||||||
|
Bone& bone = bones_.emplace_back();
|
||||||
|
bone.name = name;
|
||||||
|
bone.parent_idx = GetBoneIndex(parent_name);
|
||||||
|
bone.bind_transform = transform;
|
||||||
|
bone.inv_bind_matrix = glm::inverse(transform.ToMatrix());
|
||||||
|
|
||||||
|
bone_map_[bone.name] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int assets::Skeleton::GetBoneIndex(const std::string& name) const
|
||||||
|
{
|
||||||
|
auto it = bone_map_.find(name);
|
||||||
|
if (it != bone_map_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<assets::Skeleton> assets::Skeleton::LoadFromFile(const std::string& filename)
|
||||||
|
{
|
||||||
|
std::ifstream ifs(filename, std::ios::binary);
|
||||||
|
if (!ifs.is_open()) {
|
||||||
|
throw std::runtime_error("Failed to open skeleton file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Skeleton> skeleton = std::make_shared<Skeleton>();
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(ifs, line))
|
||||||
|
{
|
||||||
|
if (line.empty() || line[0] == '#') // Skip empty lines and comments
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::istringstream iss(line);
|
||||||
|
|
||||||
|
std::string command;
|
||||||
|
iss >> command;
|
||||||
|
|
||||||
|
if (command != "b")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Transform t;
|
||||||
|
glm::vec3 angles;
|
||||||
|
std::string bone_name, parent_name;
|
||||||
|
|
||||||
|
iss >> bone_name >> parent_name;
|
||||||
|
iss >> t.position.x >> t.position.y >> t.position.z;
|
||||||
|
iss >> angles.x >> angles.y >> angles.z;
|
||||||
|
iss >> t.scale;
|
||||||
|
|
||||||
|
if (iss.fail())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to parse bone definition in file: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
t.SetAngles(angles);
|
||||||
|
|
||||||
|
skeleton->AddBone(bone_name, parent_name, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return skeleton;
|
||||||
|
}
|
||||||
39
src/assets/skeleton.hpp
Normal file
39
src/assets/skeleton.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "utils/transform.hpp"
|
||||||
|
#include "animation.hpp"
|
||||||
|
|
||||||
|
namespace assets
|
||||||
|
{
|
||||||
|
struct Bone
|
||||||
|
{
|
||||||
|
int parent_idx;
|
||||||
|
std::string name;
|
||||||
|
Transform bind_transform;
|
||||||
|
glm::mat4 inv_bind_matrix;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Skeleton
|
||||||
|
{
|
||||||
|
std::vector<Bone> bones_;
|
||||||
|
std::map<std::string, int> bone_map_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Skeleton() = default;
|
||||||
|
|
||||||
|
void AddBone(const std::string& name, const std::string& parent_name, const Transform& transform);
|
||||||
|
|
||||||
|
int GetBoneIndex(const std::string& name) const;
|
||||||
|
|
||||||
|
const Bone& GetBone(int index) const { return bones_[index]; }
|
||||||
|
|
||||||
|
static std::shared_ptr<Skeleton> LoadFromFile(const std::string& filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
#include "vertex_array.hpp"
|
#include "vertex_array.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
// Struktura pro informace o jednotlivych vertex atributu
|
// Struktura pro informace o jednotlivych vertex atributu
|
||||||
struct VertexAttribInfo {
|
struct VertexAttribInfo {
|
||||||
GLuint index;
|
GLuint index;
|
||||||
@ -15,7 +17,9 @@ static const VertexAttribInfo s_ATTR_INFO[] = {
|
|||||||
{ 1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3 }, // VA_NORMAL
|
{ 1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3 }, // VA_NORMAL
|
||||||
{ 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, 4 }, // VA_COLOR
|
{ 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, 4 }, // VA_COLOR
|
||||||
{ 3, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2 }, // VA_UV
|
{ 3, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2 }, // VA_UV
|
||||||
{ 4, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2 } // VA_LIGHTMAP_UV
|
{ 4, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2 }, // VA_LIGHTMAP_UV
|
||||||
|
{ 5, 4, GL_INT, GL_FALSE, sizeof(int32_t) * 4 }, // VA_BONE_INDICES
|
||||||
|
{ 6, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4 }, // VA_BONE_WEIGHTS
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pocet typu vertex atributu
|
// Pocet typu vertex atributu
|
||||||
@ -45,7 +49,16 @@ gfx::VertexArray::VertexArray(int attrs, int flags) : m_usage(GL_STATIC_DRAW), m
|
|||||||
if (attrs & (1 << i)) {
|
if (attrs & (1 << i)) {
|
||||||
const auto& info = s_ATTR_INFO[i];
|
const auto& info = s_ATTR_INFO[i];
|
||||||
glEnableVertexAttribArray(info.index);
|
glEnableVertexAttribArray(info.index);
|
||||||
glVertexAttribPointer(info.index, info.size, info.type, info.normalized, stride, (const void*)offset);
|
|
||||||
|
if (info.type != GL_INT)
|
||||||
|
{
|
||||||
|
glVertexAttribPointer(info.index, info.size, info.type, info.normalized, stride, (const void*)offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glVertexAttribIPointer(info.index, info.size, info.type, stride, (const void*)offset);
|
||||||
|
}
|
||||||
|
|
||||||
offset += info.byte_size;
|
offset += info.byte_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,8 @@ namespace gfx
|
|||||||
VA_COLOR = 1 << 2,
|
VA_COLOR = 1 << 2,
|
||||||
VA_UV = 1 << 3,
|
VA_UV = 1 << 3,
|
||||||
VA_LIGHTMAP_UV = 1 << 4,
|
VA_LIGHTMAP_UV = 1 << 4,
|
||||||
|
VA_BONE_INDICES = 1 << 5,
|
||||||
|
VA_BONE_WEIGHTS = 1 << 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Informace pro vytvoreni VertexArraye
|
// Informace pro vytvoreni VertexArraye
|
||||||
|
|||||||
24
src/utils/transform.hpp
Normal file
24
src/utils/transform.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
struct Transform
|
||||||
|
{
|
||||||
|
glm::vec3 position;
|
||||||
|
glm::quat rotation;
|
||||||
|
float scale;
|
||||||
|
|
||||||
|
glm::mat4 ToMatrix() const {
|
||||||
|
// TODO: Optimize matrix construction
|
||||||
|
glm::mat4 translation_mat = glm::translate(glm::mat4(1.0f), position);
|
||||||
|
glm::mat4 rotation_mat = glm::mat4_cast(rotation);
|
||||||
|
glm::mat4 scaling_mat = glm::scale(glm::mat4(1.0f), glm::vec3(scale));
|
||||||
|
return translation_mat * rotation_mat * scaling_mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAngles(const glm::vec3& angles_deg) {
|
||||||
|
glm::vec3 angles_rad = glm::radians(angles_deg);
|
||||||
|
rotation = glm::quat(angles_rad);
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user