Просмотр исходного кода

resource: add MeshSkeletonResource

Part-of: #276
Daniele Bartolini 11 месяцев назад
Родитель
Сommit
72e08caf08

+ 2 - 0
src/device/device.cpp

@@ -44,6 +44,7 @@
 #include "resource/lua_resource.h"
 #include "resource/material_resource.h"
 #include "resource/mesh_resource.h"
+#include "resource/mesh_skeleton_resource.h"
 #include "resource/package_resource.h"
 #include "resource/physics_resource.h"
 #include "resource/resource_id.inl"
@@ -584,6 +585,7 @@ void Device::run()
 	_resource_manager->register_type(RESOURCE_TYPE_LEVEL,            RESOURCE_VERSION_LEVEL,            NULL,      NULL,        NULL,        NULL);
 	_resource_manager->register_type(RESOURCE_TYPE_MATERIAL,         RESOURCE_VERSION_MATERIAL,         NULL,      NULL,        mtr::online, mtr::offline);
 	_resource_manager->register_type(RESOURCE_TYPE_MESH,             RESOURCE_VERSION_MESH,             mhr::load, mhr::unload, mhr::online, mhr::offline);
+	_resource_manager->register_type(RESOURCE_TYPE_MESH_SKELETON,    RESOURCE_VERSION_MESH_SKELETON,    NULL,      NULL,        NULL,        NULL);
 	_resource_manager->register_type(RESOURCE_TYPE_PACKAGE,          RESOURCE_VERSION_PACKAGE,          NULL,      NULL,        NULL,        NULL);
 	_resource_manager->register_type(RESOURCE_TYPE_PHYSICS_CONFIG,   RESOURCE_VERSION_PHYSICS_CONFIG,   NULL,      NULL,        NULL,        NULL);
 	_resource_manager->register_type(RESOURCE_TYPE_SCRIPT,           RESOURCE_VERSION_SCRIPT,           NULL,      NULL,        NULL,        NULL);

+ 2 - 0
src/resource/data_compiler.cpp

@@ -36,6 +36,7 @@
 #include "resource/lua_resource.h"
 #include "resource/material_resource.h"
 #include "resource/mesh_resource.h"
+#include "resource/mesh_skeleton_resource.h"
 #include "resource/package_resource.h"
 #include "resource/physics_resource.h"
 #include "resource/resource_id.inl"
@@ -1540,6 +1541,7 @@ int main_data_compiler(const DeviceOptions &opts)
 	dc->register_compiler("level",            RESOURCE_VERSION_LEVEL,            level_resource_internal::compile);
 	dc->register_compiler("material",         RESOURCE_VERSION_MATERIAL,         material_resource_internal::compile);
 	dc->register_compiler("mesh",             RESOURCE_VERSION_MESH,             mesh_resource_internal::compile);
+	dc->register_compiler("mesh_skeleton",    RESOURCE_VERSION_MESH_SKELETON,    mesh_skeleton_resource_internal::compile);
 	dc->register_compiler("package",          RESOURCE_VERSION_PACKAGE,          package_resource_internal::compile);
 	dc->register_compiler("physics_config",   RESOURCE_VERSION_PHYSICS_CONFIG,   physics_config_resource_internal::compile);
 	dc->register_compiler("lua",              RESOURCE_VERSION_SCRIPT,           lua_resource_internal::compile);

+ 57 - 0
src/resource/mesh_skeleton.cpp

@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "resource/mesh_skeleton.h"
+
+#if CROWN_CAN_COMPILE
+#   include "core/json/json_object.inl"
+#   include "core/json/sjson.h"
+#   include "core/memory/temp_allocator.inl"
+#   include "core/strings/dynamic_string.inl"
+#   include "resource/mesh_skeleton_fbx.h"
+#   include "resource/compile_options.inl"
+
+namespace crown
+{
+namespace mesh_skeleton
+{
+	static s32 parse_internal(AnimationSkeleton &s, Buffer &buf, CompileOptions &opts)
+	{
+		TempAllocator4096 ta;
+		JsonObject obj(ta);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
+
+		DynamicString source(ta);
+		RETURN_IF_ERROR(sjson::parse_string(source, obj["source"]), opts);
+
+		RETURN_IF_FILE_MISSING(source.c_str(), opts);
+		Buffer fbx_buf = opts.read(source.c_str());
+		return fbx::parse(s, fbx_buf, opts);
+	}
+
+	s32 parse(AnimationSkeleton &s, const char *path, CompileOptions &opts)
+	{
+		RETURN_IF_FILE_MISSING(path, opts);
+		Buffer buf = opts.read(path);
+		return parse_internal(s, buf, opts);
+	}
+
+	s32 parse(AnimationSkeleton &s, CompileOptions &opts)
+	{
+		Buffer buf = opts.read();
+		return parse_internal(s, buf, opts);
+	}
+
+} // namespace mesh_skeleton
+
+AnimationSkeleton::AnimationSkeleton(Allocator &a)
+	: local_transforms(a)
+	, parents(a)
+	, binding_matrices(a)
+{
+}
+
+} // namespace crown
+#endif // if CROWN_CAN_COMPILE

