mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
f2e7606f1b
Some checks are pending
Build / 🖥️ Windows (push) Waiting to run
Build / 🐧 Linux (push) Waiting to run
Build / 🍎 MacOS (push) Waiting to run
Lint / 📝 Formatting (push) Waiting to run
Lint / 📝 Required Checks (push) Waiting to run
Lint / 📝 Optional Checks (push) Waiting to run
This adds a feature to `build_actor` to support importing skeletons and animations from .glb files. Multiple animations are handled and will use the name in the GLB. The default `viewer` process will end up playing back the first animation. There are a few limitations: - You can only have around 100 bones. It is technically possibly to have slightly more, but certain animations may fail to compress when there are more than ~100 bones. - Currently, all animations have 60 keyframes per second. This is a higher quality than what is normally used. If animation size becomes problematic, we could make this customizable somehow. - There is no support for the `align` bone. --------- Co-authored-by: water111 <awaterford1111445@gmail.com>
229 lines
6.2 KiB
C++
229 lines
6.2 KiB
C++
#pragma once
|
|
|
|
#include "common/util/gltf_util.h"
|
|
|
|
#include "goalc/build_actor/common/animation_processing.h"
|
|
#include "goalc/build_actor/common/art_types.h"
|
|
#include "goalc/build_level/collide/common/collide_common.h"
|
|
|
|
namespace jak1 {
|
|
|
|
// Note: there's some weirdness with the Joint types here - I believe that very early on in
|
|
// development, joint animations were stored separately per joint. However, all joint animations use
|
|
// the "compressed" format, which combines data for all joints into a single structure.
|
|
// By jak 2, they had cleaned this up, but for Jak 1, we have to deal with this weirdness.
|
|
|
|
struct Joint {
|
|
std::string name;
|
|
s32 number;
|
|
int parent;
|
|
math::Matrix4f bind_pose{};
|
|
|
|
Joint(const std::string& name, int number, int parent, math::Matrix4f bind_pose) {
|
|
this->name = name;
|
|
this->number = number;
|
|
this->parent = parent;
|
|
this->bind_pose = bind_pose;
|
|
}
|
|
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
// basic
|
|
// This is one of those weird leftover types.
|
|
// There's one of these per-joint, per-animation, and all it's useful for is storing the
|
|
// length of the animation. The game only looks at the data for joint 0 and assumes the rest are
|
|
// the same. (and by jak 2, this is gone entirely!)
|
|
struct JointAnimCompressed {
|
|
std::string name;
|
|
s16 number;
|
|
s16 length;
|
|
std::vector<u32> data;
|
|
explicit JointAnimCompressed(const Joint& joint, s16 num_frames)
|
|
: name(joint.name), number(joint.number), length(num_frames) {}
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
// Header for a compressed joint animation - this tells the decompressor how to read
|
|
// the data in the animation.
|
|
struct JointAnimCompressedHDR {
|
|
u32 control_bits[14];
|
|
u32 num_joints;
|
|
u32 matrix_bits;
|
|
|
|
JointAnimCompressedHDR() {
|
|
for (auto& bit : control_bits) {
|
|
bit = 0;
|
|
}
|
|
num_joints = 1;
|
|
matrix_bits = 0;
|
|
}
|
|
|
|
explicit JointAnimCompressedHDR(const anim::CompressedAnim& anim);
|
|
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
struct JointAnimCompressedFixed {
|
|
JointAnimCompressedHDR hdr;
|
|
u32 offset_64;
|
|
u32 offset_32;
|
|
u32 offset_16;
|
|
u32 reserved;
|
|
math::Vector4f data[133];
|
|
int num_data_qw_used = 0;
|
|
|
|
JointAnimCompressedFixed() {
|
|
offset_64 = 0;
|
|
offset_32 = 0x88;
|
|
offset_16 = 0x90;
|
|
reserved = 0;
|
|
data[0] = math::Vector4f(1.0f, 0.0f, 0.0f, 0.0f);
|
|
data[1] = math::Vector4f(0.0f, 1.0f, 0.0f, 0.0f);
|
|
data[2] = math::Vector4f(0.0f, 0.0f, 1.0f, 0.0f);
|
|
data[3] = math::Vector4f(0.0f, 0.0f, 0.0f, 1.0f);
|
|
data[4] = math::Vector4f(1.0f, 0.0f, 0.0f, 0.0f);
|
|
data[5] = math::Vector4f(0.0f, 1.0f, 0.0f, 0.0f);
|
|
data[6] = math::Vector4f(0.0f, 0.0f, 1.0f, 0.0f);
|
|
data[7] = math::Vector4f(0.0f, 0.0f, 0.0f, 1.0f);
|
|
num_data_qw_used = 8;
|
|
}
|
|
|
|
JointAnimCompressedFixed(const anim::CompressedAnim& anim);
|
|
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
struct JointAnimCompressedFrame {
|
|
u32 offset_64;
|
|
u32 offset_32;
|
|
u32 offset_16;
|
|
u32 reserved;
|
|
math::Vector4f data[133];
|
|
u32 num_data_qw_used = 0;
|
|
|
|
JointAnimCompressedFrame() {
|
|
offset_64 = 0;
|
|
offset_32 = 0;
|
|
offset_16 = 0;
|
|
reserved = 0;
|
|
}
|
|
|
|
JointAnimCompressedFrame(const anim::CompressedFrame& frame);
|
|
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
struct JointAnimCompressedControl {
|
|
u32 num_frames;
|
|
u32 fixed_qwc;
|
|
u32 frame_qwc;
|
|
JointAnimCompressedFixed fixed;
|
|
std::vector<JointAnimCompressedFrame> frame;
|
|
|
|
JointAnimCompressedControl() {
|
|
num_frames = 1;
|
|
fixed_qwc = 0xf;
|
|
frame_qwc = 1;
|
|
frame.emplace_back();
|
|
}
|
|
|
|
JointAnimCompressedControl(const anim::CompressedAnim& anim);
|
|
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
struct CollideMeshTri {
|
|
u8 vert_idx[3];
|
|
u8 unused;
|
|
PatSurface pat;
|
|
};
|
|
|
|
struct CollideMesh {
|
|
s32 joint_id;
|
|
u32 num_tris;
|
|
u32 num_verts;
|
|
std::vector<math::Vector4f> vertices;
|
|
std::vector<CollideMeshTri> tris;
|
|
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
size_t calc_data_size() const {
|
|
// (size-of collide-mesh) + type ptr = 36
|
|
return 36 + 16 * vertices.size() + sizeof(CollideMeshTri) * tris.size();
|
|
}
|
|
};
|
|
|
|
struct ArtJointGeo : ArtElement {
|
|
std::vector<Joint> data;
|
|
std::vector<CollideMesh> cmeshes;
|
|
ResLump lump;
|
|
size_t mesh_slot;
|
|
|
|
explicit ArtJointGeo(const std::string& name,
|
|
std::vector<CollideMesh> cmeshes,
|
|
std::vector<Joint>& joints) {
|
|
this->name = name + "-lod0";
|
|
length = joints.size();
|
|
for (auto& joint : joints) {
|
|
data.push_back(joint);
|
|
}
|
|
this->cmeshes = std::move(cmeshes);
|
|
}
|
|
void add_res();
|
|
size_t generate(DataObjectGenerator& gen);
|
|
size_t generate_mesh(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
struct ArtJointAnim : ArtElement {
|
|
MercEyeAnimBlock eye_anim_data;
|
|
float speed;
|
|
float artist_base;
|
|
float artist_step;
|
|
std::string master_art_group_name;
|
|
s32 master_art_group_index;
|
|
u8* blerc_data = nullptr;
|
|
JointAnimCompressedControl frames;
|
|
std::vector<JointAnimCompressed> data;
|
|
|
|
ArtJointAnim(const std::string& name, const std::vector<Joint>& joints) {
|
|
this->name = name + "-idle";
|
|
length = joints.size();
|
|
speed = 1.0f;
|
|
artist_base = 0.0f;
|
|
artist_step = 1.0f;
|
|
master_art_group_name = name;
|
|
master_art_group_index = 2;
|
|
for (auto& joint : joints) {
|
|
data.emplace_back(joint, 1);
|
|
}
|
|
}
|
|
|
|
ArtJointAnim(const anim::CompressedAnim& anim, const std::vector<Joint>& joints);
|
|
|
|
size_t generate(DataObjectGenerator& gen) const;
|
|
};
|
|
|
|
struct ArtGroup : Art {
|
|
FileInfo info;
|
|
std::vector<std::shared_ptr<ArtElement>> elts;
|
|
std::map<int, size_t> joint_map;
|
|
|
|
explicit ArtGroup(const std::string& file_name) {
|
|
info.file_type = "art-group";
|
|
info.file_name = "/src/next/data/art-group6/" + file_name + "-ag.go";
|
|
name = file_name;
|
|
info.major_version = versions::jak1::ART_FILE_VERSION;
|
|
info.minor_version = 0;
|
|
info.tool_debug = "Created by OpenGOAL buildactor";
|
|
info.mdb_file_name = "Unknown";
|
|
info.maya_file_name = "Unknown";
|
|
}
|
|
std::vector<u8> save_object_file() const;
|
|
int get_joint_idx(const std::string& name);
|
|
};
|
|
|
|
bool run_build_actor(const std::string& input_model,
|
|
const std::string& output_file,
|
|
bool gen_collide_mesh);
|
|
} // namespace jak1
|