fix: preserve root translation in GLB output
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
parent
8218502ee8
commit
b454f18a02
@ -14,6 +14,18 @@
|
||||
#include <map>
|
||||
#include <StepData_StepModel.hxx>
|
||||
#include <XSControl_WorkSession.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
#include <gp_Trsf.hxx>
|
||||
#include <TopLoc_Location.hxx>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "custom_progress.h"
|
||||
#include "geometry_iterator.h"
|
||||
@ -21,6 +33,206 @@
|
||||
#include "step_tree.h"
|
||||
#include "../../config_structs.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
gp_Vec extract_root_translation(const TDF_LabelSequence& labels)
|
||||
{
|
||||
const double tolerance = Precision::Confusion();
|
||||
for (Standard_Integer i = 1; i <= labels.Length(); ++i)
|
||||
{
|
||||
const TDF_Label& label = labels.Value(i);
|
||||
const TopLoc_Location location = XCAFDoc_ShapeTool::GetLocation(label);
|
||||
if (location.IsIdentity())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const gp_Trsf trsf = location.Transformation();
|
||||
const gp_XYZ translation = trsf.TranslationPart();
|
||||
if (translation.SquareModulus() > tolerance * tolerance)
|
||||
{
|
||||
return gp_Vec(translation);
|
||||
}
|
||||
}
|
||||
|
||||
return gp_Vec(0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void apply_world_translation_to_glb(const std::filesystem::path& glb_path, const gp_Vec& translation)
|
||||
{
|
||||
if (translation.SquareMagnitude() <= Precision::Confusion() * Precision::Confusion())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr std::uint32_t kGlbMagic = 0x46546C67; // 'glTF'
|
||||
constexpr std::uint32_t kJsonChunkType = 0x4E4F534A; // 'JSON'
|
||||
|
||||
std::ifstream input(glb_path, std::ios::binary);
|
||||
if (!input)
|
||||
{
|
||||
std::cerr << "Failed to open GLB for world translation update: " << glb_path << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
input.seekg(0, std::ios::end);
|
||||
const std::streamsize file_size = input.tellg();
|
||||
input.seekg(0, std::ios::beg);
|
||||
|
||||
if (file_size < static_cast<std::streamsize>(sizeof(std::uint32_t) * 3 + sizeof(std::uint32_t) * 2))
|
||||
{
|
||||
std::cerr << "GLB file too small: " << glb_path << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<char> data(static_cast<std::size_t>(file_size));
|
||||
input.read(data.data(), file_size);
|
||||
input.close();
|
||||
|
||||
auto read_u32 = [](const char* ptr)
|
||||
{
|
||||
std::uint32_t value;
|
||||
std::memcpy(&value, ptr, sizeof(std::uint32_t));
|
||||
return value;
|
||||
};
|
||||
|
||||
const std::uint32_t magic = read_u32(data.data());
|
||||
if (magic != kGlbMagic)
|
||||
{
|
||||
std::cerr << "Not a GLB file: " << glb_path << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t json_chunk_header_offset = sizeof(std::uint32_t) * 3;
|
||||
const std::uint32_t json_length = read_u32(data.data() + json_chunk_header_offset);
|
||||
const std::uint32_t json_type = read_u32(data.data() + json_chunk_header_offset + sizeof(std::uint32_t));
|
||||
|
||||
if (json_type != kJsonChunkType)
|
||||
{
|
||||
std::cerr << "Unexpected first chunk type in GLB: " << glb_path << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t json_data_offset = json_chunk_header_offset + sizeof(std::uint32_t) * 2;
|
||||
if (json_data_offset + json_length > data.size())
|
||||
{
|
||||
std::cerr << "Invalid JSON chunk length in GLB: " << glb_path << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string json_text(data.data() + json_data_offset, data.data() + json_data_offset + json_length);
|
||||
|
||||
nlohmann::json document;
|
||||
try
|
||||
{
|
||||
document = nlohmann::json::parse(json_text);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
std::cerr << "Failed to parse GLB JSON chunk: " << ex.what() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!document.contains("scenes") || !document["scenes"].is_array() || document["scenes"].empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int default_scene_index = 0;
|
||||
if (document.contains("scene") && document["scene"].is_number_integer())
|
||||
{
|
||||
default_scene_index = document["scene"].get<int>();
|
||||
}
|
||||
|
||||
if (default_scene_index < 0 || default_scene_index >= static_cast<int>(document["scenes"].size()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json& scene = document["scenes"][default_scene_index];
|
||||
if (!scene.contains("nodes") || !scene["nodes"].is_array() || scene["nodes"].empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!document.contains("nodes") || !document["nodes"].is_array())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const nlohmann::json& node_index_value = scene["nodes"].front();
|
||||
if (!node_index_value.is_number_integer())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int world_node_index = node_index_value.get<int>();
|
||||
if (world_node_index < 0 || world_node_index >= static_cast<int>(document["nodes"].size()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json& world_node = document["nodes"][world_node_index];
|
||||
if (!world_node.contains("translation") || !world_node["translation"].is_array() || world_node["translation"].size() != 3)
|
||||
{
|
||||
world_node["translation"] = nlohmann::json::array({0.0, 0.0, 0.0});
|
||||
}
|
||||
|
||||
constexpr double epsilon = 1.0e-8;
|
||||
if (std::abs(world_node["translation"][0].get<double>()) > epsilon ||
|
||||
std::abs(world_node["translation"][1].get<double>()) > epsilon ||
|
||||
std::abs(world_node["translation"][2].get<double>()) > epsilon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
world_node["translation"][0] = translation.X();
|
||||
world_node["translation"][1] = translation.Y();
|
||||
world_node["translation"][2] = translation.Z();
|
||||
|
||||
std::string updated_json = document.dump();
|
||||
const std::size_t padded_json_length = (updated_json.size() + 3) & ~std::size_t(3);
|
||||
updated_json.resize(padded_json_length, ' ');
|
||||
|
||||
const std::size_t json_chunk_total_size = sizeof(std::uint32_t) * 2 + padded_json_length;
|
||||
const std::size_t remaining_size = data.size() - (json_data_offset + json_length);
|
||||
|
||||
const std::size_t new_file_size = sizeof(std::uint32_t) * 3 + json_chunk_total_size + remaining_size;
|
||||
|
||||
std::vector<char> output;
|
||||
output.reserve(new_file_size);
|
||||
|
||||
// Header (magic, version, length)
|
||||
output.insert(output.end(), data.begin(), data.begin() + sizeof(std::uint32_t) * 3);
|
||||
|
||||
const std::uint32_t new_length = static_cast<std::uint32_t>(new_file_size);
|
||||
std::memcpy(output.data() + sizeof(std::uint32_t) * 2, &new_length, sizeof(std::uint32_t));
|
||||
|
||||
// JSON chunk header
|
||||
const std::uint32_t new_json_length = static_cast<std::uint32_t>(padded_json_length);
|
||||
const char* json_length_bytes = reinterpret_cast<const char*>(&new_json_length);
|
||||
output.insert(output.end(), json_length_bytes, json_length_bytes + sizeof(std::uint32_t));
|
||||
|
||||
const char* chunk_type_bytes = reinterpret_cast<const char*>(&kJsonChunkType);
|
||||
output.insert(output.end(), chunk_type_bytes, chunk_type_bytes + sizeof(std::uint32_t));
|
||||
|
||||
// JSON chunk data
|
||||
output.insert(output.end(), updated_json.begin(), updated_json.end());
|
||||
|
||||
// Remaining chunks (BIN + others)
|
||||
output.insert(output.end(), data.begin() + json_data_offset + json_length, data.end());
|
||||
|
||||
std::ofstream out_file(glb_path, std::ios::binary | std::ios::trunc);
|
||||
if (!out_file)
|
||||
{
|
||||
std::cerr << "Failed to open GLB for writing updated translation: " << glb_path << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
out_file.write(output.data(), static_cast<std::streamsize>(output.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void convert_stp_to_glb(const GlobalConfig& config)
|
||||
{
|
||||
// Initialize the STEPCAFControl_Reader
|
||||
@ -77,6 +289,8 @@ void convert_stp_to_glb(const GlobalConfig& config)
|
||||
shapeTool->GetShapes(labelSeq);
|
||||
std::vector<ProcessResult> failed_nodes;
|
||||
|
||||
const gp_Vec root_translation = extract_root_translation(labelSeq);
|
||||
|
||||
std::cout << "Beginning tessellation of shapes: " << labelSeq.Length() << "\n";
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
for (Standard_Integer i = 1; i <= labelSeq.Length(); ++i)
|
||||
@ -119,6 +333,8 @@ void convert_stp_to_glb(const GlobalConfig& config)
|
||||
{
|
||||
throw std::runtime_error("Error writing GLB file");
|
||||
}
|
||||
|
||||
apply_world_translation_to_glb(config.glbFile, root_translation);
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration<double>(stop - start).count();
|
||||
std::cout << "Write complete in " << std::fixed << std::setprecision(2) << duration << " seconds" << "\n";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user