From 33ca4213b6e6579dec2f9afad4ce50552e810e95 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Mon, 18 Nov 2024 18:15:55 +0200 Subject: [PATCH 01/15] WriteObjAsZip and WriteAsUSDZ MeshWriter methods --- openVulkanoCpp/Scene/MeshWriter.cpp | 375 ++++++++++++++++++++++++---- openVulkanoCpp/Scene/MeshWriter.hpp | 2 + 2 files changed, 322 insertions(+), 55 deletions(-) diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index be6326f..787f110 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -7,12 +7,19 @@ #include "MeshWriter.hpp" #include "Scene/Geometry.hpp" #include "Scene/Vertex.hpp" +#include "IO/Archive/ArchiveWriter.hpp" #include #include #include #include #include #include +#include +#include +#include +#include +#include +#include namespace { @@ -37,6 +44,243 @@ namespace return result; } + + std::string ReadEntireFile(const std::string& fname) + { + FILE *file = fopen(fname.c_str(), "rb"); + std::string buffer; + if(file) + { + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fseek(file, 0, SEEK_SET); + buffer = std::string(size, ' '); + fread(&buffer[0], size, 1, file); + fclose(file); + } + return buffer; + } + + void WriteGeometryWithoutTexturesUsingTinyusdz(OpenVulkano::Scene::Geometry* geometry, const std::string& filePath) + { + tinyusdz::Stage stage; + tinyusdz::Xform xform; + tinyusdz::GeomMesh mesh; + mesh.name = "TheMesh"; + + std::vector pts(geometry->vertexCount); + std::vector indices(geometry->indexCount); + tinyusdz::Attribute uvAttr; + std::vector uvs(geometry->vertexCount); + + for (uint32_t i = 0; i < geometry->vertexCount; ++i) + { + const OpenVulkano::Vertex& v = geometry->vertices[i]; + pts[i].x = v.position.x; + pts[i].y = v.position.y; + pts[i].z = v.position.z; + uvs[i] = { v.textureCoordinates.x, v.textureCoordinates.y }; + } + + mesh.points.set_value(pts); + uvAttr.set_value(uvs); + + std::vector counts(geometry->indexCount / 3, 3); // NOTE(vb): The value 3 is kind of arbitrary, but this array must be in the mesh! + mesh.faceVertexCounts.set_value(counts); + + for (uint32_t i = 0; i < geometry->indexCount; ++i) + { + uint32_t index = GetIndexFromGeometry(geometry, i); + indices[i] = index; + } + mesh.faceVertexIndices.set_value(indices); + + uvAttr.metas().interpolation = tinyusdz::Interpolation::FaceVarying; + tinyusdz::Property uvProp(uvAttr); + mesh.props.emplace("primvars:UVMap", uvProp); + + tinyusdz::Prim xformPrim(xform); + tinyusdz::Prim meshPrim(mesh); + std::string err; + if (!xformPrim.add_child(std::move(meshPrim), true, &err)) + { + throw std::runtime_error("Failed to construct scene: " + err); + } + + if (!stage.add_root_prim(std::move(xformPrim))) + { + throw std::runtime_error("Failed to add prim to stage root: " + stage.get_error()); + } + + stage.metas().defaultPrim = tinyusdz::value::token(xformPrim.element_name()); + stage.metas().comment = "Generated by OpenVulkanoCpp"; + if (!stage.commit()) + { + throw std::runtime_error("Failed to commit stage: " + stage.get_error()); + } + + std::ofstream file(filePath); + if (!file.is_open()) + throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); + std::string scene = to_string(stage); + file << scene << "\n"; + file.close(); + } + + std::vector ConvertToUSDC(const std::string& contents) + { + std::vector result; + tinyusdz::Stage stage; + std::string warn, err; + assert(tinyusdz::LoadUSDAFromMemory((const uint8_t *)contents.data(), (const size_t)contents.size(), "", &stage, &warn, &err)); + bool ret = tinyusdz::usdc::SaveAsUSDCToMemory(stage, &result, &warn, &err); // As for now, this reports that it's not implemented... + return result; + } + + std::string GetUsdContents(OpenVulkano::Scene::Geometry* geometry, const std::string texturePath = "") + { + std::ostringstream file; + std::ostringstream points, normals, indices, texCoords, faceCounts; + points << std::fixed << std::setprecision(6); + normals << std::fixed << std::setprecision(6); + + for (size_t i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + points << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")"; + normals << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")"; + if (i < geometry->vertexCount - 1) + { + points << ", "; + normals << ", "; + } + } + + for (size_t i = 0; i < geometry->indexCount; ++i) + { + indices << GetIndexFromGeometry(geometry, i); + if (i < geometry->indexCount - 1) + { + indices << ", "; + } + if ((i + 1) % 3 == 0) + { + faceCounts << "3"; + if (i < geometry->indexCount - 1) + { + faceCounts << ", "; + } + } + } + + texCoords << std::fixed << std::setprecision(6); + + for (size_t i = 0; i < geometry->indexCount; ++i) + { + const size_t vertexIndex = GetIndexFromGeometry(geometry, i); + const auto& v = geometry->vertices[vertexIndex]; + texCoords << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")"; + if (i < geometry->indexCount - 1) + { + texCoords << ", "; + } + } + + file << R"(#usda 1.0 +( + defaultPrim = "root" + doc = "Exported from OpenVulkano" + metersPerUnit = 1 + upAxis = "Z" +) + +def Xform "root" ( + customData = { + dictionary Blender = { + bool generated = 1 + } + } +) +{ + def Xform "model" + { + custom string userProperties:blender:object_name = "model" + float3 xformOp:rotateXYZ = (89.99999, -0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"] + + def Mesh "model" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, -0.5, 0), (0.5, 0.5, 0)] + int[] faceVertexCounts = [)" + << faceCounts.str() << R"(] + int[] faceVertexIndices = [)" + << indices.str() << R"(] + rel material:binding = + normal3f[] normals = [)" + << normals.str() << R"(] ( + interpolation = "faceVarying" + ) + point3f[] points = [)" + << points.str() << R"(] + texCoord2f[] primvars:st = [)" + << texCoords.str() << R"(] ( + interpolation = "faceVarying" + ) + uniform token subdivisionScheme = "none" + custom string userProperties:blender:data_name = "model" + } + } + + def Scope "_materials" + { + def Material "Material0" + { + token outputs:surface.connect = + custom string userProperties:blender:data_name = "Material0" + + def Shader "Principled_BSDF" + { + uniform token info:id = "UsdPreviewSurface" + float inputs:clearcoat = 0 + float inputs:clearcoatRoughness = 0.03 + color3f inputs:diffuseColor.connect = + float inputs:ior = 1.5 + float inputs:metallic = 0 + float inputs:opacity = 1 + float inputs:roughness = 1 + float inputs:specular = 0 + token outputs:surface + } + + def Shader "Image_Texture" + { + uniform token info:id = "UsdUVTexture" + asset inputs:file = @./texture.png@ + token inputs:sourceColorSpace = "sRGB" + float2 inputs:st.connect = + token inputs:wrapS = "repeat" + token inputs:wrapT = "repeat" + float3 outputs:rgb + } + + def Shader "uvmap" + { + uniform token info:id = "UsdPrimvarReader_float2" + string inputs:varname = "st" + float2 outputs:result + } + } + } +} +)"; + return file.str(); + } } namespace OpenVulkano::Scene @@ -90,65 +334,86 @@ namespace OpenVulkano::Scene void MeshWriter::WriteAsUSD(Geometry* geometry, const std::string& filePath) { - tinyusdz::Stage stage; - tinyusdz::Xform xform; - tinyusdz::GeomMesh mesh; - mesh.name = "TheMesh"; - - std::vector pts(geometry->vertexCount); - std::vector indices(geometry->indexCount); - tinyusdz::Attribute uvAttr; - std::vector uvs(geometry->vertexCount); - - for (uint32_t i = 0; i < geometry->vertexCount; ++i) - { - const Vertex& v = geometry->vertices[i]; - pts[i].x = v.position.x; - pts[i].y = v.position.y; - pts[i].z = v.position.z; - uvs[i] = { v.textureCoordinates.x, v.textureCoordinates.y }; - } - - mesh.points.set_value(pts); - uvAttr.set_value(uvs); - - std::vector counts(geometry->indexCount / 3, 3); // NOTE(vb): The value 3 is kind of arbitrary, but this array must be in the mesh! - mesh.faceVertexCounts.set_value(counts); - - for (uint32_t i = 0; i < geometry->indexCount; ++i) - { - uint32_t index = GetIndexFromGeometry(geometry, i); - indices[i] = index; - } - mesh.faceVertexIndices.set_value(indices); - - uvAttr.metas().interpolation = tinyusdz::Interpolation::FaceVarying; - tinyusdz::Property uvProp(uvAttr); - mesh.props.emplace("primvars:UVMap", uvProp); - - tinyusdz::Prim xformPrim(xform); - tinyusdz::Prim meshPrim(mesh); - std::string err; - if (!xformPrim.add_child(std::move(meshPrim), true, &err)) - { - throw std::runtime_error("Failed to construct scene: " + err); - } - if (!stage.add_root_prim(std::move(xformPrim))) - { - throw std::runtime_error("Failed to add prim to stage root: " + stage.get_error()); - } - stage.metas().defaultPrim = tinyusdz::value::token(xformPrim.element_name()); - stage.metas().comment = "Generated by OpenVulkanoCpp"; - if (!stage.commit()) - { - throw std::runtime_error("Failed to commit stage: " + stage.get_error()); - } - std::ofstream file(filePath); if (!file.is_open()) throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); - std::string scene = to_string(stage); + std::string scene = GetUsdContents(geometry); file << scene << "\n"; file.close(); } + + void MeshWriter::WriteObjAsZip(Geometry* geometry, const std::string& zipPath, const std::string& texturePath) + { + std::stringstream mtlContent; + std::string materialName = "Material0"; + mtlContent << "newmtl " << materialName << "\n"; + mtlContent << "Ka 1.000 1.000 1.000\n"; // Ambient + mtlContent << "Kd 1.000 1.000 1.000\n"; // Diffuse + mtlContent << "Ks 0.000 0.000 0.000\n"; // Specular + if (!texturePath.empty()) + { + mtlContent << "map_Ka texture.png\n"; // Ambient map + mtlContent << "map_Kd texture.png\n"; // Texture map + } + + std::stringstream objContent; + objContent << "# OBJ file generated by OpenVulkanoCpp\n"; + objContent << "mtllib material.mtl\n"; + objContent << "usemtl " << materialName << "\n"; + for (int i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + objContent << fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z); + } + for (int i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + objContent << fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); + } + for (int i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + objContent << fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y); + } + for (int i = 0; i < geometry->indexCount; i += 3) + { + uint32_t i0 = GetIndexFromGeometry(geometry, i) + 1; + uint32_t i1 = GetIndexFromGeometry(geometry, i + 1) + 1; + uint32_t i2 = GetIndexFromGeometry(geometry, i + 2) + 1; + objContent << fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); + } + + OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str()); + + auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContent.str().size()); + zipWriter.AddFile(objDesc, objContent.str().data()); + + auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContent.str().size()); + zipWriter.AddFile(mtlDesc, mtlContent.str().data()); + + if (!texturePath.empty() && std::filesystem::exists(texturePath)) + { + auto textureFileSize = std::filesystem::file_size(texturePath); + auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFileSize); + zipWriter.AddFile(texDesc, ReadEntireFile(texturePath).c_str()); + } + } + + void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& usdzPath, const std::string& texturePath) + { + OpenVulkano::ArchiveConfiguration config; + config.type = OpenVulkano::ArchiveType::ZIP; + OpenVulkano::ArchiveWriter zipWriter(usdzPath.c_str(), config); + + std::string usd = GetUsdContents(geometry, texturePath); + auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usd.size()); + zipWriter.AddFile(usdDesc, usd.data()); + + if (!texturePath.empty() && std::filesystem::exists(texturePath)) + { + auto textureFileSize = std::filesystem::file_size(texturePath); + auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFileSize); + zipWriter.AddFile(texDesc, ReadEntireFile(texturePath).c_str()); + } + } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/MeshWriter.hpp b/openVulkanoCpp/Scene/MeshWriter.hpp index 940cc97..2c8176b 100644 --- a/openVulkanoCpp/Scene/MeshWriter.hpp +++ b/openVulkanoCpp/Scene/MeshWriter.hpp @@ -16,5 +16,7 @@ namespace OpenVulkano::Scene public: static void WriteAsOBJ(Geometry* geometry, const std::string& filePath); static void WriteAsUSD(Geometry* geometry, const std::string& filePath); + static void WriteObjAsZip(Geometry* geometry, const std::string& zipPath, const std::string& texturePath); + static void WriteAsUSDZ(Geometry* geometry, const std::string& usdzPath, const std::string& texturePath); }; } \ No newline at end of file From d2beedb8a2de2c66128f64be8f8b7f981d3bd79b Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Wed, 20 Nov 2024 19:35:07 +0200 Subject: [PATCH 02/15] CRC32 implementation --- openVulkanoCpp/Math/CRC32.hpp | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 openVulkanoCpp/Math/CRC32.hpp diff --git a/openVulkanoCpp/Math/CRC32.hpp b/openVulkanoCpp/Math/CRC32.hpp new file mode 100644 index 0000000..7c95649 --- /dev/null +++ b/openVulkanoCpp/Math/CRC32.hpp @@ -0,0 +1,52 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +class CRC32 +{ + uint32_t m_table[256]; + uint32_t m_currentValue = 0; + +public: + CRC32() + { + uint32_t polynomial = 0xEDB88320; + for (uint32_t i = 0; i < 256; i++) + { + uint32_t c = i; + for (size_t j = 0; j < 8; j++) + { + if (c & 1) + { + c = polynomial ^ (c >> 1); + } + else + { + c >>= 1; + } + } + m_table[i] = c; + } + } + + void Update(size_t len, const uint8_t* buf) + { + uint32_t c = m_currentValue ^ 0xFFFFFFFF; + for (size_t i = 0; i < len; ++i) + { + c = m_table[(c ^ buf[i]) & 0xFF] ^ (c >> 8); + } + m_currentValue = c ^ 0xFFFFFFFF; + } + + uint32_t GetValue() const + { + return m_currentValue; + } +}; \ No newline at end of file From a9fd85e9b3c6cddfee345fafbe3e96c060b2b931 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Wed, 20 Nov 2024 19:36:29 +0200 Subject: [PATCH 03/15] ZipWriter implementation + tests --- openVulkanoCpp/IO/Archive/ZipWriter.cpp | 243 ++++++++++++++++++++++++ openVulkanoCpp/IO/Archive/ZipWriter.hpp | 29 +++ tests/IO/Archive/ZipWriterTest.cpp | 76 ++++++++ 3 files changed, 348 insertions(+) create mode 100644 openVulkanoCpp/IO/Archive/ZipWriter.cpp create mode 100644 openVulkanoCpp/IO/Archive/ZipWriter.hpp create mode 100644 tests/IO/Archive/ZipWriterTest.cpp diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.cpp b/openVulkanoCpp/IO/Archive/ZipWriter.cpp new file mode 100644 index 0000000..120e024 --- /dev/null +++ b/openVulkanoCpp/IO/Archive/ZipWriter.cpp @@ -0,0 +1,243 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +/* References: + * https://libzip.org/specifications/extrafld.txt + * https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html + * https://learn.microsoft.com/en-us/windows/win32/sysinfo/converting-a-time-t-value-to-a-file-time +*/ + +#include "ZipWriter.hpp" +#include "Math/CRC32.hpp" + +#include +#include + +namespace +{ +#pragma pack(push, 1) + struct LocalFileHeader + { + uint32_t signature = 0x04034b50; + uint16_t versionToExtract = 10; // Version needed to extract (minimum) + uint16_t generalPurposeFlags = 0; // General purpose bit flag + uint16_t compressionMethod = 0; // Compression method + uint16_t fileLastModTime = 0; // File last modification time + uint16_t fileLastModDate = 0; // File last modification date + uint32_t crc32 = 0; // CRC-32 + uint32_t compressedSize = 0; // Compressed size + uint32_t uncompressedSize = 0; // Uncompressed size + uint16_t fileNameLength = 0; // File name length (n) + uint16_t extraFieldLength = 0; // Extra field length (m) + // File Name[n] + // Extra Field[m] + }; + + struct CentalDirectoryFileHeader + { + uint32_t signature = 0x02014b50; + uint16_t versionMadeBy = 31; // Version made by + uint16_t versionToExtract = 10; // Version needed to extract (minimum) + uint16_t generalPurposeFlags = 0; // General purpose bit flag + uint16_t compressionMethod = 0; // Compression method + uint16_t fileLastModTime = 0; // File last modification time + uint16_t fileLastModDate = 0; // File last modification date + uint32_t crc32 = 0; // CRC-32 + uint32_t compressedSize = 0; // Compressed size + uint32_t uncompressedSize = 0; // Uncompressed size + uint16_t fileNameLength = 0; // File name length (n) + uint16_t extraFieldLength = 0; // Extra field length (m) + uint16_t fileCommentLength = 0; // File comment length (k) + uint16_t diskNumber = 0; // Disk number where file starts + uint16_t internalFileAttribs = 0; // Internal file attributes + uint32_t externalFileAttribs = 0; // External file attributes + uint32_t relativeOffsetOfLocalFileHeader = 0; // Relative offset of local file header. This is the number of bytes between the start of the first disk on which the file occurs, and the start of the local file header + // File Name[n] + // Extra Field[m] + // File Comment[k] + }; + + struct NtfsExtraField + { + uint16_t tag = 10; // Tag for this "extra" block type + uint16_t tSize = 32; // Total Data Size for this block + uint32_t reserved = 0; // for future use + uint16_t tag1 = 1; // NTFS attribute tag value #1 + uint16_t tSize1 = 24; // Size of attribute #1, in bytes + uint64_t modTime; // 64-bit NTFS file last modification time + uint64_t acTime; // 64-bit NTFS file last access time + uint64_t crTime; // 64-bit NTFS file creation time + }; + + struct EndOfCentralDirectoryHeader + { + uint32_t signature = 0x06054b50; + uint16_t diskNumber = 0; // Number of this disk + uint16_t centralDirectoryDiskNumber = 0; // Disk where central directory starts + uint16_t centralDirectoryEntries = 0; // Number of central directory records on this disk + uint16_t totalCentralDirectoryEntries = 0; // Total number of central directory records + uint32_t centralDirectorySize = 0; // Size of central directory (bytes) + uint32_t centralDirectoryOffset = 0; // Offset of start of central directory, relative to start of archive + uint16_t commentLength = 0; // Comment length (n) + // Comment[n] + }; +#pragma pack(pop) + + static_assert(sizeof(LocalFileHeader) == 30, "Well packed struct"); + static_assert(sizeof(CentalDirectoryFileHeader) == 46, "Well packed struct"); + static_assert(sizeof(EndOfCentralDirectoryHeader) == 22, "Well packed struct"); + static_assert(sizeof(NtfsExtraField) == 36, "Well packed struct"); + + std::vector ReadEntireFile(const std::string& fname) + { + FILE *file = fopen(fname.c_str(), "rb"); + std::vector buffer; + if(file) + { + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fseek(file, 0, SEEK_SET); + buffer.resize(size); + fread(buffer.data(), size, 1, file); + fclose(file); + } + return buffer; + } + + template + uint32_t Cat(std::vector& dest, T* thing) + { + uint32_t startOffset = dest.size(); + dest.insert(dest.end(), (uint8_t *)thing, (uint8_t *)(thing + 1)); + return startOffset; + } + + uint32_t Cat(std::vector& dest, size_t size, uint8_t* thing) + { + uint32_t startOffset = dest.size(); + dest.insert(dest.end(), (uint8_t*)thing, (uint8_t*)(thing + size)); + return startOffset; + } + + uint32_t Cat(std::vector& dest, const std::vector& thing) + { + uint32_t startOffset = Cat(dest, thing.size(), (uint8_t*)thing.data()); + return startOffset; + } + + void TimetToFileTime(time_t t, uint64_t* lpWinFileTime) + { + // See references for details + *lpWinFileTime = (t * 10000000LL) + 116444736000000000LL; + } + + std::pair ConvertToDosTimeDate(time_t time) + { + std::tm* tm = std::localtime(&time); + if (tm) + { + uint16_t dosTime = 0; + dosTime |= (tm->tm_sec / 2) & 0x1F; // Seconds divided by 2 (Bits 00-04) + dosTime |= (tm->tm_min & 0x3F) << 5; // Minutes (Bits 05-10) + dosTime |= (tm->tm_hour & 0x1F) << 11; // Hours (Bits 11-15) + + uint16_t dosDate = 0; + dosDate |= (tm->tm_mday & 0x1F); // Day (Bits 00-04) + dosDate |= ((tm->tm_mon + 1) & 0x0F) << 5; // Month (Bits 05-08) + dosDate |= ((tm->tm_year - 80) & 0x7F) << 9; // Year from 1980 (Bits 09-15) + return { dosTime, dosDate }; + } + + return {}; + } +} + +namespace OpenVulkano +{ + void ZipWriter::AddFile(const FileDescription& description, const void* buffer) + { + size_t fileSize = description.size; + size_t fileNameLength = description.path.size(); + uint8_t *fileName = (uint8_t*)description.path.data(); + CRC32 crc; + crc.Update(fileSize, (const uint8_t *)buffer); + uint32_t crc32 = crc.GetValue(); + time_t createTime = description.createTime; + time_t modTime = description.modTime; + time_t accessTime = modTime; // FileDescription doesn't have this field + auto [dosTime, dosDate] = ConvertToDosTimeDate(modTime); + + LocalFileHeader lfh; + lfh.fileLastModTime = dosTime; + lfh.fileLastModDate = dosDate; + lfh.crc32 = crc32; + lfh.compressedSize = lfh.uncompressedSize = fileSize; + lfh.fileNameLength = fileNameLength; + + size_t headerOffset = Cat(m_headers, &lfh); + Cat(m_headers, fileNameLength, fileName); + Cat(m_headers, description.size, (uint8_t *)buffer); + + CentalDirectoryFileHeader cdfh; + cdfh.fileLastModTime = dosTime; + cdfh.fileLastModDate = dosDate; + cdfh.crc32 = crc32; + cdfh.compressedSize = cdfh.uncompressedSize = fileSize; + cdfh.fileNameLength = fileNameLength; + cdfh.extraFieldLength = sizeof(NtfsExtraField); + cdfh.externalFileAttribs = 32; // NOTE(vb): I've no idea wtf is this value mean + cdfh.relativeOffsetOfLocalFileHeader = headerOffset; + + NtfsExtraField ntfs; + TimetToFileTime(modTime, &ntfs.modTime); + TimetToFileTime(accessTime, &ntfs.acTime); + TimetToFileTime(createTime, &ntfs.crTime); + + Cat(m_centralDirs, &cdfh); + Cat(m_centralDirs, fileNameLength, fileName); + Cat(m_centralDirs, &ntfs); + + m_numFiles += 1; + } + + std::vector ZipWriter::GetMemory() + { + std::vector buffer = m_headers; + + if (m_numFiles) + { + int centralDirsOffset = Cat(buffer, m_centralDirs); + } + + EndOfCentralDirectoryHeader eocd; + eocd.centralDirectoryEntries = eocd.totalCentralDirectoryEntries = m_numFiles; + eocd.centralDirectoryOffset = m_headers.size(); + + Cat(buffer, &eocd); + + return buffer; + } + + void ZipWriter::AddFile(const std::filesystem::path& fileName, const char* inArchiveName) + { + auto data = ReadEntireFile(fileName.string()); + auto desc = OpenVulkano::FileDescription::MakeDescriptionForFile(inArchiveName, data.size()); + AddFile(desc, data.data()); + } + + bool ZipWriter::Write(const std::filesystem::path& archivePath) + { + FILE* file = fopen(archivePath.string().c_str(), "wb"); + if (file) + { + auto mem = GetMemory(); + fwrite(mem.data(), mem.size(), 1, file); + fclose(file); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.hpp b/openVulkanoCpp/IO/Archive/ZipWriter.hpp new file mode 100644 index 0000000..c0f4649 --- /dev/null +++ b/openVulkanoCpp/IO/Archive/ZipWriter.hpp @@ -0,0 +1,29 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include +#include + +#include "IO/FileDescription.hpp" + +namespace OpenVulkano +{ + class ZipWriter + { + std::vector m_headers; + std::vector m_centralDirs; + int m_numFiles = 0; + + public: + void AddFile(const FileDescription& description, const void* buffer); + void AddFile(const std::filesystem::path& fileName, const char* inArchiveName); + + bool Write(const std::filesystem::path& archivePath); + std::vector GetMemory(); + }; +} \ No newline at end of file diff --git a/tests/IO/Archive/ZipWriterTest.cpp b/tests/IO/Archive/ZipWriterTest.cpp new file mode 100644 index 0000000..5117d18 --- /dev/null +++ b/tests/IO/Archive/ZipWriterTest.cpp @@ -0,0 +1,76 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include + +#include "IO/Archive/ZipWriter.hpp" + +using namespace OpenVulkano; + +TEST_CASE("Empty zip file", "[ZipWriter]") +{ + ZipWriter writer; + auto mem = writer.GetMemory(); + + const int expectSize = 22; + std::vector expect = {0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + CHECK(mem.size() == expectSize); + CHECK(mem == expect); +} + +TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") +{ + ZipWriter writer; + + FileDescription desc = FileDescription::MakeDescriptionForFile("AAA.txt", 3); + desc.modTime = {}; + desc.createTime = {}; + char buffer[] = {'A', 'A', 'A'}; + + writer.AddFile(desc, buffer); + + auto mem = writer.GetMemory(); + + const int expectSize = 151; + std::vector expect = {0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00}; + + CHECK(mem.size() == expectSize); + CHECK(mem == expect); +} + + +TEST_CASE("Zip with two files(AAA.txt that has 'AAA', BBB.bin that has 'BBB')", "[ZipWriter]") +{ + ZipWriter writer; + + FileDescription aaa = FileDescription::MakeDescriptionForFile("AAA.txt", 3); + aaa.modTime = {}; + aaa.createTime = {}; + char aaaBuffer[] = {'A', 'A', 'A'}; + + FileDescription bbb = FileDescription::MakeDescriptionForFile("BBB.bin", 3); + bbb.modTime = {}; + bbb.createTime = {}; + char bbbBuffer[] = {'B', 'B', 'B'}; + + writer.AddFile(aaa, aaaBuffer); + writer.AddFile(bbb, bbbBuffer); + + auto mem = writer.GetMemory(); + + const int expectSize = 280; + std::vector expect = {0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0x87, 0x8d, 0xc2, 0xd6, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x2e, 0x62, 0x69, 0x6e, 0x42, 0x42, 0x42, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0x87, 0x8d, 0xc2, 0xd6, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x2e, 0x62, 0x69, 0x6e, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00}; + + CHECK(mem.size() == expectSize); + CHECK(mem == expect); +} + + From 4133b4f0d9581d58d97c464544cffea593b90a8e Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Wed, 20 Nov 2024 19:36:51 +0200 Subject: [PATCH 04/15] Using ZipWriter in exporting USDZ files --- openVulkanoCpp/Scene/MeshWriter.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index 787f110..7eaeabe 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -8,6 +8,7 @@ #include "Scene/Geometry.hpp" #include "Scene/Vertex.hpp" #include "IO/Archive/ArchiveWriter.hpp" +#include "IO/Archive/ZipWriter.hpp" #include #include #include @@ -401,9 +402,7 @@ namespace OpenVulkano::Scene void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& usdzPath, const std::string& texturePath) { - OpenVulkano::ArchiveConfiguration config; - config.type = OpenVulkano::ArchiveType::ZIP; - OpenVulkano::ArchiveWriter zipWriter(usdzPath.c_str(), config); + OpenVulkano::ZipWriter zipWriter; std::string usd = GetUsdContents(geometry, texturePath); auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usd.size()); @@ -415,5 +414,10 @@ namespace OpenVulkano::Scene auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFileSize); zipWriter.AddFile(texDesc, ReadEntireFile(texturePath).c_str()); } + + if(!zipWriter.Write(usdzPath)) + { + throw std::runtime_error("Unable to write to USDZ file " + usdzPath); + } } } \ No newline at end of file From 6aa8a265c1da795f57899bcd55ecb53e23810d53 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Wed, 20 Nov 2024 21:22:07 +0200 Subject: [PATCH 05/15] Using gmtime instead of localtime --- openVulkanoCpp/IO/Archive/ZipWriter.cpp | 2 +- tests/IO/Archive/ZipWriterTest.cpp | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.cpp b/openVulkanoCpp/IO/Archive/ZipWriter.cpp index 120e024..037c6b1 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.cpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.cpp @@ -136,7 +136,7 @@ namespace std::pair ConvertToDosTimeDate(time_t time) { - std::tm* tm = std::localtime(&time); + std::tm* tm = std::gmtime(&time); if (tm) { uint16_t dosTime = 0; diff --git a/tests/IO/Archive/ZipWriterTest.cpp b/tests/IO/Archive/ZipWriterTest.cpp index 5117d18..9e1a115 100644 --- a/tests/IO/Archive/ZipWriterTest.cpp +++ b/tests/IO/Archive/ZipWriterTest.cpp @@ -40,7 +40,15 @@ TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") auto mem = writer.GetMemory(); const int expectSize = 151; - std::vector expect = {0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector expect = + { + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, + 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, + 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 + }; CHECK(mem.size() == expectSize); CHECK(mem == expect); @@ -67,7 +75,19 @@ TEST_CASE("Zip with two files(AAA.txt that has 'AAA', BBB.bin that has 'BBB')", auto mem = writer.GetMemory(); const int expectSize = 280; - std::vector expect = {0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0x87, 0x8d, 0xc2, 0xd6, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x2e, 0x62, 0x69, 0x6e, 0x42, 0x42, 0x42, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xec, 0x87, 0x8d, 0xc2, 0xd6, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x2e, 0x62, 0x69, 0x6e, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector expect = + { + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0x87, 0x8d, + 0xc2, 0xd6, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x2e, 0x62, 0x69, 0x6e, 0x42, 0x42, 0x42, 0x50, 0x4b, 0x01, 0x02, + 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, + 0x01, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0x87, 0x8d, 0xc2, 0xd6, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x2e, 0x62, 0x69, 0x6e, 0x0a, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x00, 0x80, + 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00 + }; CHECK(mem.size() == expectSize); CHECK(mem == expect); From 35e80a99e6d21626345d2b94ee854eb67e0d261e Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Fri, 22 Nov 2024 15:41:49 +0200 Subject: [PATCH 06/15] CRC32 changes --- openVulkanoCpp/Math/CRC32.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openVulkanoCpp/Math/CRC32.hpp b/openVulkanoCpp/Math/CRC32.hpp index 7c95649..82bc954 100644 --- a/openVulkanoCpp/Math/CRC32.hpp +++ b/openVulkanoCpp/Math/CRC32.hpp @@ -7,17 +7,18 @@ #pragma once #include +#include class CRC32 { - uint32_t m_table[256]; + std::array m_table; uint32_t m_currentValue = 0; public: CRC32() { uint32_t polynomial = 0xEDB88320; - for (uint32_t i = 0; i < 256; i++) + for (uint32_t i = 0; i < m_table.size(); i++) { uint32_t c = i; for (size_t j = 0; j < 8; j++) From ac843c0fe3d7fae507ae152babebdb6c20dde1d7 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Fri, 22 Nov 2024 15:45:57 +0200 Subject: [PATCH 07/15] Summary: - Removed ReadEntireFile - ZipWriter now writes directly to a file when possible - Added GetIndex to geometry - Moved Usd and Obj generators to different files - Removed unused procedures - Deduplicated obj generators - Updated tests for ZipWriter --- openVulkanoCpp/IO/Archive/ZipWriter.cpp | 76 ++--- openVulkanoCpp/IO/Archive/ZipWriter.hpp | 8 +- openVulkanoCpp/Scene/Geometry.cpp | 25 ++ openVulkanoCpp/Scene/Geometry.hpp | 1 + openVulkanoCpp/Scene/MeshWriter.cpp | 371 ++---------------------- openVulkanoCpp/Scene/ObjEncoder.hpp | 69 +++++ openVulkanoCpp/Scene/UsdEncoder.hpp | 158 ++++++++++ tests/IO/Archive/ZipWriterTest.cpp | 39 ++- 8 files changed, 330 insertions(+), 417 deletions(-) create mode 100644 openVulkanoCpp/Scene/ObjEncoder.hpp create mode 100644 openVulkanoCpp/Scene/UsdEncoder.hpp diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.cpp b/openVulkanoCpp/IO/Archive/ZipWriter.cpp index 037c6b1..a9c1375 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.cpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.cpp @@ -11,6 +11,7 @@ */ #include "ZipWriter.hpp" +#include "Base/Utils.hpp" #include "Math/CRC32.hpp" #include @@ -91,22 +92,6 @@ namespace static_assert(sizeof(EndOfCentralDirectoryHeader) == 22, "Well packed struct"); static_assert(sizeof(NtfsExtraField) == 36, "Well packed struct"); - std::vector ReadEntireFile(const std::string& fname) - { - FILE *file = fopen(fname.c_str(), "rb"); - std::vector buffer; - if(file) - { - fseek(file, 0, SEEK_END); - size_t size = ftell(file); - fseek(file, 0, SEEK_SET); - buffer.resize(size); - fread(buffer.data(), size, 1, file); - fclose(file); - } - return buffer; - } - template uint32_t Cat(std::vector& dest, T* thing) { @@ -157,6 +142,15 @@ namespace namespace OpenVulkano { + ZipWriter::ZipWriter(const std::filesystem::path& filePath) + { + m_file = fopen(filePath.string().c_str(), "wb"); + if (!m_file) + { + throw std::runtime_error("Unable to open file for writing: " + filePath.string()); + } + } + void ZipWriter::AddFile(const FileDescription& description, const void* buffer) { size_t fileSize = description.size; @@ -177,9 +171,10 @@ namespace OpenVulkano lfh.compressedSize = lfh.uncompressedSize = fileSize; lfh.fileNameLength = fileNameLength; - size_t headerOffset = Cat(m_headers, &lfh); - Cat(m_headers, fileNameLength, fileName); - Cat(m_headers, description.size, (uint8_t *)buffer); + size_t headerOffset = ftell(m_file); + fwrite(&lfh, sizeof(lfh), 1, m_file); + fwrite(fileName, fileNameLength, 1, m_file); + fwrite(buffer, fileSize, 1, m_file); CentalDirectoryFileHeader cdfh; cdfh.fileLastModTime = dosTime; @@ -203,41 +198,28 @@ namespace OpenVulkano m_numFiles += 1; } - std::vector ZipWriter::GetMemory() + void ZipWriter::AddFile(const std::filesystem::path& fileName, const char* inArchiveName) { - std::vector buffer = m_headers; - + auto data = Utils::ReadFile(fileName.string()); + auto desc = OpenVulkano::FileDescription::MakeDescriptionForFile(inArchiveName, data.Size()); + AddFile(desc, data.Data()); + } + + void ZipWriter::Close() + { + int centralDirsOffset = 0; if (m_numFiles) { - int centralDirsOffset = Cat(buffer, m_centralDirs); + centralDirsOffset = ftell(m_file); + fwrite(m_centralDirs.data(), m_centralDirs.size(), 1, m_file); + m_centralDirs.clear(); } EndOfCentralDirectoryHeader eocd; eocd.centralDirectoryEntries = eocd.totalCentralDirectoryEntries = m_numFiles; - eocd.centralDirectoryOffset = m_headers.size(); + eocd.centralDirectoryOffset = centralDirsOffset; - Cat(buffer, &eocd); - - return buffer; - } - - void ZipWriter::AddFile(const std::filesystem::path& fileName, const char* inArchiveName) - { - auto data = ReadEntireFile(fileName.string()); - auto desc = OpenVulkano::FileDescription::MakeDescriptionForFile(inArchiveName, data.size()); - AddFile(desc, data.data()); - } - - bool ZipWriter::Write(const std::filesystem::path& archivePath) - { - FILE* file = fopen(archivePath.string().c_str(), "wb"); - if (file) - { - auto mem = GetMemory(); - fwrite(mem.data(), mem.size(), 1, file); - fclose(file); - return true; - } - return false; + fwrite(&eocd, sizeof(eocd), 1, m_file); + fclose(m_file); } } \ No newline at end of file diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.hpp b/openVulkanoCpp/IO/Archive/ZipWriter.hpp index c0f4649..722a9de 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.hpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.hpp @@ -15,15 +15,15 @@ namespace OpenVulkano { class ZipWriter { - std::vector m_headers; std::vector m_centralDirs; int m_numFiles = 0; + FILE *m_file; public: + ZipWriter(const std::filesystem::path& filePath); + void AddFile(const FileDescription& description, const void* buffer); void AddFile(const std::filesystem::path& fileName, const char* inArchiveName); - - bool Write(const std::filesystem::path& archivePath); - std::vector GetMemory(); + void Close(); }; } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/Geometry.cpp b/openVulkanoCpp/Scene/Geometry.cpp index a05f0b2..7a780f5 100644 --- a/openVulkanoCpp/Scene/Geometry.cpp +++ b/openVulkanoCpp/Scene/Geometry.cpp @@ -153,4 +153,29 @@ namespace OpenVulkano::Scene vertices = nullptr; indices = nullptr; } + + uint32_t Geometry::GetIndex(int index) const + { + uint32_t result = 0; + + if (index >= indexCount) + throw std::out_of_range("Index is out of range"); + + if (indexType == OpenVulkano::Scene::VertexIndexType::UINT16) + { + uint16_t* indices = GetIndices16(); + result = indices[index]; + } + else if (indexType == OpenVulkano::Scene::VertexIndexType::UINT32) + { + uint32_t* indices = GetIndices32(); + result = indices[index]; + } + else + { + throw std::runtime_error("Invalid geometry index type"); + } + + return result; + } } diff --git a/openVulkanoCpp/Scene/Geometry.hpp b/openVulkanoCpp/Scene/Geometry.hpp index c5c1159..8653a52 100644 --- a/openVulkanoCpp/Scene/Geometry.hpp +++ b/openVulkanoCpp/Scene/Geometry.hpp @@ -57,6 +57,7 @@ namespace OpenVulkano Vertex* GetVertices() const { return vertices; } void* GetIndices() const { return indices; } + uint32_t GetIndex(int index) const; uint16_t* GetIndices16() const { return static_cast(indices); } uint32_t* GetIndices32() const { return static_cast(indices); } uint32_t GetIndexCount() const { return indexCount; } diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index 7eaeabe..afa03c0 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -7,6 +7,8 @@ #include "MeshWriter.hpp" #include "Scene/Geometry.hpp" #include "Scene/Vertex.hpp" +#include "Scene/UsdEncoder.hpp" +#include "Scene/ObjEncoder.hpp" #include "IO/Archive/ArchiveWriter.hpp" #include "IO/Archive/ZipWriter.hpp" #include @@ -22,268 +24,6 @@ #include #include -namespace -{ - uint32_t GetIndexFromGeometry(OpenVulkano::Scene::Geometry* geometry, int index) - { - uint32_t result = 0; - - if (geometry->indexType == OpenVulkano::Scene::VertexIndexType::UINT16) - { - uint16_t *indices = static_cast(geometry->indices); - result = indices[index]; - } - else if (geometry->indexType == OpenVulkano::Scene::VertexIndexType::UINT32) - { - uint32_t *indices = static_cast(geometry->indices); - result = indices[index]; - } - else - { - throw std::runtime_error("Invalid geometry index type"); - } - - return result; - } - - std::string ReadEntireFile(const std::string& fname) - { - FILE *file = fopen(fname.c_str(), "rb"); - std::string buffer; - if(file) - { - fseek(file, 0, SEEK_END); - size_t size = ftell(file); - fseek(file, 0, SEEK_SET); - buffer = std::string(size, ' '); - fread(&buffer[0], size, 1, file); - fclose(file); - } - return buffer; - } - - void WriteGeometryWithoutTexturesUsingTinyusdz(OpenVulkano::Scene::Geometry* geometry, const std::string& filePath) - { - tinyusdz::Stage stage; - tinyusdz::Xform xform; - tinyusdz::GeomMesh mesh; - mesh.name = "TheMesh"; - - std::vector pts(geometry->vertexCount); - std::vector indices(geometry->indexCount); - tinyusdz::Attribute uvAttr; - std::vector uvs(geometry->vertexCount); - - for (uint32_t i = 0; i < geometry->vertexCount; ++i) - { - const OpenVulkano::Vertex& v = geometry->vertices[i]; - pts[i].x = v.position.x; - pts[i].y = v.position.y; - pts[i].z = v.position.z; - uvs[i] = { v.textureCoordinates.x, v.textureCoordinates.y }; - } - - mesh.points.set_value(pts); - uvAttr.set_value(uvs); - - std::vector counts(geometry->indexCount / 3, 3); // NOTE(vb): The value 3 is kind of arbitrary, but this array must be in the mesh! - mesh.faceVertexCounts.set_value(counts); - - for (uint32_t i = 0; i < geometry->indexCount; ++i) - { - uint32_t index = GetIndexFromGeometry(geometry, i); - indices[i] = index; - } - mesh.faceVertexIndices.set_value(indices); - - uvAttr.metas().interpolation = tinyusdz::Interpolation::FaceVarying; - tinyusdz::Property uvProp(uvAttr); - mesh.props.emplace("primvars:UVMap", uvProp); - - tinyusdz::Prim xformPrim(xform); - tinyusdz::Prim meshPrim(mesh); - std::string err; - if (!xformPrim.add_child(std::move(meshPrim), true, &err)) - { - throw std::runtime_error("Failed to construct scene: " + err); - } - - if (!stage.add_root_prim(std::move(xformPrim))) - { - throw std::runtime_error("Failed to add prim to stage root: " + stage.get_error()); - } - - stage.metas().defaultPrim = tinyusdz::value::token(xformPrim.element_name()); - stage.metas().comment = "Generated by OpenVulkanoCpp"; - if (!stage.commit()) - { - throw std::runtime_error("Failed to commit stage: " + stage.get_error()); - } - - std::ofstream file(filePath); - if (!file.is_open()) - throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); - std::string scene = to_string(stage); - file << scene << "\n"; - file.close(); - } - - std::vector ConvertToUSDC(const std::string& contents) - { - std::vector result; - tinyusdz::Stage stage; - std::string warn, err; - assert(tinyusdz::LoadUSDAFromMemory((const uint8_t *)contents.data(), (const size_t)contents.size(), "", &stage, &warn, &err)); - bool ret = tinyusdz::usdc::SaveAsUSDCToMemory(stage, &result, &warn, &err); // As for now, this reports that it's not implemented... - return result; - } - - std::string GetUsdContents(OpenVulkano::Scene::Geometry* geometry, const std::string texturePath = "") - { - std::ostringstream file; - std::ostringstream points, normals, indices, texCoords, faceCounts; - points << std::fixed << std::setprecision(6); - normals << std::fixed << std::setprecision(6); - - for (size_t i = 0; i < geometry->vertexCount; ++i) - { - const auto& v = geometry->vertices[i]; - points << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")"; - normals << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")"; - if (i < geometry->vertexCount - 1) - { - points << ", "; - normals << ", "; - } - } - - for (size_t i = 0; i < geometry->indexCount; ++i) - { - indices << GetIndexFromGeometry(geometry, i); - if (i < geometry->indexCount - 1) - { - indices << ", "; - } - if ((i + 1) % 3 == 0) - { - faceCounts << "3"; - if (i < geometry->indexCount - 1) - { - faceCounts << ", "; - } - } - } - - texCoords << std::fixed << std::setprecision(6); - - for (size_t i = 0; i < geometry->indexCount; ++i) - { - const size_t vertexIndex = GetIndexFromGeometry(geometry, i); - const auto& v = geometry->vertices[vertexIndex]; - texCoords << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")"; - if (i < geometry->indexCount - 1) - { - texCoords << ", "; - } - } - - file << R"(#usda 1.0 -( - defaultPrim = "root" - doc = "Exported from OpenVulkano" - metersPerUnit = 1 - upAxis = "Z" -) - -def Xform "root" ( - customData = { - dictionary Blender = { - bool generated = 1 - } - } -) -{ - def Xform "model" - { - custom string userProperties:blender:object_name = "model" - float3 xformOp:rotateXYZ = (89.99999, -0, 0) - float3 xformOp:scale = (1, 1, 1) - double3 xformOp:translate = (0, 0, 0) - uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"] - - def Mesh "model" ( - active = true - prepend apiSchemas = ["MaterialBindingAPI"] - ) - { - uniform bool doubleSided = 1 - float3[] extent = [(-0.5, -0.5, 0), (0.5, 0.5, 0)] - int[] faceVertexCounts = [)" - << faceCounts.str() << R"(] - int[] faceVertexIndices = [)" - << indices.str() << R"(] - rel material:binding = - normal3f[] normals = [)" - << normals.str() << R"(] ( - interpolation = "faceVarying" - ) - point3f[] points = [)" - << points.str() << R"(] - texCoord2f[] primvars:st = [)" - << texCoords.str() << R"(] ( - interpolation = "faceVarying" - ) - uniform token subdivisionScheme = "none" - custom string userProperties:blender:data_name = "model" - } - } - - def Scope "_materials" - { - def Material "Material0" - { - token outputs:surface.connect = - custom string userProperties:blender:data_name = "Material0" - - def Shader "Principled_BSDF" - { - uniform token info:id = "UsdPreviewSurface" - float inputs:clearcoat = 0 - float inputs:clearcoatRoughness = 0.03 - color3f inputs:diffuseColor.connect = - float inputs:ior = 1.5 - float inputs:metallic = 0 - float inputs:opacity = 1 - float inputs:roughness = 1 - float inputs:specular = 0 - token outputs:surface - } - - def Shader "Image_Texture" - { - uniform token info:id = "UsdUVTexture" - asset inputs:file = @./texture.png@ - token inputs:sourceColorSpace = "sRGB" - float2 inputs:st.connect = - token inputs:wrapS = "repeat" - token inputs:wrapT = "repeat" - float3 outputs:rgb - } - - def Shader "uvmap" - { - uniform token info:id = "UsdPrimvarReader_float2" - string inputs:varname = "st" - float2 outputs:result - } - } - } -} -)"; - return file.str(); - } -} - namespace OpenVulkano::Scene { void MeshWriter::WriteAsOBJ(Geometry* geometry, const std::string& filePath) @@ -293,43 +33,8 @@ namespace OpenVulkano::Scene if (!file.is_open()) throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); - // Vertices - for (int i = 0; i < geometry->vertexCount; ++i) - { - const OpenVulkano::Vertex& v = geometry->vertices[i]; - std::string line; - line = fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z); - file << line; - } - - // Normals - for (int i = 0; i < geometry->vertexCount; ++i) - { - const OpenVulkano::Vertex& v = geometry->vertices[i]; - std::string line; - line = fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); - file << line; - } - - // TexCoords - for (int i = 0; i < geometry->vertexCount; ++i) - { - const OpenVulkano::Vertex& v = geometry->vertices[i]; - std::string line; - line = fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y); - file << line; - } - - // Indices - for (int i = 0; i < geometry->indexCount; i += 3) - { - uint32_t i0 = GetIndexFromGeometry(geometry, i + 0) + 1; - uint32_t i1 = GetIndexFromGeometry(geometry, i + 1) + 1; - uint32_t i2 = GetIndexFromGeometry(geometry, i + 2) + 1; - std::string line = fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); - file << line; - } - + auto [objContents, mtlContents] = GetObjContents(geometry, ""); + file << objContents; file.close(); } @@ -345,64 +50,27 @@ namespace OpenVulkano::Scene void MeshWriter::WriteObjAsZip(Geometry* geometry, const std::string& zipPath, const std::string& texturePath) { - std::stringstream mtlContent; - std::string materialName = "Material0"; - mtlContent << "newmtl " << materialName << "\n"; - mtlContent << "Ka 1.000 1.000 1.000\n"; // Ambient - mtlContent << "Kd 1.000 1.000 1.000\n"; // Diffuse - mtlContent << "Ks 0.000 0.000 0.000\n"; // Specular - if (!texturePath.empty()) - { - mtlContent << "map_Ka texture.png\n"; // Ambient map - mtlContent << "map_Kd texture.png\n"; // Texture map - } - - std::stringstream objContent; - objContent << "# OBJ file generated by OpenVulkanoCpp\n"; - objContent << "mtllib material.mtl\n"; - objContent << "usemtl " << materialName << "\n"; - for (int i = 0; i < geometry->vertexCount; ++i) - { - const auto& v = geometry->vertices[i]; - objContent << fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z); - } - for (int i = 0; i < geometry->vertexCount; ++i) - { - const auto& v = geometry->vertices[i]; - objContent << fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); - } - for (int i = 0; i < geometry->vertexCount; ++i) - { - const auto& v = geometry->vertices[i]; - objContent << fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y); - } - for (int i = 0; i < geometry->indexCount; i += 3) - { - uint32_t i0 = GetIndexFromGeometry(geometry, i) + 1; - uint32_t i1 = GetIndexFromGeometry(geometry, i + 1) + 1; - uint32_t i2 = GetIndexFromGeometry(geometry, i + 2) + 1; - objContent << fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); - } - OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str()); - auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContent.str().size()); - zipWriter.AddFile(objDesc, objContent.str().data()); + auto [objContents, mtlContents] = GetObjContents(geometry, texturePath); - auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContent.str().size()); - zipWriter.AddFile(mtlDesc, mtlContent.str().data()); + auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContents.size()); + zipWriter.AddFile(objDesc, objContents.data()); + + auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContents.size()); + zipWriter.AddFile(mtlDesc, mtlContents.data()); if (!texturePath.empty() && std::filesystem::exists(texturePath)) { - auto textureFileSize = std::filesystem::file_size(texturePath); - auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFileSize); - zipWriter.AddFile(texDesc, ReadEntireFile(texturePath).c_str()); + auto textureFile = Utils::ReadFile(texturePath); + auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); + zipWriter.AddFile(texDesc, textureFile.Data()); } } void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& usdzPath, const std::string& texturePath) { - OpenVulkano::ZipWriter zipWriter; + OpenVulkano::ZipWriter zipWriter(usdzPath); std::string usd = GetUsdContents(geometry, texturePath); auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usd.size()); @@ -410,14 +78,11 @@ namespace OpenVulkano::Scene if (!texturePath.empty() && std::filesystem::exists(texturePath)) { - auto textureFileSize = std::filesystem::file_size(texturePath); - auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFileSize); - zipWriter.AddFile(texDesc, ReadEntireFile(texturePath).c_str()); + auto textureFile = Utils::ReadFile(texturePath); + auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); + zipWriter.AddFile(texDesc, textureFile.Data()); } - if(!zipWriter.Write(usdzPath)) - { - throw std::runtime_error("Unable to write to USDZ file " + usdzPath); - } + zipWriter.Close(); } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/ObjEncoder.hpp b/openVulkanoCpp/Scene/ObjEncoder.hpp new file mode 100644 index 0000000..0b7b660 --- /dev/null +++ b/openVulkanoCpp/Scene/ObjEncoder.hpp @@ -0,0 +1,69 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include +#include +#include +#include "Scene/Geometry.hpp" + +namespace OpenVulkano::Scene +{ + // Returns [objContents, mtlContents] + std::pair GetObjContents(Geometry* geometry, const std::string& texturePath) + { + std::pair result; + + bool useTexture = texturePath.size() != 0; + + std::stringstream objContent; + objContent << "# OBJ file generated by OpenVulkanoCpp\n"; + + if (useTexture) + { + std::stringstream mtlContent; + std::string materialName = "Material0"; + mtlContent << "newmtl " << materialName << R"( +Ka 1.000 1.000 1.000 +Kd 1.000 1.000 1.000 +Ks 0.000 0.000 0.000 +map_Ka texture.png +map_Kd texture.png +)"; + objContent << "mtllib material.mtl\nusemtl " << materialName << "\n"; + + result.second = mtlContent.str(); + } + + for (int i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + objContent << fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z); + } + for (int i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + objContent << fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); + } + for (int i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + objContent << fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y); + } + for (int i = 0; i < geometry->indexCount; i += 3) + { + uint32_t i0 = geometry->GetIndex(i + 0) + 1; + uint32_t i1 = geometry->GetIndex(i + 1) + 1; + uint32_t i2 = geometry->GetIndex(i + 2) + 1; + objContent << fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); + } + + result.first = objContent.str(); + + return result; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UsdEncoder.hpp b/openVulkanoCpp/Scene/UsdEncoder.hpp new file mode 100644 index 0000000..6f7effc --- /dev/null +++ b/openVulkanoCpp/Scene/UsdEncoder.hpp @@ -0,0 +1,158 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include + +#include "Scene/Geometry.hpp" + +namespace OpenVulkano::Scene +{ + std::string GetUsdContents(OpenVulkano::Scene::Geometry* geometry, const std::string texturePath = "") + { + std::ostringstream result; + std::ostringstream points, normals, indices, texCoords, faceCounts; + points << std::fixed << std::setprecision(6); + normals << std::fixed << std::setprecision(6); + + for (size_t i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + points << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")"; + normals << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")"; + if (i < geometry->vertexCount - 1) + { + points << ", "; + normals << ", "; + } + } + + for (size_t i = 0; i < geometry->indexCount; ++i) + { + indices << geometry->GetIndex(i); + if (i < geometry->indexCount - 1) + { + indices << ", "; + } + if ((i + 1) % 3 == 0) + { + faceCounts << "3"; + if (i < geometry->indexCount - 1) + { + faceCounts << ", "; + } + } + } + + texCoords << std::fixed << std::setprecision(6); + + for (size_t i = 0; i < geometry->indexCount; ++i) + { + const size_t vertexIndex = geometry->GetIndex(i); + const auto& v = geometry->vertices[vertexIndex]; + texCoords << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")"; + if (i < geometry->indexCount - 1) + { + texCoords << ", "; + } + } + + result << R"(#usda 1.0 +( + defaultPrim = "root" + doc = "Exported from OpenVulkano" + metersPerUnit = 1 + upAxis = "Y" +) + +def Xform "root" ( + customData = { + dictionary Blender = { + bool generated = 1 + } + } +) +{ + def Xform "model" + { + custom string userProperties:blender:object_name = "model" + float3 xformOp:rotateXYZ = (90, -0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"] + + def Mesh "model" ( + active = true + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, -0.5, 0), (0.5, 0.5, 0)] + int[] faceVertexCounts = [)" + << faceCounts.str() << R"(] + int[] faceVertexIndices = [)" + << indices.str() << R"(] + rel material:binding = + normal3f[] normals = [)" + << normals.str() << R"(] ( + interpolation = "faceVarying" + ) + point3f[] points = [)" + << points.str() << R"(] + texCoord2f[] primvars:st = [)" + << texCoords.str() << R"(] ( + interpolation = "faceVarying" + ) + uniform token subdivisionScheme = "none" + custom string userProperties:blender:data_name = "model" + } + } + + def Scope "_materials" + { + def Material "Material0" + { + token outputs:surface.connect = + custom string userProperties:blender:data_name = "Material0" + + def Shader "Principled_BSDF" + { + uniform token info:id = "UsdPreviewSurface" + float inputs:clearcoat = 0 + float inputs:clearcoatRoughness = 0.03 + color3f inputs:diffuseColor.connect = + float inputs:ior = 1.5 + float inputs:metallic = 0 + float inputs:opacity = 1 + float inputs:roughness = 1 + float inputs:specular = 0 + token outputs:surface + } + + def Shader "Image_Texture" + { + uniform token info:id = "UsdUVTexture" + asset inputs:file = @./texture.png@ + token inputs:sourceColorSpace = "sRGB" + float2 inputs:st.connect = + token inputs:wrapS = "repeat" + token inputs:wrapT = "repeat" + float3 outputs:rgb + } + + def Shader "uvmap" + { + uniform token info:id = "UsdPrimvarReader_float2" + string inputs:varname = "st" + float2 outputs:result + } + } + } +} +)"; + return result.str(); + } +} \ No newline at end of file diff --git a/tests/IO/Archive/ZipWriterTest.cpp b/tests/IO/Archive/ZipWriterTest.cpp index 9e1a115..9fa99e8 100644 --- a/tests/IO/Archive/ZipWriterTest.cpp +++ b/tests/IO/Archive/ZipWriterTest.cpp @@ -10,25 +10,31 @@ #include #include +#include "Base/Utils.hpp" #include "IO/Archive/ZipWriter.hpp" +#include "IO/AppFolders.hpp" using namespace OpenVulkano; TEST_CASE("Empty zip file", "[ZipWriter]") { - ZipWriter writer; - auto mem = writer.GetMemory(); - + const auto emptyZipPath = AppFolders::GetAppTempDir() / "empty.zip"; + ZipWriter writer(emptyZipPath); + writer.Close(); + auto mem = Utils::ReadFile(emptyZipPath); const int expectSize = 22; - std::vector expect = {0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + Array expect = {0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - CHECK(mem.size() == expectSize); + CHECK(mem.Size() == expectSize); CHECK(mem == expect); + + std::filesystem::remove(emptyZipPath); } TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") { - ZipWriter writer; + const auto oneFileZipPath = AppFolders::GetAppTempDir() / "one_file.zip"; + ZipWriter writer(oneFileZipPath); FileDescription desc = FileDescription::MakeDescriptionForFile("AAA.txt", 3); desc.modTime = {}; @@ -36,11 +42,12 @@ TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") char buffer[] = {'A', 'A', 'A'}; writer.AddFile(desc, buffer); + writer.Close(); - auto mem = writer.GetMemory(); + auto mem = Utils::ReadFile(oneFileZipPath); const int expectSize = 151; - std::vector expect = + Array expect = { 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x01, 0x02, 0x1f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, @@ -50,14 +57,17 @@ TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 }; - CHECK(mem.size() == expectSize); + CHECK(mem.Size() == expectSize); CHECK(mem == expect); + + std::filesystem::remove(oneFileZipPath); } TEST_CASE("Zip with two files(AAA.txt that has 'AAA', BBB.bin that has 'BBB')", "[ZipWriter]") { - ZipWriter writer; + const auto twoFilesZipPath = AppFolders::GetAppTempDir() / "two_files.zip"; + ZipWriter writer(twoFilesZipPath); FileDescription aaa = FileDescription::MakeDescriptionForFile("AAA.txt", 3); aaa.modTime = {}; @@ -71,11 +81,12 @@ TEST_CASE("Zip with two files(AAA.txt that has 'AAA', BBB.bin that has 'BBB')", writer.AddFile(aaa, aaaBuffer); writer.AddFile(bbb, bbbBuffer); + writer.Close(); - auto mem = writer.GetMemory(); + auto mem = Utils::ReadFile(twoFilesZipPath); const int expectSize = 280; - std::vector expect = + Array expect = { 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0xa7, 0x31, 0xa0, 0x66, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x2e, 0x74, 0x78, 0x74, 0x41, 0x41, 0x41, 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xec, 0x87, 0x8d, @@ -89,8 +100,10 @@ TEST_CASE("Zip with two files(AAA.txt that has 'AAA', BBB.bin that has 'BBB')", 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00 }; - CHECK(mem.size() == expectSize); + CHECK(mem.Size() == expectSize); CHECK(mem == expect); + + std::filesystem::remove(twoFilesZipPath); } From 2a331011d5ad295288d0d4f44447fa60f593b9b6 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Fri, 22 Nov 2024 16:36:41 +0200 Subject: [PATCH 08/15] Changed order of includes, uint32_t as index for Geometry::GetIndex(), minor changes --- openVulkanoCpp/IO/Archive/ZipWriter.hpp | 6 +++--- openVulkanoCpp/Scene/Geometry.cpp | 8 ++------ openVulkanoCpp/Scene/Geometry.hpp | 2 +- openVulkanoCpp/Scene/MeshWriter.cpp | 15 +++------------ openVulkanoCpp/Scene/MeshWriter.hpp | 4 ++-- openVulkanoCpp/Scene/ObjEncoder.hpp | 3 ++- openVulkanoCpp/Scene/UsdEncoder.hpp | 4 ++-- tests/IO/Archive/ZipWriterTest.cpp | 8 ++++---- 8 files changed, 19 insertions(+), 31 deletions(-) diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.hpp b/openVulkanoCpp/IO/Archive/ZipWriter.hpp index 722a9de..7b25be8 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.hpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.hpp @@ -5,19 +5,19 @@ */ #pragma once +#include "IO/FileDescription.hpp" + #include #include #include -#include "IO/FileDescription.hpp" - namespace OpenVulkano { class ZipWriter { std::vector m_centralDirs; int m_numFiles = 0; - FILE *m_file; + FILE* m_file; public: ZipWriter(const std::filesystem::path& filePath); diff --git a/openVulkanoCpp/Scene/Geometry.cpp b/openVulkanoCpp/Scene/Geometry.cpp index 7a780f5..4fa4c9f 100644 --- a/openVulkanoCpp/Scene/Geometry.cpp +++ b/openVulkanoCpp/Scene/Geometry.cpp @@ -154,11 +154,11 @@ namespace OpenVulkano::Scene indices = nullptr; } - uint32_t Geometry::GetIndex(int index) const + uint32_t Geometry::GetIndex(uint32_t index) const { uint32_t result = 0; - if (index >= indexCount) + if (index >= indexCount) [[unlikely]] throw std::out_of_range("Index is out of range"); if (indexType == OpenVulkano::Scene::VertexIndexType::UINT16) @@ -171,10 +171,6 @@ namespace OpenVulkano::Scene uint32_t* indices = GetIndices32(); result = indices[index]; } - else - { - throw std::runtime_error("Invalid geometry index type"); - } return result; } diff --git a/openVulkanoCpp/Scene/Geometry.hpp b/openVulkanoCpp/Scene/Geometry.hpp index 8653a52..5522ac5 100644 --- a/openVulkanoCpp/Scene/Geometry.hpp +++ b/openVulkanoCpp/Scene/Geometry.hpp @@ -57,7 +57,7 @@ namespace OpenVulkano Vertex* GetVertices() const { return vertices; } void* GetIndices() const { return indices; } - uint32_t GetIndex(int index) const; + uint32_t GetIndex(uint32_t index) const; uint16_t* GetIndices16() const { return static_cast(indices); } uint32_t* GetIndices32() const { return static_cast(indices); } uint32_t GetIndexCount() const { return indexCount; } diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index afa03c0..28a7277 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -11,18 +11,9 @@ #include "Scene/ObjEncoder.hpp" #include "IO/Archive/ArchiveWriter.hpp" #include "IO/Archive/ZipWriter.hpp" + #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace OpenVulkano::Scene { @@ -48,7 +39,7 @@ namespace OpenVulkano::Scene file.close(); } - void MeshWriter::WriteObjAsZip(Geometry* geometry, const std::string& zipPath, const std::string& texturePath) + void MeshWriter::WriteObjAsZip(Geometry* geometry, const std::string& texturePath, const std::string& zipPath) { OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str()); @@ -68,7 +59,7 @@ namespace OpenVulkano::Scene } } - void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& usdzPath, const std::string& texturePath) + void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& texturePath, const std::string& usdzPath) { OpenVulkano::ZipWriter zipWriter(usdzPath); diff --git a/openVulkanoCpp/Scene/MeshWriter.hpp b/openVulkanoCpp/Scene/MeshWriter.hpp index 2c8176b..c484bee 100644 --- a/openVulkanoCpp/Scene/MeshWriter.hpp +++ b/openVulkanoCpp/Scene/MeshWriter.hpp @@ -16,7 +16,7 @@ namespace OpenVulkano::Scene public: static void WriteAsOBJ(Geometry* geometry, const std::string& filePath); static void WriteAsUSD(Geometry* geometry, const std::string& filePath); - static void WriteObjAsZip(Geometry* geometry, const std::string& zipPath, const std::string& texturePath); - static void WriteAsUSDZ(Geometry* geometry, const std::string& usdzPath, const std::string& texturePath); + static void WriteObjAsZip(Geometry* geometry, const std::string& texturePath, const std::string& zipPath); + static void WriteAsUSDZ(Geometry* geometry, const std::string& texturePath, const std::string& usdzPath); }; } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/ObjEncoder.hpp b/openVulkanoCpp/Scene/ObjEncoder.hpp index 0b7b660..8bd6311 100644 --- a/openVulkanoCpp/Scene/ObjEncoder.hpp +++ b/openVulkanoCpp/Scene/ObjEncoder.hpp @@ -5,11 +5,12 @@ */ #pragma once +#include "Scene/Geometry.hpp" + #include #include #include #include -#include "Scene/Geometry.hpp" namespace OpenVulkano::Scene { diff --git a/openVulkanoCpp/Scene/UsdEncoder.hpp b/openVulkanoCpp/Scene/UsdEncoder.hpp index 6f7effc..429ceed 100644 --- a/openVulkanoCpp/Scene/UsdEncoder.hpp +++ b/openVulkanoCpp/Scene/UsdEncoder.hpp @@ -5,10 +5,10 @@ */ #pragma once -#include - #include "Scene/Geometry.hpp" +#include + namespace OpenVulkano::Scene { std::string GetUsdContents(OpenVulkano::Scene::Geometry* geometry, const std::string texturePath = "") diff --git a/tests/IO/Archive/ZipWriterTest.cpp b/tests/IO/Archive/ZipWriterTest.cpp index 9fa99e8..fffa270 100644 --- a/tests/IO/Archive/ZipWriterTest.cpp +++ b/tests/IO/Archive/ZipWriterTest.cpp @@ -6,14 +6,14 @@ #include -#include -#include -#include - #include "Base/Utils.hpp" #include "IO/Archive/ZipWriter.hpp" #include "IO/AppFolders.hpp" +#include +#include +#include + using namespace OpenVulkano; TEST_CASE("Empty zip file", "[ZipWriter]") From 7ede43d1c26c129e06861503f53daa630b3df288 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Mon, 25 Nov 2024 19:32:53 +0200 Subject: [PATCH 09/15] NtfsExtraField constructor & helper static method, Cat refactoring, writing zip archive in destructor, using MemMappedFile --- openVulkanoCpp/IO/Archive/ZipWriter.cpp | 73 +++++++++++++------------ openVulkanoCpp/IO/Archive/ZipWriter.hpp | 2 +- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.cpp b/openVulkanoCpp/IO/Archive/ZipWriter.cpp index a9c1375..77994c4 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.cpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.cpp @@ -12,6 +12,7 @@ #include "ZipWriter.hpp" #include "Base/Utils.hpp" +#include "IO/MemMappedFile.hpp" #include "Math/CRC32.hpp" #include @@ -71,6 +72,19 @@ namespace uint64_t modTime; // 64-bit NTFS file last modification time uint64_t acTime; // 64-bit NTFS file last access time uint64_t crTime; // 64-bit NTFS file creation time + + static uint64_t TimetToWinFileTime(time_t t) + { + // See references for details + return (t * 10000000LL) + 116444736000000000LL; + } + + NtfsExtraField(time_t modTime, time_t acTime, time_t crTime) + { + this->modTime = TimetToWinFileTime(modTime); + this->acTime = TimetToWinFileTime(acTime); + this->crTime = TimetToWinFileTime(crTime); + } }; struct EndOfCentralDirectoryHeader @@ -92,14 +106,6 @@ namespace static_assert(sizeof(EndOfCentralDirectoryHeader) == 22, "Well packed struct"); static_assert(sizeof(NtfsExtraField) == 36, "Well packed struct"); - template - uint32_t Cat(std::vector& dest, T* thing) - { - uint32_t startOffset = dest.size(); - dest.insert(dest.end(), (uint8_t *)thing, (uint8_t *)(thing + 1)); - return startOffset; - } - uint32_t Cat(std::vector& dest, size_t size, uint8_t* thing) { uint32_t startOffset = dest.size(); @@ -107,16 +113,15 @@ namespace return startOffset; } - uint32_t Cat(std::vector& dest, const std::vector& thing) + template + uint32_t Cat(std::vector& dest, T* thing) { - uint32_t startOffset = Cat(dest, thing.size(), (uint8_t*)thing.data()); - return startOffset; + return Cat(dest, sizeof(T), reinterpret_cast(thing)); } - void TimetToFileTime(time_t t, uint64_t* lpWinFileTime) + uint32_t Cat(std::vector& dest, const std::vector& thing) { - // See references for details - *lpWinFileTime = (t * 10000000LL) + 116444736000000000LL; + return Cat(dest, thing.size(), const_cast(thing.data())); } std::pair ConvertToDosTimeDate(time_t time) @@ -186,10 +191,7 @@ namespace OpenVulkano cdfh.externalFileAttribs = 32; // NOTE(vb): I've no idea wtf is this value mean cdfh.relativeOffsetOfLocalFileHeader = headerOffset; - NtfsExtraField ntfs; - TimetToFileTime(modTime, &ntfs.modTime); - TimetToFileTime(accessTime, &ntfs.acTime); - TimetToFileTime(createTime, &ntfs.crTime); + NtfsExtraField ntfs(modTime, accessTime, createTime); Cat(m_centralDirs, &cdfh); Cat(m_centralDirs, fileNameLength, fileName); @@ -200,26 +202,29 @@ namespace OpenVulkano void ZipWriter::AddFile(const std::filesystem::path& fileName, const char* inArchiveName) { - auto data = Utils::ReadFile(fileName.string()); - auto desc = OpenVulkano::FileDescription::MakeDescriptionForFile(inArchiveName, data.Size()); - AddFile(desc, data.Data()); + MemMappedFile file = MemMappedFile(fileName.string()); + auto desc = OpenVulkano::FileDescription::MakeDescriptionForFile(inArchiveName, file.Size()); + AddFile(desc, file.Data()); } - void ZipWriter::Close() + ZipWriter::~ZipWriter() { - int centralDirsOffset = 0; - if (m_numFiles) + if (m_file) { - centralDirsOffset = ftell(m_file); - fwrite(m_centralDirs.data(), m_centralDirs.size(), 1, m_file); - m_centralDirs.clear(); + int centralDirsOffset = 0; + if (m_numFiles) + { + centralDirsOffset = ftell(m_file); + fwrite(m_centralDirs.data(), m_centralDirs.size(), 1, m_file); + m_centralDirs.clear(); + } + + EndOfCentralDirectoryHeader eocd; + eocd.centralDirectoryEntries = eocd.totalCentralDirectoryEntries = m_numFiles; + eocd.centralDirectoryOffset = centralDirsOffset; + + fwrite(&eocd, sizeof(eocd), 1, m_file); + fclose(m_file); } - - EndOfCentralDirectoryHeader eocd; - eocd.centralDirectoryEntries = eocd.totalCentralDirectoryEntries = m_numFiles; - eocd.centralDirectoryOffset = centralDirsOffset; - - fwrite(&eocd, sizeof(eocd), 1, m_file); - fclose(m_file); } } \ No newline at end of file diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.hpp b/openVulkanoCpp/IO/Archive/ZipWriter.hpp index 7b25be8..0ae044c 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.hpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.hpp @@ -21,9 +21,9 @@ namespace OpenVulkano public: ZipWriter(const std::filesystem::path& filePath); + ~ZipWriter(); void AddFile(const FileDescription& description, const void* buffer); void AddFile(const std::filesystem::path& fileName, const char* inArchiveName); - void Close(); }; } \ No newline at end of file From ba8574f5370dbb77de80a90e680a581c3dcfc3e0 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Mon, 25 Nov 2024 19:34:05 +0200 Subject: [PATCH 10/15] Updated tests, changed model encoders to output content to a stream --- openVulkanoCpp/Scene/MeshWriter.cpp | 27 +++--- openVulkanoCpp/Scene/ObjEncoder.hpp | 18 +--- openVulkanoCpp/Scene/UsdEncoder.hpp | 132 +++++++++++++++------------- tests/IO/Archive/ZipWriterTest.cpp | 46 +++++----- 4 files changed, 113 insertions(+), 110 deletions(-) diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index 28a7277..d8466bf 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -24,8 +24,8 @@ namespace OpenVulkano::Scene if (!file.is_open()) throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); - auto [objContents, mtlContents] = GetObjContents(geometry, ""); - file << objContents; + std::stringstream dummy; + WriteObjContents(geometry, "", file, dummy); file.close(); } @@ -34,8 +34,7 @@ namespace OpenVulkano::Scene std::ofstream file(filePath); if (!file.is_open()) throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); - std::string scene = GetUsdContents(geometry); - file << scene << "\n"; + WriteUsdContents(file, geometry); file.close(); } @@ -43,13 +42,14 @@ namespace OpenVulkano::Scene { OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str()); - auto [objContents, mtlContents] = GetObjContents(geometry, texturePath); + std::stringstream objContents, mtlContents; + WriteObjContents(geometry, texturePath, objContents, mtlContents); - auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContents.size()); - zipWriter.AddFile(objDesc, objContents.data()); + auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContents.str().size()); + zipWriter.AddFile(objDesc, objContents.str().data()); - auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContents.size()); - zipWriter.AddFile(mtlDesc, mtlContents.data()); + auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContents.str().size()); + zipWriter.AddFile(mtlDesc, mtlContents.str().data()); if (!texturePath.empty() && std::filesystem::exists(texturePath)) { @@ -63,9 +63,10 @@ namespace OpenVulkano::Scene { OpenVulkano::ZipWriter zipWriter(usdzPath); - std::string usd = GetUsdContents(geometry, texturePath); - auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usd.size()); - zipWriter.AddFile(usdDesc, usd.data()); + std::stringstream usdFile; + WriteUsdContents(usdFile, geometry); + auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usdFile.str().size()); + zipWriter.AddFile(usdDesc, usdFile.str().data()); if (!texturePath.empty() && std::filesystem::exists(texturePath)) { @@ -73,7 +74,5 @@ namespace OpenVulkano::Scene auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); zipWriter.AddFile(texDesc, textureFile.Data()); } - - zipWriter.Close(); } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/ObjEncoder.hpp b/openVulkanoCpp/Scene/ObjEncoder.hpp index 8bd6311..6323288 100644 --- a/openVulkanoCpp/Scene/ObjEncoder.hpp +++ b/openVulkanoCpp/Scene/ObjEncoder.hpp @@ -14,30 +14,22 @@ namespace OpenVulkano::Scene { - // Returns [objContents, mtlContents] - std::pair GetObjContents(Geometry* geometry, const std::string& texturePath) + void WriteObjContents(Geometry* geometry, const std::string& texturePath, std::ostream& objContent, std::ostream& mtlContent) { - std::pair result; - bool useTexture = texturePath.size() != 0; - std::stringstream objContent; objContent << "# OBJ file generated by OpenVulkanoCpp\n"; if (useTexture) { - std::stringstream mtlContent; - std::string materialName = "Material0"; - mtlContent << "newmtl " << materialName << R"( + mtlContent << R"(newmtl Material0 Ka 1.000 1.000 1.000 Kd 1.000 1.000 1.000 Ks 0.000 0.000 0.000 map_Ka texture.png map_Kd texture.png )"; - objContent << "mtllib material.mtl\nusemtl " << materialName << "\n"; - - result.second = mtlContent.str(); + objContent << "mtllib material.mtl\nusemtl Material0\n"; } for (int i = 0; i < geometry->vertexCount; ++i) @@ -62,9 +54,5 @@ map_Kd texture.png uint32_t i2 = geometry->GetIndex(i + 2) + 1; objContent << fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); } - - result.first = objContent.str(); - - return result; } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UsdEncoder.hpp b/openVulkanoCpp/Scene/UsdEncoder.hpp index 429ceed..de1235f 100644 --- a/openVulkanoCpp/Scene/UsdEncoder.hpp +++ b/openVulkanoCpp/Scene/UsdEncoder.hpp @@ -11,56 +11,9 @@ namespace OpenVulkano::Scene { - std::string GetUsdContents(OpenVulkano::Scene::Geometry* geometry, const std::string texturePath = "") + void WriteUsdContents(std::ostream& output, OpenVulkano::Scene::Geometry* geometry) { - std::ostringstream result; - std::ostringstream points, normals, indices, texCoords, faceCounts; - points << std::fixed << std::setprecision(6); - normals << std::fixed << std::setprecision(6); - - for (size_t i = 0; i < geometry->vertexCount; ++i) - { - const auto& v = geometry->vertices[i]; - points << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")"; - normals << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")"; - if (i < geometry->vertexCount - 1) - { - points << ", "; - normals << ", "; - } - } - - for (size_t i = 0; i < geometry->indexCount; ++i) - { - indices << geometry->GetIndex(i); - if (i < geometry->indexCount - 1) - { - indices << ", "; - } - if ((i + 1) % 3 == 0) - { - faceCounts << "3"; - if (i < geometry->indexCount - 1) - { - faceCounts << ", "; - } - } - } - - texCoords << std::fixed << std::setprecision(6); - - for (size_t i = 0; i < geometry->indexCount; ++i) - { - const size_t vertexIndex = geometry->GetIndex(i); - const auto& v = geometry->vertices[vertexIndex]; - texCoords << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")"; - if (i < geometry->indexCount - 1) - { - texCoords << ", "; - } - } - - result << R"(#usda 1.0 + output << R"(#usda 1.0 ( defaultPrim = "root" doc = "Exported from OpenVulkano" @@ -91,19 +44,79 @@ def Xform "root" ( { uniform bool doubleSided = 1 float3[] extent = [(-0.5, -0.5, 0), (0.5, 0.5, 0)] - int[] faceVertexCounts = [)" - << faceCounts.str() << R"(] - int[] faceVertexIndices = [)" - << indices.str() << R"(] + int[] faceVertexCounts = [)"; + + for (size_t i = 0; i < geometry->indexCount; ++i) + { + if ((i + 1) % 3 == 0) + { + output << "3"; + if (i < geometry->indexCount - 1) + { + output << ", "; + } + } + } + + output << R"(] + int[] faceVertexIndices = [)"; + + for (size_t i = 0; i < geometry->indexCount; ++i) + { + output << geometry->GetIndex(i); + if (i < geometry->indexCount - 1) + { + output << ", "; + } + } + + output << R"(] rel material:binding = - normal3f[] normals = [)" - << normals.str() << R"(] ( + normal3f[] normals = [)"; + + output << std::fixed << std::setprecision(6); + for (size_t i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + output << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")"; + if (i < geometry->vertexCount - 1) + { + output << ", "; + } + } + + output << R"(] ( interpolation = "faceVarying" ) - point3f[] points = [)" - << points.str() << R"(] - texCoord2f[] primvars:st = [)" - << texCoords.str() << R"(] ( + point3f[] points = [)"; + + for (size_t i = 0; i < geometry->vertexCount; ++i) + { + const auto& v = geometry->vertices[i]; + output << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")"; + if (i < geometry->vertexCount - 1) + { + output << ", "; + } + } + + output << R"(] + texCoord2f[] primvars:st = [)"; + + output << std::fixed << std::setprecision(6); + + for (size_t i = 0; i < geometry->indexCount; ++i) + { + const size_t vertexIndex = geometry->GetIndex(i); + const auto& v = geometry->vertices[vertexIndex]; + output << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")"; + if (i < geometry->indexCount - 1) + { + output << ", "; + } + } + + output << R"(] ( interpolation = "faceVarying" ) uniform token subdivisionScheme = "none" @@ -153,6 +166,5 @@ def Xform "root" ( } } )"; - return result.str(); } } \ No newline at end of file diff --git a/tests/IO/Archive/ZipWriterTest.cpp b/tests/IO/Archive/ZipWriterTest.cpp index fffa270..6b70d21 100644 --- a/tests/IO/Archive/ZipWriterTest.cpp +++ b/tests/IO/Archive/ZipWriterTest.cpp @@ -19,8 +19,9 @@ using namespace OpenVulkano; TEST_CASE("Empty zip file", "[ZipWriter]") { const auto emptyZipPath = AppFolders::GetAppTempDir() / "empty.zip"; - ZipWriter writer(emptyZipPath); - writer.Close(); + { + ZipWriter writer(emptyZipPath); + } auto mem = Utils::ReadFile(emptyZipPath); const int expectSize = 22; Array expect = {0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -34,15 +35,17 @@ TEST_CASE("Empty zip file", "[ZipWriter]") TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") { const auto oneFileZipPath = AppFolders::GetAppTempDir() / "one_file.zip"; - ZipWriter writer(oneFileZipPath); + + { + ZipWriter writer(oneFileZipPath); - FileDescription desc = FileDescription::MakeDescriptionForFile("AAA.txt", 3); - desc.modTime = {}; - desc.createTime = {}; - char buffer[] = {'A', 'A', 'A'}; + FileDescription desc = FileDescription::MakeDescriptionForFile("AAA.txt", 3); + desc.modTime = {}; + desc.createTime = {}; + char buffer[] = {'A', 'A', 'A'}; - writer.AddFile(desc, buffer); - writer.Close(); + writer.AddFile(desc, buffer); + } auto mem = Utils::ReadFile(oneFileZipPath); @@ -67,21 +70,22 @@ TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") TEST_CASE("Zip with two files(AAA.txt that has 'AAA', BBB.bin that has 'BBB')", "[ZipWriter]") { const auto twoFilesZipPath = AppFolders::GetAppTempDir() / "two_files.zip"; - ZipWriter writer(twoFilesZipPath); + { + ZipWriter writer(twoFilesZipPath); - FileDescription aaa = FileDescription::MakeDescriptionForFile("AAA.txt", 3); - aaa.modTime = {}; - aaa.createTime = {}; - char aaaBuffer[] = {'A', 'A', 'A'}; + FileDescription aaa = FileDescription::MakeDescriptionForFile("AAA.txt", 3); + aaa.modTime = {}; + aaa.createTime = {}; + char aaaBuffer[] = {'A', 'A', 'A'}; - FileDescription bbb = FileDescription::MakeDescriptionForFile("BBB.bin", 3); - bbb.modTime = {}; - bbb.createTime = {}; - char bbbBuffer[] = {'B', 'B', 'B'}; + FileDescription bbb = FileDescription::MakeDescriptionForFile("BBB.bin", 3); + bbb.modTime = {}; + bbb.createTime = {}; + char bbbBuffer[] = {'B', 'B', 'B'}; - writer.AddFile(aaa, aaaBuffer); - writer.AddFile(bbb, bbbBuffer); - writer.Close(); + writer.AddFile(aaa, aaaBuffer); + writer.AddFile(bbb, bbbBuffer); + } auto mem = Utils::ReadFile(twoFilesZipPath); From 34bfebbdd389dcb3d41c3e2848e1775d2ba8f569 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Wed, 27 Nov 2024 20:21:14 +0200 Subject: [PATCH 11/15] Summary: - Changes in Cat functions - Using gmtime_s and gmtime_r as a thread-safe functions - ZFill() that is used for padding - Option for ZipWriter to pad LocalFileHeaders(it is used to properly run tests) - ZipWriter::IsOpen() - Moved material creation away from WriteObjContents - Using MemMappedFile instead of ReadFile - Scoping files and adding them to archive - UsdEncoder refactoring --- openVulkanoCpp/IO/Archive/ZipWriter.cpp | 61 ++++++++++++++++--------- openVulkanoCpp/IO/Archive/ZipWriter.hpp | 5 +- openVulkanoCpp/Scene/MeshWriter.cpp | 44 +++++++++++------- openVulkanoCpp/Scene/ObjEncoder.hpp | 20 ++++---- openVulkanoCpp/Scene/UsdEncoder.hpp | 25 ++-------- 5 files changed, 85 insertions(+), 70 deletions(-) diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.cpp b/openVulkanoCpp/IO/Archive/ZipWriter.cpp index 77994c4..a5c12a8 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.cpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.cpp @@ -106,54 +106,64 @@ namespace static_assert(sizeof(EndOfCentralDirectoryHeader) == 22, "Well packed struct"); static_assert(sizeof(NtfsExtraField) == 36, "Well packed struct"); - uint32_t Cat(std::vector& dest, size_t size, uint8_t* thing) + uint32_t Cat(std::vector& dest, size_t size, const uint8_t* thing) { uint32_t startOffset = dest.size(); - dest.insert(dest.end(), (uint8_t*)thing, (uint8_t*)(thing + size)); + dest.insert(dest.end(), thing, thing + size); return startOffset; } template - uint32_t Cat(std::vector& dest, T* thing) + uint32_t Cat(std::vector& dest, const T* thing) { - return Cat(dest, sizeof(T), reinterpret_cast(thing)); + return Cat(dest, sizeof(T), reinterpret_cast(thing)); } uint32_t Cat(std::vector& dest, const std::vector& thing) { - return Cat(dest, thing.size(), const_cast(thing.data())); + return Cat(dest, thing.size(), thing.data()); } - std::pair ConvertToDosTimeDate(time_t time) + std::pair ConvertToDosTimeDate(const time_t time) { - std::tm* tm = std::gmtime(&time); - if (tm) - { - uint16_t dosTime = 0; - dosTime |= (tm->tm_sec / 2) & 0x1F; // Seconds divided by 2 (Bits 00-04) - dosTime |= (tm->tm_min & 0x3F) << 5; // Minutes (Bits 05-10) - dosTime |= (tm->tm_hour & 0x1F) << 11; // Hours (Bits 11-15) + std::tm tm; +#if _MSC_VER >= 1400 + if (gmtime_s(&tm, &time) != 0) [[unlikely]] + throw std::runtime_error("gmtime_s() failed"); +#else + if (gmtime_r(&time, &tm) == nullptr) [[unlikely]] + throw std::runtime_error("gmtime_r() failed"); +#endif + uint16_t dosTime = 0; + dosTime |= (tm.tm_sec / 2) & 0x1F; // Seconds divided by 2 (Bits 00-04) + dosTime |= (tm.tm_min & 0x3F) << 5; // Minutes (Bits 05-10) + dosTime |= (tm.tm_hour & 0x1F) << 11; // Hours (Bits 11-15) - uint16_t dosDate = 0; - dosDate |= (tm->tm_mday & 0x1F); // Day (Bits 00-04) - dosDate |= ((tm->tm_mon + 1) & 0x0F) << 5; // Month (Bits 05-08) - dosDate |= ((tm->tm_year - 80) & 0x7F) << 9; // Year from 1980 (Bits 09-15) - return { dosTime, dosDate }; - } + uint16_t dosDate = 0; + dosDate |= (tm.tm_mday & 0x1F); // Day (Bits 00-04) + dosDate |= ((tm.tm_mon + 1) & 0x0F) << 5; // Month (Bits 05-08) + dosDate |= ((tm.tm_year - 80) & 0x7F) << 9; // Year from 1980 (Bits 09-15) + return { dosTime, dosDate }; + } - return {}; + template + void ZFill(FILE *handle, size_t num) + { + uint8_t zeros[MAX_PADDING_SIZE] = {}; + fwrite(&zeros, num, 1, handle); } } namespace OpenVulkano { - ZipWriter::ZipWriter(const std::filesystem::path& filePath) + ZipWriter::ZipWriter(const std::filesystem::path& filePath, bool alignHeadersby64) { m_file = fopen(filePath.string().c_str(), "wb"); if (!m_file) { throw std::runtime_error("Unable to open file for writing: " + filePath.string()); } + m_pad = alignHeadersby64; } void ZipWriter::AddFile(const FileDescription& description, const void* buffer) @@ -169,6 +179,13 @@ namespace OpenVulkano time_t accessTime = modTime; // FileDescription doesn't have this field auto [dosTime, dosDate] = ConvertToDosTimeDate(modTime); + if (m_pad) + { + size_t headerSize = sizeof(LocalFileHeader) + fileNameLength; + size_t padding = 64 - ((ftell(m_file) + headerSize) & 63); + ZFill<64>(m_file, padding); + } + LocalFileHeader lfh; lfh.fileLastModTime = dosTime; lfh.fileLastModDate = dosDate; @@ -209,7 +226,7 @@ namespace OpenVulkano ZipWriter::~ZipWriter() { - if (m_file) + if (IsOpen()) { int centralDirsOffset = 0; if (m_numFiles) diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.hpp b/openVulkanoCpp/IO/Archive/ZipWriter.hpp index 0ae044c..24be87a 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.hpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.hpp @@ -17,12 +17,15 @@ namespace OpenVulkano { std::vector m_centralDirs; int m_numFiles = 0; + bool m_pad = false; FILE* m_file; public: - ZipWriter(const std::filesystem::path& filePath); + ZipWriter(const std::filesystem::path& filePath, bool alignHeadersby64 = false); ~ZipWriter(); + bool IsOpen() const { return m_file != nullptr; } + void AddFile(const FileDescription& description, const void* buffer); void AddFile(const std::filesystem::path& fileName, const char* inArchiveName); }; diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index d8466bf..78f1dbd 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -5,6 +5,7 @@ */ #include "MeshWriter.hpp" +#include "IO/MemMappedFile.hpp" #include "Scene/Geometry.hpp" #include "Scene/Vertex.hpp" #include "Scene/UsdEncoder.hpp" @@ -24,8 +25,7 @@ namespace OpenVulkano::Scene if (!file.is_open()) throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); - std::stringstream dummy; - WriteObjContents(geometry, "", file, dummy); + WriteObjContents(geometry, "", file); file.close(); } @@ -42,18 +42,25 @@ namespace OpenVulkano::Scene { OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str()); - std::stringstream objContents, mtlContents; - WriteObjContents(geometry, texturePath, objContents, mtlContents); - - auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContents.str().size()); - zipWriter.AddFile(objDesc, objContents.str().data()); - - auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContents.str().size()); - zipWriter.AddFile(mtlDesc, mtlContents.str().data()); + const char* materialName = "Material0"; + { + std::stringstream objContents; + WriteObjContents(geometry, materialName, objContents); + auto objContentsStr = objContents.str(); + auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContentsStr.size()); + zipWriter.AddFile(objDesc, objContentsStr.data()); + } + { + std::stringstream mtlContents; + mtlContents << "newmtl " << materialName << "\n" << ObjMaterialContents; + auto mtlContentsStr = mtlContents.str(); + auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContentsStr.size()); + zipWriter.AddFile(mtlDesc, mtlContentsStr.data()); + } if (!texturePath.empty() && std::filesystem::exists(texturePath)) { - auto textureFile = Utils::ReadFile(texturePath); + auto textureFile = MemMappedFile(texturePath); auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); zipWriter.AddFile(texDesc, textureFile.Data()); } @@ -61,16 +68,19 @@ namespace OpenVulkano::Scene void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& texturePath, const std::string& usdzPath) { - OpenVulkano::ZipWriter zipWriter(usdzPath); + OpenVulkano::ZipWriter zipWriter(usdzPath);// NOCHECKIN, true); - std::stringstream usdFile; - WriteUsdContents(usdFile, geometry); - auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usdFile.str().size()); - zipWriter.AddFile(usdDesc, usdFile.str().data()); + { + std::stringstream usdFile; + WriteUsdContents(usdFile, geometry); + auto usdFileStr = usdFile.str(); + auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usdFileStr.size()); + zipWriter.AddFile(usdDesc, usdFileStr.data()); + } if (!texturePath.empty() && std::filesystem::exists(texturePath)) { - auto textureFile = Utils::ReadFile(texturePath); + auto textureFile = MemMappedFile(texturePath); auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); zipWriter.AddFile(texDesc, textureFile.Data()); } diff --git a/openVulkanoCpp/Scene/ObjEncoder.hpp b/openVulkanoCpp/Scene/ObjEncoder.hpp index 6323288..2d069cd 100644 --- a/openVulkanoCpp/Scene/ObjEncoder.hpp +++ b/openVulkanoCpp/Scene/ObjEncoder.hpp @@ -14,22 +14,22 @@ namespace OpenVulkano::Scene { - void WriteObjContents(Geometry* geometry, const std::string& texturePath, std::ostream& objContent, std::ostream& mtlContent) - { - bool useTexture = texturePath.size() != 0; - - objContent << "# OBJ file generated by OpenVulkanoCpp\n"; - - if (useTexture) - { - mtlContent << R"(newmtl Material0 + static constexpr std::string_view ObjMaterialContents = R"( Ka 1.000 1.000 1.000 Kd 1.000 1.000 1.000 Ks 0.000 0.000 0.000 map_Ka texture.png map_Kd texture.png )"; - objContent << "mtllib material.mtl\nusemtl Material0\n"; + + void WriteObjContents(Geometry* geometry, const std::string& materialName, std::ostream& objContent) + { + objContent << "# OBJ file generated by OpenVulkanoCpp\n"; + + if (materialName.size() != 0) + { + objContent << "mtllib material.mtl\n"; + objContent << "usemtl " << materialName << "\n"; } for (int i = 0; i < geometry->vertexCount; ++i) diff --git a/openVulkanoCpp/Scene/UsdEncoder.hpp b/openVulkanoCpp/Scene/UsdEncoder.hpp index de1235f..bd795af 100644 --- a/openVulkanoCpp/Scene/UsdEncoder.hpp +++ b/openVulkanoCpp/Scene/UsdEncoder.hpp @@ -50,11 +50,8 @@ def Xform "root" ( { if ((i + 1) % 3 == 0) { + if (i > 2) output << ", "; output << "3"; - if (i < geometry->indexCount - 1) - { - output << ", "; - } } } @@ -63,11 +60,8 @@ def Xform "root" ( for (size_t i = 0; i < geometry->indexCount; ++i) { + if (i > 0) output << ", "; output << geometry->GetIndex(i); - if (i < geometry->indexCount - 1) - { - output << ", "; - } } output << R"(] @@ -78,11 +72,8 @@ def Xform "root" ( for (size_t i = 0; i < geometry->vertexCount; ++i) { const auto& v = geometry->vertices[i]; + if (i > 0) output << ", "; output << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")"; - if (i < geometry->vertexCount - 1) - { - output << ", "; - } } output << R"(] ( @@ -93,11 +84,8 @@ def Xform "root" ( for (size_t i = 0; i < geometry->vertexCount; ++i) { const auto& v = geometry->vertices[i]; + if (i > 0) output << ", "; output << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")"; - if (i < geometry->vertexCount - 1) - { - output << ", "; - } } output << R"(] @@ -109,11 +97,8 @@ def Xform "root" ( { const size_t vertexIndex = geometry->GetIndex(i); const auto& v = geometry->vertices[vertexIndex]; + if (i > 0) output << ", "; output << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")"; - if (i < geometry->indexCount - 1) - { - output << ", "; - } } output << R"(] ( From 2d936096e7ec1a495d899363429654dcd640a2e5 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Wed, 27 Nov 2024 20:23:10 +0200 Subject: [PATCH 12/15] Duh, removed temporary code --- openVulkanoCpp/Scene/MeshWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index 78f1dbd..cbb7c6c 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -68,7 +68,7 @@ namespace OpenVulkano::Scene void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& texturePath, const std::string& usdzPath) { - OpenVulkano::ZipWriter zipWriter(usdzPath);// NOCHECKIN, true); + OpenVulkano::ZipWriter zipWriter(usdzPath, true); { std::stringstream usdFile; From ba9d37cc6f55370973ae1e4d62238d640241bf99 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 28 Nov 2024 16:48:22 +0200 Subject: [PATCH 13/15] Added operator bool for ZipWriter, filling centralDirSize, padding zip via extraField --- openVulkanoCpp/IO/Archive/ZipWriter.cpp | 31 ++++++++++++++++++------- openVulkanoCpp/IO/Archive/ZipWriter.hpp | 1 + 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.cpp b/openVulkanoCpp/IO/Archive/ZipWriter.cpp index a5c12a8..b4b8a4f 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.cpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.cpp @@ -179,23 +179,34 @@ namespace OpenVulkano time_t accessTime = modTime; // FileDescription doesn't have this field auto [dosTime, dosDate] = ConvertToDosTimeDate(modTime); - if (m_pad) - { - size_t headerSize = sizeof(LocalFileHeader) + fileNameLength; - size_t padding = 64 - ((ftell(m_file) + headerSize) & 63); - ZFill<64>(m_file, padding); - } - LocalFileHeader lfh; lfh.fileLastModTime = dosTime; lfh.fileLastModDate = dosDate; lfh.crc32 = crc32; lfh.compressedSize = lfh.uncompressedSize = fileSize; lfh.fileNameLength = fileNameLength; + + if (m_pad) + { + size_t headerSize = sizeof(LocalFileHeader) + fileNameLength; + size_t padding = 64 - ((ftell(m_file) + headerSize) & 63); + if (padding < 4) padding += 64; + lfh.extraFieldLength = padding; + } size_t headerOffset = ftell(m_file); fwrite(&lfh, sizeof(lfh), 1, m_file); fwrite(fileName, fileNameLength, 1, m_file); + + if (m_pad) + { + uint16_t marker = 6534; + uint16_t dataSize = lfh.extraFieldLength - 4; + fwrite(&marker, sizeof(uint16_t), 1, m_file); + fwrite(&dataSize, sizeof(uint16_t), 1, m_file); + ZFill<128>(m_file, dataSize); + } + fwrite(buffer, fileSize, 1, m_file); CentalDirectoryFileHeader cdfh; @@ -229,17 +240,19 @@ namespace OpenVulkano if (IsOpen()) { int centralDirsOffset = 0; + size_t centralDirsSize = m_centralDirs.size(); if (m_numFiles) { centralDirsOffset = ftell(m_file); - fwrite(m_centralDirs.data(), m_centralDirs.size(), 1, m_file); + fwrite(m_centralDirs.data(), centralDirsSize, 1, m_file); m_centralDirs.clear(); } EndOfCentralDirectoryHeader eocd; eocd.centralDirectoryEntries = eocd.totalCentralDirectoryEntries = m_numFiles; eocd.centralDirectoryOffset = centralDirsOffset; - + eocd.centralDirectorySize = centralDirsSize; + fwrite(&eocd, sizeof(eocd), 1, m_file); fclose(m_file); } diff --git a/openVulkanoCpp/IO/Archive/ZipWriter.hpp b/openVulkanoCpp/IO/Archive/ZipWriter.hpp index 24be87a..d93e6f1 100644 --- a/openVulkanoCpp/IO/Archive/ZipWriter.hpp +++ b/openVulkanoCpp/IO/Archive/ZipWriter.hpp @@ -25,6 +25,7 @@ namespace OpenVulkano ~ZipWriter(); bool IsOpen() const { return m_file != nullptr; } + operator bool() const { return IsOpen(); } void AddFile(const FileDescription& description, const void* buffer); void AddFile(const std::filesystem::path& fileName, const char* inArchiveName); From 7a214397a19fe9d46a8a56a2a5a9cbdc1418da70 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 28 Nov 2024 22:01:43 +0200 Subject: [PATCH 14/15] Hardcoded material name, capitalized constants --- openVulkanoCpp/Scene/MeshWriter.cpp | 7 ++----- openVulkanoCpp/Scene/ObjEncoder.hpp | 5 +++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index cbb7c6c..03329aa 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -42,18 +42,15 @@ namespace OpenVulkano::Scene { OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str()); - const char* materialName = "Material0"; { std::stringstream objContents; - WriteObjContents(geometry, materialName, objContents); + WriteObjContents(geometry, DEFAULT_OBJ_MATERIAL_NAME, objContents); auto objContentsStr = objContents.str(); auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContentsStr.size()); zipWriter.AddFile(objDesc, objContentsStr.data()); } { - std::stringstream mtlContents; - mtlContents << "newmtl " << materialName << "\n" << ObjMaterialContents; - auto mtlContentsStr = mtlContents.str(); + auto mtlContentsStr = DEFAULT_OBJ_MATERIAL_CONTENTS; auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContentsStr.size()); zipWriter.AddFile(mtlDesc, mtlContentsStr.data()); } diff --git a/openVulkanoCpp/Scene/ObjEncoder.hpp b/openVulkanoCpp/Scene/ObjEncoder.hpp index 2d069cd..68fc959 100644 --- a/openVulkanoCpp/Scene/ObjEncoder.hpp +++ b/openVulkanoCpp/Scene/ObjEncoder.hpp @@ -14,7 +14,8 @@ namespace OpenVulkano::Scene { - static constexpr std::string_view ObjMaterialContents = R"( + static constexpr std::string_view DEFAULT_OBJ_MATERIAL_NAME = "Material0"; + static constexpr std::string_view DEFAULT_OBJ_MATERIAL_CONTENTS = R"(newmtl Material0 Ka 1.000 1.000 1.000 Kd 1.000 1.000 1.000 Ks 0.000 0.000 0.000 @@ -22,7 +23,7 @@ map_Ka texture.png map_Kd texture.png )"; - void WriteObjContents(Geometry* geometry, const std::string& materialName, std::ostream& objContent) + void WriteObjContents(Geometry* geometry, const std::string_view& materialName, std::ostream& objContent) { objContent << "# OBJ file generated by OpenVulkanoCpp\n"; From 0a38791beef0fcb12b7cd9bbedd7487dcdf70ada Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Fri, 29 Nov 2024 12:20:29 +0200 Subject: [PATCH 15/15] Syntax changes, removed rotation in usd file format --- openVulkanoCpp/Scene/MeshWriter.cpp | 25 ++++++++++++------------- openVulkanoCpp/Scene/ObjEncoder.hpp | 22 ++++++++++++++++------ openVulkanoCpp/Scene/UsdEncoder.hpp | 2 +- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index 03329aa..7ee4ab5 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -22,7 +22,7 @@ namespace OpenVulkano::Scene { std::ofstream file(filePath); - if (!file.is_open()) + if (!file.is_open()) [[unlikely]] throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); WriteObjContents(geometry, "", file); @@ -32,7 +32,7 @@ namespace OpenVulkano::Scene void MeshWriter::WriteAsUSD(Geometry* geometry, const std::string& filePath) { std::ofstream file(filePath); - if (!file.is_open()) + if (!file.is_open()) [[unlikely]] throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); WriteUsdContents(file, geometry); file.close(); @@ -45,20 +45,19 @@ namespace OpenVulkano::Scene { std::stringstream objContents; WriteObjContents(geometry, DEFAULT_OBJ_MATERIAL_NAME, objContents); - auto objContentsStr = objContents.str(); - auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContentsStr.size()); + std::string objContentsStr = objContents.str(); + FileDescription objDesc = FileDescription::MakeDescriptionForFile("model.obj", objContentsStr.size()); zipWriter.AddFile(objDesc, objContentsStr.data()); } { - auto mtlContentsStr = DEFAULT_OBJ_MATERIAL_CONTENTS; - auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContentsStr.size()); - zipWriter.AddFile(mtlDesc, mtlContentsStr.data()); + FileDescription mtlDesc = FileDescription::MakeDescriptionForFile("material.mtl", DEFAULT_OBJ_MATERIAL_CONTENTS.size()); + zipWriter.AddFile(mtlDesc, DEFAULT_OBJ_MATERIAL_CONTENTS.data()); } if (!texturePath.empty() && std::filesystem::exists(texturePath)) { - auto textureFile = MemMappedFile(texturePath); - auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); + MemMappedFile textureFile(texturePath); + FileDescription texDesc = FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); zipWriter.AddFile(texDesc, textureFile.Data()); } } @@ -70,15 +69,15 @@ namespace OpenVulkano::Scene { std::stringstream usdFile; WriteUsdContents(usdFile, geometry); - auto usdFileStr = usdFile.str(); - auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usdFileStr.size()); + std::string usdFileStr = usdFile.str(); + FileDescription usdDesc = FileDescription::MakeDescriptionForFile("geometry.usda", usdFileStr.size()); zipWriter.AddFile(usdDesc, usdFileStr.data()); } if (!texturePath.empty() && std::filesystem::exists(texturePath)) { - auto textureFile = MemMappedFile(texturePath); - auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); + MemMappedFile textureFile(texturePath); + FileDescription texDesc = FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size()); zipWriter.AddFile(texDesc, textureFile.Data()); } } diff --git a/openVulkanoCpp/Scene/ObjEncoder.hpp b/openVulkanoCpp/Scene/ObjEncoder.hpp index 68fc959..f69eced 100644 --- a/openVulkanoCpp/Scene/ObjEncoder.hpp +++ b/openVulkanoCpp/Scene/ObjEncoder.hpp @@ -29,31 +29,41 @@ map_Kd texture.png if (materialName.size() != 0) { - objContent << "mtllib material.mtl\n"; - objContent << "usemtl " << materialName << "\n"; + std::string_view content = "mtllib material.mtl\n"; + objContent.write(content.data(), content.size()); + + content = "usemtl "; + objContent.write(content.data(), content.size()); + + objContent.write(materialName.data(), materialName.size()); + objContent.write("\n", 1); } for (int i = 0; i < geometry->vertexCount; ++i) { const auto& v = geometry->vertices[i]; - objContent << fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z); + const std::string content = fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z); + objContent.write(content.data(), content.size()); } for (int i = 0; i < geometry->vertexCount; ++i) { const auto& v = geometry->vertices[i]; - objContent << fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); + const std::string content = fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); + objContent.write(content.data(), content.size()); } for (int i = 0; i < geometry->vertexCount; ++i) { const auto& v = geometry->vertices[i]; - objContent << fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y); + const std::string content = fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y); + objContent.write(content.data(), content.size()); } for (int i = 0; i < geometry->indexCount; i += 3) { uint32_t i0 = geometry->GetIndex(i + 0) + 1; uint32_t i1 = geometry->GetIndex(i + 1) + 1; uint32_t i2 = geometry->GetIndex(i + 2) + 1; - objContent << fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); + const std::string content = fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); + objContent.write(content.data(), content.size()); } } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/UsdEncoder.hpp b/openVulkanoCpp/Scene/UsdEncoder.hpp index bd795af..40d4632 100644 --- a/openVulkanoCpp/Scene/UsdEncoder.hpp +++ b/openVulkanoCpp/Scene/UsdEncoder.hpp @@ -32,7 +32,7 @@ def Xform "root" ( def Xform "model" { custom string userProperties:blender:object_name = "model" - float3 xformOp:rotateXYZ = (90, -0, 0) + float3 xformOp:rotateXYZ = (0, -0, 0) float3 xformOp:scale = (1, 1, 1) double3 xformOp:translate = (0, 0, 0) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]