- 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
This commit is contained in:
Vladyslav Baranovskyi
2024-11-22 15:45:57 +02:00
parent 35e80a99e6
commit ac843c0fe3
8 changed files with 330 additions and 417 deletions

View File

@@ -11,6 +11,7 @@
*/ */
#include "ZipWriter.hpp" #include "ZipWriter.hpp"
#include "Base/Utils.hpp"
#include "Math/CRC32.hpp" #include "Math/CRC32.hpp"
#include <ctime> #include <ctime>
@@ -91,22 +92,6 @@ namespace
static_assert(sizeof(EndOfCentralDirectoryHeader) == 22, "Well packed struct"); static_assert(sizeof(EndOfCentralDirectoryHeader) == 22, "Well packed struct");
static_assert(sizeof(NtfsExtraField) == 36, "Well packed struct"); static_assert(sizeof(NtfsExtraField) == 36, "Well packed struct");
std::vector<uint8_t> ReadEntireFile(const std::string& fname)
{
FILE *file = fopen(fname.c_str(), "rb");
std::vector<uint8_t> 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<typename T> template<typename T>
uint32_t Cat(std::vector<uint8_t>& dest, T* thing) uint32_t Cat(std::vector<uint8_t>& dest, T* thing)
{ {
@@ -157,6 +142,15 @@ namespace
namespace OpenVulkano 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) void ZipWriter::AddFile(const FileDescription& description, const void* buffer)
{ {
size_t fileSize = description.size; size_t fileSize = description.size;
@@ -177,9 +171,10 @@ namespace OpenVulkano
lfh.compressedSize = lfh.uncompressedSize = fileSize; lfh.compressedSize = lfh.uncompressedSize = fileSize;
lfh.fileNameLength = fileNameLength; lfh.fileNameLength = fileNameLength;
size_t headerOffset = Cat(m_headers, &lfh); size_t headerOffset = ftell(m_file);
Cat(m_headers, fileNameLength, fileName); fwrite(&lfh, sizeof(lfh), 1, m_file);
Cat(m_headers, description.size, (uint8_t *)buffer); fwrite(fileName, fileNameLength, 1, m_file);
fwrite(buffer, fileSize, 1, m_file);
CentalDirectoryFileHeader cdfh; CentalDirectoryFileHeader cdfh;
cdfh.fileLastModTime = dosTime; cdfh.fileLastModTime = dosTime;
@@ -203,41 +198,28 @@ namespace OpenVulkano
m_numFiles += 1; m_numFiles += 1;
} }
std::vector<uint8_t> ZipWriter::GetMemory() void ZipWriter::AddFile(const std::filesystem::path& fileName, const char* inArchiveName)
{ {
std::vector<uint8_t> 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) 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; EndOfCentralDirectoryHeader eocd;
eocd.centralDirectoryEntries = eocd.totalCentralDirectoryEntries = m_numFiles; eocd.centralDirectoryEntries = eocd.totalCentralDirectoryEntries = m_numFiles;
eocd.centralDirectoryOffset = m_headers.size(); eocd.centralDirectoryOffset = centralDirsOffset;
Cat(buffer, &eocd); fwrite(&eocd, sizeof(eocd), 1, m_file);
fclose(m_file);
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;
} }
} }

View File

@@ -15,15 +15,15 @@ namespace OpenVulkano
{ {
class ZipWriter class ZipWriter
{ {
std::vector<uint8_t> m_headers;
std::vector<uint8_t> m_centralDirs; std::vector<uint8_t> m_centralDirs;
int m_numFiles = 0; int m_numFiles = 0;
FILE *m_file;
public: public:
ZipWriter(const std::filesystem::path& filePath);
void AddFile(const FileDescription& description, const void* buffer); void AddFile(const FileDescription& description, const void* buffer);
void AddFile(const std::filesystem::path& fileName, const char* inArchiveName); void AddFile(const std::filesystem::path& fileName, const char* inArchiveName);
void Close();
bool Write(const std::filesystem::path& archivePath);
std::vector<uint8_t> GetMemory();
}; };
} }

View File

@@ -153,4 +153,29 @@ namespace OpenVulkano::Scene
vertices = nullptr; vertices = nullptr;
indices = 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;
}
} }