+ 41 - 0
src/resource/mesh_skeleton.h

@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include "config.h"
+
+#if CROWN_CAN_COMPILE
+#   include "core/containers/types.h"
+#   include "resource/mesh_skeleton_resource.h"
+
+namespace crown
+{
+struct AnimationSkeleton
+{
+	Array<BoneTransform> local_transforms;
+	Array<u32> parents;
+	Array<Matrix4x4> binding_matrices;
+
+	///
+	explicit AnimationSkeleton(Allocator &a);
+};
+
+namespace mesh_skeleton
+{
+	///
+	s32 parse(AnimationSkeleton &s, const char *path, CompileOptions &opts);
+
+	///
+	s32 parse(AnimationSkeleton &s, CompileOptions &opts);
+
+	///
+	s32 write(AnimationSkeleton &s, CompileOptions &opts);
+
+} // namespace mesh_skeleton
+
+} // namespace crown
+
+#endif // if CROWN_CAN_COMPILE

+ 111 - 0
src/resource/mesh_skeleton_fbx.cpp

@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "resource/mesh_skeleton_fbx.h"
+
+#if CROWN_CAN_COMPILE
+#   include "core/containers/array.inl"
+#   include "core/containers/hash_map.inl"
+#   include "core/math/matrix4x4.inl"
+#   include "core/math/vector2.inl"
+#   include "core/math/vector3.inl"
+#   include "core/memory/temp_allocator.inl"
+#   include "core/strings/dynamic_string.inl"
+#   include "core/strings/string_id.inl"
+#   include "resource/mesh_skeleton.h"
+#   include "resource/compile_options.inl"
+#   include "resource/fbx_document.h"
+#   include <ufbx.h>
+
+namespace crown
+{
+namespace fbx
+{
+	static ufbx_skin_cluster *find_cluster(ufbx_scene *scene, ufbx_node *node)
+	{
+		for (size_t i = 0; i < scene->skin_clusters.count; ++i) {
+			ufbx_skin_cluster *cluster = scene->skin_clusters.data[i];
+			if (cluster->bone_node == node)
+				return cluster;
+		}
+
+		return NULL;
+	}
+
+	static s32 parse_skeleton(AnimationSkeleton &as, FBXDocument &fbx, ufbx_node *bone, CompileOptions &opts)
+	{
+		Vector3 pos;
+		pos.x = bone->local_transform.translation.x;
+		pos.y = bone->local_transform.translation.y;
+		pos.z = bone->local_transform.translation.z;
+
+		Quaternion rot;
+		rot.x = bone->local_transform.rotation.x;
+		rot.y = bone->local_transform.rotation.y;
+		rot.z = bone->local_transform.rotation.z;
+		rot.w = bone->local_transform.rotation.w;
+
+		Vector3 scl;
+		scl.x = bone->local_transform.scale.x;
+		scl.y = bone->local_transform.scale.y;
+		scl.z = bone->local_transform.scale.z;
+
+		u16 parent_bone_id = bone_id(fbx, bone->parent->name.data);
+		array::push_back(as.local_transforms, { pos, rot, scl });
+		array::push_back(as.parents, (u32)parent_bone_id);
+
+		ufbx_skin_cluster *cluster = find_cluster(fbx.scene, bone);
+		if (cluster != NULL) {
+			Matrix4x4 m;
+			m.x.x = cluster->geometry_to_bone.cols[0].x;
+			m.x.y = cluster->geometry_to_bone.cols[0].y;
+			m.x.z = cluster->geometry_to_bone.cols[0].z;
+			m.x.w = 0.0f;
+			m.y.x = cluster->geometry_to_bone.cols[1].x;
+			m.y.y = cluster->geometry_to_bone.cols[1].y;
+			m.y.z = cluster->geometry_to_bone.cols[1].z;
+			m.y.w = 0.0f;
+			m.z.x = cluster->geometry_to_bone.cols[2].x;
+			m.z.y = cluster->geometry_to_bone.cols[2].y;
+			m.z.z = cluster->geometry_to_bone.cols[2].z;
+			m.z.w = 0.0f;
+			m.t.x = cluster->geometry_to_bone.cols[3].x;
+			m.t.y = cluster->geometry_to_bone.cols[3].y;
+			m.t.z = cluster->geometry_to_bone.cols[3].z;
+			m.t.w = 1.0f;
+			array::push_back(as.binding_matrices, m);
+		} else {
+			array::push_back(as.binding_matrices, MATRIX4X4_IDENTITY);
+		}
+
+		CE_ENSURE(bone_id(fbx, bone->name.data) == array::size(as.binding_matrices) - 1);
+
+		for (size_t i = 0; i < bone->children.count; ++i) {
+			ufbx_node *child = bone->children.data[i];
+			s32 err = parse_skeleton(as, fbx, child, opts);
+			ENSURE_OR_RETURN(err == 0, opts);
+		}
+
+		return 0;
+	}
+
+	s32 parse(AnimationSkeleton &as, Buffer &buf, CompileOptions &opts)
+	{
+		FBXDocument fbx(default_allocator());
+		s32 err = fbx::parse(fbx, buf, opts);
+		ENSURE_OR_RETURN(err == 0, opts);
+		RETURN_IF_FALSE(fbx.skeleton_root_node != NULL
+			, opts
+			, "No skeleton in FBX source"
+			);
+
+		return parse_skeleton(as, fbx, fbx.skeleton_root_node, opts);
+	}
+
+} // namespace fbx
+
+} // namespace crown
+
+#endif // if CROWN_CAN_COMPILE

