From 14186bcd6b994bb0b455fc04a290280da6c0e6a3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 27 Aug 2022 14:07:45 +0200 Subject: [PATCH] Code cleanup and some new unittests for edgecases. --- code/AssetLib/MMD/MMDPmdParser.h | 1034 ++++++++++++++++-------------- code/Common/Base64.cpp | 17 +- include/assimp/Base64.hpp | 30 +- test/unit/Common/utBase64.cpp | 41 +- 4 files changed, 604 insertions(+), 518 deletions(-) diff --git a/code/AssetLib/MMD/MMDPmdParser.h b/code/AssetLib/MMD/MMDPmdParser.h index 35e4f09d1..95d4d4552 100644 --- a/code/AssetLib/MMD/MMDPmdParser.h +++ b/code/AssetLib/MMD/MMDPmdParser.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -48,550 +47,595 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "MMDCpp14.h" -namespace pmd -{ - class PmdHeader - { - public: - std::string name; - std::string name_english; - std::string comment; - std::string comment_english; +namespace pmd { - bool Read(std::ifstream* stream) - { - char buffer[256]; - stream->read(buffer, 20); - name = std::string(buffer); - stream->read(buffer, 256); - comment = std::string(buffer); - return true; - } +struct PmdHeader { + std::string name; + std::string name_english; + std::string comment; + std::string comment_english; - bool ReadExtension(std::ifstream* stream) - { - char buffer[256]; - stream->read(buffer, 20); - name_english = std::string(buffer); - stream->read(buffer, 256); - comment_english = std::string(buffer); - return true; - } - }; + PmdHeader() = default; + ~PmdHeader() = default; - class PmdVertex - { - public: - float position[3]; + bool Read(std::ifstream *stream) { + if (stream == nullptr) { + return false; + } + char buffer[256] = {}; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read(buffer, 256); + comment = std::string(buffer); + return true; + } - float normal[3]; + bool ReadExtension(std::ifstream *stream) { + if (stream == nullptr) { + return false; + } - float uv[2]; + char buffer[256] = {}; + stream->read(buffer, 20); + name_english = std::string(buffer); + stream->read(buffer, 256); + comment_english = std::string(buffer); - uint16_t bone_index[2]; + return true; + } +}; - uint8_t bone_weight; +struct PmdVertex { + float position[3]; + float normal[3]; + float uv[2]; + uint16_t bone_index[2]; + uint8_t bone_weight; + bool edge_invisible; - bool edge_invisible; + PmdVertex() : + position{ 0.0f }, normal{ 0.0f }, uv{ 0.0f }, bone_index{ 0 }, bone_weight(0), edge_invisible(false) {} - bool Read(std::ifstream* stream) - { - stream->read((char*) position, sizeof(float) * 3); - stream->read((char*) normal, sizeof(float) * 3); - stream->read((char*) uv, sizeof(float) * 2); - stream->read((char*) bone_index, sizeof(uint16_t) * 2); - stream->read((char*) &bone_weight, sizeof(uint8_t)); - stream->read((char*) &edge_invisible, sizeof(uint8_t)); - return true; - } - }; + ~PmdVertex() = default; - class PmdMaterial - { - public: - float diffuse[4]; - float power; - float specular[3]; - float ambient[3]; - uint8_t toon_index; - uint8_t edge_flag; - uint32_t index_count; - std::string texture_filename; - std::string sphere_filename; + bool Read(std::ifstream *stream) { + if (stream == nullptr) { + return false; + } - bool Read(std::ifstream* stream) - { - char buffer[20]; - stream->read((char*) &diffuse, sizeof(float) * 4); - stream->read((char*) &power, sizeof(float)); - stream->read((char*) &specular, sizeof(float) * 3); - stream->read((char*) &ambient, sizeof(float) * 3); - stream->read((char*) &toon_index, sizeof(uint8_t)); - stream->read((char*) &edge_flag, sizeof(uint8_t)); - stream->read((char*) &index_count, sizeof(uint32_t)); - stream->read((char*) &buffer, sizeof(char) * 20); - char* pstar = strchr(buffer, '*'); - if (nullptr == pstar) - { - texture_filename = std::string(buffer); - sphere_filename.clear(); - } - else { - *pstar = 0; - texture_filename = std::string(buffer); - sphere_filename = std::string(pstar+1); - } - return true; - } - }; + stream->read((char *)position, sizeof(float) * 3); + stream->read((char *)normal, sizeof(float) * 3); + stream->read((char *)uv, sizeof(float) * 2); + stream->read((char *)bone_index, sizeof(uint16_t) * 2); + stream->read((char *)&bone_weight, sizeof(uint8_t)); + stream->read((char *)&edge_invisible, sizeof(uint8_t)); + return true; + } +}; - enum class BoneType : uint8_t - { - Rotation, - RotationAndMove, - IkEffector, - Unknown, - IkEffectable, - RotationEffectable, - IkTarget, - Invisible, - Twist, - RotationMovement - }; +struct PmdMaterial { + float diffuse[4]; + float power; + float specular[3]; + float ambient[3]; + uint8_t toon_index; + uint8_t edge_flag; + uint32_t index_count; + std::string texture_filename; + std::string sphere_filename; - class PmdBone - { - public: - std::string name; - std::string name_english; - uint16_t parent_bone_index; - uint16_t tail_pos_bone_index; - BoneType bone_type; - uint16_t ik_parent_bone_index; - float bone_head_pos[3]; + PmdMaterial() : + diffuse{ 0.0f }, power(0.0f), specular{ 0.0f }, ambient{ 0.0f }, toon_index(0), edge_flag(0), index_count(0), texture_filename(), sphere_filename() {} - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name = std::string(buffer); - stream->read((char*) &parent_bone_index, sizeof(uint16_t)); - stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t)); - stream->read((char*) &bone_type, sizeof(uint8_t)); - stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t)); - stream->read((char*) &bone_head_pos, sizeof(float) * 3); - } + ~PmdMaterial() = default; - void ReadExpantion(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name_english = std::string(buffer); - } - }; + bool Read(std::ifstream *stream) { + if (stream == nullptr) { + return false; + } + constexpr size_t BufferSize = 20; - class PmdIk - { - public: - uint16_t ik_bone_index; - uint16_t target_bone_index; - uint16_t iterations; - float angle_limit; - std::vector ik_child_bone_index; + char buffer[BufferSize] = {}; + stream->read((char *)&diffuse, sizeof(float) * 4); + stream->read((char *)&power, sizeof(float)); + stream->read((char *)&specular, sizeof(float) * 3); + stream->read((char *)&ambient, sizeof(float) * 3); + stream->read((char *)&toon_index, sizeof(uint8_t)); + stream->read((char *)&edge_flag, sizeof(uint8_t)); + stream->read((char *)&index_count, sizeof(uint32_t)); + stream->read((char *)&buffer, sizeof(char) * BufferSize); + char *pstar = strchr(buffer, '*'); + if (nullptr == pstar) { + texture_filename = std::string(buffer); + sphere_filename.clear(); + } else { + *pstar = 0; + texture_filename = std::string(buffer); + sphere_filename = std::string(pstar + 1); + } - void Read(std::istream *stream) - { - stream->read((char *) &ik_bone_index, sizeof(uint16_t)); - stream->read((char *) &target_bone_index, sizeof(uint16_t)); - uint8_t ik_chain_length; - stream->read((char*) &ik_chain_length, sizeof(uint8_t)); - stream->read((char *) &iterations, sizeof(uint16_t)); - stream->read((char *) &angle_limit, sizeof(float)); - ik_child_bone_index.resize(ik_chain_length); - for (int i = 0; i < ik_chain_length; i++) - { - stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t)); - } - } - }; + return true; + } +}; - class PmdFaceVertex - { - public: - int vertex_index; - float position[3]; +enum class BoneType : uint8_t { + Rotation, + RotationAndMove, + IkEffector, + Unknown, + IkEffectable, + RotationEffectable, + IkTarget, + Invisible, + Twist, + RotationMovement - void Read(std::istream *stream) - { - stream->read((char *) &vertex_index, sizeof(int)); - stream->read((char *) position, sizeof(float) * 3); - } - }; +}; - enum class FaceCategory : uint8_t - { - Base, - Eyebrow, - Eye, - Mouth, - Other - }; +struct PmdBone { + std::string name; + std::string name_english; + uint16_t parent_bone_index; + uint16_t tail_pos_bone_index; + BoneType bone_type; + uint16_t ik_parent_bone_index; + float bone_head_pos[3]; - class PmdFace - { - public: - std::string name; - FaceCategory type; - std::vector vertices; - std::string name_english; + PmdBone() : + name(), name_english(), parent_bone_index(0), tail_pos_bone_index(0), bone_type(BoneType::Unknown), ik_parent_bone_index(0), bone_head_pos{ 0.0f } {} - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name = std::string(buffer); - int vertex_count; - stream->read((char*) &vertex_count, sizeof(int)); - stream->read((char*) &type, sizeof(uint8_t)); - vertices.resize(vertex_count); - for (int i = 0; i < vertex_count; i++) - { - vertices[i].Read(stream); - } - } + ~PmdBone() = default; - void ReadExpantion(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name_english = std::string(buffer); - } - }; + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } + constexpr size_t BufferSize = 20; + char buffer[BufferSize] = {}; + stream->read(buffer, BufferSize); + name = std::string(buffer); + stream->read((char *)&parent_bone_index, sizeof(uint16_t)); + stream->read((char *)&tail_pos_bone_index, sizeof(uint16_t)); + stream->read((char *)&bone_type, sizeof(uint8_t)); + stream->read((char *)&ik_parent_bone_index, sizeof(uint16_t)); + stream->read((char *)&bone_head_pos, sizeof(float) * 3); + } - class PmdBoneDispName - { - public: - std::string bone_disp_name; - std::string bone_disp_name_english; + void ReadExpantion(std::istream *stream) { + if (stream == nullptr) { + return; + } + constexpr size_t BufferSize = 20; + char buffer[BufferSize] = {}; + stream->read(buffer, BufferSize); + name_english = std::string(buffer); + } +}; - void Read(std::istream *stream) - { - char buffer[50]; - stream->read(buffer, 50); - bone_disp_name = std::string(buffer); - bone_disp_name_english.clear(); - } - void ReadExpantion(std::istream *stream) - { - char buffer[50]; - stream->read(buffer, 50); - bone_disp_name_english = std::string(buffer); - } - }; +struct PmdIk { + uint16_t ik_bone_index; + uint16_t target_bone_index; + uint16_t iterations; + float angle_limit; + std::vector ik_child_bone_index; - class PmdBoneDisp - { - public: - uint16_t bone_index; - uint8_t bone_disp_index; + PmdIk() : + ik_child_bone_index(0), target_bone_index(0), iterations(0), angle_limit(0.0f) {} - void Read(std::istream *stream) - { - stream->read((char*) &bone_index, sizeof(uint16_t)); - stream->read((char*) &bone_disp_index, sizeof(uint8_t)); - } - }; + ~PmdIk() = default; - enum class RigidBodyShape : uint8_t - { - Sphere = 0, - Box = 1, - Cpusel = 2 - }; + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } - enum class RigidBodyType : uint8_t - { - BoneConnected = 0, - Physics = 1, - ConnectedPhysics = 2 - }; + stream->read((char *)&ik_bone_index, sizeof(uint16_t)); + stream->read((char *)&target_bone_index, sizeof(uint16_t)); + uint8_t ik_chain_length; + stream->read((char *)&ik_chain_length, sizeof(uint8_t)); + stream->read((char *)&iterations, sizeof(uint16_t)); + stream->read((char *)&angle_limit, sizeof(float)); + ik_child_bone_index.resize(ik_chain_length); + for (int i = 0; i < ik_chain_length; i++) { + stream->read((char *)&ik_child_bone_index[i], sizeof(uint16_t)); + } + } +}; - class PmdRigidBody - { - public: - std::string name; - uint16_t related_bone_index; - uint8_t group_index; - uint16_t mask; - RigidBodyShape shape; - float size[3]; - float position[3]; - float orientation[3]; - float weight; - float linear_damping; - float anglar_damping; - float restitution; - float friction; - RigidBodyType rigid_type; +struct PmdFaceVertex { + int vertex_index; + float position[3]; - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, sizeof(char) * 20); - name = (std::string(buffer)); - stream->read((char*) &related_bone_index, sizeof(uint16_t)); - stream->read((char*) &group_index, sizeof(uint8_t)); - stream->read((char*) &mask, sizeof(uint16_t)); - stream->read((char*) &shape, sizeof(uint8_t)); - stream->read((char*) size, sizeof(float) * 3); - stream->read((char*) position, sizeof(float) * 3); - stream->read((char*) orientation, sizeof(float) * 3); - stream->read((char*) &weight, sizeof(float)); - stream->read((char*) &linear_damping, sizeof(float)); - stream->read((char*) &anglar_damping, sizeof(float)); - stream->read((char*) &restitution, sizeof(float)); - stream->read((char*) &friction, sizeof(float)); - stream->read((char*) &rigid_type, sizeof(char)); - } - }; + PmdFaceVertex() : + vertex_index(0), position{ 0.0f } {} - class PmdConstraint - { - public: - std::string name; - uint32_t rigid_body_index_a; - uint32_t rigid_body_index_b; - float position[3]; - float orientation[3]; - float linear_lower_limit[3]; - float linear_upper_limit[3]; - float angular_lower_limit[3]; - float angular_upper_limit[3]; - float linear_stiffness[3]; - float angular_stiffness[3]; + ~PmdFaceVertex() = default; - void Read(std::istream *stream) - { - char buffer[20]; - stream->read(buffer, 20); - name = std::string(buffer); - stream->read((char *) &rigid_body_index_a, sizeof(uint32_t)); - stream->read((char *) &rigid_body_index_b, sizeof(uint32_t)); - stream->read((char *) position, sizeof(float) * 3); - stream->read((char *) orientation, sizeof(float) * 3); - stream->read((char *) linear_lower_limit, sizeof(float) * 3); - stream->read((char *) linear_upper_limit, sizeof(float) * 3); - stream->read((char *) angular_lower_limit, sizeof(float) * 3); - stream->read((char *) angular_upper_limit, sizeof(float) * 3); - stream->read((char *) linear_stiffness, sizeof(float) * 3); - stream->read((char *) angular_stiffness, sizeof(float) * 3); - } - }; + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } + stream->read((char *)&vertex_index, sizeof(int)); + stream->read((char *)position, sizeof(float) * 3); + } +}; - class PmdModel - { - public: - float version; - PmdHeader header; - std::vector vertices; - std::vector indices; - std::vector materials; - std::vector bones; - std::vector iks; - std::vector faces; - std::vector faces_indices; - std::vector bone_disp_name; - std::vector bone_disp; - std::vector toon_filenames; - std::vector rigid_bodies; - std::vector constraints; +enum class FaceCategory : uint8_t { + Base, + Eyebrow, + Eye, + Mouth, + Other +}; - static std::unique_ptr LoadFromFile(const char *filename) - { - std::ifstream stream(filename, std::ios::binary); - if (stream.fail()) - { - std::cerr << "could not open \"" << filename << "\"" << std::endl; - return nullptr; - } - auto result = LoadFromStream(&stream); - stream.close(); - return result; - } +struct PmdFace { + std::string name; + FaceCategory type; + std::vector vertices; + std::string name_english; - static std::unique_ptr LoadFromStream(std::ifstream *stream) - { - auto result = mmd::make_unique(); - char buffer[100]; + PmdFace() : + name(), type(FaceCategory::Other), vertices(), name_english() {} - // magic - char magic[3]; - stream->read(magic, 3); - if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') - { - std::cerr << "invalid file" << std::endl; - return nullptr; - } + ~PmdFace() = default; - // version - stream->read((char*) &(result->version), sizeof(float)); - if (result ->version != 1.0f) - { - std::cerr << "invalid version" << std::endl; - return nullptr; - } + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } + constexpr size_t BufferSize = 20; + char buffer[BufferSize]; + stream->read(buffer, BufferSize); + name = std::string(buffer); + int vertex_count; + stream->read((char *)&vertex_count, sizeof(int)); + stream->read((char *)&type, sizeof(uint8_t)); + vertices.resize(vertex_count); + for (int i = 0; i < vertex_count; i++) { + vertices[i].Read(stream); + } + } - // header - result->header.Read(stream); + void ReadExpantion(std::istream *stream) { + if (stream == nullptr) { + return; + } - // vertices - uint32_t vertex_num; - stream->read((char*) &vertex_num, sizeof(uint32_t)); - result->vertices.resize(vertex_num); - for (uint32_t i = 0; i < vertex_num; i++) - { - result->vertices[i].Read(stream); - } + char buffer[20]; + stream->read(buffer, 20); + name_english = std::string(buffer); + } +}; - // indices - uint32_t index_num; - stream->read((char*) &index_num, sizeof(uint32_t)); - result->indices.resize(index_num); - for (uint32_t i = 0; i < index_num; i++) - { - stream->read((char*) &result->indices[i], sizeof(uint16_t)); - } +struct PmdBoneDispName { + std::string bone_disp_name; + std::string bone_disp_name_english; - // materials - uint32_t material_num; - stream->read((char*) &material_num, sizeof(uint32_t)); - result->materials.resize(material_num); - for (uint32_t i = 0; i < material_num; i++) - { - result->materials[i].Read(stream); - } + PmdBoneDispName() = default; - // bones - uint16_t bone_num; - stream->read((char*) &bone_num, sizeof(uint16_t)); - result->bones.resize(bone_num); - for (uint32_t i = 0; i < bone_num; i++) - { - result->bones[i].Read(stream); - } + ~PmdBoneDispName() = default; - // iks - uint16_t ik_num; - stream->read((char*) &ik_num, sizeof(uint16_t)); - result->iks.resize(ik_num); - for (uint32_t i = 0; i < ik_num; i++) - { - result->iks[i].Read(stream); - } + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name = std::string(buffer); + bone_disp_name_english.clear(); + } - // faces - uint16_t face_num; - stream->read((char*) &face_num, sizeof(uint16_t)); - result->faces.resize(face_num); - for (uint32_t i = 0; i < face_num; i++) - { - result->faces[i].Read(stream); - } + void ReadExpantion(std::istream *stream) { + if (stream == nullptr) { + return; + } + char buffer[50]; + stream->read(buffer, 50); + bone_disp_name_english = std::string(buffer); + } +}; - // face frames - uint8_t face_frame_num; - stream->read((char*) &face_frame_num, sizeof(uint8_t)); - result->faces_indices.resize(face_frame_num); - for (uint32_t i = 0; i < face_frame_num; i++) - { - stream->read((char*) &result->faces_indices[i], sizeof(uint16_t)); - } +struct PmdBoneDisp { + uint16_t bone_index; + uint8_t bone_disp_index; - // bone names - uint8_t bone_disp_num; - stream->read((char*) &bone_disp_num, sizeof(uint8_t)); - result->bone_disp_name.resize(bone_disp_num); - for (uint32_t i = 0; i < bone_disp_num; i++) - { - result->bone_disp_name[i].Read(stream); - } + PmdBoneDisp() : + bone_index(0), bone_disp_index(0) {} - // bone frame - uint32_t bone_frame_num; - stream->read((char*) &bone_frame_num, sizeof(uint32_t)); - result->bone_disp.resize(bone_frame_num); - for (uint32_t i = 0; i < bone_frame_num; i++) - { - result->bone_disp[i].Read(stream); - } + ~PmdBoneDisp() = default; - // english name - bool english; - stream->read((char*) &english, sizeof(char)); - if (english) - { - result->header.ReadExtension(stream); - for (uint32_t i = 0; i < bone_num; i++) - { - result->bones[i].ReadExpantion(stream); - } - for (uint32_t i = 0; i < face_num; i++) - { - if (result->faces[i].type == pmd::FaceCategory::Base) - { - continue; - } - result->faces[i].ReadExpantion(stream); - } - for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) - { - result->bone_disp_name[i].ReadExpantion(stream); - } - } + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } - // toon textures - if (stream->peek() == std::ios::traits_type::eof()) - { - result->toon_filenames.clear(); - } - else { - result->toon_filenames.resize(10); - for (uint32_t i = 0; i < 10; i++) - { - stream->read(buffer, 100); - result->toon_filenames[i] = std::string(buffer); - } - } + stream->read((char *)&bone_index, sizeof(uint16_t)); + stream->read((char *)&bone_disp_index, sizeof(uint8_t)); + } +}; - // physics - if (stream->peek() == std::ios::traits_type::eof()) - { - result->rigid_bodies.clear(); - result->constraints.clear(); - } - else { - uint32_t rigid_body_num; - stream->read((char*) &rigid_body_num, sizeof(uint32_t)); - result->rigid_bodies.resize(rigid_body_num); - for (uint32_t i = 0; i < rigid_body_num; i++) - { - result->rigid_bodies[i].Read(stream); - } - uint32_t constraint_num; - stream->read((char*) &constraint_num, sizeof(uint32_t)); - result->constraints.resize(constraint_num); - for (uint32_t i = 0; i < constraint_num; i++) - { - result->constraints[i].Read(stream); - } - } +enum class RigidBodyShape : uint8_t { + Sphere = 0, + Box = 1, + Cpusel = 2 +}; - if (stream->peek() != std::ios::traits_type::eof()) - { - std::cerr << "there is unknown data" << std::endl; - } +enum class RigidBodyType : uint8_t { + BoneConnected = 0, + Physics = 1, + ConnectedPhysics = 2 +}; + +struct PmdRigidBody { + std::string name; + uint16_t related_bone_index; + uint8_t group_index; + uint16_t mask; + RigidBodyShape shape; + float size[3]; + float position[3]; + float orientation[3]; + float weight; + float linear_damping; + float anglar_damping; + float restitution; + float friction; + RigidBodyType rigid_type; + + PmdRigidBody() : + name(), related_bone_index(0), group_index(0), mask(0), shape(RigidBodyShape::Box), size{ 0.0f }, position{ 0.0f }, weight(0.0f), linear_damping(0.0f), anglar_damping(0.0f), restitution(0.0f), friction(0.0f), rigid_type(RigidBodyType::BoneConnected) {} + + ~PmdRigidBody() = default; + + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } + + char buffer[20]; + stream->read(buffer, sizeof(char) * 20); + name = (std::string(buffer)); + stream->read((char *)&related_bone_index, sizeof(uint16_t)); + stream->read((char *)&group_index, sizeof(uint8_t)); + stream->read((char *)&mask, sizeof(uint16_t)); + stream->read((char *)&shape, sizeof(uint8_t)); + stream->read((char *)size, sizeof(float) * 3); + stream->read((char *)position, sizeof(float) * 3); + stream->read((char *)orientation, sizeof(float) * 3); + stream->read((char *)&weight, sizeof(float)); + stream->read((char *)&linear_damping, sizeof(float)); + stream->read((char *)&anglar_damping, sizeof(float)); + stream->read((char *)&restitution, sizeof(float)); + stream->read((char *)&friction, sizeof(float)); + stream->read((char *)&rigid_type, sizeof(char)); + } +}; + +struct PmdConstraint { + std::string name; + uint32_t rigid_body_index_a; + uint32_t rigid_body_index_b; + float position[3]; + float orientation[3]; + float linear_lower_limit[3]; + float linear_upper_limit[3]; + float angular_lower_limit[3]; + float angular_upper_limit[3]; + float linear_stiffness[3]; + float angular_stiffness[3]; + + PmdConstraint() : + name(), rigid_body_index_a(0), rigid_body_index_b(0), position{ 0.0f }, orientation{ 0.0f }, linear_lower_limit{ 0.0f }, linear_upper_limit{ 0.0f }, angular_lower_limit{ 0.0f }, angular_upper_limit{ 0.0f }, linear_stiffness{ 0.0f }, angular_stiffness{ 0.0f } {} + + ~PmdConstraint() = default; + + void Read(std::istream *stream) { + if (stream == nullptr) { + return; + } + + char buffer[20]; + stream->read(buffer, 20); + name = std::string(buffer); + stream->read((char *)&rigid_body_index_a, sizeof(uint32_t)); + stream->read((char *)&rigid_body_index_b, sizeof(uint32_t)); + stream->read((char *)position, sizeof(float) * 3); + stream->read((char *)orientation, sizeof(float) * 3); + stream->read((char *)linear_lower_limit, sizeof(float) * 3); + stream->read((char *)linear_upper_limit, sizeof(float) * 3); + stream->read((char *)angular_lower_limit, sizeof(float) * 3); + stream->read((char *)angular_upper_limit, sizeof(float) * 3); + stream->read((char *)linear_stiffness, sizeof(float) * 3); + stream->read((char *)angular_stiffness, sizeof(float) * 3); + } +}; + +struct PmdModel { + float version; + PmdHeader header; + std::vector vertices; + std::vector indices; + std::vector materials; + std::vector bones; + std::vector iks; + std::vector faces; + std::vector faces_indices; + std::vector bone_disp_name; + std::vector bone_disp; + std::vector toon_filenames; + std::vector rigid_bodies; + std::vector constraints; + + PmdModel() : + version(0.0f) {} + + ~PmdModel() = default; + + static std::unique_ptr LoadFromFile(const char *filename) { + if (filename == nullptr) { + return nullptr; + } + + std::ifstream stream(filename, std::ios::binary); + if (stream.fail()) { + std::cerr << "could not open \"" << filename << "\"" << std::endl; + return nullptr; + } + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + static std::unique_ptr LoadFromStream(std::ifstream *stream) { + auto result = mmd::make_unique(); + char buffer[100]; + + // magic + char magic[3]; + stream->read(magic, 3); + if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') { + std::cerr << "invalid file" << std::endl; + return nullptr; + } + + // version + stream->read((char *)&(result->version), sizeof(float)); + if (result->version != 1.0f) { + std::cerr << "invalid version" << std::endl; + return nullptr; + } + + // header + result->header.Read(stream); + + // vertices + uint32_t vertex_num; + stream->read((char *)&vertex_num, sizeof(uint32_t)); + result->vertices.resize(vertex_num); + for (uint32_t i = 0; i < vertex_num; i++) { + result->vertices[i].Read(stream); + } + + // indices + uint32_t index_num; + stream->read((char *)&index_num, sizeof(uint32_t)); + result->indices.resize(index_num); + for (uint32_t i = 0; i < index_num; i++) { + stream->read((char *)&result->indices[i], sizeof(uint16_t)); + } + + // materials + uint32_t material_num; + stream->read((char *)&material_num, sizeof(uint32_t)); + result->materials.resize(material_num); + for (uint32_t i = 0; i < material_num; i++) { + result->materials[i].Read(stream); + } + + // bones + uint16_t bone_num; + stream->read((char *)&bone_num, sizeof(uint16_t)); + result->bones.resize(bone_num); + for (uint32_t i = 0; i < bone_num; i++) { + result->bones[i].Read(stream); + } + + // iks + uint16_t ik_num; + stream->read((char *)&ik_num, sizeof(uint16_t)); + result->iks.resize(ik_num); + for (uint32_t i = 0; i < ik_num; i++) { + result->iks[i].Read(stream); + } + + // faces + uint16_t face_num; + stream->read((char *)&face_num, sizeof(uint16_t)); + result->faces.resize(face_num); + for (uint32_t i = 0; i < face_num; i++) { + result->faces[i].Read(stream); + } + + // face frames + uint8_t face_frame_num; + stream->read((char *)&face_frame_num, sizeof(uint8_t)); + result->faces_indices.resize(face_frame_num); + for (uint32_t i = 0; i < face_frame_num; i++) { + stream->read((char *)&result->faces_indices[i], sizeof(uint16_t)); + } + + // bone names + uint8_t bone_disp_num; + stream->read((char *)&bone_disp_num, sizeof(uint8_t)); + result->bone_disp_name.resize(bone_disp_num); + for (uint32_t i = 0; i < bone_disp_num; i++) { + result->bone_disp_name[i].Read(stream); + } + + // bone frame + uint32_t bone_frame_num; + stream->read((char *)&bone_frame_num, sizeof(uint32_t)); + result->bone_disp.resize(bone_frame_num); + for (uint32_t i = 0; i < bone_frame_num; i++) { + result->bone_disp[i].Read(stream); + } + + // english name + bool english; + stream->read((char *)&english, sizeof(char)); + if (english) { + result->header.ReadExtension(stream); + for (uint32_t i = 0; i < bone_num; i++) { + result->bones[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < face_num; i++) { + if (result->faces[i].type == pmd::FaceCategory::Base) { + continue; + } + result->faces[i].ReadExpantion(stream); + } + for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) { + result->bone_disp_name[i].ReadExpantion(stream); + } + } + + // toon textures + if (stream->peek() == std::ios::traits_type::eof()) { + result->toon_filenames.clear(); + } else { + result->toon_filenames.resize(10); + for (uint32_t i = 0; i < 10; i++) { + stream->read(buffer, 100); + result->toon_filenames[i] = std::string(buffer); + } + } + + // physics + if (stream->peek() == std::ios::traits_type::eof()) { + result->rigid_bodies.clear(); + result->constraints.clear(); + } else { + uint32_t rigid_body_num; + stream->read((char *)&rigid_body_num, sizeof(uint32_t)); + result->rigid_bodies.resize(rigid_body_num); + for (uint32_t i = 0; i < rigid_body_num; i++) { + result->rigid_bodies[i].Read(stream); + } + uint32_t constraint_num; + stream->read((char *)&constraint_num, sizeof(uint32_t)); + result->constraints.resize(constraint_num); + for (uint32_t i = 0; i < constraint_num; i++) { + result->constraints[i].Read(stream); + } + } + + if (stream->peek() != std::ios::traits_type::eof()) { + std::cerr << "there is unknown data" << std::endl; + } + + return result; + } +}; + +} // namespace pmd - return result; - } - }; -} diff --git a/code/Common/Base64.cpp b/code/Common/Base64.cpp index 3e9c47405..e55e0ba75 100644 --- a/code/Common/Base64.cpp +++ b/code/Common/Base64.cpp @@ -46,7 +46,7 @@ namespace Assimp { namespace Base64 { -static const uint8_t tableDecodeBase64[128] = { +static constexpr uint8_t tableDecodeBase64[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, @@ -57,7 +57,7 @@ static const uint8_t tableDecodeBase64[128] = { 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0 }; -static const char *tableEncodeBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +static constexpr char *tableEncodeBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; static inline char EncodeChar(uint8_t b) { return tableEncodeBase64[size_t(b)]; @@ -71,6 +71,11 @@ inline uint8_t DecodeChar(char c) { } void Encode(const uint8_t *in, size_t inLength, std::string &out) { + if (in == nullptr || inLength==0) { + out.clear(); + return; + } + size_t outLength = ((inLength + 2) / 3) * 4; size_t j = out.size(); @@ -115,8 +120,14 @@ std::string Encode(const std::vector &in) { } size_t Decode(const char *in, size_t inLength, uint8_t *&out) { + if (in == nullptr) { + out = nullptr; + return 0; + } + if (inLength % 4 != 0) { - throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength); + throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), + "\", length:", inLength); } if (inLength < 4) { diff --git a/include/assimp/Base64.hpp b/include/assimp/Base64.hpp index ee319aceb..403723857 100644 --- a/include/assimp/Base64.hpp +++ b/include/assimp/Base64.hpp @@ -50,16 +50,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace Base64 { -/// @brief Will encode the given -/// @param in -/// @param inLength -/// @param out +/// @brief Will encode the given character buffer from UTF64 to ASCII +/// @param in The UTF-64 buffer. +/// @param inLength The size of the buffer +/// @param out The encoded ASCII string. void Encode(const uint8_t *in, size_t inLength, std::string &out); + +/// @brief Will encode the given character buffer from UTF64 to ASCII. +/// @param in A vector, which contains the buffer for encoding. +/// @param out The encoded ASCII string. void Encode(const std::vector& in, std::string &out); + +/// @brief Will encode the given character buffer from UTF64 to ASCII. +/// @param in A vector, which contains the buffer for encoding. +/// @return The encoded ASCII string. std::string Encode(const std::vector& in); +/// @brief Will decode the given character buffer from ASCII to UTF64. +/// @param in The ASCII buffer to decode. +/// @param inLength The size of the buffer. +/// @param out The decoded buffer. +/// @return The new buffer size. size_t Decode(const char *in, size_t inLength, uint8_t *&out); + +/// @brief Will decode the given character buffer from ASCII to UTF64. +/// @param in The ASCII buffer to decode as a std::string. +/// @param out The decoded buffer. +/// @return The new buffer size. size_t Decode(const std::string& in, std::vector& out); + +/// @brief Will decode the given character buffer from ASCII to UTF64. +/// @param in The ASCII string. +/// @return The decoded buffer in a vector. std::vector Decode(const std::string& in); } // namespace Base64 diff --git a/test/unit/Common/utBase64.cpp b/test/unit/Common/utBase64.cpp index 8b0a60e47..ec927b980 100644 --- a/test/unit/Common/utBase64.cpp +++ b/test/unit/Common/utBase64.cpp @@ -47,26 +47,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace std; using namespace Assimp; -class Base64Test : public ::testing::Test { -public: - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; +class Base64Test : public ::testing::Test {}; static const std::vector assimpStringBinary = { 97, 115, 115, 105, 109, 112 }; static const std::string assimpStringEncoded = "YXNzaW1w"; -TEST_F( Base64Test, encodeTest ) { - EXPECT_EQ( "", Base64::Encode (std::vector{}) ); - EXPECT_EQ( "Vg==", Base64::Encode (std::vector{ 86 }) ); - EXPECT_EQ( assimpStringEncoded, Base64::Encode (assimpStringBinary) ); +TEST_F( Base64Test, encodeTest) { + EXPECT_EQ( "", Base64::Encode(std::vector{}) ); + EXPECT_EQ( "Vg==", Base64::Encode(std::vector{ 86 }) ); + EXPECT_EQ( assimpStringEncoded, Base64::Encode(assimpStringBinary) ); } -TEST_F( Base64Test, decodeTest ) { - EXPECT_EQ( std::vector {}, Base64::Decode ("") ); - EXPECT_EQ( std::vector { 86 }, Base64::Decode ("Vg==") ); - EXPECT_EQ( assimpStringBinary, Base64::Decode (assimpStringEncoded) ); +TEST_F( Base64Test, encodeTestWithNullptr ) { + std::string out; + Base64::Encode(nullptr, 100u, out); + EXPECT_TRUE(out.empty()); + + Base64::Encode(&assimpStringBinary[0], 0u, out); + EXPECT_TRUE(out.empty()); +} + +TEST_F( Base64Test, decodeTest) { + EXPECT_EQ( std::vector {}, Base64::Decode("") ); + EXPECT_EQ( std::vector { 86 }, Base64::Decode("Vg==") ); + EXPECT_EQ( assimpStringBinary, Base64::Decode(assimpStringEncoded) ); +} + +TEST_F(Base64Test, decodeTestWithNullptr) { + uint8_t *out = nullptr; + size_t size = Base64::Decode(nullptr, 100u, out); + EXPECT_EQ(nullptr, out); + EXPECT_EQ(0u, size); }