View File

@@ -57,6 +57,7 @@ namespace OpenVulkano
Vertex* GetVertices() const { return vertices; } Vertex* GetVertices() const { return vertices; }
void* GetIndices() const { return indices; } void* GetIndices() const { return indices; }
uint32_t GetIndex(int index) const;
uint16_t* GetIndices16() const { return static_cast<uint16_t*>(indices); } uint16_t* GetIndices16() const { return static_cast<uint16_t*>(indices); }
uint32_t* GetIndices32() const { return static_cast<uint32_t*>(indices); } uint32_t* GetIndices32() const { return static_cast<uint32_t*>(indices); }
uint32_t GetIndexCount() const { return indexCount; } uint32_t GetIndexCount() const { return indexCount; }

View File

@@ -7,6 +7,8 @@
#include "MeshWriter.hpp" #include "MeshWriter.hpp"
#include "Scene/Geometry.hpp" #include "Scene/Geometry.hpp"
#include "Scene/Vertex.hpp" #include "Scene/Vertex.hpp"
#include "Scene/UsdEncoder.hpp"
#include "Scene/ObjEncoder.hpp"
#include "IO/Archive/ArchiveWriter.hpp" #include "IO/Archive/ArchiveWriter.hpp"
#include "IO/Archive/ZipWriter.hpp" #include "IO/Archive/ZipWriter.hpp"
#include <fstream> #include <fstream>
@@ -22,268 +24,6 @@
#include <usdc-reader.hh> #include <usdc-reader.hh>
#include <usdc-writer.hh> #include <usdc-writer.hh>
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<uint16_t *>(geometry->indices);
result = indices[index];
}
else if (geometry->indexType == OpenVulkano::Scene::VertexIndexType::UINT32)
{
uint32_t *indices = static_cast<uint32_t *>(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<tinyusdz::value::point3f> pts(geometry->vertexCount);
std::vector<int> indices(geometry->indexCount);
tinyusdz::Attribute uvAttr;
std::vector<tinyusdz::value::texcoord2f> 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<int> 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<uint8_t> ConvertToUSDC(const std::string& contents)
{
std::vector<uint8_t> 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 = </root/_materials/Material0>
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 = </root/_materials/Material0/Principled_BSDF.outputs:surface>
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 = </root/_materials/Material0/Image_Texture.outputs:rgb>
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 = </root/_materials/Material0/uvmap.outputs:result>
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 namespace OpenVulkano::Scene
{ {
void MeshWriter::WriteAsOBJ(Geometry* geometry, const std::string& filePath) void MeshWriter::WriteAsOBJ(Geometry* geometry, const std::string& filePath)
@@ -293,43 +33,8 @@ namespace OpenVulkano::Scene
if (!file.is_open()) if (!file.is_open())
throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); throw std::runtime_error("Failed to open file '" + filePath + "' for writing!");
// Vertices auto [objContents, mtlContents] = GetObjContents(geometry, "");
for (int i = 0; i < geometry->vertexCount; ++i) file << objContents;
{
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;
}
file.close(); file.close();
} }
@@ -345,64 +50,27 @@ namespace OpenVulkano::Scene
void MeshWriter::WriteObjAsZip(Geometry* geometry, const std::string& zipPath, const std::string& texturePath) 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()); OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str());
auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContent.str().size()); auto [objContents, mtlContents] = GetObjContents(geometry, texturePath);
zipWriter.AddFile(objDesc, objContent.str().data());
auto mtlDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("material.mtl", mtlContent.str().size()); auto objDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("model.obj", objContents.size());
zipWriter.AddFile(mtlDesc, mtlContent.str().data()); 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)) if (!texturePath.empty() && std::filesystem::exists(texturePath))
{ {
auto textureFileSize = std::filesystem::file_size(texturePath); auto textureFile = Utils::ReadFile(texturePath);
auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFileSize); auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size());
zipWriter.AddFile(texDesc, ReadEntireFile(texturePath).c_str()); zipWriter.AddFile(texDesc, textureFile.Data());
} }
} }
void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& usdzPath, const std::string& texturePath) 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); std::string usd = GetUsdContents(geometry, texturePath);
auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usd.size()); auto usdDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("geometry.usda", usd.size());
@@ -410,14 +78,11 @@ namespace OpenVulkano::Scene
if (!texturePath.empty() && std::filesystem::exists(texturePath)) if (!texturePath.empty() && std::filesystem::exists(texturePath))
{ {
auto textureFileSize = std::filesystem::file_size(texturePath); auto textureFile = Utils::ReadFile(texturePath);
auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFileSize); auto texDesc = OpenVulkano::FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size());
zipWriter.AddFile(texDesc, ReadEntireFile(texturePath).c_str()); zipWriter.AddFile(texDesc, textureFile.Data());
} }
if(!zipWriter.Write(usdzPath)) zipWriter.Close();
{
throw std::runtime_error("Unable to write to USDZ file " + usdzPath);
}
} }
} }