+ 25 - 0
src/resource/mesh_skeleton_fbx.h

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include "config.h"
+
+#if CROWN_CAN_COMPILE
+#   include "resource/mesh_skeleton.h"
+#   include "resource/types.h"
+
+namespace crown
+{
+namespace fbx
+{
+	///
+	s32 parse(AnimationSkeleton &s, Buffer &buf, CompileOptions &opts);
+
+} // namespace fbx
+
+} // namespace crown
+
+#endif // if CROWN_CAN_COMPILE

+ 76 - 0
src/resource/mesh_skeleton_resource.cpp

@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "resource/mesh_skeleton_resource.h"
+#include "core/containers/array.inl"
+#include "core/json/json_object.inl"
+#include "core/json/sjson.h"
+#include "core/memory/temp_allocator.inl"
+#include "resource/mesh_skeleton_fbx.h"
+#include "resource/compile_options.inl"
+
+namespace crown
+{
+#if CROWN_CAN_COMPILE
+namespace mesh_skeleton_resource_internal
+{
+	s32 write(AnimationSkeleton &s, CompileOptions &opts)
+	{
+		MeshSkeletonResource asr;
+		asr.version = RESOURCE_VERSION_MESH_SKELETON;
+		asr.num_bones = array::size(s.local_transforms);
+		asr.local_transforms_offset = sizeof(asr);
+		asr.parents_offset = asr.local_transforms_offset + sizeof(BoneTransform) * asr.num_bones;
+		asr.binding_matrices_offset = asr.parents_offset + sizeof(u32) * asr.num_bones;
+
+		opts.write(asr.version);
+		opts.write(asr.num_bones);
+		opts.write(asr.local_transforms_offset);
+		opts.write(asr.parents_offset);
+		opts.write(asr.binding_matrices_offset);
+
+		for (u32 i = 0; i < asr.num_bones; ++i)
+			opts.write(s.local_transforms[i]);
+
+		for (u32 i = 0; i < asr.num_bones; ++i)
+			opts.write(s.parents[i]);
+
+		for (u32 i = 0; i < asr.num_bones; ++i)
+			opts.write(s.binding_matrices[i]);
+
+		return 0;
+	}
+
+	s32 compile(CompileOptions &opts)
+	{
+		AnimationSkeleton s(default_allocator());
+		s32 err = mesh_skeleton::parse(s, opts);
+		ENSURE_OR_RETURN(err == 0, opts);
+		return write(s, opts);
+	}
+
+} // namespace mesh_skeleton_resource_internal
+#endif // if CROWN_CAN_COMPILE
+
+namespace mesh_skeleton_resource
+{
+	const BoneTransform *local_transforms(const MeshSkeletonResource *asr)
+	{
+		return (BoneTransform *)((char *)asr + asr->local_transforms_offset);
+	}
+
+	const u32 *parents(const MeshSkeletonResource *asr)
+	{
+		return (u32 *)((char *)asr + asr->parents_offset);
+	}
+
+	const Matrix4x4 *binding_matrices(const MeshSkeletonResource *asr)
+	{
+		return (Matrix4x4 *)((char *)asr + asr->binding_matrices_offset);
+	}
+
+} // namespace mesh_skeleton_resource
+
+} // namespace crown

