From 4a5ec9f85c45bcdc5c2fb5e79462630705d386dd Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 12 Aug 2020 17:39:40 +0200 Subject: [PATCH 01/10] Restructured json output to be easier to read --- src/dts-to-json/convert_dts.cpp | 189 ++++++++++++++++++++++++++++++-- src/structures.hpp | 46 ++++---- 2 files changed, 201 insertions(+), 34 deletions(-) diff --git a/src/dts-to-json/convert_dts.cpp b/src/dts-to-json/convert_dts.cpp index 400b7bf7..f1344665 100644 --- a/src/dts-to-json/convert_dts.cpp +++ b/src/dts-to-json/convert_dts.cpp @@ -293,19 +293,14 @@ dts::shape_or_material_list read_shape(const fs::path& file_name, std::basic_ifs } } -void read_from_json(const std::filesystem::path& file_name) -{ - auto new_file_name = file_name.string() + ".json"; - std::ifstream test_file(new_file_name); - auto fresh_shape_json = nlohmann::json::parse(test_file); - const dts::shape_variant fresh_shape = fresh_shape_json; -} - int main(int argc, const char** argv) { const auto files = dts::shared::find_files( std::vector(argv + 1, argv + argc), - ".dts",".DTS", ".dml", ".DML"); + ".dts", + ".DTS", + ".dml", + ".DML"); std::for_each(std::execution::par_unseq, files.begin(), files.end(), [](auto&& file_name) { try @@ -323,9 +318,181 @@ int main(int argc, const char** argv) std::visit([&](const auto& item) { nlohmann::ordered_json item_as_json = item; + auto sequences = nlohmann::json::object(); + + for (auto& sequence : item_as_json["sequences"]) + { + auto sequence_name = item_as_json["names"][sequence["nameIndex"].get()].get(); + sequence["name"] = sequence_name; + sequences[sequence_name] = sequence; + } + + auto nodes = nlohmann::json::object(); + + for (auto& node : item_as_json["nodes"]) + { + auto parent_index = node["parentNodeIndex"].get(); + + if (parent_index != -1) + { + auto& parent_node = item_as_json["nodes"][parent_index]; + + auto parent_name = item_as_json["names"][parent_node["nameIndex"].get()].get(); + node["parentNodeIndex"] = parent_name; + } + else + { + node["parentNodeIndex"] = nullptr; + } + //const auto default_transform_index = element["defaultTransformIndex"].get(); + + std::string name = item_as_json["names"][node["nameIndex"].get()].get(); + node["name"] = name; + + + nlohmann::json element = node; + + element["sequences"] = nlohmann::json::object(); + + auto& sub_sequences = item_as_json["subSequences"]; + const auto num_sub_sequences = element["numSubSequences"].get(); + const auto first_sub_sequence = element["firstSubSequence"].get(); + for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) + { + auto& local_sub_sequence = sub_sequences[i]; + auto& sequence = item_as_json["sequences"][local_sub_sequence["sequenceIndex"].get()]; + auto sequence_name = sequence["name"].get(); + + auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); + const auto first_key_frame = local_sub_sequence["firstKeyFrame"].get(); + const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); + + auto& keyframes = item_as_json["keyframes"]; + for (auto j = first_key_frame; j < first_key_frame + num_key_frames; ++j) + { + auto& keyframe = keyframes[j]; + keyframe["keyframeIndex"] = j; + auto transform_index = keyframe["transformIndex"].get(); + auto& transform = item_as_json["transforms"][transform_index]; + + keyframe["transformation"] = transform; + + key_frames.emplace_back(keyframe); + } + } + + nodes.emplace(name, element); + } + + auto objects = nlohmann::json::object(); + + for (auto& object : item_as_json["objects"]) + { + + auto name = item_as_json["names"][object["nameIndex"].get()].get(); + object["name"] = name; + // objects[name] = element; + + if (nodes.contains(name)) + { + auto& node = nodes[name]; + object["parentObjectIndex"] = node["parentNodeIndex"]; + object["nodeIndex"] = name; + node["objectIndex"] = name; + } + + + nlohmann::json element = object; + element["sequences"] = nlohmann::json::object(); + + auto& sub_sequences = item_as_json["subSequences"]; + const auto num_sub_sequences = element["numSubSequences"].get(); + const auto first_sub_sequence = element["firstSubSequence"].get(); + for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) + { + auto& local_sub_sequence = sub_sequences[i]; + auto& sequence = item_as_json["sequences"][local_sub_sequence["sequenceIndex"].get()]; + auto sequence_name = sequence["name"].get(); + + auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); + const auto first_key_frame = local_sub_sequence["firstKeyFrame"].get(); + const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); + + auto& keyframes = item_as_json["keyframes"]; + for (auto j = first_key_frame; j < first_key_frame + num_key_frames; ++j) + { + auto& keyframe = keyframes[j]; + keyframe["keyframeIndex"] = j; + auto transform_index = keyframe["transformIndex"].get(); + auto& transform = item_as_json["transforms"][transform_index]; + + keyframe["transformation"] = transform; + + key_frames.emplace_back(keyframe); + } + } + objects.emplace(name, element); + } + + for (auto& element : item_as_json["details"]) + { + auto& node = item_as_json["nodes"][element["rootNodeIndex"].get()]; + + element["rootNodeIndex"] = node["name"]; + } + + auto count = 0u; + for (auto& element : item_as_json["transitions"]) + { + const auto start_index = element["startSequenceIndex"].get(); + const auto end_index = element["endSequenceIndex"].get(); + + element["startSequenceIndex"] = item_as_json["sequences"][start_index]["name"]; + element["endSequenceIndex"] = item_as_json["sequences"][end_index]["name"]; + + std::stringstream possible_name; + possible_name << "Trans"; + + if (count < 10) + { + possible_name << '0' << count; + } + else + { + possible_name << count; + } + + for (auto& name : item_as_json["names"]) + { + if (const auto raw_name = name.get(); raw_name.rfind(possible_name.str(), 0) == 0) + { + element["name"] = raw_name; + } + } + count++; + } + + item_as_json["nodes"] = nodes; + item_as_json["objects"] = objects; + item_as_json["sequences"] = sequences; + item_as_json.erase("transforms"); + item_as_json.erase("keyframes"); + item_as_json.erase("subSequences"); + item_as_json.erase("names"); + item_as_json.erase("header"); + auto new_file_name = file_name.string() + ".json"; - std::ofstream item_as_file(new_file_name, std::ios::trunc); - item_as_file << item_as_json.dump(4); + { + std::ofstream item_as_file(new_file_name, std::ios::trunc); + item_as_file << item_as_json.dump(4); + } + + //new_file_name = file_name.string() + ".pack"; + { + //std::basic_ofstream item_as_file(new_file_name, std::ios::trunc | std::ios::binary); + //const auto pack = nlohmann::json::to_msgpack(item_as_json); + //item_as_file.write(&pack[0], pack.size()); + } std::stringstream msg; msg << "Created " << new_file_name << '\n'; diff --git a/src/structures.hpp b/src/structures.hpp index 25d13efe..3126d76f 100644 --- a/src/structures.hpp +++ b/src/structures.hpp @@ -391,12 +391,12 @@ namespace darkstar::dts struct node { - constexpr static auto keys = make_keys({ "nameIndex", "parent", "numSubSequences", "firstSubSequence", "defaultTransform" }); + constexpr static auto keys = make_keys({ "nameIndex", "parentNodeIndex", "numSubSequences", "firstSubSequence", "defaultTransformIndex" }); endian::little_int32_t name_index; - endian::little_int32_t parent; + endian::little_int32_t parent_node_index; endian::little_int32_t num_sub_sequences; endian::little_int32_t first_sub_sequence; - endian::little_int32_t default_transform; + endian::little_int32_t default_transform_index; }; struct sequence @@ -421,9 +421,9 @@ namespace darkstar::dts struct keyframe { - constexpr static auto keys = make_keys({ "position", "keyValue" }); + constexpr static auto keys = make_keys({ "position", "transformIndex" }); float position; - endian::little_uint32_t key_value; + endian::little_uint32_t transform_index; }; struct transform @@ -452,21 +452,21 @@ namespace darkstar::dts struct detail { - constexpr static auto keys = make_keys({ "nameIndex", "size" }); - endian::little_int32_t name_index; + constexpr static auto keys = make_keys({ "rootNodeIndex", "size" }); + endian::little_int32_t root_node_index; float size; }; struct transition { - constexpr static auto keys = make_keys({ "startSequence", - "endSequence", + constexpr static auto keys = make_keys({ "startSequenceIndex", + "endSequenceIndex", "startPosition", "endPosition", "duration", "transform" }); - endian::little_int32_t start_sequence; - endian::little_int32_t end_sequence; + endian::little_int32_t start_sequence_index; + endian::little_int32_t end_sequence_index; float start_position; float end_position; float duration; @@ -515,9 +515,9 @@ namespace darkstar::dts { struct keyframe { - constexpr static auto keys = make_keys({ "position", "keyValue", "matIndex" }); + constexpr static auto keys = make_keys({ "position", "transformIndex", "matIndex" }); float position; - endian::little_uint32_t key_value; + endian::little_uint32_t transform_index; endian::little_uint32_t mat_index; }; @@ -722,16 +722,16 @@ namespace darkstar::dts struct transition { - constexpr static auto keys = make_keys({ "startSequence", - "endSequence", + constexpr static auto keys = make_keys({ "startSequenceIndex", + "endSequenceIndex", "startPosition", "endPosition", "duration", "rotation", "translation", "scale" }); - endian::little_int32_t start_sequence; - endian::little_int32_t end_sequence; + endian::little_int32_t start_sequence_index; + endian::little_int32_t end_sequence_index; float start_position; float end_position; float duration; @@ -793,12 +793,12 @@ namespace darkstar::dts struct node { - constexpr static auto keys = make_keys({ "name", "parent", "numSubSequences", "firstSubSequence", "defaultTransform" }); - endian::little_int16_t name; - endian::little_int16_t parent; + constexpr static auto keys = make_keys({ "nameIndex", "parentNodeIndex", "numSubSequences", "firstSubSequence", "defaultTransformIndex" }); + endian::little_int16_t name_index; + endian::little_int16_t parent_node_index; endian::little_int16_t num_sub_sequences; endian::little_int16_t first_sub_sequence; - endian::little_int16_t default_transform; + endian::little_int16_t default_transform_index; }; struct sub_sequence @@ -812,9 +812,9 @@ namespace darkstar::dts struct keyframe { - constexpr static auto keys = make_keys({ "position", "keyValue", "matIndex" }); + constexpr static auto keys = make_keys({ "position", "transformIndex", "matIndex" }); float position; - endian::little_uint16_t key_value; + endian::little_uint16_t transform_index; endian::little_uint16_t mat_index; }; From 33bfaeeab5b8bbe071152ab69b516b299e80257c Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 15 Aug 2020 09:44:42 +0200 Subject: [PATCH 02/10] Fixed naming in structures Figured out a reference pattern for nodes and objects and their keyframes --- src/dts-to-json/convert_dts.cpp | 112 ++++++++++++++++++++++++++++---- src/structures.hpp | 32 ++++----- 2 files changed, 115 insertions(+), 29 deletions(-) diff --git a/src/dts-to-json/convert_dts.cpp b/src/dts-to-json/convert_dts.cpp index f1344665..81fa9ad7 100644 --- a/src/dts-to-json/convert_dts.cpp +++ b/src/dts-to-json/convert_dts.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include "structures.hpp" @@ -328,6 +330,7 @@ int main(int argc, const char** argv) } auto nodes = nlohmann::json::object(); + auto transform_keyframe_mapping = std::map>{}; for (auto& node : item_as_json["nodes"]) { @@ -344,19 +347,21 @@ int main(int argc, const char** argv) { node["parentNodeIndex"] = nullptr; } - //const auto default_transform_index = element["defaultTransformIndex"].get(); - std::string name = item_as_json["names"][node["nameIndex"].get()].get(); node["name"] = name; nlohmann::json element = node; + element["objectIndexes"] = nlohmann::json::array(); + element["sequences"] = nlohmann::json::object(); auto& sub_sequences = item_as_json["subSequences"]; + + const auto default_transform_index = element["defaultTransformIndex"].get(); const auto num_sub_sequences = element["numSubSequences"].get(); - const auto first_sub_sequence = element["firstSubSequence"].get(); + const auto first_sub_sequence = element["firstSubSequenceIndex"].get(); for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) { auto& local_sub_sequence = sub_sequences[i]; @@ -364,7 +369,7 @@ int main(int argc, const char** argv) auto sequence_name = sequence["name"].get(); auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); - const auto first_key_frame = local_sub_sequence["firstKeyFrame"].get(); + const auto first_key_frame = local_sub_sequence["firstKeyFrameIndex"].get(); const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); auto& keyframes = item_as_json["keyframes"]; @@ -373,32 +378,65 @@ int main(int argc, const char** argv) auto& keyframe = keyframes[j]; keyframe["keyframeIndex"] = j; auto transform_index = keyframe["transformIndex"].get(); + + if (!element.contains("defaultTransformationSequence") && default_transform_index == transform_index) + { + element["defaultTransformationSequence"] = sequence_name; + element["defaultTransformationFrameIndex"] = key_frames.size(); + } + + transform_keyframe_mapping.emplace(transform_index, std::make_tuple(name, sequence_name, key_frames.size())); + auto& transform = item_as_json["transforms"][transform_index]; keyframe["transformation"] = transform; + const auto raw_flags = keyframe["matIndex"].get(); + std::bitset flags{ raw_flags }; + + { + auto flags_as_string = flags.to_string(); + std::reverse(flags_as_string.begin(), flags_as_string.end()); + flags = decltype(flags){ flags_as_string }; + }; + + keyframe["matIndex"] = flags.to_string(); + keyframe["isVisible"] = bool(flags[0]); + keyframe["doesVisibilityMatter"] = bool(flags[1]); + keyframe["doesMaterialMatter"] = bool(flags[2]); + keyframe["doesFrameMatter"] = bool(flags[3]); + + key_frames.emplace_back(keyframe); } } + if (!element.contains("defaultTransformationSequence")) + { + element["defaultTransformation"] = item_as_json["transforms"][default_transform_index]; + } + + //element.erase("defaultTransformIndex"); + nodes.emplace(name, element); } auto objects = nlohmann::json::object(); + auto meshes = nlohmann::json::object(); for (auto& object : item_as_json["objects"]) { - auto name = item_as_json["names"][object["nameIndex"].get()].get(); object["name"] = name; - // objects[name] = element; + + meshes.emplace(name, item_as_json["meshes"][object["meshIndex"].get()]); if (nodes.contains(name)) { auto& node = nodes[name]; object["parentObjectIndex"] = node["parentNodeIndex"]; object["nodeIndex"] = name; - node["objectIndex"] = name; + node["objectIndexes"].emplace_back(name); } @@ -407,7 +445,7 @@ int main(int argc, const char** argv) auto& sub_sequences = item_as_json["subSequences"]; const auto num_sub_sequences = element["numSubSequences"].get(); - const auto first_sub_sequence = element["firstSubSequence"].get(); + const auto first_sub_sequence = element["firstSubSequenceIndex"].get(); for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) { auto& local_sub_sequence = sub_sequences[i]; @@ -415,18 +453,56 @@ int main(int argc, const char** argv) auto sequence_name = sequence["name"].get(); auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); - const auto first_key_frame = local_sub_sequence["firstKeyFrame"].get(); + const auto first_key_frame = local_sub_sequence["firstKeyFrameIndex"].get(); const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); auto& keyframes = item_as_json["keyframes"]; for (auto j = first_key_frame; j < first_key_frame + num_key_frames; ++j) { - auto& keyframe = keyframes[j]; + nlohmann::json keyframe = keyframes[j]; keyframe["keyframeIndex"] = j; auto transform_index = keyframe["transformIndex"].get(); - auto& transform = item_as_json["transforms"][transform_index]; - keyframe["transformation"] = transform; + for (const auto& [key, node] : nodes.items()) + { + if (node["defaultTransformIndex"].get() == transform_index) + { + keyframe["transformationNodeIndex"] = key; + break; + } + } + + if (!keyframe.contains("transformationNodeIndex")) + { + if (const auto& result = transform_keyframe_mapping.find(transform_index); result != transform_keyframe_mapping.end()) + { + const auto& [node_name, other_sequence_name, key_frame_index] = result->second; + keyframe["transformationNodeIndex"] = node_name; + keyframe["transformationSequenceIndex"] = other_sequence_name; + keyframe["transformationFrameIndex"] = key_frame_index; + } + else + { + auto& transform = item_as_json["transforms"][transform_index]; + + keyframe["transformation"] = transform; + } + } + + const auto raw_flags = keyframe["matIndex"].get(); + std::bitset flags{ raw_flags }; + + { + auto flags_as_string = flags.to_string(); + std::reverse(flags_as_string.begin(), flags_as_string.end()); + flags = decltype(flags){ flags_as_string }; + }; + + keyframe["matIndex"] = flags.to_string(); + keyframe["isVisible"] = bool(flags[0]); + keyframe["doesVisibilityMatter"] = bool(flags[1]); + keyframe["doesMaterialMatter"] = bool(flags[2]); + keyframe["doesFrameMatter"] = bool(flags[3]); key_frames.emplace_back(keyframe); } @@ -472,14 +548,24 @@ int main(int argc, const char** argv) count++; } + for (auto& element : item_as_json["materialList"]["materials"]) + { + const auto raw_flags = element["flags"].get(); + + const std::bitset flags{ raw_flags }; + + element["flags"] = flags.to_string(); + } + item_as_json["nodes"] = nodes; item_as_json["objects"] = objects; item_as_json["sequences"] = sequences; + item_as_json["meshes"] = meshes; item_as_json.erase("transforms"); item_as_json.erase("keyframes"); item_as_json.erase("subSequences"); item_as_json.erase("names"); - item_as_json.erase("header"); + //item_as_json.erase("header"); auto new_file_name = file_name.string() + ".json"; { diff --git a/src/structures.hpp b/src/structures.hpp index 3126d76f..cdf8f7a0 100644 --- a/src/structures.hpp +++ b/src/structures.hpp @@ -391,11 +391,11 @@ namespace darkstar::dts struct node { - constexpr static auto keys = make_keys({ "nameIndex", "parentNodeIndex", "numSubSequences", "firstSubSequence", "defaultTransformIndex" }); + constexpr static auto keys = make_keys({ "nameIndex", "parentNodeIndex", "numSubSequences", "firstSubSequenceIndex", "defaultTransformIndex" }); endian::little_int32_t name_index; endian::little_int32_t parent_node_index; endian::little_int32_t num_sub_sequences; - endian::little_int32_t first_sub_sequence; + endian::little_int32_t first_sub_sequence_index; endian::little_int32_t default_transform_index; }; @@ -413,10 +413,10 @@ namespace darkstar::dts struct sub_sequence { - constexpr static auto keys = make_keys({ "sequenceIndex", "numKeyFrames", "firstKeyFrame" }); + constexpr static auto keys = make_keys({ "sequenceIndex", "numKeyFrames", "firstKeyFrameIndex" }); endian::little_int32_t sequence_index; endian::little_int32_t num_key_frames; - endian::little_int32_t first_key_frame; + endian::little_int32_t first_key_frame_index; }; struct keyframe @@ -438,7 +438,7 @@ namespace darkstar::dts struct object { - constexpr static auto keys = make_keys({ "nameIndex", "flags", "meshIndex", "nodeIndex", "depFlags", "dep", "objectOffset", "numSubSequences", "firstSubSequence" }); + constexpr static auto keys = make_keys({ "nameIndex", "flags", "meshIndex", "nodeIndex", "depFlags", "dep", "objectOffset", "numSubSequences", "firstSubSequenceIndex" }); endian::little_int16_t name_index; endian::little_int16_t flags; endian::little_int32_t mesh_index; @@ -447,7 +447,7 @@ namespace darkstar::dts std::array dep; vector3f object_offset; endian::little_int32_t num_sub_sequences; - endian::little_int32_t first_sub_sequence; + endian::little_int32_t first_sub_sequence_index; }; struct detail @@ -591,18 +591,18 @@ namespace darkstar::dts "cyclic", "duration", "priority", - "firstFrameTrigger", + "firstFrameTriggerIndex", "numFrameTriggers", "numIflSubSequences", - "firstIflSubSequence" }); + "firstIflSubSequenceIndex" }); endian::little_int32_t name_index; endian::little_int32_t cyclic; float duration; endian::little_int32_t priority; - endian::little_int32_t first_frame_trigger; + endian::little_int32_t first_frame_trigger_index; endian::little_int32_t num_frame_triggers; endian::little_int32_t num_ifl_sub_sequences; - endian::little_int32_t first_ifl_sub_sequence; + endian::little_int32_t first_ifl_sub_sequence_index; }; struct frame_trigger @@ -793,20 +793,20 @@ namespace darkstar::dts struct node { - constexpr static auto keys = make_keys({ "nameIndex", "parentNodeIndex", "numSubSequences", "firstSubSequence", "defaultTransformIndex" }); + constexpr static auto keys = make_keys({ "nameIndex", "parentNodeIndex", "numSubSequences", "firstSubSequenceIndex", "defaultTransformIndex" }); endian::little_int16_t name_index; endian::little_int16_t parent_node_index; endian::little_int16_t num_sub_sequences; - endian::little_int16_t first_sub_sequence; + endian::little_int16_t first_sub_sequence_index; endian::little_int16_t default_transform_index; }; struct sub_sequence { - constexpr static auto keys = make_keys({ "sequenceIndex", "numKeyFrames", "firstKeyFrame" }); + constexpr static auto keys = make_keys({ "sequenceIndex", "numKeyFrames", "firstKeyFrameIndex" }); endian::little_int16_t sequence_index; endian::little_int16_t num_key_frames; - endian::little_int16_t first_key_frame; + endian::little_int16_t first_key_frame_index; }; @@ -827,14 +827,14 @@ namespace darkstar::dts struct object { - constexpr static auto keys = make_keys({ "nameIndex", "flags", "meshIndex", "nodeIndex", "objectOffset", "numSubSequences", "firstSubSequence" }); + constexpr static auto keys = make_keys({ "nameIndex", "flags", "meshIndex", "nodeIndex", "objectOffset", "numSubSequences", "firstSubSequenceIndex" }); endian::little_int16_t name_index; endian::little_int16_t flags; endian::little_int32_t mesh_index; endian::little_int16_t node_index; vector3f object_offset; endian::little_int16_t num_sub_sequences; - endian::little_int16_t first_sub_sequence; + endian::little_int16_t first_sub_sequence_index; }; struct transition From 5dfab4dc101719de69eade36ccd2b00ea81e92c5 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 16 Aug 2020 13:16:52 +0200 Subject: [PATCH 03/10] Split files relating to binary io into their own headers Moved json formatting code to its own header --- src/binary_io.hpp | 90 +++++ src/dts-to-json/convert_dts.cpp | 542 +------------------------------ src/dts_io.hpp | 280 ++++++++++++++++ src/dts_json_formatting.hpp | 258 +++++++++++++++ src/json-to-dts/convert_json.cpp | 86 +---- 5 files changed, 641 insertions(+), 615 deletions(-) create mode 100644 src/binary_io.hpp create mode 100644 src/dts_io.hpp create mode 100644 src/dts_json_formatting.hpp diff --git a/src/binary_io.hpp b/src/binary_io.hpp new file mode 100644 index 00000000..7a22707e --- /dev/null +++ b/src/binary_io.hpp @@ -0,0 +1,90 @@ +#ifndef DARKSTARDTSCONVERTER_BINARY_IO_HPP +#define DARKSTARDTSCONVERTER_BINARY_IO_HPP + +#include +#include +#include +#include +#include +#include "structures.hpp" +#include "endian_arithmetic.hpp" + +namespace binary::io +{ + template + std::array read(std::basic_ifstream& stream) + { + std::array dest{}; + + stream.read(&dest[0], size); + + return dest; + } + + template + destination_type read(std::basic_ifstream& stream) + { + destination_type dest{}; + + stream.read(reinterpret_cast(&dest), sizeof(destination_type)); + + return dest; + } + + template + std::vector read_vector(std::basic_ifstream& stream, std::size_t size) + { + if (size == 0) + { + return {}; + } + + std::vector dest(size); + + stream.read(reinterpret_cast(&dest[0]), sizeof(destination_type) * size); + + return dest; + } + + std::string read_string(std::basic_ifstream& stream, std::size_t size, std::size_t max_size = 16) + { + std::string dest(size, '\0'); + + stream.read(reinterpret_cast(&dest[0]), size); + + // There is always an embedded \0 in the + // file if the string length is less than 16 bytes. + if (size < max_size) + { + stream.seekg(1, std::ios_base::cur); + } + + return dest; + } + + + template + void write(std::basic_ostream& stream, const std::array& value) + { + stream.write(value.data(), Size); + } + + template + void write(std::basic_ostream& stream, const ValueType& value) + { + stream.write(reinterpret_cast(&value), sizeof(value)); + } + + template + void write(std::basic_ostream& stream, const std::vector& values) + { + stream.write(reinterpret_cast(values.data()), values.size() * sizeof(ValueType)); + } + + template + void write(std::basic_ostream& stream, const ValueType* value, std::size_t size) + { + stream.write(reinterpret_cast(value), size); + } +} +#endif//DARKSTARDTSCONVERTER_BINARY_IO_HPP \ No newline at end of file diff --git a/src/dts-to-json/convert_dts.cpp b/src/dts-to-json/convert_dts.cpp index 81fa9ad7..60eadf63 100644 --- a/src/dts-to-json/convert_dts.cpp +++ b/src/dts-to-json/convert_dts.cpp @@ -1,300 +1,16 @@ #include -#include #include -#include #include #include -#include #include -#include -#include -#include "structures.hpp" #include "json_boost.hpp" #include "complex_serializer.hpp" #include "shared.hpp" +#include "dts_io.hpp" namespace fs = std::filesystem; namespace dts = darkstar::dts; -std::string read_string(std::basic_ifstream& stream, std::size_t size) -{ - std::string dest(size, '\0'); - - stream.read(reinterpret_cast(&dest[0]), size); - - // There is always an embedded \0 in the - // file if the string length is less than 16 bytes. - if (size < 16) - { - stream.seekg(1, std::ios_base::cur); - } - - return dest; -} - - -template -std::vector read_vector(std::basic_ifstream& stream, std::size_t size) -{ - if (size == 0) - { - return {}; - } - - std::vector dest(size); - - stream.read(reinterpret_cast(&dest[0]), sizeof(destination_type) * size); - - return dest; -} - - -template -std::array read(std::basic_ifstream& stream) -{ - std::array dest{}; - - stream.read(&dest[0], size); - - return dest; -} - -template -destination_type read(std::basic_ifstream& stream) -{ - destination_type dest{}; - - stream.read(reinterpret_cast(&dest), sizeof(destination_type)); - - return dest; -} - -dts::tag_header read_object_header(std::basic_ifstream& stream) -{ - dts::tag_header file_header = { - read(stream), - read(stream) - }; - - if (file_header.tag != dts::pers_tag) - { - std::stringstream msg; - msg << "There was an error trying to parse a portion of the DTS/DML file at " << stream.tellg() << ". "; - msg << "Expected " << std::string(reinterpret_cast(dts::pers_tag.data()), dts::pers_tag.size()) << " to be present but was not found.\n"; - - throw std::invalid_argument(msg.str()); - } - - file_header.class_name = read_string(stream, file_header.file_info.class_name_length); - file_header.version = read(stream); - - return file_header; -} - -template -void read_meshes(ShapeType& shape, std::size_t num_meshes, std::basic_ifstream& stream) -{ - using namespace dts::mesh; - shape.meshes.reserve(num_meshes); - - for (auto i = 0u; i < num_meshes; ++i) - { - auto mesh_tag_header = read_object_header(stream); - - if (mesh_tag_header.class_name != v1::mesh::type_name) - { - throw std::invalid_argument("The object provided is not a mesh as expected."); - } - - if (mesh_tag_header.version > 3) - { - throw std::invalid_argument("The mesh version was not version 1, 2 or 3 as expected."); - } - - if (mesh_tag_header.version == 1) - { - auto mesh_header = read(stream); - - v1::mesh mesh{ - mesh_header, - read_vector(stream, mesh_header.num_verts), - read_vector(stream, mesh_header.num_texture_verts), - read_vector(stream, mesh_header.num_faces), - read_vector(stream, mesh_header.num_frames) - }; - - shape.meshes.push_back(mesh); - } - - if (mesh_tag_header.version == 2) - { - auto mesh_header = read(stream); - - v2::mesh mesh{ - mesh_header, - read_vector(stream, mesh_header.num_verts), - read_vector(stream, mesh_header.num_texture_verts), - read_vector(stream, mesh_header.num_faces), - read_vector(stream, mesh_header.num_frames) - }; - - shape.meshes.push_back(mesh); - } - - if (mesh_tag_header.version == 3) - { - auto mesh_header = read(stream); - - v3::mesh mesh{ - mesh_header, - read_vector(stream, mesh_header.num_verts), - read_vector(stream, mesh_header.num_texture_verts), - read_vector(stream, mesh_header.num_faces), - read_vector(stream, mesh_header.num_frames) - }; - - shape.meshes.push_back(mesh); - } - } -} - - -dts::material_list_variant read_material_list(const dts::tag_header& object_header, std::basic_ifstream& stream) -{ - using namespace dts::material_list; - if (object_header.class_name != v2::material_list::type_name) - { - throw std::invalid_argument("The object was not a material list as expected."); - } - - if (object_header.version == 2) - { - auto main_header = read(stream); - - return v2::material_list{ - main_header, - read_vector(stream, main_header.num_materials * main_header.num_details) - }; - } - - if (object_header.version == 3) - { - auto main_header = read(stream); - - return v3::material_list{ - main_header, - read_vector(stream, main_header.num_materials * main_header.num_details) - }; - } - - if (object_header.version == 4) - { - auto main_header = read(stream); - - return v4::material_list{ - main_header, - read_vector(stream, main_header.num_materials * main_header.num_details) - }; - } - - throw std::invalid_argument("The material list version provided is not supported: " + std::to_string(object_header.version)); -} - -template -void read_materials(ShapeType& shape, std::basic_ifstream& stream) -{ - if (auto has_material_list = read(stream); has_material_list == 1) - { - auto object_header = read_object_header(stream); - - shape.material_list = read_material_list(object_header, stream); - } -} - - -template -ShapeType read_shape_impl(std::basic_ifstream& stream) -{ - auto header = read(stream); - ShapeType shape{ - header, - read(stream), - read_vector(stream, header.num_nodes), - read_vector(stream, header.num_sequences), - read_vector(stream, header.num_sub_sequences), - read_vector(stream, header.num_key_frames), - read_vector(stream, header.num_transforms), - read_vector(stream, header.num_names), - read_vector(stream, header.num_objects), - read_vector(stream, header.num_details), - read_vector(stream, header.num_transitions), - }; - - if constexpr (ShapeType::version > 3) - { - shape.frame_triggers = read_vector(stream, header.num_frame_triggers); - shape.footer = read(stream); - } - - read_meshes(shape, header.num_meshes, stream); - - read_materials(shape, stream); - - return shape; -} - -dts::shape_or_material_list read_shape(const fs::path& file_name, std::basic_ifstream& stream) -{ - using namespace dts::shape; - dts::tag_header file_header = read_object_header(stream); - - if (file_header.class_name == dts::material_list::v2::material_list::type_name) - { - return read_material_list(file_header, stream); - } - - if (file_header.class_name != v2::shape::type_name) - { - throw std::invalid_argument("The object provided is not a shape as expected."); - } - - if (file_header.version > 8) - { - throw std::invalid_argument("The shape is not supported."); - } - - if (file_header.version == 2) - { - return read_shape_impl(stream); - } - else if (file_header.version == 3) - { - return read_shape_impl(stream); - } - else if (file_header.version == 5) - { - return read_shape_impl(stream); - } - else if (file_header.version == 6) - { - return read_shape_impl(stream); - } - else if (file_header.version == 7) - { - return read_shape_impl(stream); - } - else if (file_header.version == 8) - { - return read_shape_impl(stream); - } - else - { - std::stringstream error; - error << file_name << " is DTS version " << file_header.version << " which is currently unsupported."; - throw std::invalid_argument(error.str()); - } -} - int main(int argc, const char** argv) { const auto files = dts::shared::find_files( @@ -315,271 +31,17 @@ int main(int argc, const char** argv) std::basic_ifstream input(file_name, std::ios::binary); - auto shape = read_shape(file_name, input); + auto shape = dts::read_shape(file_name, input); std::visit([&](const auto& item) { nlohmann::ordered_json item_as_json = item; - auto sequences = nlohmann::json::object(); - - for (auto& sequence : item_as_json["sequences"]) - { - auto sequence_name = item_as_json["names"][sequence["nameIndex"].get()].get(); - sequence["name"] = sequence_name; - sequences[sequence_name] = sequence; - } - - auto nodes = nlohmann::json::object(); - auto transform_keyframe_mapping = std::map>{}; - - for (auto& node : item_as_json["nodes"]) - { - auto parent_index = node["parentNodeIndex"].get(); - - if (parent_index != -1) - { - auto& parent_node = item_as_json["nodes"][parent_index]; - - auto parent_name = item_as_json["names"][parent_node["nameIndex"].get()].get(); - node["parentNodeIndex"] = parent_name; - } - else - { - node["parentNodeIndex"] = nullptr; - } - std::string name = item_as_json["names"][node["nameIndex"].get()].get(); - node["name"] = name; - - - nlohmann::json element = node; - - element["objectIndexes"] = nlohmann::json::array(); - - element["sequences"] = nlohmann::json::object(); - - auto& sub_sequences = item_as_json["subSequences"]; - - const auto default_transform_index = element["defaultTransformIndex"].get(); - const auto num_sub_sequences = element["numSubSequences"].get(); - const auto first_sub_sequence = element["firstSubSequenceIndex"].get(); - for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) - { - auto& local_sub_sequence = sub_sequences[i]; - auto& sequence = item_as_json["sequences"][local_sub_sequence["sequenceIndex"].get()]; - auto sequence_name = sequence["name"].get(); - - auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); - const auto first_key_frame = local_sub_sequence["firstKeyFrameIndex"].get(); - const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); - - auto& keyframes = item_as_json["keyframes"]; - for (auto j = first_key_frame; j < first_key_frame + num_key_frames; ++j) - { - auto& keyframe = keyframes[j]; - keyframe["keyframeIndex"] = j; - auto transform_index = keyframe["transformIndex"].get(); - - if (!element.contains("defaultTransformationSequence") && default_transform_index == transform_index) - { - element["defaultTransformationSequence"] = sequence_name; - element["defaultTransformationFrameIndex"] = key_frames.size(); - } - - transform_keyframe_mapping.emplace(transform_index, std::make_tuple(name, sequence_name, key_frames.size())); - - auto& transform = item_as_json["transforms"][transform_index]; - - keyframe["transformation"] = transform; - - const auto raw_flags = keyframe["matIndex"].get(); - std::bitset flags{ raw_flags }; - - { - auto flags_as_string = flags.to_string(); - std::reverse(flags_as_string.begin(), flags_as_string.end()); - flags = decltype(flags){ flags_as_string }; - }; - - keyframe["matIndex"] = flags.to_string(); - keyframe["isVisible"] = bool(flags[0]); - keyframe["doesVisibilityMatter"] = bool(flags[1]); - keyframe["doesMaterialMatter"] = bool(flags[2]); - keyframe["doesFrameMatter"] = bool(flags[3]); - - - key_frames.emplace_back(keyframe); - } - } - - if (!element.contains("defaultTransformationSequence")) - { - element["defaultTransformation"] = item_as_json["transforms"][default_transform_index]; - } - - //element.erase("defaultTransformIndex"); - - nodes.emplace(name, element); - } - - auto objects = nlohmann::json::object(); - auto meshes = nlohmann::json::object(); - - for (auto& object : item_as_json["objects"]) - { - auto name = item_as_json["names"][object["nameIndex"].get()].get(); - object["name"] = name; - - meshes.emplace(name, item_as_json["meshes"][object["meshIndex"].get()]); - - if (nodes.contains(name)) - { - auto& node = nodes[name]; - object["parentObjectIndex"] = node["parentNodeIndex"]; - object["nodeIndex"] = name; - node["objectIndexes"].emplace_back(name); - } - - - nlohmann::json element = object; - element["sequences"] = nlohmann::json::object(); - - auto& sub_sequences = item_as_json["subSequences"]; - const auto num_sub_sequences = element["numSubSequences"].get(); - const auto first_sub_sequence = element["firstSubSequenceIndex"].get(); - for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) - { - auto& local_sub_sequence = sub_sequences[i]; - auto& sequence = item_as_json["sequences"][local_sub_sequence["sequenceIndex"].get()]; - auto sequence_name = sequence["name"].get(); - - auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); - const auto first_key_frame = local_sub_sequence["firstKeyFrameIndex"].get(); - const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); - - auto& keyframes = item_as_json["keyframes"]; - for (auto j = first_key_frame; j < first_key_frame + num_key_frames; ++j) - { - nlohmann::json keyframe = keyframes[j]; - keyframe["keyframeIndex"] = j; - auto transform_index = keyframe["transformIndex"].get(); - - for (const auto& [key, node] : nodes.items()) - { - if (node["defaultTransformIndex"].get() == transform_index) - { - keyframe["transformationNodeIndex"] = key; - break; - } - } - - if (!keyframe.contains("transformationNodeIndex")) - { - if (const auto& result = transform_keyframe_mapping.find(transform_index); result != transform_keyframe_mapping.end()) - { - const auto& [node_name, other_sequence_name, key_frame_index] = result->second; - keyframe["transformationNodeIndex"] = node_name; - keyframe["transformationSequenceIndex"] = other_sequence_name; - keyframe["transformationFrameIndex"] = key_frame_index; - } - else - { - auto& transform = item_as_json["transforms"][transform_index]; - - keyframe["transformation"] = transform; - } - } - - const auto raw_flags = keyframe["matIndex"].get(); - std::bitset flags{ raw_flags }; - - { - auto flags_as_string = flags.to_string(); - std::reverse(flags_as_string.begin(), flags_as_string.end()); - flags = decltype(flags){ flags_as_string }; - }; - - keyframe["matIndex"] = flags.to_string(); - keyframe["isVisible"] = bool(flags[0]); - keyframe["doesVisibilityMatter"] = bool(flags[1]); - keyframe["doesMaterialMatter"] = bool(flags[2]); - keyframe["doesFrameMatter"] = bool(flags[3]); - - key_frames.emplace_back(keyframe); - } - } - objects.emplace(name, element); - } - - for (auto& element : item_as_json["details"]) - { - auto& node = item_as_json["nodes"][element["rootNodeIndex"].get()]; - - element["rootNodeIndex"] = node["name"]; - } - - auto count = 0u; - for (auto& element : item_as_json["transitions"]) - { - const auto start_index = element["startSequenceIndex"].get(); - const auto end_index = element["endSequenceIndex"].get(); - - element["startSequenceIndex"] = item_as_json["sequences"][start_index]["name"]; - element["endSequenceIndex"] = item_as_json["sequences"][end_index]["name"]; - - std::stringstream possible_name; - possible_name << "Trans"; - - if (count < 10) - { - possible_name << '0' << count; - } - else - { - possible_name << count; - } - - for (auto& name : item_as_json["names"]) - { - if (const auto raw_name = name.get(); raw_name.rfind(possible_name.str(), 0) == 0) - { - element["name"] = raw_name; - } - } - count++; - } - - for (auto& element : item_as_json["materialList"]["materials"]) - { - const auto raw_flags = element["flags"].get(); - - const std::bitset flags{ raw_flags }; - - element["flags"] = flags.to_string(); - } - - item_as_json["nodes"] = nodes; - item_as_json["objects"] = objects; - item_as_json["sequences"] = sequences; - item_as_json["meshes"] = meshes; - item_as_json.erase("transforms"); - item_as_json.erase("keyframes"); - item_as_json.erase("subSequences"); - item_as_json.erase("names"); - //item_as_json.erase("header"); - auto new_file_name = file_name.string() + ".json"; { std::ofstream item_as_file(new_file_name, std::ios::trunc); item_as_file << item_as_json.dump(4); } - //new_file_name = file_name.string() + ".pack"; - { - //std::basic_ofstream item_as_file(new_file_name, std::ios::trunc | std::ios::binary); - //const auto pack = nlohmann::json::to_msgpack(item_as_json); - //item_as_file.write(&pack[0], pack.size()); - } - std::stringstream msg; msg << "Created " << new_file_name << '\n'; std::cout << msg.str(); diff --git a/src/dts_io.hpp b/src/dts_io.hpp new file mode 100644 index 00000000..c7e62e6c --- /dev/null +++ b/src/dts_io.hpp @@ -0,0 +1,280 @@ +#ifndef DARKSTARDTSCONVERTER_DTS_IO_HPP +#define DARKSTARDTSCONVERTER_DTS_IO_HPP + +#include "structures.hpp" +#include "binary_io.hpp" + +namespace darkstar::dts +{ + dts::tag_header read_object_header(std::basic_ifstream& stream) + { + using namespace binary::io; + + dts::tag_header file_header = { + read(stream), + read(stream) + }; + + if (file_header.tag != dts::pers_tag) + { + std::stringstream msg; + msg << "There was an error trying to parse a portion of the DTS/DML file at " << stream.tellg() << ". "; + msg << "Expected " << std::string(reinterpret_cast(dts::pers_tag.data()), dts::pers_tag.size()) << " to be present but was not found.\n"; + + throw std::invalid_argument(msg.str()); + } + + file_header.class_name = read_string(stream, file_header.file_info.class_name_length); + file_header.version = read(stream); + + return file_header; + } + + template + void read_meshes(ShapeType& shape, std::size_t num_meshes, std::basic_ifstream& stream) + { + using namespace binary::io; + using namespace dts::mesh; + shape.meshes.reserve(num_meshes); + + for (auto i = 0u; i < num_meshes; ++i) + { + auto mesh_tag_header = read_object_header(stream); + + if (mesh_tag_header.class_name != v1::mesh::type_name) + { + throw std::invalid_argument("The object provided is not a mesh as expected."); + } + + if (mesh_tag_header.version > 3) + { + throw std::invalid_argument("The mesh version was not version 1, 2 or 3 as expected."); + } + + if (mesh_tag_header.version == 1) + { + auto mesh_header = read(stream); + + v1::mesh mesh{ + mesh_header, + read_vector(stream, mesh_header.num_verts), + read_vector(stream, mesh_header.num_texture_verts), + read_vector(stream, mesh_header.num_faces), + read_vector(stream, mesh_header.num_frames) + }; + + shape.meshes.push_back(mesh); + } + + if (mesh_tag_header.version == 2) + { + auto mesh_header = read(stream); + + v2::mesh mesh{ + mesh_header, + read_vector(stream, mesh_header.num_verts), + read_vector(stream, mesh_header.num_texture_verts), + read_vector(stream, mesh_header.num_faces), + read_vector(stream, mesh_header.num_frames) + }; + + shape.meshes.push_back(mesh); + } + + if (mesh_tag_header.version == 3) + { + auto mesh_header = read(stream); + + v3::mesh mesh{ + mesh_header, + read_vector(stream, mesh_header.num_verts), + read_vector(stream, mesh_header.num_texture_verts), + read_vector(stream, mesh_header.num_faces), + read_vector(stream, mesh_header.num_frames) + }; + + shape.meshes.push_back(mesh); + } + } + } + + dts::material_list_variant read_material_list(const dts::tag_header& object_header, std::basic_ifstream& stream) + { + using namespace binary::io; + using namespace dts::material_list; + if (object_header.class_name != v2::material_list::type_name) + { + throw std::invalid_argument("The object was not a material list as expected."); + } + + if (object_header.version == 2) + { + auto main_header = read(stream); + + return v2::material_list{ + main_header, + read_vector(stream, main_header.num_materials * main_header.num_details) + }; + } + + if (object_header.version == 3) + { + auto main_header = read(stream); + + return v3::material_list{ + main_header, + read_vector(stream, main_header.num_materials * main_header.num_details) + }; + } + + if (object_header.version == 4) + { + auto main_header = read(stream); + + return v4::material_list{ + main_header, + read_vector(stream, main_header.num_materials * main_header.num_details) + }; + } + + throw std::invalid_argument("The material list version provided is not supported: " + std::to_string(object_header.version)); + } + + template + void read_materials(ShapeType& shape, std::basic_ifstream& stream) + { + if (auto has_material_list = binary::io::read(stream); has_material_list == 1) + { + auto object_header = read_object_header(stream); + + shape.material_list = read_material_list(object_header, stream); + } + } + + template + ShapeType read_shape_impl(std::basic_ifstream& stream) + { + using namespace binary::io; + auto header = read(stream); + ShapeType shape{ + header, + read(stream), + read_vector(stream, header.num_nodes), + read_vector(stream, header.num_sequences), + read_vector(stream, header.num_sub_sequences), + read_vector(stream, header.num_key_frames), + read_vector(stream, header.num_transforms), + read_vector(stream, header.num_names), + read_vector(stream, header.num_objects), + read_vector(stream, header.num_details), + read_vector(stream, header.num_transitions), + }; + + if constexpr (ShapeType::version > 3) + { + shape.frame_triggers = read_vector(stream, header.num_frame_triggers); + shape.footer = read(stream); + } + + read_meshes(shape, header.num_meshes, stream); + + read_materials(shape, stream); + + return shape; + } + + dts::shape_or_material_list read_shape(const std::filesystem::path& file_name, std::basic_ifstream& stream) + { + using namespace dts::shape; + dts::tag_header file_header = read_object_header(stream); + + if (file_header.class_name == dts::material_list::v2::material_list::type_name) + { + return read_material_list(file_header, stream); + } + + if (file_header.class_name != v2::shape::type_name) + { + throw std::invalid_argument("The object provided is not a shape as expected."); + } + + if (file_header.version > 8) + { + throw std::invalid_argument("The shape is not supported."); + } + + if (file_header.version == 2) + { + return read_shape_impl(stream); + } + else if (file_header.version == 3) + { + return read_shape_impl(stream); + } + else if (file_header.version == 5) + { + return read_shape_impl(stream); + } + else if (file_header.version == 6) + { + return read_shape_impl(stream); + } + else if (file_header.version == 7) + { + return read_shape_impl(stream); + } + else if (file_header.version == 8) + { + return read_shape_impl(stream); + } + else + { + std::stringstream error; + error << file_name << " is DTS version " << file_header.version << " which is currently unsupported."; + throw std::invalid_argument(error.str()); + } + } + + template + void write_header(std::basic_ostream& stream, const RootType& root) + { + using namespace binary::io; + constexpr static auto empty = std::byte{ '\0' }; + boost::endian::little_uint32_t size_in_bytes{}; + write(stream, dts::pers_tag); + // This is a placeholder for the real size which will come later + write(stream, size_in_bytes); + + constexpr std::string_view type_name = std::remove_reference_t::type_name; + const boost::endian::little_int16_t type_size = static_cast(type_name.size()); + + write(stream, type_size); + write(stream, type_name.data(), type_size); + + if constexpr (type_name.size() < 16) + { + write(stream, empty); + } + + const boost::endian::little_int32_t version = std::remove_reference_t::version; + write(stream, version); + } + + void write_size(std::basic_ostream& stream, std::optional start_offset = std::nullopt) + { + boost::endian::little_uint32_t size_in_bytes{}; + + start_offset = start_offset.has_value() ? start_offset.value() + dts::pers_tag.size() : dts::pers_tag.size(); + std::uint32_t end_offset = static_cast(stream.tellp()); + + // sort out the size we reserved earlier + size_in_bytes = end_offset - start_offset.value() - sizeof(size_in_bytes); + stream.seekp(start_offset.value(), std::ios_base::beg); + + binary::io::write(stream, size_in_bytes); + + stream.seekp(end_offset, std::ios_base::beg); + } +}// namespace darkstar::dts + +#endif//DARKSTARDTSCONVERTER_DTS_IO_HPP diff --git a/src/dts_json_formatting.hpp b/src/dts_json_formatting.hpp new file mode 100644 index 00000000..343f0e88 --- /dev/null +++ b/src/dts_json_formatting.hpp @@ -0,0 +1,258 @@ +#ifndef DARKSTARDTSCONVERTER_DTS_JSON_FORMATTING_HPP +#define DARKSTARDTSCONVERTER_DTS_JSON_FORMATTING_HPP + +#include +#include +#include "json_boost.hpp" + +void format_json(nlohmann::ordered_json& item_as_json) +{ + auto sequences = nlohmann::json::object(); + + for (auto& sequence : item_as_json["sequences"]) + { + auto sequence_name = item_as_json["names"][sequence["nameIndex"].get()].get(); + sequence["name"] = sequence_name; + sequences[sequence_name] = sequence; + } + + auto nodes = nlohmann::json::object(); + auto transform_keyframe_mapping = std::map>{}; + + for (auto& node : item_as_json["nodes"]) + { + auto parent_index = node["parentNodeIndex"].get(); + + if (parent_index != -1) + { + auto& parent_node = item_as_json["nodes"][parent_index]; + + auto parent_name = item_as_json["names"][parent_node["nameIndex"].get()].get(); + node["parentNodeIndex"] = parent_name; + } + else + { + node["parentNodeIndex"] = nullptr; + } + std::string name = item_as_json["names"][node["nameIndex"].get()].get(); + node["name"] = name; + + + nlohmann::json element = node; + + element["objectIndexes"] = nlohmann::json::array(); + + element["sequences"] = nlohmann::json::object(); + + auto& sub_sequences = item_as_json["subSequences"]; + + const auto default_transform_index = element["defaultTransformIndex"].get(); + const auto num_sub_sequences = element["numSubSequences"].get(); + const auto first_sub_sequence = element["firstSubSequenceIndex"].get(); + for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) + { + auto& local_sub_sequence = sub_sequences[i]; + auto& sequence = item_as_json["sequences"][local_sub_sequence["sequenceIndex"].get()]; + auto sequence_name = sequence["name"].get(); + + auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); + const auto first_key_frame = local_sub_sequence["firstKeyFrameIndex"].get(); + const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); + + auto& keyframes = item_as_json["keyframes"]; + for (auto j = first_key_frame; j < first_key_frame + num_key_frames; ++j) + { + auto& keyframe = keyframes[j]; + keyframe["keyframeIndex"] = j; + auto transform_index = keyframe["transformIndex"].get(); + + if (!element.contains("defaultTransformationSequence") && default_transform_index == transform_index) + { + element["defaultTransformationSequence"] = sequence_name; + element["defaultTransformationFrameIndex"] = key_frames.size(); + } + + transform_keyframe_mapping.emplace(transform_index, std::make_tuple(name, sequence_name, key_frames.size())); + + auto& transform = item_as_json["transforms"][transform_index]; + + keyframe["transformation"] = transform; + + const auto raw_flags = keyframe["matIndex"].get(); + std::bitset flags{ raw_flags }; + + { + auto flags_as_string = flags.to_string(); + std::reverse(flags_as_string.begin(), flags_as_string.end()); + flags = decltype(flags){ flags_as_string }; + }; + + keyframe["matIndex"] = flags.to_string(); + keyframe["isVisible"] = bool(flags[0]); + keyframe["doesVisibilityMatter"] = bool(flags[1]); + keyframe["doesMaterialMatter"] = bool(flags[2]); + keyframe["doesFrameMatter"] = bool(flags[3]); + + + key_frames.emplace_back(keyframe); + } + } + + if (!element.contains("defaultTransformationSequence")) + { + element["defaultTransformation"] = item_as_json["transforms"][default_transform_index]; + } + + //element.erase("defaultTransformIndex"); + + nodes.emplace(name, element); + } + + auto objects = nlohmann::json::object(); + auto meshes = nlohmann::json::object(); + + for (auto& object : item_as_json["objects"]) + { + auto name = item_as_json["names"][object["nameIndex"].get()].get(); + object["name"] = name; + + meshes.emplace(name, item_as_json["meshes"][object["meshIndex"].get()]); + + if (nodes.contains(name)) + { + auto& node = nodes[name]; + object["parentObjectIndex"] = node["parentNodeIndex"]; + object["nodeIndex"] = name; + node["objectIndexes"].emplace_back(name); + } + + + nlohmann::json element = object; + element["sequences"] = nlohmann::json::object(); + + auto& sub_sequences = item_as_json["subSequences"]; + const auto num_sub_sequences = element["numSubSequences"].get(); + const auto first_sub_sequence = element["firstSubSequenceIndex"].get(); + for (auto i = first_sub_sequence; i < first_sub_sequence + num_sub_sequences; ++i) + { + auto& local_sub_sequence = sub_sequences[i]; + auto& sequence = item_as_json["sequences"][local_sub_sequence["sequenceIndex"].get()]; + auto sequence_name = sequence["name"].get(); + + auto& key_frames = element["subSequences"][sequence_name] = nlohmann::json::array(); + const auto first_key_frame = local_sub_sequence["firstKeyFrameIndex"].get(); + const auto num_key_frames = local_sub_sequence["numKeyFrames"].get(); + + auto& keyframes = item_as_json["keyframes"]; + for (auto j = first_key_frame; j < first_key_frame + num_key_frames; ++j) + { + nlohmann::json keyframe = keyframes[j]; + keyframe["keyframeIndex"] = j; + auto transform_index = keyframe["transformIndex"].get(); + + for (const auto& [key, node] : nodes.items()) + { + if (node["defaultTransformIndex"].get() == transform_index) + { + keyframe["transformationNodeIndex"] = key; + break; + } + } + + if (!keyframe.contains("transformationNodeIndex")) + { + if (const auto& result = transform_keyframe_mapping.find(transform_index); result != transform_keyframe_mapping.end()) + { + const auto& [node_name, other_sequence_name, key_frame_index] = result->second; + keyframe["transformationNodeIndex"] = node_name; + keyframe["transformationSequenceIndex"] = other_sequence_name; + keyframe["transformationFrameIndex"] = key_frame_index; + } + else + { + auto& transform = item_as_json["transforms"][transform_index]; + + keyframe["transformation"] = transform; + } + } + + const auto raw_flags = keyframe["matIndex"].get(); + std::bitset flags{ raw_flags }; + + { + auto flags_as_string = flags.to_string(); + std::reverse(flags_as_string.begin(), flags_as_string.end()); + flags = decltype(flags){ flags_as_string }; + }; + + keyframe["matIndex"] = flags.to_string(); + keyframe["isVisible"] = bool(flags[0]); + keyframe["doesVisibilityMatter"] = bool(flags[1]); + keyframe["doesMaterialMatter"] = bool(flags[2]); + keyframe["doesFrameMatter"] = bool(flags[3]); + + key_frames.emplace_back(keyframe); + } + } + objects.emplace(name, element); + } + + for (auto& element : item_as_json["details"]) + { + auto& node = item_as_json["nodes"][element["rootNodeIndex"].get()]; + + element["rootNodeIndex"] = node["name"]; + } + + auto count = 0u; + for (auto& element : item_as_json["transitions"]) + { + const auto start_index = element["startSequenceIndex"].get(); + const auto end_index = element["endSequenceIndex"].get(); + + element["startSequenceIndex"] = item_as_json["sequences"][start_index]["name"]; + element["endSequenceIndex"] = item_as_json["sequences"][end_index]["name"]; + + std::stringstream possible_name; + possible_name << "Trans"; + + if (count < 10) + { + possible_name << '0' << count; + } + else + { + possible_name << count; + } + + for (auto& name : item_as_json["names"]) + { + if (const auto raw_name = name.get(); raw_name.rfind(possible_name.str(), 0) == 0) + { + element["name"] = raw_name; + } + } + count++; + } + + for (auto& element : item_as_json["materialList"]["materials"]) + { + const auto raw_flags = element["flags"].get(); + + const std::bitset flags{ raw_flags }; + + element["flags"] = flags.to_string(); + } + + item_as_json["nodes"] = nodes; + item_as_json["objects"] = objects; + item_as_json["sequences"] = sequences; + item_as_json["meshes"] = meshes; + item_as_json.erase("transforms"); + item_as_json.erase("keyframes"); + item_as_json.erase("subSequences"); + item_as_json.erase("names"); + item_as_json.erase("header"); +} + +#endif//DARKSTARDTSCONVERTER_DTS_JSON_FORMATTING_HPP diff --git a/src/json-to-dts/convert_json.cpp b/src/json-to-dts/convert_json.cpp index 33d9d8b9..afda587c 100644 --- a/src/json-to-dts/convert_json.cpp +++ b/src/json-to-dts/convert_json.cpp @@ -1,82 +1,16 @@ #include -#include -#include #include #include #include -#include #include "structures.hpp" #include "json_boost.hpp" #include "complex_serializer.hpp" #include "shared.hpp" +#include "dts_io.hpp" namespace fs = std::filesystem; namespace dts = darkstar::dts; -template -void write(std::basic_ostream& stream, const std::array& value) -{ - stream.write(value.data(), Size); -} - -template -void write(std::basic_ostream& stream, const ValueType& value) -{ - stream.write(reinterpret_cast(&value), sizeof(value)); -} - -template -void write(std::basic_ostream& stream, const std::vector& values) -{ - stream.write(reinterpret_cast(values.data()), values.size() * sizeof(ValueType)); -} - -template -void write(std::basic_ostream& stream, const ValueType* value, std::size_t size) -{ - stream.write(reinterpret_cast(value), size); -} - -template -void write_header(std::basic_ostream& stream, const RootType& root) -{ - constexpr static auto empty = std::byte{ '\0' }; - boost::endian::little_uint32_t size_in_bytes{}; - write(stream, dts::pers_tag); - // This is a placeholder for the real size which will come later - write(stream, size_in_bytes); - - constexpr std::string_view type_name = std::remove_reference_t::type_name; - const boost::endian::little_int16_t type_size = static_cast(type_name.size()); - - write(stream, type_size); - write(stream, type_name.data(), type_size); - - if constexpr (type_name.size() < 16) - { - write(stream, empty); - } - - const boost::endian::little_int32_t version = std::remove_reference_t::version; - write(stream, version); -} - -void write_size(std::basic_ostream& stream, std::optional start_offset = std::nullopt) -{ - boost::endian::little_uint32_t size_in_bytes{}; - - start_offset = start_offset.has_value() ? start_offset.value() + dts::pers_tag.size() : dts::pers_tag.size(); - std::uint32_t end_offset = static_cast(stream.tellp()); - - // sort out the size we reserved earlier - size_in_bytes = end_offset - start_offset.value() - sizeof(size_in_bytes); - stream.seekp(start_offset.value(), std::ios_base::beg); - - write(stream, size_in_bytes); - - stream.seekp(end_offset, std::ios_base::beg); -} - bool replace(std::string& str, const std::string& from, const std::string& to) { size_t start_pos = str.rfind(from); @@ -120,15 +54,16 @@ int main(int argc, const char** argv) if (json_type_name == dts::material_list::v2::material_list::type_name) { + using namespace binary::io; const dts::material_list_variant fresh_shape = fresh_shape_json; std::basic_ofstream stream(new_file_name, std::ios::binary); std::visit([&](const auto& materials) { - write_header(stream, materials); + dts::write_header(stream, materials); write(stream, materials.header); write(stream, materials.materials); - write_size(stream); + dts::write_size(stream); }, fresh_shape); @@ -138,12 +73,13 @@ int main(int argc, const char** argv) } else if (json_type_name == dts::shape::v2::shape::type_name) { + using namespace binary::io; const dts::shape_variant fresh_shape = fresh_shape_json; std::basic_ofstream stream(new_file_name, std::ios::binary); std::visit([&](const auto& shape) { - write_header(stream, shape); + dts::write_header(stream, shape); write(stream, shape.header); write(stream, shape.data); write(stream, shape.nodes); @@ -167,13 +103,13 @@ int main(int argc, const char** argv) const auto start_offset = static_cast(stream.tellp()); std::visit([&](const auto& mesh) { - write_header(stream, mesh); + dts::write_header(stream, mesh); write(stream, mesh.header); write(stream, mesh.vertices); write(stream, mesh.texture_vertices); write(stream, mesh.faces); write(stream, mesh.frames); - write_size(stream, start_offset); + dts::write_size(stream, start_offset); }, mesh_var); } @@ -183,14 +119,14 @@ int main(int argc, const char** argv) const auto start_offset = static_cast(stream.tellp()); std::visit([&](const auto& materials) { - write_header(stream, materials); + dts::write_header(stream, materials); write(stream, materials.header); write(stream, materials.materials); - write_size(stream, start_offset); + dts::write_size(stream, start_offset); }, shape.material_list); - write_size(stream); + dts::write_size(stream); std::stringstream msg; msg << "Created " << new_file_name << '\n'; From ae50e3d1f3eedcd069b46cdfaa932a86b45815dc Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 16 Aug 2020 14:12:24 +0200 Subject: [PATCH 04/10] Added basic support for mdl file format --- CMakeLists.txt | 5 + src/binary_io.hpp | 2 +- src/complex_serializer.hpp | 2 +- src/dts_io.hpp | 2 +- src/{structures.hpp => dts_structures.hpp} | 0 src/json-to-dts/convert_json.cpp | 2 +- src/mdl-to-json/convert_mdl.cpp | 96 ++++++++++++++++ src/mdl_structures.hpp | 123 +++++++++++++++++++++ src/shared.hpp | 2 +- 9 files changed, 229 insertions(+), 5 deletions(-) rename src/{structures.hpp => dts_structures.hpp} (100%) create mode 100644 src/mdl-to-json/convert_mdl.cpp create mode 100644 src/mdl_structures.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b17fad74..ecb36405 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,22 +7,27 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}) +file(GLOB MDL_SRC_FILES src/mdl-to-json/*.cpp) file(GLOB DTS_SRC_FILES src/dts-to-json/*.cpp) file(GLOB JSON_SRC_FILES src/json-to-dts/*.cpp) +add_executable(mdl-to-json ${MDL_SRC_FILES}) add_executable(dts-to-json ${DTS_SRC_FILES}) add_executable(json-to-dts ${JSON_SRC_FILES}) include_directories(${CONAN_INCLUDE_DIRS}) include_directories(src) +conan_target_link_libraries(mdl-to-json) conan_target_link_libraries(dts-to-json) conan_target_link_libraries(json-to-dts) if(MSVC) + target_compile_options(mdl-to-json PRIVATE /W4 /WX $<$:/O2>) target_compile_options(dts-to-json PRIVATE /W4 /WX $<$:/O2>) target_compile_options(json-to-dts PRIVATE /W4 /WX $<$:/O2>) else() + target_compile_options(mdl-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(dts-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(json-to-dts PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) endif() \ No newline at end of file diff --git a/src/binary_io.hpp b/src/binary_io.hpp index 7a22707e..5547aa63 100644 --- a/src/binary_io.hpp +++ b/src/binary_io.hpp @@ -6,7 +6,7 @@ #include #include #include -#include "structures.hpp" +#include "dts_structures.hpp" #include "endian_arithmetic.hpp" namespace binary::io diff --git a/src/complex_serializer.hpp b/src/complex_serializer.hpp index d742a08d..1940cfa1 100644 --- a/src/complex_serializer.hpp +++ b/src/complex_serializer.hpp @@ -5,7 +5,7 @@ #include #include #include "json_boost.hpp" -#include "structures.hpp" +#include "dts_structures.hpp" namespace nlohmann { diff --git a/src/dts_io.hpp b/src/dts_io.hpp index c7e62e6c..84268605 100644 --- a/src/dts_io.hpp +++ b/src/dts_io.hpp @@ -1,7 +1,7 @@ #ifndef DARKSTARDTSCONVERTER_DTS_IO_HPP #define DARKSTARDTSCONVERTER_DTS_IO_HPP -#include "structures.hpp" +#include "dts_structures.hpp" #include "binary_io.hpp" namespace darkstar::dts diff --git a/src/structures.hpp b/src/dts_structures.hpp similarity index 100% rename from src/structures.hpp rename to src/dts_structures.hpp diff --git a/src/json-to-dts/convert_json.cpp b/src/json-to-dts/convert_json.cpp index afda587c..7149c97a 100644 --- a/src/json-to-dts/convert_json.cpp +++ b/src/json-to-dts/convert_json.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "structures.hpp" +#include "dts_structures.hpp" #include "json_boost.hpp" #include "complex_serializer.hpp" #include "shared.hpp" diff --git a/src/mdl-to-json/convert_mdl.cpp b/src/mdl-to-json/convert_mdl.cpp new file mode 100644 index 00000000..34eeb3f4 --- /dev/null +++ b/src/mdl-to-json/convert_mdl.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include "json_boost.hpp" +#include "shared.hpp" +#include "binary_io.hpp" +#include "mdl_structures.hpp" + +namespace fs = std::filesystem; +namespace mdl = idtech2::mdl; +namespace io = binary::io; + +int main(int argc, const char** argv) +{ + const auto files = shared::find_files( + std::vector(argv + 1, argv + argc), + ".mdl", + ".MDL"); + + std::for_each(std::execution::par_unseq, files.begin(), files.end(), [](auto&& file_name) { + try + { + { + std::stringstream msg; + msg << "Converting " << file_name.string() << '\n'; + std::cout << msg.str(); + } + + std::basic_ifstream input(file_name, std::ios::binary); + + auto header = io::read(input); + + if (header.file_tag != mdl::mdl_tag) + { + throw std::invalid_argument("The file presented does not appear to be a valid MDL file."); + } + + if (header.version != 6) + { + throw std::invalid_argument("Only version 6 MDL files are supported."); + } + + auto data_header = io::read(input); + + for (auto i = 0; i < data_header.num_skins; ++i) + { + auto skin = io::read(input); + + if (skin.group != 0 && skin.group != 1) + { + throw std::invalid_argument("Skin not parsed correctly."); + } + + auto raw_data = io::read_vector(input, + static_cast(data_header.skin_width * data_header.skin_height)); + + std::cout << "Read " << raw_data.size() << " bytes for texture\n"; + } + + auto texture_verts = io::read_vector(input, data_header.num_vertices); + + auto faces = io::read_vector(input, data_header.num_triangles); + + std::vector> frames; + frames.reserve(data_header.num_frames); + + for (auto i = 0; i < data_header.num_frames; ++i) + { + int frame_type = io::read(input); + + if (frame_type == mdl::simple_frame::frame_type) + { + frames.emplace_back(io::read(input)); + } + else + { + frames.emplace_back(io::read(input)); + } + } + + std::cout << "File has read " << input.tellg() << " out of " + << std::filesystem::file_size(file_name) << " bytes\n"; + std::cout << "File has " << frames.size() << " frames\n"; + } + catch (const std::exception& ex) + { + std::stringstream msg; + msg << file_name << " " << ex.what() << '\n'; + std::cerr << msg.str(); + } + }); + + return 0; +} \ No newline at end of file diff --git a/src/mdl_structures.hpp b/src/mdl_structures.hpp new file mode 100644 index 00000000..8dbebc2a --- /dev/null +++ b/src/mdl_structures.hpp @@ -0,0 +1,123 @@ +#ifndef MDL_STRUCTURES_HPP +#define MDL_STRUCTURES_HPP + +#include +#include +#include +#include +#include "endian_arithmetic.hpp" + +namespace idtech2::mdl +{ + namespace endian = boost::endian; + using file_tag = std::array; + + template + constexpr std::array make_keys(const char*(&&keys)[Size]) + { + std::array result; + for (auto i = 0; i < Size; i++) + { + result[i] = keys[i]; + } + return result; + } + + constexpr file_tag to_tag(const std::array values) + { + file_tag result{}; + + for (auto i = 0u; i < values.size(); i++) + { + result[i] = std::byte{ values[i] }; + } + return result; + } + + constexpr file_tag mdl_tag = to_tag({ 'I', 'D', 'P', 'O' }); + + struct vector3f + { + constexpr static auto keys = make_keys({ "x", "y", "z" }); + float x; + float y; + float z; + }; + + static_assert(sizeof(vector3f) == sizeof(std::array)); + + struct vertex + { + constexpr static auto keys = make_keys({ "x", "y", "z", "normal" }); + std::uint8_t x; + std::uint8_t y; + std::uint8_t z; + std::uint8_t normal; + }; + + struct texture_vertex + { + constexpr static auto keys = make_keys({ "onSeam", "x", "y" }); + std::uint8_t on_seam; + std::uint8_t x; + std::uint8_t y; + }; + + struct face + { + constexpr static auto keys = make_keys({ "facesFront", "vertexIndices" }); + endian::little_int32_t faces_front; + std::array vertex_indices; + }; + + struct simple_frame + { + constexpr static auto frame_type = 0; + vertex bounding_box_min; + vertex bounding_box_max; + std::array name; + }; + + using frame_type = endian::little_int32_t; + + struct group_frame + { + vertex min_position; + vertex max_position; + }; + + struct skin + { + endian::little_int32_t group; + }; + + static_assert(sizeof(skin) == sizeof(endian::little_int32_t)); + + struct file_header + { + file_tag file_tag; + endian::little_int32_t version; + }; + + struct data_header + { + vector3f scale; + vector3f translation; + float bounding_radius; + vector3f eye_position; + + endian::little_int32_t num_skins; + endian::little_int32_t skin_width; + endian::little_int32_t skin_height; + + endian::little_int32_t num_vertices; + endian::little_int32_t num_triangles; + endian::little_int32_t num_frames; + + endian::little_int32_t sync_type; + endian::little_int32_t flags; + float size; + }; +}// namespace idtech2::mdl + +#endif//DARKSTARDTSCONVERTER_STRUCTURES_HPP diff --git a/src/shared.hpp b/src/shared.hpp index 11a5fd74..176f6bb5 100644 --- a/src/shared.hpp +++ b/src/shared.hpp @@ -7,7 +7,7 @@ #include #include -namespace darkstar::dts::shared +namespace shared { namespace fs = std::filesystem; From 574e9ddf6f34637e26e482ea7d44e3d7eef756fe Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 17 Sep 2020 16:09:50 +0200 Subject: [PATCH 05/10] Fixed compiler errors with updated compiler Added initial code to be able to convert to obj --- CMakeLists.txt | 5 + src/dts-to-json/convert_dts.cpp | 5 +- src/dts-to-obj/convert_dts.cpp | 211 +++++++++++++++++++++ src/dts_io.hpp | 2 +- src/dts_json_formatting.hpp | 1 + src/dts_structures.hpp | 317 +++++++++++++++++++------------- 6 files changed, 408 insertions(+), 133 deletions(-) create mode 100644 src/dts-to-obj/convert_dts.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ecb36405..19fc51ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,11 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}) file(GLOB MDL_SRC_FILES src/mdl-to-json/*.cpp) file(GLOB DTS_SRC_FILES src/dts-to-json/*.cpp) +file(GLOB OBJ_SRC_FILES src/dts-to-obj/*.cpp) file(GLOB JSON_SRC_FILES src/json-to-dts/*.cpp) add_executable(mdl-to-json ${MDL_SRC_FILES}) add_executable(dts-to-json ${DTS_SRC_FILES}) +add_executable(dts-to-obj ${OBJ_SRC_FILES}) add_executable(json-to-dts ${JSON_SRC_FILES}) include_directories(${CONAN_INCLUDE_DIRS}) @@ -19,15 +21,18 @@ include_directories(src) conan_target_link_libraries(mdl-to-json) conan_target_link_libraries(dts-to-json) +conan_target_link_libraries(dts-to-obj) conan_target_link_libraries(json-to-dts) if(MSVC) target_compile_options(mdl-to-json PRIVATE /W4 /WX $<$:/O2>) target_compile_options(dts-to-json PRIVATE /W4 /WX $<$:/O2>) + target_compile_options(dts-to-obj PRIVATE /W4 /WX $<$:/O2>) target_compile_options(json-to-dts PRIVATE /W4 /WX $<$:/O2>) else() target_compile_options(mdl-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(dts-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) + target_compile_options(dts-to-obj PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(json-to-dts PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) endif() \ No newline at end of file diff --git a/src/dts-to-json/convert_dts.cpp b/src/dts-to-json/convert_dts.cpp index 60eadf63..b1cfdea4 100644 --- a/src/dts-to-json/convert_dts.cpp +++ b/src/dts-to-json/convert_dts.cpp @@ -7,13 +7,14 @@ #include "complex_serializer.hpp" #include "shared.hpp" #include "dts_io.hpp" +#include "dts_json_formatting.hpp" namespace fs = std::filesystem; namespace dts = darkstar::dts; int main(int argc, const char** argv) { - const auto files = dts::shared::find_files( + const auto files = shared::find_files( std::vector(argv + 1, argv + argc), ".dts", ".DTS", @@ -36,6 +37,8 @@ int main(int argc, const char** argv) std::visit([&](const auto& item) { nlohmann::ordered_json item_as_json = item; + format_json(item_as_json); + auto new_file_name = file_name.string() + ".json"; { std::ofstream item_as_file(new_file_name, std::ios::trunc); diff --git a/src/dts-to-obj/convert_dts.cpp b/src/dts-to-obj/convert_dts.cpp new file mode 100644 index 00000000..d398f71f --- /dev/null +++ b/src/dts-to-obj/convert_dts.cpp @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include +#include +#include +#include "json_boost.hpp" +#include "complex_serializer.hpp" +#include "shared.hpp" +#include "dts_io.hpp" +//#include "dts_json_formatting.hpp" + +namespace fs = std::filesystem; +namespace dts = darkstar::dts; + + +struct shape_renderer +{ + virtual void update_node(std::string_view node_name) = 0; + virtual void update_object(std::string_view object_name) = 0; + virtual void new_face(std::optional num_vertices = std::nullopt) = 0; + virtual void emit_vertex(const darkstar::dts::vector3f& vertex) = 0; + virtual void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) = 0; + + virtual ~shape_renderer() = default; + + shape_renderer() = delete; + shape_renderer(const shape_renderer&) = delete; + shape_renderer(shape_renderer&&) = delete; +}; + + +void render_dts(const dts::shape::v7::shape& some_shape, shape_renderer& renderer) +{ + //std::visit([&](const auto& item) + // { + std::vector buffer(1024, std::byte{ 0 }); + std::pmr::monotonic_buffer_resource resource{ buffer.data(), buffer.size() }; + std::pmr::map> node_indexes{ &resource }; + std::pmr::map> object_indexes{ &resource }; + std::pmr::set excluded_node_indexes{ &resource }; + + const auto& detail_level = some_shape.details[0]; + const auto root_note_index = detail_level.root_node_index; + + for (auto node = std::begin(some_shape.nodes); node != std::end(some_shape.nodes); ++node) + { + const auto index = static_cast(std::distance(std::begin(some_shape.nodes), node)); + + if (node->parent_node_index == -1 && index != root_note_index) + { + excluded_node_indexes.emplace(index); + continue; + } + + if (excluded_node_indexes.find(node->parent_node_index) != excluded_node_indexes.end()) + { + excluded_node_indexes.emplace(index); + continue; + } + + auto item = node_indexes.find(node->parent_node_index); + + if (item == node_indexes.end()) + { + item = object_indexes.emplace(node->parent_node_index, std::pmr::set{ &resource }).first; + } + + item->second.emplace(index); + } + + for (auto object = std::begin(some_shape.objects); object != std::end(some_shape.objects); ++object) + { + if (const auto item = node_indexes.find(object->node_index); + item != node_indexes.cend()) + { + auto other = object_indexes.find(object->node_index); + + if (other == object_indexes.end()) + { + other = object_indexes.emplace(object->node_index, std::pmr::set{ &resource }).first; + } + const auto index = static_cast(std::distance(std::begin(some_shape.objects), object)); + other->second.emplace(index); + } + } + + std::optional default_scale; + dts::vector3f default_translation = { 0, 0, 0}; + + for (const auto& [parent_node_index, child_node_indexes] : node_indexes) + { + if (parent_node_index != -1) + { + const auto& parent_node = some_shape.nodes[parent_node_index]; + const std::string_view parent_node_name = some_shape.names[parent_node.name_index].data(); + renderer.update_node(parent_node_name); + const auto& default_transform = some_shape.transforms[parent_node.default_transform_index]; + + // TODO get scale for default transform + if (!default_scale.has_value()) + { + default_scale = default_transform.scale; + } + + default_translation += default_transform.translation; + } + + for (const auto child_node_index : child_node_indexes) + { + std::pmr::set& objects = object_indexes[child_node_index]; + + for (const std::int32_t object_index : objects) + { + const auto& object = some_shape.objects[object_index]; + const std::string_view parent_object_name = some_shape.names[object.name_index].data(); + + renderer.update_object(parent_object_name); + + //const auto& mesh = some_shape.meshes[object.mesh_index]; + const dts::mesh::v3::mesh mesh{}; + //TODO add code to get scale and origin from mesh object + // if there is no value it is nullopt + std::optional mesh_scale; + std::optional mesh_origin; + + for (const auto& frame : mesh.frames) + { + //TODO add code to get scale and origin from frame object + // if there is no value it is a default value, which should not be the case since it should + // either exist on the mesh or the frame, or we have bad data. + dts::vector3f scale = mesh_scale.has_value() ? mesh_scale.value() : frame.scale; + + if (default_scale.has_value()) + { + scale *= default_scale.value(); + } + + dts::vector3f origin = (mesh_origin.has_value() ? mesh_origin.value() : frame.origin) + default_translation; + + for (const auto& face : mesh.faces) + { + renderer.new_face(3); + std::array vertices{ std::cref(mesh.vertices[face.vi1]), + std::cref(mesh.vertices[face.vi2]), + std::cref(mesh.vertices[face.vi3]) }; + + std::array texture_vertices{ std::cref(mesh.texture_vertices[face.ti1]), + std::cref(mesh.texture_vertices[face.ti2]), + std::cref(mesh.texture_vertices[face.ti3]) }; + + for (const auto& raw_vertex : vertices) + { + renderer.emit_vertex(raw_vertex.get() * scale + origin); + } + + for (const auto& raw_texture_vertex : texture_vertices) + { + renderer.emit_texture_vertex(raw_texture_vertex.get()); + } + } + } + } + } + } + + // }, some_shape); +} + + +int main(int argc, const char** argv) +{ + const auto files = shared::find_files( + std::vector(argv + 1, argv + argc), + ".dts", + ".DTS", + ".dml", + ".DML"); + + std::for_each(std::execution::par_unseq, files.begin(), files.end(), [](auto&& file_name) { + try + { + { + std::stringstream msg; + msg << "Converting " << file_name.string() << '\n'; + std::cout << msg.str(); + } + + std::basic_ifstream input(file_name, std::ios::binary); + + auto shape = dts::read_shape(file_name, input); + + // std::visit([&](const auto& item) { + // + // std::stringstream msg; + // msg << "Created " << item.nodes.size() << '\n'; + // std::cout << msg.str(); + // }, + // shape); + } + catch (const std::exception& ex) + { + std::stringstream msg; + msg << file_name << " " << ex.what() << '\n'; + std::cerr << msg.str(); + } + }); + + return 0; +} \ No newline at end of file diff --git a/src/dts_io.hpp b/src/dts_io.hpp index 84268605..eed7b874 100644 --- a/src/dts_io.hpp +++ b/src/dts_io.hpp @@ -264,7 +264,7 @@ namespace darkstar::dts { boost::endian::little_uint32_t size_in_bytes{}; - start_offset = start_offset.has_value() ? start_offset.value() + dts::pers_tag.size() : dts::pers_tag.size(); + start_offset = start_offset.has_value() ? start_offset.value() + static_cast(dts::pers_tag.size()) : static_cast(dts::pers_tag.size()); std::uint32_t end_offset = static_cast(stream.tellp()); // sort out the size we reserved earlier diff --git a/src/dts_json_formatting.hpp b/src/dts_json_formatting.hpp index 343f0e88..33899fd2 100644 --- a/src/dts_json_formatting.hpp +++ b/src/dts_json_formatting.hpp @@ -3,6 +3,7 @@ #include #include +#include "complex_serializer.hpp" #include "json_boost.hpp" void format_json(nlohmann::ordered_json& item_as_json) diff --git a/src/dts_structures.hpp b/src/dts_structures.hpp index cdf8f7a0..310acfca 100644 --- a/src/dts_structures.hpp +++ b/src/dts_structures.hpp @@ -60,6 +60,38 @@ namespace darkstar::dts float z; }; + vector3f operator+(const vector3f& left, const vector3f& right) + { + vector3f result{}; + + result.x = left.x + right.x; + result.y = left.y + right.y; + result.z = left.z + right.z; + + return result; + } + + vector3f operator+=(const vector3f& left, const vector3f& right) + { + return left + right; + } + + vector3f operator*(const vector3f& left, const vector3f& right) + { + vector3f result{}; + + result.x = left.x * right.x; + result.y = left.y * right.y; + result.z = left.z * right.z; + + return result; + } + + vector3f operator*=(const vector3f& left, const vector3f& right) + { + return left * right; + } + struct vector3f_pair { constexpr static auto keys = make_keys({ "min", "max" }); @@ -105,13 +137,13 @@ namespace darkstar::dts struct header { constexpr static auto keys = make_keys({ "numVerts", - "vertsPerFrame", - "numTextureVerts", - "numFaces", - "numFrames", - "scale", - "origin", - "radius" }); + "vertsPerFrame", + "numTextureVerts", + "numFaces", + "numFrames", + "scale", + "origin", + "radius" }); endian::little_int32_t num_verts; endian::little_int32_t verts_per_frame; @@ -134,6 +166,29 @@ namespace darkstar::dts static_assert(sizeof(vertex) == sizeof(std::int32_t)); + vector3f operator*(const vertex& left, const vector3f& right) + { + vector3f result{}; + + result.x = left.x * right.x; + result.y = left.y * right.y; + result.z = left.z * right.z; + + return result; + } + + + vector3f operator+(const vertex& left, const vector3f& right) + { + vector3f result{}; + + result.x = left.x + right.x; + result.y = left.y + right.y; + result.z = left.z + right.z; + + return result; + } + struct texture_vertex { constexpr static auto keys = make_keys({ "x", "y" }); @@ -178,14 +233,14 @@ namespace darkstar::dts struct header { constexpr static auto keys = make_keys({ "numVerts", - "vertsPerFrame", - "numTextureVerts", - "numFaces", - "numFrames", - "textureVertsPerFrame", - "scale", - "origin", - "radius" }); + "vertsPerFrame", + "numTextureVerts", + "numFaces", + "numFrames", + "textureVertsPerFrame", + "scale", + "origin", + "radius" }); endian::little_int32_t num_verts; endian::little_int32_t verts_per_frame; @@ -217,12 +272,12 @@ namespace darkstar::dts struct header { constexpr static auto keys = make_keys({ "numVerts", - "vertsPerFrame", - "numTextureVerts", - "numFaces", - "numFrames", - "textureVertsPerFrame", - "radius" }); + "vertsPerFrame", + "numTextureVerts", + "numFaces", + "numFrames", + "textureVertsPerFrame", + "radius" }); endian::little_int32_t num_verts; endian::little_int32_t verts_per_frame; @@ -261,7 +316,7 @@ namespace darkstar::dts struct header { constexpr static auto keys = make_keys({ "numDetails", - "numMaterials" }); + "numMaterials" }); endian::little_int32_t num_details; endian::little_int32_t num_materials; }; @@ -402,9 +457,9 @@ namespace darkstar::dts struct sequence { constexpr static auto keys = make_keys({ "nameIndex", - "cyclic", - "duration", - "priority" }); + "cyclic", + "duration", + "priority" }); endian::little_int32_t name_index; endian::little_int32_t cyclic; float duration; @@ -460,11 +515,11 @@ namespace darkstar::dts struct transition { constexpr static auto keys = make_keys({ "startSequenceIndex", - "endSequenceIndex", - "startPosition", - "endPosition", - "duration", - "transform" }); + "endSequenceIndex", + "startPosition", + "endPosition", + "duration", + "transform" }); endian::little_int32_t start_sequence_index; endian::little_int32_t end_sequence_index; float start_position; @@ -481,18 +536,18 @@ namespace darkstar::dts constexpr static auto type_name = std::string_view{ "TS::Shape" }; constexpr static auto version = 2; constexpr static auto keys = make_keys({ "header", - "data", - "nodes", - "sequences", - "subSequences", - "keyframes", - "transforms", - "names", - "objects", - "details", - "transitions", - "meshes", - "materialList" }); + "data", + "nodes", + "sequences", + "subSequences", + "keyframes", + "transforms", + "names", + "objects", + "details", + "transitions", + "meshes", + "materialList" }); header header; data data; @@ -526,18 +581,18 @@ namespace darkstar::dts constexpr static auto type_name = v2::shape::type_name; constexpr static auto version = 3; constexpr static auto keys = make_keys({ "header", - "data", - "nodes", - "sequences", - "subSequences", - "keyframes", - "transforms", - "names", - "objects", - "details", - "transitions", - "meshes", - "materialList" }); + "data", + "nodes", + "sequences", + "subSequences", + "keyframes", + "transforms", + "names", + "objects", + "details", + "transitions", + "meshes", + "materialList" }); v2::header header; v2::data data; @@ -554,23 +609,23 @@ namespace darkstar::dts material_list_variant material_list; }; - } + }// namespace shape::v3 namespace shape::v5 { struct header { constexpr static auto keys = make_keys({ "numNodes", - "numSequences", - "numSubSequences", - "numKeyFrames", - "numTransforms", - "numNames", - "numObjects", - "numDetails", - "numMeshes", - "numTransitions", - "numFrameTriggers" }); + "numSequences", + "numSubSequences", + "numKeyFrames", + "numTransforms", + "numNames", + "numObjects", + "numDetails", + "numMeshes", + "numTransitions", + "numFrameTriggers" }); endian::little_int32_t num_nodes; endian::little_int32_t num_sequences; @@ -588,13 +643,13 @@ namespace darkstar::dts struct sequence { constexpr static auto keys = make_keys({ "nameIndex", - "cyclic", - "duration", - "priority", - "firstFrameTriggerIndex", - "numFrameTriggers", - "numIflSubSequences", - "firstIflSubSequenceIndex" }); + "cyclic", + "duration", + "priority", + "firstFrameTriggerIndex", + "numFrameTriggers", + "numIflSubSequences", + "firstIflSubSequenceIndex" }); endian::little_int32_t name_index; endian::little_int32_t cyclic; float duration; @@ -608,7 +663,7 @@ namespace darkstar::dts struct frame_trigger { constexpr static auto keys = make_keys({ "position", - "value" }); + "value" }); float position; float value; }; @@ -624,20 +679,20 @@ namespace darkstar::dts constexpr static auto type_name = v2::shape::type_name; constexpr static auto version = 5; constexpr static auto keys = make_keys({ "header", - "data", - "nodes", - "sequences", - "subSequences", - "keyframes", - "transforms", - "names", - "objects", - "details", - "transitions", - "frameTriggers", - "footer", - "meshes", - "materialList" }); + "data", + "nodes", + "sequences", + "subSequences", + "keyframes", + "transforms", + "names", + "objects", + "details", + "transitions", + "frameTriggers", + "footer", + "meshes", + "materialList" }); header header; v2::data data; @@ -664,7 +719,7 @@ namespace darkstar::dts struct footer { constexpr static auto keys = make_keys({ "numDefaultMaterials", - "alwaysNode" }); + "alwaysNode" }); endian::little_int32_t num_default_materials; endian::little_int32_t always_node; }; @@ -674,20 +729,20 @@ namespace darkstar::dts constexpr static auto type_name = v2::shape::type_name; constexpr static auto version = 6; constexpr static auto keys = make_keys({ "header", - "data", - "nodes", - "sequences", - "subSequences", - "keyframes", - "transforms", - "names", - "objects", - "details", - "transitions", - "frameTriggers", - "footer", - "meshes", - "materialList" }); + "data", + "nodes", + "sequences", + "subSequences", + "keyframes", + "transforms", + "names", + "objects", + "details", + "transitions", + "frameTriggers", + "footer", + "meshes", + "materialList" }); v5::header header; v2::data data; @@ -745,20 +800,20 @@ namespace darkstar::dts constexpr static auto type_name = v2::shape::type_name; constexpr static auto version = 7; constexpr static auto keys = make_keys({ "header", - "data", - "nodes", - "sequences", - "subSequences", - "keyframes", - "transforms", - "names", - "objects", - "details", - "transitions", - "frameTriggers", - "footer", - "meshes", - "materialList" }); + "data", + "nodes", + "sequences", + "subSequences", + "keyframes", + "transforms", + "names", + "objects", + "details", + "transitions", + "frameTriggers", + "footer", + "meshes", + "materialList" }); v5::header header; v2::data data; @@ -858,20 +913,20 @@ namespace darkstar::dts constexpr static auto type_name = v2::shape::type_name; constexpr static auto version = 8; constexpr static auto keys = make_keys({ "header", - "data", - "nodes", - "sequences", - "subSequences", - "keyframes", - "transforms", - "names", - "objects", - "details", - "transitions", - "frameTriggers", - "footer", - "meshes", - "materialList" }); + "data", + "nodes", + "sequences", + "subSequences", + "keyframes", + "transforms", + "names", + "objects", + "details", + "transitions", + "frameTriggers", + "footer", + "meshes", + "materialList" }); v5::header header; data data; @@ -891,7 +946,7 @@ namespace darkstar::dts material_list_variant material_list; }; }// namespace shape::v8 - + using shape_variant = std::variant; using shape_or_material_list = std::variant; From 9c55f37da56a43865c2e46490ca29dd8497fbcc3 Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 18 Sep 2020 10:28:37 +0200 Subject: [PATCH 06/10] Added support for properly converting dts files to obj --- src/dts-to-obj/convert_dts.cpp | 298 +++++++++++++++++++++------------ src/dts_json_formatting.hpp | 2 +- src/dts_structures.hpp | 34 ++-- 3 files changed, 204 insertions(+), 130 deletions(-) diff --git a/src/dts-to-obj/convert_dts.cpp b/src/dts-to-obj/convert_dts.cpp index d398f71f..0a4def50 100644 --- a/src/dts-to-obj/convert_dts.cpp +++ b/src/dts-to-obj/convert_dts.cpp @@ -3,7 +3,9 @@ #include #include #include -#include +#include +#include +#include #include #include "json_boost.hpp" #include "complex_serializer.hpp" @@ -14,158 +16,171 @@ namespace fs = std::filesystem; namespace dts = darkstar::dts; +// helper type for the visitor #4 +template +struct overloaded : Ts... +{ + using Ts::operator()...; +}; +// explicit deduction guide (not needed as of C++20) +template +overloaded(Ts...) -> overloaded; struct shape_renderer { virtual void update_node(std::string_view node_name) = 0; virtual void update_object(std::string_view object_name) = 0; - virtual void new_face(std::optional num_vertices = std::nullopt) = 0; + virtual void new_face(std::size_t num_vertices) = 0; + virtual void end_face() = 0; virtual void emit_vertex(const darkstar::dts::vector3f& vertex) = 0; virtual void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) = 0; virtual ~shape_renderer() = default; - shape_renderer() = delete; + shape_renderer() = default; shape_renderer(const shape_renderer&) = delete; shape_renderer(shape_renderer&&) = delete; }; - -void render_dts(const dts::shape::v7::shape& some_shape, shape_renderer& renderer) +template +void render_dts(const ShapeType& shape, shape_renderer& renderer, std::optional detail_level_index = std::nullopt) { - //std::visit([&](const auto& item) - // { - std::vector buffer(1024, std::byte{ 0 }); + std::vector buffer(8192, std::byte{ 0 }); std::pmr::monotonic_buffer_resource resource{ buffer.data(), buffer.size() }; - std::pmr::map> node_indexes{ &resource }; - std::pmr::map> object_indexes{ &resource }; - std::pmr::set excluded_node_indexes{ &resource }; - const auto& detail_level = some_shape.details[0]; + using transform_set = std::pmr::set; + + detail_level_index = detail_level_index.has_value() ? detail_level_index : 0; + const auto& detail_level = shape.details[detail_level_index.value()]; const auto root_note_index = detail_level.root_node_index; - for (auto node = std::begin(some_shape.nodes); node != std::end(some_shape.nodes); ++node) + std::pmr::map node_indexes{ &resource }; + std::pmr::map> object_indexes{ &resource }; + + object_indexes.emplace(root_note_index, std::pmr::set{ &resource }); + + for (const auto& [parent_index, objects] : object_indexes) { - const auto index = static_cast(std::distance(std::begin(some_shape.nodes), node)); + const auto [location, added] = node_indexes.emplace(parent_index, transform_set{ &resource }); - if (node->parent_node_index == -1 && index != root_note_index) - { - excluded_node_indexes.emplace(index); - continue; - } + auto transform_index = shape.nodes[parent_index].default_transform_index; + const auto* transform = &shape.transforms[transform_index]; + location->second.emplace(transform); - if (excluded_node_indexes.find(node->parent_node_index) != excluded_node_indexes.end()) + for (auto other_node = std::begin(shape.nodes); other_node != std::end(shape.nodes); ++other_node) { - excluded_node_indexes.emplace(index); - continue; - } + if (other_node->parent_node_index == parent_index) + { + const auto index = static_cast(std::distance(std::begin(shape.nodes), other_node)); - auto item = node_indexes.find(node->parent_node_index); + object_indexes.emplace(index, std::pmr::set{ &resource }); - if (item == node_indexes.end()) - { - item = object_indexes.emplace(node->parent_node_index, std::pmr::set{ &resource }).first; + for (const auto* other_transform : node_indexes[parent_index]) + { + const auto [child_location, child_added] = node_indexes.emplace(index, transform_set{ &resource }); + child_location->second.emplace(other_transform); + } + } } - - item->second.emplace(index); } - for (auto object = std::begin(some_shape.objects); object != std::end(some_shape.objects); ++object) + for (auto object = std::begin(shape.objects); object != std::end(shape.objects); ++object) { - if (const auto item = node_indexes.find(object->node_index); - item != node_indexes.cend()) + if (const auto item = object_indexes.find(object->node_index); + item != object_indexes.cend()) { - auto other = object_indexes.find(object->node_index); - - if (other == object_indexes.end()) - { - other = object_indexes.emplace(object->node_index, std::pmr::set{ &resource }).first; - } - const auto index = static_cast(std::distance(std::begin(some_shape.objects), object)); - other->second.emplace(index); + const auto index = static_cast(std::distance(std::begin(shape.objects), object)); + item->second.emplace(index); } } - std::optional default_scale; - dts::vector3f default_translation = { 0, 0, 0}; - - for (const auto& [parent_node_index, child_node_indexes] : node_indexes) + for (const auto& [child_node_index, transforms] : node_indexes) { - if (parent_node_index != -1) - { - const auto& parent_node = some_shape.nodes[parent_node_index]; - const std::string_view parent_node_name = some_shape.names[parent_node.name_index].data(); - renderer.update_node(parent_node_name); - const auto& default_transform = some_shape.transforms[parent_node.default_transform_index]; + std::optional default_scale; + dts::vector3f default_translation = { 0, 0, 0 }; - // TODO get scale for default transform - if (!default_scale.has_value()) + for (auto transform : transforms) + { + if constexpr (std::remove_reference_t::version < 8) { - default_scale = default_transform.scale; + if (!default_scale.has_value()) + { + default_scale = transform->scale; + } } - - default_translation += default_transform.translation; + default_translation = default_translation + transform->translation; } - for (const auto child_node_index : child_node_indexes) + const auto& child_node = shape.nodes[child_node_index]; + const std::string_view child_node_name = shape.names[child_node.name_index].data(); + renderer.update_node(child_node_name); + + auto& objects = object_indexes[child_node_index]; + + for (const std::int32_t object_index : objects) { - std::pmr::set& objects = object_indexes[child_node_index]; + const auto& object = shape.objects[object_index]; + const std::string_view parent_object_name = shape.names[object.name_index].data(); - for (const std::int32_t object_index : objects) - { - const auto& object = some_shape.objects[object_index]; - const std::string_view parent_object_name = some_shape.names[object.name_index].data(); + renderer.update_object(parent_object_name); + + std::visit([&](const auto& mesh) { + dts::vector3f mesh_scale; + dts::vector3f mesh_origin; + + if constexpr (std::remove_reference_t::version < 3) + { + mesh_scale = mesh.header.scale; + mesh_origin = mesh.header.origin; + } + else if constexpr (std::remove_reference_t::version >= 3) + { + if (!mesh.frames.empty()) + { + mesh_scale = mesh.frames[0].scale; + mesh_origin = mesh.frames[0].origin; + } + else + { + mesh_scale = { 1, 1, 1 }; + mesh_origin = { 0, 0, 0 }; + } + } - renderer.update_object(parent_object_name); + if (default_scale.has_value()) + { + mesh_scale = mesh_scale * default_scale.value(); + } - //const auto& mesh = some_shape.meshes[object.mesh_index]; - const dts::mesh::v3::mesh mesh{}; - //TODO add code to get scale and origin from mesh object - // if there is no value it is nullopt - std::optional mesh_scale; - std::optional mesh_origin; + mesh_origin = mesh_origin + default_translation; - for (const auto& frame : mesh.frames) + for (const auto& face : mesh.faces) { - //TODO add code to get scale and origin from frame object - // if there is no value it is a default value, which should not be the case since it should - // either exist on the mesh or the frame, or we have bad data. - dts::vector3f scale = mesh_scale.has_value() ? mesh_scale.value() : frame.scale; + renderer.new_face(3); + std::array vertices{ std::cref(mesh.vertices[face.vi1]), + std::cref(mesh.vertices[face.vi2]), + std::cref(mesh.vertices[face.vi3]) }; + + std::array texture_vertices{ std::cref(mesh.texture_vertices[face.ti1]), + std::cref(mesh.texture_vertices[face.ti2]), + std::cref(mesh.texture_vertices[face.ti3]) }; - if (default_scale.has_value()) + for (const auto& raw_vertex : vertices) { - scale *= default_scale.value(); + renderer.emit_vertex(raw_vertex.get() * mesh_scale + mesh_origin); } - dts::vector3f origin = (mesh_origin.has_value() ? mesh_origin.value() : frame.origin) + default_translation; - - for (const auto& face : mesh.faces) + for (const auto& raw_texture_vertex : texture_vertices) { - renderer.new_face(3); - std::array vertices{ std::cref(mesh.vertices[face.vi1]), - std::cref(mesh.vertices[face.vi2]), - std::cref(mesh.vertices[face.vi3]) }; - - std::array texture_vertices{ std::cref(mesh.texture_vertices[face.ti1]), - std::cref(mesh.texture_vertices[face.ti2]), - std::cref(mesh.texture_vertices[face.ti3]) }; - - for (const auto& raw_vertex : vertices) - { - renderer.emit_vertex(raw_vertex.get() * scale + origin); - } - - for (const auto& raw_texture_vertex : texture_vertices) - { - renderer.emit_texture_vertex(raw_texture_vertex.get()); - } + renderer.emit_texture_vertex(raw_texture_vertex.get()); } + + renderer.end_face(); } - } + }, + shape.meshes[object.mesh_index]); } } - - // }, some_shape); } @@ -191,13 +206,84 @@ int main(int argc, const char** argv) auto shape = dts::read_shape(file_name, input); - // std::visit([&](const auto& item) { - // - // std::stringstream msg; - // msg << "Created " << item.nodes.size() << '\n'; - // std::cout << msg.str(); - // }, - // shape); + struct obj_render final : shape_renderer + { + std::optional new_face_str = std::nullopt; + std::size_t face_count = 0; + std::ofstream& output; + + obj_render(std::ofstream& output) + : output(output) + { + output << std::setprecision(32); + } + + void update_node(std::string_view) override + { + } + + void update_object(std::string_view object_name) override + { + output << "o " << object_name << '\n'; + } + + void new_face(std::size_t num_vertices) override + { + std::stringstream stream; + stream << "\tf"; + + for (auto i = 1u; i <= num_vertices; ++i) + { + stream << " " << face_count + i << "/" << face_count + i; + } + + stream << '\n'; + + new_face_str = stream.str(); + + face_count += num_vertices; + } + + void end_face() override + { + if (new_face_str.has_value()) + { + output << new_face_str.value(); + } + } + + void emit_vertex(const darkstar::dts::vector3f& vertex) override + { + output << "\tv " << vertex.x << ' ' << vertex.y << ' ' << vertex.z << '\n'; + } + + void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) override + { + output << "\tvt " << vertex.x << ' ' << vertex.y << '\n'; + } + }; + + std::visit(overloaded{ + [&](const dts::shape_variant& core_shape) + { + std::visit([&](const auto& main_shape) { + for (auto i = 0u; i < main_shape.details.size(); ++i) + { + const auto& detail_level = main_shape.details[i]; + const auto root_node = main_shape.nodes[detail_level.root_node_index]; + const std::string root_node_name = main_shape.names[root_node.name_index].data(); + std::ofstream output(file_name.string() + "." + root_node_name + ".obj", std::ios::trunc); + auto renderer = obj_render{output}; + + render_dts(main_shape, renderer, i); + } + }, + core_shape); + }, + [&](const dts::material_list_variant&) { + //Do nothing + } }, + shape); } catch (const std::exception& ex) { diff --git a/src/dts_json_formatting.hpp b/src/dts_json_formatting.hpp index 33899fd2..2df49270 100644 --- a/src/dts_json_formatting.hpp +++ b/src/dts_json_formatting.hpp @@ -73,7 +73,7 @@ void format_json(nlohmann::ordered_json& item_as_json) element["defaultTransformationFrameIndex"] = key_frames.size(); } - transform_keyframe_mapping.emplace(transform_index, std::make_tuple(name, sequence_name, key_frames.size())); + transform_keyframe_mapping.emplace(transform_index, std::make_tuple(name, sequence_name, static_cast(key_frames.size()))); auto& transform = item_as_json["transforms"][transform_index]; diff --git a/src/dts_structures.hpp b/src/dts_structures.hpp index 310acfca..39a7d1b7 100644 --- a/src/dts_structures.hpp +++ b/src/dts_structures.hpp @@ -62,35 +62,23 @@ namespace darkstar::dts vector3f operator+(const vector3f& left, const vector3f& right) { - vector3f result{}; - - result.x = left.x + right.x; - result.y = left.y + right.y; - result.z = left.z + right.z; - - return result; + return { left.x + right.x, left.y + right.y, left.z + right.z }; } - vector3f operator+=(const vector3f& left, const vector3f& right) - { - return left + right; - } +// vector3f operator+=(const vector3f& left, const vector3f& right) +// { +// return left + right; +// } vector3f operator*(const vector3f& left, const vector3f& right) { - vector3f result{}; - - result.x = left.x * right.x; - result.y = left.y * right.y; - result.z = left.z * right.z; - - return result; - } - - vector3f operator*=(const vector3f& left, const vector3f& right) - { - return left * right; + return { left.x * right.x, left.y * right.y, left.z * right.z }; } +// +// vector3f operator*=(const vector3f& left, const vector3f& right) +// { +// return left * right; +// } struct vector3f_pair { From 8a4b2e078cde56cbf254dd53e2d6608ccc2b9397 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 19 Sep 2020 12:28:28 +0200 Subject: [PATCH 07/10] Added dts viewer with open gl support --- .gitignore | 4 +- CMakeLists.txt | 8 +- conanfile.py | 18 ++- src/dts-to-obj/convert_dts.cpp | 233 ++++++--------------------------- src/dts-viewer/dts-viewer.cpp | 227 ++++++++++++++++++++++++++++++++ src/dts-viewer/settings.toml | 13 ++ src/dts-viewer/sfml_keys.hpp | 103 +++++++++++++++ src/dts_io.hpp | 1 + src/dts_render.hpp | 171 ++++++++++++++++++++++++ 9 files changed, 578 insertions(+), 200 deletions(-) create mode 100644 src/dts-viewer/dts-viewer.cpp create mode 100644 src/dts-viewer/settings.toml create mode 100644 src/dts-viewer/sfml_keys.hpp create mode 100644 src/dts_render.hpp diff --git a/.gitignore b/.gitignore index 1516be0f..60e0481d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ venv/ build/ cmake-build-debug/ +cmake-build-release/ +packages/ bin/ x64/ dist/ @@ -25,4 +27,4 @@ activate.sh deactivate.bat deactivate.ps1 deactivate.sh -environment.*.env \ No newline at end of file +environment.*.env diff --git a/CMakeLists.txt b/CMakeLists.txt index 19fc51ad..0872b8a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,28 +11,34 @@ file(GLOB MDL_SRC_FILES src/mdl-to-json/*.cpp) file(GLOB DTS_SRC_FILES src/dts-to-json/*.cpp) file(GLOB OBJ_SRC_FILES src/dts-to-obj/*.cpp) file(GLOB JSON_SRC_FILES src/json-to-dts/*.cpp) +file(GLOB DTS_VIEWER_SRC_FILES src/dts-viewer/*.cpp) + add_executable(mdl-to-json ${MDL_SRC_FILES}) add_executable(dts-to-json ${DTS_SRC_FILES}) add_executable(dts-to-obj ${OBJ_SRC_FILES}) add_executable(json-to-dts ${JSON_SRC_FILES}) +add_executable(dts-viewer ${DTS_VIEWER_SRC_FILES}) include_directories(${CONAN_INCLUDE_DIRS}) +include_directories(packages/include) include_directories(src) conan_target_link_libraries(mdl-to-json) conan_target_link_libraries(dts-to-json) conan_target_link_libraries(dts-to-obj) conan_target_link_libraries(json-to-dts) - +target_link_libraries(dts-viewer PRIVATE glu32 ${CONAN_LIBS}) if(MSVC) target_compile_options(mdl-to-json PRIVATE /W4 /WX $<$:/O2>) target_compile_options(dts-to-json PRIVATE /W4 /WX $<$:/O2>) target_compile_options(dts-to-obj PRIVATE /W4 /WX $<$:/O2>) target_compile_options(json-to-dts PRIVATE /W4 /WX $<$:/O2>) + target_compile_options(dts-viewer PRIVATE /W4 /WX $<$:/O2>) else() target_compile_options(mdl-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(dts-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(dts-to-obj PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(json-to-dts PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) + target_compile_options(dts-viewer PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) endif() \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index 52023686..610b4ba6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -2,9 +2,10 @@ import os class LocalConanFile(ConanFile): - build_requires = "cmake/3.17.3", "cppcheck_installer/2.0@bincrafters/stable" + system_requires = "opengl/system" + build_requires = "cmake/3.17.3", "cppcheck_installer/2.0@bincrafters/stable", "toml11/3.4.0" settings = "os", "compiler", "build_type", "arch" - requires = "boost_endian/1.69.0@bincrafters/stable", "nlohmann_json/3.9.0" + requires = "boost_endian/1.69.0@bincrafters/stable", "nlohmann_json/3.9.0", "imgui-sfml/2.1@bincrafters/stable" generators = "cmake", "virtualenv" build_folder = "build" @@ -15,3 +16,16 @@ def build(self): self.run("cppcheck src --error-exitcode=1") cmake.build() + def imports(self): + registry_url = "https://www.khronos.org/registry" + + if not os.path.exists("packages/include/KHR/khrplatform.h"): + tools.download(f"{registry_url}/EGL/api/KHR/khrplatform.h", "packages/include/KHR/khrplatform.h") + tools.download(f"{registry_url}/OpenGL/api/GL/glext.h", "packages/include/GL/glext.h") + tools.download(f"{registry_url}/OpenGL/api/GL/glxext.h", "packages/include/GL/glxext.h") + tools.download(f"{registry_url}/OpenGL/api/GL/wgl.h", "packages/include/GL/wgl.h") + tools.download(f"{registry_url}/OpenGL/api/GL/wglext.h", "packages/include/GL/wglext.h") + tools.download(f"{registry_url}/OpenGL/api/GL/glcorearb.h", "packages/include/GL/glcorearb.h") + + + diff --git a/src/dts-to-obj/convert_dts.cpp b/src/dts-to-obj/convert_dts.cpp index 0a4def50..bb825ed4 100644 --- a/src/dts-to-obj/convert_dts.cpp +++ b/src/dts-to-obj/convert_dts.cpp @@ -6,13 +6,11 @@ #include #include #include -#include #include "json_boost.hpp" #include "complex_serializer.hpp" #include "shared.hpp" #include "dts_io.hpp" -//#include "dts_json_formatting.hpp" - +#include "dts_render.hpp" namespace fs = std::filesystem; namespace dts = darkstar::dts; @@ -26,163 +24,63 @@ struct overloaded : Ts... template overloaded(Ts...) -> overloaded; -struct shape_renderer -{ - virtual void update_node(std::string_view node_name) = 0; - virtual void update_object(std::string_view object_name) = 0; - virtual void new_face(std::size_t num_vertices) = 0; - virtual void end_face() = 0; - virtual void emit_vertex(const darkstar::dts::vector3f& vertex) = 0; - virtual void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) = 0; - - virtual ~shape_renderer() = default; - shape_renderer() = default; - shape_renderer(const shape_renderer&) = delete; - shape_renderer(shape_renderer&&) = delete; -}; - -template -void render_dts(const ShapeType& shape, shape_renderer& renderer, std::optional detail_level_index = std::nullopt) +struct obj_render final : shape_renderer { - std::vector buffer(8192, std::byte{ 0 }); - std::pmr::monotonic_buffer_resource resource{ buffer.data(), buffer.size() }; - - using transform_set = std::pmr::set; - - detail_level_index = detail_level_index.has_value() ? detail_level_index : 0; - const auto& detail_level = shape.details[detail_level_index.value()]; - const auto root_note_index = detail_level.root_node_index; + std::optional new_face_str = std::nullopt; + std::size_t face_count = 0; + std::ofstream& output; - std::pmr::map node_indexes{ &resource }; - std::pmr::map> object_indexes{ &resource }; - - object_indexes.emplace(root_note_index, std::pmr::set{ &resource }); - - for (const auto& [parent_index, objects] : object_indexes) + obj_render(std::ofstream& output) + : output(output) { - const auto [location, added] = node_indexes.emplace(parent_index, transform_set{ &resource }); - - auto transform_index = shape.nodes[parent_index].default_transform_index; - const auto* transform = &shape.transforms[transform_index]; - location->second.emplace(transform); - - for (auto other_node = std::begin(shape.nodes); other_node != std::end(shape.nodes); ++other_node) - { - if (other_node->parent_node_index == parent_index) - { - const auto index = static_cast(std::distance(std::begin(shape.nodes), other_node)); - - object_indexes.emplace(index, std::pmr::set{ &resource }); + output << std::setprecision(32); + } - for (const auto* other_transform : node_indexes[parent_index]) - { - const auto [child_location, child_added] = node_indexes.emplace(index, transform_set{ &resource }); - child_location->second.emplace(other_transform); - } - } - } + void update_node(std::string_view) override + { } - for (auto object = std::begin(shape.objects); object != std::end(shape.objects); ++object) + void update_object(std::string_view object_name) override { - if (const auto item = object_indexes.find(object->node_index); - item != object_indexes.cend()) - { - const auto index = static_cast(std::distance(std::begin(shape.objects), object)); - item->second.emplace(index); - } + output << "o " << object_name << '\n'; } - for (const auto& [child_node_index, transforms] : node_indexes) + void new_face(std::size_t num_vertices) override { - std::optional default_scale; - dts::vector3f default_translation = { 0, 0, 0 }; + std::stringstream stream; + stream << "\tf"; - for (auto transform : transforms) + for (auto i = 1u; i <= num_vertices; ++i) { - if constexpr (std::remove_reference_t::version < 8) - { - if (!default_scale.has_value()) - { - default_scale = transform->scale; - } - } - default_translation = default_translation + transform->translation; + stream << " " << face_count + i << "/" << face_count + i; } - const auto& child_node = shape.nodes[child_node_index]; - const std::string_view child_node_name = shape.names[child_node.name_index].data(); - renderer.update_node(child_node_name); - - auto& objects = object_indexes[child_node_index]; - - for (const std::int32_t object_index : objects) - { - const auto& object = shape.objects[object_index]; - const std::string_view parent_object_name = shape.names[object.name_index].data(); - - renderer.update_object(parent_object_name); - - std::visit([&](const auto& mesh) { - dts::vector3f mesh_scale; - dts::vector3f mesh_origin; + stream << '\n'; - if constexpr (std::remove_reference_t::version < 3) - { - mesh_scale = mesh.header.scale; - mesh_origin = mesh.header.origin; - } - else if constexpr (std::remove_reference_t::version >= 3) - { - if (!mesh.frames.empty()) - { - mesh_scale = mesh.frames[0].scale; - mesh_origin = mesh.frames[0].origin; - } - else - { - mesh_scale = { 1, 1, 1 }; - mesh_origin = { 0, 0, 0 }; - } - } + new_face_str = stream.str(); - if (default_scale.has_value()) - { - mesh_scale = mesh_scale * default_scale.value(); - } - - mesh_origin = mesh_origin + default_translation; - - for (const auto& face : mesh.faces) - { - renderer.new_face(3); - std::array vertices{ std::cref(mesh.vertices[face.vi1]), - std::cref(mesh.vertices[face.vi2]), - std::cref(mesh.vertices[face.vi3]) }; - - std::array texture_vertices{ std::cref(mesh.texture_vertices[face.ti1]), - std::cref(mesh.texture_vertices[face.ti2]), - std::cref(mesh.texture_vertices[face.ti3]) }; - - for (const auto& raw_vertex : vertices) - { - renderer.emit_vertex(raw_vertex.get() * mesh_scale + mesh_origin); - } - - for (const auto& raw_texture_vertex : texture_vertices) - { - renderer.emit_texture_vertex(raw_texture_vertex.get()); - } + face_count += num_vertices; + } - renderer.end_face(); - } - }, - shape.meshes[object.mesh_index]); + void end_face() override + { + if (new_face_str.has_value()) + { + output << new_face_str.value(); } } -} + void emit_vertex(const darkstar::dts::vector3f& vertex) override + { + output << "\tv " << vertex.x << ' ' << vertex.y << ' ' << vertex.z << '\n'; + } + + void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) override + { + output << "\tvt " << vertex.x << ' ' << vertex.y << '\n'; + } +}; int main(int argc, const char** argv) { @@ -206,63 +104,6 @@ int main(int argc, const char** argv) auto shape = dts::read_shape(file_name, input); - struct obj_render final : shape_renderer - { - std::optional new_face_str = std::nullopt; - std::size_t face_count = 0; - std::ofstream& output; - - obj_render(std::ofstream& output) - : output(output) - { - output << std::setprecision(32); - } - - void update_node(std::string_view) override - { - } - - void update_object(std::string_view object_name) override - { - output << "o " << object_name << '\n'; - } - - void new_face(std::size_t num_vertices) override - { - std::stringstream stream; - stream << "\tf"; - - for (auto i = 1u; i <= num_vertices; ++i) - { - stream << " " << face_count + i << "/" << face_count + i; - } - - stream << '\n'; - - new_face_str = stream.str(); - - face_count += num_vertices; - } - - void end_face() override - { - if (new_face_str.has_value()) - { - output << new_face_str.value(); - } - } - - void emit_vertex(const darkstar::dts::vector3f& vertex) override - { - output << "\tv " << vertex.x << ' ' << vertex.y << ' ' << vertex.z << '\n'; - } - - void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) override - { - output << "\tvt " << vertex.x << ' ' << vertex.y << '\n'; - } - }; - std::visit(overloaded{ [&](const dts::shape_variant& core_shape) { diff --git a/src/dts-viewer/dts-viewer.cpp b/src/dts-viewer/dts-viewer.cpp new file mode 100644 index 00000000..8b971f2d --- /dev/null +++ b/src/dts-viewer/dts-viewer.cpp @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include + +//#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dts_io.hpp" +#include "dts_render.hpp" +#include "sfml_keys.hpp" + +namespace fs = std::filesystem; +namespace dts = darkstar::dts; +// helper type for the visitor #4 +template +struct overloaded : Ts... +{ + using Ts::operator()...; +}; +// explicit deduction guide (not needed as of C++20) +template +overloaded(Ts...) -> overloaded; + +struct gl_renderer final : shape_renderer +{ + const std::array max_colour = { 255, 255, 0 }; + std::string_view current_object_name; + std::uint8_t num_faces = 0; + std::map& visible_nodes; + std::map::iterator current_node; + + gl_renderer(std::map& visible_nodes) : visible_nodes(visible_nodes) + { + current_node = visible_nodes.end(); + } + + void update_node(std::string_view node_name) override + { + current_node = visible_nodes.emplace(node_name, true).first; + } + + void update_object(std::string_view object_name) override + { + num_faces = 0; + current_object_name = object_name; + } + + void new_face(std::size_t) override + { + if (current_node->second) + { + const auto [red, green, blue] = max_colour; + glColor3ub(red - num_faces, green - num_faces, std::uint8_t(current_object_name.size())); + num_faces += 255 / 15; + } + } + + void end_face() override + { + } + + void emit_vertex(const darkstar::dts::vector3f& vertex) override + { + if (current_node->second) + { + glVertex3f(vertex.x, vertex.y, vertex.z); + } + } + + void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex&) override + { + } +}; + + +int main(int, const char** argv) +{ + //const auto hello_toml = toml::parse("settings.toml"); + + // Create the main window + sf::RenderWindow window(sf::VideoMode(800, 600, 32), "SFML OpenGL"); + + ImGui::SFML::Init(window); + + //sf::Color bgColor; + + std::array color = { 0.f, 0.f, 0.f }; + std::array windowTitle = { "Hello ImGui + SFML" }; + + // Create a clock for measuring time elapsed + sf::Clock Clock; + + //prepare OpenGL surface for HSR + glClearDepth(1.f); + glClearColor(0.3f, 0.3f, 0.3f, 0.f); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + + //// Setup a perspective projection & Camera position + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(90.f, 1.f, 1.f, 300.0f);//fov, aspect, zNear, zFar + + float x_angle = 180; + float y_angle = 120; + float z_angle = -30; + + dts::vector3f translation = { 0, 0, -15 }; + + std::basic_ifstream input(argv[1], std::ios::binary); + + auto shape = dts::read_shape(argv[1], input); + + std::map> callbacks; + std::map visible_nodes; + + callbacks.emplace(config::get_key_for_name("Left"), [&]() { x_angle--; }); + callbacks.emplace(config::get_key_for_name("Numpad4"), [&]() { x_angle--; }); + callbacks.emplace(config::get_key_for_name("Right"), [&]() { x_angle++; }); + callbacks.emplace(config::get_key_for_name("Numpad6"), [&]() { x_angle++; }); + callbacks.emplace(config::get_key_for_name("Up"), [&]() { y_angle++; }); + callbacks.emplace(config::get_key_for_name("Numpad8"), [&]() { y_angle++; }); + callbacks.emplace(config::get_key_for_name("Down"), [&]() { y_angle--; }); + callbacks.emplace(config::get_key_for_name("Numpad2"), [&]() { y_angle--; }); + callbacks.emplace(config::get_key_for_name("Home"), [&]() { z_angle++; }); + callbacks.emplace(config::get_key_for_name("Numpad7"), [&]() { z_angle++; }); + callbacks.emplace(config::get_key_for_name("PageUp"), [&]() { z_angle--; }); + callbacks.emplace(config::get_key_for_name("Numpad9"), [&]() { z_angle--; }); + callbacks.emplace(config::get_key_for_name("End"), [&]() { translation.x++; }); + callbacks.emplace(config::get_key_for_name("Numpad1"), [&]() { translation.x++; }); + callbacks.emplace(config::get_key_for_name("PageDown"), [&]() { translation.x--; }); + callbacks.emplace(config::get_key_for_name("Numpad3"), [&]() { translation.x--; }); + + callbacks.emplace(config::get_key_for_name("Add"), [&]() { translation.z++; }); + callbacks.emplace(config::get_key_for_name("Subtract"), [&]() { translation.z--; }); + + // Start game loop + while (window.isOpen()) + { + // Process events + sf::Event event; + while (window.pollEvent(event)) + { + ImGui::SFML::ProcessEvent(event); + if (event.type == sf::Event::KeyPressed && (event.key.code != sf::Keyboard::Escape)) + { + const auto callback = callbacks.find(event.key.code); + + if (callback != callbacks.end()) + { + callback->second(); + } + } + + // Close window : exit + if (event.type == sf::Event::Closed) + { + window.close(); + } + + // Escape key : exit + if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)) + { + window.close(); + } + } + + // Clear color and depth buffer + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Apply some transformations for the cube + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(translation.x, translation.y, translation.z); + + glRotatef(y_angle, 1.f, 0.f, 0.f); + glRotatef(x_angle, 0.f, 1.f, 0.f); + glRotatef(z_angle, 0.f, 0.f, 1.f); + + std::visit(overloaded{ + [&](const dts::shape_variant& core_shape) { + std::visit([&](const auto& main_shape) { + glBegin(GL_TRIANGLES); + auto renderer = gl_renderer{visible_nodes}; + + render_dts(main_shape, renderer); + + glEnd(); + }, + core_shape); + }, + [&](const dts::material_list_variant&) { + //Do nothing + } }, + shape); + + window.pushGLStates(); + + ImGui::SFML::Update(window, Clock.restart()); + + ImGui::Begin("Nodes");// begin window + + for (auto& [node_name, is_visible] : visible_nodes) + { + ImGui::Checkbox(node_name.c_str(), &is_visible); + } + + ImGui::End(); + + ImGui::SFML::Render(window); + + window.popGLStates(); + window.display(); + } + + ImGui::SFML::Shutdown(); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/dts-viewer/settings.toml b/src/dts-viewer/settings.toml new file mode 100644 index 00000000..a4fbdb78 --- /dev/null +++ b/src/dts-viewer/settings.toml @@ -0,0 +1,13 @@ +[keybindings] + +[keybindings.keyboard] +increase_x_rotation = ["Left"] +decrease_x_rotation = ["Right"] +increase_y_rotation = ["Up"] +decrease_y_rotation = ["Down"] +increase_z_rotation = ["Home"] +decrease_z_rotation = ["PageUp"] +zoom_in = ["Add"] +zoom_out = ["Subtract"] +pan_left = ["End"] +pan_right = ["PageDown"] diff --git a/src/dts-viewer/sfml_keys.hpp b/src/dts-viewer/sfml_keys.hpp new file mode 100644 index 00000000..f2d318bd --- /dev/null +++ b/src/dts-viewer/sfml_keys.hpp @@ -0,0 +1,103 @@ +#ifndef DARKSTARDTSCONVERTER_SFML_KEYS_HPP +#define DARKSTARDTSCONVERTER_SFML_KEYS_HPP + +#include +#include +#include +#include +#include + +namespace config +{ + using namespace std::literals; + + constexpr static auto letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + constexpr static std::array numpad_names = { "Numpad0"sv, "Numpad1"sv, "Numpad2"sv, "Numpad3"sv, "Numpad4"sv, "Numpad5"sv, "Numpad6"sv, "Numpad7"sv, "Numpad8"sv, "Numpad9"sv }; + + constexpr auto get_numpad_key_names() + { + std::array, 10> keys{}; + + int name_index = 0; + for (int i = sf::Keyboard::Key::Numpad0; i < sf::Keyboard::Key::Numpad0 + 10; ++i) + { + keys[name_index] = std::make_pair(numpad_names[name_index], static_cast(i)); + ++name_index; + } + + return keys; + } + + constexpr auto get_letter_key_names() + { + std::array, 26> keys{}; + + for (int i = 0; i < 26; ++i) + { + keys[i] = std::make_pair(std::string_view(&letters[i], 1), static_cast(i)); + } + + return keys; + } + + constexpr static std::array special_key_names = { + std::make_pair("Up"sv, sf::Keyboard::Up), + std::make_pair("Down"sv, sf::Keyboard::Down), + std::make_pair("Left"sv, sf::Keyboard::Left), + std::make_pair("Right"sv, sf::Keyboard::Right), + std::make_pair("Insert"sv, sf::Keyboard::Insert), + std::make_pair("Delete"sv, sf::Keyboard::Delete), + std::make_pair("Home"sv, sf::Keyboard::Home), + std::make_pair("End"sv, sf::Keyboard::End), + std::make_pair("PageUp"sv, sf::Keyboard::PageUp), + std::make_pair("PageDown"sv, sf::Keyboard::PageDown), + std::make_pair("Add"sv, sf::Keyboard::Add), + std::make_pair("Subtract"sv, sf::Keyboard::Subtract), + std::make_pair("Multiply"sv, sf::Keyboard::Multiply), + std::make_pair("Divide"sv, sf::Keyboard::Divide), + std::make_pair("Tab"sv, sf::Keyboard::Tab), + std::make_pair("Space"sv, sf::Keyboard::Space), + std::make_pair("Enter"sv, sf::Keyboard::Enter), + std::make_pair("Backspace"sv, sf::Keyboard::Backspace) + }; + + + sf::Keyboard::Key get_key_for_name(const std::string_view name) + { + static auto numpad_key_names = get_numpad_key_names(); + static auto letter_names = get_letter_key_names(); + + auto numpad_key = find_if(numpad_key_names.begin(), numpad_key_names.end(), + [&](const auto& value) { return value.first == name;}); + + if (numpad_key != numpad_key_names.end()) + { + return numpad_key->second; + } + + auto special_key = find_if(special_key_names.begin(), special_key_names.end(), + [&](const auto& value) { return value.first == name;}); + + if (special_key != special_key_names.end()) + { + return special_key->second; + } + + auto letter_key = find_if(letter_names.begin(), letter_names.end(), + [&](const auto& value) { return value.first == name;}); + + if (letter_key != letter_names.end()) + { + return letter_key->second; + } + + return sf::Keyboard::Key::Unknown; + } + +} + + + + +#endif//DARKSTARDTSCONVERTER_SFML_KEYS_HPP diff --git a/src/dts_io.hpp b/src/dts_io.hpp index eed7b874..24e5b628 100644 --- a/src/dts_io.hpp +++ b/src/dts_io.hpp @@ -1,6 +1,7 @@ #ifndef DARKSTARDTSCONVERTER_DTS_IO_HPP #define DARKSTARDTSCONVERTER_DTS_IO_HPP +#include #include "dts_structures.hpp" #include "binary_io.hpp" diff --git a/src/dts_render.hpp b/src/dts_render.hpp new file mode 100644 index 00000000..441d1417 --- /dev/null +++ b/src/dts_render.hpp @@ -0,0 +1,171 @@ +#ifndef DARKSTARDTSCONVERTER_DTS_RENDER_HPP +#define DARKSTARDTSCONVERTER_DTS_RENDER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include "dts_structures.hpp" + +struct shape_renderer +{ + virtual void update_node(std::string_view node_name) = 0; + virtual void update_object(std::string_view object_name) = 0; + virtual void new_face(std::size_t num_vertices) = 0; + virtual void end_face() = 0; + virtual void emit_vertex(const darkstar::dts::vector3f& vertex) = 0; + virtual void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) = 0; + + virtual ~shape_renderer() = default; + + shape_renderer() = default; + shape_renderer(const shape_renderer&) = delete; + shape_renderer(shape_renderer&&) = delete; +}; + +template +void render_dts(const ShapeType& shape, shape_renderer& renderer, std::optional detail_level_index = std::nullopt) +{ + namespace dts = darkstar::dts; + std::vector buffer(8192, std::byte{ 0 }); + std::pmr::monotonic_buffer_resource resource{ buffer.data(), buffer.size() }; + + using transform_set = std::pmr::set; + + detail_level_index = detail_level_index.has_value() ? detail_level_index : 0; + const auto& detail_level = shape.details[detail_level_index.value()]; + const auto root_note_index = detail_level.root_node_index; + + std::pmr::unordered_map node_indexes{ &resource }; + std::pmr::unordered_map> object_indexes{ &resource }; + + object_indexes.emplace(root_note_index, std::pmr::set{ &resource }); + + for (const auto& [parent_index, objects] : object_indexes) + { + const auto [location, added] = node_indexes.emplace(parent_index, transform_set{ &resource }); + + auto transform_index = shape.nodes[parent_index].default_transform_index; + const auto* transform = &shape.transforms[transform_index]; + location->second.emplace(transform); + + for (auto other_node = std::begin(shape.nodes); other_node != std::end(shape.nodes); ++other_node) + { + if (other_node->parent_node_index == parent_index) + { + const auto index = static_cast(std::distance(std::begin(shape.nodes), other_node)); + + object_indexes.emplace(index, std::pmr::set{ &resource }); + + for (const auto* other_transform : node_indexes[parent_index]) + { + const auto [child_location, child_added] = node_indexes.emplace(index, transform_set{ &resource }); + child_location->second.emplace(other_transform); + } + } + } + } + + for (auto object = std::begin(shape.objects); object != std::end(shape.objects); ++object) + { + if (const auto item = object_indexes.find(object->node_index); + item != object_indexes.cend()) + { + const auto index = static_cast(std::distance(std::begin(shape.objects), object)); + item->second.emplace(index); + } + } + + for (const auto& [child_node_index, transforms] : node_indexes) + { + std::optional default_scale; + dts::vector3f default_translation = { 0, 0, 0 }; + + for (auto transform : transforms) + { + if constexpr (std::remove_reference_t::version < 8) + { + if (!default_scale.has_value()) + { + default_scale = transform->scale; + } + } + default_translation = default_translation + transform->translation; + } + + const auto& child_node = shape.nodes[child_node_index]; + const std::string_view child_node_name = shape.names[child_node.name_index].data(); + renderer.update_node(child_node_name); + + auto& objects = object_indexes[child_node_index]; + + for (const std::int32_t object_index : objects) + { + const auto& object = shape.objects[object_index]; + const std::string_view parent_object_name = shape.names[object.name_index].data(); + + renderer.update_object(parent_object_name); + + std::visit([&](const auto& mesh) { + dts::vector3f mesh_scale; + dts::vector3f mesh_origin; + + if constexpr (std::remove_reference_t::version < 3) + { + mesh_scale = mesh.header.scale; + mesh_origin = mesh.header.origin; + } + else if constexpr (std::remove_reference_t::version >= 3) + { + if (!mesh.frames.empty()) + { + mesh_scale = mesh.frames[0].scale; + mesh_origin = mesh.frames[0].origin; + } + else + { + mesh_scale = { 1, 1, 1 }; + mesh_origin = { 0, 0, 0 }; + } + } + + if (default_scale.has_value()) + { + mesh_scale = mesh_scale * default_scale.value(); + } + + mesh_origin = mesh_origin + default_translation; + + for (const auto& face : mesh.faces) + { + renderer.new_face(3); + std::array vertices{ std::cref(mesh.vertices[face.vi1]), + std::cref(mesh.vertices[face.vi2]), + std::cref(mesh.vertices[face.vi3]) }; + + std::array texture_vertices{ std::cref(mesh.texture_vertices[face.ti1]), + std::cref(mesh.texture_vertices[face.ti2]), + std::cref(mesh.texture_vertices[face.ti3]) }; + + for (const auto& raw_vertex : vertices) + { + renderer.emit_vertex(raw_vertex.get() * mesh_scale + mesh_origin); + } + + for (const auto& raw_texture_vertex : texture_vertices) + { + renderer.emit_texture_vertex(raw_texture_vertex.get()); + } + + renderer.end_face(); + } + }, + shape.meshes[object.mesh_index]); + } + } +} + +#endif//DARKSTARDTSCONVERTER_DTS_RENDER_HPP From 833ba1d2a421274c33b0f4dde5d99ca9eec3f999 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 20 Sep 2020 14:48:21 +0200 Subject: [PATCH 08/10] Added support for opening files when none are present Fixed issue with rendering Added support for changing the detail level --- CMakeLists.txt | 6 +- README.md | 9 +- conanfile.py | 6 +- src/dts-to-obj/convert_dts.cpp | 72 +--------- src/dts-viewer/dts-viewer.cpp | 191 ++++++++++++++++++-------- src/dts-viewer/sfml_keys.hpp | 4 +- src/dts_io.hpp | 42 +++--- src/dts_render.hpp | 227 +++++++++++++++++-------------- src/json-to-dts/convert_json.cpp | 2 +- src/obj_renderer.hpp | 67 +++++++++ 10 files changed, 382 insertions(+), 244 deletions(-) create mode 100644 src/obj_renderer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0872b8a9..74f9cf2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,18 +27,18 @@ conan_target_link_libraries(mdl-to-json) conan_target_link_libraries(dts-to-json) conan_target_link_libraries(dts-to-obj) conan_target_link_libraries(json-to-dts) -target_link_libraries(dts-viewer PRIVATE glu32 ${CONAN_LIBS}) +target_link_libraries(dts-viewer PRIVATE ${CONAN_LIBS}) if(MSVC) target_compile_options(mdl-to-json PRIVATE /W4 /WX $<$:/O2>) target_compile_options(dts-to-json PRIVATE /W4 /WX $<$:/O2>) target_compile_options(dts-to-obj PRIVATE /W4 /WX $<$:/O2>) target_compile_options(json-to-dts PRIVATE /W4 /WX $<$:/O2>) - target_compile_options(dts-viewer PRIVATE /W4 /WX $<$:/O2>) + target_compile_options(dts-viewer PRIVATE $<$:/O2>) else() target_compile_options(mdl-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(dts-to-json PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(dts-to-obj PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) target_compile_options(json-to-dts PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) - target_compile_options(dts-viewer PRIVATE -Wall -Wextra -Werror -pedantic $<$:-O3>) + target_compile_options(dts-viewer PRIVATE $<$:-O3>) endif() \ No newline at end of file diff --git a/README.md b/README.md index 325b3cf7..20adc7a9 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,16 @@ Darkstar DTS versions 3.2, 3.3, 3.5, 3.6, 3.7 and 3.8 can be processed, which co If you don't already have Conan on your system, find instructions here: https://conan.io/downloads.html +As a setup command, without any pre-built packages, run ```conan install cmake/3.17.3@/ -g virtualenv``` + +Then run ```activate``` or ```./activate``` + To install project dependencies, use: -```conan install .``` or ```conan install . --missing``` if you system is missing precompiled packages for the dependencies. +```conan install .``` or ```conan install . --build=missing``` if you system is missing precompiled packages for the dependencies. + +For debug builds use: +```conan install . -s build_type=Debug``` or ```conan install . -s build_type=Debug --build=missing``` To build the project, use: diff --git a/conanfile.py b/conanfile.py index 610b4ba6..e8e76da2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,9 +3,9 @@ class LocalConanFile(ConanFile): system_requires = "opengl/system" - build_requires = "cmake/3.17.3", "cppcheck_installer/2.0@bincrafters/stable", "toml11/3.4.0" + build_requires = "cmake/3.17.3", "cppcheck_installer/2.0@bincrafters/stable" settings = "os", "compiler", "build_type", "arch" - requires = "boost_endian/1.69.0@bincrafters/stable", "nlohmann_json/3.9.0", "imgui-sfml/2.1@bincrafters/stable" + requires = "toml11/3.4.0", "nlohmann_json/3.9.0", "boost_endian/1.69.0@bincrafters/stable", "imgui-sfml/2.1@bincrafters/stable", "wxwidgets/3.1.3@bincrafters/stable" generators = "cmake", "virtualenv" build_folder = "build" @@ -13,7 +13,7 @@ def build(self): self.build_folder = "build" cmake = CMake(self) cmake.configure() - self.run("cppcheck src --error-exitcode=1") + #self.run("cppcheck src --error-exitcode=1") cmake.build() def imports(self): diff --git a/src/dts-to-obj/convert_dts.cpp b/src/dts-to-obj/convert_dts.cpp index bb825ed4..a9ff3583 100644 --- a/src/dts-to-obj/convert_dts.cpp +++ b/src/dts-to-obj/convert_dts.cpp @@ -5,12 +5,12 @@ #include #include #include -#include #include "json_boost.hpp" #include "complex_serializer.hpp" #include "shared.hpp" #include "dts_io.hpp" -#include "dts_render.hpp" +#include "obj_renderer.hpp" + namespace fs = std::filesystem; namespace dts = darkstar::dts; @@ -24,64 +24,6 @@ struct overloaded : Ts... template overloaded(Ts...) -> overloaded; - -struct obj_render final : shape_renderer -{ - std::optional new_face_str = std::nullopt; - std::size_t face_count = 0; - std::ofstream& output; - - obj_render(std::ofstream& output) - : output(output) - { - output << std::setprecision(32); - } - - void update_node(std::string_view) override - { - } - - void update_object(std::string_view object_name) override - { - output << "o " << object_name << '\n'; - } - - void new_face(std::size_t num_vertices) override - { - std::stringstream stream; - stream << "\tf"; - - for (auto i = 1u; i <= num_vertices; ++i) - { - stream << " " << face_count + i << "/" << face_count + i; - } - - stream << '\n'; - - new_face_str = stream.str(); - - face_count += num_vertices; - } - - void end_face() override - { - if (new_face_str.has_value()) - { - output << new_face_str.value(); - } - } - - void emit_vertex(const darkstar::dts::vector3f& vertex) override - { - output << "\tv " << vertex.x << ' ' << vertex.y << ' ' << vertex.z << '\n'; - } - - void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) override - { - output << "\tvt " << vertex.x << ' ' << vertex.y << '\n'; - } -}; - int main(int argc, const char** argv) { const auto files = shared::find_files( @@ -108,21 +50,21 @@ int main(int argc, const char** argv) [&](const dts::shape_variant& core_shape) { std::visit([&](const auto& main_shape) { - for (auto i = 0u; i < main_shape.details.size(); ++i) + //for (auto i = 0u; i < main_shape.details.size(); ++i) { - const auto& detail_level = main_shape.details[i]; + const auto& detail_level = main_shape.details[0]; const auto root_node = main_shape.nodes[detail_level.root_node_index]; const std::string root_node_name = main_shape.names[root_node.name_index].data(); std::ofstream output(file_name.string() + "." + root_node_name + ".obj", std::ios::trunc); - auto renderer = obj_render{output}; + auto renderer = obj_renderer{output}; - render_dts(main_shape, renderer, i); + render_dts(main_shape, renderer, 0); } }, core_shape); }, [&](const dts::material_list_variant&) { - //Do nothing + //TODO generate a MTL file } }, shape); } diff --git a/src/dts-viewer/dts-viewer.cpp b/src/dts-viewer/dts-viewer.cpp index 8b971f2d..c9995e49 100644 --- a/src/dts-viewer/dts-viewer.cpp +++ b/src/dts-viewer/dts-viewer.cpp @@ -2,17 +2,15 @@ #include #include #include -#include - -//#include +#include +#include #include #include #include #include #include #include -#include #include "dts_io.hpp" #include "dts_render.hpp" @@ -20,15 +18,6 @@ namespace fs = std::filesystem; namespace dts = darkstar::dts; -// helper type for the visitor #4 -template -struct overloaded : Ts... -{ - using Ts::operator()...; -}; -// explicit deduction guide (not needed as of C++20) -template -overloaded(Ts...) -> overloaded; struct gl_renderer final : shape_renderer { @@ -59,7 +48,7 @@ struct gl_renderer final : shape_renderer if (current_node->second) { const auto [red, green, blue] = max_colour; - glColor3ub(red - num_faces, green - num_faces, std::uint8_t(current_object_name.size())); + glColor4ub(red - num_faces, green - num_faces, std::uint8_t(current_object_name.size()), 255); num_faces += 255 / 15; } } @@ -82,33 +71,91 @@ struct gl_renderer final : shape_renderer }; -int main(int, const char** argv) +void perspectiveGL(GLdouble fovY, GLdouble aspect, GLdouble zNear, GLdouble zFar) { - //const auto hello_toml = toml::parse("settings.toml"); + constexpr GLdouble pi = 3.1415926535897932384626433832795; + GLdouble fW, fH; - // Create the main window - sf::RenderWindow window(sf::VideoMode(800, 600, 32), "SFML OpenGL"); + //fH = tan( (fovY / 2) / 180 * pi ) * zNear; + fH = tan(fovY / 360 * pi) * zNear; + fW = fH * aspect; - ImGui::SFML::Init(window); + glFrustum(-fW, fW, -fH, fH, zNear, zFar); +} - //sf::Color bgColor; - std::array color = { 0.f, 0.f, 0.f }; - std::array windowTitle = { "Hello ImGui + SFML" }; +std::optional get_shape_path() +{ + constexpr static auto filetypes = "Darkstar DTS files|*.dts;*.DTS"; + auto dialog = std::make_unique(nullptr, "Open a Darkstar DTS File", "", "", filetypes, wxFD_OPEN, wxDefaultPosition); + + if (dialog->ShowModal() == wxID_OK) + { + const auto buffer = dialog->GetPath().ToAscii(); + return std::string_view{ buffer.data(), buffer.length() }; + } + + return std::nullopt; +} + +int main(int argc, const char** argv) +{ + using namespace std::literals; + std::optional shape_path; + + dts::shape_variant shape; + + if (argc > 1) + { + shape_path = argv[1]; + } + + if (!shape_path.has_value()) + { + shape_path = get_shape_path(); + } + + if (!shape_path.has_value()) + { + wxMessageBox("No file has been selected.", "Cannot Continue.", wxICON_ERROR); + return EXIT_FAILURE; + } + + try + { + std::basic_ifstream input(shape_path.value(), std::ios::binary); + shape = dts::read_shape(shape_path.value(), input, std::nullopt); + } + catch (const std::exception& ex) + { + wxMessageBox(ex.what(), "Error Loading Model.", wxICON_ERROR); + return EXIT_FAILURE; + } + + sf::ContextSettings context; + context.depthBits = 24; + constexpr auto main_title = "3Space Studio - Darkstar DTS Viewer -"; + + sf::RenderWindow window(sf::VideoMode(800, 600, 32), main_title + shape_path.value().filename().string(), sf::Style::Default, context); - // Create a clock for measuring time elapsed - sf::Clock Clock; + ImGui::SFML::Init(window); + + std::array color = { 0.f, 0.f, 0.f }; + sf::Clock clock; - //prepare OpenGL surface for HSR glClearDepth(1.f); glClearColor(0.3f, 0.3f, 0.3f, 0.f); + glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); - //// Setup a perspective projection & Camera position + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); - gluPerspective(90.f, 1.f, 1.f, 300.0f);//fov, aspect, zNear, zFar + perspectiveGL(90.f, 1.f, 1.f, 300.0f); + float x_angle = 180; float y_angle = 120; @@ -116,12 +163,10 @@ int main(int, const char** argv) dts::vector3f translation = { 0, 0, -15 }; - std::basic_ifstream input(argv[1], std::ios::binary); - - auto shape = dts::read_shape(argv[1], input); - std::map> callbacks; std::map visible_nodes; + auto detail_levels = get_detail_levels(shape); + int detail_level_index = 0; callbacks.emplace(config::get_key_for_name("Left"), [&]() { x_angle--; }); callbacks.emplace(config::get_key_for_name("Numpad4"), [&]() { x_angle--; }); @@ -143,10 +188,16 @@ int main(int, const char** argv) callbacks.emplace(config::get_key_for_name("Add"), [&]() { translation.z++; }); callbacks.emplace(config::get_key_for_name("Subtract"), [&]() { translation.z--; }); - // Start game loop + callbacks.emplace(config::get_key_for_name("Divide"), [&]() { translation.y++; }); + callbacks.emplace(config::get_key_for_name("Insert"), [&]() { translation.y++; }); + callbacks.emplace(config::get_key_for_name("Numpad0"), [&]() { translation.y++; }); + + callbacks.emplace(config::get_key_for_name("Multiply"), [&]() { translation.y--; }); + callbacks.emplace(config::get_key_for_name("Delete"), [&]() { translation.y--; }); + callbacks.emplace(config::get_key_for_name("Period"), [&]() { translation.y--; }); + while (window.isOpen()) { - // Process events sf::Event event; while (window.pollEvent(event)) { @@ -161,23 +212,18 @@ int main(int, const char** argv) } } - // Close window : exit if (event.type == sf::Event::Closed) { window.close(); } - // Escape key : exit if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)) { window.close(); } } - // Clear color and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Apply some transformations for the cube glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(translation.x, translation.y, translation.z); @@ -186,28 +232,47 @@ int main(int, const char** argv) glRotatef(x_angle, 0.f, 1.f, 0.f); glRotatef(z_angle, 0.f, 0.f, 1.f); - std::visit(overloaded{ - [&](const dts::shape_variant& core_shape) { - std::visit([&](const auto& main_shape) { - glBegin(GL_TRIANGLES); - auto renderer = gl_renderer{visible_nodes}; + glBegin(GL_TRIANGLES); + auto renderer = gl_renderer{ visible_nodes }; + render_dts(shape, renderer, detail_level_index); + glEnd(); - render_dts(main_shape, renderer); + window.pushGLStates(); - glEnd(); - }, - core_shape); - }, - [&](const dts::material_list_variant&) { - //Do nothing - } }, - shape); + ImGui::SFML::Update(window, clock.restart()); - window.pushGLStates(); + if (ImGui::Begin("Options")) + { + if (ImGui::Button("Open")) + { + const auto path = get_shape_path(); - ImGui::SFML::Update(window, Clock.restart()); + if (path.has_value()) + { + try + { + std::basic_ifstream input(path.value(), std::ios::binary); + shape = dts::read_shape(path.value(), input, std::nullopt); + window.setTitle(main_title + path.value().filename().string()); + visible_nodes.clear(); + } + catch (const std::exception& ex) + { + wxMessageBox(ex.what(), "Error Loading Model.", wxICON_ERROR); + } + } + } + ImGui::SameLine(); + if (ImGui::Button("Exit")) + { + window.close(); + break; + } + + ImGui::End(); + } - ImGui::Begin("Nodes");// begin window + ImGui::Begin("Nodes"); for (auto& [node_name, is_visible] : visible_nodes) { @@ -216,6 +281,22 @@ int main(int, const char** argv) ImGui::End(); + ImGui::Begin("Detail Levels"); + + if (ImGui::ListBox( + "", &detail_level_index, [](void* data, int idx, const char** out_text) { + *out_text = reinterpret_cast(data)[idx].c_str(); + return true; + }, + detail_levels.data(), + detail_levels.size())) + { + visible_nodes.clear(); + } + + ImGui::End(); + + ImGui::SFML::Render(window); window.popGLStates(); diff --git a/src/dts-viewer/sfml_keys.hpp b/src/dts-viewer/sfml_keys.hpp index f2d318bd..3c41af6f 100644 --- a/src/dts-viewer/sfml_keys.hpp +++ b/src/dts-viewer/sfml_keys.hpp @@ -59,7 +59,9 @@ namespace config std::make_pair("Tab"sv, sf::Keyboard::Tab), std::make_pair("Space"sv, sf::Keyboard::Space), std::make_pair("Enter"sv, sf::Keyboard::Enter), - std::make_pair("Backspace"sv, sf::Keyboard::Backspace) + std::make_pair("Backspace"sv, sf::Keyboard::Backspace), + std::make_pair("Period"sv, sf::Keyboard::Period), + std::make_pair("Comma"sv, sf::Keyboard::Comma) }; diff --git a/src/dts_io.hpp b/src/dts_io.hpp index 24e5b628..2d435463 100644 --- a/src/dts_io.hpp +++ b/src/dts_io.hpp @@ -184,58 +184,68 @@ namespace darkstar::dts return shape; } - dts::shape_or_material_list read_shape(const std::filesystem::path& file_name, std::basic_ifstream& stream) + + dts::shape_variant read_shape(const std::filesystem::path& file_name, std::basic_ifstream& stream, std::optional file_header) { using namespace dts::shape; - dts::tag_header file_header = read_object_header(stream); + file_header = !file_header.has_value() ? read_object_header(stream) : file_header; - if (file_header.class_name == dts::material_list::v2::material_list::type_name) - { - return read_material_list(file_header, stream); - } - - if (file_header.class_name != v2::shape::type_name) + if (file_header->class_name != v2::shape::type_name) { throw std::invalid_argument("The object provided is not a shape as expected."); } - if (file_header.version > 8) + if (file_header->version > 8) { throw std::invalid_argument("The shape is not supported."); } - if (file_header.version == 2) + if (file_header->version == 2) { return read_shape_impl(stream); } - else if (file_header.version == 3) + else if (file_header->version == 3) { return read_shape_impl(stream); } - else if (file_header.version == 5) + else if (file_header->version == 5) { return read_shape_impl(stream); } - else if (file_header.version == 6) + else if (file_header->version == 6) { return read_shape_impl(stream); } - else if (file_header.version == 7) + else if (file_header->version == 7) { return read_shape_impl(stream); } - else if (file_header.version == 8) + else if (file_header->version == 8) { return read_shape_impl(stream); } else { std::stringstream error; - error << file_name << " is DTS version " << file_header.version << " which is currently unsupported."; + error << file_name << " is DTS version " << file_header->version << " which is currently unsupported."; throw std::invalid_argument(error.str()); } } + + dts::shape_or_material_list read_shape(const std::filesystem::path& file_name, std::basic_ifstream& stream) + { + using namespace dts::shape; + dts::tag_header file_header = read_object_header(stream); + + if (file_header.class_name == dts::material_list::v2::material_list::type_name) + { + return read_material_list(file_header, stream); + } + + return read_shape(file_name, stream, file_header); + } + template void write_header(std::basic_ostream& stream, const RootType& root) { diff --git a/src/dts_render.hpp b/src/dts_render.hpp index 441d1417..b609ae47 100644 --- a/src/dts_render.hpp +++ b/src/dts_render.hpp @@ -26,146 +26,175 @@ struct shape_renderer shape_renderer(shape_renderer&&) = delete; }; -template -void render_dts(const ShapeType& shape, shape_renderer& renderer, std::optional detail_level_index = std::nullopt) + +std::vector get_detail_levels(const darkstar::dts::shape_variant& shape) { - namespace dts = darkstar::dts; - std::vector buffer(8192, std::byte{ 0 }); - std::pmr::monotonic_buffer_resource resource{ buffer.data(), buffer.size() }; + return std::visit([](const auto& instance) { + std::vector results; + results.reserve(instance.details.size()); + + for (const auto& detail : instance.details) + { + const auto root_note_index = detail.root_node_index; + const auto& node = instance.nodes[root_note_index]; + results.emplace_back(instance.names[node.name_index].data()); + } + + return results; + }, + shape); +} - using transform_set = std::pmr::set; +void render_dts(const darkstar::dts::shape_variant& shape_variant, shape_renderer& renderer, std::optional detail_level_index = std::nullopt) +{ + std::visit([&](const auto& shape) { + namespace dts = darkstar::dts; + std::vector buffer(8192, std::byte{ 0 }); + std::pmr::monotonic_buffer_resource resource{ buffer.data(), buffer.size() }; - detail_level_index = detail_level_index.has_value() ? detail_level_index : 0; - const auto& detail_level = shape.details[detail_level_index.value()]; - const auto root_note_index = detail_level.root_node_index; + using transform_set = std::pmr::set; - std::pmr::unordered_map node_indexes{ &resource }; - std::pmr::unordered_map> object_indexes{ &resource }; + detail_level_index = detail_level_index.has_value() ? detail_level_index : 0; + const auto& detail_level = shape.details[detail_level_index.value()]; + const auto root_note_index = detail_level.root_node_index; - object_indexes.emplace(root_note_index, std::pmr::set{ &resource }); + std::pmr::unordered_map node_indexes{ &resource }; + std::pmr::unordered_map> object_indexes{ &resource }; - for (const auto& [parent_index, objects] : object_indexes) - { - const auto [location, added] = node_indexes.emplace(parent_index, transform_set{ &resource }); + std::list valid_nodes; - auto transform_index = shape.nodes[parent_index].default_transform_index; - const auto* transform = &shape.transforms[transform_index]; - location->second.emplace(transform); + valid_nodes.emplace_back(object_indexes.emplace(root_note_index, std::pmr::set{ &resource }).first->first); - for (auto other_node = std::begin(shape.nodes); other_node != std::end(shape.nodes); ++other_node) + for (const auto parent_index : valid_nodes) { - if (other_node->parent_node_index == parent_index) - { - const auto index = static_cast(std::distance(std::begin(shape.nodes), other_node)); + const auto [location, added] = node_indexes.emplace(parent_index, transform_set{ &resource }); - object_indexes.emplace(index, std::pmr::set{ &resource }); + auto transform_index = shape.nodes[parent_index].default_transform_index; + const auto* transform = &shape.transforms[transform_index]; + location->second.emplace(transform); - for (const auto* other_transform : node_indexes[parent_index]) + for (auto other_node = std::begin(shape.nodes); other_node != std::end(shape.nodes); ++other_node) + { + if (other_node->parent_node_index == parent_index) { - const auto [child_location, child_added] = node_indexes.emplace(index, transform_set{ &resource }); - child_location->second.emplace(other_transform); + const std::string_view data = shape.names[other_node->name_index].data(); + const auto index = static_cast(std::distance(std::begin(shape.nodes), other_node)); + + auto [iterator, added] = object_indexes.emplace(index, std::pmr::set{ &resource }); + + if (added) + { + valid_nodes.emplace_back(iterator->first); + } + + for (const auto* other_transform : node_indexes[parent_index]) + { + const auto [child_location, child_added] = node_indexes.emplace(index, transform_set{ &resource }); + child_location->second.emplace(other_transform); + } } } } - } - for (auto object = std::begin(shape.objects); object != std::end(shape.objects); ++object) - { - if (const auto item = object_indexes.find(object->node_index); - item != object_indexes.cend()) + for (auto object = std::begin(shape.objects); object != std::end(shape.objects); ++object) { - const auto index = static_cast(std::distance(std::begin(shape.objects), object)); - item->second.emplace(index); + if (const auto item = object_indexes.find(object->node_index); + item != object_indexes.cend()) + { + const auto index = static_cast(std::distance(std::begin(shape.objects), object)); + item->second.emplace(index); + } } - } - for (const auto& [child_node_index, transforms] : node_indexes) - { - std::optional default_scale; - dts::vector3f default_translation = { 0, 0, 0 }; - - for (auto transform : transforms) + for (const auto& [child_node_index, transforms] : node_indexes) { - if constexpr (std::remove_reference_t::version < 8) + std::optional default_scale; + dts::vector3f default_translation = { 0, 0, 0 }; + + for (auto transform : transforms) { - if (!default_scale.has_value()) + if constexpr (std::remove_reference_t::version < 8) { - default_scale = transform->scale; + if (!default_scale.has_value()) + { + default_scale = transform->scale; + } } + default_translation = default_translation + transform->translation; } - default_translation = default_translation + transform->translation; - } - const auto& child_node = shape.nodes[child_node_index]; - const std::string_view child_node_name = shape.names[child_node.name_index].data(); - renderer.update_node(child_node_name); + const auto& child_node = shape.nodes[child_node_index]; + const std::string_view child_node_name = shape.names[child_node.name_index].data(); + renderer.update_node(child_node_name); - auto& objects = object_indexes[child_node_index]; + auto& objects = object_indexes[child_node_index]; - for (const std::int32_t object_index : objects) - { - const auto& object = shape.objects[object_index]; - const std::string_view parent_object_name = shape.names[object.name_index].data(); + for (const std::int32_t object_index : objects) + { + const auto& object = shape.objects[object_index]; + const std::string_view parent_object_name = shape.names[object.name_index].data(); - renderer.update_object(parent_object_name); + renderer.update_object(parent_object_name); - std::visit([&](const auto& mesh) { - dts::vector3f mesh_scale; - dts::vector3f mesh_origin; + std::visit([&](const auto& mesh) { + dts::vector3f mesh_scale; + dts::vector3f mesh_origin; - if constexpr (std::remove_reference_t::version < 3) - { - mesh_scale = mesh.header.scale; - mesh_origin = mesh.header.origin; - } - else if constexpr (std::remove_reference_t::version >= 3) - { - if (!mesh.frames.empty()) + if constexpr (std::remove_reference_t::version < 3) { - mesh_scale = mesh.frames[0].scale; - mesh_origin = mesh.frames[0].origin; + mesh_scale = mesh.header.scale; + mesh_origin = mesh.header.origin; } - else + else if constexpr (std::remove_reference_t::version >= 3) { - mesh_scale = { 1, 1, 1 }; - mesh_origin = { 0, 0, 0 }; + if (!mesh.frames.empty()) + { + mesh_scale = mesh.frames[0].scale; + mesh_origin = mesh.frames[0].origin; + } + else + { + mesh_scale = { 1, 1, 1 }; + mesh_origin = { 0, 0, 0 }; + } } - } - - if (default_scale.has_value()) - { - mesh_scale = mesh_scale * default_scale.value(); - } - - mesh_origin = mesh_origin + default_translation; - - for (const auto& face : mesh.faces) - { - renderer.new_face(3); - std::array vertices{ std::cref(mesh.vertices[face.vi1]), - std::cref(mesh.vertices[face.vi2]), - std::cref(mesh.vertices[face.vi3]) }; - std::array texture_vertices{ std::cref(mesh.texture_vertices[face.ti1]), - std::cref(mesh.texture_vertices[face.ti2]), - std::cref(mesh.texture_vertices[face.ti3]) }; - - for (const auto& raw_vertex : vertices) + if (default_scale.has_value()) { - renderer.emit_vertex(raw_vertex.get() * mesh_scale + mesh_origin); + mesh_scale = mesh_scale * default_scale.value(); } - for (const auto& raw_texture_vertex : texture_vertices) + mesh_origin = mesh_origin + default_translation; + + for (const auto& face : mesh.faces) { - renderer.emit_texture_vertex(raw_texture_vertex.get()); + renderer.new_face(3); + std::array vertices{ std::cref(mesh.vertices[face.vi3]), + std::cref(mesh.vertices[face.vi2]), + std::cref(mesh.vertices[face.vi1]) }; + + std::array texture_vertices{ std::cref(mesh.texture_vertices[face.ti3]), + std::cref(mesh.texture_vertices[face.ti2]), + std::cref(mesh.texture_vertices[face.ti1]) }; + + for (const auto& raw_vertex : vertices) + { + renderer.emit_vertex(raw_vertex.get() * mesh_scale + mesh_origin); + } + + for (const auto& raw_texture_vertex : texture_vertices) + { + renderer.emit_texture_vertex(raw_texture_vertex.get()); + } + + renderer.end_face(); } - - renderer.end_face(); - } - }, - shape.meshes[object.mesh_index]); + }, + shape.meshes[object.mesh_index]); + } } - } + }, + shape_variant); } #endif//DARKSTARDTSCONVERTER_DTS_RENDER_HPP diff --git a/src/json-to-dts/convert_json.cpp b/src/json-to-dts/convert_json.cpp index 7149c97a..aa01c33d 100644 --- a/src/json-to-dts/convert_json.cpp +++ b/src/json-to-dts/convert_json.cpp @@ -24,7 +24,7 @@ bool replace(std::string& str, const std::string& from, const std::string& to) int main(int argc, const char** argv) { - const auto files = dts::shared::find_files( + const auto files = shared::find_files( std::vector(argv + 1, argv + argc), ".dts.json", ".DTS.json", diff --git a/src/obj_renderer.hpp b/src/obj_renderer.hpp new file mode 100644 index 00000000..1cea7268 --- /dev/null +++ b/src/obj_renderer.hpp @@ -0,0 +1,67 @@ +#ifndef DARKSTARDTSCONVERTER_OBJ_RENDERER_HPP +#define DARKSTARDTSCONVERTER_OBJ_RENDERER_HPP + +#include +#include +#include +#include +#include "dts_render.hpp" + +struct obj_renderer final : shape_renderer +{ + std::optional new_face_str = std::nullopt; + std::size_t face_count = 0; + std::ofstream& output; + + obj_renderer(std::ofstream& output) + : output(output) + { + output << std::setprecision(32); + } + + void update_node(std::string_view) override + { + } + + void update_object(std::string_view object_name) override + { + output << "o " << object_name << '\n'; + } + + void new_face(std::size_t num_vertices) override + { + std::stringstream stream; + stream << "\tf"; + + for (auto i = 1u; i <= num_vertices; ++i) + { + stream << " " << face_count + i << "/" << face_count + i; + } + + stream << '\n'; + + new_face_str = stream.str(); + + face_count += num_vertices; + } + + void end_face() override + { + if (new_face_str.has_value()) + { + output << new_face_str.value(); + } + } + + void emit_vertex(const darkstar::dts::vector3f& vertex) override + { + output << "\tv " << vertex.x << ' ' << vertex.y << ' ' << vertex.z << '\n'; + } + + void emit_texture_vertex(const darkstar::dts::mesh::v1::texture_vertex& vertex) override + { + output << "\tvt " << vertex.x << ' ' << vertex.y << '\n'; + } +}; + +#endif//DARKSTARDTSCONVERTER_OBJ_RENDERER_HPP From cd46fc2867271471a1754e005f59125de4f8ada2 Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 20 Sep 2020 15:31:23 +0200 Subject: [PATCH 09/10] Fixed build error --- src/dts_render.hpp | 8 ++++---- src/dts_structures.hpp | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dts_render.hpp b/src/dts_render.hpp index b609ae47..ba81a08b 100644 --- a/src/dts_render.hpp +++ b/src/dts_render.hpp @@ -80,9 +80,9 @@ void render_dts(const darkstar::dts::shape_variant& shape_variant, shape_rendere const std::string_view data = shape.names[other_node->name_index].data(); const auto index = static_cast(std::distance(std::begin(shape.nodes), other_node)); - auto [iterator, added] = object_indexes.emplace(index, std::pmr::set{ &resource }); + auto [iterator, node_added] = object_indexes.emplace(index, std::pmr::set{ &resource }); - if (added) + if (node_added) { valid_nodes.emplace_back(iterator->first); } @@ -132,9 +132,9 @@ void render_dts(const darkstar::dts::shape_variant& shape_variant, shape_rendere for (const std::int32_t object_index : objects) { const auto& object = shape.objects[object_index]; - const std::string_view parent_object_name = shape.names[object.name_index].data(); + const std::string_view object_name = shape.names[object.name_index].data(); - renderer.update_object(parent_object_name); + renderer.update_object(object_name); std::visit([&](const auto& mesh) { dts::vector3f mesh_scale; diff --git a/src/dts_structures.hpp b/src/dts_structures.hpp index 39a7d1b7..96c92ef3 100644 --- a/src/dts_structures.hpp +++ b/src/dts_structures.hpp @@ -65,6 +65,11 @@ namespace darkstar::dts return { left.x + right.x, left.y + right.y, left.z + right.z }; } + vector3f operator-(const vector3f& left, const vector3f& right) + { + return { left.x - right.x, left.y - right.y, left.z - right.z }; + } + // vector3f operator+=(const vector3f& left, const vector3f& right) // { // return left + right; From b550fa385588d7043103ad552964b76cb2a368ba Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 20 Sep 2020 16:11:51 +0200 Subject: [PATCH 10/10] Fixed space in title Removed debug code for obj converter Removed formatting --- src/dts-to-json/convert_dts.cpp | 2 +- src/dts-to-obj/convert_dts.cpp | 6 +++--- src/dts-viewer/dts-viewer.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dts-to-json/convert_dts.cpp b/src/dts-to-json/convert_dts.cpp index b1cfdea4..1f5a7f25 100644 --- a/src/dts-to-json/convert_dts.cpp +++ b/src/dts-to-json/convert_dts.cpp @@ -37,7 +37,7 @@ int main(int argc, const char** argv) std::visit([&](const auto& item) { nlohmann::ordered_json item_as_json = item; - format_json(item_as_json); + //format_json(item_as_json); auto new_file_name = file_name.string() + ".json"; { diff --git a/src/dts-to-obj/convert_dts.cpp b/src/dts-to-obj/convert_dts.cpp index a9ff3583..748d54ae 100644 --- a/src/dts-to-obj/convert_dts.cpp +++ b/src/dts-to-obj/convert_dts.cpp @@ -50,15 +50,15 @@ int main(int argc, const char** argv) [&](const dts::shape_variant& core_shape) { std::visit([&](const auto& main_shape) { - //for (auto i = 0u; i < main_shape.details.size(); ++i) + for (auto i = 0u; i < main_shape.details.size(); ++i) { - const auto& detail_level = main_shape.details[0]; + const auto& detail_level = main_shape.details[i]; const auto root_node = main_shape.nodes[detail_level.root_node_index]; const std::string root_node_name = main_shape.names[root_node.name_index].data(); std::ofstream output(file_name.string() + "." + root_node_name + ".obj", std::ios::trunc); auto renderer = obj_renderer{output}; - render_dts(main_shape, renderer, 0); + render_dts(main_shape, renderer, i); } }, core_shape); diff --git a/src/dts-viewer/dts-viewer.cpp b/src/dts-viewer/dts-viewer.cpp index c9995e49..42b42046 100644 --- a/src/dts-viewer/dts-viewer.cpp +++ b/src/dts-viewer/dts-viewer.cpp @@ -134,7 +134,7 @@ int main(int argc, const char** argv) sf::ContextSettings context; context.depthBits = 24; - constexpr auto main_title = "3Space Studio - Darkstar DTS Viewer -"; + constexpr auto main_title = "3Space Studio - Darkstar DTS Viewer - "; sf::RenderWindow window(sf::VideoMode(800, 600, 32), main_title + shape_path.value().filename().string(), sf::Style::Default, context);