/* by korenkonder GitHub/GitLab: korenkonder */ #include "obj.hpp" #include #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 alloc); static void obj_move_data_mesh(obj_mesh* mesh_dst, const obj_mesh* mesh_src, prj::shared_ptr alloc); static void obj_move_data_sub_mesh(obj_sub_mesh* sub_mesh_dst, const obj_sub_mesh* sub_mesh_src, prj::shared_ptr alloc); static obj_skin* obj_move_data_skin(const obj_skin* sk_src, prj::shared_ptr alloc); static void obj_move_data_skin_bone(obj_skin_bone* bone_dst, const obj_skin_bone* bone_src, prj::shared_ptr alloc); static obj_skin_ex_data* obj_move_data_skin_ex_data(const obj_skin_ex_data* ex_src, prj::shared_ptr alloc); static obj_skin_block_cloth* obj_move_data_skin_block_cloth(const obj_skin_block_cloth* cls_src, prj::shared_ptr 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 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 alloc); static obj_skin_block_constraint* obj_move_data_skin_block_constraint(const obj_skin_block_constraint* cns_src, prj::shared_ptr 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 alloc); static obj_skin_block_expression* obj_move_data_skin_block_expression(const obj_skin_block_expression* exp_src, prj::shared_ptr alloc); static obj_skin_block_motion* obj_move_data_skin_block_motion(const obj_skin_block_motion* mot_src, prj::shared_ptr 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 alloc); static obj_skin_block_osage* obj_move_data_skin_block_osage(const obj_skin_block_osage* osg_src, prj::shared_ptr alloc); static obj_skin_skin_param* obj_move_data_skin_param(const obj_skin_skin_param* skp_src, prj::shared_ptr alloc); static void obj_set_classic_read_inner(obj_set* set, prj::shared_ptr& 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 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 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 alloc, stream& s, int64_t base_offset); static void obj_classic_read_model_sub_mesh(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s, int64_t base_offset); static obj_skin* obj_classic_read_skin(prj::shared_ptr 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 alloc, stream& s, int64_t base_offset); static obj_skin_block_cloth* obj_classic_read_skin_block_cloth( prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_cloth(obj_skin_block_cloth* cls, stream& s, std::vector& strings, std::vector& 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 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& strings, std::vector& 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 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& strings, std::vector& string_offsets); static obj_skin_block_constraint* obj_classic_read_skin_block_constraint( prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_constraint(obj_skin_block_constraint* cns, stream& s, std::vector& strings, std::vector& 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& strings, std::vector& string_offsets); static void obj_classic_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector, prj::shared_ptr 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& strings, std::vector& string_offsets); static obj_skin_block_expression* obj_classic_read_skin_block_expression( prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_expression(obj_skin_block_expression* exp, stream& s, std::vector& strings, std::vector& string_offsets, const char** bone_name_array); static obj_skin_block_motion* obj_classic_read_skin_block_motion( prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_motion(obj_skin_block_motion* mot, stream& s, std::vector& strings, std::vector& 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 alloc, stream& s, const char** str); static void obj_classic_write_skin_block_node(obj_skin_block_node* node, stream& s, std::vector& strings, std::vector& string_offsets); static obj_skin_block_osage* obj_classic_read_skin_block_osage( prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_osage(obj_skin_block_osage* osg, stream& s, std::vector& strings, std::vector& string_offsets, int64_t* node_array_offset); static obj_skin_skin_param* obj_classic_read_skin_param( prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_param(obj_skin_skin_param* skp, stream& s, std::vector& strings, std::vector& string_offsets); static void obj_classic_read_vertex(obj_mesh* mesh, prj::shared_ptr 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& 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 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 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 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 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 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 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 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& strings, std::vector& 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 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& strings, std::vector& 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 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& strings, std::vector& string_offsets, bool is_x); static obj_skin_block_constraint* obj_modern_read_skin_block_constraint( prj::shared_ptr 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& strings, std::vector& 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& strings, std::vector& 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 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& strings, std::vector& string_offsets, bool is_x); static obj_skin_block_expression* obj_modern_read_skin_block_expression( prj::shared_ptr 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& strings, std::vector& string_offsets, const char** bone_name_array, bool is_x); static obj_skin_block_motion* obj_modern_read_skin_block_motion( prj::shared_ptr 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& strings, std::vector& 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 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& strings, std::vector& string_offsets, bool is_x); static obj_skin_block_osage* obj_modern_read_skin_block_osage( prj::shared_ptr 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& strings, std::vector& string_offsets, bool is_x); static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr 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& alloc); static const char* obj_read_utf8_string_null_terminated_offset( prj::shared_ptr& alloc, stream& s, int64_t offset); static uint32_t obj_skin_strings_get_string_index(std::vector& vec, const char* str); static int64_t obj_skin_strings_get_string_offset(std::vector& vec, std::vector& vec_off, const char* str); static int64_t obj_skin_strings_get_string_offset_by_index(std::vector& vec, std::vector& vec_off, const char** strings, uint32_t index); static void obj_skin_strings_push_back_check(std::vector& vec, const char* str); static void obj_skin_strings_push_back_check_by_index(std::vector& 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 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_num); for (int32_t i = 0; i < obj_num; i++) { obj_data_dst[i] = alloc->allocate(); 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(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 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(num_vertex); vec3* bitangents = force_malloc(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 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(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_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 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(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(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 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(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(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 alloc) { if (!sk_src) return 0; obj_skin* sk_dst = alloc->allocate(); 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(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 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 alloc) { if (!ex_src) return 0; obj_skin_ex_data* ex_dst = alloc->allocate(); int32_t num_osage_node = ex_src->num_osage_node; ex_dst->osage_node_array = alloc->allocate( 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(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(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( 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 alloc) { obj_skin_block_cloth* cls_dst = alloc->allocate(); 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(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(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( 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( 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( 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 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 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 alloc) { obj_skin_block_constraint* cns_dst = alloc->allocate(); 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(); 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_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_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_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 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 alloc) { obj_skin_block_expression* exp_dst = alloc->allocate(); 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 alloc) { obj_skin_block_motion* mot_dst = alloc->allocate(); 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(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 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 alloc) { obj_skin_block_osage* osg_dst = alloc->allocate(); 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(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 alloc) { if (!skp_src) return 0; obj_skin_skin_param* skp_dst = alloc->allocate(); 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(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& 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_num); for (int32_t i = 0; i < obj_num; i++) set->obj_data[i] = alloc->allocate<::obj>(); uint32_t* data = force_malloc(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(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(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 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(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 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(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->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(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 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(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 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(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 alloc, stream& s, int64_t base_offset) { obj_skin* sk = alloc->allocate(); 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(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 strings; std::vector string_offsets; std::vector 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(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 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 alloc, stream& s, int64_t base_offset) { obj_skin_ex_data* ex = alloc->allocate(); 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(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(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(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(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 alloc, stream& s, const char** str) { obj_skin_block_cloth* cls = alloc->allocate(); 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(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(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( 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(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(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& strings, std::vector& 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 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& strings, std::vector& 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 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& strings, std::vector& 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 alloc, stream& s, const char** str) { obj_skin_block_constraint* cns = alloc->allocate(); 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(); 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_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_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_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& strings, std::vector& 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& strings, std::vector& 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 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& strings, std::vector& 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 alloc, stream& s, const char** str) { obj_skin_block_expression* exp = alloc->allocate(); 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& strings, std::vector& 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 alloc, stream& s, const char** str) { obj_skin_block_motion* mot = alloc->allocate(); 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(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& strings, std::vector& 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 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& strings, std::vector& 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 alloc, stream& s, const char** str) { obj_skin_block_osage* osg = alloc->allocate(); 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(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& strings, std::vector& 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 alloc, stream& s, const char** str) { obj_skin_skin_param* skp = alloc->allocate(); 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(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& strings, std::vector& 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 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(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& 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_num); for (int32_t i = 0; i < obj_num; i++) set->obj_data[i] = alloc->allocate<::obj>(); int64_t* data = force_malloc(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(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(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 alloc, stream& s) { bool tri_strip = sub_mesh->primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP; uint32_t* index_array = alloc->allocate(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 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(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->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(num_mesh); obj_sub_mesh_header** smhss = force_malloc(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(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 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(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 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(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 alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x) { obj_skin* sk = alloc->allocate(); 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(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 strings; std::vector string_offsets; std::vector 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(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 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 alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x) { obj_skin_ex_data* ex = alloc->allocate(); 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(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(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(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(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 alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_cloth* cls = alloc->allocate(); 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(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(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( 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(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(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& strings, std::vector& 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 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& strings, std::vector& 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 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& strings, std::vector& 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 alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_constraint* cns = alloc->allocate(); 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(); 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_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_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_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& strings, std::vector& 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& strings, std::vector& 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 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& strings, std::vector& 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 alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_expression* exp = alloc->allocate(); 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& strings, std::vector& 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 alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_motion* mot = alloc->allocate(); 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(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& strings, std::vector& 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 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& strings, std::vector& 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 alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_osage* osg = alloc->allocate(); 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& strings, std::vector& 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 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(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& alloc) { if (str) return alloc->allocate(str, utf8_length(str) + 1); return 0; } inline static const char* obj_read_utf8_string_null_terminated_offset( prj::shared_ptr& alloc, stream& s, int64_t offset) { size_t len = s.read_utf8_string_null_terminated_offset_length(offset); char* str = alloc->allocate(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& 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& vec, std::vector& 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& vec, std::vector& 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& 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& 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); }