+ 58 - 0
src/resource/mesh_skeleton_resource.h

@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include "config.h"
+#include "core/math/types.h"
+#include "core/types.h"
+#include "resource/types.h"
+
+namespace crown
+{
+#define MESH_SKELETON_MAX_BONES 1024
+
+struct BoneTransform
+{
+	Vector3 position;
+	Quaternion rotation;
+	Vector3 scale;
+};
+
+struct MeshSkeletonResource
+{
+	u32 version;
+	u32 num_bones;               ///< Number of bones in the skeleton.
+	u32 local_transforms_offset; ///< Offset to first local transform.
+	u32 parents_offset;          ///< Offset to first parent of first transform.
+	u32 binding_matrices_offset; ///< Offset to first binding matrix.
+	// BoneTransform local_transforms[num_bones];
+	// u32 parents[num_bones];
+	// Matrix4x4 binding_matrices[num_bones];
+};
+
+#if CROWN_CAN_COMPILE
+namespace mesh_skeleton_resource_internal
+{
+	///
+	s32 compile(CompileOptions &opts);
+
+} // namespace mesh_skeleton_resource_internal
+#endif
+
+namespace mesh_skeleton_resource
+{
+	///
+	const BoneTransform *local_transforms(const MeshSkeletonResource *asr);
+
+	///
+	const u32 *parents(const MeshSkeletonResource *asr);
+
+	///
+	const Matrix4x4 *binding_matrices(const MeshSkeletonResource *asr);
+
+} // namespace mesh_skeleton_resource
+
+} // namespace crown

+ 3 - 0
src/resource/types.h

@@ -25,6 +25,7 @@ struct LevelResource;
 struct LuaResource;
 struct MaterialResource;
 struct MeshResource;
+struct MeshSkeletonResource;
 struct PackageResource;
 struct PhysicsConfigResource;
 struct ShaderResource;
@@ -60,6 +61,7 @@ struct Platform
 #define RESOURCE_TYPE_LEVEL            STRING_ID_64("level",            UINT64_C(0x2a690fd348fe9ac5))
 #define RESOURCE_TYPE_MATERIAL         STRING_ID_64("material",         UINT64_C(0xeac0b497876adedf))
 #define RESOURCE_TYPE_MESH             STRING_ID_64("mesh",             UINT64_C(0x48ff313713a997a1))
+#define RESOURCE_TYPE_MESH_SKELETON    STRING_ID_64("mesh_skeleton",    UINT64_C(0x2597bb272931eded))
 #define RESOURCE_TYPE_PACKAGE          STRING_ID_64("package",          UINT64_C(0xad9c6d9ed1e5e77a))
 #define RESOURCE_TYPE_PHYSICS_CONFIG   STRING_ID_64("physics_config",   UINT64_C(0x72e3cc03787a11a1))
 #define RESOURCE_TYPE_SCRIPT           STRING_ID_64("lua",              UINT64_C(0xa14e8dfa2cd117e2))
@@ -81,6 +83,7 @@ struct Platform
 #define RESOURCE_VERSION_LEVEL            (RESOURCE_VERSION_UNIT + 4) //!< Level embeds UnitResource
 #define RESOURCE_VERSION_MATERIAL         RESOURCE_VERSION(6)
 #define RESOURCE_VERSION_MESH             RESOURCE_VERSION(6)
+#define RESOURCE_VERSION_MESH_SKELETON    RESOURCE_VERSION(1)
 #define RESOURCE_VERSION_PACKAGE          RESOURCE_VERSION(7)
 #define RESOURCE_VERSION_PHYSICS_CONFIG   RESOURCE_VERSION(3)
 #define RESOURCE_VERSION_SCRIPT           RESOURCE_VERSION(4)