mirror of
https://github.com/korenkonder/ReDIVA.git
synced 2026-10-06 13:48:01 +03:00
7914 lines
320 KiB
C++
7914 lines
320 KiB
C++
/*
|
|
by korenkonder
|
|
GitHub/GitLab: korenkonder
|
|
*/
|
|
|
|
#include "obj.hpp"
|
|
#include <vector>
|
|
#include "io/memory_stream.hpp"
|
|
#include "f2/struct.hpp"
|
|
#include "half_t.hpp"
|
|
#include "hash.hpp"
|
|
#include "sort.hpp"
|
|
#include "str_utils.hpp"
|
|
|
|
enum obj_vertex_format_file {
|
|
OBJ_VERTEX_FILE_POSITION = 0x00000001,
|
|
OBJ_VERTEX_FILE_NORMAL = 0x00000002,
|
|
OBJ_VERTEX_FILE_TANGENT = 0x00000004,
|
|
OBJ_VERTEX_FILE_BINORMAL = 0x00000008,
|
|
OBJ_VERTEX_FILE_TEXCOORD0 = 0x00000010,
|
|
OBJ_VERTEX_FILE_TEXCOORD1 = 0x00000020,
|
|
OBJ_VERTEX_FILE_TEXCOORD2 = 0x00000040,
|
|
OBJ_VERTEX_FILE_TEXCOORD3 = 0x00000080,
|
|
OBJ_VERTEX_FILE_COLOR0 = 0x00000100,
|
|
OBJ_VERTEX_FILE_COLOR1 = 0x00000200,
|
|
OBJ_VERTEX_FILE_BONE_WEIGHT = 0x00000400,
|
|
OBJ_VERTEX_FILE_BONE_INDEX = 0x00000800,
|
|
OBJ_VERTEX_FILE_UNKNOWN = 0x00001000,
|
|
OBJ_VERTEX_FILE_MODERN_STORAGE = 0x80000000,
|
|
};
|
|
|
|
struct obj_skin_block_header {
|
|
int64_t block_signature_offset;
|
|
int64_t block_offset;
|
|
};
|
|
|
|
struct obj_skin_ex_data_header {
|
|
int32_t num_osage;
|
|
int64_t osage_node_array_offset;
|
|
int64_t osage_name_array_offset;
|
|
int64_t block_array_offset;
|
|
int32_t num_bone_name;
|
|
int64_t bone_name_array_offset;
|
|
int64_t osage_sibling_info_array_offset;
|
|
int32_t cloth_count;
|
|
};
|
|
|
|
struct obj_skin_header {
|
|
int64_t bone_id_array_offset;
|
|
int64_t bone_matrix_array_offset;
|
|
int64_t bone_name_array_offset;
|
|
int64_t ex_data_offset;
|
|
int64_t bone_parent_id_array_offset;
|
|
};
|
|
|
|
struct obj_sub_mesh_header {
|
|
int64_t bone_index_array_offset;
|
|
int64_t index_array_offset;
|
|
};
|
|
|
|
struct obj_mesh_header {
|
|
int64_t submesh_array;
|
|
obj_vertex_format_file format;
|
|
int32_t size_vertex;
|
|
int32_t num_vertex;
|
|
int64_t vertex[20];
|
|
uint32_t vertex_format_index;
|
|
};
|
|
|
|
struct obj_header {
|
|
int64_t mesh_array;
|
|
int64_t material_array;
|
|
};
|
|
|
|
struct obj_set_header {
|
|
int64_t last_obj_id;
|
|
int64_t obj_data;
|
|
int64_t obj_skin_data;
|
|
int64_t obj_name_data;
|
|
int64_t obj_id_data;
|
|
int64_t tex_id_data;
|
|
};
|
|
|
|
const uint8_t obj_material_texture_enrs_table_bin[] =
|
|
"\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
"\x00\x40\xBA\x44\xB0\x01\x10\x02\x18\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x18\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x50\x20\x01\x10\x01\x10\x01\x18\x01\x10\x01\x10\x01\x10"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x50\x20\x01\x10\x01"
|
|
"\x10\x01\x18\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x50\x20\x01\x10\x01\x10\x01\x18\x01\x10\x01\x10"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x50\x20\x01"
|
|
"\x10\x01\x10\x01\x18\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x50\x20\x01\x10\x01\x10\x01\x18\x01\x10"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x50"
|
|
"\x20\x01\x10\x01\x10\x01\x18\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x10\x01\x50\x20\x01\x10\x01\x10\x01\x18"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10"
|
|
"\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10"
|
|
"\x01\x50\x20\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01\x10\x01"
|
|
"\x50\x40\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
|
|
|
static enrs obj_material_texture_enrs_table;
|
|
static bool obj_material_texture_enrs_table_initialized;
|
|
|
|
static void obj_material_texture_enrs_table_init();
|
|
static void obj_material_texture_enrs_table_free(void);
|
|
|
|
static void obj_vertex_add_bone_weight(vec4& bone_weight, vec4i16& bone_index, int16_t index, float_t weight);
|
|
static void obj_vertex_generate_tangents(obj_mesh* mesh);
|
|
static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i16& bone_index);
|
|
|
|
static void obj_move_data(obj* obj_dst, const obj* obj_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static void obj_move_data_mesh(obj_mesh* mesh_dst, const obj_mesh* mesh_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static void obj_move_data_sub_mesh(obj_sub_mesh* sub_mesh_dst, const obj_sub_mesh* sub_mesh_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin* obj_move_data_skin(const obj_skin* sk_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static void obj_move_data_skin_bone(obj_skin_bone* bone_dst, const obj_skin_bone* bone_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin_ex_data* obj_move_data_skin_ex_data(const obj_skin_ex_data* ex_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin_block_cloth* obj_move_data_skin_block_cloth(const obj_skin_block_cloth* cls_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static void obj_move_data_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root_dst,
|
|
const obj_skin_block_cloth_root* cloth_root_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static void obj_move_data_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight_dst,
|
|
const obj_skin_block_cloth_root_bone_weight* bone_weight_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin_block_constraint* obj_move_data_skin_block_constraint(const obj_skin_block_constraint* cns_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static void obj_move_data_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector_dst,
|
|
const obj_skin_block_constraint_up_vector* up_vector_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin_block_expression* obj_move_data_skin_block_expression(const obj_skin_block_expression* exp_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin_block_motion* obj_move_data_skin_block_motion(const obj_skin_block_motion* mot_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static void obj_move_data_skin_block_node(obj_skin_block_node* node_dst, const obj_skin_block_node* node_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin_block_osage* obj_move_data_skin_block_osage(const obj_skin_block_osage* osg_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
static obj_skin_skin_param* obj_move_data_skin_param(const obj_skin_skin_param* skp_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc);
|
|
|
|
static void obj_set_classic_read_inner(obj_set* set, prj::shared_ptr<prj::stack_allocator>& alloc, stream& s);
|
|
static void obj_set_classic_write_inner(obj_set* set, stream& s);
|
|
static void obj_classic_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr<prj::stack_allocator> alloc, stream& s);
|
|
static void obj_classic_write_index(obj_sub_mesh* sub_mesh, stream& s);
|
|
static void obj_classic_read_model(obj* obj, prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset);
|
|
static void obj_classic_write_model(obj* obj, stream& s, int64_t base_offset);
|
|
static void obj_classic_read_model_mesh(obj_mesh* mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset);
|
|
static void obj_classic_read_model_sub_mesh(obj_sub_mesh* sub_mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset);
|
|
static obj_skin* obj_classic_read_skin(prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset);
|
|
static void obj_classic_write_skin(obj_skin* sk, stream& s, int64_t base_offset);
|
|
static obj_skin_ex_data* obj_classic_read_skin_ex_data(prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset);
|
|
static obj_skin_block_cloth* obj_classic_read_skin_block_cloth(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_cloth(obj_skin_block_cloth* cls,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
int64_t* mat_array_offset, int64_t* root_array_offset, int64_t* node_array_offset,
|
|
int64_t* mesh_index_array_offset, int64_t* backface_mesh_index_array_offset);
|
|
static void obj_classic_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets);
|
|
static void obj_classic_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets);
|
|
static obj_skin_block_constraint* obj_classic_read_skin_block_constraint(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_constraint(obj_skin_block_constraint* cns,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array, int64_t* offsets);
|
|
static void obj_classic_read_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets);
|
|
static void obj_classic_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets);
|
|
static obj_skin_block_expression* obj_classic_read_skin_block_expression(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_expression(obj_skin_block_expression* exp,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array);
|
|
static obj_skin_block_motion* obj_classic_read_skin_block_motion(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_motion(obj_skin_block_motion* mot,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array, int64_t* bone_name_array_offset, int64_t* bone_matrix_array_offset);
|
|
static void obj_classic_read_skin_block_node(obj_skin_block_node* node,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_node(obj_skin_block_node* node,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets);
|
|
static obj_skin_block_osage* obj_classic_read_skin_block_osage(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_block_osage(obj_skin_block_osage* osg,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, int64_t* node_array_offset);
|
|
static obj_skin_skin_param* obj_classic_read_skin_param(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str);
|
|
static void obj_classic_write_skin_param(obj_skin_skin_param* skp,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets);
|
|
static void obj_classic_read_vertex(obj_mesh* mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t* vertex, int64_t base_offset, int32_t num_vertex,
|
|
obj_vertex_format_file vertex_format_file);
|
|
static void obj_classic_write_vertex(obj_mesh* mesh,
|
|
stream& s, int64_t* vertex, int64_t base_offset, int32_t* num_vertex,
|
|
obj_vertex_format_file* vertex_format_file, int32_t* size_vertex);
|
|
|
|
static void obj_set_modern_read_inner(obj_set* set, prj::shared_ptr<prj::stack_allocator>& alloc, stream& s);
|
|
static void obj_set_modern_write_inner(obj_set* set, stream& s);
|
|
static void obj_modern_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr<prj::stack_allocator> alloc, stream& s);
|
|
static void obj_modern_write_index(obj_sub_mesh* sub_mesh, stream& s, bool is_x, f2_struct* ovtx);
|
|
static void obj_modern_read_model(obj* obj, prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset,
|
|
uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx);
|
|
static void obj_modern_write_model(obj* obj, stream& s,
|
|
int64_t base_offset, bool is_x, f2_struct* omdl);
|
|
static void obj_modern_read_model_mesh(obj_mesh* mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset,
|
|
uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx);
|
|
static void obj_modern_read_model_sub_mesh(obj_sub_mesh* sub_mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset,
|
|
uint32_t header_length, bool is_x, stream* s_oidx);
|
|
static obj_skin* obj_modern_read_skin(prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset, uint32_t header_length, bool is_x);
|
|
static void obj_modern_write_skin(obj_skin* sk, stream& s,
|
|
int64_t base_offset, bool is_x, f2_struct* oskn);
|
|
static obj_skin_ex_data* obj_modern_read_skin_ex_data(prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset, uint32_t header_length, bool is_x);
|
|
static obj_skin_block_cloth* obj_modern_read_skin_block_cloth(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_cloth(obj_skin_block_cloth* cls,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x,
|
|
int64_t* mat_array_offset, int64_t* root_array_offset, int64_t* node_array_offset,
|
|
int64_t* mesh_index_array_offset, int64_t* backface_mesh_index_array_offset);
|
|
static void obj_modern_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x);
|
|
static void obj_modern_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x);
|
|
static obj_skin_block_constraint* obj_modern_read_skin_block_constraint(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_constraint(obj_skin_block_constraint* cns,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, const char** bone_name_array,
|
|
bool is_x, int64_t* offsets);
|
|
static void obj_modern_read_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x);
|
|
static void obj_modern_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x);
|
|
static obj_skin_block_expression* obj_modern_read_skin_block_expression(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_expression(obj_skin_block_expression* exp,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array, bool is_x);
|
|
static obj_skin_block_motion* obj_modern_read_skin_block_motion(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_motion(obj_skin_block_motion* mot,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x,
|
|
const char** bone_name_array, int64_t* bone_name_array_offset, int64_t* bone_matrix_array_offset);
|
|
static void obj_modern_read_skin_block_node(obj_skin_block_node* node,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_node(obj_skin_block_node* node,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x);
|
|
static obj_skin_block_osage* obj_modern_read_skin_block_osage(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x);
|
|
static void obj_modern_write_skin_block_osage(obj_skin_block_osage* osg,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x);
|
|
static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr<prj::stack_allocator> alloc, stream& s,
|
|
int64_t* vertex, const uint32_t vertex_format_index, int32_t num_vertex, uint32_t size_vertex);
|
|
static void obj_modern_write_vertex(obj_mesh* mesh, stream& s, int64_t* vertex,
|
|
uint32_t* vertex_format_index, int32_t* num_vertex, int32_t* size_vertex, f2_struct* ovtx);
|
|
|
|
static const char* obj_move_data_string(const char* str,
|
|
prj::shared_ptr<prj::stack_allocator>& alloc);
|
|
|
|
static const char* obj_read_utf8_string_null_terminated_offset(
|
|
prj::shared_ptr<prj::stack_allocator>& alloc, stream& s, int64_t offset);
|
|
|
|
static uint32_t obj_skin_strings_get_string_index(std::vector<string_hash>& vec, const char* str);
|
|
static int64_t obj_skin_strings_get_string_offset(std::vector<string_hash>& vec,
|
|
std::vector<int64_t>& vec_off, const char* str);
|
|
static int64_t obj_skin_strings_get_string_offset_by_index(std::vector<string_hash>& vec,
|
|
std::vector<int64_t>& vec_off, const char** strings, uint32_t index);
|
|
static void obj_skin_strings_push_back_check(std::vector<string_hash>& vec, const char* str);
|
|
static void obj_skin_strings_push_back_check_by_index(std::vector<string_hash>& vec,
|
|
const char** strings, uint32_t index);
|
|
|
|
obj_axis_aligned_bounding_box::obj_axis_aligned_bounding_box() {
|
|
|
|
}
|
|
|
|
obj_bounding_box::obj_bounding_box() {
|
|
|
|
}
|
|
|
|
obj_bounding_sphere::obj_bounding_sphere() : radius() {
|
|
|
|
}
|
|
|
|
obj_bounding_sphere::obj_bounding_sphere(vec3 center, float_t radius) {
|
|
this->center = center;
|
|
this->radius = radius;
|
|
}
|
|
|
|
obj_material_shader_lighting_type obj_material_shader_attrib::get_lighting_type() const {
|
|
if (!m.is_lgt_diffuse && !m.is_lgt_specular)
|
|
return OBJ_MATERIAL_SHADER_LIGHTING_CONSTANT;
|
|
else if (!m.is_lgt_specular)
|
|
return OBJ_MATERIAL_SHADER_LIGHTING_LAMBERT;
|
|
else
|
|
return OBJ_MATERIAL_SHADER_LIGHTING_PHONG;
|
|
}
|
|
|
|
int32_t obj_texture_attrib::get_blend() const {
|
|
switch (m.blend) {
|
|
case 4:
|
|
return 2;
|
|
case 6:
|
|
return 1;
|
|
case 16:
|
|
return 3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
obj_material_texture_data::obj_material_texture_data() : attrib(),
|
|
tex_index(), shader_info(), ex_shader(), weight(), reserved() {
|
|
|
|
}
|
|
|
|
obj_material_color::obj_material_color() : shininess(), intensity() {
|
|
|
|
}
|
|
|
|
obj_material::obj_material() : shader_compo(), shader(), shader_info(),
|
|
attrib(), center(), radius(), name(), bump_depth(), reserved() {
|
|
}
|
|
|
|
obj_material_data::obj_material_data() : num_of_textures() {
|
|
|
|
}
|
|
|
|
obj_sub_mesh::obj_sub_mesh() : flags(), bounding_sphere(), material_index(), uv_index(), num_bone_index(),
|
|
bone_index_array(), bones_per_vertex(), primitive_type(), index_format(), num_index(), index_array(),
|
|
attrib(), axis_aligned_bounding_box(), first_index(), last_index(), index_offset() {
|
|
|
|
}
|
|
|
|
obj_vertex_data::obj_vertex_data() {
|
|
|
|
}
|
|
|
|
obj_mesh::obj_mesh() : flags(), num_submesh(), submesh_array(), vertex_format(),
|
|
size_vertex(), num_vertex(), vertex_array(), attrib(), reserved(), name() {
|
|
|
|
}
|
|
|
|
obj_skin_block_node::obj_skin_block_node() : parent_name() {
|
|
|
|
}
|
|
|
|
obj_skin_skin_param_coli::obj_skin_skin_param_coli() : type(), bone0_index(), bone1_index(), radius() {
|
|
|
|
}
|
|
|
|
obj_skin_skin_param::obj_skin_skin_param() : unk0(), force(), force_gain(), air_res(), rot_y(), rot_z(),
|
|
hinge_y(), hinge_z(), name(), coli(), coli_count(), coli_r(), friction(), wind_afc(), unk44() {
|
|
|
|
}
|
|
|
|
obj_skin_block_cloth_root_bone_weight::obj_skin_block_cloth_root_bone_weight() :
|
|
bone_name(), weight(), matrix_index(), reserved() {
|
|
|
|
}
|
|
|
|
obj_skin_block_cloth_root::obj_skin_block_cloth_root() : field_18(), field_1C(), field_20(), field_24() {
|
|
|
|
}
|
|
|
|
obj_skin_block_cloth_node::obj_skin_block_cloth_node() : flags(),
|
|
dist_top(), dist_bottom(), dist_right(), dist_left() {
|
|
|
|
}
|
|
|
|
obj_skin_block_cloth::obj_skin_block_cloth() : mesh_name(), backface_mesh_name(), field_8(),
|
|
num_root(), num_node(), loop(), mat_array(), num_mat(), root_array(), node_array(), mesh_index_array(),
|
|
num_mesh_index(), backface_mesh_index_array(), num_backface_mesh_index(), skin_param(), reserved() {
|
|
|
|
}
|
|
|
|
obj_skin_block_constraint_attach_point::obj_skin_block_constraint_attach_point()
|
|
: affected_by_orientation(), affected_by_scaling() {
|
|
|
|
}
|
|
|
|
obj_skin_block_constraint_up_vector::obj_skin_block_constraint_up_vector() : active(), roll(), name() {
|
|
|
|
}
|
|
|
|
obj_skin_block_constraint_direction::obj_skin_block_constraint_direction() {
|
|
|
|
}
|
|
|
|
obj_skin_block_constraint_distance::obj_skin_block_constraint_distance() : distance() {
|
|
|
|
}
|
|
|
|
obj_skin_block_constraint_orientation::obj_skin_block_constraint_orientation() {
|
|
|
|
}
|
|
|
|
obj_skin_block_constraint_position::obj_skin_block_constraint_position() {
|
|
|
|
}
|
|
|
|
obj_skin_block_constraint::obj_skin_block_constraint()
|
|
: name_index(), source_node_name(), coupling(), type(), data() {
|
|
|
|
}
|
|
|
|
obj_skin_block_expression::obj_skin_block_expression()
|
|
: name_index(), num_expression(), expression_array() {
|
|
|
|
}
|
|
|
|
obj_skin_motion_node::obj_skin_motion_node() : name_index() {
|
|
|
|
}
|
|
|
|
obj_skin_block_motion::obj_skin_block_motion() : name(), node_array(), num_node() {
|
|
|
|
}
|
|
|
|
obj_skin_osage_node::obj_skin_osage_node() : name_index(), length() {
|
|
|
|
}
|
|
|
|
obj_skin_block_osage::obj_skin_block_osage() : start_index(), count(), node_array(),
|
|
num_node(), skin_param(), name_index(), end_name_index(), motion_node_name() {
|
|
|
|
}
|
|
|
|
obj_skin_block::obj_skin_block() : type(), node() {
|
|
|
|
}
|
|
|
|
obj_skin_osage_sibling_info::obj_skin_osage_sibling_info()
|
|
: name_index(), sibling_name_index(), max_distance() {
|
|
|
|
}
|
|
|
|
obj_skin_ex_data::obj_skin_ex_data() : osage_node_array(), num_osage_node(), block_array(), num_block(),
|
|
bone_name_array(), num_bone_name(), osage_sibling_info_array(), num_osage_sibling_info(), reserved() {
|
|
|
|
}
|
|
|
|
obj_skin_bone::obj_skin_bone() : id(), parent(), name() {
|
|
|
|
}
|
|
|
|
obj_skin::obj_skin() : bone_array(), num_bone(), ex_data() {
|
|
|
|
}
|
|
|
|
obj::obj() : num_mesh(), mesh_array(), num_material(),
|
|
material_array(), flags(), reserved(), skin(), name(), id() {
|
|
hash = hash_murmurhash_empty;
|
|
}
|
|
|
|
obj_mesh* obj::get_obj_mesh(const char* name) {
|
|
uint32_t name_hash = hash_utf8_murmurhash(name);
|
|
for (int32_t k = 0; k < num_mesh; k++)
|
|
if (hash_utf8_murmurhash(mesh_array[k].name) == name_hash)
|
|
return &mesh_array[k];
|
|
return 0;
|
|
}
|
|
|
|
int32_t obj::get_obj_mesh_index(const char* name) {
|
|
uint32_t name_hash = hash_utf8_murmurhash(name);
|
|
for (int32_t k = 0; k < num_mesh; k++)
|
|
if (hash_utf8_murmurhash(mesh_array[k].name) == name_hash)
|
|
return k;
|
|
return -1;
|
|
}
|
|
|
|
obj_set::obj_set() : ready(), modern(), big_endian(), is_x(), obj_data(),
|
|
obj_num(), tex_id_data(), tex_id_num(), reserved() {
|
|
|
|
}
|
|
|
|
void obj_set::move_data(obj_set* set_src, prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
if (!set_src->ready) {
|
|
ready = false;
|
|
modern = false;
|
|
big_endian = false;
|
|
is_x = false;
|
|
return;
|
|
}
|
|
|
|
ready = true;
|
|
modern = set_src->modern;
|
|
big_endian = set_src->big_endian;
|
|
is_x = set_src->is_x;
|
|
|
|
int32_t obj_num = set_src->obj_num;
|
|
obj** obj_data_src = set_src->obj_data;
|
|
obj** obj_data_dst = alloc->allocate<obj*>(obj_num);
|
|
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj_data_dst[i] = alloc->allocate<obj>();
|
|
obj_move_data(obj_data_dst[i], obj_data_src[i], alloc);
|
|
}
|
|
|
|
this->obj_data = obj_data_dst;
|
|
this->obj_num = obj_num;
|
|
|
|
int32_t tex_id_num = set_src->tex_id_num;
|
|
this->tex_id_data = alloc->allocate<uint32_t>(set_src->tex_id_data, tex_id_num);
|
|
this->tex_id_num = tex_id_num;
|
|
|
|
memcpy(this->reserved, set_src->reserved, sizeof(uint32_t) * 2);
|
|
}
|
|
|
|
void obj_set::pack_file(void** data, size_t* size) {
|
|
if (!data || !ready)
|
|
return;
|
|
|
|
memory_stream s;
|
|
s.open();
|
|
if (!modern)
|
|
obj_set_classic_write_inner(this, s);
|
|
else
|
|
obj_set_modern_write_inner(this, s);
|
|
s.align_write(0x10);
|
|
s.copy(data, size);
|
|
}
|
|
|
|
void obj_set::unpack_file(prj::shared_ptr<prj::stack_allocator> alloc, const void* data, size_t size, bool modern) {
|
|
if (!data || !size)
|
|
return;
|
|
|
|
memory_stream s;
|
|
s.open(data, size);
|
|
if (!modern)
|
|
obj_set_classic_read_inner(this, alloc, s);
|
|
else
|
|
obj_set_modern_read_inner(this, alloc, s);
|
|
}
|
|
|
|
static void obj_material_texture_enrs_table_init() {
|
|
if (!obj_material_texture_enrs_table_initialized) {
|
|
memory_stream s;
|
|
s.open((void*)&obj_material_texture_enrs_table_bin,
|
|
sizeof(obj_material_texture_enrs_table_bin));
|
|
obj_material_texture_enrs_table.read(s);
|
|
atexit(obj_material_texture_enrs_table_free);
|
|
obj_material_texture_enrs_table_initialized = true;
|
|
}
|
|
}
|
|
|
|
static void obj_material_texture_enrs_table_free(void) {
|
|
obj_material_texture_enrs_table.vec.clear();
|
|
obj_material_texture_enrs_table.vec.shrink_to_fit();
|
|
obj_material_texture_enrs_table_initialized = false;
|
|
}
|
|
|
|
static void obj_vertex_add_bone_weight(vec4& bone_weight, vec4i16& bone_index, int16_t index, float_t weight) {
|
|
if (bone_index.x < 0) {
|
|
bone_index.x = index;
|
|
bone_weight.x = weight;
|
|
}
|
|
else if (bone_index.y < 0) {
|
|
bone_index.y = index;
|
|
bone_weight.y = weight;
|
|
}
|
|
else if (bone_index.z < 0) {
|
|
bone_index.z = index;
|
|
bone_weight.z = weight;
|
|
}
|
|
else if (bone_index.w < 0) {
|
|
bone_index.w = index;
|
|
bone_weight.w = weight;
|
|
}
|
|
}
|
|
|
|
static void calculate_tangent(obj_vertex_data* vtx,
|
|
vec3* tangents, vec3* bitangents, uint32_t a, uint32_t b, uint32_t c) {
|
|
obj_vertex_data* vtx_a = &vtx[a];
|
|
obj_vertex_data* vtx_b = &vtx[b];
|
|
obj_vertex_data* vtx_c = &vtx[c];
|
|
|
|
vec3 pos_a = vtx_c->position - vtx_a->position;
|
|
vec3 pos_b = vtx_b->position - vtx_a->position;
|
|
|
|
vec2 uv_a = vtx_c->texcoord0 - vtx_a->texcoord0;
|
|
vec2 uv_b = vtx_b->texcoord0 - vtx_a->texcoord0;
|
|
|
|
vec3 tangent = pos_a * uv_b.y - pos_b * uv_a.y;
|
|
vec3 bitangent = pos_b * uv_a.x - pos_a * uv_b.x;
|
|
|
|
if (uv_a.x * uv_b.y - uv_a.y * uv_b.x <= 0.0f) {
|
|
tangent = -tangent;
|
|
bitangent = -bitangent;
|
|
}
|
|
|
|
tangents[a] += tangent;
|
|
tangents[b] += tangent;
|
|
tangents[c] += tangent;
|
|
|
|
bitangents[a] += bitangent;
|
|
bitangents[b] += bitangent;
|
|
bitangents[c] += bitangent;
|
|
}
|
|
|
|
static void obj_vertex_generate_tangents(obj_mesh* mesh) {
|
|
if (!(mesh->vertex_format & OBJ_VERTEX_NORMAL)
|
|
|| mesh->vertex_format & OBJ_VERTEX_TANGENT
|
|
|| !(mesh->vertex_format & OBJ_VERTEX_TEXCOORD0))
|
|
return;
|
|
|
|
int32_t num_vertex = mesh->num_vertex;
|
|
|
|
vec3* tangents = force_malloc<vec3>(num_vertex);
|
|
vec3* bitangents = force_malloc<vec3>(num_vertex);
|
|
|
|
obj_vertex_data* vtx = mesh->vertex_array;
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
for (int32_t i = 0; i < num_submesh; i++) {
|
|
obj_sub_mesh* sub_mesh = &mesh->submesh_array[i];
|
|
if (!sub_mesh->index_array || !sub_mesh->num_index
|
|
|| (sub_mesh->primitive_type != OBJ_PRIMITIVE_TRIANGLES
|
|
&& sub_mesh->primitive_type != OBJ_PRIMITIVE_TRIANGLE_STRIP))
|
|
continue;
|
|
|
|
uint32_t* start = sub_mesh->index_array;
|
|
uint32_t* end = sub_mesh->index_array + sub_mesh->num_index;
|
|
|
|
bool triangle_strip = sub_mesh->primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP;
|
|
if (!triangle_strip) {
|
|
while (start < end) {
|
|
uint32_t a = *start++;
|
|
uint32_t b = *start++;
|
|
uint32_t c = *start++;
|
|
calculate_tangent(vtx, tangents, bitangents, a, b, c);
|
|
}
|
|
}
|
|
else {
|
|
bool direction = true;
|
|
|
|
uint32_t a = *start++;
|
|
uint32_t b = *start++;
|
|
|
|
while (start < end) {
|
|
uint32_t c = *start++;
|
|
|
|
if (c == -1) {
|
|
a = *start++;
|
|
b = *start++;
|
|
direction = true;
|
|
}
|
|
else {
|
|
direction ^= true;
|
|
if (a != b && b != c && c != a) {
|
|
if (!direction)
|
|
calculate_tangent(vtx, tangents, bitangents, a, b, c);
|
|
else
|
|
calculate_tangent(vtx, tangents, bitangents, a, c, b);
|
|
}
|
|
|
|
a = b;
|
|
b = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int32_t i = 0; i < num_vertex; i++) {
|
|
vec3 normal = vtx[i].normal;
|
|
|
|
vec3 tangent = vec3::normalize(tangents[i]);
|
|
vec3 bitangent = vec3::normalize(bitangents[i]);
|
|
|
|
tangent = vec3::normalize(tangent - normal * vec3::dot(tangent, normal));
|
|
bitangent = vec3::normalize(bitangent - normal * vec3::dot(bitangent, normal));
|
|
|
|
vec3 binormal = vec3::normalize(vec3::cross(normal, tangent));
|
|
|
|
float_t dir_check = vec3::dot(binormal, bitangent);
|
|
|
|
*(vec3*)&vtx[i].tangent = tangent;
|
|
vtx[i].tangent.w = dir_check > 0.0f ? 1.0f : -1.0f;
|
|
tangents[i] = tangent;
|
|
bitangents[i] = bitangent;
|
|
}
|
|
|
|
for (int32_t i = 0; i < num_vertex; i++) {
|
|
vec3 position = vtx[i].position;
|
|
vec3 tangent = tangents[i];
|
|
|
|
if (tangent != 0.0f)
|
|
continue;
|
|
|
|
int32_t nearest_vtx_idx = -1;
|
|
float_t current_distance = FLT_MAX;
|
|
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vec3 position_to_compare = vtx[j].position;
|
|
vec3 tangent_to_compare = tangents[j];
|
|
|
|
if (i == j || tangent_to_compare == 0.0f)
|
|
continue;
|
|
|
|
float_t distance = vec3::distance_squared(position, position_to_compare);
|
|
|
|
if (current_distance >= distance) {
|
|
nearest_vtx_idx = j;
|
|
current_distance = distance;
|
|
}
|
|
}
|
|
|
|
if (nearest_vtx_idx != -1)
|
|
vtx[i].tangent = vtx[nearest_vtx_idx].tangent;
|
|
else {
|
|
vec3 normal = vtx[i].normal;
|
|
|
|
vec3 temp1 = vec3::cross(normal, { 0.0f, 1.0f, 0.0f });
|
|
vec3 temp2 = vec3::cross(normal, { 1.0f, 0.0f, 0.0f });
|
|
|
|
float_t temp3 = vec3::length_squared(temp1);
|
|
float_t temp4 = vec3::length_squared(temp2);
|
|
|
|
vec3 tangent = vec3::normalize(temp3 > temp4 ? temp1 : temp2);
|
|
|
|
*(vec3*)&vtx[i].tangent = tangent;
|
|
vtx[i].tangent.w = 1.0f;
|
|
}
|
|
}
|
|
|
|
free_def(tangents);
|
|
free_def(bitangents);
|
|
|
|
enum_or(mesh->vertex_format, OBJ_VERTEX_TANGENT);
|
|
}
|
|
|
|
static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i16& bone_index) {
|
|
vec4 _bone_weight = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
vec4i16 _bone_index = { -1, -1, -1, -1 };
|
|
|
|
if (bone_index.x >= 0 && bone_weight.x > 0.0f)
|
|
obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.x, bone_weight.x);
|
|
|
|
if (bone_index.y >= 0 && bone_weight.y > 0.0f)
|
|
obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.y, bone_weight.y);
|
|
|
|
if (bone_index.z >= 0 && bone_weight.z > 0.0f)
|
|
obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.z, bone_weight.z);
|
|
|
|
if (bone_index.w >= 0 && bone_weight.w > 0.0f)
|
|
obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.w, bone_weight.w);
|
|
|
|
float_t sum = _bone_weight.x + _bone_weight.y + _bone_weight.z + _bone_weight.w;
|
|
if (sum > 0.0f && fabsf(sum - 1.0f) > 0.000001f)
|
|
_bone_weight *= 1.0f / sum;
|
|
|
|
bone_weight = _bone_weight;
|
|
bone_index = _bone_index;
|
|
}
|
|
|
|
static void obj_move_data(obj* obj_dst, const obj* obj_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
obj_dst->bounding_sphere = obj_src->bounding_sphere;
|
|
|
|
int32_t num_mesh = obj_src->num_mesh;
|
|
obj_mesh* mesh_array_src = obj_src->mesh_array;
|
|
obj_mesh* mesh_array_dst = alloc->allocate<obj_mesh>(num_mesh);
|
|
|
|
for (int32_t i = 0; i < num_mesh; i++)
|
|
obj_move_data_mesh(&mesh_array_dst[i], &mesh_array_src[i], alloc);
|
|
|
|
obj_dst->mesh_array = mesh_array_dst;
|
|
obj_dst->num_mesh = num_mesh;
|
|
|
|
int32_t num_material = obj_src->num_material;
|
|
obj_dst->material_array = alloc->allocate<obj_material_data>(obj_src->material_array, num_material);
|
|
obj_dst->num_material = num_material;
|
|
|
|
obj_dst->flags = obj_src->flags;
|
|
memcpy(obj_dst->reserved, obj_src->reserved, sizeof(uint32_t) * 10);
|
|
|
|
obj_dst->skin = obj_move_data_skin(obj_src->skin, alloc);
|
|
|
|
obj_dst->name = obj_move_data_string(obj_src->name, alloc);
|
|
obj_dst->id = obj_src->id;
|
|
obj_dst->hash = obj_src->hash;
|
|
}
|
|
|
|
static void obj_move_data_mesh(obj_mesh* mesh_dst, const obj_mesh* mesh_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
mesh_dst->flags = mesh_src->flags;
|
|
mesh_dst->bounding_sphere = mesh_src->bounding_sphere;
|
|
|
|
int32_t num_submesh = mesh_src->num_submesh;
|
|
obj_sub_mesh* submesh_array_src = mesh_src->submesh_array;
|
|
obj_sub_mesh* submesh_array_dst = alloc->allocate<obj_sub_mesh>(num_submesh);
|
|
|
|
for (int32_t i = 0; i < num_submesh; i++)
|
|
obj_move_data_sub_mesh(&submesh_array_dst[i], &submesh_array_src[i], alloc);
|
|
|
|
mesh_dst->submesh_array = submesh_array_dst;
|
|
mesh_dst->num_submesh = num_submesh;
|
|
|
|
mesh_dst->vertex_format = mesh_src->vertex_format;
|
|
mesh_dst->size_vertex = mesh_src->size_vertex;
|
|
|
|
int32_t num_vertex = mesh_src->num_vertex;
|
|
mesh_dst->vertex_array = alloc->allocate<obj_vertex_data>(mesh_src->vertex_array, num_vertex);
|
|
mesh_dst->num_vertex = num_vertex;
|
|
|
|
mesh_dst->attrib = mesh_src->attrib;
|
|
memcpy(mesh_dst->reserved, mesh_src->reserved, sizeof(uint32_t) * 6);
|
|
memcpy(mesh_dst->name, mesh_src->name, 0x40);
|
|
}
|
|
|
|
static void obj_move_data_sub_mesh(obj_sub_mesh* sub_mesh_dst, const obj_sub_mesh* sub_mesh_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
sub_mesh_dst->flags = sub_mesh_src->flags;
|
|
sub_mesh_dst->bounding_sphere = sub_mesh_src->bounding_sphere;
|
|
sub_mesh_dst->material_index = sub_mesh_src->material_index;
|
|
memcpy(sub_mesh_dst->uv_index, sub_mesh_src->uv_index, 8);
|
|
|
|
int32_t num_bone_index = sub_mesh_src->num_bone_index;
|
|
sub_mesh_dst->bone_index_array = alloc->allocate<uint16_t>(sub_mesh_src->bone_index_array, num_bone_index);
|
|
sub_mesh_dst->num_bone_index = num_bone_index;
|
|
|
|
sub_mesh_dst->bones_per_vertex = sub_mesh_src->bones_per_vertex;
|
|
sub_mesh_dst->primitive_type = sub_mesh_src->primitive_type;
|
|
sub_mesh_dst->index_format = sub_mesh_src->index_format;
|
|
|
|
int32_t num_index = sub_mesh_src->num_index;
|
|
sub_mesh_dst->index_array = alloc->allocate<uint32_t>(sub_mesh_src->index_array, num_index);
|
|
sub_mesh_dst->num_index = num_index;
|
|
|
|
sub_mesh_dst->attrib = sub_mesh_src->attrib;
|
|
sub_mesh_dst->bounding_box = sub_mesh_src->bounding_box;
|
|
|
|
sub_mesh_dst->first_index = sub_mesh_src->first_index;
|
|
sub_mesh_dst->last_index = sub_mesh_src->last_index;
|
|
sub_mesh_dst->index_offset = sub_mesh_src->index_offset;
|
|
}
|
|
|
|
static obj_skin* obj_move_data_skin(const obj_skin* sk_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
if (!sk_src)
|
|
return 0;
|
|
|
|
obj_skin* sk_dst = alloc->allocate<obj_skin>();
|
|
|
|
int32_t num_bone = sk_src->num_bone;
|
|
obj_skin_bone* bone_array_src = sk_src->bone_array;
|
|
obj_skin_bone* bone_array_dst = alloc->allocate<obj_skin_bone>(num_bone);
|
|
|
|
for (int32_t i = 0; i < num_bone; i++)
|
|
obj_move_data_skin_bone(&bone_array_dst[i], &bone_array_src[i], alloc);
|
|
|
|
sk_dst->bone_array = bone_array_dst;
|
|
sk_dst->num_bone = num_bone;
|
|
|
|
sk_dst->ex_data = obj_move_data_skin_ex_data(sk_src->ex_data, alloc);
|
|
return sk_dst;
|
|
}
|
|
|
|
static void obj_move_data_skin_bone(obj_skin_bone* bone_dst, const obj_skin_bone* bone_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
bone_dst->id = bone_src->id;
|
|
bone_dst->parent = bone_src->parent;
|
|
bone_dst->inv_bind_pose_mat = bone_src->inv_bind_pose_mat;
|
|
bone_dst->name = obj_move_data_string(bone_src->name, alloc);
|
|
}
|
|
|
|
static obj_skin_ex_data* obj_move_data_skin_ex_data(const obj_skin_ex_data* ex_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
if (!ex_src)
|
|
return 0;
|
|
|
|
obj_skin_ex_data* ex_dst = alloc->allocate<obj_skin_ex_data>();
|
|
|
|
int32_t num_osage_node = ex_src->num_osage_node;
|
|
ex_dst->osage_node_array = alloc->allocate<obj_skin_osage_node>(
|
|
ex_src->osage_node_array, num_osage_node);
|
|
ex_dst->num_osage_node = num_osage_node;
|
|
|
|
int32_t num_block = ex_src->num_block;
|
|
obj_skin_block* block_array_src = ex_src->block_array;
|
|
obj_skin_block* block_array_dst = alloc->allocate<obj_skin_block>(num_block);
|
|
|
|
for (int32_t i = 0; i < num_block; i++) {
|
|
obj_skin_block* block_src = &block_array_src[i];
|
|
obj_skin_block* block_dst = &block_array_dst[i];
|
|
switch (block_src->type) {
|
|
default:
|
|
block_dst->type = OBJ_SKIN_BLOCK_NONE;
|
|
block_dst->node = 0;
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CLOTH:
|
|
block_dst->type = OBJ_SKIN_BLOCK_CLOTH;
|
|
block_dst->cloth = obj_move_data_skin_block_cloth(block_src->cloth, alloc);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT:
|
|
block_dst->type = OBJ_SKIN_BLOCK_CONSTRAINT;
|
|
block_dst->constraint = obj_move_data_skin_block_constraint(block_src->constraint, alloc);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_EXPRESSION:
|
|
block_dst->type = OBJ_SKIN_BLOCK_EXPRESSION;
|
|
block_dst->expression = obj_move_data_skin_block_expression(block_src->expression, alloc);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_MOTION:
|
|
block_dst->type = OBJ_SKIN_BLOCK_MOTION;
|
|
block_dst->motion = obj_move_data_skin_block_motion(block_src->motion, alloc);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_OSAGE:
|
|
block_dst->type = OBJ_SKIN_BLOCK_OSAGE;
|
|
block_dst->osage = obj_move_data_skin_block_osage(block_src->osage, alloc);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ex_dst->block_array = block_array_dst;
|
|
ex_dst->num_block = num_block;
|
|
|
|
int32_t num_bone_name = ex_src->num_bone_name;
|
|
const char** bone_array_src = ex_src->bone_name_array;
|
|
const char** bone_array_dst = alloc->allocate<const char*>(num_bone_name);
|
|
|
|
for (int32_t i = 0; i < num_bone_name; i++)
|
|
bone_array_dst[i] = obj_move_data_string(bone_array_src[i], alloc);
|
|
|
|
ex_dst->bone_name_array = bone_array_dst;
|
|
ex_dst->num_bone_name = num_bone_name;
|
|
|
|
int32_t num_osage_sibling_info = ex_src->num_osage_sibling_info;
|
|
ex_dst->osage_sibling_info_array = alloc->allocate<obj_skin_osage_sibling_info>(
|
|
ex_src->osage_sibling_info_array, num_osage_sibling_info);
|
|
ex_dst->num_osage_sibling_info = num_osage_sibling_info;
|
|
|
|
memcpy(ex_dst->reserved, ex_src->reserved, sizeof(int64_t) * 7);
|
|
return ex_dst;
|
|
}
|
|
|
|
static obj_skin_block_cloth* obj_move_data_skin_block_cloth(const obj_skin_block_cloth* cls_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
obj_skin_block_cloth* cls_dst = alloc->allocate<obj_skin_block_cloth>();
|
|
cls_dst->mesh_name = obj_move_data_string(cls_src->mesh_name, alloc);
|
|
cls_dst->backface_mesh_name = obj_move_data_string(cls_src->backface_mesh_name, alloc);
|
|
cls_dst->field_8 = cls_src->field_8;
|
|
|
|
int32_t num_root = cls_src->num_root;
|
|
int32_t num_node = cls_src->num_node;
|
|
cls_dst->num_root = num_root;
|
|
cls_dst->num_node = num_node;
|
|
cls_dst->loop = cls_src->loop;
|
|
|
|
int32_t num_mat = cls_src->num_mat;
|
|
cls_dst->mat_array = alloc->allocate<mat4>(cls_src->mat_array, num_mat);
|
|
cls_dst->num_mat = num_mat;
|
|
|
|
obj_skin_block_cloth_root* root_array_src = cls_src->root_array;
|
|
obj_skin_block_cloth_root* root_array_dst = alloc->allocate<obj_skin_block_cloth_root>(num_root);
|
|
|
|
for (int32_t i = 0; i < num_root; i++)
|
|
obj_move_data_skin_block_cloth_root(&root_array_dst[i], &root_array_src[i], alloc);
|
|
|
|
cls_dst->root_array = root_array_dst;
|
|
|
|
cls_dst->node_array = alloc->allocate<obj_skin_block_cloth_node>(
|
|
cls_src->node_array, num_root * (num_node - 1ULL));
|
|
|
|
int32_t num_mesh_index = cls_src->num_mesh_index;
|
|
cls_dst->mesh_index_array = alloc->allocate<uint16_t>(
|
|
cls_src->mesh_index_array, num_mesh_index);
|
|
cls_dst->num_mesh_index = num_mesh_index;
|
|
|
|
int32_t num_backface_mesh_index = cls_src->num_backface_mesh_index;
|
|
cls_dst->backface_mesh_index_array = alloc->allocate<uint16_t>(
|
|
cls_src->backface_mesh_index_array, num_backface_mesh_index);
|
|
cls_dst->num_backface_mesh_index = num_backface_mesh_index;
|
|
|
|
cls_dst->skin_param = obj_move_data_skin_param(cls_src->skin_param, alloc);
|
|
cls_dst->reserved = cls_src->reserved;
|
|
return cls_dst;
|
|
}
|
|
|
|
static void obj_move_data_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root_dst,
|
|
const obj_skin_block_cloth_root* cloth_root_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
cloth_root_dst->pos = cloth_root_src->pos;
|
|
cloth_root_dst->normal = cloth_root_src->normal;
|
|
cloth_root_dst->field_18 = cloth_root_src->field_18;
|
|
cloth_root_dst->field_1C = cloth_root_src->field_1C;
|
|
cloth_root_dst->field_20 = cloth_root_src->field_20;
|
|
cloth_root_dst->field_24 = cloth_root_src->field_24;
|
|
|
|
for (uint32_t i = 0; i < 4; i++)
|
|
obj_move_data_skin_block_cloth_root_bone_weight(&cloth_root_dst->bone_weights[i],
|
|
&cloth_root_src->bone_weights[i], alloc);
|
|
}
|
|
|
|
static void obj_move_data_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight_dst,
|
|
const obj_skin_block_cloth_root_bone_weight* bone_weight_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
bone_weight_dst->bone_name = obj_move_data_string(bone_weight_src->bone_name, alloc);
|
|
bone_weight_dst->weight = bone_weight_src->weight;
|
|
bone_weight_dst->matrix_index = bone_weight_src->matrix_index;
|
|
bone_weight_dst->reserved = bone_weight_src->reserved;
|
|
}
|
|
|
|
static obj_skin_block_constraint* obj_move_data_skin_block_constraint(const obj_skin_block_constraint* cns_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
obj_skin_block_constraint* cns_dst = alloc->allocate<obj_skin_block_constraint>();
|
|
obj_move_data_skin_block_node(&cns_dst->node, &cns_src->node, alloc);
|
|
cns_dst->name_index = cns_src->name_index;
|
|
cns_dst->source_node_name = obj_move_data_string(cns_src->source_node_name, alloc);
|
|
cns_dst->coupling = cns_src->coupling;
|
|
|
|
switch (cns_src->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION: {
|
|
cns_dst->type = OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION;
|
|
|
|
obj_skin_block_constraint_orientation* orientation_src = cns_src->orientation;
|
|
obj_skin_block_constraint_orientation* orientation_dst
|
|
= alloc->allocate<obj_skin_block_constraint_orientation>();
|
|
orientation_dst->offset = orientation_src->offset;
|
|
cns_dst->orientation = orientation_dst;
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION: {
|
|
cns_dst->type = OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION;
|
|
|
|
obj_skin_block_constraint_direction* direction_src = cns_src->direction;
|
|
obj_skin_block_constraint_direction* direction_dst
|
|
= alloc->allocate<obj_skin_block_constraint_direction>();
|
|
obj_move_data_skin_block_constraint_up_vector(
|
|
&direction_dst->up_vector, &direction_src->up_vector, alloc);
|
|
direction_dst->align_axis = direction_src->align_axis;
|
|
direction_dst->target_offset = direction_src->target_offset;
|
|
cns_dst->direction = direction_dst;
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION: {
|
|
cns_dst->type = OBJ_SKIN_BLOCK_CONSTRAINT_POSITION;
|
|
|
|
obj_skin_block_constraint_position* position_src = cns_src->position;
|
|
obj_skin_block_constraint_position* position_dst
|
|
= alloc->allocate<obj_skin_block_constraint_position>();
|
|
obj_move_data_skin_block_constraint_up_vector(
|
|
&position_dst->up_vector, &position_src->up_vector, alloc);
|
|
position_dst->constrained_object = position_src->constrained_object;
|
|
position_dst->constraining_object = position_src->constraining_object;
|
|
cns_dst->position = position_dst;
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE: {
|
|
cns_dst->type = OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE;
|
|
|
|
obj_skin_block_constraint_distance* distance_src = cns_src->distance;
|
|
obj_skin_block_constraint_distance* distance_dst
|
|
= alloc->allocate<obj_skin_block_constraint_distance>();
|
|
obj_move_data_skin_block_constraint_up_vector(
|
|
&distance_dst->up_vector, &distance_src->up_vector, alloc);
|
|
distance_dst->distance = distance_src->distance;
|
|
distance_dst->constrained_object = distance_src->constrained_object;
|
|
distance_dst->constraining_object = distance_src->constraining_object;
|
|
cns_dst->distance = distance_dst;
|
|
} break;
|
|
default:
|
|
cns_dst->type = OBJ_SKIN_BLOCK_CONSTRAINT_NONE;
|
|
cns_dst->data = 0;
|
|
}
|
|
return cns_dst;
|
|
}
|
|
|
|
static void obj_move_data_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector_dst,
|
|
const obj_skin_block_constraint_up_vector* up_vector_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
up_vector_dst->active = up_vector_src->active;
|
|
up_vector_dst->roll = up_vector_src->roll;
|
|
up_vector_dst->affected_axis = up_vector_src->affected_axis;
|
|
up_vector_dst->point_at = up_vector_src->point_at;
|
|
up_vector_dst->name = obj_move_data_string(up_vector_src->name, alloc);
|
|
}
|
|
|
|
static obj_skin_block_expression* obj_move_data_skin_block_expression(const obj_skin_block_expression* exp_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
obj_skin_block_expression* exp_dst = alloc->allocate<obj_skin_block_expression>();
|
|
obj_move_data_skin_block_node(&exp_dst->node, &exp_src->node, alloc);
|
|
exp_dst->name_index = exp_src->name_index;
|
|
|
|
int32_t num_expression = exp_src->num_expression;
|
|
num_expression = min_def(num_expression, 9);
|
|
exp_dst->num_expression = num_expression;
|
|
for (int32_t i = 0; i < num_expression; i++)
|
|
exp_dst->expression_array[i] = obj_move_data_string(exp_src->expression_array[i], alloc);
|
|
|
|
for (int32_t i = num_expression; i < 9; i++)
|
|
exp_dst->expression_array[i] = 0;
|
|
return exp_dst;
|
|
}
|
|
|
|
static obj_skin_block_motion* obj_move_data_skin_block_motion(const obj_skin_block_motion* mot_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
obj_skin_block_motion* mot_dst = alloc->allocate<obj_skin_block_motion>();
|
|
obj_move_data_skin_block_node(&mot_dst->node, &mot_src->node, alloc);
|
|
|
|
mot_dst->name = 0;
|
|
if ((mot_src->name_index & ~0x7FFF) == 0x8000)
|
|
mot_dst->name_index = mot_src->name_index;
|
|
else
|
|
mot_dst->name = obj_move_data_string(mot_src->name, alloc);
|
|
|
|
int32_t num_node = mot_src->num_node;
|
|
mot_dst->node_array = alloc->allocate<obj_skin_motion_node>(mot_src->node_array, num_node);
|
|
mot_dst->num_node = num_node;
|
|
return mot_dst;
|
|
}
|
|
|
|
static void obj_move_data_skin_block_node(obj_skin_block_node* node_dst, const obj_skin_block_node* node_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
node_dst->parent_name = obj_move_data_string(node_src->parent_name, alloc);
|
|
node_dst->position = node_src->position;
|
|
node_dst->rotation = node_src->rotation;
|
|
node_dst->scale = node_src->scale;
|
|
}
|
|
|
|
static obj_skin_block_osage* obj_move_data_skin_block_osage(const obj_skin_block_osage* osg_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
obj_skin_block_osage* osg_dst = alloc->allocate<obj_skin_block_osage>();
|
|
obj_move_data_skin_block_node(&osg_dst->node, &osg_src->node, alloc);
|
|
osg_dst->start_index = osg_src->start_index;
|
|
osg_dst->count = osg_src->count;
|
|
|
|
int32_t num_node = osg_src->num_node;
|
|
osg_dst->node_array = alloc->allocate<obj_skin_osage_node>(osg_src->node_array, num_node);
|
|
osg_dst->num_node = num_node;
|
|
|
|
osg_dst->skin_param = obj_move_data_skin_param(osg_src->skin_param, alloc);
|
|
osg_dst->name_index = osg_src->name_index;
|
|
osg_dst->end_name_index = osg_src->end_name_index;
|
|
osg_dst->motion_node_name = obj_move_data_string(osg_src->motion_node_name, alloc);
|
|
return osg_dst;
|
|
}
|
|
|
|
static obj_skin_skin_param* obj_move_data_skin_param(const obj_skin_skin_param* skp_src,
|
|
prj::shared_ptr<prj::stack_allocator> alloc) {
|
|
if (!skp_src)
|
|
return 0;
|
|
|
|
obj_skin_skin_param* skp_dst = alloc->allocate<obj_skin_skin_param>();
|
|
skp_dst->unk0 = skp_src->unk0;
|
|
skp_dst->force = skp_src->force;
|
|
skp_dst->force_gain = skp_src->force_gain;
|
|
skp_dst->air_res = skp_src->air_res;
|
|
skp_dst->rot_y = skp_src->rot_y;
|
|
skp_dst->rot_z = skp_src->rot_z;
|
|
skp_dst->hinge_y = skp_src->hinge_y;
|
|
skp_dst->hinge_z = skp_src->hinge_z;
|
|
skp_dst->name = obj_move_data_string(skp_src->name, alloc);
|
|
|
|
uint32_t coli_count = skp_src->coli_count;
|
|
skp_dst->coli = alloc->allocate<obj_skin_skin_param_coli>(skp_src->coli, coli_count);
|
|
skp_dst->coli_count = coli_count;
|
|
|
|
skp_dst->coli_r = skp_src->coli_r;
|
|
skp_dst->friction = skp_src->friction;
|
|
skp_dst->wind_afc = skp_src->wind_afc;
|
|
skp_dst->unk44 = skp_src->unk44;
|
|
return skp_dst;
|
|
}
|
|
|
|
static void obj_set_classic_read_inner(obj_set* set, prj::shared_ptr<prj::stack_allocator>& alloc, stream& s) {
|
|
uint32_t version = s.read_uint32_t();
|
|
if (version != 0x05062500) {
|
|
set->ready = false;
|
|
set->modern = false;
|
|
set->big_endian = false;
|
|
set->is_x = false;
|
|
return;
|
|
}
|
|
|
|
obj_set_header osh = {};
|
|
set->obj_num = s.read_int32_t();
|
|
osh.last_obj_id = s.read_uint32_t();
|
|
osh.obj_data = s.read_uint32_t();
|
|
osh.obj_skin_data = s.read_uint32_t();
|
|
osh.obj_name_data = s.read_uint32_t();
|
|
osh.obj_id_data = s.read_uint32_t();
|
|
osh.tex_id_data = s.read_uint32_t();
|
|
set->tex_id_num = s.read_int32_t();
|
|
set->reserved[0] = s.read_uint32_t();
|
|
set->reserved[1] = s.read_uint32_t();
|
|
|
|
int32_t obj_num = set->obj_num;
|
|
set->obj_data = alloc->allocate<obj*>(obj_num);
|
|
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
set->obj_data[i] = alloc->allocate<::obj>();
|
|
|
|
uint32_t* data = force_malloc<uint32_t>(obj_num * 3ULL);
|
|
|
|
uint32_t* obj_data = 0;
|
|
if (osh.obj_data) {
|
|
obj_data = data;
|
|
s.set_position(osh.obj_data, SEEK_SET);
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_data[i] = s.read_uint32_t();
|
|
}
|
|
|
|
uint32_t* obj_skin_data = 0;
|
|
if (osh.obj_skin_data) {
|
|
obj_skin_data = data + obj_num;
|
|
s.set_position(osh.obj_skin_data, SEEK_SET);
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_skin_data[i] = s.read_uint32_t();
|
|
}
|
|
|
|
uint32_t* obj_name_data = 0;
|
|
if (osh.obj_name_data) {
|
|
obj_name_data = data + obj_num * 2ULL;
|
|
s.set_position(osh.obj_name_data, SEEK_SET);
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_name_data[i] = s.read_uint32_t();
|
|
}
|
|
|
|
if (osh.obj_data) {
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj* obj = set->obj_data[i];
|
|
if (obj_data[i])
|
|
obj_classic_read_model(obj, alloc, s, obj_data[i]);
|
|
|
|
if (osh.obj_skin_data && obj_skin_data[i])
|
|
obj->skin = obj_classic_read_skin(alloc, s, obj_skin_data[i]);
|
|
|
|
if (osh.obj_name_data && obj_name_data[i]) {
|
|
obj->name = obj_read_utf8_string_null_terminated_offset(alloc, s, obj_name_data[i]);
|
|
obj->hash = hash_utf8_murmurhash(obj->name);
|
|
}
|
|
else
|
|
obj->hash = hash_murmurhash_empty;
|
|
}
|
|
|
|
s.set_position(osh.obj_id_data, SEEK_SET);
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
set->obj_data[i]->id = s.read_uint32_t();
|
|
}
|
|
|
|
if (osh.tex_id_data) {
|
|
s.set_position(osh.tex_id_data, SEEK_SET);
|
|
int32_t tex_id_num = set->tex_id_num;
|
|
uint32_t* tex_id_data = alloc->allocate<uint32_t>(tex_id_num);
|
|
set->tex_id_data = tex_id_data;
|
|
for (int32_t i = 0; i < tex_id_num; i++)
|
|
tex_id_data[i] = s.read_uint32_t();
|
|
}
|
|
|
|
free_def(data);
|
|
|
|
set->ready = true;
|
|
set->modern = false;
|
|
set->big_endian = false;
|
|
set->is_x = false;
|
|
}
|
|
|
|
static void obj_set_classic_write_inner(obj_set* set, stream& s) {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
|
|
obj_set_header osh = {};
|
|
osh.last_obj_id = -1;
|
|
|
|
int32_t obj_num = set->obj_num;
|
|
|
|
osh.obj_data = s.get_position();
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
|
|
int32_t* data = force_malloc<int32_t>(obj_num);
|
|
|
|
int32_t* obj_data = data;
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj_data[i] = (int32_t)s.get_position();
|
|
obj_classic_write_model(set->obj_data[i], s, obj_data[i]);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
s.position_push(osh.obj_data, SEEK_SET);
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s.write_uint32_t(obj_data[i]);
|
|
s.position_pop();
|
|
|
|
osh.obj_id_data = s.get_position();
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
uint32_t object_id = set->obj_data[i]->id;
|
|
s.write_uint32_t(object_id);
|
|
if (osh.last_obj_id < object_id)
|
|
osh.last_obj_id = object_id;
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
int32_t* obj_name_data = data;
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj_name_data[i] = (int32_t)s.get_position();
|
|
s.write_utf8_string_null_terminated(set->obj_data[i]->name);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
osh.obj_name_data = s.get_position();
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s.write_int32_t(obj_name_data[i]);
|
|
s.align_write(0x10);
|
|
|
|
osh.tex_id_data = s.get_position();
|
|
for (int32_t i = 0; i < set->tex_id_num; i++)
|
|
s.write_uint32_t(set->tex_id_data[i]);
|
|
s.align_write(0x10);
|
|
|
|
osh.obj_skin_data = s.get_position();
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
|
|
int32_t* obj_skin_data = data;
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj_skin* sk = set->obj_data[i]->skin;
|
|
if (!sk) {
|
|
obj_skin_data[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
obj_skin_data[i] = (int32_t)s.get_position();
|
|
obj_classic_write_skin(sk, s, obj_skin_data[i]);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
s.position_push(osh.obj_skin_data, SEEK_SET);
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s.write_int32_t(obj_skin_data[i]);
|
|
s.position_pop();
|
|
|
|
free_def(data);
|
|
|
|
s.position_push(0x00, SEEK_SET);
|
|
s.write_uint32_t(0x05062500);
|
|
s.write_int32_t(set->obj_num);
|
|
s.write_uint32_t((uint32_t)osh.last_obj_id);
|
|
s.write_uint32_t((uint32_t)osh.obj_data);
|
|
s.write_uint32_t((uint32_t)osh.obj_skin_data);
|
|
s.write_uint32_t((uint32_t)osh.obj_name_data);
|
|
s.write_uint32_t((uint32_t)osh.obj_id_data);
|
|
s.write_uint32_t((uint32_t)osh.tex_id_data);
|
|
s.write_int32_t(set->tex_id_num);
|
|
s.write_uint32_t(set->reserved[0]);
|
|
s.write_uint32_t(set->reserved[1]);
|
|
s.position_pop();
|
|
}
|
|
|
|
static void obj_classic_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr<prj::stack_allocator> alloc, stream& s) {
|
|
bool tri_strip = sub_mesh->primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP;
|
|
int32_t num_index = sub_mesh->num_index;
|
|
uint32_t* index_array = alloc->allocate<uint32_t>(num_index);
|
|
switch (sub_mesh->index_format) {
|
|
case OBJ_INDEX_U8:
|
|
if (tri_strip)
|
|
for (int32_t i = 0; i < num_index; i++) {
|
|
uint8_t idx = s.read_uint8_t();
|
|
index_array[i] = idx == 0xFF ? 0xFFFFFFFF : idx;
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
index_array[i] = s.read_uint8_t();
|
|
break;
|
|
case OBJ_INDEX_U16:
|
|
if (tri_strip)
|
|
for (int32_t i = 0; i < num_index; i++) {
|
|
uint16_t idx = s.read_uint16_t();
|
|
index_array[i] = idx == 0xFFFF ? 0xFFFFFFFF : idx;
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
index_array[i] = s.read_uint16_t();
|
|
break;
|
|
case OBJ_INDEX_U32:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
index_array[i] = s.read_uint32_t();
|
|
break;
|
|
}
|
|
sub_mesh->index_array = index_array;
|
|
}
|
|
|
|
static void obj_classic_write_index(obj_sub_mesh* sub_mesh, stream& s) {
|
|
uint32_t* index_array = sub_mesh->index_array;
|
|
int32_t num_index = sub_mesh->num_index;
|
|
switch (sub_mesh->index_format) {
|
|
case OBJ_INDEX_U8:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
s.write_uint8_t((uint8_t)index_array[i]);
|
|
break;
|
|
case OBJ_INDEX_U16:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
s.write_uint16_t((uint16_t)index_array[i]);
|
|
break;
|
|
case OBJ_INDEX_U32:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
s.write_uint32_t(index_array[i]);
|
|
break;
|
|
}
|
|
s.align_write(0x04);
|
|
}
|
|
|
|
static void obj_classic_read_model(obj* obj, prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset) {
|
|
const size_t mesh_size = 0xD8;
|
|
|
|
s.set_position(base_offset, SEEK_SET);
|
|
|
|
obj_header oh = {};
|
|
s.read_uint32_t(); // version
|
|
s.read_uint32_t(); // flags
|
|
obj->bounding_sphere.center.x = s.read_float_t();
|
|
obj->bounding_sphere.center.y = s.read_float_t();
|
|
obj->bounding_sphere.center.z = s.read_float_t();
|
|
obj->bounding_sphere.radius = s.read_float_t();
|
|
obj->num_mesh = s.read_int32_t();
|
|
oh.mesh_array = s.read_uint32_t();
|
|
obj->num_material = s.read_int32_t();
|
|
oh.material_array = s.read_uint32_t();
|
|
obj->reserved[0] = s.read_uint32_t();
|
|
obj->reserved[1] = s.read_uint32_t();
|
|
obj->reserved[2] = s.read_uint32_t();
|
|
obj->reserved[3] = s.read_uint32_t();
|
|
obj->reserved[4] = s.read_uint32_t();
|
|
obj->reserved[5] = s.read_uint32_t();
|
|
obj->reserved[6] = s.read_uint32_t();
|
|
obj->reserved[7] = s.read_uint32_t();
|
|
obj->reserved[8] = s.read_uint32_t();
|
|
obj->reserved[9] = s.read_uint32_t();
|
|
|
|
if (oh.mesh_array > 0) {
|
|
int32_t num_mesh = obj->num_mesh;
|
|
obj->mesh_array = alloc->allocate<obj_mesh>(num_mesh);
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
s.set_position(base_offset + oh.mesh_array + mesh_size * i, SEEK_SET);
|
|
obj_classic_read_model_mesh(&obj->mesh_array[i],
|
|
alloc, s, base_offset);
|
|
}
|
|
}
|
|
|
|
if (oh.material_array > 0) {
|
|
s.set_position(base_offset + oh.material_array, SEEK_SET);
|
|
obj->material_array = alloc->allocate<obj_material_data>(obj->num_material);
|
|
for (int32_t i = 0; i < obj->num_material; i++) {
|
|
obj_material_data& mat_data = obj->material_array[i];
|
|
s.read_data(mat_data);
|
|
|
|
for (obj_material_texture_data& j : mat_data.material.texdata)
|
|
mat4_transpose(&j.tex_coord_mat, &j.tex_coord_mat);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void obj_classic_write_model(obj* obj, stream& s, int64_t base_offset) {
|
|
const size_t mesh_size = 0xD8;
|
|
const size_t sub_mesh_size = 0x5C;
|
|
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.align_write(0x10);
|
|
|
|
int32_t num_mesh = obj->num_mesh;
|
|
|
|
obj_header oh = {};
|
|
if (num_mesh) {
|
|
oh.mesh_array = s.get_position() - base_offset;
|
|
|
|
obj_mesh_header* mhs = force_malloc<obj_mesh_header>(num_mesh);
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
obj_mesh_header* mh = &mhs[i];
|
|
|
|
s.write(0x04);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
|
|
for (int64_t& j : mh->vertex)
|
|
s.write_int32_t(0);
|
|
|
|
s.write_int32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write(0x18);
|
|
s.write(sizeof(mesh->name));
|
|
}
|
|
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
obj_mesh_header* mh = &mhs[i];
|
|
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
if (num_submesh) {
|
|
mh->submesh_array = s.get_position() - base_offset;
|
|
for (int32_t j = 0; j < num_submesh; j++) {
|
|
s.write(0x04);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_uint32_t(0);
|
|
s.write(0x08);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write(0x18);
|
|
s.write_uint32_t(0);
|
|
}
|
|
}
|
|
|
|
obj_classic_write_vertex(mesh, s, mh->vertex,
|
|
base_offset, &mh->num_vertex, &mh->format, &mh->size_vertex);
|
|
|
|
for (int32_t j = 0; j < num_submesh; j++) {
|
|
obj_sub_mesh* sub_mesh = &mesh->submesh_array[j];
|
|
|
|
obj_sub_mesh_header smh = {};
|
|
if (sub_mesh->bones_per_vertex == 4 && sub_mesh->num_bone_index) {
|
|
smh.bone_index_array_offset = s.get_position() - base_offset;
|
|
s.write(sub_mesh->bone_index_array, sub_mesh->num_bone_index * sizeof(uint16_t));
|
|
s.align_write(0x04);
|
|
}
|
|
|
|
smh.index_array_offset = s.get_position() - base_offset;
|
|
obj_classic_write_index(sub_mesh, s);
|
|
|
|
s.position_push(base_offset + mh->submesh_array + sub_mesh_size * j, SEEK_SET);
|
|
s.write_uint32_t(sub_mesh->flags);
|
|
s.write_float_t(sub_mesh->bounding_sphere.center.x);
|
|
s.write_float_t(sub_mesh->bounding_sphere.center.y);
|
|
s.write_float_t(sub_mesh->bounding_sphere.center.z);
|
|
s.write_float_t(sub_mesh->bounding_sphere.radius);
|
|
s.write_uint32_t(sub_mesh->material_index);
|
|
s.write(&sub_mesh->uv_index, 0x08);
|
|
s.write_int32_t(sub_mesh->num_bone_index);
|
|
s.write_uint32_t((uint32_t)smh.bone_index_array_offset);
|
|
s.write_uint32_t(sub_mesh->bones_per_vertex);
|
|
s.write_uint32_t(sub_mesh->primitive_type);
|
|
s.write_uint32_t(sub_mesh->index_format);
|
|
s.write_int32_t(sub_mesh->num_index);
|
|
s.write_uint32_t((uint32_t)smh.index_array_offset);
|
|
s.write_uint32_t(sub_mesh->attrib.w);
|
|
s.write(0x1C);
|
|
s.position_pop();
|
|
}
|
|
}
|
|
|
|
s.position_push(base_offset + oh.mesh_array, SEEK_SET);
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
obj_mesh_header* mh = &mhs[i];
|
|
|
|
s.write_uint32_t(mesh->flags);
|
|
s.write_float_t(mesh->bounding_sphere.center.x);
|
|
s.write_float_t(mesh->bounding_sphere.center.y);
|
|
s.write_float_t(mesh->bounding_sphere.center.z);
|
|
s.write_float_t(mesh->bounding_sphere.radius);
|
|
s.write_int32_t(mesh->num_submesh);
|
|
s.write_uint32_t((uint32_t)mh->submesh_array);
|
|
s.write_uint32_t(mh->format);
|
|
s.write_int32_t(mh->size_vertex);
|
|
s.write_int32_t(mh->num_vertex);
|
|
|
|
for (int64_t& j : mh->vertex)
|
|
s.write_uint32_t((uint32_t)j);
|
|
|
|
s.write_uint32_t(mesh->attrib.w & 0x3FFFFFFF);
|
|
s.write_uint32_t(mh->vertex_format_index);
|
|
s.write_uint32_t(mesh->reserved[0]);
|
|
s.write_uint32_t(mesh->reserved[1]);
|
|
s.write_uint32_t(mesh->reserved[2]);
|
|
s.write_uint32_t(mesh->reserved[3]);
|
|
s.write_uint32_t(mesh->reserved[4]);
|
|
s.write_uint32_t(mesh->reserved[5]);
|
|
s.write(mesh->name, sizeof(mesh->name) - 1);
|
|
s.write_char('\0');
|
|
}
|
|
s.position_pop();
|
|
free_def(mhs);
|
|
}
|
|
|
|
if (obj->num_material) {
|
|
oh.material_array = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < obj->num_material; i++) {
|
|
obj_material_data mat_data = obj->material_array[i];
|
|
for (obj_material_texture_data& j : mat_data.material.texdata)
|
|
mat4_transpose(&j.tex_coord_mat, &j.tex_coord_mat);
|
|
s.write_data(mat_data);
|
|
}
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
s.position_push(base_offset, SEEK_SET);
|
|
s.write_uint32_t(0x10000);
|
|
s.write_uint32_t(0x00);
|
|
s.write_float_t(obj->bounding_sphere.center.x);
|
|
s.write_float_t(obj->bounding_sphere.center.y);
|
|
s.write_float_t(obj->bounding_sphere.center.z);
|
|
s.write_float_t(obj->bounding_sphere.radius);
|
|
s.write_int32_t(obj->num_mesh);
|
|
s.write_uint32_t((uint32_t)oh.mesh_array);
|
|
s.write_int32_t(obj->num_material);
|
|
s.write_uint32_t((uint32_t)oh.material_array);
|
|
s.write_uint32_t(obj->reserved[0]);
|
|
s.write_uint32_t(obj->reserved[1]);
|
|
s.write_uint32_t(obj->reserved[2]);
|
|
s.write_uint32_t(obj->reserved[3]);
|
|
s.write_uint32_t(obj->reserved[4]);
|
|
s.write_uint32_t(obj->reserved[5]);
|
|
s.write_uint32_t(obj->reserved[6]);
|
|
s.write_uint32_t(obj->reserved[7]);
|
|
s.write_uint32_t(obj->reserved[8]);
|
|
s.write_uint32_t(obj->reserved[9]);
|
|
s.position_pop();
|
|
}
|
|
|
|
static void obj_classic_read_model_mesh(obj_mesh* mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset) {
|
|
const size_t sub_mesh_size = 0x5C;
|
|
|
|
obj_mesh_header mh = {};
|
|
mesh->flags = s.read_uint32_t();
|
|
mesh->bounding_sphere.center.x = s.read_float_t();
|
|
mesh->bounding_sphere.center.y = s.read_float_t();
|
|
mesh->bounding_sphere.center.z = s.read_float_t();
|
|
mesh->bounding_sphere.radius = s.read_float_t();
|
|
mesh->num_submesh = s.read_int32_t();
|
|
mh.submesh_array = s.read_uint32_t();
|
|
mh.format = (obj_vertex_format_file)s.read_uint32_t();
|
|
mh.size_vertex = s.read_int32_t();
|
|
mh.num_vertex = s.read_int32_t();
|
|
|
|
for (int64_t& i : mh.vertex)
|
|
i = s.read_uint32_t();
|
|
|
|
mesh->attrib.w = s.read_uint32_t() & 0x3FFFFFFF;
|
|
mh.vertex_format_index = s.read_uint32_t();
|
|
mesh->reserved[0] = s.read_uint32_t();
|
|
mesh->reserved[1] = s.read_uint32_t();
|
|
mesh->reserved[2] = s.read_uint32_t();
|
|
mesh->reserved[3] = s.read_uint32_t();
|
|
mesh->reserved[4] = s.read_uint32_t();
|
|
mesh->reserved[5] = s.read_uint32_t();
|
|
s.read(&mesh->name, sizeof(mesh->name));
|
|
mesh->name[sizeof(mesh->name) - 1] = 0;
|
|
|
|
if (mh.submesh_array) {
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
mesh->submesh_array = alloc->allocate<obj_sub_mesh>(num_submesh);
|
|
for (int32_t i = 0; i < num_submesh; i++) {
|
|
s.set_position(base_offset + mh.submesh_array + sub_mesh_size * i, SEEK_SET);
|
|
obj_classic_read_model_sub_mesh(&mesh->submesh_array[i],
|
|
alloc, s, base_offset);
|
|
}
|
|
}
|
|
|
|
obj_classic_read_vertex(mesh, alloc, s, mh.vertex,
|
|
base_offset, mh.num_vertex, mh.format);
|
|
obj_vertex_generate_tangents(mesh);
|
|
}
|
|
|
|
static void obj_classic_read_model_sub_mesh(obj_sub_mesh* sub_mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset) {
|
|
obj_sub_mesh_header smh = {};
|
|
sub_mesh->flags = s.read_uint32_t();
|
|
sub_mesh->bounding_sphere.center.x = s.read_float_t();
|
|
sub_mesh->bounding_sphere.center.y = s.read_float_t();
|
|
sub_mesh->bounding_sphere.center.z = s.read_float_t();
|
|
sub_mesh->bounding_sphere.radius = s.read_float_t();
|
|
sub_mesh->material_index = s.read_int32_t();
|
|
s.read(&sub_mesh->uv_index, 0x08);
|
|
sub_mesh->num_bone_index = s.read_int32_t();
|
|
smh.bone_index_array_offset = s.read_uint32_t();
|
|
sub_mesh->bones_per_vertex = s.read_uint32_t();
|
|
sub_mesh->primitive_type = (obj_primitive_type)s.read_uint32_t();
|
|
sub_mesh->index_format = (obj_index_format)s.read_uint32_t();
|
|
sub_mesh->num_index = s.read_int32_t();
|
|
smh.index_array_offset = s.read_uint32_t();
|
|
sub_mesh->attrib.w = s.read_uint32_t();
|
|
sub_mesh->bounding_box.center = sub_mesh->bounding_sphere.center;
|
|
sub_mesh->bounding_box.size.x = sub_mesh->bounding_sphere.radius * 2.0f;
|
|
sub_mesh->bounding_box.size.y = sub_mesh->bounding_sphere.radius * 2.0f;
|
|
sub_mesh->bounding_box.size.z = sub_mesh->bounding_sphere.radius * 2.0f;
|
|
s.read(0, 0x1C);
|
|
|
|
if (sub_mesh->bones_per_vertex == 4 && smh.bone_index_array_offset) {
|
|
sub_mesh->bone_index_array = alloc->allocate<uint16_t>(sub_mesh->num_bone_index);
|
|
s.set_position(base_offset + smh.bone_index_array_offset, SEEK_SET);
|
|
s.read(sub_mesh->bone_index_array, sub_mesh->num_bone_index * sizeof(uint16_t));
|
|
}
|
|
|
|
s.set_position(base_offset + smh.index_array_offset, SEEK_SET);
|
|
obj_classic_read_index(sub_mesh, alloc, s);
|
|
}
|
|
|
|
static obj_skin* obj_classic_read_skin(prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset) {
|
|
obj_skin* sk = alloc->allocate<obj_skin>();
|
|
s.set_position(base_offset, SEEK_SET);
|
|
|
|
obj_skin_header sh = {};
|
|
sh.bone_id_array_offset = s.read_uint32_t();
|
|
sh.bone_matrix_array_offset = s.read_uint32_t();
|
|
sh.bone_name_array_offset = s.read_uint32_t();
|
|
sh.ex_data_offset = s.read_uint32_t();
|
|
sk->num_bone = s.read_int32_t();
|
|
sh.bone_parent_id_array_offset = s.read_uint32_t();
|
|
s.read(0, 0x0C);
|
|
|
|
if (sh.bone_id_array_offset) {
|
|
sk->bone_array = alloc->allocate<obj_skin_bone>(sk->num_bone);
|
|
|
|
s.set_position(sh.bone_id_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
sk->bone_array[i].id = s.read_uint32_t();
|
|
|
|
if (sh.bone_matrix_array_offset) {
|
|
s.set_position(sh.bone_matrix_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
mat4& mat = sk->bone_array[i].inv_bind_pose_mat;
|
|
mat.row0.x = s.read_float_t();
|
|
mat.row1.x = s.read_float_t();
|
|
mat.row2.x = s.read_float_t();
|
|
mat.row3.x = s.read_float_t();
|
|
mat.row0.y = s.read_float_t();
|
|
mat.row1.y = s.read_float_t();
|
|
mat.row2.y = s.read_float_t();
|
|
mat.row3.y = s.read_float_t();
|
|
mat.row0.z = s.read_float_t();
|
|
mat.row1.z = s.read_float_t();
|
|
mat.row2.z = s.read_float_t();
|
|
mat.row3.z = s.read_float_t();
|
|
mat.row0.w = s.read_float_t();
|
|
mat.row1.w = s.read_float_t();
|
|
mat.row2.w = s.read_float_t();
|
|
mat.row3.w = s.read_float_t();
|
|
}
|
|
}
|
|
|
|
if (sh.bone_parent_id_array_offset) {
|
|
s.set_position(sh.bone_parent_id_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
sk->bone_array[i].parent = s.read_uint32_t();
|
|
}
|
|
|
|
if (sh.bone_name_array_offset) {
|
|
s.set_position(sh.bone_name_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
sk->bone_array[i].name = obj_read_utf8_string_null_terminated_offset(
|
|
alloc, s, s.read_uint32_t());
|
|
}
|
|
}
|
|
|
|
if (sh.ex_data_offset)
|
|
sk->ex_data = obj_classic_read_skin_ex_data(alloc, s, sh.ex_data_offset);
|
|
return sk;
|
|
}
|
|
|
|
static void obj_classic_write_skin(obj_skin* sk, stream& s, int64_t base_offset) {
|
|
obj_skin_header sh = {};
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write(0x0C);
|
|
s.align_write(0x10);
|
|
|
|
std::vector<string_hash> strings;
|
|
std::vector<int64_t> string_offsets;
|
|
std::vector<string_hash> bone_names;
|
|
if (sk->bone_array) {
|
|
sh.bone_id_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
s.write_int32_t(sk->bone_array[i].id);
|
|
s.align_write(0x10);
|
|
|
|
sh.bone_parent_id_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
s.write_int32_t(sk->bone_array[i].parent);
|
|
s.align_write(0x10);
|
|
|
|
sh.bone_name_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
s.write_int32_t(0);
|
|
obj_skin_strings_push_back_check(strings, sk->bone_array[i].name);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
sh.bone_matrix_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
mat4& mat = sk->bone_array[i].inv_bind_pose_mat;
|
|
s.write_float_t(mat.row0.x);
|
|
s.write_float_t(mat.row1.x);
|
|
s.write_float_t(mat.row2.x);
|
|
s.write_float_t(mat.row3.x);
|
|
s.write_float_t(mat.row0.y);
|
|
s.write_float_t(mat.row1.y);
|
|
s.write_float_t(mat.row2.y);
|
|
s.write_float_t(mat.row3.y);
|
|
s.write_float_t(mat.row0.z);
|
|
s.write_float_t(mat.row1.z);
|
|
s.write_float_t(mat.row2.z);
|
|
s.write_float_t(mat.row3.z);
|
|
s.write_float_t(mat.row0.w);
|
|
s.write_float_t(mat.row1.w);
|
|
s.write_float_t(mat.row2.w);
|
|
s.write_float_t(mat.row3.w);
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
obj_skin_block_header* bhs = 0;
|
|
int64_t osage_block_node = 0;
|
|
int64_t motion_block_node_mats = 0;
|
|
int64_t motion_block_node_name_offset = 0;
|
|
int64_t cloth_mats = 0;
|
|
int64_t cloth_root = 0;
|
|
int64_t cloth_nodes = 0;
|
|
int64_t cloth_mesh_indices = 0;
|
|
int64_t cloth_backface_mesh_indices = 0;
|
|
|
|
obj_skin_ex_data_header exh = {};
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
sh.ex_data_offset = s.get_position();
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write(0x04);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write(0x1C);
|
|
s.align_write(0x10);
|
|
|
|
if (ex->num_osage_node) {
|
|
exh.osage_node_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_osage_node; i++) {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
exh.osage_sibling_info_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_osage_sibling_info; i++) {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
}
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
exh.cloth_count = 0;
|
|
exh.num_osage = 0;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type == OBJ_SKIN_BLOCK_CLOTH)
|
|
exh.cloth_count++;
|
|
else if (block->type == OBJ_SKIN_BLOCK_OSAGE)
|
|
exh.num_osage++;
|
|
}
|
|
|
|
if (exh.num_osage || exh.cloth_count) {
|
|
exh.osage_name_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < exh.num_osage; i++)
|
|
s.write_int32_t(0);
|
|
|
|
for (int32_t i = 0; i < exh.cloth_count; i++)
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
const char** bone_name_array = ex->bone_name_array;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
switch (block->type) {
|
|
case OBJ_SKIN_BLOCK_CLOTH: {
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
obj_skin_strings_push_back_check(strings, cloth->mesh_name);
|
|
obj_skin_strings_push_back_check(strings, cloth->backface_mesh_name);
|
|
for (int32_t k = 0; k < cloth->num_root; k++) {
|
|
obj_skin_block_cloth_root* root_array = &cloth->root_array[k];
|
|
for (uint32_t l = 0; l < 4; l++)
|
|
obj_skin_strings_push_back_check(strings, root_array->bone_weights[l].bone_name);
|
|
}
|
|
|
|
if (cloth->skin_param)
|
|
obj_skin_strings_push_back_check(strings, cloth->skin_param->name);
|
|
obj_skin_strings_push_back_check(strings, "CLS");
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT: {
|
|
obj_skin_block_constraint* constraint = block->constraint;
|
|
obj_skin_strings_push_back_check(strings, constraint->node.parent_name);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, constraint->name_index);
|
|
obj_skin_strings_push_back_check(strings, constraint->source_node_name);
|
|
switch (constraint->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
obj_skin_strings_push_back_check(strings, "Orientation");
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
obj_skin_strings_push_back_check(strings, "Direction");
|
|
obj_skin_strings_push_back_check(strings, constraint->direction->up_vector.name);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
obj_skin_strings_push_back_check(strings, "Position");
|
|
obj_skin_strings_push_back_check(strings, constraint->position->up_vector.name);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
obj_skin_strings_push_back_check(strings, "Distance");
|
|
obj_skin_strings_push_back_check(strings, constraint->distance->up_vector.name);
|
|
break;
|
|
}
|
|
obj_skin_strings_push_back_check(strings, "CNS");
|
|
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, constraint->name_index);
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_EXPRESSION: {
|
|
obj_skin_block_expression* expression = block->expression;
|
|
for (int32_t j = 0; j < expression->num_expression; j++)
|
|
obj_skin_strings_push_back_check(strings, expression->expression_array[j]);
|
|
obj_skin_strings_push_back_check(strings, expression->node.parent_name);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, expression->name_index);
|
|
obj_skin_strings_push_back_check(strings, "EXP");
|
|
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, expression->name_index);
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_MOTION: {
|
|
obj_skin_block_motion* motion = block->motion;
|
|
obj_skin_strings_push_back_check(strings, motion->node.parent_name);
|
|
obj_skin_strings_push_back_check(strings, motion->name);
|
|
for (int32_t j = 0; j < motion->num_node; j++)
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, motion->node_array[j].name_index);
|
|
obj_skin_strings_push_back_check(strings, "MOT");
|
|
|
|
obj_skin_strings_push_back_check(bone_names, motion->name);
|
|
for (int32_t j = 0; j < motion->num_node; j++)
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, motion->node_array[j].name_index);
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_OSAGE: {
|
|
obj_skin_block_osage* osage = block->osage;
|
|
obj_skin_strings_push_back_check(strings, osage->node.parent_name);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage->end_name_index);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage->name_index);
|
|
for (int32_t j = 0; j < osage->count; j++) {
|
|
obj_skin_osage_node* osage_node = &ex->osage_node_array[j];
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage_node->name_index);
|
|
|
|
int32_t end_name_index = osage->end_name_index;
|
|
obj_skin_osage_sibling_info* osage_sibling_info = ex->osage_sibling_info_array;
|
|
for (int32_t k = 0; k < ex->num_osage_sibling_info; k++) {
|
|
if (end_name_index == osage_sibling_info->name_index) {
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage_sibling_info->sibling_name_index);
|
|
break;
|
|
}
|
|
osage_sibling_info++;
|
|
}
|
|
}
|
|
|
|
if (osage->skin_param)
|
|
obj_skin_strings_push_back_check(strings, osage->skin_param->name);
|
|
obj_skin_strings_push_back_check(strings, "OSG");
|
|
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage->name_index);
|
|
for (int32_t j = 0; j < osage->count; j++) {
|
|
obj_skin_osage_node* osage_node = &ex->osage_node_array[j];
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage_node->name_index);
|
|
|
|
int32_t end_name_index = osage->end_name_index;
|
|
obj_skin_osage_sibling_info* osage_sibling_info = ex->osage_sibling_info_array;
|
|
for (int32_t k = 0; k < ex->num_osage_sibling_info; k++) {
|
|
if (end_name_index == osage_sibling_info->name_index) {
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage_sibling_info->sibling_name_index);
|
|
break;
|
|
}
|
|
osage_sibling_info++;
|
|
}
|
|
}
|
|
|
|
obj_skin_osage_node* child_osage_node = &ex->osage_node_array[osage->start_index];
|
|
for (int32_t j = 0; j < osage->count; j++) {
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, child_osage_node->name_index);
|
|
|
|
int32_t name_index = child_osage_node->name_index;
|
|
obj_skin_osage_sibling_info* osage_sibling_info = ex->osage_sibling_info_array;
|
|
for (int32_t k = 0; k < ex->num_osage_sibling_info; k++) {
|
|
if (name_index == osage_sibling_info->name_index) {
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage_sibling_info->sibling_name_index);
|
|
break;
|
|
}
|
|
osage_sibling_info++;
|
|
}
|
|
child_osage_node++;
|
|
}
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage->end_name_index);
|
|
|
|
if (osage->skin_param)
|
|
obj_skin_strings_push_back_check(strings, osage->skin_param->name);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
exh.num_bone_name = (int32_t)bone_names.size();
|
|
exh.bone_name_array_offset = s.get_position();
|
|
for (string_hash& i : bone_names)
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
|
|
if (ex->num_block > 0) {
|
|
exh.block_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
}
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
|
|
bhs = force_malloc<obj_skin_block_header>(ex->num_block);
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_OSAGE)
|
|
continue;
|
|
|
|
obj_skin_block_osage* osage = block->osage;
|
|
if (osage->skin_param) {
|
|
obj_skin_skin_param* skin_param = osage->skin_param;
|
|
if (skin_param->coli) {
|
|
s.write(0x28ULL * skin_param->coli_count);
|
|
s.align_write(0x10);
|
|
}
|
|
s.write(0x38);
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
s.write(0x28);
|
|
s.write(0x14);
|
|
s.write(0x14);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_EXPRESSION)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
s.write(0x28);
|
|
s.write(0x2C);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
if (cloth->skin_param) {
|
|
obj_skin_skin_param* skin_param = cloth->skin_param;
|
|
if (skin_param->coli) {
|
|
s.write(0x28ULL * skin_param->coli_count);
|
|
s.align_write(0x10);
|
|
}
|
|
s.write(0x38);
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
s.write(0x34);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CONSTRAINT)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
s.write(0x28);
|
|
s.write(0x10);
|
|
switch (block->constraint->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
s.write(0x0C);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
s.write(0x24);
|
|
s.write(0x18);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
s.write(0x24);
|
|
s.write(0x14);
|
|
s.write(0x14);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
s.write(0x24);
|
|
s.write(0x04);
|
|
s.write(0x14);
|
|
s.write(0x14);
|
|
break;
|
|
}
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
int32_t motion_block_count = 0;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
s.write(0x28);
|
|
s.write(0x10);
|
|
motion_block_count++;
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (exh.num_osage) {
|
|
osage_block_node = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_OSAGE)
|
|
continue;
|
|
|
|
obj_skin_block_osage* osage = block->osage;
|
|
s.write(osage->num_node * (sizeof(uint32_t) + sizeof(float_t) * 4));
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
if (motion_block_count) {
|
|
motion_block_node_mats = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
obj_skin_block_motion* motion = block->motion;
|
|
s.write(motion->num_node * sizeof(mat4));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
motion_block_node_name_offset = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
obj_skin_block_motion* motion = block->motion;
|
|
s.write(motion->num_node * sizeof(uint32_t));
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
if (exh.cloth_count) {
|
|
cloth_mats = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
if (cloth->mat_array)
|
|
s.write(cloth->num_mat * sizeof(mat4));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_root = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write((sizeof(int32_t) + 4 * (sizeof(int32_t) + 3 * sizeof(int32_t)))
|
|
* cloth->num_root * (cloth->num_node - 1ULL));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_nodes = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write((11 * sizeof(int32_t)) * cloth->num_root * (cloth->num_node - 1ULL));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_mesh_indices = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write(sizeof(uint16_t) + cloth->num_mesh_index * sizeof(uint16_t));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_backface_mesh_indices = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write(sizeof(uint16_t) + cloth->num_backface_mesh_index * sizeof(uint16_t));
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sk->bone_array || sk->ex_data) {
|
|
quicksort_string_hash(strings.data(), strings.size());
|
|
string_offsets.reserve(strings.size());
|
|
for (string_hash& i : strings) {
|
|
string_offsets.push_back(s.get_position());
|
|
s.write_string_null_terminated(i.str);
|
|
}
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
std::vector<string_hash> osage_names;
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
int64_t cls_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "CLS");
|
|
int64_t cns_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "CNS");
|
|
int64_t exp_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "EXP");
|
|
int64_t mot_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "MOT");
|
|
int64_t osg_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "OSG");
|
|
|
|
if (ex->num_block > 0) {
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_classic_write_skin_block_cloth(block->cloth,
|
|
s, strings, string_offsets, &cloth_mats, &cloth_root,
|
|
&cloth_nodes, &cloth_mesh_indices, &cloth_backface_mesh_indices);
|
|
s.position_pop();
|
|
}
|
|
|
|
int64_t constraint_type_name_offsets[4];
|
|
constraint_type_name_offsets[0] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Orientation");
|
|
constraint_type_name_offsets[1] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Direction");
|
|
constraint_type_name_offsets[2] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Position");
|
|
constraint_type_name_offsets[3] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Distance");
|
|
|
|
const char** bone_name_array = ex->bone_name_array;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CONSTRAINT)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_classic_write_skin_block_constraint(block->constraint,
|
|
s, strings, string_offsets, bone_name_array, constraint_type_name_offsets);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_EXPRESSION)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_classic_write_skin_block_expression(block->expression,
|
|
s, strings, string_offsets, bone_name_array);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_classic_write_skin_block_motion(block->motion,
|
|
s, strings, string_offsets, bone_name_array,
|
|
&motion_block_node_name_offset, &motion_block_node_mats);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_OSAGE)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_classic_write_skin_block_osage(block->osage,
|
|
s, strings, string_offsets, &osage_block_node);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type == OBJ_SKIN_BLOCK_CLOTH) {
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
obj_skin_strings_push_back_check(osage_names, cloth->mesh_name);
|
|
}
|
|
else if (block->type == OBJ_SKIN_BLOCK_OSAGE) {
|
|
obj_skin_block_osage* osage = block->osage;
|
|
obj_skin_strings_push_back_check_by_index(osage_names,
|
|
bone_name_array, osage->name_index);
|
|
}
|
|
}
|
|
|
|
s.position_push(exh.block_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
switch (block->type) {
|
|
case OBJ_SKIN_BLOCK_CLOTH:
|
|
s.write_uint32_t((uint32_t)cls_offset);
|
|
s.write_uint32_t((uint32_t)bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT:
|
|
s.write_uint32_t((uint32_t)cns_offset);
|
|
s.write_uint32_t((uint32_t)bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_EXPRESSION:
|
|
s.write_uint32_t((uint32_t)exp_offset);
|
|
s.write_uint32_t((uint32_t)bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_MOTION:
|
|
s.write_uint32_t((uint32_t)mot_offset);
|
|
s.write_uint32_t((uint32_t)bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_OSAGE:
|
|
s.write_uint32_t((uint32_t)osg_offset);
|
|
s.write_uint32_t((uint32_t)bhs[i].block_offset);
|
|
break;
|
|
default:
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
break;
|
|
}
|
|
}
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.position_pop();
|
|
free_def(bhs);
|
|
}
|
|
}
|
|
|
|
if (sk->bone_array) {
|
|
s.position_push(sh.bone_name_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(
|
|
strings, string_offsets, sk->bone_array[i].name);
|
|
s.write_uint32_t((uint32_t)str_offset);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
if (ex->num_block > 0) {
|
|
s.position_push(exh.bone_name_array_offset, SEEK_SET);
|
|
for (string_hash& i : bone_names) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, i.c_str());
|
|
s.write_uint32_t((uint32_t)str_offset);
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(exh.osage_node_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_node; i++) {
|
|
obj_skin_osage_node* osage_node = &ex->osage_node_array[i];
|
|
s.write_uint32_t(osage_node->name_index);
|
|
s.write_float_t(osage_node->length);
|
|
s.write_uint32_t(osage_node->name_index & 0x7FFF);
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(exh.osage_sibling_info_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_sibling_info; i++) {
|
|
obj_skin_osage_sibling_info* osage_sibling_info = &ex->osage_sibling_info_array[i];
|
|
s.write_uint32_t(osage_sibling_info->name_index);
|
|
s.write_uint32_t(osage_sibling_info->sibling_name_index);
|
|
s.write_float_t(osage_sibling_info->max_distance);
|
|
}
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.position_pop();
|
|
|
|
exh.num_osage = (int32_t)osage_names.size();
|
|
exh.num_osage -= exh.cloth_count;
|
|
s.position_push(exh.osage_name_array_offset, SEEK_SET);
|
|
for (string_hash& i : osage_names) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, i.c_str());
|
|
s.write_uint32_t((uint32_t)str_offset);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
s.position_push(sh.ex_data_offset, SEEK_SET);
|
|
s.write_int32_t(exh.num_osage);
|
|
s.write_int32_t(ex->num_osage_node);
|
|
s.write(0x04);
|
|
s.write_uint32_t((uint32_t)exh.osage_node_array_offset);
|
|
s.write_uint32_t((uint32_t)exh.osage_name_array_offset);
|
|
s.write_uint32_t((uint32_t)exh.block_array_offset);
|
|
s.write_int32_t(exh.num_bone_name);
|
|
s.write_uint32_t((uint32_t)exh.bone_name_array_offset);
|
|
s.write_uint32_t((uint32_t)exh.osage_sibling_info_array_offset);
|
|
s.write_int32_t(exh.cloth_count);
|
|
s.write_uint32_t((uint32_t)ex->reserved[0]);
|
|
s.write_uint32_t((uint32_t)ex->reserved[1]);
|
|
s.write_uint32_t((uint32_t)ex->reserved[2]);
|
|
s.write_uint32_t((uint32_t)ex->reserved[3]);
|
|
s.write_uint32_t((uint32_t)ex->reserved[4]);
|
|
s.write_uint32_t((uint32_t)ex->reserved[5]);
|
|
s.write_uint32_t((uint32_t)ex->reserved[6]);
|
|
s.position_pop();
|
|
}
|
|
|
|
s.position_push(base_offset, SEEK_SET);
|
|
s.write_uint32_t((uint32_t)sh.bone_id_array_offset);
|
|
s.write_uint32_t((uint32_t)sh.bone_matrix_array_offset);
|
|
s.write_uint32_t((uint32_t)sh.bone_name_array_offset);
|
|
s.write_uint32_t((uint32_t)sh.ex_data_offset);
|
|
s.write_int32_t(sk->num_bone);
|
|
s.write_uint32_t((uint32_t)sh.bone_parent_id_array_offset);
|
|
s.write(0x0C);
|
|
s.position_pop();
|
|
}
|
|
|
|
static obj_skin_ex_data* obj_classic_read_skin_ex_data(prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset) {
|
|
obj_skin_ex_data* ex = alloc->allocate<obj_skin_ex_data>();
|
|
s.set_position(base_offset, SEEK_SET);
|
|
|
|
obj_skin_ex_data_header exh = {};
|
|
exh.num_osage = s.read_int32_t();
|
|
ex->num_osage_node = s.read_int32_t();
|
|
s.read(0, 0x04);
|
|
exh.osage_node_array_offset = s.read_uint32_t();
|
|
exh.osage_name_array_offset = s.read_uint32_t();
|
|
exh.block_array_offset = s.read_uint32_t();
|
|
exh.num_bone_name = s.read_int32_t();
|
|
exh.bone_name_array_offset = s.read_uint32_t();
|
|
exh.osage_sibling_info_array_offset = s.read_uint32_t();
|
|
exh.cloth_count = s.read_int32_t();
|
|
ex->reserved[0] = s.read_uint32_t();
|
|
ex->reserved[1] = s.read_uint32_t();
|
|
ex->reserved[2] = s.read_uint32_t();
|
|
ex->reserved[3] = s.read_uint32_t();
|
|
ex->reserved[4] = s.read_uint32_t();
|
|
ex->reserved[5] = s.read_uint32_t();
|
|
ex->reserved[6] = s.read_uint32_t();
|
|
|
|
ex->bone_name_array = 0;
|
|
ex->num_bone_name = 0;
|
|
if (!exh.bone_name_array_offset)
|
|
return ex;
|
|
|
|
int32_t num_bone_name = exh.num_bone_name;
|
|
const char** bone_name_array = alloc->allocate<const char*>(num_bone_name);
|
|
if (!bone_name_array)
|
|
return ex;
|
|
|
|
ex->bone_name_array = bone_name_array;
|
|
ex->num_bone_name = num_bone_name;
|
|
|
|
s.set_position(exh.bone_name_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < num_bone_name; i++) {
|
|
int32_t string_offset = s.read_uint32_t();
|
|
if (string_offset)
|
|
bone_name_array[i] = obj_read_utf8_string_null_terminated_offset(alloc, s, string_offset);
|
|
else
|
|
bone_name_array[i] = 0;
|
|
}
|
|
|
|
if (exh.osage_node_array_offset) {
|
|
ex->osage_node_array = alloc->allocate<obj_skin_osage_node>(ex->num_osage_node);
|
|
|
|
s.set_position(exh.osage_node_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_node; i++) {
|
|
obj_skin_osage_node* osage_node = &ex->osage_node_array[i];
|
|
|
|
osage_node->name_index = s.read_uint32_t();
|
|
osage_node->length = s.read_float_t();
|
|
s.read(0, 0x04);
|
|
}
|
|
}
|
|
|
|
if (exh.block_array_offset) {
|
|
ex->num_block = 0;
|
|
s.set_position(exh.block_array_offset, SEEK_SET);
|
|
while (s.read_uint32_t()) {
|
|
s.read(0, 0x04);
|
|
ex->num_block++;
|
|
}
|
|
|
|
obj_skin_block_header* bhs = force_malloc<obj_skin_block_header>(ex->num_block);
|
|
s.set_position(exh.block_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
bhs[i].block_signature_offset = s.read_uint32_t();
|
|
bhs[i].block_offset = s.read_uint32_t();
|
|
}
|
|
|
|
ex->block_array = alloc->allocate<obj_skin_block>(ex->num_block);
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
|
|
std::string block_signature = s.read_string_null_terminated_offset(
|
|
bhs[i].block_signature_offset);
|
|
if (block_signature.size() != 3)
|
|
continue;
|
|
|
|
uint32_t signature = load_reverse_endianness_uint32_t(block_signature.c_str());
|
|
switch (signature) {
|
|
case 'CLS\0':
|
|
block->type = OBJ_SKIN_BLOCK_CLOTH;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->cloth = obj_classic_read_skin_block_cloth(
|
|
alloc, s, bone_name_array);
|
|
break;
|
|
case 'CNS\0':
|
|
block->type = OBJ_SKIN_BLOCK_CONSTRAINT;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->constraint = obj_classic_read_skin_block_constraint(
|
|
alloc, s, bone_name_array);
|
|
break;
|
|
case 'EXP\0':
|
|
block->type = OBJ_SKIN_BLOCK_EXPRESSION;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->expression = obj_classic_read_skin_block_expression(
|
|
alloc, s, bone_name_array);
|
|
break;
|
|
case 'MOT\0':
|
|
block->type = OBJ_SKIN_BLOCK_MOTION;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->motion = obj_classic_read_skin_block_motion(
|
|
alloc, s, bone_name_array);
|
|
break;
|
|
case 'OSG\0':
|
|
block->type = OBJ_SKIN_BLOCK_OSAGE;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->osage = obj_classic_read_skin_block_osage(
|
|
alloc, s, bone_name_array);
|
|
break;
|
|
}
|
|
}
|
|
free_def(bhs);
|
|
}
|
|
|
|
if (exh.osage_sibling_info_array_offset) {
|
|
ex->num_osage_sibling_info = 0;
|
|
s.set_position(exh.osage_sibling_info_array_offset, SEEK_SET);
|
|
while (s.read_uint32_t()) {
|
|
s.read(0, 0x08);
|
|
ex->num_osage_sibling_info++;
|
|
}
|
|
|
|
obj_skin_osage_sibling_info* osi = alloc->allocate<
|
|
obj_skin_osage_sibling_info>(ex->num_osage_sibling_info);
|
|
ex->osage_sibling_info_array = osi;
|
|
s.set_position(exh.osage_sibling_info_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_sibling_info; i++, osi++) {
|
|
osi->name_index = s.read_uint32_t();
|
|
osi->sibling_name_index = s.read_uint32_t();
|
|
osi->max_distance = s.read_float_t();
|
|
}
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_OSAGE)
|
|
continue;
|
|
|
|
obj_skin_block_osage* osage = block->osage;
|
|
if (!osage->node_array)
|
|
continue;
|
|
|
|
int32_t count = osage->count;
|
|
obj_skin_osage_node* child_osage_node = &ex->osage_node_array[osage->start_index];
|
|
obj_skin_osage_node* osage_node = osage->node_array;
|
|
for (int32_t j = 0; j < count; j++) {
|
|
osage_node->name_index = child_osage_node->name_index;
|
|
osage_node->length = child_osage_node->length;
|
|
|
|
child_osage_node++;
|
|
osage_node++;
|
|
}
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static obj_skin_block_cloth* obj_classic_read_skin_block_cloth(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
obj_skin_block_cloth* cls = alloc->allocate<obj_skin_block_cloth>();
|
|
uint32_t mesh_name_offset = s.read_uint32_t();
|
|
uint32_t backface_mesh_name_offset = s.read_uint32_t();
|
|
cls->field_8 = s.read_uint32_t();
|
|
cls->num_root = s.read_int32_t();
|
|
cls->num_node = s.read_int32_t();
|
|
cls->loop = s.read_uint32_t();
|
|
uint32_t mat_array_offset = s.read_uint32_t();
|
|
uint32_t root_array_offset = s.read_uint32_t();
|
|
uint32_t node_array_offset = s.read_uint32_t();
|
|
uint32_t mesh_index_array_offset = s.read_uint32_t();
|
|
uint32_t backface_mesh_index_array_offset = s.read_uint32_t();
|
|
uint32_t skin_param_offset = s.read_uint32_t();
|
|
cls->reserved = s.read_uint32_t();
|
|
|
|
cls->mesh_name = obj_read_utf8_string_null_terminated_offset(alloc, s, mesh_name_offset);
|
|
cls->backface_mesh_name = obj_read_utf8_string_null_terminated_offset(alloc, s, backface_mesh_name_offset);
|
|
|
|
if (mat_array_offset) {
|
|
int32_t max_matrix_index = -1;
|
|
|
|
s.position_push(root_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < cls->num_root; i++) {
|
|
s.read(0x28);
|
|
for (uint32_t j = 0; j < 4; j++) {
|
|
uint32_t name_offset = s.read_uint32_t();
|
|
s.read(0x04);
|
|
int32_t matrix_index = s.read_int32_t();
|
|
s.read(0x04);
|
|
if (name_offset && max_matrix_index < matrix_index)
|
|
max_matrix_index = matrix_index;
|
|
}
|
|
}
|
|
s.position_pop();
|
|
|
|
cls->num_mat = max_matrix_index > -1 ? max_matrix_index + 1 : 0;
|
|
cls->mat_array = alloc->allocate<mat4>(cls->num_mat);
|
|
s.position_push(mat_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < cls->num_mat; i++) {
|
|
mat4& mat = cls->mat_array[i];
|
|
mat.row0.x = s.read_float_t();
|
|
mat.row1.x = s.read_float_t();
|
|
mat.row2.x = s.read_float_t();
|
|
mat.row3.x = s.read_float_t();
|
|
mat.row0.y = s.read_float_t();
|
|
mat.row1.y = s.read_float_t();
|
|
mat.row2.y = s.read_float_t();
|
|
mat.row3.y = s.read_float_t();
|
|
mat.row0.z = s.read_float_t();
|
|
mat.row1.z = s.read_float_t();
|
|
mat.row2.z = s.read_float_t();
|
|
mat.row3.z = s.read_float_t();
|
|
mat.row0.w = s.read_float_t();
|
|
mat.row1.w = s.read_float_t();
|
|
mat.row2.w = s.read_float_t();
|
|
mat.row3.w = s.read_float_t();
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (root_array_offset) {
|
|
s.position_push(root_array_offset, SEEK_SET);
|
|
cls->root_array = alloc->allocate<obj_skin_block_cloth_root>(cls->num_root);
|
|
for (int32_t i = 0; i < cls->num_root; i++)
|
|
obj_classic_read_skin_block_cloth_root(&cls->root_array[i], alloc, s, str);
|
|
s.position_pop();
|
|
}
|
|
|
|
if (node_array_offset) {
|
|
s.position_push(node_array_offset, SEEK_SET);
|
|
cls->node_array = alloc->allocate<obj_skin_block_cloth_node>(
|
|
cls->num_root * (cls->num_node - 1ULL));
|
|
for (int32_t i = 0; i < cls->num_node - 1; i++)
|
|
for (int32_t j = 0; j < cls->num_root; j++) {
|
|
obj_skin_block_cloth_node* f = &cls->node_array[i * cls->num_root + j];
|
|
f->flags = s.read_uint32_t();
|
|
f->pos.x = s.read_float_t();
|
|
f->pos.y = s.read_float_t();
|
|
f->pos.z = s.read_float_t();
|
|
f->delta_pos.x = s.read_float_t();
|
|
f->delta_pos.y = s.read_float_t();
|
|
f->delta_pos.z = s.read_float_t();
|
|
f->dist_top = s.read_float_t();
|
|
f->dist_bottom = s.read_float_t();
|
|
f->dist_right = s.read_float_t();
|
|
f->dist_left = s.read_float_t();
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (mesh_index_array_offset) {
|
|
s.position_push(mesh_index_array_offset, SEEK_SET);
|
|
cls->num_mesh_index = s.read_uint16_t_reverse_endianness();
|
|
cls->mesh_index_array = alloc->allocate<uint16_t>(cls->num_mesh_index);
|
|
s.read(cls->mesh_index_array, cls->num_mesh_index * sizeof(uint16_t));
|
|
s.position_pop();
|
|
}
|
|
|
|
if (backface_mesh_index_array_offset) {
|
|
s.position_push(backface_mesh_index_array_offset, SEEK_SET);
|
|
cls->num_backface_mesh_index = s.read_uint16_t_reverse_endianness();
|
|
cls->backface_mesh_index_array = alloc->allocate<uint16_t>(cls->num_backface_mesh_index);
|
|
s.read(cls->backface_mesh_index_array, cls->num_backface_mesh_index * sizeof(uint16_t));
|
|
s.position_pop();
|
|
}
|
|
|
|
if (skin_param_offset)
|
|
cls->skin_param = obj_classic_read_skin_param(alloc, s, str);
|
|
return cls;
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_cloth(obj_skin_block_cloth* cls,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
int64_t* mat_array_offset, int64_t* root_array_offset, int64_t* node_array_offset,
|
|
int64_t* mesh_index_array_offset, int64_t* backface_mesh_index_array_offset) {
|
|
int64_t skin_param_offset = 0;
|
|
if (cls->skin_param) {
|
|
skin_param_offset = s.get_position();
|
|
obj_classic_write_skin_param(cls->skin_param, s, strings, string_offsets);
|
|
}
|
|
|
|
int64_t mesh_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, cls->mesh_name);
|
|
int64_t backface_mesh_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, cls->backface_mesh_name);
|
|
|
|
s.write_uint32_t((uint32_t)mesh_name_offset);
|
|
s.write_uint32_t((uint32_t)backface_mesh_name_offset);
|
|
s.write_int32_t(cls->field_8);
|
|
s.write_int32_t(cls->num_root);
|
|
s.write_int32_t(cls->num_node);
|
|
s.write_int32_t(cls->loop);
|
|
s.write_uint32_t((uint32_t)*mat_array_offset);
|
|
s.write_uint32_t((uint32_t)*root_array_offset);
|
|
s.write_uint32_t((uint32_t)*node_array_offset);
|
|
s.write_uint32_t((uint32_t)*mesh_index_array_offset);
|
|
s.write_uint32_t((uint32_t)*backface_mesh_index_array_offset);
|
|
s.write_uint32_t((uint32_t)skin_param_offset);
|
|
s.write_uint32_t(cls->reserved);
|
|
|
|
if (cls->mat_array) {
|
|
s.position_push(*mat_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < cls->num_mat; i++) {
|
|
mat4& mat = cls->mat_array[i];
|
|
s.write_float_t(mat.row0.x);
|
|
s.write_float_t(mat.row1.x);
|
|
s.write_float_t(mat.row2.x);
|
|
s.write_float_t(mat.row3.x);
|
|
s.write_float_t(mat.row0.y);
|
|
s.write_float_t(mat.row1.y);
|
|
s.write_float_t(mat.row2.y);
|
|
s.write_float_t(mat.row3.y);
|
|
s.write_float_t(mat.row0.z);
|
|
s.write_float_t(mat.row1.z);
|
|
s.write_float_t(mat.row2.z);
|
|
s.write_float_t(mat.row3.z);
|
|
s.write_float_t(mat.row0.w);
|
|
s.write_float_t(mat.row1.w);
|
|
s.write_float_t(mat.row2.w);
|
|
s.write_float_t(mat.row3.w);
|
|
*mat_array_offset += sizeof(mat4);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (cls->root_array) {
|
|
s.position_push(*root_array_offset, SEEK_SET);
|
|
int32_t num_root = cls->num_root;
|
|
obj_skin_block_cloth_root* root_array = cls->root_array;
|
|
for (int32_t i = 0; i < num_root; i++)
|
|
obj_classic_write_skin_block_cloth_root(&root_array[i],
|
|
s, strings, string_offsets);
|
|
s.position_pop();
|
|
*root_array_offset += (sizeof(int32_t) + 4 * (sizeof(int32_t) + 3 * sizeof(int32_t)))
|
|
* cls->num_root * (cls->num_node - 1ULL);
|
|
}
|
|
|
|
if (cls->node_array) {
|
|
s.position_push(*node_array_offset, SEEK_SET);
|
|
int32_t num_root = cls->num_root;
|
|
int32_t num_node = cls->num_node;
|
|
obj_skin_block_cloth_node* node_array = cls->node_array;
|
|
for (int32_t i = 0; i < num_node - 1; i++)
|
|
for (int32_t j = 0; j < num_root; j++) {
|
|
obj_skin_block_cloth_node* f = &node_array[i * cls->num_root + j];
|
|
s.write_uint32_t(f->flags);
|
|
s.write_float_t(f->pos.x);
|
|
s.write_float_t(f->pos.y);
|
|
s.write_float_t(f->pos.z);
|
|
s.write_float_t(f->delta_pos.x);
|
|
s.write_float_t(f->delta_pos.y);
|
|
s.write_float_t(f->delta_pos.z);
|
|
s.write_float_t(f->dist_top);
|
|
s.write_float_t(f->dist_bottom);
|
|
s.write_float_t(f->dist_right);
|
|
s.write_float_t(f->dist_left);
|
|
}
|
|
s.position_pop();
|
|
*node_array_offset += (11 * sizeof(int32_t)) * cls->num_root * (cls->num_node - 1ULL);
|
|
}
|
|
|
|
if (cls->mesh_index_array) {
|
|
s.position_push(*mesh_index_array_offset, SEEK_SET);
|
|
s.write_uint16_t((uint16_t)cls->num_mesh_index);
|
|
s.write(cls->mesh_index_array, cls->num_mesh_index * sizeof(uint16_t));
|
|
s.position_pop();
|
|
*mesh_index_array_offset += sizeof(uint16_t) + cls->num_mesh_index * sizeof(uint16_t);
|
|
}
|
|
|
|
if (cls->backface_mesh_index_array) {
|
|
s.position_push(*backface_mesh_index_array_offset, SEEK_SET);
|
|
s.write_uint16_t((uint16_t)cls->num_backface_mesh_index);
|
|
s.write(cls->backface_mesh_index_array, cls->num_backface_mesh_index * sizeof(uint16_t));
|
|
s.position_pop();
|
|
*backface_mesh_index_array_offset += sizeof(uint16_t) + cls->num_backface_mesh_index * sizeof(uint16_t);
|
|
}
|
|
}
|
|
|
|
static void obj_classic_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
cloth_root->pos.x = s.read_float_t();
|
|
cloth_root->pos.y = s.read_float_t();
|
|
cloth_root->pos.z = s.read_float_t();
|
|
cloth_root->normal.x = s.read_float_t();
|
|
cloth_root->normal.y = s.read_float_t();
|
|
cloth_root->normal.z = s.read_float_t();
|
|
cloth_root->field_18 = s.read_float_t();
|
|
cloth_root->field_1C = s.read_int32_t();
|
|
cloth_root->field_20 = s.read_int32_t();
|
|
cloth_root->field_24 = s.read_int32_t();
|
|
|
|
for (uint32_t i = 0; i < 4; i++)
|
|
obj_classic_read_skin_block_cloth_root_bone_weight(&cloth_root->bone_weights[i],
|
|
alloc, s, str);
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets) {
|
|
s.write_float_t(cloth_root->pos.x);
|
|
s.write_float_t(cloth_root->pos.y);
|
|
s.write_float_t(cloth_root->pos.z);
|
|
s.write_float_t(cloth_root->normal.x);
|
|
s.write_float_t(cloth_root->normal.y);
|
|
s.write_float_t(cloth_root->normal.z);
|
|
s.write_float_t(cloth_root->field_18);
|
|
s.write_int32_t(cloth_root->field_1C);
|
|
s.write_int32_t(cloth_root->field_20);
|
|
s.write_int32_t(cloth_root->field_24);
|
|
|
|
for (uint32_t j = 0; j < 4; j++)
|
|
obj_classic_write_skin_block_cloth_root_bone_weight(&cloth_root->bone_weights[j],
|
|
s, strings, string_offsets);
|
|
}
|
|
|
|
static void obj_classic_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
int32_t bone_name_offset = s.read_uint32_t();
|
|
bone_weight->bone_name = obj_read_utf8_string_null_terminated_offset(alloc, s, bone_name_offset);
|
|
bone_weight->weight = s.read_float_t();
|
|
bone_weight->matrix_index = s.read_uint32_t();
|
|
bone_weight->reserved = s.read_uint32_t();
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets) {
|
|
int64_t bone_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, bone_weight->bone_name);
|
|
s.write_uint32_t((uint32_t)bone_name_offset);
|
|
s.write_float_t(bone_weight->weight);
|
|
s.write_uint32_t(bone_weight->matrix_index);
|
|
s.write_uint32_t(bone_weight->reserved);
|
|
}
|
|
|
|
static obj_skin_block_constraint* obj_classic_read_skin_block_constraint(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
obj_skin_block_constraint* cns = alloc->allocate<obj_skin_block_constraint>();
|
|
obj_classic_read_skin_block_node(&cns->node, alloc, s, str);
|
|
|
|
uint32_t type_offset = s.read_uint32_t();
|
|
char* type = s.read_utf8_string_null_terminated_offset(type_offset);
|
|
|
|
uint32_t name_offset = s.read_uint32_t();
|
|
char* name = s.read_utf8_string_null_terminated_offset(name_offset);
|
|
|
|
cns->name_index = 0;
|
|
if (name)
|
|
for (const char** i = str; *i; i++)
|
|
if (!str_utils_compare(name, *i)) {
|
|
cns->name_index = 0x8000 | (int32_t)(i - str);
|
|
break;
|
|
}
|
|
free_def(name);
|
|
|
|
cns->coupling = (obj_skin_block_constraint_coupling)s.read_uint32_t();
|
|
|
|
uint32_t source_node_name_offset = s.read_uint32_t();
|
|
cns->source_node_name = obj_read_utf8_string_null_terminated_offset(alloc, s, source_node_name_offset);
|
|
|
|
if (!type) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_NONE;
|
|
cns->data = 0;
|
|
}
|
|
else if (!str_utils_compare(type, "Orientation")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION;
|
|
|
|
obj_skin_block_constraint_orientation* orientation
|
|
= alloc->allocate<obj_skin_block_constraint_orientation>();
|
|
orientation->offset.x = s.read_float_t();
|
|
orientation->offset.y = s.read_float_t();
|
|
orientation->offset.z = s.read_float_t();
|
|
cns->orientation = orientation;
|
|
}
|
|
else if (!str_utils_compare(type, "Direction")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION;
|
|
|
|
obj_skin_block_constraint_direction* direction
|
|
= alloc->allocate<obj_skin_block_constraint_direction>();
|
|
obj_classic_read_skin_block_constraint_up_vector(&direction->up_vector,
|
|
alloc, s, str);
|
|
direction->align_axis.x = s.read_float_t();
|
|
direction->align_axis.y = s.read_float_t();
|
|
direction->align_axis.z = s.read_float_t();
|
|
direction->target_offset.x = s.read_float_t();
|
|
direction->target_offset.y = s.read_float_t();
|
|
direction->target_offset.z = s.read_float_t();
|
|
cns->direction = direction;
|
|
}
|
|
else if (!str_utils_compare(type, "Position")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_POSITION;
|
|
|
|
obj_skin_block_constraint_position* position
|
|
= alloc->allocate<obj_skin_block_constraint_position>();
|
|
obj_classic_read_skin_block_constraint_up_vector(&position->up_vector,
|
|
alloc, s, str);
|
|
obj_classic_read_skin_block_constraint_attach_point(&position->constrained_object,
|
|
s, str);
|
|
obj_classic_read_skin_block_constraint_attach_point(&position->constraining_object,
|
|
s, str);
|
|
cns->position = position;
|
|
}
|
|
else if (!str_utils_compare(type, "Distance")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE;
|
|
|
|
obj_skin_block_constraint_distance* distance
|
|
= alloc->allocate<obj_skin_block_constraint_distance>();
|
|
obj_classic_read_skin_block_constraint_up_vector(&distance->up_vector,
|
|
alloc, s, str);
|
|
distance->distance = s.read_float_t();
|
|
obj_classic_read_skin_block_constraint_attach_point(&distance->constrained_object,
|
|
s, str);
|
|
obj_classic_read_skin_block_constraint_attach_point(&distance->constraining_object,
|
|
s, str);
|
|
cns->distance = distance;
|
|
}
|
|
else {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_NONE;
|
|
cns->data = 0;
|
|
}
|
|
free_def(type);
|
|
return cns;
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_constraint(obj_skin_block_constraint* cns,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array, int64_t* offsets) {
|
|
obj_classic_write_skin_block_node(&cns->node, s, strings, string_offsets);
|
|
|
|
int64_t type_offset = 0;
|
|
switch (cns->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
type_offset = offsets[0];
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
type_offset = offsets[1];
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
type_offset = offsets[2];
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
type_offset = offsets[3];
|
|
break;
|
|
}
|
|
s.write_uint32_t((uint32_t)type_offset);
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset_by_index(strings,
|
|
string_offsets, bone_name_array, cns->name_index);
|
|
s.write_uint32_t((uint32_t)name_offset);
|
|
|
|
s.write_int32_t(cns->coupling);
|
|
|
|
int64_t source_node_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, cns->source_node_name);
|
|
s.write_uint32_t((uint32_t)source_node_name_offset);
|
|
|
|
switch (cns->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
s.write_float_t(cns->orientation->offset.x);
|
|
s.write_float_t(cns->orientation->offset.y);
|
|
s.write_float_t(cns->orientation->offset.z);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
obj_classic_write_skin_block_constraint_up_vector(&cns->direction->up_vector,
|
|
s, strings, string_offsets);
|
|
s.write_float_t(cns->direction->align_axis.x);
|
|
s.write_float_t(cns->direction->align_axis.y);
|
|
s.write_float_t(cns->direction->align_axis.z);
|
|
s.write_float_t(cns->direction->target_offset.x);
|
|
s.write_float_t(cns->direction->target_offset.y);
|
|
s.write_float_t(cns->direction->target_offset.z);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
obj_classic_write_skin_block_constraint_up_vector(&cns->position->up_vector,
|
|
s, strings, string_offsets);
|
|
obj_classic_write_skin_block_constraint_attach_point(&cns->position->constrained_object,
|
|
s, strings, string_offsets);
|
|
obj_classic_write_skin_block_constraint_attach_point(&cns->position->constraining_object,
|
|
s, strings, string_offsets);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
obj_classic_write_skin_block_constraint_up_vector(&cns->distance->up_vector,
|
|
s, strings, string_offsets);
|
|
s.write_float_t(cns->distance->distance);
|
|
obj_classic_write_skin_block_constraint_attach_point(&cns->distance->constrained_object,
|
|
s, strings, string_offsets);
|
|
obj_classic_write_skin_block_constraint_attach_point(&cns->distance->constraining_object,
|
|
s, strings, string_offsets);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void obj_classic_read_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, const char** str) {
|
|
attach_point->affected_by_orientation = s.read_uint32_t() != 0;
|
|
attach_point->affected_by_scaling = s.read_uint32_t() != 0;
|
|
attach_point->offset.x = s.read_float_t();
|
|
attach_point->offset.y = s.read_float_t();
|
|
attach_point->offset.z = s.read_float_t();
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets) {
|
|
s.write_int32_t(attach_point->affected_by_orientation ? 1 : 0);
|
|
s.write_int32_t(attach_point->affected_by_scaling ? 1 : 0);
|
|
s.write_float_t(attach_point->offset.x);
|
|
s.write_float_t(attach_point->offset.y);
|
|
s.write_float_t(attach_point->offset.z);
|
|
}
|
|
|
|
static void obj_classic_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
up_vector->active = !!s.read_uint32_t();
|
|
up_vector->roll = s.read_float_t();
|
|
up_vector->affected_axis.x = s.read_float_t();
|
|
up_vector->affected_axis.y = s.read_float_t();
|
|
up_vector->affected_axis.z = s.read_float_t();
|
|
up_vector->point_at.x = s.read_float_t();
|
|
up_vector->point_at.y = s.read_float_t();
|
|
up_vector->point_at.z = s.read_float_t();
|
|
|
|
uint32_t name_offset = s.read_uint32_t();
|
|
up_vector->name = obj_read_utf8_string_null_terminated_offset(alloc, s, name_offset);
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets) {
|
|
s.write_int32_t(up_vector->active ? 1 : 0);
|
|
s.write_float_t(up_vector->roll);
|
|
s.write_float_t(up_vector->affected_axis.x);
|
|
s.write_float_t(up_vector->affected_axis.y);
|
|
s.write_float_t(up_vector->affected_axis.z);
|
|
s.write_float_t(up_vector->point_at.x);
|
|
s.write_float_t(up_vector->point_at.y);
|
|
s.write_float_t(up_vector->point_at.z);
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, up_vector->name);
|
|
s.write_uint32_t((uint32_t)name_offset);
|
|
}
|
|
|
|
static obj_skin_block_expression* obj_classic_read_skin_block_expression(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
obj_skin_block_expression* exp = alloc->allocate<obj_skin_block_expression>();
|
|
obj_classic_read_skin_block_node(&exp->node, alloc, s, str);
|
|
|
|
uint32_t name_offset = s.read_uint32_t();
|
|
char* name = s.read_utf8_string_null_terminated_offset(name_offset);
|
|
|
|
exp->name_index = 0;
|
|
if (name)
|
|
for (const char** i = str; *i; i++)
|
|
if (!str_utils_compare(name, *i)) {
|
|
exp->name_index = 0x8000 | (int32_t)(i - str);
|
|
break;
|
|
}
|
|
free_def(name);
|
|
|
|
int32_t num_expression = s.read_int32_t();
|
|
num_expression = min_def(num_expression, 9);
|
|
exp->num_expression = num_expression;
|
|
for (int32_t i = 0; i < num_expression; i++) {
|
|
uint32_t expression_offset = s.read_uint32_t();
|
|
if (expression_offset)
|
|
exp->expression_array[i]
|
|
= obj_read_utf8_string_null_terminated_offset(alloc, s, expression_offset);
|
|
}
|
|
|
|
for (int32_t i = num_expression; i < 9; i++)
|
|
exp->expression_array[i] = 0;
|
|
return exp;
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_expression(obj_skin_block_expression* exp,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array) {
|
|
obj_classic_write_skin_block_node(&exp->node, s, strings, string_offsets);
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset_by_index(strings,
|
|
string_offsets, bone_name_array, exp->name_index);
|
|
s.write_uint32_t((uint32_t)name_offset);
|
|
|
|
int32_t num_expression = exp->num_expression;
|
|
num_expression = min_def(num_expression, 9);
|
|
s.write_int32_t(num_expression);
|
|
for (int32_t i = 0; i < num_expression; i++) {
|
|
int64_t expression_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, exp->expression_array[i]);
|
|
s.write_uint32_t((uint32_t)expression_offset);
|
|
}
|
|
|
|
for (int32_t i = num_expression; i < 9; i++)
|
|
s.write_int32_t(0);
|
|
}
|
|
|
|
static obj_skin_block_motion* obj_classic_read_skin_block_motion(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
obj_skin_block_motion* mot = alloc->allocate<obj_skin_block_motion>();
|
|
obj_classic_read_skin_block_node(&mot->node, alloc, s, str);
|
|
|
|
uint32_t name_offset = s.read_uint32_t();
|
|
mot->num_node = s.read_int32_t();
|
|
uint32_t bone_name_array_offset = s.read_uint32_t();
|
|
uint32_t bone_matrix_array_offset = s.read_uint32_t();
|
|
|
|
char* name = s.read_utf8_string_null_terminated_offset(name_offset);
|
|
|
|
mot->name_index = 0;
|
|
if (name)
|
|
for (const char** i = str; *i; i++)
|
|
if (!str_utils_compare(name, *i)) {
|
|
mot->name_index = 0x8000 | (int32_t)(i - str);
|
|
break;
|
|
}
|
|
free_def(name);
|
|
|
|
mot->node_array = 0;
|
|
|
|
if (!mot->num_node)
|
|
return mot;
|
|
|
|
mot->node_array = alloc->allocate<obj_skin_motion_node>(mot->num_node);
|
|
|
|
if (bone_name_array_offset) {
|
|
s.position_push(bone_name_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++)
|
|
mot->node_array[i].name_index = s.read_uint32_t();
|
|
s.position_pop();
|
|
}
|
|
|
|
if (bone_matrix_array_offset) {
|
|
s.position_push(bone_matrix_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++) {
|
|
mat4& mat = mot->node_array[i].inv_bind_pose_mat;
|
|
mat.row0.x = s.read_float_t();
|
|
mat.row1.x = s.read_float_t();
|
|
mat.row2.x = s.read_float_t();
|
|
mat.row3.x = s.read_float_t();
|
|
mat.row0.y = s.read_float_t();
|
|
mat.row1.y = s.read_float_t();
|
|
mat.row2.y = s.read_float_t();
|
|
mat.row3.y = s.read_float_t();
|
|
mat.row0.z = s.read_float_t();
|
|
mat.row1.z = s.read_float_t();
|
|
mat.row2.z = s.read_float_t();
|
|
mat.row3.z = s.read_float_t();
|
|
mat.row0.w = s.read_float_t();
|
|
mat.row1.w = s.read_float_t();
|
|
mat.row2.w = s.read_float_t();
|
|
mat.row3.w = s.read_float_t();
|
|
}
|
|
s.position_pop();
|
|
}
|
|
return mot;
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_motion(obj_skin_block_motion* mot,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array, int64_t* bone_name_array_offset, int64_t* bone_matrix_array_offset) {
|
|
obj_classic_write_skin_block_node(&mot->node, s, strings, string_offsets);
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset_by_index(strings,
|
|
string_offsets, bone_name_array, mot->name_index);
|
|
s.write_uint32_t((uint32_t)name_offset);
|
|
s.write_int32_t(mot->num_node);
|
|
s.write_uint32_t((uint32_t)*bone_name_array_offset);
|
|
s.write_uint32_t((uint32_t)*bone_matrix_array_offset);
|
|
|
|
if (mot->node_array) {
|
|
s.position_push(*bone_name_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++)
|
|
s.write_uint32_t(mot->node_array[i].name_index);
|
|
s.position_pop();
|
|
*bone_name_array_offset += mot->num_node * sizeof(uint32_t);
|
|
|
|
s.position_push(*bone_matrix_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++) {
|
|
mat4& mat = mot->node_array[i].inv_bind_pose_mat;
|
|
s.write_float_t(mat.row0.x);
|
|
s.write_float_t(mat.row1.x);
|
|
s.write_float_t(mat.row2.x);
|
|
s.write_float_t(mat.row3.x);
|
|
s.write_float_t(mat.row0.y);
|
|
s.write_float_t(mat.row1.y);
|
|
s.write_float_t(mat.row2.y);
|
|
s.write_float_t(mat.row3.y);
|
|
s.write_float_t(mat.row0.z);
|
|
s.write_float_t(mat.row1.z);
|
|
s.write_float_t(mat.row2.z);
|
|
s.write_float_t(mat.row3.z);
|
|
s.write_float_t(mat.row0.w);
|
|
s.write_float_t(mat.row1.w);
|
|
s.write_float_t(mat.row2.w);
|
|
s.write_float_t(mat.row3.w);
|
|
}
|
|
s.position_pop();
|
|
*bone_matrix_array_offset += mot->num_node * sizeof(mat4);
|
|
}
|
|
}
|
|
|
|
static void obj_classic_read_skin_block_node(obj_skin_block_node* node,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
uint32_t parent_name_offset = s.read_uint32_t();
|
|
node->parent_name = obj_read_utf8_string_null_terminated_offset(alloc, s, parent_name_offset);
|
|
|
|
node->position.x = s.read_float_t();
|
|
node->position.y = s.read_float_t();
|
|
node->position.z = s.read_float_t();
|
|
node->rotation.x = s.read_float_t();
|
|
node->rotation.y = s.read_float_t();
|
|
node->rotation.z = s.read_float_t();
|
|
node->scale.x = s.read_float_t();
|
|
node->scale.y = s.read_float_t();
|
|
node->scale.z = s.read_float_t();
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_node(obj_skin_block_node* node,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets) {
|
|
int64_t parent_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, node->parent_name);
|
|
s.write_uint32_t((uint32_t)parent_name_offset);
|
|
|
|
s.write_float_t(node->position.x);
|
|
s.write_float_t(node->position.y);
|
|
s.write_float_t(node->position.z);
|
|
s.write_float_t(node->rotation.x);
|
|
s.write_float_t(node->rotation.y);
|
|
s.write_float_t(node->rotation.z);
|
|
s.write_float_t(node->scale.x);
|
|
s.write_float_t(node->scale.y);
|
|
s.write_float_t(node->scale.z);
|
|
}
|
|
|
|
static obj_skin_block_osage* obj_classic_read_skin_block_osage(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
obj_skin_block_osage* osg = alloc->allocate<obj_skin_block_osage>();
|
|
obj_classic_read_skin_block_node(&osg->node, alloc, s, str);
|
|
|
|
osg->start_index = s.read_uint32_t();
|
|
osg->count = s.read_uint32_t();
|
|
osg->name_index = s.read_uint32_t();
|
|
osg->end_name_index = s.read_uint32_t();
|
|
|
|
uint32_t offset = s.read_uint32_t();
|
|
s.read(0, 0x14);
|
|
|
|
osg->node_array = 0;
|
|
|
|
if (!osg->count || !offset)
|
|
return osg;
|
|
|
|
osg->num_node = osg->count;
|
|
osg->node_array = alloc->allocate<obj_skin_osage_node>(osg->count);
|
|
|
|
s.position_push(offset, SEEK_SET);
|
|
if (!(s.read_uint32_t() & 0x8000))
|
|
osg->skin_param = obj_classic_read_skin_param(alloc, s, str);
|
|
else {
|
|
s.set_position(offset, SEEK_SET);
|
|
for (int32_t i = 0; i < osg->num_node; i++) {
|
|
obj_skin_osage_node* node = &osg->node_array[i];
|
|
node->name_index = s.read_uint32_t();
|
|
node->length = s.read_float_t();
|
|
node->rotation.x = s.read_float_t();
|
|
node->rotation.y = s.read_float_t();
|
|
node->rotation.z = s.read_float_t();
|
|
}
|
|
}
|
|
s.position_pop();
|
|
return osg;
|
|
}
|
|
|
|
static void obj_classic_write_skin_block_osage(obj_skin_block_osage* osg,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, int64_t* node_array_offset) {
|
|
int64_t skin_param_offset = 0;
|
|
if (osg->skin_param) {
|
|
skin_param_offset = s.get_position();
|
|
obj_classic_write_skin_param(osg->skin_param, s, strings, string_offsets);
|
|
}
|
|
|
|
obj_classic_write_skin_block_node(&osg->node, s, strings, string_offsets);
|
|
|
|
s.write_int32_t(osg->start_index);
|
|
s.write_int32_t(osg->count);
|
|
s.write_uint32_t(osg->name_index);
|
|
s.write_uint32_t(osg->end_name_index);
|
|
|
|
if (osg->skin_param)
|
|
s.write_uint32_t((uint32_t)skin_param_offset);
|
|
else
|
|
s.write_uint32_t((uint32_t)*node_array_offset);
|
|
s.write(0x14);
|
|
|
|
if (!osg->skin_param && osg->node_array) {
|
|
s.position_push(*node_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < osg->num_node; i++) {
|
|
obj_skin_osage_node* node = &osg->node_array[i];
|
|
s.write_uint32_t(node->name_index);
|
|
s.write_float_t(node->length);
|
|
s.write_float_t(node->rotation.x);
|
|
s.write_float_t(node->rotation.y);
|
|
s.write_float_t(node->rotation.z);
|
|
}
|
|
s.position_pop();
|
|
*node_array_offset += osg->num_node * (sizeof(uint32_t) + sizeof(float_t) * 4);
|
|
}
|
|
}
|
|
|
|
static obj_skin_skin_param* obj_classic_read_skin_param(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, const char** str) {
|
|
obj_skin_skin_param* skp = alloc->allocate<obj_skin_skin_param>();
|
|
skp->coli = 0;
|
|
skp->coli_count = 0;
|
|
|
|
skp->unk0 = s.read_int32_t();
|
|
skp->force = s.read_float_t();
|
|
skp->force_gain = s.read_float_t();
|
|
skp->air_res = s.read_float_t();
|
|
skp->rot_y = s.read_float_t();
|
|
skp->rot_z = s.read_float_t();
|
|
skp->hinge_y = s.read_float_t();
|
|
skp->hinge_z = s.read_float_t();
|
|
uint32_t name_offset = s.read_uint32_t();
|
|
uint32_t coli_offset = s.read_uint32_t();
|
|
skp->coli_r = s.read_float_t();
|
|
skp->friction = s.read_float_t();
|
|
skp->wind_afc = s.read_float_t();
|
|
skp->unk44 = s.read_int32_t();
|
|
|
|
skp->name = obj_read_utf8_string_null_terminated_offset(alloc, s, name_offset);
|
|
|
|
if (coli_offset) {
|
|
s.set_position(coli_offset, SEEK_SET);
|
|
int32_t coli_count = 0;
|
|
while (s.read_int32_t()) {
|
|
s.read(44);
|
|
coli_count++;
|
|
}
|
|
|
|
skp->coli = alloc->allocate<obj_skin_skin_param_coli>(coli_count);
|
|
skp->coli_count = coli_count;
|
|
|
|
s.set_position(coli_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < skp->coli_count; i++) {
|
|
obj_skin_skin_param_coli* coli = &skp->coli[i];
|
|
coli->type = (obj_skin_skin_param_coli_type)s.read_int32_t();
|
|
coli->bone0_index = s.read_int32_t();
|
|
coli->bone1_index = s.read_int32_t();
|
|
coli->radius = s.read_float_t();
|
|
coli->bone0_pos.x = s.read_float_t();
|
|
coli->bone0_pos.y = s.read_float_t();
|
|
coli->bone0_pos.z = s.read_float_t();
|
|
coli->bone1_pos.x = s.read_float_t();
|
|
coli->bone1_pos.y = s.read_float_t();
|
|
coli->bone1_pos.z = s.read_float_t();
|
|
}
|
|
}
|
|
return skp;
|
|
}
|
|
|
|
static void obj_classic_write_skin_param(obj_skin_skin_param* skp,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets) {
|
|
int64_t coli_offset = 0;
|
|
if (skp->coli) {
|
|
coli_offset = s.get_position();
|
|
for (int32_t i = 0; i < skp->coli_count; i++) {
|
|
obj_skin_skin_param_coli* coli = &skp->coli[i];
|
|
s.write_int32_t(coli->type);
|
|
s.write_int32_t(coli->bone0_index);
|
|
s.write_int32_t(coli->bone1_index);
|
|
s.write_float_t(coli->radius);
|
|
s.write_float_t(coli->bone0_pos.x);
|
|
s.write_float_t(coli->bone0_pos.y);
|
|
s.write_float_t(coli->bone0_pos.z);
|
|
s.write_float_t(coli->bone1_pos.x);
|
|
s.write_float_t(coli->bone1_pos.y);
|
|
s.write_float_t(coli->bone1_pos.z);
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, skp->name);
|
|
|
|
s.write_int32_t(skp->unk0);
|
|
s.write_float_t(skp->force);
|
|
s.write_float_t(skp->force_gain);
|
|
s.write_float_t(skp->air_res);
|
|
s.write_float_t(skp->rot_y);
|
|
s.write_float_t(skp->rot_z);
|
|
s.write_float_t(skp->hinge_y);
|
|
s.write_float_t(skp->hinge_z);
|
|
s.write_uint32_t((uint32_t)name_offset);
|
|
s.write_uint32_t((uint32_t)coli_offset);
|
|
s.write_float_t(skp->coli_r);
|
|
s.write_float_t(skp->friction);
|
|
s.write_float_t(skp->wind_afc);
|
|
s.write_int32_t(skp->unk44);
|
|
s.write(0x08);
|
|
}
|
|
|
|
static void obj_classic_read_vertex(obj_mesh* mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t* vertex, int64_t base_offset, int32_t num_vertex,
|
|
obj_vertex_format_file vertex_format_file) {
|
|
obj_vertex_format vertex_format = OBJ_VERTEX_NONE;
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_POSITION)
|
|
enum_or(vertex_format, OBJ_VERTEX_POSITION);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_NORMAL)
|
|
enum_or(vertex_format, OBJ_VERTEX_NORMAL);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_TANGENT)
|
|
enum_or(vertex_format, OBJ_VERTEX_TANGENT);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_BINORMAL)
|
|
enum_or(vertex_format, OBJ_VERTEX_BINORMAL);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD0)
|
|
enum_or(vertex_format, OBJ_VERTEX_TEXCOORD0);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD1)
|
|
enum_or(vertex_format, OBJ_VERTEX_TEXCOORD1);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD2)
|
|
enum_or(vertex_format, OBJ_VERTEX_TEXCOORD2);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD3)
|
|
enum_or(vertex_format, OBJ_VERTEX_TEXCOORD3);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_COLOR0)
|
|
enum_or(vertex_format, OBJ_VERTEX_COLOR0);
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_COLOR1)
|
|
enum_or(vertex_format, OBJ_VERTEX_COLOR1);
|
|
|
|
if ((vertex_format_file & (OBJ_VERTEX_FILE_BONE_WEIGHT | OBJ_VERTEX_FILE_BONE_INDEX))
|
|
== (OBJ_VERTEX_FILE_BONE_WEIGHT | OBJ_VERTEX_FILE_BONE_INDEX))
|
|
enum_or(vertex_format, OBJ_VERTEX_BONE_DATA);
|
|
else
|
|
enum_and(vertex_format_file, ~(OBJ_VERTEX_FILE_BONE_WEIGHT | OBJ_VERTEX_FILE_BONE_INDEX));
|
|
|
|
if (vertex_format_file & OBJ_VERTEX_FILE_UNKNOWN)
|
|
enum_or(vertex_format, OBJ_VERTEX_UNKNOWN);
|
|
|
|
obj_vertex_data* vtx = alloc->allocate<obj_vertex_data>(num_vertex);
|
|
for (uint32_t i = 0; i < 20; i++) {
|
|
obj_vertex_format_file attribute = (obj_vertex_format_file)(1 << i);
|
|
if (!(vertex_format_file & attribute))
|
|
continue;
|
|
|
|
s.set_position(base_offset + vertex[i], SEEK_SET);
|
|
switch (attribute) {
|
|
case OBJ_VERTEX_FILE_POSITION:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].position.x = s.read_float_t();
|
|
vtx[j].position.y = s.read_float_t();
|
|
vtx[j].position.z = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_NORMAL:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].normal.x = s.read_float_t();
|
|
vtx[j].normal.y = s.read_float_t();
|
|
vtx[j].normal.z = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_TANGENT:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].tangent.x = s.read_float_t();
|
|
vtx[j].tangent.y = s.read_float_t();
|
|
vtx[j].tangent.z = s.read_float_t();
|
|
vtx[j].tangent.w = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_BINORMAL:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].binormal.x = s.read_float_t();
|
|
vtx[j].binormal.y = s.read_float_t();
|
|
vtx[j].binormal.z = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_TEXCOORD0:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].texcoord0.x = s.read_float_t();
|
|
vtx[j].texcoord0.y = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_TEXCOORD1:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].texcoord1.x = s.read_float_t();
|
|
vtx[j].texcoord1.y = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_TEXCOORD2:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].texcoord2.x = s.read_float_t();
|
|
vtx[j].texcoord2.y = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_TEXCOORD3:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].texcoord3.x = s.read_float_t();
|
|
vtx[j].texcoord3.y = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_COLOR0:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].color0.x = s.read_float_t();
|
|
vtx[j].color0.y = s.read_float_t();
|
|
vtx[j].color0.z = s.read_float_t();
|
|
vtx[j].color0.w = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_COLOR1:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].color1.x = s.read_float_t();
|
|
vtx[j].color1.y = s.read_float_t();
|
|
vtx[j].color1.z = s.read_float_t();
|
|
vtx[j].color1.w = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_BONE_WEIGHT:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].bone_weight.x = s.read_float_t();
|
|
vtx[j].bone_weight.y = s.read_float_t();
|
|
vtx[j].bone_weight.z = s.read_float_t();
|
|
vtx[j].bone_weight.w = s.read_float_t();
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_BONE_INDEX:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
int32_t bone_index_x = (int32_t)s.read_float_t();
|
|
int32_t bone_index_y = (int32_t)s.read_float_t();
|
|
int32_t bone_index_z = (int32_t)s.read_float_t();
|
|
int32_t bone_index_w = (int32_t)s.read_float_t();
|
|
vtx[j].bone_index.x = (int16_t)(bone_index_x >= 0 ? bone_index_x / 3 : -1);
|
|
vtx[j].bone_index.y = (int16_t)(bone_index_y >= 0 ? bone_index_y / 3 : -1);
|
|
vtx[j].bone_index.z = (int16_t)(bone_index_z >= 0 ? bone_index_z / 3 : -1);
|
|
vtx[j].bone_index.w = (int16_t)(bone_index_w >= 0 ? bone_index_w / 3 : -1);
|
|
}
|
|
break;
|
|
case OBJ_VERTEX_FILE_UNKNOWN:
|
|
for (int32_t j = 0; j < num_vertex; j++) {
|
|
vtx[j].unknown.x = s.read_float_t();
|
|
vtx[j].unknown.y = s.read_float_t();
|
|
vtx[j].unknown.z = s.read_float_t();
|
|
vtx[j].unknown.w = s.read_float_t();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vertex_format_file & (OBJ_VERTEX_FILE_BONE_WEIGHT | OBJ_VERTEX_FILE_BONE_INDEX))
|
|
for (int32_t i = 0; i < num_vertex; i++)
|
|
obj_vertex_validate_bone_data(vtx[i].bone_weight, vtx[i].bone_index);
|
|
|
|
mesh->vertex_array = vtx;
|
|
mesh->num_vertex = num_vertex;
|
|
mesh->vertex_format = vertex_format;
|
|
mesh->attrib.m.compression = 0;
|
|
}
|
|
|
|
static void obj_classic_write_vertex(obj_mesh* mesh,
|
|
stream& s, int64_t* vertex, int64_t base_offset, int32_t* num_vertex,
|
|
obj_vertex_format_file* vertex_format_file, int32_t* size_vertex) {
|
|
obj_vertex_data* vtx = mesh->vertex_array;
|
|
int32_t _num_vertex = mesh->num_vertex;
|
|
obj_vertex_format vertex_format = mesh->vertex_format;
|
|
|
|
obj_vertex_format_file _vertex_format_file = (obj_vertex_format_file)0;
|
|
int32_t _size_vertex = 0;
|
|
if (vertex_format & OBJ_VERTEX_POSITION) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_POSITION);
|
|
_size_vertex += 0x0C;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_NORMAL) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_NORMAL);
|
|
_size_vertex += 0x0C;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_TANGENT) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_TANGENT);
|
|
_size_vertex += 0x10;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_BINORMAL) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_BINORMAL);
|
|
_size_vertex += 0x0C;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_TEXCOORD0) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_TEXCOORD0);
|
|
_size_vertex += 0x08;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_TEXCOORD1) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_TEXCOORD1);
|
|
_size_vertex += 0x08;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_TEXCOORD2) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_TEXCOORD2);
|
|
_size_vertex += 0x08;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_TEXCOORD3) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_TEXCOORD3);
|
|
_size_vertex += 0x08;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_COLOR0) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_COLOR0);
|
|
_size_vertex += 0x10;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_COLOR1) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_COLOR1);
|
|
_size_vertex += 0x10;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_BONE_DATA) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_BONE_WEIGHT | OBJ_VERTEX_FILE_BONE_INDEX);
|
|
_size_vertex += 0x20;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_UNKNOWN) {
|
|
enum_or(_vertex_format_file, OBJ_VERTEX_FILE_UNKNOWN);
|
|
_size_vertex += 0x10;
|
|
}
|
|
*num_vertex = _num_vertex;
|
|
*vertex_format_file = _vertex_format_file;
|
|
*size_vertex = _size_vertex;
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_POSITION) {
|
|
vertex[0] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].position.x);
|
|
s.write_float_t(vtx[i].position.y);
|
|
s.write_float_t(vtx[i].position.z);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_NORMAL) {
|
|
vertex[1] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].normal.x);
|
|
s.write_float_t(vtx[i].normal.y);
|
|
s.write_float_t(vtx[i].normal.z);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_TANGENT) {
|
|
vertex[2] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].tangent.x);
|
|
s.write_float_t(vtx[i].tangent.y);
|
|
s.write_float_t(vtx[i].tangent.z);
|
|
s.write_float_t(vtx[i].tangent.w);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_BINORMAL) {
|
|
vertex[3] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].binormal.x);
|
|
s.write_float_t(vtx[i].binormal.y);
|
|
s.write_float_t(vtx[i].binormal.z);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD0) {
|
|
vertex[4] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].texcoord0.x);
|
|
s.write_float_t(vtx[i].texcoord0.y);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_BONE_WEIGHT) {
|
|
vertex[10] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].bone_weight.x);
|
|
s.write_float_t(vtx[i].bone_weight.y);
|
|
s.write_float_t(vtx[i].bone_weight.z);
|
|
s.write_float_t(vtx[i].bone_weight.w);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_BONE_INDEX) {
|
|
vertex[11] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].bone_index.x >= 0 ? (float_t)(vtx[i].bone_index.x * 3) : -1.0f);
|
|
s.write_float_t(vtx[i].bone_index.y >= 0 ? (float_t)(vtx[i].bone_index.y * 3) : -1.0f);
|
|
s.write_float_t(vtx[i].bone_index.z >= 0 ? (float_t)(vtx[i].bone_index.z * 3) : -1.0f);
|
|
s.write_float_t(vtx[i].bone_index.w >= 0 ? (float_t)(vtx[i].bone_index.w * 3) : -1.0f);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD1) {
|
|
vertex[5] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].texcoord1.x);
|
|
s.write_float_t(vtx[i].texcoord1.y);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD2) {
|
|
vertex[6] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].texcoord2.x);
|
|
s.write_float_t(vtx[i].texcoord2.y);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_TEXCOORD3) {
|
|
vertex[7] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].texcoord3.x);
|
|
s.write_float_t(vtx[i].texcoord3.y);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_COLOR0) {
|
|
vertex[8] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].color0.x);
|
|
s.write_float_t(vtx[i].color0.y);
|
|
s.write_float_t(vtx[i].color0.z);
|
|
s.write_float_t(vtx[i].color0.w);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_COLOR1) {
|
|
vertex[9] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].color1.x);
|
|
s.write_float_t(vtx[i].color1.y);
|
|
s.write_float_t(vtx[i].color1.z);
|
|
s.write_float_t(vtx[i].color1.w);
|
|
}
|
|
}
|
|
|
|
if (_vertex_format_file & OBJ_VERTEX_FILE_UNKNOWN) {
|
|
vertex[12] = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
s.write_float_t(vtx[i].unknown.x);
|
|
s.write_float_t(vtx[i].unknown.y);
|
|
s.write_float_t(vtx[i].unknown.z);
|
|
s.write_float_t(vtx[i].unknown.w);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void obj_set_modern_read_inner(obj_set* set, prj::shared_ptr<prj::stack_allocator>& alloc, stream& s) {
|
|
f2_struct st;
|
|
st.read(s);
|
|
if (st.header.signature != reverse_endianness_uint32_t('MOSD') || !st.data.size())
|
|
return;
|
|
|
|
uint32_t header_length = st.header.get_length();
|
|
bool big_endian = st.header.attrib.get_big_endian();
|
|
bool is_x = st.pof.shift_x;
|
|
|
|
memory_stream s_mosd;
|
|
s_mosd.open(st.data);
|
|
s_mosd.big_endian = big_endian;
|
|
|
|
uint32_t version = s_mosd.read_uint32_t_reverse_endianness();
|
|
if (version != 0x05062501) {
|
|
set->ready = false;
|
|
set->modern = false;
|
|
set->big_endian = false;
|
|
set->is_x = false;
|
|
return;
|
|
}
|
|
|
|
s_mosd.set_position(0x04, SEEK_SET);
|
|
obj_set_header osh = {};
|
|
if (!is_x) {
|
|
set->obj_num = s_mosd.read_int32_t_reverse_endianness();
|
|
osh.last_obj_id = s_mosd.read_uint32_t_reverse_endianness();
|
|
osh.obj_data = s_mosd.read_offset_f2(header_length);
|
|
osh.obj_skin_data = s_mosd.read_offset_f2(header_length);
|
|
osh.obj_name_data = s_mosd.read_offset_f2(header_length);
|
|
osh.obj_id_data = s_mosd.read_offset_f2(header_length);
|
|
osh.tex_id_data = s_mosd.read_offset_f2(header_length);
|
|
set->tex_id_num = s_mosd.read_int32_t_reverse_endianness();
|
|
set->reserved[0] = s_mosd.read_uint32_t_reverse_endianness();
|
|
set->reserved[1] = s_mosd.read_uint32_t_reverse_endianness();
|
|
}
|
|
else {
|
|
set->obj_num = s_mosd.read_int32_t_reverse_endianness();
|
|
osh.last_obj_id = s_mosd.read_uint32_t_reverse_endianness();
|
|
osh.obj_data = s_mosd.read_offset_x();
|
|
osh.obj_skin_data = s_mosd.read_offset_x();
|
|
osh.obj_name_data = s_mosd.read_offset_x();
|
|
osh.obj_id_data = s_mosd.read_offset_x();
|
|
osh.tex_id_data = s_mosd.read_offset_x();
|
|
set->tex_id_num = s_mosd.read_int32_t_reverse_endianness();
|
|
set->reserved[0] = s_mosd.read_uint32_t_reverse_endianness();
|
|
set->reserved[1] = s_mosd.read_uint32_t_reverse_endianness();
|
|
}
|
|
|
|
int32_t obj_num = set->obj_num;
|
|
set->obj_data = alloc->allocate<obj*>(obj_num);
|
|
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
set->obj_data[i] = alloc->allocate<::obj>();
|
|
|
|
int64_t* data = force_malloc<int64_t>(obj_num * 3ULL);
|
|
|
|
int64_t* obj_data = 0;
|
|
if (osh.obj_data) {
|
|
obj_data = data;
|
|
s_mosd.set_position(osh.obj_data, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_data[i] = s_mosd.read_offset_f2(header_length);
|
|
else
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_data[i] = s_mosd.read_offset_x();
|
|
}
|
|
|
|
int64_t* obj_skin_data = 0;
|
|
if (osh.obj_skin_data) {
|
|
obj_skin_data = data + obj_num;
|
|
s_mosd.set_position(osh.obj_skin_data, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_skin_data[i] = s_mosd.read_offset_f2(header_length);
|
|
else
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_skin_data[i] = s_mosd.read_offset_x();
|
|
}
|
|
|
|
int64_t* obj_name_data = 0;
|
|
if (osh.obj_name_data) {
|
|
obj_name_data = data + obj_num * 2ULL;
|
|
s_mosd.set_position(osh.obj_name_data, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_name_data[i] = s_mosd.read_offset_f2(header_length);
|
|
else
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
obj_name_data[i] = s_mosd.read_offset_x();
|
|
}
|
|
|
|
if (osh.obj_data)
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj* obj = set->obj_data[i];
|
|
if (osh.obj_name_data && obj_name_data[i]) {
|
|
obj->name = obj_read_utf8_string_null_terminated_offset(alloc, s_mosd, obj_name_data[i]);
|
|
obj->hash = hash_utf8_murmurhash(obj->name);
|
|
}
|
|
else
|
|
obj->hash = hash_murmurhash_empty;
|
|
}
|
|
|
|
if (osh.obj_id_data) {
|
|
s_mosd.set_position(osh.obj_id_data, SEEK_SET);
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
set->obj_data[i]->id = s_mosd.read_uint32_t_reverse_endianness();
|
|
}
|
|
|
|
free_def(data);
|
|
|
|
if (osh.tex_id_data) {
|
|
s_mosd.set_position(osh.tex_id_data, SEEK_SET);
|
|
int32_t tex_id_num = set->tex_id_num;
|
|
uint32_t* tex_id_data = alloc->allocate<uint32_t>(tex_id_num);
|
|
set->tex_id_data = tex_id_data;
|
|
for (int32_t i = 0; i < tex_id_num; i++)
|
|
tex_id_data[i] = s_mosd.read_uint32_t_reverse_endianness();
|
|
}
|
|
|
|
int32_t omdl_index = 0;
|
|
for (f2_struct& i : st.sub_structs) {
|
|
if (i.header.signature != reverse_endianness_uint32_t('OMDL'))
|
|
continue;
|
|
|
|
f2_struct* oskn = 0;
|
|
f2_struct* oidx = 0;
|
|
f2_struct* ovtx = 0;
|
|
for (f2_struct& j : i.sub_structs)
|
|
if (!oskn && j.header.signature == reverse_endianness_uint32_t('OSKN'))
|
|
oskn = &j;
|
|
else if (!oidx && j.header.signature == reverse_endianness_uint32_t('OIDX'))
|
|
oidx = &j;
|
|
else if (!ovtx && j.header.signature == reverse_endianness_uint32_t('OVTX'))
|
|
ovtx = &j;
|
|
|
|
memory_stream s_oskn;
|
|
memory_stream s_oidx;
|
|
memory_stream s_ovtx;
|
|
stream* s_oskn_ptr = 0;
|
|
stream* s_oidx_ptr = 0;
|
|
stream* s_ovtx_ptr = 0;
|
|
if (oskn) {
|
|
s_oskn.open(oskn->data);
|
|
s_oskn.big_endian = oskn->header.attrib.get_big_endian();
|
|
s_oskn_ptr = &s_oskn;
|
|
}
|
|
|
|
if (oidx) {
|
|
s_oidx.open(oidx->data);
|
|
s_oidx.big_endian = oidx->header.attrib.get_big_endian();
|
|
s_oidx_ptr = &s_oidx;
|
|
}
|
|
|
|
if (ovtx) {
|
|
s_ovtx.open(ovtx->data);
|
|
s_ovtx.big_endian = ovtx->header.attrib.get_big_endian();
|
|
s_ovtx_ptr = &s_ovtx;
|
|
}
|
|
|
|
obj* obj = set->obj_data[omdl_index];
|
|
memory_stream s_omdl;
|
|
s_omdl.open(i.data);
|
|
s_omdl.big_endian = i.header.attrib.get_big_endian();
|
|
obj_modern_read_model(obj, alloc,
|
|
s_omdl, 0, i.header.get_length(), is_x, s_oidx_ptr, s_ovtx_ptr);
|
|
s_omdl.close();
|
|
|
|
if (s_oskn_ptr)
|
|
obj->skin = obj_modern_read_skin(alloc,
|
|
*s_oskn_ptr, 0, oskn->header.get_length(), is_x);
|
|
omdl_index++;
|
|
}
|
|
|
|
set->ready = true;
|
|
set->modern = true;
|
|
set->big_endian = big_endian;
|
|
set->is_x = is_x;
|
|
}
|
|
|
|
static void obj_set_modern_write_inner(obj_set* set, stream& s) {
|
|
bool big_endian = s.big_endian;
|
|
|
|
memory_stream s_mosd;
|
|
s_mosd.open();
|
|
s_mosd.big_endian = big_endian;
|
|
|
|
uint32_t off;
|
|
enrs e;
|
|
enrs_entry ee;
|
|
pof pof;
|
|
|
|
bool is_x = set->is_x;
|
|
|
|
obj_set_header osh = {};
|
|
osh.last_obj_id = -1;
|
|
|
|
int32_t obj_num = set->obj_num;
|
|
|
|
if (!is_x) {
|
|
ee = { 0, 1, 44, 1 };
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = 44;
|
|
}
|
|
else {
|
|
ee = { 0, 3, 72, 1 };
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
ee.append(4, 5, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = 72;
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (!is_x)
|
|
off += (uint32_t)(obj_num * 4ULL);
|
|
else
|
|
off += (uint32_t)(obj_num * 8ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
if (!is_x)
|
|
off += (uint32_t)(obj_num * 4ULL);
|
|
else
|
|
off += (uint32_t)(obj_num * 8ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
if (!is_x) {
|
|
ee = { off, 1, 4, (uint32_t)obj_num };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(obj_num * 4ULL);
|
|
}
|
|
else {
|
|
ee = { off, 1, 8, (uint32_t)obj_num };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(obj_num * 8ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 4, (uint32_t)obj_num };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(obj_num * 4ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 4, (uint32_t)set->tex_id_num };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(set->tex_id_num * 4ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
if (!is_x) {
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
io_write_offset_f2_pof_add(s_mosd, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s_mosd, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s_mosd, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s_mosd, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s_mosd, 0, 0x20, &pof);
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
}
|
|
else {
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
io_write_offset_x_pof_add(s_mosd, 0, &pof);
|
|
io_write_offset_x_pof_add(s_mosd, 0, &pof);
|
|
io_write_offset_x_pof_add(s_mosd, 0, &pof);
|
|
io_write_offset_x_pof_add(s_mosd, 0, &pof);
|
|
io_write_offset_x_pof_add(s_mosd, 0, &pof);
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.write_int32_t(0);
|
|
s_mosd.align_write(0x10);
|
|
}
|
|
|
|
osh.obj_data = s_mosd.get_position();
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s_mosd.write_offset_f2(0, 0x20);
|
|
else
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s_mosd.write_offset_x(0);
|
|
s_mosd.align_write(0x10);
|
|
|
|
osh.obj_skin_data = s_mosd.get_position();
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s_mosd.write_offset_f2(0, 0x20);
|
|
else
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s_mosd.write_offset_x(0);
|
|
s_mosd.align_write(0x10);
|
|
|
|
osh.obj_name_data = s_mosd.get_position();
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
io_write_offset_f2_pof_add(s_mosd, 0, 0x20, &pof);
|
|
else
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
io_write_offset_x_pof_add(s_mosd, 0, &pof);
|
|
s_mosd.align_write(0x10);
|
|
|
|
osh.obj_id_data = s_mosd.get_position();
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s_mosd.write_uint32_t_reverse_endianness(set->obj_data[i]->id);
|
|
s_mosd.align_write(0x10);
|
|
|
|
osh.tex_id_data = s_mosd.get_position();
|
|
for (int32_t i = 0; i < set->tex_id_num; i++)
|
|
s_mosd.write_uint32_t_reverse_endianness(set->tex_id_data[i]);
|
|
s_mosd.align_write(0x10);
|
|
|
|
int64_t* obj_name_data = force_malloc<int64_t>(obj_num);
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj_name_data[i] = (int32_t)s_mosd.get_position();
|
|
s_mosd.write_utf8_string_null_terminated(set->obj_data[i]->name);
|
|
}
|
|
s_mosd.align_write(0x10);
|
|
|
|
s_mosd.position_push(osh.obj_name_data, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s_mosd.write_offset_f2(obj_name_data[i], 0x20);
|
|
else
|
|
for (int32_t i = 0; i < obj_num; i++)
|
|
s_mosd.write_offset_x(obj_name_data[i]);
|
|
s_mosd.position_pop();
|
|
free_def(obj_name_data);
|
|
|
|
s_mosd.position_push(0x00, SEEK_SET);
|
|
if (!is_x) {
|
|
s_mosd.write_uint32_t_reverse_endianness(0x05062501);
|
|
s_mosd.write_int32_t_reverse_endianness(set->obj_num);
|
|
s_mosd.write_int32_t_reverse_endianness(-1);
|
|
s_mosd.write_offset_f2(osh.obj_data, 0x20);
|
|
s_mosd.write_offset_f2(osh.obj_skin_data, 0x20);
|
|
s_mosd.write_offset_f2(osh.obj_name_data, 0x20);
|
|
s_mosd.write_offset_f2(osh.obj_id_data, 0x20);
|
|
s_mosd.write_offset_f2(osh.tex_id_data, 0x20);
|
|
s_mosd.write_int32_t_reverse_endianness(set->tex_id_num);
|
|
s_mosd.write_uint32_t_reverse_endianness(set->reserved[0]);
|
|
s_mosd.write_uint32_t_reverse_endianness(set->reserved[1]);
|
|
}
|
|
else {
|
|
s_mosd.write_uint32_t_reverse_endianness(0x05062501);
|
|
s_mosd.write_int32_t_reverse_endianness(set->obj_num);
|
|
s_mosd.write_int32_t_reverse_endianness(-1);
|
|
s_mosd.write_offset_x(osh.obj_data);
|
|
s_mosd.write_offset_x(osh.obj_skin_data);
|
|
s_mosd.write_offset_x(osh.obj_name_data);
|
|
s_mosd.write_offset_x(osh.obj_id_data);
|
|
s_mosd.write_offset_x(osh.tex_id_data);
|
|
s_mosd.write_int32_t_reverse_endianness(set->tex_id_num);
|
|
s_mosd.write_uint32_t_reverse_endianness(set->reserved[0]);
|
|
s_mosd.write_uint32_t_reverse_endianness(set->reserved[1]);
|
|
}
|
|
s_mosd.position_pop();
|
|
|
|
f2_struct st;
|
|
for (int32_t i = 0; i < obj_num; i++) {
|
|
obj* obj = set->obj_data[i];
|
|
|
|
st.sub_structs.push_back({});
|
|
f2_struct* omdl = &st.sub_structs.back();
|
|
|
|
memory_stream s_omdl;
|
|
s_omdl.open();
|
|
s_omdl.big_endian = big_endian;
|
|
|
|
if (obj->skin) {
|
|
omdl->sub_structs.push_back({});
|
|
f2_struct* oskn = &omdl->sub_structs.back();
|
|
|
|
memory_stream s_oskn;
|
|
s_oskn.open();
|
|
s_oskn.big_endian = big_endian;
|
|
|
|
obj_modern_write_skin(obj->skin, s_oskn, 0, is_x, oskn);
|
|
|
|
s_oskn.align_write(0x10);
|
|
s_oskn.copy(oskn->data);
|
|
s_oskn.close();
|
|
|
|
new (&oskn->header) f2_header('OSKN');
|
|
omdl->header.attrib.set_big_endian(big_endian);
|
|
}
|
|
|
|
obj_modern_write_model(obj, s_omdl, 0, is_x, omdl);
|
|
|
|
s_omdl.align_write(0x10);
|
|
s_omdl.copy(omdl->data);
|
|
s_omdl.close();
|
|
|
|
new (&omdl->header) f2_header('OMDL');
|
|
omdl->header.attrib.set_big_endian(big_endian);
|
|
}
|
|
|
|
s_mosd.align_write(0x10);
|
|
s_mosd.copy(st.data);
|
|
s_mosd.close();
|
|
|
|
st.enrs = e;
|
|
st.pof = pof;
|
|
|
|
new (&st.header) f2_header('MOSD');
|
|
st.header.attrib.set_big_endian(big_endian);
|
|
|
|
st.write(s, true, set->is_x);
|
|
}
|
|
|
|
static void obj_modern_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr<prj::stack_allocator> alloc, stream& s) {
|
|
bool tri_strip = sub_mesh->primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP;
|
|
uint32_t* index_array = alloc->allocate<uint32_t>(sub_mesh->num_index);
|
|
int32_t num_index = sub_mesh->num_index;
|
|
switch (sub_mesh->index_format) {
|
|
case OBJ_INDEX_U8:
|
|
for (int32_t i = 0; i < num_index; i++) {
|
|
uint8_t idx = s.read_uint8_t();
|
|
index_array[i] = tri_strip && idx == 0xFF ? 0xFFFFFFFF : idx;
|
|
}
|
|
break;
|
|
case OBJ_INDEX_U16:
|
|
for (int32_t i = 0; i < num_index; i++) {
|
|
uint16_t idx = s.read_uint16_t_reverse_endianness();
|
|
index_array[i] = tri_strip && idx == 0xFFFF ? 0xFFFFFFFF : idx;
|
|
}
|
|
break;
|
|
case OBJ_INDEX_U32:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
index_array[i] = s.read_uint32_t_reverse_endianness();
|
|
break;
|
|
}
|
|
sub_mesh->index_array = index_array;
|
|
}
|
|
|
|
static void obj_modern_write_index(obj_sub_mesh* sub_mesh,
|
|
stream& s, bool is_x, f2_struct* oidx) {
|
|
int32_t size = 1;
|
|
enrs_type type = ENRS_INVALID;
|
|
switch (sub_mesh->index_format) {
|
|
case OBJ_INDEX_U16:
|
|
size = 2;
|
|
type = ENRS_WORD;
|
|
break;
|
|
case OBJ_INDEX_U32:
|
|
size = 4;
|
|
type = ENRS_DWORD;
|
|
break;
|
|
}
|
|
|
|
uint32_t off = 0;
|
|
enrs* e = &oidx->enrs;
|
|
enrs_entry ee;
|
|
bool add_enrs = true;
|
|
if (e->vec.size() > 0) {
|
|
off = (uint32_t)((size_t)e->vec.back().size * e->vec.back().repeat_count);
|
|
if (e->vec.back().count && e->vec.back().sub.front().type == type) {
|
|
e->vec.back().repeat_count += sub_mesh->num_index;
|
|
add_enrs = false;
|
|
}
|
|
}
|
|
|
|
if (add_enrs)
|
|
if (type != ENRS_INVALID) {
|
|
ee = { off, 1, (uint32_t)size, (uint32_t)sub_mesh->num_index };
|
|
ee.append(0, 1, type);
|
|
e->vec.push_back(ee);
|
|
}
|
|
else {
|
|
ee = { off, 0, (uint32_t)size, (uint32_t)sub_mesh->num_index };
|
|
e->vec.push_back(ee);
|
|
}
|
|
|
|
uint32_t* index_array = sub_mesh->index_array;
|
|
int32_t num_index = sub_mesh->num_index;
|
|
switch (sub_mesh->index_format) {
|
|
case OBJ_INDEX_U8:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
s.write_uint8_t((uint8_t)index_array[i]);
|
|
break;
|
|
case OBJ_INDEX_U16:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
s.write_uint16_t_reverse_endianness((uint16_t)index_array[i]);
|
|
break;
|
|
case OBJ_INDEX_U32:
|
|
for (int32_t i = 0; i < num_index; i++)
|
|
s.write_uint32_t_reverse_endianness(index_array[i]);
|
|
break;
|
|
}
|
|
s.align_write(0x04);
|
|
}
|
|
|
|
static void obj_modern_read_model(obj* obj, prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset,
|
|
uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx) {
|
|
const size_t mesh_size = is_x ? 0x130 : 0xD8;
|
|
|
|
s.set_position(base_offset, SEEK_SET);
|
|
|
|
obj_header oh = {};
|
|
if (!is_x) {
|
|
s.read_uint32_t(); // version
|
|
s.read_uint32_t(); // flags
|
|
obj->bounding_sphere.center.x = s.read_float_t_reverse_endianness();
|
|
obj->bounding_sphere.center.y = s.read_float_t_reverse_endianness();
|
|
obj->bounding_sphere.center.z = s.read_float_t_reverse_endianness();
|
|
obj->bounding_sphere.radius = s.read_float_t_reverse_endianness();
|
|
obj->num_mesh = s.read_int32_t_reverse_endianness();
|
|
oh.mesh_array = s.read_offset_f2(header_length);
|
|
obj->num_material = s.read_int32_t_reverse_endianness();
|
|
oh.material_array = s.read_offset_f2(header_length);
|
|
obj->reserved[0] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[1] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[2] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[3] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[4] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[5] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[6] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[7] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[8] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[9] = s.read_uint32_t_reverse_endianness();
|
|
}
|
|
else {
|
|
s.read_uint32_t(); // version
|
|
s.read_uint32_t(); // flags
|
|
obj->num_mesh = s.read_int32_t_reverse_endianness();
|
|
obj->num_material = s.read_int32_t_reverse_endianness();
|
|
obj->bounding_sphere.center.x = s.read_float_t_reverse_endianness();
|
|
obj->bounding_sphere.center.y = s.read_float_t_reverse_endianness();
|
|
obj->bounding_sphere.center.z = s.read_float_t_reverse_endianness();
|
|
obj->bounding_sphere.radius = s.read_float_t_reverse_endianness();
|
|
oh.mesh_array = s.read_offset_x();
|
|
oh.material_array = s.read_offset_x();
|
|
s.read(0, 0x10);
|
|
obj->flags = s.read_uint8_t();
|
|
s.read(0, 0x07);
|
|
obj->reserved[0] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[1] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[2] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[3] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[4] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[5] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[6] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[7] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[8] = s.read_uint32_t_reverse_endianness();
|
|
obj->reserved[9] = s.read_uint32_t_reverse_endianness();
|
|
}
|
|
|
|
if (oh.mesh_array > 0) {
|
|
int32_t num_mesh = obj->num_mesh;
|
|
obj->mesh_array = alloc->allocate<obj_mesh>(num_mesh);
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
s.set_position(base_offset + oh.mesh_array + mesh_size * i, SEEK_SET);
|
|
obj_modern_read_model_mesh(&obj->mesh_array[i],
|
|
alloc, s, base_offset,
|
|
header_length, is_x, s_oidx, s_ovtx);
|
|
}
|
|
}
|
|
|
|
if (oh.material_array > 0) {
|
|
obj_material_texture_enrs_table_init();
|
|
|
|
s.set_position(base_offset + oh.material_array, SEEK_SET);
|
|
obj->material_array = alloc->allocate<obj_material_data>(obj->num_material);
|
|
for (int32_t i = 0; i < obj->num_material; i++) {
|
|
obj_material_data& mat_data = obj->material_array[i];
|
|
s.read_data(mat_data);
|
|
|
|
if (s.big_endian)
|
|
obj_material_texture_enrs_table.apply(&mat_data);
|
|
|
|
for (obj_material_texture_data& j : mat_data.material.texdata)
|
|
mat4_transpose(&j.tex_coord_mat, &j.tex_coord_mat);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void obj_modern_write_model(obj* obj, stream& s,
|
|
int64_t base_offset, bool is_x, f2_struct* omdl) {
|
|
bool big_endian = s.big_endian;
|
|
|
|
const size_t mesh_size = is_x ? 0x130 : 0xD8;
|
|
const size_t sub_mesh_size = is_x ? 0x80 : 0x70;
|
|
|
|
omdl->sub_structs.push_back({});
|
|
omdl->sub_structs.push_back({});
|
|
f2_struct* oidx = &omdl->sub_structs.end()[-2];
|
|
f2_struct* ovtx = &omdl->sub_structs.back();
|
|
|
|
memory_stream s_oidx;
|
|
memory_stream s_ovtx;
|
|
s_oidx.open();
|
|
s_ovtx.open();
|
|
s_oidx.big_endian = big_endian;
|
|
s_ovtx.big_endian = big_endian;
|
|
|
|
uint32_t off;
|
|
enrs e;
|
|
enrs_entry ee;
|
|
pof pof;
|
|
|
|
obj_header oh = {};
|
|
if (!is_x) {
|
|
ee = { 0, 1, 80, 1 };
|
|
ee.append(0, 10, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = 80;
|
|
}
|
|
else {
|
|
ee = { 0, 2, 112, 1 };
|
|
ee.append(0, 8, ENRS_DWORD);
|
|
ee.append(0, 2, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = 112;
|
|
}
|
|
|
|
int32_t num_mesh = obj->num_mesh;
|
|
|
|
if (!is_x) {
|
|
ee = { off, 1, 216, (uint32_t)num_mesh };
|
|
ee.append(0, 32, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_mesh * 216ULL);
|
|
}
|
|
else {
|
|
ee = { off, 5, 304, (uint32_t)num_mesh };
|
|
ee.append(0, 6, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
ee.append(4, 20, ENRS_QWORD);
|
|
ee.append(0, 2, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_mesh * 304ULL);
|
|
}
|
|
|
|
uint32_t total_sub_meshes = 0;
|
|
for (int32_t i = 0; i < num_mesh; i++)
|
|
total_sub_meshes += obj->mesh_array[i].num_submesh;
|
|
|
|
if (!is_x) {
|
|
ee = { off, 17, 112, total_sub_meshes };
|
|
ee.append(0, 6, ENRS_DWORD);
|
|
ee.append(8, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(16, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_WORD);
|
|
ee.append(0, 1, ENRS_WORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_mesh * 112ULL);
|
|
}
|
|
else {
|
|
ee = { off, 17, 128, total_sub_meshes };
|
|
ee.append(0, 6, ENRS_DWORD);
|
|
ee.append(8, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(16, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_WORD);
|
|
ee.append(0, 1, ENRS_WORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_mesh * 128ULL);
|
|
}
|
|
|
|
int32_t total_bone_indices_count = 0;
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
for (int32_t j = 0; j < num_submesh; j++) {
|
|
obj_sub_mesh* sub_mesh = &mesh->submesh_array[i];
|
|
if (sub_mesh->bones_per_vertex == 4)
|
|
total_bone_indices_count += sub_mesh->num_bone_index;
|
|
}
|
|
}
|
|
|
|
ee = { off, 1, 2, (uint32_t)total_bone_indices_count };
|
|
ee.append(0, 1, ENRS_WORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(2 * (size_t)total_bone_indices_count);
|
|
off = align_val(off, is_x ? 0x10 : 0x04);
|
|
|
|
if (obj->num_material) {
|
|
obj_material_texture_enrs_table_init();
|
|
|
|
enrs_entry* mte = &obj_material_texture_enrs_table.vec[0];
|
|
ee = { off, 186, 1200, (uint32_t)obj->num_material, };
|
|
ee.offset = off;
|
|
ee.count = mte->count;
|
|
ee.size = mte->size;
|
|
ee.repeat_count = obj->num_material;
|
|
ee.sub = mte->sub;
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(ee.size * ee.repeat_count);
|
|
}
|
|
|
|
if (!is_x) {
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_int32_t(0);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write_int32_t(0);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
}
|
|
else {
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x10);
|
|
s.write_uint8_t(0);
|
|
s.write(0x07);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (num_mesh) {
|
|
oh.mesh_array = s.get_position() - base_offset;
|
|
|
|
obj_mesh_header* mhs = force_malloc<obj_mesh_header>(num_mesh);
|
|
obj_sub_mesh_header** smhss = force_malloc<obj_sub_mesh_header*>(num_mesh);
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
|
|
s.write(0x04);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_int32_t(0);
|
|
io_write_offset_pof_add(s, 0, 0x20, is_x, &pof);
|
|
s.write_uint32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
|
|
if (!is_x)
|
|
for (uint32_t j = 0; j < 20; j++)
|
|
s.write_offset_f2(0, 0);
|
|
else
|
|
for (uint32_t j = 0; j < 20; j++)
|
|
s.write_offset_x(0);
|
|
|
|
s.write_int32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write(0x18);
|
|
s.write(sizeof(mesh->name));
|
|
}
|
|
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
obj_mesh_header* mh = &mhs[i];
|
|
|
|
mh->format = OBJ_VERTEX_FILE_MODERN_STORAGE;
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
if (num_submesh) {
|
|
mh->submesh_array = s.get_position() - base_offset;
|
|
for (int32_t j = 0; j < num_submesh; j++) {
|
|
s.write(0x04);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_float_t(0.0f);
|
|
s.write_uint32_t(0);
|
|
s.write(0x08);
|
|
s.write_int32_t(0);
|
|
io_write_offset_pof_add(s, 0, 0x20, is_x, &pof);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_uint32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_offset(0, 0, is_x);
|
|
s.write_uint32_t(0);
|
|
s.write(0x10);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write_float_t(0);
|
|
s.write(0x04);
|
|
s.write_uint32_t(0);
|
|
|
|
if (is_x)
|
|
s.write(0x04);
|
|
}
|
|
}
|
|
|
|
obj_modern_write_vertex(mesh, s_ovtx, mh->vertex,
|
|
&mh->vertex_format_index, &mh->num_vertex, &mh->size_vertex, ovtx);
|
|
}
|
|
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
obj_mesh_header* mh = &mhs[i];
|
|
obj_sub_mesh_header* smhs = 0;
|
|
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
if (num_submesh) {
|
|
smhs = force_malloc<obj_sub_mesh_header>(num_submesh);
|
|
smhss[i] = smhs;
|
|
for (int32_t j = 0; j < num_submesh; j++) {
|
|
obj_sub_mesh* sub_mesh = &mesh->submesh_array[j];
|
|
obj_sub_mesh_header* smh = &smhs[j];
|
|
|
|
if (sub_mesh->bones_per_vertex == 4 && sub_mesh->num_bone_index) {
|
|
smh->bone_index_array_offset = s.get_position() - base_offset;
|
|
s.write(sub_mesh->bone_index_array, sub_mesh->num_bone_index * sizeof(uint16_t));
|
|
}
|
|
|
|
smh->index_array_offset = s_oidx.get_position();
|
|
obj_modern_write_index(sub_mesh, s_oidx, is_x, oidx);
|
|
}
|
|
}
|
|
}
|
|
s.align_write(is_x ? 0x10 : 0x04);
|
|
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
obj_mesh_header* mh = &mhs[i];
|
|
obj_sub_mesh_header* smhs = smhss[i];
|
|
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
if (num_submesh) {
|
|
s.position_push(base_offset + mh->submesh_array, SEEK_SET);
|
|
for (int32_t j = 0; j < num_submesh; j++) {
|
|
obj_sub_mesh* sub_mesh = &mesh->submesh_array[j];
|
|
obj_sub_mesh_header* smh = &smhs[j];
|
|
|
|
s.write_uint32_t_reverse_endianness(sub_mesh->flags);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_sphere.center.x);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_sphere.center.y);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_sphere.center.z);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_sphere.radius);
|
|
s.write_uint32_t_reverse_endianness(sub_mesh->material_index);
|
|
s.write(&sub_mesh->uv_index, 0x08);
|
|
s.write_int32_t_reverse_endianness(sub_mesh->num_bone_index);
|
|
s.write_offset(smh->bone_index_array_offset, 0x20, is_x);
|
|
s.write_uint32_t_reverse_endianness(sub_mesh->bones_per_vertex);
|
|
s.write_uint32_t_reverse_endianness(sub_mesh->primitive_type);
|
|
s.write_uint32_t_reverse_endianness(sub_mesh->index_format);
|
|
s.write_int32_t_reverse_endianness(sub_mesh->num_index);
|
|
s.write_offset(smh->index_array_offset, 0, is_x);
|
|
s.write_uint32_t_reverse_endianness(sub_mesh->attrib.w);
|
|
s.write(0x10);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_box.center.x);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_box.center.y);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_box.center.z);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_box.size.x);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_box.size.y);
|
|
s.write_float_t_reverse_endianness(sub_mesh->bounding_box.size.z);
|
|
|
|
uint32_t index_max = 0;
|
|
uint32_t* index = sub_mesh->index_array;
|
|
int32_t num_index = sub_mesh->num_index;
|
|
for (int32_t k = 0; k < num_index; k++, index++)
|
|
if (index_max < *index)
|
|
index_max = *index;
|
|
|
|
switch (sub_mesh->index_format) {
|
|
case OBJ_INDEX_U8:
|
|
s.write_uint32_t_reverse_endianness(index_max << 24);
|
|
break;
|
|
case OBJ_INDEX_U16:
|
|
s.write_uint32_t_reverse_endianness(index_max << 16);
|
|
break;
|
|
case OBJ_INDEX_U32:
|
|
s.write_uint32_t_reverse_endianness(index_max);
|
|
break;
|
|
}
|
|
s.write_uint32_t_reverse_endianness(sub_mesh->index_offset);
|
|
|
|
if (is_x)
|
|
s.write(0x04);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
free_def(smhs);
|
|
}
|
|
|
|
s.position_push(base_offset + oh.mesh_array, SEEK_SET);
|
|
for (int32_t i = 0; i < num_mesh; i++) {
|
|
obj_mesh* mesh = &obj->mesh_array[i];
|
|
obj_mesh_header* mh = &mhs[i];
|
|
|
|
s.write_uint32_t_reverse_endianness(mesh->flags);
|
|
s.write_float_t_reverse_endianness(mesh->bounding_sphere.center.x);
|
|
s.write_float_t_reverse_endianness(mesh->bounding_sphere.center.y);
|
|
s.write_float_t_reverse_endianness(mesh->bounding_sphere.center.z);
|
|
s.write_float_t_reverse_endianness(mesh->bounding_sphere.radius);
|
|
s.write_int32_t_reverse_endianness(mesh->num_submesh);
|
|
if (mh->submesh_array && !is_x)
|
|
mh->submesh_array += 0x20;
|
|
s.write_offset(mh->submesh_array, 0, is_x);
|
|
s.write_uint32_t_reverse_endianness(mh->format);
|
|
s.write_int32_t_reverse_endianness(mh->size_vertex);
|
|
s.write_int32_t_reverse_endianness(mh->num_vertex);
|
|
|
|
if (!is_x)
|
|
for (uint32_t j = 0; j < 20; j++)
|
|
s.write_offset_f2(mh->vertex[j], 0);
|
|
else
|
|
for (uint32_t j = 0; j < 20; j++)
|
|
s.write_offset_x(mh->vertex[j]);
|
|
|
|
s.write_uint32_t_reverse_endianness(mesh->attrib.w & 0x3FFFFFFF);
|
|
s.write_uint32_t_reverse_endianness(mh->vertex_format_index);
|
|
s.write_uint32_t_reverse_endianness(mesh->reserved[0]);
|
|
s.write_uint32_t_reverse_endianness(mesh->reserved[1]);
|
|
s.write_uint32_t_reverse_endianness(mesh->reserved[2]);
|
|
s.write_uint32_t_reverse_endianness(mesh->reserved[3]);
|
|
s.write_uint32_t_reverse_endianness(mesh->reserved[4]);
|
|
s.write_uint32_t_reverse_endianness(mesh->reserved[5]);
|
|
s.write(&mesh->name, sizeof(mesh->name) - 1);
|
|
s.write_char('\0');
|
|
}
|
|
s.position_pop();
|
|
free_def(mhs);
|
|
free_def(smhss);
|
|
}
|
|
|
|
if (obj->material_array) {
|
|
oh.material_array = s.get_position() - base_offset;
|
|
for (int32_t i = 0; i < obj->num_material; i++) {
|
|
obj_material_data mat_data = obj->material_array[i];
|
|
for (obj_material_texture_data& j : mat_data.material.texdata)
|
|
mat4_transpose(&j.tex_coord_mat, &j.tex_coord_mat);
|
|
s.write_data(mat_data);
|
|
}
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
s.position_push(base_offset, SEEK_SET);
|
|
if (!is_x) {
|
|
s.write_uint32_t_reverse_endianness(0x10000); // version
|
|
s.write_uint32_t_reverse_endianness(0x00); // flags
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.center.x);
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.center.y);
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.center.z);
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.radius);
|
|
s.write_int32_t_reverse_endianness(obj->num_mesh);
|
|
s.write_offset_f2(oh.mesh_array, 0x20);
|
|
s.write_int32_t_reverse_endianness(obj->num_material);
|
|
s.write_offset_f2(oh.material_array, 0x20);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[0]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[1]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[2]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[3]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[4]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[5]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[6]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[7]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[8]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[9]);
|
|
}
|
|
else {
|
|
s.write_uint32_t_reverse_endianness(0x10000); // version
|
|
s.write_uint32_t_reverse_endianness(0x00); // flags
|
|
s.write_int32_t_reverse_endianness(obj->num_mesh);
|
|
s.write_int32_t_reverse_endianness(obj->num_material);
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.center.x);
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.center.y);
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.center.z);
|
|
s.write_float_t_reverse_endianness(obj->bounding_sphere.radius);
|
|
s.write_offset_x(oh.mesh_array);
|
|
s.write_offset_x(oh.material_array);
|
|
s.write(0x10);
|
|
s.write_uint8_t(obj->flags);
|
|
s.write(0x07);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[0]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[1]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[2]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[3]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[4]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[5]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[6]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[7]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[8]);
|
|
s.write_uint32_t_reverse_endianness(obj->reserved[9]);
|
|
}
|
|
s.position_pop();
|
|
|
|
omdl->enrs = e;
|
|
omdl->pof = pof;
|
|
|
|
s_oidx.align_write(0x10);
|
|
s_oidx.copy(oidx->data);
|
|
s_oidx.close();
|
|
|
|
new (&oidx->header) f2_header('OIDX');
|
|
oidx->header.attrib.set_big_endian(big_endian);
|
|
|
|
s_ovtx.align_write(0x10);
|
|
s_ovtx.copy(ovtx->data);
|
|
s_ovtx.close();
|
|
|
|
new (&ovtx->header) f2_header('OVTX');
|
|
ovtx->header.attrib.set_big_endian(big_endian);
|
|
}
|
|
|
|
static void obj_modern_read_model_mesh(obj_mesh* mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset,
|
|
uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx) {
|
|
const size_t sub_mesh_size = is_x ? 0x80 : 0x70;
|
|
|
|
obj_mesh_header mh = {};
|
|
mesh->flags = s.read_uint32_t_reverse_endianness();
|
|
mesh->bounding_sphere.center.x = s.read_float_t_reverse_endianness();
|
|
mesh->bounding_sphere.center.y = s.read_float_t_reverse_endianness();
|
|
mesh->bounding_sphere.center.z = s.read_float_t_reverse_endianness();
|
|
mesh->bounding_sphere.radius = s.read_float_t_reverse_endianness();
|
|
mesh->num_submesh = s.read_int32_t_reverse_endianness();
|
|
mh.submesh_array = s.read_offset(header_length, is_x);
|
|
mh.format = (obj_vertex_format_file)s.read_uint32_t_reverse_endianness();
|
|
mh.size_vertex = s.read_int32_t_reverse_endianness();
|
|
mh.num_vertex = s.read_int32_t_reverse_endianness();
|
|
|
|
if (!is_x)
|
|
for (int64_t& i : mh.vertex)
|
|
i = s.read_offset_f2(0);
|
|
else
|
|
for (int64_t& i : mh.vertex)
|
|
i = s.read_offset_x();
|
|
|
|
mesh->attrib.w = s.read_uint32_t_reverse_endianness() & 0x3FFFFFFF;
|
|
mh.vertex_format_index = s.read_uint32_t_reverse_endianness();
|
|
mesh->reserved[0] = s.read_uint32_t_reverse_endianness();
|
|
mesh->reserved[1] = s.read_uint32_t_reverse_endianness();
|
|
mesh->reserved[2] = s.read_uint32_t_reverse_endianness();
|
|
mesh->reserved[3] = s.read_uint32_t_reverse_endianness();
|
|
mesh->reserved[4] = s.read_uint32_t_reverse_endianness();
|
|
mesh->reserved[5] = s.read_uint32_t_reverse_endianness();
|
|
s.read(&mesh->name, sizeof(mesh->name));
|
|
mesh->name[sizeof(mesh->name) - 1] = 0;
|
|
|
|
if (mh.submesh_array) {
|
|
int32_t num_submesh = mesh->num_submesh;
|
|
mesh->submesh_array = alloc->allocate<obj_sub_mesh>(num_submesh);
|
|
for (int32_t i = 0; i < num_submesh; i++) {
|
|
s.set_position(base_offset + mh.submesh_array + sub_mesh_size * i, SEEK_SET);
|
|
obj_modern_read_model_sub_mesh(&mesh->submesh_array[i],
|
|
alloc, s, base_offset, header_length, is_x, s_oidx);
|
|
}
|
|
}
|
|
|
|
obj_modern_read_vertex(mesh, alloc, *s_ovtx, mh.vertex,
|
|
mh.vertex_format_index, mh.num_vertex, mh.size_vertex);
|
|
obj_vertex_generate_tangents(mesh);
|
|
}
|
|
|
|
static void obj_modern_read_model_sub_mesh(obj_sub_mesh* sub_mesh,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, int64_t base_offset,
|
|
uint32_t header_length, bool is_x, stream* s_oidx) {
|
|
obj_sub_mesh_header smh = {};
|
|
sub_mesh->flags = s.read_uint32_t_reverse_endianness();
|
|
sub_mesh->bounding_sphere.center.x = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_sphere.center.y = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_sphere.center.z = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_sphere.radius = s.read_float_t_reverse_endianness();
|
|
sub_mesh->material_index = s.read_int32_t_reverse_endianness();
|
|
s.read(&sub_mesh->uv_index, 0x08);
|
|
sub_mesh->num_bone_index = s.read_int32_t_reverse_endianness();
|
|
smh.bone_index_array_offset = s.read_offset(header_length, is_x);
|
|
sub_mesh->bones_per_vertex = s.read_uint32_t_reverse_endianness();
|
|
sub_mesh->primitive_type = (obj_primitive_type)s.read_uint32_t_reverse_endianness();
|
|
sub_mesh->index_format = (obj_index_format)s.read_uint32_t_reverse_endianness();
|
|
sub_mesh->num_index = s.read_int32_t_reverse_endianness();
|
|
smh.index_array_offset = s.read_offset(0, is_x);
|
|
sub_mesh->attrib.w = s.read_uint32_t_reverse_endianness();
|
|
s.read(0, 0x10);
|
|
sub_mesh->bounding_box.center.x = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_box.center.y = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_box.center.z = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_box.size.x = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_box.size.y = s.read_float_t_reverse_endianness();
|
|
sub_mesh->bounding_box.size.z = s.read_float_t_reverse_endianness();
|
|
s.read(0, 0x04);
|
|
sub_mesh->index_offset = s.read_uint32_t_reverse_endianness();
|
|
|
|
if (is_x)
|
|
s.read(0, 0x04);
|
|
|
|
if (sub_mesh->bones_per_vertex == 4 && smh.bone_index_array_offset) {
|
|
sub_mesh->bone_index_array = alloc->allocate<uint16_t>(sub_mesh->num_bone_index);
|
|
s.set_position(base_offset + smh.bone_index_array_offset, SEEK_SET);
|
|
s.read(sub_mesh->bone_index_array, sub_mesh->num_bone_index * sizeof(uint16_t));
|
|
if (s.big_endian) {
|
|
uint16_t* bone_index_array = sub_mesh->bone_index_array;
|
|
for (int32_t k = 0; k < sub_mesh->num_bone_index; k++)
|
|
bone_index_array[k] = reverse_endianness_uint16_t(bone_index_array[k]);
|
|
}
|
|
}
|
|
|
|
s_oidx->set_position(smh.index_array_offset, SEEK_SET);
|
|
obj_modern_read_index(sub_mesh, alloc, *s_oidx);
|
|
}
|
|
|
|
static obj_skin* obj_modern_read_skin(prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset, uint32_t header_length, bool is_x) {
|
|
obj_skin* sk = alloc->allocate<obj_skin>();
|
|
s.set_position(base_offset, SEEK_SET);
|
|
|
|
obj_skin_header sh = {};
|
|
if (!is_x) {
|
|
sh.bone_id_array_offset = s.read_offset_f2(header_length);
|
|
sh.bone_matrix_array_offset = s.read_offset_f2(header_length);
|
|
sh.bone_name_array_offset = s.read_offset_f2(header_length);
|
|
sh.ex_data_offset = s.read_offset_f2(header_length);
|
|
sk->num_bone = s.read_int32_t_reverse_endianness();
|
|
sh.bone_parent_id_array_offset = s.read_offset_f2(header_length);
|
|
s.read(0, 0x0C);
|
|
}
|
|
else {
|
|
sh.bone_id_array_offset = s.read_offset_x();
|
|
sh.bone_matrix_array_offset = s.read_offset_x();
|
|
sh.bone_name_array_offset = s.read_offset_x();
|
|
sh.ex_data_offset = s.read_offset_x();
|
|
sk->num_bone = s.read_int32_t_reverse_endianness();
|
|
sh.bone_parent_id_array_offset = s.read_offset_x();
|
|
s.read(0, 0x18);
|
|
}
|
|
|
|
if (sh.bone_id_array_offset) {
|
|
sk->bone_array = alloc->allocate<obj_skin_bone>(sk->num_bone);
|
|
|
|
s.set_position(sh.bone_id_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
sk->bone_array[i].id = s.read_uint32_t_reverse_endianness();
|
|
|
|
if (sh.bone_matrix_array_offset) {
|
|
s.set_position(sh.bone_matrix_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
mat4& mat = sk->bone_array[i].inv_bind_pose_mat;
|
|
mat.row0.x = s.read_float_t_reverse_endianness();
|
|
mat.row1.x = s.read_float_t_reverse_endianness();
|
|
mat.row2.x = s.read_float_t_reverse_endianness();
|
|
mat.row3.x = s.read_float_t_reverse_endianness();
|
|
mat.row0.y = s.read_float_t_reverse_endianness();
|
|
mat.row1.y = s.read_float_t_reverse_endianness();
|
|
mat.row2.y = s.read_float_t_reverse_endianness();
|
|
mat.row3.y = s.read_float_t_reverse_endianness();
|
|
mat.row0.z = s.read_float_t_reverse_endianness();
|
|
mat.row1.z = s.read_float_t_reverse_endianness();
|
|
mat.row2.z = s.read_float_t_reverse_endianness();
|
|
mat.row3.z = s.read_float_t_reverse_endianness();
|
|
mat.row0.w = s.read_float_t_reverse_endianness();
|
|
mat.row1.w = s.read_float_t_reverse_endianness();
|
|
mat.row2.w = s.read_float_t_reverse_endianness();
|
|
mat.row3.w = s.read_float_t_reverse_endianness();
|
|
}
|
|
}
|
|
|
|
if (sh.bone_name_array_offset) {
|
|
s.set_position(sh.bone_name_array_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
sk->bone_array[i].name = obj_read_utf8_string_null_terminated_offset(
|
|
alloc, s, s.read_offset_f2(header_length));
|
|
else
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
sk->bone_array[i].name = obj_read_utf8_string_null_terminated_offset(
|
|
alloc, s, s.read_offset_x());
|
|
}
|
|
|
|
if (sh.bone_parent_id_array_offset) {
|
|
s.set_position(sh.bone_parent_id_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
sk->bone_array[i].parent = s.read_uint32_t_reverse_endianness();
|
|
}
|
|
}
|
|
|
|
if (sh.ex_data_offset)
|
|
sk->ex_data = obj_modern_read_skin_ex_data(alloc,
|
|
s, sh.ex_data_offset, header_length, is_x);
|
|
return sk;
|
|
}
|
|
|
|
static void obj_modern_write_skin(obj_skin* sk, stream& s,
|
|
int64_t base_offset, bool is_x, f2_struct* oskn) {
|
|
uint32_t off;
|
|
enrs e;
|
|
enrs_entry ee;
|
|
pof pof;
|
|
|
|
if (!is_x) {
|
|
ee = { 0, 1, 48, 1 };
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = 48;
|
|
}
|
|
else {
|
|
ee = { 0, 3, 72, 1 };
|
|
ee.append(0, 4, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = 72;
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
obj_skin_header sh = {};
|
|
if (sk->num_bone) {
|
|
if (sk->num_bone % 4) {
|
|
ee = { off, 1, 4, (uint32_t)sk->num_bone };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sk->num_bone * 4ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 4, (uint32_t)sk->num_bone };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sk->num_bone * 4ULL);
|
|
}
|
|
else {
|
|
ee = { off, 1, 4, (uint32_t)(sk->num_bone * 2ULL) };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sk->num_bone * 2 * 4ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
if (!is_x) {
|
|
ee = { off, 1, 4, (uint32_t)sk->num_bone };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sk->num_bone * 4ULL);
|
|
}
|
|
else {
|
|
ee = { off, 1, 8, (uint32_t)sk->num_bone };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sk->num_bone * 8ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 64, (uint32_t)sk->num_bone };
|
|
ee.append(0, 16, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(sk->num_bone * 64ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
std::vector<string_hash> strings;
|
|
std::vector<int64_t> string_offsets;
|
|
std::vector<string_hash> bone_names;
|
|
|
|
obj_skin_ex_data_header exh = {};
|
|
|
|
obj_skin_block_header* bhs = 0;
|
|
int64_t motion_block_node_mats = 0;
|
|
int64_t motion_block_node_name_offset = 0;
|
|
int64_t cloth_mats = 0;
|
|
int64_t cloth_root = 0;
|
|
int64_t cloth_nodes = 0;
|
|
int64_t cloth_mesh_indices = 0;
|
|
int64_t cloth_backface_mesh_indices = 0;
|
|
int32_t expressions_count = 0;
|
|
int32_t motion_count = 0;
|
|
int32_t motion_nodes_count = 0;
|
|
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
const char** bone_name_array = ex->bone_name_array;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
switch (block->type) {
|
|
case OBJ_SKIN_BLOCK_CLOTH: {
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
obj_skin_strings_push_back_check(strings, cloth->mesh_name);
|
|
obj_skin_strings_push_back_check(strings, cloth->backface_mesh_name);
|
|
|
|
for (int32_t k = 0; k < cloth->num_root; k++) {
|
|
obj_skin_block_cloth_root* root_array = &cloth->root_array[k];
|
|
for (uint32_t l = 0; l < 4; l++)
|
|
obj_skin_strings_push_back_check(strings, root_array->bone_weights[l].bone_name);
|
|
}
|
|
obj_skin_strings_push_back_check(strings, "CLS");
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT: {
|
|
obj_skin_block_constraint* constraint = block->constraint;
|
|
obj_skin_strings_push_back_check(strings, constraint->node.parent_name);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, constraint->name_index);
|
|
obj_skin_strings_push_back_check(strings, constraint->source_node_name);
|
|
switch (constraint->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
obj_skin_strings_push_back_check(strings, "Orientation");
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
obj_skin_strings_push_back_check(strings, "Direction");
|
|
obj_skin_strings_push_back_check(strings, constraint->direction->up_vector.name);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
obj_skin_strings_push_back_check(strings, "Position");
|
|
obj_skin_strings_push_back_check(strings, constraint->position->up_vector.name);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
obj_skin_strings_push_back_check(strings, "Distance");
|
|
obj_skin_strings_push_back_check(strings, constraint->distance->up_vector.name);
|
|
break;
|
|
}
|
|
obj_skin_strings_push_back_check(strings, "CNS");
|
|
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, constraint->name_index);
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_EXPRESSION: {
|
|
obj_skin_block_expression* expression = block->expression;
|
|
for (int32_t j = 0; j < expression->num_expression; j++)
|
|
obj_skin_strings_push_back_check(strings, expression->expression_array[j]);
|
|
obj_skin_strings_push_back_check(strings, expression->node.parent_name);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, expression->name_index);
|
|
obj_skin_strings_push_back_check(strings, "EXP");
|
|
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, expression->name_index);
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_MOTION: {
|
|
obj_skin_block_motion* motion = block->motion;
|
|
obj_skin_strings_push_back_check(strings, motion->node.parent_name);
|
|
obj_skin_strings_push_back_check(strings, motion->name);
|
|
for (int32_t j = 0; j < motion->num_node; j++)
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, motion->node_array[j].name_index);
|
|
obj_skin_strings_push_back_check(strings, "MOT");
|
|
|
|
if (!is_x)
|
|
obj_skin_strings_push_back_check(bone_names, motion->name);
|
|
for (int32_t j = 0; j < motion->num_node; j++)
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, motion->node_array[j].name_index);
|
|
} break;
|
|
case OBJ_SKIN_BLOCK_OSAGE: {
|
|
obj_skin_block_osage* osage = block->osage;
|
|
obj_skin_strings_push_back_check(strings, osage->node.parent_name);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage->end_name_index);
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage->name_index);
|
|
obj_skin_osage_node* osage_node = &ex->osage_node_array[osage->start_index];
|
|
for (int32_t j = 0; j < osage->count; j++) {
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage_node->name_index);
|
|
|
|
int32_t end_name_index = osage->end_name_index;
|
|
obj_skin_osage_sibling_info* osage_sibling_info = ex->osage_sibling_info_array;
|
|
for (int32_t k = 0; k < ex->num_osage_sibling_info; k++) {
|
|
if (end_name_index == osage_sibling_info->name_index) {
|
|
obj_skin_strings_push_back_check_by_index(strings,
|
|
bone_name_array, osage_sibling_info->sibling_name_index);
|
|
break;
|
|
}
|
|
osage_sibling_info++;
|
|
}
|
|
osage_node++;
|
|
}
|
|
obj_skin_strings_push_back_check(strings, "OSG");
|
|
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage->name_index);
|
|
osage_node = &ex->osage_node_array[osage->start_index];
|
|
for (int32_t j = 0; j < osage->count; j++) {
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage_node->name_index);
|
|
|
|
int32_t end_name_index = osage->end_name_index;
|
|
obj_skin_osage_sibling_info* osage_sibling_info = ex->osage_sibling_info_array;
|
|
for (int32_t k = 0; k < ex->num_osage_sibling_info; k++) {
|
|
if (end_name_index == osage_sibling_info->name_index) {
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage_sibling_info->sibling_name_index);
|
|
break;
|
|
}
|
|
osage_sibling_info++;
|
|
}
|
|
osage_node++;
|
|
}
|
|
obj_skin_strings_push_back_check_by_index(bone_names,
|
|
bone_name_array, osage->end_name_index);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
exh.cloth_count = 0;
|
|
exh.num_osage = 0;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type == OBJ_SKIN_BLOCK_CLOTH)
|
|
exh.cloth_count++;
|
|
else if (block->type == OBJ_SKIN_BLOCK_EXPRESSION)
|
|
expressions_count++;
|
|
else if (block->type == OBJ_SKIN_BLOCK_MOTION) {
|
|
motion_count++;
|
|
motion_nodes_count += block->motion->num_node;
|
|
}
|
|
else if (block->type == OBJ_SKIN_BLOCK_OSAGE)
|
|
exh.num_osage++;
|
|
}
|
|
|
|
exh.num_bone_name = (int32_t)bone_names.size();
|
|
}
|
|
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
if (!is_x) {
|
|
ee = { off, 1, 80, 1 };
|
|
ee.append(0, 10, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = 80;
|
|
}
|
|
else {
|
|
ee = { off, 5, 128, 1 };
|
|
ee.append(0, 2, ENRS_DWORD);
|
|
ee.append(0, 4, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 2, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = 128;
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
if (ex->num_osage_node) {
|
|
ee = { off, 1, 12, (uint32_t)ex->num_osage_node };
|
|
ee.append(0, 2, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(ex->num_osage_node * 12ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 12, (uint32_t)ex->num_osage_sibling_info };
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(ex->num_osage_sibling_info * 12ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (exh.num_osage || exh.cloth_count) {
|
|
int32_t count = exh.num_osage + exh.cloth_count;
|
|
if (!is_x) {
|
|
ee = { off, 1, 4, (uint32_t)count };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(count * 4ULL);
|
|
}
|
|
else {
|
|
ee = { off, 1, 8, (uint32_t)count };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(count * 8ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (!is_x) {
|
|
ee = { off, 1, 4, (uint32_t)exh.num_bone_name };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(exh.num_bone_name * 4ULL);
|
|
}
|
|
else {
|
|
ee = { off, 1, 8, (uint32_t)exh.num_bone_name };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(exh.num_bone_name * 8ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
if (ex->num_block > 0) {
|
|
if (!is_x) {
|
|
ee = { off, 1, 8, (uint32_t)ex->num_block };
|
|
ee.append(0, 2, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(ex->num_block * 8ULL);
|
|
}
|
|
else {
|
|
ee = { off, 1, 16, (uint32_t)ex->num_block };
|
|
ee.append(0, 2, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(ex->num_block * 16ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
if (exh.num_osage) {
|
|
if (!is_x) {
|
|
ee = { off, 2, 76, (uint32_t)exh.num_osage };
|
|
ee.append(0, 14, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(exh.num_osage * 76ULL);
|
|
}
|
|
else {
|
|
ee = { off, 4, 104, (uint32_t)exh.num_osage };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
ee.append(4, 4, ENRS_DWORD);
|
|
ee.append(8, 1, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(exh.num_osage * 104ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (expressions_count) {
|
|
if (!is_x) {
|
|
ee = { off, 1, 84, (uint32_t)expressions_count };
|
|
ee.append(0, 19, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(expressions_count * 84ULL);
|
|
}
|
|
else {
|
|
ee = { off, 5, 136, (uint32_t)expressions_count };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 9, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(expressions_count * 136ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (exh.cloth_count) {
|
|
if (!is_x) {
|
|
ee = { off, 1, 52, (uint32_t)exh.cloth_count };
|
|
ee.append(0, 13, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(exh.cloth_count * 52ULL);
|
|
}
|
|
else {
|
|
ee = { off, 4, 88, (uint32_t)exh.cloth_count };
|
|
ee.append(0, 2, ENRS_QWORD);
|
|
ee.append(0, 4, ENRS_DWORD);
|
|
ee.append(0, 6, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(exh.cloth_count * 88ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
int32_t constraint_count = 0;
|
|
obj_skin_block_constraint_type cns_type = OBJ_SKIN_BLOCK_CONSTRAINT_NONE;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CONSTRAINT)
|
|
continue;
|
|
|
|
if (cns_type == block->constraint->type) {
|
|
constraint_count++;
|
|
continue;
|
|
}
|
|
|
|
if (cns_type) {
|
|
ee.repeat_count = constraint_count;
|
|
e.vec.push_back(ee);
|
|
off = ee.size * ee.repeat_count;
|
|
}
|
|
|
|
cns_type = block->constraint->type;
|
|
if (!is_x)
|
|
switch (cns_type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
ee = { off, 6, 68, 0 };
|
|
ee.append(0, 17, ENRS_DWORD);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
ee = { off, 8, 144, 0 };
|
|
ee.append(0, 29, ENRS_DWORD);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
ee = { off, 8, 132, 0 };
|
|
ee.append(0, 33, ENRS_DWORD);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
ee = { off, 8, 136, 0 };
|
|
ee.append(0, 34, ENRS_DWORD);
|
|
break;
|
|
}
|
|
else
|
|
switch (cns_type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
ee = { off, 6, 96, 0 };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
ee.append(4, 2, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
ee = { off, 8, 144, 0 };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
ee.append(4, 2, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 8, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 6, ENRS_DWORD);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
ee = { off, 8, 160, 0 };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
ee.append(4, 2, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 8, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 10, ENRS_DWORD);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
ee = { off, 8, 168, 0 };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
ee.append(4, 2, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 8, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 11, ENRS_DWORD);
|
|
break;
|
|
}
|
|
constraint_count = 1;
|
|
}
|
|
|
|
if (constraint_count) {
|
|
ee.repeat_count = constraint_count;
|
|
e.vec.push_back(ee);
|
|
off = ee.size * ee.repeat_count;
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
if (!is_x) {
|
|
ee = { off, 1, 56, (uint32_t)motion_count };
|
|
ee.append(0, 14, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(motion_count * 56ULL);
|
|
}
|
|
else {
|
|
ee = { off, 5, 80, (uint32_t)motion_count };
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 9, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
ee.append(4, 2, ENRS_QWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(motion_count * 80ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
|
|
if (motion_count) {
|
|
ee = { off, 1, 64, (uint32_t)motion_nodes_count };
|
|
ee.append(0, 16, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(motion_nodes_count * 64ULL);
|
|
off = align_val(off, 0x10);
|
|
|
|
ee = { off, 1, 4, (uint32_t)motion_nodes_count };
|
|
ee.append(0, 1, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(motion_nodes_count * 4ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (exh.cloth_count) {
|
|
int32_t num_mat = 0;
|
|
int32_t num_root = 0;
|
|
int32_t num_node = 0;
|
|
int32_t num_mesh_index = 0;
|
|
int32_t num_backface_mesh_index = 0;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type == OBJ_SKIN_BLOCK_CLOTH) {
|
|
num_mat += block->cloth->mat_array ? block->cloth->num_mat : 0;
|
|
num_root += block->cloth->root_array ? block->cloth->num_root : 0;
|
|
num_node += block->cloth->node_array
|
|
? block->cloth->num_root * (block->cloth->num_node - 1) : 0;
|
|
num_mesh_index += block->cloth->mesh_index_array
|
|
? block->cloth->num_mesh_index + 1 : 0;
|
|
num_backface_mesh_index += block->cloth->backface_mesh_index_array
|
|
? block->cloth->num_backface_mesh_index + 1 : 0;
|
|
}
|
|
}
|
|
|
|
if (num_mat) {
|
|
ee = { off, 1, sizeof(mat4), (uint32_t)num_mat };
|
|
ee.append(0, 16, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_mat * sizeof(mat4));
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (num_root) {
|
|
if (!is_x) {
|
|
ee = { off, 1, 104, (uint32_t)num_root };
|
|
ee.append(0, 26, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_root * 104ULL);
|
|
}
|
|
else {
|
|
ee = { off, 9, 136, (uint32_t)num_root };
|
|
ee.append(0, 10, ENRS_DWORD);
|
|
ee.append(0, 1, ENRS_QWORD);
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
ee.append(4, 1, ENRS_QWORD);
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_root * 136ULL);
|
|
}
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (num_node) {
|
|
ee = { off, 1, 44, (uint32_t)num_node };
|
|
ee.append(0, 11, ENRS_DWORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_node * 44ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (num_mesh_index) {
|
|
ee = { off, 1, 2, (uint32_t)num_mesh_index };
|
|
ee.append(0, 1, ENRS_WORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_mesh_index * 2ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
|
|
if (num_backface_mesh_index) {
|
|
ee = { off, 1, 2, (uint32_t)num_backface_mesh_index };
|
|
ee.append(0, 1, ENRS_WORD);
|
|
e.vec.push_back(ee);
|
|
off = (uint32_t)(num_backface_mesh_index * 2ULL);
|
|
off = align_val(off, 0x10);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_x) {
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write_int32_t(0);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x0C);
|
|
}
|
|
else {
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write_int32_t(0);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x18);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (sk->num_bone) {
|
|
sh.bone_id_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
s.write_int32_t_reverse_endianness(sk->bone_array[i].id);
|
|
s.align_write(0x10);
|
|
|
|
sh.bone_parent_id_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < sk->num_bone; i++)
|
|
s.write_int32_t_reverse_endianness(sk->bone_array[i].parent);
|
|
s.align_write(0x10);
|
|
|
|
sh.bone_name_array_offset = s.get_position();
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
obj_skin_strings_push_back_check(strings, sk->bone_array[i].name);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
obj_skin_strings_push_back_check(strings, sk->bone_array[i].name);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
sh.bone_matrix_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
mat4& mat = sk->bone_array[i].inv_bind_pose_mat;
|
|
s.write_float_t_reverse_endianness(mat.row0.x);
|
|
s.write_float_t_reverse_endianness(mat.row1.x);
|
|
s.write_float_t_reverse_endianness(mat.row2.x);
|
|
s.write_float_t_reverse_endianness(mat.row3.x);
|
|
s.write_float_t_reverse_endianness(mat.row0.y);
|
|
s.write_float_t_reverse_endianness(mat.row1.y);
|
|
s.write_float_t_reverse_endianness(mat.row2.y);
|
|
s.write_float_t_reverse_endianness(mat.row3.y);
|
|
s.write_float_t_reverse_endianness(mat.row0.z);
|
|
s.write_float_t_reverse_endianness(mat.row1.z);
|
|
s.write_float_t_reverse_endianness(mat.row2.z);
|
|
s.write_float_t_reverse_endianness(mat.row3.z);
|
|
s.write_float_t_reverse_endianness(mat.row0.w);
|
|
s.write_float_t_reverse_endianness(mat.row1.w);
|
|
s.write_float_t_reverse_endianness(mat.row2.w);
|
|
s.write_float_t_reverse_endianness(mat.row3.w);
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
sh.ex_data_offset = s.get_position();
|
|
if (!is_x) {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write(0x04);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write_int32_t(0);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write_int32_t(0);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
}
|
|
else {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write(0x08);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write_int32_t(0);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write_int32_t(0);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (ex->num_osage_node) {
|
|
exh.osage_node_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_osage_node; i++) {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
exh.osage_sibling_info_array_offset = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_osage_sibling_info; i++) {
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
}
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
if (exh.num_osage || exh.cloth_count) {
|
|
exh.osage_name_array_offset = s.get_position();
|
|
if (!is_x) {
|
|
for (int32_t i = 0; i < exh.num_osage; i++)
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
|
|
for (int32_t i = 0; i < exh.cloth_count; i++)
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
}
|
|
else {
|
|
for (int32_t i = 0; i < exh.num_osage; i++)
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
|
|
for (int32_t i = 0; i < exh.cloth_count; i++)
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
exh.bone_name_array_offset = s.get_position();
|
|
if (!is_x)
|
|
for (string_hash& i : bone_names)
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
else
|
|
for (string_hash& i : bone_names)
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.align_write(0x10);
|
|
|
|
if (ex->num_block > 0) {
|
|
exh.block_array_offset = s.get_position();
|
|
if (!is_x) {
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
}
|
|
s.write_offset_f2(0, 0x20);
|
|
s.write_offset_f2(0, 0x20);
|
|
}
|
|
else {
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
}
|
|
s.write_offset_x(0);
|
|
s.write_offset_x(0);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
bhs = force_malloc<obj_skin_block_header>(ex->num_block);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_OSAGE)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x24);
|
|
s.write(0x24);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_OSAGE)
|
|
continue;
|
|
|
|
obj_skin_block_osage* osage = block->osage;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x28);
|
|
if (osage->motion_node_name) {
|
|
s.write(0x18);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x18);
|
|
}
|
|
else
|
|
s.write(0x38);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_EXPRESSION)
|
|
continue;
|
|
|
|
obj_skin_block_expression* expression = block->expression;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x24);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x04);
|
|
|
|
for (int32_t j = 0; j < expression->num_expression && j < 9; j++)
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
|
|
for (int32_t j = expression->num_expression; j < 9; j++)
|
|
s.write(0x04);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_EXPRESSION)
|
|
continue;
|
|
obj_skin_block_expression* expression = block->expression;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x28);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x08);
|
|
|
|
for (int32_t j = 0; j < expression->num_expression && j < 9; j++)
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
|
|
for (int32_t j = expression->num_expression; j < 9; j++)
|
|
s.write(0x08);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x10);
|
|
|
|
if (cloth->num_node)
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
else
|
|
s.write(0x04);
|
|
|
|
if (cloth->num_root) {
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
}
|
|
else
|
|
s.write(0x08);
|
|
|
|
if (cloth->num_mesh_index)
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
else
|
|
s.write(0x04);
|
|
|
|
if (cloth->num_backface_mesh_index)
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
else
|
|
s.write(0x04);
|
|
|
|
s.write(0x08);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x10);
|
|
|
|
if (cloth->num_node)
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
else
|
|
s.write(0x08);
|
|
|
|
if (cloth->num_root) {
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
}
|
|
else
|
|
s.write(0x10);
|
|
|
|
if (cloth->num_mesh_index)
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
else
|
|
s.write(0x08);
|
|
|
|
if (cloth->num_backface_mesh_index)
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
else
|
|
s.write(0x08);
|
|
|
|
s.write(0x10);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CONSTRAINT)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x24);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x04);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
switch (block->constraint->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
s.write(0x0C);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
s.write(0x20);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x18);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
s.write(0x20);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x14);
|
|
s.write(0x14);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
s.write(0x20);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x04);
|
|
s.write(0x14);
|
|
s.write(0x14);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CONSTRAINT)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x28);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x08);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
switch (block->constraint->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
s.write(0x0C);
|
|
s.write(0x04);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
s.write(0x20);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x18);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
s.write(0x20);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x14);
|
|
s.write(0x14);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
s.write(0x20);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x04);
|
|
s.write(0x14);
|
|
s.write(0x14);
|
|
s.write(0x04);
|
|
break;
|
|
}
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x24);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
s.write(0x04);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
io_write_offset_f2_pof_add(s, 0, 0x20, &pof);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
bhs[i].block_offset = s.get_position();
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x28);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
s.write(0x08);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
io_write_offset_x_pof_add(s, 0, &pof);
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
if (motion_count) {
|
|
motion_block_node_mats = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
obj_skin_block_motion* motion = block->motion;
|
|
s.write(motion->num_node * sizeof(mat4));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
motion_block_node_name_offset = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
obj_skin_block_motion* motion = block->motion;
|
|
s.write(motion->num_node * sizeof(uint32_t));
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
|
|
if (exh.cloth_count) {
|
|
cloth_mats = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
if (cloth->mat_array)
|
|
s.write(cloth->num_mat * sizeof(mat4));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_root = s.get_position();
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write((sizeof(int32_t) + 4 * (sizeof(int32_t) + 3 * sizeof(int32_t)))
|
|
* cloth->num_root * (cloth->num_node - 1ULL));
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write((sizeof(int32_t) + 4 * (sizeof(int64_t) + 4 * sizeof(int32_t)))
|
|
* cloth->num_root* (cloth->num_node - 1ULL));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_nodes = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write((11 * sizeof(int32_t)) * cloth->num_root * (cloth->num_node - 1ULL));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_mesh_indices = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write(sizeof(uint16_t) + cloth->num_mesh_index * sizeof(uint16_t));
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
cloth_backface_mesh_indices = s.get_position();
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
s.write(sizeof(uint16_t) + cloth->num_backface_mesh_index * sizeof(uint16_t));
|
|
}
|
|
s.align_write(0x10);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sk->bone_array || sk->ex_data) {
|
|
quicksort_string_hash(strings.data(), strings.size());
|
|
string_offsets.reserve(strings.size());
|
|
for (string_hash& i : strings) {
|
|
string_offsets.push_back(s.get_position());
|
|
s.write_string_null_terminated(i.str);
|
|
}
|
|
}
|
|
s.align_write(0x10);
|
|
|
|
std::vector<string_hash> osage_names;
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
int64_t cls_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "CLS");
|
|
int64_t cns_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "CNS");
|
|
int64_t exp_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "EXP");
|
|
int64_t mot_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "MOT");
|
|
int64_t osg_offset = obj_skin_strings_get_string_offset(strings, string_offsets, "OSG");
|
|
|
|
if (ex->num_block > 0) {
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CLOTH)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_modern_write_skin_block_cloth(block->cloth,
|
|
s, strings, string_offsets, is_x, &cloth_mats, &cloth_root,
|
|
&cloth_nodes, &cloth_mesh_indices, &cloth_backface_mesh_indices);
|
|
s.position_pop();
|
|
}
|
|
|
|
int64_t constraint_type_name_offsets[4];
|
|
constraint_type_name_offsets[0] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Orientation");
|
|
constraint_type_name_offsets[1] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Direction");
|
|
constraint_type_name_offsets[2] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Position");
|
|
constraint_type_name_offsets[3] = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, "Distance");
|
|
|
|
const char** bone_name_array = ex->bone_name_array;
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_CONSTRAINT)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_modern_write_skin_block_constraint(block->constraint,
|
|
s, strings, string_offsets, bone_name_array, is_x, constraint_type_name_offsets);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_EXPRESSION)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_modern_write_skin_block_expression(block->expression,
|
|
s, strings, string_offsets, bone_name_array, is_x);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_MOTION)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_modern_write_skin_block_motion(block->motion,
|
|
s, strings, string_offsets, is_x, bone_name_array,
|
|
&motion_block_node_name_offset, &motion_block_node_mats);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type != OBJ_SKIN_BLOCK_OSAGE)
|
|
continue;
|
|
|
|
s.position_push(bhs[i].block_offset, SEEK_SET);
|
|
obj_modern_write_skin_block_osage(block->osage,
|
|
s, strings, string_offsets, is_x);
|
|
s.position_pop();
|
|
}
|
|
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
if (block->type == OBJ_SKIN_BLOCK_CLOTH) {
|
|
obj_skin_block_cloth* cloth = block->cloth;
|
|
obj_skin_strings_push_back_check(osage_names, cloth->mesh_name);
|
|
}
|
|
else if (block->type == OBJ_SKIN_BLOCK_OSAGE) {
|
|
obj_skin_block_osage* osage = block->osage;
|
|
obj_skin_strings_push_back_check_by_index(osage_names,
|
|
bone_name_array, osage->name_index);
|
|
}
|
|
}
|
|
|
|
s.position_push(exh.block_array_offset, SEEK_SET);
|
|
if (!is_x) {
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
switch (block->type) {
|
|
case OBJ_SKIN_BLOCK_CLOTH:
|
|
s.write_offset_f2(cls_offset, 0x20);
|
|
s.write_offset_f2(bhs[i].block_offset, 0x20);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT:
|
|
s.write_offset_f2(cns_offset, 0x20);
|
|
s.write_offset_f2(bhs[i].block_offset, 0x20);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_EXPRESSION:
|
|
s.write_offset_f2(exp_offset, 0x20);
|
|
s.write_offset_f2(bhs[i].block_offset, 0x20);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_MOTION:
|
|
s.write_offset_f2(mot_offset, 0x20);
|
|
s.write_offset_f2(bhs[i].block_offset, 0x20);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_OSAGE:
|
|
s.write_offset_f2(osg_offset, 0x20);
|
|
s.write_offset_f2(bhs[i].block_offset, 0x20);
|
|
break;
|
|
default:
|
|
s.write_offset_f2(0, 0);
|
|
s.write_offset_f2(0, 0);
|
|
break;
|
|
}
|
|
}
|
|
s.write_offset_f2(0, 0);
|
|
s.write_offset_f2(0, 0);
|
|
}
|
|
else {
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
switch (block->type) {
|
|
case OBJ_SKIN_BLOCK_CLOTH:
|
|
s.write_offset_x(cls_offset);
|
|
s.write_offset_x(bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT:
|
|
s.write_offset_x(cns_offset);
|
|
s.write_offset_x(bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_EXPRESSION:
|
|
s.write_offset_x(exp_offset);
|
|
s.write_offset_x(bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_MOTION:
|
|
s.write_offset_x(mot_offset);
|
|
s.write_offset_x(bhs[i].block_offset);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_OSAGE:
|
|
s.write_offset_x(osg_offset);
|
|
s.write_offset_x(bhs[i].block_offset);
|
|
break;
|
|
default:
|
|
s.write_offset_x(0);
|
|
s.write_offset_x(0);
|
|
break;
|
|
}
|
|
}
|
|
s.write_offset_x(0);
|
|
s.write_offset_x(0);
|
|
}
|
|
s.position_pop();
|
|
free_def(bhs);
|
|
}
|
|
}
|
|
|
|
if (sk->bone_array) {
|
|
s.position_push(sh.bone_name_array_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, sk->bone_array[i].name);
|
|
s.write_offset_f2(str_offset, 0x20);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < sk->num_bone; i++) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, sk->bone_array[i].name);
|
|
s.write_offset_x(str_offset);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (sk->ex_data) {
|
|
obj_skin_ex_data* ex = sk->ex_data;
|
|
|
|
if (ex->num_block > 0) {
|
|
s.position_push(exh.bone_name_array_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (string_hash& i : bone_names) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, i.c_str());
|
|
s.write_offset_f2(str_offset, 0x20);
|
|
}
|
|
else
|
|
for (string_hash& i : bone_names) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, i.c_str());
|
|
s.write_offset_x(str_offset);
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(exh.osage_node_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_node; i++) {
|
|
obj_skin_osage_node* osage_node = &ex->osage_node_array[i];
|
|
s.write_uint32_t_reverse_endianness(osage_node->name_index);
|
|
s.write_float_t_reverse_endianness(osage_node->length);
|
|
s.write_uint32_t_reverse_endianness(0);
|
|
}
|
|
s.position_pop();
|
|
|
|
s.position_push(exh.osage_sibling_info_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_sibling_info; i++) {
|
|
obj_skin_osage_sibling_info* osage_sibling_info = &ex->osage_sibling_info_array[i];
|
|
s.write_uint32_t_reverse_endianness(osage_sibling_info->name_index);
|
|
s.write_uint32_t_reverse_endianness(osage_sibling_info->sibling_name_index);
|
|
s.write_float_t_reverse_endianness(osage_sibling_info->max_distance);
|
|
}
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.write_int32_t(0);
|
|
s.position_pop();
|
|
|
|
exh.num_osage = (int32_t)osage_names.size();
|
|
exh.num_osage -= exh.cloth_count;
|
|
s.position_push(exh.osage_name_array_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (string_hash& i : osage_names) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, i.c_str());
|
|
s.write_offset_f2(str_offset, 0x20);
|
|
}
|
|
else
|
|
for (string_hash& i : osage_names) {
|
|
size_t str_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, i.c_str());
|
|
s.write_offset_x(str_offset);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
s.position_push(sh.ex_data_offset, SEEK_SET);
|
|
if (!is_x) {
|
|
s.write_int32_t_reverse_endianness(exh.num_osage);
|
|
s.write_int32_t_reverse_endianness(ex->num_osage_node);
|
|
s.write(0x04);
|
|
s.write_offset_f2(exh.osage_node_array_offset, 0x20);
|
|
s.write_offset_f2(exh.osage_name_array_offset, 0x20);
|
|
s.write_offset_f2(exh.block_array_offset, 0x20);
|
|
s.write_int32_t_reverse_endianness(exh.num_bone_name);
|
|
s.write_offset_f2(exh.bone_name_array_offset, 0x20);
|
|
s.write_offset_f2(exh.osage_sibling_info_array_offset, 0x20);
|
|
s.write_int32_t_reverse_endianness(exh.cloth_count);
|
|
s.write_offset_f2(ex->reserved[0], 0x20);
|
|
s.write_offset_f2(ex->reserved[1], 0x20);
|
|
s.write_offset_f2(ex->reserved[2], 0x20);
|
|
s.write_offset_f2(ex->reserved[3], 0x20);
|
|
s.write_offset_f2(ex->reserved[4], 0x20);
|
|
s.write_offset_f2(ex->reserved[5], 0x20);
|
|
s.write_offset_f2(ex->reserved[6], 0x20);
|
|
}
|
|
else {
|
|
s.write_int32_t_reverse_endianness(exh.num_osage);
|
|
s.write_int32_t_reverse_endianness(ex->num_osage_node);
|
|
s.write(0x08);
|
|
s.write_offset_x(exh.osage_node_array_offset);
|
|
s.write_offset_x(exh.osage_name_array_offset);
|
|
s.write_offset_x(exh.block_array_offset);
|
|
s.write_int32_t_reverse_endianness(exh.num_bone_name);
|
|
s.write_offset_x(exh.bone_name_array_offset);
|
|
s.write_offset_x(exh.osage_sibling_info_array_offset);
|
|
s.write_int32_t_reverse_endianness(exh.cloth_count);
|
|
s.write_offset_x(ex->reserved[0]);
|
|
s.write_offset_x(ex->reserved[1]);
|
|
s.write_offset_x(ex->reserved[2]);
|
|
s.write_offset_x(ex->reserved[3]);
|
|
s.write_offset_x(ex->reserved[4]);
|
|
s.write_offset_x(ex->reserved[5]);
|
|
s.write_offset_x(ex->reserved[6]);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
s.position_push(base_offset, SEEK_SET);
|
|
if (!is_x) {
|
|
s.write_offset_f2(sh.bone_id_array_offset, 0x20);
|
|
s.write_offset_f2(sh.bone_matrix_array_offset, 0x20);
|
|
s.write_offset_f2(sh.bone_name_array_offset, 0x20);
|
|
s.write_offset_f2(sh.ex_data_offset, 0x20);
|
|
s.write_int32_t_reverse_endianness(sk->num_bone);
|
|
s.write_offset_f2(sh.bone_parent_id_array_offset, 0x20);
|
|
s.write(0x0C);
|
|
}
|
|
else {
|
|
s.write_offset_x(sh.bone_id_array_offset);
|
|
s.write_offset_x(sh.bone_matrix_array_offset);
|
|
s.write_offset_x(sh.bone_name_array_offset);
|
|
s.write_offset_x(sh.ex_data_offset);
|
|
s.write_int32_t_reverse_endianness(sk->num_bone);
|
|
s.write_offset_x(sh.bone_parent_id_array_offset);
|
|
s.write(0x18);
|
|
}
|
|
s.position_pop();
|
|
|
|
oskn->enrs = e;
|
|
oskn->pof = pof;
|
|
}
|
|
|
|
static obj_skin_ex_data* obj_modern_read_skin_ex_data(prj::shared_ptr<prj::stack_allocator> alloc,
|
|
stream& s, int64_t base_offset, uint32_t header_length, bool is_x) {
|
|
obj_skin_ex_data* ex = alloc->allocate<obj_skin_ex_data>();
|
|
s.set_position(base_offset, SEEK_SET);
|
|
|
|
obj_skin_ex_data_header exh = {};
|
|
if (!is_x) {
|
|
exh.num_osage = s.read_int32_t_reverse_endianness();
|
|
ex->num_osage_node = s.read_int32_t_reverse_endianness();
|
|
s.read(0, 0x04);
|
|
exh.osage_node_array_offset = s.read_offset_f2(header_length);
|
|
exh.osage_name_array_offset = s.read_offset_f2(header_length);
|
|
exh.block_array_offset = s.read_offset_f2(header_length);
|
|
exh.num_bone_name = s.read_int32_t_reverse_endianness();
|
|
exh.bone_name_array_offset = s.read_offset_f2(header_length);
|
|
exh.osage_sibling_info_array_offset = s.read_offset_f2(header_length);
|
|
exh.cloth_count = s.read_int32_t_reverse_endianness();
|
|
ex->reserved[0] = s.read_offset_f2(header_length);
|
|
ex->reserved[1] = s.read_offset_f2(header_length);
|
|
ex->reserved[2] = s.read_offset_f2(header_length);
|
|
ex->reserved[3] = s.read_offset_f2(header_length);
|
|
ex->reserved[4] = s.read_offset_f2(header_length);
|
|
ex->reserved[5] = s.read_offset_f2(header_length);
|
|
ex->reserved[6] = s.read_offset_f2(header_length);
|
|
}
|
|
else {
|
|
exh.num_osage = s.read_int32_t_reverse_endianness();
|
|
ex->num_osage_node = s.read_int32_t_reverse_endianness();
|
|
s.read(0, 0x08);
|
|
exh.osage_node_array_offset = s.read_offset_x();
|
|
exh.osage_name_array_offset = s.read_offset_x();
|
|
exh.block_array_offset = s.read_offset_x();
|
|
exh.num_bone_name = s.read_int32_t_reverse_endianness();
|
|
exh.bone_name_array_offset = s.read_offset_x();
|
|
exh.osage_sibling_info_array_offset = s.read_offset_x();
|
|
exh.cloth_count = s.read_int32_t_reverse_endianness();
|
|
ex->reserved[0] = s.read_offset_x();
|
|
ex->reserved[1] = s.read_offset_x();
|
|
ex->reserved[2] = s.read_offset_x();
|
|
ex->reserved[3] = s.read_offset_x();
|
|
ex->reserved[4] = s.read_offset_x();
|
|
ex->reserved[5] = s.read_offset_x();
|
|
ex->reserved[6] = s.read_offset_x();
|
|
}
|
|
|
|
ex->bone_name_array = 0;
|
|
ex->num_bone_name = 0;
|
|
if (!exh.bone_name_array_offset) {
|
|
return ex;
|
|
}
|
|
|
|
int32_t num_bone_name = exh.num_bone_name;
|
|
|
|
const char** bone_name_array = alloc->allocate<const char*>(num_bone_name);
|
|
ex->bone_name_array = bone_name_array;
|
|
ex->num_bone_name = num_bone_name;
|
|
|
|
s.set_position(exh.bone_name_array_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < num_bone_name; i++) {
|
|
int64_t string_offset = s.read_offset_f2(header_length);
|
|
if (string_offset)
|
|
bone_name_array[i] = obj_read_utf8_string_null_terminated_offset(alloc, s, string_offset);
|
|
else
|
|
bone_name_array[i] = 0;
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < num_bone_name; i++) {
|
|
int64_t string_offset = s.read_offset_x();
|
|
if (string_offset)
|
|
bone_name_array[i] = obj_read_utf8_string_null_terminated_offset(alloc, s, string_offset);
|
|
else
|
|
bone_name_array[i] = 0;
|
|
}
|
|
|
|
if (exh.osage_node_array_offset) {
|
|
ex->osage_node_array = alloc->allocate<obj_skin_osage_node>(ex->num_osage_node);
|
|
|
|
s.set_position(exh.osage_node_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_node; i++) {
|
|
obj_skin_osage_node* osage_node = &ex->osage_node_array[i];
|
|
|
|
osage_node->name_index = s.read_uint32_t_reverse_endianness();
|
|
osage_node->length = s.read_float_t_reverse_endianness();
|
|
s.read(0, 0x04);
|
|
}
|
|
}
|
|
|
|
if (exh.block_array_offset) {
|
|
ex->num_block = 0;
|
|
s.set_position(exh.block_array_offset, SEEK_SET);
|
|
if (!is_x)
|
|
while (s.read_uint32_t()) {
|
|
s.read(0, 0x04);
|
|
ex->num_block++;
|
|
}
|
|
else
|
|
while (s.read_int64_t()) {
|
|
s.read(0, 0x08);
|
|
ex->num_block++;
|
|
}
|
|
|
|
obj_skin_block_header* bhs = force_malloc<obj_skin_block_header>(ex->num_block);
|
|
s.set_position(exh.block_array_offset, SEEK_SET);
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
bhs[i].block_signature_offset = s.read_offset_f2(header_length);
|
|
bhs[i].block_offset = s.read_offset_f2(header_length);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
bhs[i].block_signature_offset = s.read_offset_x();
|
|
bhs[i].block_offset = s.read_offset_x();
|
|
}
|
|
|
|
ex->block_array = alloc->allocate<obj_skin_block>(ex->num_block);
|
|
for (int32_t i = 0; i < ex->num_block; i++) {
|
|
obj_skin_block* block = &ex->block_array[i];
|
|
|
|
std::string block_signature = s.read_string_null_terminated_offset(
|
|
bhs[i].block_signature_offset);
|
|
if (block_signature.size() != 3)
|
|
continue;
|
|
|
|
uint32_t signature = load_reverse_endianness_uint32_t(block_signature.c_str());
|
|
switch (signature) {
|
|
case 'CLS\0':
|
|
block->type = OBJ_SKIN_BLOCK_CLOTH;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->cloth = obj_modern_read_skin_block_cloth(
|
|
alloc, s, header_length, bone_name_array, is_x);
|
|
break;
|
|
case 'CNS\0':
|
|
block->type = OBJ_SKIN_BLOCK_CONSTRAINT;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->constraint = obj_modern_read_skin_block_constraint(
|
|
alloc, s, header_length, bone_name_array, is_x);
|
|
break;
|
|
case 'EXP\0':
|
|
block->type = OBJ_SKIN_BLOCK_EXPRESSION;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->expression = obj_modern_read_skin_block_expression(
|
|
alloc, s, header_length, bone_name_array, is_x);
|
|
break;
|
|
case 'MOT\0':
|
|
block->type = OBJ_SKIN_BLOCK_MOTION;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->motion = obj_modern_read_skin_block_motion(
|
|
alloc, s, header_length, bone_name_array, is_x);
|
|
break;
|
|
case 'OSG\0':
|
|
block->type = OBJ_SKIN_BLOCK_OSAGE;
|
|
s.set_position(bhs[i].block_offset, SEEK_SET);
|
|
block->osage = obj_modern_read_skin_block_osage(
|
|
alloc, s, header_length, bone_name_array, is_x);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
free_def(bhs);
|
|
}
|
|
|
|
if (exh.osage_sibling_info_array_offset) {
|
|
ex->num_osage_sibling_info = 0;
|
|
s.set_position(exh.osage_sibling_info_array_offset, SEEK_SET);
|
|
while (s.read_uint32_t()) {
|
|
s.read(0, 0x08);
|
|
ex->num_osage_sibling_info++;
|
|
}
|
|
|
|
obj_skin_osage_sibling_info* osi = alloc->allocate<
|
|
obj_skin_osage_sibling_info>(ex->num_osage_sibling_info);
|
|
ex->osage_sibling_info_array = osi;
|
|
s.set_position(exh.osage_sibling_info_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < ex->num_osage_sibling_info; i++, osi++) {
|
|
osi->name_index = s.read_uint32_t_reverse_endianness();
|
|
osi->sibling_name_index = s.read_uint32_t_reverse_endianness();
|
|
osi->max_distance = s.read_float_t_reverse_endianness();
|
|
}
|
|
}
|
|
return ex;
|
|
}
|
|
|
|
static obj_skin_block_cloth* obj_modern_read_skin_block_cloth(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
obj_skin_block_cloth* cls = alloc->allocate<obj_skin_block_cloth>();
|
|
int64_t mesh_name_offset = s.read_offset(header_length, is_x);
|
|
int64_t backface_mesh_name_offset = s.read_offset(header_length, is_x);
|
|
cls->field_8 = s.read_uint32_t_reverse_endianness();
|
|
cls->num_root = s.read_int32_t_reverse_endianness();
|
|
cls->num_node = s.read_int32_t_reverse_endianness();
|
|
cls->loop = s.read_uint32_t_reverse_endianness();
|
|
int64_t mat_array_offset = s.read_offset(header_length, is_x);
|
|
int64_t root_array_offset = s.read_offset(header_length, is_x);
|
|
int64_t node_array_offset = s.read_offset(header_length, is_x);
|
|
int64_t mesh_index_array_offset = s.read_offset(header_length, is_x);
|
|
int64_t backface_mesh_index_array_offset = s.read_offset(header_length, is_x);
|
|
int64_t skp_root_offset = s.read_uint32_t_reverse_endianness();
|
|
cls->reserved = s.read_uint32_t_reverse_endianness();
|
|
|
|
cls->mesh_name = obj_read_utf8_string_null_terminated_offset(alloc, s, mesh_name_offset);
|
|
cls->backface_mesh_name = obj_read_utf8_string_null_terminated_offset(alloc, s, backface_mesh_name_offset);
|
|
|
|
if (mat_array_offset) {
|
|
int32_t max_matrix_index = -1;
|
|
|
|
s.position_push(root_array_offset, SEEK_SET);
|
|
int32_t num_root = cls->num_root;
|
|
for (int32_t i = 0; i < num_root; i++) {
|
|
s.read(0x28);
|
|
for (uint32_t j = 0; j < 4; j++) {
|
|
uint32_t name_offset = s.read_uint32_t_reverse_endianness();
|
|
int32_t matrix_index = s.read_int32_t_reverse_endianness();
|
|
if (name_offset&& max_matrix_index < matrix_index)
|
|
max_matrix_index = matrix_index;
|
|
}
|
|
}
|
|
s.position_pop();
|
|
|
|
int32_t num_mat = max_matrix_index > -1 ? max_matrix_index + 1 : 0;
|
|
cls->num_mat = num_mat;
|
|
cls->mat_array = alloc->allocate<mat4>(num_mat);
|
|
s.position_push(mat_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < num_mat; i++) {
|
|
mat4& mat = cls->mat_array[i];
|
|
mat.row0.x = s.read_float_t_reverse_endianness();
|
|
mat.row1.x = s.read_float_t_reverse_endianness();
|
|
mat.row2.x = s.read_float_t_reverse_endianness();
|
|
mat.row3.x = s.read_float_t_reverse_endianness();
|
|
mat.row0.y = s.read_float_t_reverse_endianness();
|
|
mat.row1.y = s.read_float_t_reverse_endianness();
|
|
mat.row2.y = s.read_float_t_reverse_endianness();
|
|
mat.row3.y = s.read_float_t_reverse_endianness();
|
|
mat.row0.z = s.read_float_t_reverse_endianness();
|
|
mat.row1.z = s.read_float_t_reverse_endianness();
|
|
mat.row2.z = s.read_float_t_reverse_endianness();
|
|
mat.row3.z = s.read_float_t_reverse_endianness();
|
|
mat.row0.w = s.read_float_t_reverse_endianness();
|
|
mat.row1.w = s.read_float_t_reverse_endianness();
|
|
mat.row2.w = s.read_float_t_reverse_endianness();
|
|
mat.row3.w = s.read_float_t_reverse_endianness();
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (root_array_offset) {
|
|
s.position_push(root_array_offset, SEEK_SET);
|
|
int32_t num_root = cls->num_root;
|
|
obj_skin_block_cloth_root* root_array = alloc->allocate<obj_skin_block_cloth_root>(num_root);
|
|
cls->root_array = root_array;
|
|
for (int32_t i = 0; i < num_root; i++)
|
|
obj_modern_read_skin_block_cloth_root(&root_array[i],
|
|
alloc, s, header_length, str, is_x);
|
|
s.position_pop();
|
|
}
|
|
|
|
if (node_array_offset) {
|
|
s.position_push(node_array_offset, SEEK_SET);
|
|
int32_t num_root = cls->num_root;
|
|
int32_t num_node = cls->num_node;
|
|
obj_skin_block_cloth_node* node_array = alloc->allocate<obj_skin_block_cloth_node>(
|
|
num_root * (cls->num_node - 1ULL));
|
|
cls->node_array = node_array;
|
|
for (int32_t i = 0; i < num_node - 1; i++)
|
|
for (int32_t j = 0; j < num_root; j++) {
|
|
obj_skin_block_cloth_node* f = &node_array[(size_t)i * num_root + j];
|
|
f->flags = s.read_uint32_t_reverse_endianness();
|
|
f->pos.x = s.read_float_t_reverse_endianness();
|
|
f->pos.y = s.read_float_t_reverse_endianness();
|
|
f->pos.z = s.read_float_t_reverse_endianness();
|
|
f->delta_pos.x = s.read_float_t_reverse_endianness();
|
|
f->delta_pos.y = s.read_float_t_reverse_endianness();
|
|
f->delta_pos.z = s.read_float_t_reverse_endianness();
|
|
f->dist_top = s.read_float_t_reverse_endianness();
|
|
f->dist_bottom = s.read_float_t_reverse_endianness();
|
|
f->dist_right = s.read_float_t_reverse_endianness();
|
|
f->dist_left = s.read_float_t_reverse_endianness();
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (mesh_index_array_offset) {
|
|
s.position_push(mesh_index_array_offset, SEEK_SET);
|
|
cls->num_mesh_index = s.read_uint16_t_reverse_endianness();
|
|
cls->mesh_index_array = alloc->allocate<uint16_t>(cls->num_mesh_index);
|
|
s.read(cls->mesh_index_array, cls->num_mesh_index * sizeof(uint16_t));
|
|
if (s.big_endian) {
|
|
uint16_t* mesh_index_array = cls->mesh_index_array;
|
|
for (int32_t i = cls->num_mesh_index; i > 0; i--, mesh_index_array++)
|
|
*mesh_index_array = reverse_endianness_uint16_t(*mesh_index_array);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (backface_mesh_index_array_offset) {
|
|
s.position_push(backface_mesh_index_array_offset, SEEK_SET);
|
|
cls->num_backface_mesh_index = s.read_uint16_t_reverse_endianness();
|
|
cls->backface_mesh_index_array = alloc->allocate<uint16_t>(cls->num_backface_mesh_index);
|
|
s.read(cls->backface_mesh_index_array, cls->num_backface_mesh_index * sizeof(uint16_t));
|
|
if (s.big_endian) {
|
|
uint16_t* backface_mesh_index_array = cls->backface_mesh_index_array;
|
|
for (int32_t i = cls->num_mesh_index; i > 0; i--, backface_mesh_index_array++)
|
|
*backface_mesh_index_array = reverse_endianness_uint16_t(*backface_mesh_index_array);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
return cls;
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_cloth(obj_skin_block_cloth* cls,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x,
|
|
int64_t* mat_array_offset, int64_t* root_array_offset, int64_t* node_array_offset,
|
|
int64_t* mesh_index_array_offset, int64_t* backface_mesh_index_array_offset) {
|
|
int64_t mesh_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, cls->mesh_name);
|
|
int64_t backface_mesh_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, cls->backface_mesh_name);
|
|
|
|
if (!is_x) {
|
|
s.write_offset_f2(mesh_name_offset, 0x20);
|
|
s.write_offset_f2(backface_mesh_name_offset, 0x20);
|
|
s.write_int32_t_reverse_endianness(cls->field_8);
|
|
s.write_int32_t_reverse_endianness(cls->num_root);
|
|
s.write_int32_t_reverse_endianness(cls->num_node);
|
|
s.write_int32_t_reverse_endianness(cls->loop);
|
|
s.write_offset_f2(cls->num_node ? *mat_array_offset : 0, 0x20);
|
|
s.write_offset_f2(cls->root_array ? *root_array_offset : 0, 0x20);
|
|
s.write_offset_f2(cls->node_array ? *node_array_offset : 0, 0x20);
|
|
s.write_offset_f2(cls->mesh_index_array ? *mesh_index_array_offset : 0, 0x20);
|
|
s.write_offset_f2(cls->backface_mesh_index_array ? *backface_mesh_index_array_offset : 0, 0x20);
|
|
s.write_uint32_t_reverse_endianness(0);
|
|
s.write_uint32_t_reverse_endianness(cls->reserved);
|
|
}
|
|
else {
|
|
s.write_offset_x(mesh_name_offset);
|
|
s.write_offset_x(backface_mesh_name_offset);
|
|
s.write_int32_t_reverse_endianness(cls->field_8);
|
|
s.write_int32_t_reverse_endianness(cls->num_root);
|
|
s.write_int32_t_reverse_endianness(cls->num_node);
|
|
s.write_int32_t_reverse_endianness(cls->loop);
|
|
s.write_offset_x(cls->num_node ? *mat_array_offset : 0);
|
|
s.write_offset_x(cls->root_array ? *root_array_offset : 0);
|
|
s.write_offset_x(cls->node_array ? *node_array_offset : 0);
|
|
s.write_offset_x(cls->mesh_index_array ? *mesh_index_array_offset : 0);
|
|
s.write_offset_x(cls->backface_mesh_index_array ? *backface_mesh_index_array_offset : 0);
|
|
s.write_uint32_t_reverse_endianness(0);
|
|
s.write_uint32_t_reverse_endianness(cls->reserved);
|
|
}
|
|
|
|
if (cls->mat_array) {
|
|
s.position_push(*mat_array_offset, SEEK_SET);
|
|
int32_t num_mat = cls->num_mat;
|
|
for (int32_t i = 0; i < num_mat; i++) {
|
|
mat4& mat = cls->mat_array[i];
|
|
s.write_float_t_reverse_endianness(mat.row0.x);
|
|
s.write_float_t_reverse_endianness(mat.row1.x);
|
|
s.write_float_t_reverse_endianness(mat.row2.x);
|
|
s.write_float_t_reverse_endianness(mat.row3.x);
|
|
s.write_float_t_reverse_endianness(mat.row0.y);
|
|
s.write_float_t_reverse_endianness(mat.row1.y);
|
|
s.write_float_t_reverse_endianness(mat.row2.y);
|
|
s.write_float_t_reverse_endianness(mat.row3.y);
|
|
s.write_float_t_reverse_endianness(mat.row0.z);
|
|
s.write_float_t_reverse_endianness(mat.row1.z);
|
|
s.write_float_t_reverse_endianness(mat.row2.z);
|
|
s.write_float_t_reverse_endianness(mat.row3.z);
|
|
s.write_float_t_reverse_endianness(mat.row0.w);
|
|
s.write_float_t_reverse_endianness(mat.row1.w);
|
|
s.write_float_t_reverse_endianness(mat.row2.w);
|
|
s.write_float_t_reverse_endianness(mat.row3.w);
|
|
*mat_array_offset += sizeof(mat4);
|
|
}
|
|
s.position_pop();
|
|
}
|
|
|
|
if (cls->root_array) {
|
|
s.position_push(*root_array_offset, SEEK_SET);
|
|
int32_t num_root = cls->num_root;
|
|
int32_t num_node = cls->num_node;
|
|
obj_skin_block_cloth_root* root_array = cls->root_array;
|
|
for (int32_t i = 0; i < num_root; i++)
|
|
obj_modern_write_skin_block_cloth_root(&root_array[i],
|
|
s, strings, string_offsets, is_x);
|
|
s.position_pop();
|
|
if (!is_x)
|
|
*root_array_offset += (sizeof(int32_t) + 4 * (sizeof(int64_t) + 4 * sizeof(int32_t)))
|
|
* cls->num_root * (cls->num_node - 1ULL);
|
|
else
|
|
*root_array_offset += (sizeof(int32_t) + 4 * (sizeof(int32_t) + 3 * sizeof(int32_t)))
|
|
* cls->num_root * (cls->num_node - 1ULL);
|
|
}
|
|
|
|
if (cls->node_array) {
|
|
s.position_push(*node_array_offset, SEEK_SET);
|
|
int32_t num_root = cls->num_root;
|
|
int32_t num_node = cls->num_node;
|
|
obj_skin_block_cloth_node* node_array = cls->node_array;
|
|
for (int32_t i = 0; i < num_node - 1; i++)
|
|
for (int32_t j = 0; j < num_root; j++) {
|
|
obj_skin_block_cloth_node* f = &node_array[(size_t)i * num_root + j];
|
|
s.write_uint32_t_reverse_endianness(f->flags);
|
|
s.write_float_t_reverse_endianness(f->pos.x);
|
|
s.write_float_t_reverse_endianness(f->pos.y);
|
|
s.write_float_t_reverse_endianness(f->pos.z);
|
|
s.write_float_t_reverse_endianness(f->delta_pos.x);
|
|
s.write_float_t_reverse_endianness(f->delta_pos.y);
|
|
s.write_float_t_reverse_endianness(f->delta_pos.z);
|
|
s.write_float_t_reverse_endianness(f->dist_top);
|
|
s.write_float_t_reverse_endianness(f->dist_bottom);
|
|
s.write_float_t_reverse_endianness(f->dist_right);
|
|
s.write_float_t_reverse_endianness(f->dist_left);
|
|
}
|
|
s.position_pop();
|
|
*node_array_offset += (11 * sizeof(int32_t)) * cls->num_root * (cls->num_node - 1ULL);
|
|
}
|
|
|
|
if (cls->mesh_index_array) {
|
|
s.position_push(*mesh_index_array_offset, SEEK_SET);
|
|
s.write_uint16_t_reverse_endianness((uint16_t)cls->num_mesh_index);
|
|
uint16_t* mesh_index_array = cls->mesh_index_array;
|
|
for (int32_t i = cls->num_mesh_index; i > 0; i--, mesh_index_array++)
|
|
s.write_uint16_t_reverse_endianness(*mesh_index_array);
|
|
s.position_pop();
|
|
*mesh_index_array_offset += sizeof(uint16_t) + cls->num_mesh_index * sizeof(uint16_t);
|
|
}
|
|
|
|
if (cls->backface_mesh_index_array) {
|
|
s.position_push(*backface_mesh_index_array_offset, SEEK_SET);
|
|
s.write_uint16_t_reverse_endianness((uint16_t)cls->num_backface_mesh_index);
|
|
uint16_t* backface_mesh_index_array = cls->backface_mesh_index_array;
|
|
for (int32_t i = cls->num_backface_mesh_index; i > 0; i--, backface_mesh_index_array++)
|
|
s.write_uint16_t_reverse_endianness(*backface_mesh_index_array);
|
|
s.position_pop();
|
|
*backface_mesh_index_array_offset += sizeof(uint16_t) + cls->num_backface_mesh_index * sizeof(uint16_t);
|
|
}
|
|
}
|
|
|
|
static void obj_modern_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
cloth_root->pos.x = s.read_float_t();
|
|
cloth_root->pos.y = s.read_float_t();
|
|
cloth_root->pos.z = s.read_float_t();
|
|
cloth_root->normal.x = s.read_float_t();
|
|
cloth_root->normal.y = s.read_float_t();
|
|
cloth_root->normal.z = s.read_float_t();
|
|
cloth_root->field_18 = s.read_float_t();
|
|
cloth_root->field_1C = s.read_int32_t();
|
|
cloth_root->field_20 = s.read_int32_t();
|
|
cloth_root->field_24 = s.read_int32_t();
|
|
|
|
for (uint32_t i = 0; i < 4; i++)
|
|
obj_modern_read_skin_block_cloth_root_bone_weight(&cloth_root->bone_weights[i],
|
|
alloc, s, header_length, str, is_x);
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x) {
|
|
s.write_float_t(cloth_root->pos.x);
|
|
s.write_float_t(cloth_root->pos.y);
|
|
s.write_float_t(cloth_root->pos.z);
|
|
s.write_float_t(cloth_root->normal.x);
|
|
s.write_float_t(cloth_root->normal.y);
|
|
s.write_float_t(cloth_root->normal.z);
|
|
s.write_float_t(cloth_root->field_18);
|
|
s.write_int32_t(cloth_root->field_1C);
|
|
s.write_int32_t(cloth_root->field_20);
|
|
s.write_int32_t(cloth_root->field_24);
|
|
|
|
for (uint32_t j = 0; j < 4; j++)
|
|
obj_modern_write_skin_block_cloth_root_bone_weight(&cloth_root->bone_weights[j],
|
|
s, strings, string_offsets, is_x);
|
|
}
|
|
|
|
static void obj_modern_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
int64_t bone_name_offset = s.read_offset(header_length, is_x);
|
|
bone_weight->bone_name = obj_read_utf8_string_null_terminated_offset(alloc, s, bone_name_offset);
|
|
bone_weight->weight = s.read_float_t_reverse_endianness();
|
|
bone_weight->matrix_index = s.read_uint32_t_reverse_endianness();
|
|
bone_weight->reserved = s.read_uint32_t_reverse_endianness();
|
|
if (is_x)
|
|
s.read(0, 0x04);
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x) {
|
|
int64_t bone_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, bone_weight->bone_name);
|
|
s.write_offset(bone_name_offset, 0x20, is_x);
|
|
s.write_float_t_reverse_endianness(bone_weight->weight);
|
|
s.write_uint32_t_reverse_endianness(bone_weight->matrix_index);
|
|
s.write_uint32_t_reverse_endianness(bone_weight->reserved);
|
|
if (is_x)
|
|
s.write(0x04);
|
|
}
|
|
|
|
static obj_skin_block_constraint* obj_modern_read_skin_block_constraint(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
obj_skin_block_constraint* cns = alloc->allocate<obj_skin_block_constraint>();
|
|
obj_modern_read_skin_block_node(&cns->node, alloc, s, header_length, str, is_x);
|
|
|
|
int64_t type_offset = s.read_offset(header_length, is_x);
|
|
char* type = s.read_utf8_string_null_terminated_offset(type_offset);
|
|
|
|
int64_t name_offset = s.read_offset(header_length, is_x);
|
|
char* name = s.read_utf8_string_null_terminated_offset(name_offset);
|
|
|
|
cns->name_index = 0;
|
|
if (name)
|
|
for (const char** i = str; *i; i++)
|
|
if (!str_utils_compare(name, *i)) {
|
|
cns->name_index = 0x8000 | (int32_t)(i - str);
|
|
break;
|
|
}
|
|
free_def(name);
|
|
|
|
cns->coupling = (obj_skin_block_constraint_coupling)s.read_uint32_t_reverse_endianness();
|
|
|
|
int64_t source_node_name_offset = s.read_offset(header_length, is_x);
|
|
cns->source_node_name = obj_read_utf8_string_null_terminated_offset(alloc, s, source_node_name_offset);
|
|
|
|
if (!type) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_NONE;
|
|
cns->data = 0;
|
|
}
|
|
else if (!str_utils_compare(type, "Orientation")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION;
|
|
|
|
obj_skin_block_constraint_orientation* orientation
|
|
= alloc->allocate<obj_skin_block_constraint_orientation>();
|
|
orientation->offset.x = s.read_float_t_reverse_endianness();
|
|
orientation->offset.y = s.read_float_t_reverse_endianness();
|
|
orientation->offset.z = s.read_float_t_reverse_endianness();
|
|
cns->orientation = orientation;
|
|
}
|
|
else if (!str_utils_compare(type, "Direction")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION;
|
|
|
|
obj_skin_block_constraint_direction* direction
|
|
= alloc->allocate<obj_skin_block_constraint_direction>();
|
|
obj_modern_read_skin_block_constraint_up_vector(&direction->up_vector,
|
|
alloc, s, header_length, str, is_x);
|
|
direction->align_axis.x = s.read_float_t_reverse_endianness();
|
|
direction->align_axis.y = s.read_float_t_reverse_endianness();
|
|
direction->align_axis.z = s.read_float_t_reverse_endianness();
|
|
direction->target_offset.x = s.read_float_t_reverse_endianness();
|
|
direction->target_offset.y = s.read_float_t_reverse_endianness();
|
|
direction->target_offset.z = s.read_float_t_reverse_endianness();
|
|
cns->direction = direction;
|
|
}
|
|
else if (!str_utils_compare(type, "Position")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_POSITION;
|
|
|
|
obj_skin_block_constraint_position* position
|
|
= alloc->allocate<obj_skin_block_constraint_position>();
|
|
obj_modern_read_skin_block_constraint_up_vector(&position->up_vector,
|
|
alloc, s, header_length, str, is_x);
|
|
obj_modern_read_skin_block_constraint_attach_point(&position->constrained_object,
|
|
s, header_length, str, is_x);
|
|
obj_modern_read_skin_block_constraint_attach_point(&position->constraining_object,
|
|
s, header_length, str, is_x);
|
|
cns->position = position;
|
|
}
|
|
else if (!str_utils_compare(type, "Distance")) {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE;
|
|
|
|
obj_skin_block_constraint_distance* distance
|
|
= alloc->allocate<obj_skin_block_constraint_distance>();
|
|
obj_modern_read_skin_block_constraint_up_vector(&distance->up_vector,
|
|
alloc, s, header_length, str, is_x);
|
|
distance->distance = s.read_float_t_reverse_endianness();
|
|
obj_modern_read_skin_block_constraint_attach_point(&distance->constrained_object,
|
|
s, header_length, str, is_x);
|
|
obj_modern_read_skin_block_constraint_attach_point(&distance->constraining_object,
|
|
s, header_length, str, is_x);
|
|
cns->distance = distance;
|
|
}
|
|
else {
|
|
cns->type = OBJ_SKIN_BLOCK_CONSTRAINT_NONE;
|
|
cns->data = 0;
|
|
}
|
|
free_def(type);
|
|
return cns;
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_constraint(obj_skin_block_constraint* cns,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, const char** bone_name_array,
|
|
bool is_x, int64_t* offsets) {
|
|
obj_modern_write_skin_block_node(&cns->node, s, strings, string_offsets, is_x);
|
|
|
|
int64_t type_offset = 0;
|
|
switch (cns->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
type_offset = offsets[0];
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
type_offset = offsets[1];
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
type_offset = offsets[2];
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
type_offset = offsets[3];
|
|
break;
|
|
}
|
|
s.write_offset(type_offset, 0x20, is_x);
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset_by_index(strings,
|
|
string_offsets, bone_name_array, cns->name_index);
|
|
s.write_offset(name_offset, 0x20, is_x);
|
|
|
|
s.write_int32_t_reverse_endianness(cns->coupling);
|
|
|
|
int64_t source_node_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, cns->source_node_name);
|
|
s.write_offset(source_node_name_offset, 0x20, is_x);
|
|
|
|
switch (cns->type) {
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_ORIENTATION:
|
|
s.write_float_t_reverse_endianness(cns->orientation->offset.x);
|
|
s.write_float_t_reverse_endianness(cns->orientation->offset.y);
|
|
s.write_float_t_reverse_endianness(cns->orientation->offset.z);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DIRECTION:
|
|
obj_modern_write_skin_block_constraint_up_vector(&cns->direction->up_vector,
|
|
s, strings, string_offsets, is_x);
|
|
s.write_float_t_reverse_endianness(cns->direction->align_axis.x);
|
|
s.write_float_t_reverse_endianness(cns->direction->align_axis.y);
|
|
s.write_float_t_reverse_endianness(cns->direction->align_axis.z);
|
|
s.write_float_t_reverse_endianness(cns->direction->target_offset.x);
|
|
s.write_float_t_reverse_endianness(cns->direction->target_offset.y);
|
|
s.write_float_t_reverse_endianness(cns->direction->target_offset.z);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_POSITION:
|
|
obj_modern_write_skin_block_constraint_up_vector(&cns->position->up_vector,
|
|
s, strings, string_offsets, is_x);
|
|
obj_modern_write_skin_block_constraint_attach_point(&cns->position->constrained_object,
|
|
s, strings, string_offsets, is_x);
|
|
obj_modern_write_skin_block_constraint_attach_point(&cns->position->constraining_object,
|
|
s, strings, string_offsets, is_x);
|
|
break;
|
|
case OBJ_SKIN_BLOCK_CONSTRAINT_DISTANCE:
|
|
obj_modern_write_skin_block_constraint_up_vector(&cns->distance->up_vector,
|
|
s, strings, string_offsets, is_x);
|
|
s.write_float_t_reverse_endianness(cns->distance->distance);
|
|
obj_modern_write_skin_block_constraint_attach_point(&cns->distance->constrained_object,
|
|
s, strings, string_offsets, is_x);
|
|
obj_modern_write_skin_block_constraint_attach_point(&cns->distance->constraining_object,
|
|
s, strings, string_offsets, is_x);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void obj_modern_read_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
attach_point->affected_by_orientation = s.read_uint32_t_reverse_endianness() != 0;
|
|
attach_point->affected_by_scaling = s.read_uint32_t_reverse_endianness() != 0;
|
|
attach_point->offset.x = s.read_float_t_reverse_endianness();
|
|
attach_point->offset.y = s.read_float_t_reverse_endianness();
|
|
attach_point->offset.z = s.read_float_t_reverse_endianness();
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x) {
|
|
s.write_int32_t_reverse_endianness(attach_point->affected_by_orientation ? 1 : 0);
|
|
s.write_int32_t_reverse_endianness(attach_point->affected_by_scaling ? 1 : 0);
|
|
s.write_float_t_reverse_endianness(attach_point->offset.x);
|
|
s.write_float_t_reverse_endianness(attach_point->offset.y);
|
|
s.write_float_t_reverse_endianness(attach_point->offset.z);
|
|
}
|
|
|
|
static void obj_modern_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
up_vector->active = !!s.read_uint32_t_reverse_endianness();
|
|
up_vector->roll = s.read_float_t_reverse_endianness();
|
|
up_vector->affected_axis.x = s.read_float_t_reverse_endianness();
|
|
up_vector->affected_axis.y = s.read_float_t_reverse_endianness();
|
|
up_vector->affected_axis.z = s.read_float_t_reverse_endianness();
|
|
up_vector->point_at.x = s.read_float_t_reverse_endianness();
|
|
up_vector->point_at.y = s.read_float_t_reverse_endianness();
|
|
up_vector->point_at.z = s.read_float_t_reverse_endianness();
|
|
|
|
int64_t name_offset = s.read_offset(header_length, is_x);
|
|
up_vector->name = obj_read_utf8_string_null_terminated_offset(alloc, s, name_offset);
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x) {
|
|
s.write_int32_t_reverse_endianness(up_vector->active ? 1 : 0);
|
|
s.write_float_t_reverse_endianness(up_vector->roll);
|
|
s.write_float_t_reverse_endianness(up_vector->affected_axis.x);
|
|
s.write_float_t_reverse_endianness(up_vector->affected_axis.y);
|
|
s.write_float_t_reverse_endianness(up_vector->affected_axis.z);
|
|
s.write_float_t_reverse_endianness(up_vector->point_at.x);
|
|
s.write_float_t_reverse_endianness(up_vector->point_at.y);
|
|
s.write_float_t_reverse_endianness(up_vector->point_at.z);
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, up_vector->name);
|
|
s.write_offset(name_offset, 0x20, is_x);
|
|
}
|
|
|
|
static obj_skin_block_expression* obj_modern_read_skin_block_expression(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
obj_skin_block_expression* exp = alloc->allocate<obj_skin_block_expression>();
|
|
obj_modern_read_skin_block_node(&exp->node, alloc, s, header_length, str, is_x);
|
|
|
|
int64_t name_offset = s.read_offset(header_length, is_x);
|
|
char* name = s.read_utf8_string_null_terminated_offset(name_offset);
|
|
|
|
exp->name_index = 0;
|
|
if (name)
|
|
for (const char** i = str; *i; i++)
|
|
if (!str_utils_compare(name, *i)) {
|
|
exp->name_index = 0x8000 | (int32_t)(i - str);
|
|
break;
|
|
}
|
|
free_def(name);
|
|
|
|
int32_t num_expression = s.read_int32_t_reverse_endianness();
|
|
num_expression = min_def(num_expression, 9);
|
|
exp->num_expression = num_expression;
|
|
if (!is_x)
|
|
for (int32_t i = 0; i < num_expression; i++) {
|
|
int64_t expression_offset = s.read_offset_f2(header_length);
|
|
if (expression_offset)
|
|
exp->expression_array[i]
|
|
= obj_read_utf8_string_null_terminated_offset(alloc, s, expression_offset);
|
|
}
|
|
else
|
|
for (int32_t i = 0; i < num_expression; i++) {
|
|
int64_t expression_offset = s.read_offset_x();
|
|
if (expression_offset)
|
|
exp->expression_array[i]
|
|
= obj_read_utf8_string_null_terminated_offset(alloc, s, expression_offset);
|
|
}
|
|
|
|
for (int32_t i = num_expression; i < 9; i++)
|
|
exp->expression_array[i] = 0;
|
|
return exp;
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_expression(obj_skin_block_expression* exp,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets,
|
|
const char** bone_name_array, bool is_x) {
|
|
obj_modern_write_skin_block_node(&exp->node, s, strings, string_offsets, is_x);
|
|
|
|
int64_t name_offset = obj_skin_strings_get_string_offset_by_index(strings,
|
|
string_offsets, bone_name_array, exp->name_index);
|
|
s.write_offset(name_offset, 0x20, is_x);
|
|
|
|
int32_t num_expression = exp->num_expression;
|
|
num_expression = min_def(num_expression, 9);
|
|
s.write_int32_t_reverse_endianness(num_expression);
|
|
if (!is_x) {
|
|
for (int32_t i = 0; i < num_expression; i++) {
|
|
int64_t expression_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, exp->expression_array[i]);
|
|
s.write_offset_f2((int32_t)expression_offset, 0x20);
|
|
}
|
|
|
|
for (int32_t i = num_expression; i < 9; i++)
|
|
s.write_offset_f2(0, 0x20);
|
|
}
|
|
else {
|
|
for (int32_t i = 0; i < num_expression; i++) {
|
|
int64_t expression_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, exp->expression_array[i]);
|
|
s.write_offset_x(expression_offset);
|
|
}
|
|
|
|
for (int32_t i = num_expression; i < 9; i++)
|
|
s.write_offset_x(0);
|
|
}
|
|
}
|
|
|
|
static obj_skin_block_motion* obj_modern_read_skin_block_motion(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
obj_skin_block_motion* mot = alloc->allocate<obj_skin_block_motion>();
|
|
obj_modern_read_skin_block_node(&mot->node, alloc, s, header_length, str, is_x);
|
|
|
|
int64_t name_offset = s.read_offset(header_length, is_x);
|
|
mot->num_node = s.read_int32_t_reverse_endianness();
|
|
int64_t bone_name_array_offset = s.read_offset(header_length, is_x);
|
|
int64_t bone_matrix_array_offset = s.read_offset(header_length, is_x);
|
|
|
|
if (!is_x) {
|
|
mot->name_index = 0;
|
|
char* name = s.read_utf8_string_null_terminated_offset(name_offset);
|
|
if (name)
|
|
for (const char** i = str; *i; i++)
|
|
if (!str_utils_compare(name, *i)) {
|
|
mot->name_index = 0x8000 | (int32_t)(i - str);
|
|
break;
|
|
}
|
|
free_def(name);
|
|
}
|
|
else
|
|
mot->name = obj_read_utf8_string_null_terminated_offset(alloc, s, name_offset);
|
|
|
|
mot->node_array = 0;
|
|
|
|
if (!mot->num_node)
|
|
return mot;
|
|
|
|
mot->node_array = alloc->allocate<obj_skin_motion_node>(mot->num_node);
|
|
|
|
if (bone_name_array_offset) {
|
|
s.position_push(bone_name_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++)
|
|
mot->node_array[i].name_index = s.read_uint32_t_reverse_endianness();
|
|
s.position_pop();
|
|
}
|
|
|
|
if (bone_matrix_array_offset) {
|
|
s.position_push(bone_matrix_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++) {
|
|
mat4& mat = mot->node_array[i].inv_bind_pose_mat;
|
|
mat.row0.x = s.read_float_t_reverse_endianness();
|
|
mat.row1.x = s.read_float_t_reverse_endianness();
|
|
mat.row2.x = s.read_float_t_reverse_endianness();
|
|
mat.row3.x = s.read_float_t_reverse_endianness();
|
|
mat.row0.y = s.read_float_t_reverse_endianness();
|
|
mat.row1.y = s.read_float_t_reverse_endianness();
|
|
mat.row2.y = s.read_float_t_reverse_endianness();
|
|
mat.row3.y = s.read_float_t_reverse_endianness();
|
|
mat.row0.z = s.read_float_t_reverse_endianness();
|
|
mat.row1.z = s.read_float_t_reverse_endianness();
|
|
mat.row2.z = s.read_float_t_reverse_endianness();
|
|
mat.row3.z = s.read_float_t_reverse_endianness();
|
|
mat.row0.w = s.read_float_t_reverse_endianness();
|
|
mat.row1.w = s.read_float_t_reverse_endianness();
|
|
mat.row2.w = s.read_float_t_reverse_endianness();
|
|
mat.row3.w = s.read_float_t_reverse_endianness();
|
|
}
|
|
s.position_pop();
|
|
}
|
|
return mot;
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_motion(obj_skin_block_motion* mot,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x,
|
|
const char** bone_name_array, int64_t* bone_name_array_offset, int64_t* bone_matrix_array_offset) {
|
|
obj_modern_write_skin_block_node(&mot->node, s, strings, string_offsets, is_x);
|
|
|
|
int64_t name_offset;
|
|
if (!is_x)
|
|
name_offset = obj_skin_strings_get_string_offset_by_index(strings,
|
|
string_offsets, bone_name_array, mot->name_index);
|
|
else
|
|
name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, mot->name);
|
|
s.write_offset(name_offset, 0x20, is_x);
|
|
s.write_int32_t_reverse_endianness(mot->num_node);
|
|
s.write_offset(mot->node_array ? *bone_name_array_offset : 0, 0x20, is_x);
|
|
s.write_offset(mot->node_array ? *bone_matrix_array_offset : 0, 0x20, is_x);
|
|
|
|
if (mot->node_array) {
|
|
s.position_push(*bone_name_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++)
|
|
s.write_uint32_t_reverse_endianness(mot->node_array[i].name_index);
|
|
s.position_pop();
|
|
*bone_name_array_offset += mot->num_node * sizeof(uint32_t);
|
|
|
|
s.position_push(*bone_matrix_array_offset, SEEK_SET);
|
|
for (int32_t i = 0; i < mot->num_node; i++) {
|
|
mat4& mat = mot->node_array[i].inv_bind_pose_mat;
|
|
s.write_float_t_reverse_endianness(mat.row0.x);
|
|
s.write_float_t_reverse_endianness(mat.row1.x);
|
|
s.write_float_t_reverse_endianness(mat.row2.x);
|
|
s.write_float_t_reverse_endianness(mat.row3.x);
|
|
s.write_float_t_reverse_endianness(mat.row0.y);
|
|
s.write_float_t_reverse_endianness(mat.row1.y);
|
|
s.write_float_t_reverse_endianness(mat.row2.y);
|
|
s.write_float_t_reverse_endianness(mat.row3.y);
|
|
s.write_float_t_reverse_endianness(mat.row0.z);
|
|
s.write_float_t_reverse_endianness(mat.row1.z);
|
|
s.write_float_t_reverse_endianness(mat.row2.z);
|
|
s.write_float_t_reverse_endianness(mat.row3.z);
|
|
s.write_float_t_reverse_endianness(mat.row0.w);
|
|
s.write_float_t_reverse_endianness(mat.row1.w);
|
|
s.write_float_t_reverse_endianness(mat.row2.w);
|
|
s.write_float_t_reverse_endianness(mat.row3.w);
|
|
}
|
|
s.position_pop();
|
|
*bone_matrix_array_offset += mot->num_node * sizeof(mat4);
|
|
}
|
|
}
|
|
|
|
static void obj_modern_read_skin_block_node(obj_skin_block_node* node,
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
int64_t parent_name = s.read_offset(header_length, is_x);
|
|
node->parent_name = obj_read_utf8_string_null_terminated_offset(alloc, s, parent_name);
|
|
|
|
node->position.x = s.read_float_t_reverse_endianness();
|
|
node->position.y = s.read_float_t_reverse_endianness();
|
|
node->position.z = s.read_float_t_reverse_endianness();
|
|
node->rotation.x = s.read_float_t_reverse_endianness();
|
|
node->rotation.y = s.read_float_t_reverse_endianness();
|
|
node->rotation.z = s.read_float_t_reverse_endianness();
|
|
node->scale.x = s.read_float_t_reverse_endianness();
|
|
node->scale.y = s.read_float_t_reverse_endianness();
|
|
node->scale.z = s.read_float_t_reverse_endianness();
|
|
if (is_x)
|
|
s.read(0, 0x04);
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_node(obj_skin_block_node* node,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x) {
|
|
int64_t parent_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, node->parent_name);
|
|
s.write_offset(parent_name_offset, 0x20, is_x);
|
|
|
|
s.write_float_t_reverse_endianness(node->position.x);
|
|
s.write_float_t_reverse_endianness(node->position.y);
|
|
s.write_float_t_reverse_endianness(node->position.z);
|
|
s.write_float_t_reverse_endianness(node->rotation.x);
|
|
s.write_float_t_reverse_endianness(node->rotation.y);
|
|
s.write_float_t_reverse_endianness(node->rotation.z);
|
|
s.write_float_t_reverse_endianness(node->scale.x);
|
|
s.write_float_t_reverse_endianness(node->scale.y);
|
|
s.write_float_t_reverse_endianness(node->scale.z);
|
|
if (is_x)
|
|
s.write(0x04);
|
|
}
|
|
|
|
static obj_skin_block_osage* obj_modern_read_skin_block_osage(
|
|
prj::shared_ptr<prj::stack_allocator> alloc, stream& s, uint32_t header_length, const char** str, bool is_x) {
|
|
obj_skin_block_osage* osg = alloc->allocate<obj_skin_block_osage>();
|
|
obj_modern_read_skin_block_node(&osg->node, alloc, s, header_length, str, is_x);
|
|
|
|
osg->start_index = s.read_uint32_t_reverse_endianness();
|
|
osg->count = s.read_uint32_t_reverse_endianness();
|
|
osg->name_index = s.read_uint32_t_reverse_endianness();
|
|
osg->end_name_index = s.read_uint32_t_reverse_endianness();
|
|
|
|
if (!is_x)
|
|
s.read(0, 0x14);
|
|
else {
|
|
s.read(0, 0x08);
|
|
int64_t motion_node_name_offset = s.read_offset_x();
|
|
osg->motion_node_name = obj_read_utf8_string_null_terminated_offset(alloc, s, motion_node_name_offset);
|
|
s.read(0, 0x18);
|
|
}
|
|
|
|
osg->node_array = 0;
|
|
osg->num_node = 0;
|
|
return osg;
|
|
}
|
|
|
|
static void obj_modern_write_skin_block_osage(obj_skin_block_osage* osg,
|
|
stream& s, std::vector<string_hash>& strings, std::vector<int64_t>& string_offsets, bool is_x) {
|
|
obj_modern_write_skin_block_node(&osg->node, s, strings, string_offsets, is_x);
|
|
|
|
s.write_int32_t_reverse_endianness(osg->start_index);
|
|
s.write_int32_t_reverse_endianness(osg->count);
|
|
s.write_uint32_t_reverse_endianness(osg->name_index);
|
|
s.write_uint32_t_reverse_endianness(osg->end_name_index);
|
|
|
|
if (!is_x)
|
|
s.write(0x14);
|
|
else {
|
|
s.write(0x08);
|
|
int64_t motion_node_name_offset = obj_skin_strings_get_string_offset(strings,
|
|
string_offsets, osg->motion_node_name);
|
|
s.write_offset_x(motion_node_name_offset);
|
|
s.write(0x18);
|
|
}
|
|
}
|
|
|
|
static int32_t vtx_fmt_map[] = {
|
|
-1, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
|
20
|
|
};
|
|
|
|
static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr<prj::stack_allocator> alloc, stream& s,
|
|
int64_t* vertex, const uint32_t vertex_format_index, int32_t num_vertex, uint32_t size_vertex) {
|
|
int32_t vtx_fmt = vertex_format_index >= 0 && vertex_format_index <= 20
|
|
? vtx_fmt_map[vertex_format_index] : -1;
|
|
|
|
bool tex2 = false;
|
|
bool tex3 = false;
|
|
bool tex4 = false;
|
|
bool tex5 = false;
|
|
bool bone = false;
|
|
|
|
switch (vtx_fmt) {
|
|
default:
|
|
return;
|
|
case 2:
|
|
break;
|
|
case 4:
|
|
bone = true;
|
|
break;
|
|
case 6:
|
|
tex2 = true;
|
|
break;
|
|
case 8:
|
|
tex2 = true;
|
|
bone = true;
|
|
break;
|
|
case 10:
|
|
tex2 = true;
|
|
tex3 = true;
|
|
break;
|
|
case 12:
|
|
tex2 = true;
|
|
tex3 = true;
|
|
bone = true;
|
|
break;
|
|
case 14:
|
|
tex2 = true;
|
|
tex3 = true;
|
|
tex4 = true;
|
|
break;
|
|
case 16:
|
|
tex2 = true;
|
|
tex3 = true;
|
|
tex4 = true;
|
|
bone = true;
|
|
break;
|
|
case 18:
|
|
tex2 = true;
|
|
tex3 = true;
|
|
tex4 = true;
|
|
tex5 = true;
|
|
break;
|
|
case 20:
|
|
tex2 = true;
|
|
tex3 = true;
|
|
tex4 = true;
|
|
tex5 = true;
|
|
bone = true;
|
|
break;
|
|
}
|
|
|
|
obj_vertex_format vertex_format = (obj_vertex_format)(OBJ_VERTEX_POSITION
|
|
| OBJ_VERTEX_NORMAL
|
|
| OBJ_VERTEX_TANGENT
|
|
| OBJ_VERTEX_TEXCOORD0
|
|
| OBJ_VERTEX_TEXCOORD1
|
|
| OBJ_VERTEX_COLOR0);
|
|
|
|
if (tex2)
|
|
enum_or(vertex_format, OBJ_VERTEX_TEXCOORD2);
|
|
if (tex3)
|
|
enum_or(vertex_format, OBJ_VERTEX_TEXCOORD3);
|
|
if (bone)
|
|
enum_or(vertex_format, OBJ_VERTEX_BONE_DATA);
|
|
|
|
int64_t vtx_offset = vertex[13];
|
|
|
|
bool has_tangents = false;
|
|
obj_vertex_data* vtx = alloc->allocate<obj_vertex_data>(num_vertex);
|
|
for (int32_t i = 0; i < num_vertex; i++) {
|
|
s.set_position(vtx_offset + (int64_t)size_vertex * i, SEEK_SET);
|
|
|
|
vec3 position;
|
|
position.x = s.read_float_t_reverse_endianness();
|
|
position.y = s.read_float_t_reverse_endianness();
|
|
position.z = s.read_float_t_reverse_endianness();
|
|
vtx[i].position = position;
|
|
|
|
vec3 normal;
|
|
normal.x = (float_t)s.read_int16_t_reverse_endianness();
|
|
normal.y = (float_t)s.read_int16_t_reverse_endianness();
|
|
normal.z = (float_t)s.read_int16_t_reverse_endianness();
|
|
s.read(0, 0x02);
|
|
vtx[i].normal = vec3::div_min_max(normal, -32768.0f, 32767.0f);
|
|
|
|
vec4 tangent;
|
|
tangent.x = (float_t)s.read_int16_t_reverse_endianness();
|
|
tangent.y = (float_t)s.read_int16_t_reverse_endianness();
|
|
tangent.z = (float_t)s.read_int16_t_reverse_endianness();
|
|
tangent.w = (float_t)s.read_int16_t_reverse_endianness();
|
|
vtx[i].tangent = vec4::div_min_max(tangent, -32768.0f, 32767.0f);
|
|
|
|
vec2h texcoord0;
|
|
texcoord0.x = s.read_half_t_reverse_endianness();
|
|
texcoord0.y = s.read_half_t_reverse_endianness();
|
|
vec2h_to_vec2(texcoord0, vtx[i].texcoord0);
|
|
|
|
vec2h texcoord1;
|
|
texcoord1.x = s.read_half_t_reverse_endianness();
|
|
texcoord1.y = s.read_half_t_reverse_endianness();
|
|
vec2h_to_vec2(texcoord1, vtx[i].texcoord1);
|
|
|
|
if (tex2) {
|
|
vec2h texcoord2;
|
|
texcoord2.x = s.read_half_t_reverse_endianness();
|
|
texcoord2.y = s.read_half_t_reverse_endianness();
|
|
vec2h_to_vec2(texcoord2, vtx[i].texcoord2);
|
|
}
|
|
|
|
if (tex3) {
|
|
vec2h texcoord3;
|
|
texcoord3.x = s.read_half_t_reverse_endianness();
|
|
texcoord3.y = s.read_half_t_reverse_endianness();
|
|
vec2h_to_vec2(texcoord3, vtx[i].texcoord3);
|
|
}
|
|
|
|
if (tex4)
|
|
s.read(sizeof(half_t) * 2);
|
|
|
|
if (tex5)
|
|
s.read(sizeof(half_t) * 2);
|
|
|
|
vec4h color;
|
|
color.x = s.read_half_t_reverse_endianness();
|
|
color.y = s.read_half_t_reverse_endianness();
|
|
color.z = s.read_half_t_reverse_endianness();
|
|
color.w = s.read_half_t_reverse_endianness();
|
|
vec4h_to_vec4(color, vtx[i].color0);
|
|
|
|
if (bone) {
|
|
vec4 bone_weight;
|
|
bone_weight.x = (float_t)s.read_int16_t_reverse_endianness();
|
|
bone_weight.y = (float_t)s.read_int16_t_reverse_endianness();
|
|
bone_weight.z = (float_t)s.read_int16_t_reverse_endianness();
|
|
bone_weight.w = (float_t)s.read_int16_t_reverse_endianness();
|
|
bone_weight = vec4::div_min_max(bone_weight, -32768.0f, 32767.0f);
|
|
|
|
vec4i16 bone_index;
|
|
bone_index.x = s.read_uint8_t();
|
|
bone_index.y = s.read_uint8_t();
|
|
bone_index.z = s.read_uint8_t();
|
|
bone_index.w = s.read_uint8_t();
|
|
bone_index.x = bone_index.x != 0xFF ? bone_index.x / 3 : -1;
|
|
bone_index.y = bone_index.y != 0xFF ? bone_index.y / 3 : -1;
|
|
bone_index.z = bone_index.z != 0xFF ? bone_index.z / 3 : -1;
|
|
bone_index.w = bone_index.w != 0xFF ? bone_index.w / 3 : -1;
|
|
|
|
obj_vertex_validate_bone_data(bone_weight, bone_index);
|
|
|
|
vtx[i].bone_weight = bone_weight;
|
|
vtx[i].bone_index = bone_index;
|
|
}
|
|
|
|
if (!has_tangents && vtx[i].tangent != 0.0f)
|
|
has_tangents = true;
|
|
}
|
|
|
|
if (!has_tangents)
|
|
enum_and(vertex_format, ~OBJ_VERTEX_TANGENT);
|
|
|
|
mesh->vertex_array = vtx;
|
|
mesh->num_vertex = num_vertex;
|
|
mesh->vertex_format = vertex_format;
|
|
mesh->attrib.m.compression = 1;
|
|
}
|
|
|
|
static void obj_modern_write_vertex(obj_mesh* mesh, stream& s, int64_t* vertex,
|
|
uint32_t* vertex_format_index, int32_t* num_vertex, int32_t* size_vertex, f2_struct* ovtx) {
|
|
obj_vertex_data* vtx = mesh->vertex_array;
|
|
int32_t _num_vertex = mesh->num_vertex;
|
|
obj_vertex_format vertex_format = mesh->vertex_format;
|
|
|
|
bool tex2 = false;
|
|
bool tex3 = false;
|
|
bool bone = false;
|
|
|
|
uint32_t _vertex_format_index = 2;
|
|
int32_t _size_vertex = 0x2C;
|
|
uint32_t enrs_sub_entry3_rep_count = 12;
|
|
if (vertex_format & OBJ_VERTEX_TEXCOORD2) {
|
|
_vertex_format_index += 4;
|
|
_size_vertex += 0x04;
|
|
enrs_sub_entry3_rep_count += 2;
|
|
tex2 = true;
|
|
}
|
|
|
|
if ((vertex_format & OBJ_VERTEX_TEXCOORD2 | OBJ_VERTEX_TEXCOORD3)
|
|
== (OBJ_VERTEX_TEXCOORD2 | OBJ_VERTEX_TEXCOORD3)) {
|
|
_vertex_format_index += 4;
|
|
_size_vertex += 0x04;
|
|
enrs_sub_entry3_rep_count += 2;
|
|
tex3 = true;
|
|
}
|
|
|
|
if (vertex_format & OBJ_VERTEX_BONE_DATA) {
|
|
_vertex_format_index += 2;
|
|
_size_vertex += 0x0C;
|
|
enrs_sub_entry3_rep_count += 4;
|
|
bone = true;
|
|
}
|
|
|
|
uint32_t off = 0;
|
|
enrs* e = &ovtx->enrs;
|
|
enrs_entry ee;
|
|
bool add_enrs = true;
|
|
if (e->vec.size() > 0) {
|
|
off = (uint32_t)((size_t)e->vec.back().size * e->vec.back().repeat_count);
|
|
if (e->vec.back().sub.data()[2].repeat_count == enrs_sub_entry3_rep_count) {
|
|
e->vec.back().repeat_count += _num_vertex;
|
|
add_enrs = false;
|
|
}
|
|
}
|
|
|
|
if (add_enrs) {
|
|
ee = { off, 3, (uint32_t)_size_vertex, (uint32_t)_num_vertex };
|
|
ee.append(0, 3, ENRS_DWORD);
|
|
ee.append(0, 3, ENRS_WORD);
|
|
ee.append(2, enrs_sub_entry3_rep_count, ENRS_WORD);
|
|
e->vec.push_back(ee);
|
|
}
|
|
|
|
bool has_tangents = false;
|
|
vertex[13] = s.get_position();
|
|
for (int32_t i = 0; i < _num_vertex; i++) {
|
|
vec3 position = vtx[i].position;
|
|
s.write_float_t_reverse_endianness(position.x);
|
|
s.write_float_t_reverse_endianness(position.y);
|
|
s.write_float_t_reverse_endianness(position.z);
|
|
|
|
vec3 normal = vec3::mult_min_max(vtx[i].normal, -32768.0f, 32767.0f);
|
|
s.write_int16_t_reverse_endianness((int16_t)prj::roundf(normal.x));
|
|
s.write_int16_t_reverse_endianness((int16_t)prj::roundf(normal.y));
|
|
s.write_int16_t_reverse_endianness((int16_t)prj::roundf(normal.z));
|
|
s.write(0x02);
|
|
|
|
vec4 tangent = vec4::mult_min_max(vtx[i].tangent, -32768.0f, 32767.0f);
|
|
s.write_int16_t_reverse_endianness((int16_t)prj::roundf(tangent.x));
|
|
s.write_int16_t_reverse_endianness((int16_t)prj::roundf(tangent.y));
|
|
s.write_int16_t_reverse_endianness((int16_t)prj::roundf(tangent.z));
|
|
s.write_int16_t_reverse_endianness((int16_t)prj::roundf(tangent.w));
|
|
|
|
vec2h texcoord0;
|
|
vec2_to_vec2h(vtx[i].texcoord0, texcoord0);
|
|
s.write_half_t_reverse_endianness(texcoord0.x);
|
|
s.write_half_t_reverse_endianness(texcoord0.y);
|
|
|
|
vec2h texcoord1;
|
|
vec2_to_vec2h(vtx[i].texcoord1, texcoord1);
|
|
s.write_half_t_reverse_endianness(texcoord1.x);
|
|
s.write_half_t_reverse_endianness(texcoord1.y);
|
|
|
|
if (tex2) {
|
|
vec2h texcoord2;
|
|
vec2_to_vec2h(vtx[i].texcoord2, texcoord2);
|
|
s.write_half_t_reverse_endianness(texcoord2.x);
|
|
s.write_half_t_reverse_endianness(texcoord2.y);
|
|
}
|
|
|
|
if (tex3) {
|
|
vec2h texcoord3;
|
|
vec2_to_vec2h(vtx[i].texcoord3, texcoord3);
|
|
s.write_half_t_reverse_endianness(texcoord3.x);
|
|
s.write_half_t_reverse_endianness(texcoord3.y);
|
|
}
|
|
|
|
vec4h color;
|
|
vec4_to_vec4h(vtx[i].color0, color);
|
|
s.write_half_t_reverse_endianness(color.x);
|
|
s.write_half_t_reverse_endianness(color.y);
|
|
s.write_half_t_reverse_endianness(color.z);
|
|
s.write_half_t_reverse_endianness(color.w);
|
|
|
|
if (bone) {
|
|
vec4 bone_weight = vec4::mult_min_max(vtx[i].bone_weight, -32768.0f, 32767.0f);
|
|
s.write_int16_t_reverse_endianness((int16_t)bone_weight.x);
|
|
s.write_int16_t_reverse_endianness((int16_t)bone_weight.y);
|
|
s.write_int16_t_reverse_endianness((int16_t)bone_weight.z);
|
|
s.write_int16_t_reverse_endianness((int16_t)bone_weight.w);
|
|
|
|
s.write_uint8_t((uint8_t)(vtx[i].bone_index.x >= 0 ? vtx[i].bone_index.x * 3 : -1));
|
|
s.write_uint8_t((uint8_t)(vtx[i].bone_index.y >= 0 ? vtx[i].bone_index.y * 3 : -1));
|
|
s.write_uint8_t((uint8_t)(vtx[i].bone_index.z >= 0 ? vtx[i].bone_index.z * 3 : -1));
|
|
s.write_uint8_t((uint8_t)(vtx[i].bone_index.w >= 0 ? vtx[i].bone_index.w * 3 : -1));
|
|
}
|
|
}
|
|
|
|
*vertex_format_index = _vertex_format_index;
|
|
*num_vertex = _num_vertex;
|
|
*size_vertex = _size_vertex;
|
|
}
|
|
|
|
inline static const char* obj_move_data_string(const char* str,
|
|
prj::shared_ptr<prj::stack_allocator>& alloc) {
|
|
if (str)
|
|
return alloc->allocate<char>(str, utf8_length(str) + 1);
|
|
return 0;
|
|
}
|
|
|
|
inline static const char* obj_read_utf8_string_null_terminated_offset(
|
|
prj::shared_ptr<prj::stack_allocator>& alloc, stream& s, int64_t offset) {
|
|
size_t len = s.read_utf8_string_null_terminated_offset_length(offset);
|
|
char* str = alloc->allocate<char>(len + 1);
|
|
s.position_push(offset, SEEK_SET);
|
|
s.read(str, len);
|
|
s.position_pop();
|
|
str[len] = 0;
|
|
return str;
|
|
}
|
|
|
|
inline static uint32_t obj_skin_strings_get_string_index(std::vector<string_hash>& vec, const char* str) {
|
|
size_t len = utf8_length(str);
|
|
uint64_t hash_fnv1a64m = hash_string_fnv1a64m(str);
|
|
uint64_t hash_murmurhash = hash_string_murmurhash(str);
|
|
for (string_hash& i : vec)
|
|
if (hash_fnv1a64m == i.hash_fnv1a64m && hash_murmurhash == i.hash_murmurhash)
|
|
return (uint32_t)(0x8000 | &i - vec.data());
|
|
return 0x8000;
|
|
}
|
|
|
|
inline static int64_t obj_skin_strings_get_string_offset(std::vector<string_hash>& vec,
|
|
std::vector<int64_t>& vec_off, const char* str) {
|
|
size_t len = utf8_length(str);
|
|
uint64_t hash_fnv1a64m = hash_string_fnv1a64m(str);
|
|
uint64_t hash_murmurhash = hash_string_murmurhash(str);
|
|
for (string_hash& i : vec)
|
|
if (hash_fnv1a64m == i.hash_fnv1a64m && hash_murmurhash == i.hash_murmurhash)
|
|
return vec_off[&i - vec.data()];
|
|
return 0;
|
|
}
|
|
|
|
inline static int64_t obj_skin_strings_get_string_offset_by_index(std::vector<string_hash>& vec,
|
|
std::vector<int64_t>& vec_off, const char** strings, uint32_t index) {
|
|
if (!(index & 0x8000))
|
|
return 0;
|
|
|
|
const char* str = strings[index & 0x7FFF];
|
|
size_t len = utf8_length(str);
|
|
uint64_t hash_fnv1a64m = hash_string_fnv1a64m(str);
|
|
uint64_t hash_murmurhash = hash_string_murmurhash(str);
|
|
for (string_hash& i : vec)
|
|
if (hash_fnv1a64m == i.hash_fnv1a64m && hash_murmurhash == i.hash_murmurhash)
|
|
return vec_off[&i - vec.data()];
|
|
return 0;
|
|
}
|
|
|
|
inline static void obj_skin_strings_push_back_check(std::vector<string_hash>& vec, const char* str) {
|
|
size_t len = utf8_length(str);
|
|
uint64_t hash_fnv1a64m = hash_string_fnv1a64m(str);
|
|
uint64_t hash_murmurhash = hash_string_murmurhash(str);
|
|
for (string_hash& i : vec)
|
|
if (hash_fnv1a64m == i.hash_fnv1a64m && hash_murmurhash == i.hash_murmurhash)
|
|
return;
|
|
|
|
vec.push_back(str);
|
|
}
|
|
|
|
inline static void obj_skin_strings_push_back_check_by_index(std::vector<string_hash>& vec,
|
|
const char** strings, uint32_t index) {
|
|
if (!(index & 0x8000))
|
|
return;
|
|
|
|
const char* str = strings[index & 0x7FFF];
|
|
size_t len = utf8_length(str);
|
|
uint64_t hash_fnv1a64m = hash_string_fnv1a64m(str);
|
|
uint64_t hash_murmurhash = hash_string_murmurhash(str);
|
|
for (string_hash& i : vec)
|
|
if (hash_fnv1a64m == i.hash_fnv1a64m && hash_murmurhash == i.hash_murmurhash)
|
|
return;
|
|
|
|
vec.push_back(str);
|
|
}
|