diff --git a/Code/BuildInfo.hpp b/Code/BuildInfo.hpp index 557c5bc..ff7d8b7 100644 --- a/Code/BuildInfo.hpp +++ b/Code/BuildInfo.hpp @@ -1,2 +1,2 @@ #pragma once -#define SM_CONVERTER_BUILD_VERSION 2662 \ No newline at end of file +#define SM_CONVERTER_BUILD_VERSION 2679 \ No newline at end of file diff --git a/Code/Converter/BlueprintConverter/BlueprintConverter.cpp b/Code/Converter/BlueprintConverter/BlueprintConverter.cpp index 1e67f9c..0d784a1 100644 --- a/Code/Converter/BlueprintConverter/BlueprintConverter.cpp +++ b/Code/Converter/BlueprintConverter/BlueprintConverter.cpp @@ -12,29 +12,30 @@ SM_UNMANAGED_CODE -void BlueprintConv::WriteToFileInternal(SMBlueprint* pBlueprint, const std::wstring& bp_name, ConvertError& error) +void BlueprintConv::WriteToFileInternal(SMBlueprint* pBlueprint, const std::wstring& blueprintName, ConvertError& error) { if (error) return; - const std::wstring v_bp_out_dir(DatabaseConfig::BlueprintOutputFolder); - if (!File::CreateDirectorySafe(v_bp_out_dir)) + const std::wstring v_bpOutDir(DatabaseConfig::BlueprintOutputFolder); + if (!File::CreateDirectorySafe(v_bpOutDir)) { error.setError(1, "Couldn't create the main output directory"); return; } - const std::wstring v_bp_dir_path = v_bp_out_dir + L"/" + bp_name; - if (!File::CreateDirectorySafe(v_bp_dir_path)) + const std::wstring v_bpDirPath = v_bpOutDir + L"/" + blueprintName; + if (!File::CreateDirectorySafe(v_bpDirPath)) { error.setError(1, "Couldn't create the blueprint output directory"); return; } - const std::wstring v_bp_output_path = v_bp_dir_path + L"/" + bp_name; + const std::wstring v_bpOutputPath = v_bpDirPath + L"/" + blueprintName; +#if 0 // OBJ implementation { //Write object file - std::ofstream v_obj_writer(v_bp_output_path + L".obj"); + std::ofstream v_obj_writer(v_bpOutputPath + L".obj"); if (!v_obj_writer.is_open()) { error.setError(1, "Couldn't create an object file"); @@ -70,8 +71,26 @@ void BlueprintConv::WriteToFileInternal(SMBlueprint* pBlueprint, const std::wstr ProgCounter::ProgressMax = v_textureMap.size(); } - MtlFileWriter::Write(v_bp_output_path + L".mtl", v_textureMap); + MtlFileWriter::Write(v_bpOutputPath + L".mtl", v_textureMap); } +#else + ProgCounter::SetState(ProgState::WritingObjects, pBlueprint->GetAmountOfObjects()); + + const std::wstring v_bpBinaryPath = v_bpDirPath + L"/mesh_data.bin"; + GltfWriterContext v_writerContext(v_bpBinaryPath); + + if (!v_writerContext.m_outFile.is_open()) + { + error.setError(1, "Couldn't create an object file"); + return; + } + + v_writerContext.getOrCreateNewGroup(L"Main"); + pBlueprint->WriteObjectToFileGltf(v_writerContext, glm::mat4(1.0f)); + + ProgCounter::SetState(ProgState::WritingMtlFile, 0); + v_writerContext.writeGltfToFile(v_bpOutputPath + L".gltf"); +#endif } SMBody* BlueprintConv::CreateCollection( diff --git a/Code/Converter/BlueprintConverter/BlueprintConverter.hpp b/Code/Converter/BlueprintConverter/BlueprintConverter.hpp index a963d98..35690e8 100644 --- a/Code/Converter/BlueprintConverter/BlueprintConverter.hpp +++ b/Code/Converter/BlueprintConverter/BlueprintConverter.hpp @@ -26,7 +26,7 @@ enum : std::uint8_t class BlueprintConv { - static void WriteToFileInternal(SMBlueprint* pBlueprint, const std::wstring& bp_name, ConvertError& error); + static void WriteToFileInternal(SMBlueprint* pBlueprint, const std::wstring& blueprintName, ConvertError& error); static SMBody* CreateCollection(SMBlueprint* self, const std::string_view& name); diff --git a/Code/Converter/Entity/Block.cpp b/Code/Converter/Entity/Block.cpp index 76789e6..3cea525 100644 --- a/Code/Converter/Entity/Block.cpp +++ b/Code/Converter/Entity/Block.cpp @@ -96,7 +96,14 @@ static void GenerateUVs(Model& model, const glm::vec3& bounds, const glm::vec3& { model.m_uvs.resize(24); - const static glm::vec4 v_blockTextureNormals[6] = + const static glm::vec4 v_blockUvNormal1( 0.0f, 0.0f, 1.0f, 1.0f); + const static glm::vec4 v_blockUvNormal2( 1.0f, 1.0f, 0.0f, 0.0f); + const static glm::vec4 v_blockUvNormal3( 0.0f, 0.0f, 1.0f, -1.0f); + const static glm::vec4 v_blockUvNormal4( 1.0f, 1.0f, 0.0f, 0.0f); + const static glm::vec4 v_blockUvNormal5( 1.0f, 0.0f, 0.0f, -1.0f); + const static glm::vec4 v_blockUvNormal6(-1.0f, 0.0f, 0.0f, -1.0f); + + /*const static glm::vec4 v_blockTextureNormals[6] = { { 0.0f, 0.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, 0.0f, 0.0f }, @@ -104,6 +111,18 @@ static void GenerateUVs(Model& model, const glm::vec3& bounds, const glm::vec3& { 1.0f, 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, -1.0f }, { -1.0f, 0.0f, 0.0f, -1.0f } + };*/ + + const static glm::vec4 v_blockTextureNormals[24] = + { + v_blockUvNormal1, v_blockUvNormal2, v_blockUvNormal3, + v_blockUvNormal1, v_blockUvNormal4, v_blockUvNormal3, + v_blockUvNormal5, v_blockUvNormal2, v_blockUvNormal3, + v_blockUvNormal5, v_blockUvNormal4, v_blockUvNormal3, + v_blockUvNormal1, v_blockUvNormal2, v_blockUvNormal6, + v_blockUvNormal1, v_blockUvNormal4, v_blockUvNormal6, + v_blockUvNormal5, v_blockUvNormal2, v_blockUvNormal6, + v_blockUvNormal5, v_blockUvNormal4, v_blockUvNormal6 }; const float v_tilingAdj = 1.0f / (float)tiling; @@ -130,6 +149,32 @@ static void FillCustomCube(Model& model, const glm::vec3& bounds, const glm::vec { model.m_vertices = { + { bounds.x, bounds.y, -bounds.z }, + { bounds.x, bounds.y, -bounds.z }, + { bounds.x, bounds.y, -bounds.z }, + { bounds.x, -bounds.y, -bounds.z }, + { bounds.x, -bounds.y, -bounds.z }, + { bounds.x, -bounds.y, -bounds.z }, + { bounds.x, bounds.y, bounds.z }, + { bounds.x, bounds.y, bounds.z }, + { bounds.x, bounds.y, bounds.z }, + { bounds.x, -bounds.y, bounds.z }, + { bounds.x, -bounds.y, bounds.z }, + { bounds.x, -bounds.y, bounds.z }, + { -bounds.x, bounds.y, -bounds.z }, + { -bounds.x, bounds.y, -bounds.z }, + { -bounds.x, bounds.y, -bounds.z }, + { -bounds.x, -bounds.y, -bounds.z }, + { -bounds.x, -bounds.y, -bounds.z }, + { -bounds.x, -bounds.y, -bounds.z }, + { -bounds.x, bounds.y, bounds.z }, + { -bounds.x, bounds.y, bounds.z }, + { -bounds.x, bounds.y, bounds.z }, + { -bounds.x, -bounds.y, bounds.z }, + { -bounds.x, -bounds.y, bounds.z }, + { -bounds.x, -bounds.y, bounds.z } + + /* { -bounds.x, bounds.y, bounds.z }, { -bounds.x, -bounds.y, -bounds.z }, { -bounds.x, -bounds.y, bounds.z }, @@ -138,15 +183,28 @@ static void FillCustomCube(Model& model, const glm::vec3& bounds, const glm::vec { bounds.x, bounds.y, -bounds.z }, { bounds.x, -bounds.y, bounds.z }, { bounds.x, bounds.y, bounds.z } + */ }; if (SharedConverterSettings::ExportNormals) { model.m_normals = { - { -1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f }, - { 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, - { 0.0f, -1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } + { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f }, { 0.0f, -1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, { 0.0f, -1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f }, { 0.0f, -1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, { 0.0f, -1.0f, 0.0f }, { -1.0f, 0.0f, 0.0f } + + /*{ -1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f }, + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, -1.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }*/ }; } @@ -155,7 +213,19 @@ static void FillCustomCube(Model& model, const glm::vec3& bounds, const glm::vec SharedConverterSettings::ExportNormals, SharedConverterSettings::ExportUvs, std::initializer_list>{ - { { 0, 0 , 0 }, { 1, 1 , 0 }, { 2, 2 , 0 } }, + { { 13, 13, 13 }, { 7, 7, 7 }, { 1, 1, 1 } }, + { { 6, 6, 6 }, { 21, 21, 21 }, { 9, 9, 9 } }, + { { 20, 20, 20 }, { 17, 17, 17 }, { 23, 23, 23 } }, + { { 4, 4, 4 }, { 22, 22, 22 }, { 16, 16, 16 } }, + { { 2, 2, 2 }, { 11, 11, 11 }, { 5, 5, 5 } }, + { { 12, 12, 12 }, { 3, 3, 3 }, { 15, 15, 15 } }, + { { 13, 13, 13 }, { 19, 19, 19 }, { 7, 7, 7 } }, + { { 6, 6, 6 }, { 18, 18, 18 }, { 21, 21, 21 } }, + { { 20, 20, 20 }, { 14, 14, 14 }, { 17, 17, 17 } }, + { { 4, 4, 4 }, { 10, 10, 10 }, { 22, 22, 22 } }, + { { 2, 2, 2 }, { 8, 8, 8 }, { 11, 11, 11 } }, + { { 12, 12, 12 }, { 0, 0, 0 }, { 3, 3, 3 } } + /*{ { 0, 0 , 0 }, { 1, 1 , 0 }, { 2, 2 , 0 } }, { { 3, 3 , 1 }, { 4, 4 , 1 }, { 1, 5 , 1 } }, { { 5, 6 , 2 }, { 6, 7 , 2 }, { 4, 8 , 2 } }, { { 7, 9 , 3 }, { 2, 10, 3 }, { 6, 11, 3 } }, @@ -166,7 +236,7 @@ static void FillCustomCube(Model& model, const glm::vec3& bounds, const glm::vec { { 5, 6 , 2 }, { 7, 20, 2 }, { 6, 7 , 2 } }, { { 7, 9 , 3 }, { 0, 21, 3 }, { 2, 10, 3 } }, { { 4, 12, 4 }, { 6, 22, 4 }, { 2, 13, 4 } }, - { { 3, 15, 5 }, { 0, 23, 5 }, { 7, 16, 5 } } + { { 3, 15, 5 }, { 0, 23, 5 }, { 7, 16, 5 } }*/ } ); @@ -188,6 +258,19 @@ void SMBlock::WriteObjectToFile( ProgCounter::ProgressValue++; } +void SMBlock::WriteObjectToFileGltf( + GltfWriterContext& context, + const glm::mat4& transform) const +{ + Model v_blockModel; + FillCustomCube(v_blockModel, m_size / 2.0f, m_position, m_parent->m_tiling); + + const glm::mat4 v_blockTransform = transform * this->GetTransformMatrix(); + v_blockModel.WriteToFileGltf(context, v_blockTransform, this); + + ProgCounter::ProgressValue++; +} + glm::mat4 SMBlock::GetTransformMatrix() const { const glm::mat4 v_blockRotation = Rotations::GetRotationMatrix(m_xzRotation); diff --git a/Code/Converter/Entity/Block.hpp b/Code/Converter/Entity/Block.hpp index 932b4af..75782c0 100644 --- a/Code/Converter/Entity/Block.hpp +++ b/Code/Converter/Entity/Block.hpp @@ -38,6 +38,7 @@ class SMBlock final : public SMEntityWithUuid std::string GetMtlName(const std::size_t idx) const override; void FillTextureMap(EntityTextureMap& textureMap) const override; void WriteObjectToFile(std::ofstream& file, WriterOffsetData& offset, const glm::mat4& transform) const override; + void WriteObjectToFileGltf(GltfWriterContext& context, const glm::mat4& transform) const override; glm::mat4 GetTransformMatrix() const override; private: diff --git a/Code/Converter/Entity/Blueprint.cpp b/Code/Converter/Entity/Blueprint.cpp index b9140ab..a5bc61c 100644 --- a/Code/Converter/Entity/Blueprint.cpp +++ b/Code/Converter/Entity/Blueprint.cpp @@ -219,6 +219,16 @@ void SMBlueprint::WriteObjectToFile( v_pCurEntity->WriteObjectToFile(file, offset, v_blueprintTransform); } +void SMBlueprint::WriteObjectToFileGltf( + GltfWriterContext& context, + const glm::mat4& transform) const +{ + const glm::mat4 v_blueprintTransform = transform * this->GetTransformMatrix(); + + for (const SMEntity* v_pCurEntity : m_objects) + v_pCurEntity->WriteObjectToFileGltf(context, v_blueprintTransform); +} + std::size_t SMBlueprint::GetAmountOfObjects() const { std::size_t v_output = 0; diff --git a/Code/Converter/Entity/Blueprint.hpp b/Code/Converter/Entity/Blueprint.hpp index 44baf32..cfeb9a2 100644 --- a/Code/Converter/Entity/Blueprint.hpp +++ b/Code/Converter/Entity/Blueprint.hpp @@ -38,6 +38,7 @@ class SMBlueprint final : public SMEntity EntityType Type() const override; void FillTextureMap(EntityTextureMap& textureMap) const override; void WriteObjectToFile(std::ofstream& file, WriterOffsetData& offset, const glm::mat4& transform) const override; + void WriteObjectToFileGltf(GltfWriterContext& context, const glm::mat4& transform) const override; std::size_t GetAmountOfObjects() const override; void CalculateCenterPoint(glm::vec3& outInput) const override; diff --git a/Code/Converter/Entity/Entity.cpp b/Code/Converter/Entity/Entity.cpp index 2ac0410..ef35468 100644 --- a/Code/Converter/Entity/Entity.cpp +++ b/Code/Converter/Entity/Entity.cpp @@ -127,6 +127,11 @@ void SMEntity::WriteObjectToFile( const glm::mat4& transform) const {} +void SMEntity::WriteObjectToFileGltf( + GltfWriterContext& context, + const glm::mat4& transform) const +{} + void SMEntity::CalculateCenterPoint(glm::vec3& outInput) const { outInput += m_position; @@ -219,9 +224,18 @@ void SMEntityWithModel::WriteObjectToFile( WriterOffsetData& offset, const glm::mat4& transform) const { - const glm::mat4 model_matrix = transform * this->GetTransformMatrix(); + const glm::mat4 v_modelMatrix = transform * this->GetTransformMatrix(); + m_model->WriteToFile(v_modelMatrix, offset, file, this); + + ProgCounter::ProgressValue++; +} - m_model->WriteToFile(model_matrix, offset, file, this); +void SMEntityWithModel::WriteObjectToFileGltf( + GltfWriterContext& context, + const glm::mat4& transform) const +{ + const glm::mat4 v_modelMatrix = transform * this->GetTransformMatrix(); + m_model->WriteToFileGltf(context, v_modelMatrix, this); ProgCounter::ProgressValue++; } diff --git a/Code/Converter/Entity/Entity.hpp b/Code/Converter/Entity/Entity.hpp index e400ec9..eebdfd4 100644 --- a/Code/Converter/Entity/Entity.hpp +++ b/Code/Converter/Entity/Entity.hpp @@ -98,6 +98,7 @@ class SMC_NOVTABLE SMEntity virtual std::string GetMtlName(const std::size_t idx) const; virtual void FillTextureMap(EntityTextureMap& textureMap) const = 0; virtual void WriteObjectToFile(std::ofstream& file, WriterOffsetData& offset, const glm::mat4& transform) const; + virtual void WriteObjectToFileGltf(GltfWriterContext& context, const glm::mat4& transform) const; virtual void CalculateCenterPoint(glm::vec3& outInput) const; virtual std::size_t GetAmountOfObjects() const; virtual SMColor GetColor() const; @@ -139,6 +140,9 @@ class SMC_NOVTABLE SMEntityWithModel : public SMEntity std::ofstream& file, WriterOffsetData& offset, const glm::mat4& transform) const override; + void WriteObjectToFileGltf( + GltfWriterContext& context, + const glm::mat4& transform) const override; protected: Model* m_model; diff --git a/Code/Converter/WriterOffset.cpp b/Code/Converter/WriterOffset.cpp new file mode 100644 index 0000000..b33e684 --- /dev/null +++ b/Code/Converter/WriterOffset.cpp @@ -0,0 +1,283 @@ +#include "WriterOffset.hpp" +#include "Utils/Json.hpp" + +SM_UNMANAGED_CODE + +std::string_view GltfAccessorTypeToString(const GltfAccessorType type) noexcept +{ + switch (type) + { + case GltfAccessorType::SCALAR: + return "SCALAR"; + case GltfAccessorType::VEC2: + return "VEC2"; + case GltfAccessorType::VEC3: + return "VEC3"; + case GltfAccessorType::VEC4: + return "VEC4"; + case GltfAccessorType::MAT2: + return "MAT2"; + case GltfAccessorType::MAT3: + return "MAT3"; + case GltfAccessorType::MAT4: + return "MAT4"; + default: + return "UNKNOWN"; + } +} + +////////////////// GLTF BUFFER VIEW ////////////////// + +GltfBufferView::GltfBufferView( + const std::size_t byteLength, + const std::size_t byteOffset +) noexcept + : m_byteLength(byteLength) + , m_byteOffset(byteOffset) + , m_target(GltfBufferViewTarget::ARRAY_BUFFER) +{} + +//////////////// GLTF BUFFER ACCESSOR ///////////////// + +GltfBufferAccessor::GltfBufferAccessor( + const std::size_t viewIdx, + const std::size_t viewOffset, + const GltfComponentType componentType, + const std::size_t itemCount, + const GltfAccessorType type +) noexcept + : m_bufferViewIdx(viewIdx) + , m_bufferOffset(viewOffset) + , m_componentType(componentType) + , m_itemCount(itemCount) + , m_type(type) +{} + +/////////////// GLTF MESH PRIMITIVE //////////////// + +GltfMeshPrimitive::GltfMeshPrimitive() noexcept + : m_vertexAccessorIdx(std::size_t(-1)) + , m_uvAccessorIdx(std::size_t(-1)) + , m_normalAccessorIdx(std::size_t(-1)) + , m_indexAccessorIdx(std::size_t(-1)) + , m_materialIdx(std::size_t(-1)) +{} + +////////////// GLTF MESH //////////////// + +GltfMesh::GltfMesh() + : m_name() + , m_vecPrimitives() +{} + +GltfMeshPrimitive& GltfMesh::createNewPrimitive() +{ + return m_vecPrimitives.emplace_back(); +} + +//////////// GLTF WRITER CONTEXT //////////// + +GltfWriterContext::GltfWriterContext( + const std::wstring& outputFile +) + : m_outFile(outputFile.c_str(), std::ios::binary) + , m_bytesWritten(0) + , m_vecBufferViews() + , m_vecBufferAccessors() + , m_vecMeshes() + , m_vecObjects() + , m_mapPathToMeshIndex() + , m_mapGroupNameToIndex() +{} + +GltfBufferView& GltfWriterContext::createNewView( + const std::size_t offset, + const std::size_t length) +{ + return m_vecBufferViews.emplace_back(length, offset); +} + +GltfBufferAccessor& GltfWriterContext::createNewAccessor( + const std::size_t viewIdx, + const std::size_t viewOffset, + const GltfComponentType componentType, + const std::size_t itemCount, + const GltfAccessorType type) +{ + return m_vecBufferAccessors.emplace_back(viewIdx, viewOffset, componentType, itemCount, type); +} + +GltfMesh& GltfWriterContext::getOrCreateNewGroup( + const std::wstring_view& name) +{ + auto v_iter = m_mapGroupNameToIndex.find(name); + if (v_iter != m_mapGroupNameToIndex.end()) + { + m_selectedMeshIdx = v_iter->second; + return m_vecMeshes[v_iter->second]; + } + + m_mapGroupNameToIndex.emplace(name, m_vecMeshes.size()); + m_selectedMeshIdx = m_vecMeshes.size(); + + auto v_newObject = m_vecObjects.emplace_back(); + v_newObject.m_meshIdx = m_selectedMeshIdx; + v_newObject.m_name = name; + + return m_vecMeshes.emplace_back(); +} + +void GltfWriterContext::writeToFile(const void* buffer, const std::size_t bufferSize) +{ + m_outFile.write(reinterpret_cast(buffer), bufferSize); + m_bytesWritten += bufferSize; +} + +void GltfWriterContext::writeGltfToFile(const std::wstring& filePath) +{ + nlohmann::json v_root(nlohmann::json::value_t::object); + v_root["scene"] = 0; + + { + nlohmann::json v_asset(nlohmann::json::value_t::object); + v_asset["generator"] = "Khronos glTF SM-Converter"; + v_asset["version"] = "2.0"; + + v_root["asset"] = std::move(v_asset); + } + + { + nlohmann::json v_scenes(nlohmann::json::value_t::array); + + { + nlohmann::json v_sceneObject(nlohmann::json::value_t::object); + v_sceneObject["name"] = "Scene"; + + { + nlohmann::json v_nodeArray(nlohmann::json::value_t::array); + + for (std::size_t a = 0; a < m_vecObjects.size(); a++) + v_nodeArray.emplace_back(a); + + v_sceneObject["nodes"] = std::move(v_nodeArray); + } + + v_scenes.emplace_back(std::move(v_sceneObject)); + } + + v_root["scenes"] = std::move(v_scenes); + } + + { + nlohmann::json v_nodeArray(nlohmann::json::value_t::array); + + for (const auto& v_curObject : m_vecObjects) + { + nlohmann::json v_curObjectJson(nlohmann::json::value_t::object); + v_curObjectJson["mesh"] = v_curObject.m_meshIdx; + + v_nodeArray.emplace_back(std::move(v_curObjectJson)); + } + + v_root["nodes"] = std::move(v_nodeArray); + } + + { + nlohmann::json v_accessorArray(nlohmann::json::value_t::array); + + for (const auto& v_curAccessor : m_vecBufferAccessors) + { + nlohmann::json v_curAccessorJson(nlohmann::json::value_t::object); + v_curAccessorJson["componentType"] = static_cast(v_curAccessor.m_componentType); + v_curAccessorJson["byteOffset"] = v_curAccessor.m_bufferOffset; + v_curAccessorJson["bufferView"] = v_curAccessor.m_bufferViewIdx; + v_curAccessorJson["count"] = v_curAccessor.m_itemCount; + v_curAccessorJson["type"] = GltfAccessorTypeToString(v_curAccessor.m_type); + + v_accessorArray.emplace_back(std::move(v_curAccessorJson)); + } + + v_root["accessors"] = std::move(v_accessorArray); + } + + { + nlohmann::json v_meshArray(nlohmann::json::value_t::array); + + for (const auto& v_curMesh : m_vecMeshes) + { + nlohmann::json v_curMeshJson(nlohmann::json::value_t::object); + v_curMeshJson["name"] = "Test"; + + { + nlohmann::json v_primitivesArray(nlohmann::json::value_t::array); + + for (const auto& v_curPrimitive : v_curMesh.m_vecPrimitives) + { + nlohmann::json v_primitiveObject(nlohmann::json::value_t::object); + v_primitiveObject["indices"] = v_curPrimitive.m_indexAccessorIdx; + + if (v_curPrimitive.m_materialIdx != std::size_t(-1)) + v_primitiveObject["material"] = v_curPrimitive.m_materialIdx; + + { + nlohmann::json v_attributesObject(nlohmann::json::value_t::object); + + if (v_curPrimitive.m_vertexAccessorIdx != std::size_t(-1)) + v_attributesObject["POSITION"] = v_curPrimitive.m_vertexAccessorIdx; + + if (v_curPrimitive.m_uvAccessorIdx != std::size_t(-1)) + v_attributesObject["TEXTURE_0"] = v_curPrimitive.m_uvAccessorIdx; + + if (v_curPrimitive.m_normalAccessorIdx != std::size_t(-1)) + v_attributesObject["NORMAL"] = v_curPrimitive.m_normalAccessorIdx; + + v_primitiveObject["attributes"] = std::move(v_attributesObject); + } + + v_primitivesArray.emplace_back(std::move(v_primitiveObject)); + } + + v_curMeshJson["primitives"] = std::move(v_primitivesArray); + } + + v_meshArray.emplace_back(std::move(v_curMeshJson)); + } + + v_root["meshes"] = std::move(v_meshArray); + } + + { + nlohmann::json v_bufferViewArray(nlohmann::json::value_t::array); + + for (const auto& v_curView : m_vecBufferViews) + { + nlohmann::json v_curViewObject(nlohmann::json::value_t::object); + v_curViewObject["buffer"] = 0; + v_curViewObject["byteLength"] = v_curView.m_byteLength; + v_curViewObject["byteOffset"] = v_curView.m_byteOffset; + v_curViewObject["target"] = v_curView.m_target; + + v_bufferViewArray.emplace_back(std::move(v_curViewObject)); + } + + v_root["bufferViews"] = std::move(v_bufferViewArray); + } + + { + nlohmann::json v_bufferArray(nlohmann::json::value_t::array); + + { + nlohmann::json v_bufferObject(nlohmann::json::value_t::object); + v_bufferObject["byteLength"] = m_bytesWritten; + v_bufferObject["uri"] = "mesh_data.bin"; + + v_bufferArray.emplace_back(std::move(v_bufferObject)); + } + + v_root["buffers"] = std::move(v_bufferArray); + } + + std::ofstream v_gltfOutput(filePath); + if (v_gltfOutput.is_open()) + v_gltfOutput << v_root; +} \ No newline at end of file diff --git a/Code/Converter/WriterOffset.hpp b/Code/Converter/WriterOffset.hpp index 319c388..41d57ac 100644 --- a/Code/Converter/WriterOffset.hpp +++ b/Code/Converter/WriterOffset.hpp @@ -2,21 +2,135 @@ #include "Utils/GlmUnmanaged.hpp" #include "Utils/clr_include.hpp" +#include "Utils/Hashing.hpp" SM_UNMANAGED_CODE +#include +#include +#include + #include -#include +enum class GltfComponentType : std::uint16_t +{ + SIGNED_BYTE = 5120, + UNSIGNED_BYTE = 5121, + SIGNED_SHORT = 5122, + UNSIGNED_SHORT = 5123, + UNSIGNED_INT = 5125, + FLOAT = 5126 +}; + +enum class GltfAccessorType : std::uint8_t +{ + NONE = 0, + SCALAR = 1, + VEC2 = 2, + VEC3 = 3, + VEC4 = 4, + MAT2 = 5, + MAT3 = 6, + MAT4 = 7 +}; + +enum class GltfBufferViewTarget : std::uint32_t +{ + ARRAY_BUFFER = 34962, + ELEMENT_ARRAY_BUFFER = 34963 +}; + +std::string_view GltfAccessorTypeToString(const GltfAccessorType type) noexcept; + +struct GltfBufferView +{ + GltfBufferView(const std::size_t byteLength, const std::size_t byteOffset) noexcept; + + std::size_t m_byteLength; + std::size_t m_byteOffset; + GltfBufferViewTarget m_target; +}; + +struct GltfBufferAccessor +{ + GltfBufferAccessor( + const std::size_t viewIdx, + const std::size_t viewOffset, + const GltfComponentType componentType, + const std::size_t itemCount, + const GltfAccessorType type + ) noexcept; + + std::size_t m_bufferViewIdx; + std::size_t m_bufferOffset; + GltfComponentType m_componentType; + std::size_t m_itemCount; + GltfAccessorType m_type; +}; + +struct GltfMeshPrimitive +{ + GltfMeshPrimitive() noexcept; + + std::size_t m_vertexAccessorIdx; + std::size_t m_uvAccessorIdx; + std::size_t m_normalAccessorIdx; + std::size_t m_indexAccessorIdx; + std::size_t m_materialIdx; +}; + +struct GltfMesh +{ + GltfMesh(); + + GltfMeshPrimitive& createNewPrimitive(); + + std::wstring m_name; + std::vector m_vecPrimitives; +}; + +struct GltfObject +{ + std::wstring m_name; + std::size_t m_meshIdx; + glm::mat4 m_matrix; +}; + +struct GltfWriterContext +{ + GltfWriterContext(const std::wstring& outputFile); + + GltfBufferView& createNewView(const std::size_t offset, const std::size_t length); + GltfBufferAccessor& createNewAccessor( + const std::size_t viewIdx, + const std::size_t viewOffset, + const GltfComponentType componentType, + const std::size_t itemCount, + const GltfAccessorType type); + + GltfMesh& getOrCreateNewGroup(const std::wstring_view& name); + + void writeToFile(const void* buffer, const std::size_t bufferSize); + void writeGltfToFile(const std::wstring& filePath); + + std::ofstream m_outFile; + std::size_t m_bytesWritten; + std::size_t m_selectedMeshIdx = std::size_t(-1); + + std::vector m_vecBufferViews; + std::vector m_vecBufferAccessors; + std::vector m_vecMeshes; + std::vector m_vecObjects; + + emhash8::HashMap> m_mapPathToMeshIndex; + emhash8::HashMap> m_mapGroupNameToIndex; +}; struct WriterOffsetData { emhash8::HashMap VertexMap = {}; emhash8::HashMap NormalMap = {}; emhash8::HashMap UvMap = {}; - //std::unordered_map VertexMap = {}; - //std::unordered_map NormalMap = {}; - //std::unordered_map UvMap = {}; std::size_t Vertex = 1; std::size_t Normal = 1; diff --git a/Code/ObjectDatabase/ModelStorage.cpp b/Code/ObjectDatabase/ModelStorage.cpp index 1046d16..96d6e5a 100644 --- a/Code/ObjectDatabase/ModelStorage.cpp +++ b/Code/ObjectDatabase/ModelStorage.cpp @@ -20,6 +20,7 @@ SubMeshData::SubMeshData( : m_materialName() , m_dataIdx() , m_subMeshIdx(idx) + , m_indexBufferIdx(std::size_t(-1)) , m_hasNormals(hasNormals) , m_hasUvs(hasUvs) {} @@ -33,6 +34,7 @@ SubMeshData::SubMeshData( : m_materialName() , m_dataIdx(dataIdx.begin(), dataIdx.end()) , m_subMeshIdx(idx) + , m_indexBufferIdx(std::size_t(-1)) , m_hasNormals(hasNormals) , m_hasUvs(hasUvs) {} @@ -41,6 +43,7 @@ SubMeshData::SubMeshData(SubMeshData&& other) noexcept : m_materialName(std::move(other.m_materialName)) , m_dataIdx(std::move(other.m_dataIdx)) , m_subMeshIdx(other.m_subMeshIdx) + , m_indexBufferIdx(other.m_indexBufferIdx) , m_hasNormals(other.m_hasNormals) , m_hasUvs(other.m_hasUvs) {} @@ -193,7 +196,7 @@ Model::Model(const std::wstring_view& mesh_path) , m_normals() , m_uvs() , m_subMeshData() - , m_bUvsCached(false) + , m_uvCacheIdx(std::size_t(-1)) {} Model::Model() @@ -202,7 +205,7 @@ Model::Model() , m_normals() , m_uvs() , m_subMeshData() - , m_bUvsCached(false) + , m_uvCacheIdx(std::size_t(-1)) {} // This is actually faster than sprintf("%g %g %g") @@ -263,9 +266,9 @@ void Model::WriteToFile( } } - if (SharedConverterSettings::ExportUvs && !m_bUvsCached) + if (SharedConverterSettings::ExportUvs && m_uvCacheIdx == std::size_t(-1)) { - m_bUvsCached = true; + m_uvCacheIdx = 0; g_modelWriterBuf[1] = 't'; g_modelWriterBuf[2] = ' '; @@ -417,6 +420,206 @@ void Model::WriteToFile( offset.Vertex += m_vertices.size(); } +void Model::WriteToFileGltf( + GltfWriterContext& context, + const glm::mat4& modelMatrix, + const SMEntity* pEntity) +{ + std::size_t v_vertexAccessorIdx = std::size_t(-1); + std::size_t v_uvAccessorIdx = std::size_t(-1); + std::size_t v_normalAccessorIdx = std::size_t(-1); + + const std::size_t v_meshBufferViewIndex = context.m_vecBufferViews.size(); + // The length is deterined at the end of the function + auto& v_newMeshBufferView = context.createNewView(context.m_bytesWritten, 0); + + { + v_vertexAccessorIdx = context.m_vecBufferAccessors.size(); + + context.createNewAccessor( + v_meshBufferViewIndex, + context.m_bytesWritten - v_newMeshBufferView.m_byteOffset, + GltfComponentType::FLOAT, + m_vertices.size(), + GltfAccessorType::VEC3 + ); + + for (const glm::vec3& v_curVtx : m_vertices) + { + const glm::vec3 v_vertex = modelMatrix * glm::vec4(v_curVtx, 1.0f); + context.writeToFile(&v_vertex, sizeof(v_vertex)); + } + } + + if (SharedConverterSettings::ExportUvs) + { + if (m_uvCacheIdx == std::size_t(-1)) + { + v_uvAccessorIdx = context.m_vecBufferAccessors.size(); + m_uvCacheIdx = v_uvAccessorIdx; + + context.createNewAccessor( + v_meshBufferViewIndex, + context.m_bytesWritten - v_newMeshBufferView.m_byteOffset, + GltfComponentType::FLOAT, + m_uvs.size(), + GltfAccessorType::VEC2 + ); + + context.writeToFile(m_uvs.data(), m_uvs.size() * sizeof(glm::vec2)); + } + else + { + v_uvAccessorIdx = m_uvCacheIdx; + } + } + + if (SharedConverterSettings::ExportNormals) + { + v_normalAccessorIdx = context.m_vecBufferAccessors.size(); + + context.createNewAccessor( + v_meshBufferViewIndex, + context.m_bytesWritten - v_newMeshBufferView.m_byteOffset, + GltfComponentType::FLOAT, + m_normals.size(), + GltfAccessorType::VEC3 + ); + + const glm::mat3 v_rotMatrix(modelMatrix); + + for (const glm::vec3& v_curNormal : m_normals) + { + const glm::vec3 v_normal = v_rotMatrix * v_curNormal; + context.writeToFile(&v_normal, sizeof(v_normal)); + } + } + + GltfMesh& v_curMesh = context.m_vecMeshes[context.m_selectedMeshIdx]; + + const std::size_t v_subMeshDataCount = m_subMeshData.size(); + for (std::size_t v_idx = 0; v_idx < v_subMeshDataCount; v_idx++) + { + SubMeshData& v_curSubMesh = m_subMeshData[v_idx]; + // Skip writing the sub mesh if entity doesn't allow it + if (pEntity != nullptr && !pEntity->GetCanWrite(v_curSubMesh.m_materialName, v_idx)) + continue; + + auto& v_newPrimitive = v_curMesh.m_vecPrimitives.emplace_back(); + v_newPrimitive.m_vertexAccessorIdx = v_vertexAccessorIdx; + v_newPrimitive.m_uvAccessorIdx = v_uvAccessorIdx; + v_newPrimitive.m_normalAccessorIdx = v_normalAccessorIdx; + + if (v_curSubMesh.m_indexBufferIdx != std::size_t(-1)) + { + v_newPrimitive.m_indexAccessorIdx = v_curSubMesh.m_indexBufferIdx; + continue; + } + + v_newPrimitive.m_indexAccessorIdx = context.m_vecBufferAccessors.size(); + v_curSubMesh.m_indexBufferIdx = v_newPrimitive.m_indexAccessorIdx; + + auto& v_indexAccessor = context.createNewAccessor( + v_meshBufferViewIndex, + context.m_bytesWritten - v_newMeshBufferView.m_byteOffset, + GltfComponentType::UNSIGNED_SHORT, + 0, + GltfAccessorType::SCALAR + ); + + for (const auto& v_curVertices : v_curSubMesh.m_dataIdx) + { + v_indexAccessor.m_itemCount += v_curVertices.size(); + + for (const VertexData& v_curVertex : v_curVertices) + { + const std::uint16_t v_shortIdx = static_cast(v_curVertex.m_vert); + context.writeToFile(&v_shortIdx, sizeof(v_shortIdx)); + } + } + } + + v_newMeshBufferView.m_byteLength = context.m_bytesWritten - v_newMeshBufferView.m_byteOffset; + /* + + IndexWriterArguments v_idxWriterArgs(this, offset, v_translatedVertices, v_translatedNormals); + + const std::size_t v_subMeshDataCount = m_subMeshData.size(); + for (std::size_t mIdx = 0; mIdx < v_subMeshDataCount; mIdx++) + { + const SubMeshData& v_curSubMesh = m_subMeshData[mIdx]; + + if (pEntity != nullptr) + { + //Skip writing the sub mesh if entity doesn't allow it + if (!pEntity->GetCanWrite(v_curSubMesh.m_materialName, mIdx)) + continue; + + if (SharedConverterSettings::ExportMaterials) + { + g_modelWriterPtr = g_modelWriterBuf; + *g_modelWriterPtr++ = 'u'; + *g_modelWriterPtr++ = 's'; + *g_modelWriterPtr++ = 'e'; + *g_modelWriterPtr++ = 'm'; + *g_modelWriterPtr++ = 't'; + *g_modelWriterPtr++ = 'l'; + *g_modelWriterPtr++ = ' '; + + g_modelWriterPtr = pEntity->GetMtlNameCStr(v_curSubMesh.m_materialName, mIdx, g_modelWriterPtr); + *g_modelWriterPtr++ = '\n'; + + file.write(g_modelWriterBuf, g_modelWriterPtr - g_modelWriterBuf); + } + } + + v_idxWriterArgs.m_subMesh = &v_curSubMesh; + + SubMeshData::IndexWriterFunction v_pWriterFunc = v_curSubMesh.getWriterFunction(); + + const std::size_t v_quadCount = v_curSubMesh.m_dataIdx.size(); + for (std::size_t a = 0; a < v_quadCount; a++) + { + //Put the pointer back to the beginning of the buffer + g_modelWriterPtr = g_modelWriterBuf; + *g_modelWriterPtr++ = 'f'; + + const std::vector& v_vecVerts = v_curSubMesh.m_dataIdx[a]; + const std::size_t v_vecVertsSz = v_vecVerts.size(); + + if (v_vecVertsSz > 30) //The N-Gon is too big to be handled with a fast for loop + { + for (std::size_t b = 0; b < 30; b++) + v_pWriterFunc(v_idxWriterArgs, v_vecVerts[b]); + + for (std::size_t b = 30; b < v_vecVertsSz; b++) + { + v_pWriterFunc(v_idxWriterArgs, v_vecVerts[b]); + + //never let the writer reach the end of the buffer + if ((g_modelWriterBufferEnd - g_modelWriterPtr) < 100) + { + DebugOutL("Reaching the end of the model writer buffer, resetting... (Remaining space: ", std::size_t(g_modelWriterBufferEnd - g_modelWriterPtr), ")"); + + file.write(g_modelWriterBuf, g_modelWriterPtr - g_modelWriterBuf); + g_modelWriterPtr = g_modelWriterBuf; + } + } + } + else + { + for (std::size_t b = 0; b < v_vecVertsSz; b++) + v_pWriterFunc(v_idxWriterArgs, v_vecVerts[b]); + } + + *g_modelWriterPtr++ = '\n'; + + file.write(g_modelWriterBuf, g_modelWriterPtr - g_modelWriterBuf); + } + } + */ +} + ////////////////// MODEL STORAGE /////////////////////// const aiScene* ModelStorage::LoadScene(const std::wstring_view& path) @@ -426,7 +629,8 @@ const aiScene* ModelStorage::LoadScene(const std::wstring_view& path) aiProcess_FindInvalidData | aiProcess_RemoveComponent | aiProcess_JoinIdenticalVertices | - aiProcess_FindDegenerates + aiProcess_FindDegenerates | + aiProcess_Triangulate ); } diff --git a/Code/ObjectDatabase/ModelStorage.hpp b/Code/ObjectDatabase/ModelStorage.hpp index a27a3a6..140eca4 100644 --- a/Code/ObjectDatabase/ModelStorage.hpp +++ b/Code/ObjectDatabase/ModelStorage.hpp @@ -93,6 +93,7 @@ struct SubMeshData std::vector> m_dataIdx; std::uint32_t m_subMeshIdx; + std::size_t m_indexBufferIdx; bool m_hasNormals; bool m_hasUvs; }; @@ -106,6 +107,7 @@ struct Model Model(Model&) = delete; void WriteToFile(const glm::mat4& model_mat, WriterOffsetData& offset, std::ofstream& file, const class SMEntity* pEntity); + void WriteToFileGltf(GltfWriterContext& context, const glm::mat4& modelMatrix, const class SMEntity* pEntity); inline bool IsEmpty() const noexcept { @@ -119,7 +121,7 @@ struct Model std::vector m_uvs; std::vector m_subMeshData; - bool m_bUvsCached; + std::size_t m_uvCacheIdx; }; class ModelStorage diff --git a/SMConverterQt.vcxproj b/SMConverterQt.vcxproj index cb26b9f..d9ce590 100644 --- a/SMConverterQt.vcxproj +++ b/SMConverterQt.vcxproj @@ -162,6 +162,7 @@ xcopy /y /d "$(SolutionDir)Media\icon.ico" "$(SolutionDir)Build\$(ProjectName)-$ + diff --git a/SMConverterQt.vcxproj.filters b/SMConverterQt.vcxproj.filters index c012eb1..c329d86 100644 --- a/SMConverterQt.vcxproj.filters +++ b/SMConverterQt.vcxproj.filters @@ -269,6 +269,9 @@ Source Files + + Source Files +