View File

@@ -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 <utility>
#include <string>
#include <sstream>
#include <fmt/core.h>
#include "Scene/Geometry.hpp"
namespace OpenVulkano::Scene
{
// Returns [objContents, mtlContents]
std::pair<std::string, std::string> GetObjContents(Geometry* geometry, const std::string& texturePath)
{
std::pair<std::string, std::string> 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;
}
}

View File

@@ -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 <sstream>
#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 = </root/_materials/Material0>
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 = </root/_materials/Material0/Principled_BSDF.outputs:surface>
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 = </root/_materials/Material0/Image_Texture.outputs:rgb>
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 = </root/_materials/Material0/uvmap.outputs:result>
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();
}
}

View File

@@ -10,25 +10,31 @@
#include <filesystem> #include <filesystem>
#include <string> #include <string>
#include "Base/Utils.hpp"
#include "IO/Archive/ZipWriter.hpp" #include "IO/Archive/ZipWriter.hpp"
#include "IO/AppFolders.hpp"
using namespace OpenVulkano; using namespace OpenVulkano;
TEST_CASE("Empty zip file", "[ZipWriter]") TEST_CASE("Empty zip file", "[ZipWriter]")
{ {
ZipWriter writer; const auto emptyZipPath = AppFolders::GetAppTempDir() / "empty.zip";
auto mem = writer.GetMemory(); ZipWriter writer(emptyZipPath);
writer.Close();
auto mem = Utils::ReadFile(emptyZipPath);
const int expectSize = 22; const int expectSize = 22;
std::vector<uint8_t> expect = {0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; Array<uint8_t> 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); CHECK(mem == expect);
std::filesystem::remove(emptyZipPath);
} }
TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]") 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); FileDescription desc = FileDescription::MakeDescriptionForFile("AAA.txt", 3);
desc.modTime = {}; desc.modTime = {};
@@ -36,11 +42,12 @@ TEST_CASE("Zip with one file(AAA.txt that has 'AAA')", "[ZipWriter]")
char buffer[] = {'A', 'A', 'A'}; char buffer[] = {'A', 'A', 'A'};
writer.AddFile(desc, buffer); writer.AddFile(desc, buffer);
writer.Close();
auto mem = writer.GetMemory(); auto mem = Utils::ReadFile(oneFileZipPath);
const int expectSize = 151; const int expectSize = 151;
std::vector<uint8_t> expect = Array<uint8_t> 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, 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, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
CHECK(mem.size() == expectSize); CHECK(mem.Size() == expectSize);
CHECK(mem == expect); CHECK(mem == expect);
std::filesystem::remove(oneFileZipPath);
} }
TEST_CASE("Zip with two files(AAA.txt that has 'AAA', BBB.bin that has 'BBB')", "[ZipWriter]") 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); FileDescription aaa = FileDescription::MakeDescriptionForFile("AAA.txt", 3);
aaa.modTime = {}; 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(aaa, aaaBuffer);
writer.AddFile(bbb, bbbBuffer); writer.AddFile(bbb, bbbBuffer);
writer.Close();
auto mem = writer.GetMemory(); auto mem = Utils::ReadFile(twoFilesZipPath);
const int expectSize = 280; const int expectSize = 280;
std::vector<uint8_t> expect = Array<uint8_t> 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, 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, 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 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); CHECK(mem == expect);
std::filesystem::remove(twoFilesZipPath);
} }