/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see . *
* *
********************************************************************************/
#include "SidecarCache.h"
#include
#include
// Binary layout (all multi-byte fields native-endian):
//
// SidecarHeader (16 bytes)
// uint64_t source_file_size
//
// uint32_t num_vertices (count of floats)
// float[num_vertices] vertex data
//
// uint32_t num_indices
// uint32_t[num_indices] index data
//
// uint32_t num_draw_infos
// ObjectDrawInfo[N] draw info array
//
// uint32_t num_elements
// PackedElementInfo[N] element records
// uint32_t string_table_bytes
// char[string_table_bytes]
//
// uint32_t num_bvh_models
// for each model:
// uint32_t model_id
// uint32_t num_nodes
// BvhNode[num_nodes]
// uint32_t num_object_indices
// uint32_t[num_object_indices]
struct SidecarHeader {
uint32_t magic;
uint32_t version;
uint32_t endian;
uint32_t reserved;
};
static std::string sidecarPath(const std::string& ifc_path) {
return ifc_path + ".ifcview";
}
template
static bool writeVec(FILE* f, const std::vector& v) {
uint32_t n = static_cast(v.size());
if (fwrite(&n, 4, 1, f) != 1) return false;
if (n > 0 && fwrite(v.data(), sizeof(T), n, f) != n) return false;
return true;
}
template
static bool readVec(FILE* f, std::vector& v) {
uint32_t n;
if (fread(&n, 4, 1, f) != 1) return false;
v.resize(n);
if (n > 0 && fread(v.data(), sizeof(T), n, f) != n) return false;
return true;
}
bool writeSidecar(const std::string& ifc_path,
const SidecarData& data,
uint64_t ifc_file_size) {
std::string path = sidecarPath(ifc_path);
FILE* f = fopen(path.c_str(), "wb");
if (!f) return false;
// Header
SidecarHeader hdr = { SIDECAR_MAGIC, SIDECAR_VERSION, SIDECAR_ENDIAN, 0 };
fwrite(&hdr, sizeof(hdr), 1, f);
fwrite(&ifc_file_size, 8, 1, f);
// Geometry
if (!writeVec(f, data.vertices)) { fclose(f); return false; }
if (!writeVec(f, data.indices)) { fclose(f); return false; }
// Draw info
if (!writeVec(f, data.draw_info)) { fclose(f); return false; }
// Elements + string table
if (!writeVec(f, data.elements)) { fclose(f); return false; }
uint32_t stbl_len = static_cast(data.string_table.size());
fwrite(&stbl_len, 4, 1, f);
if (stbl_len > 0) fwrite(data.string_table.data(), 1, stbl_len, f);
// BVH
uint32_t num_bvh_models = data.bvh_set
? static_cast(data.bvh_set->models.size()) : 0;
fwrite(&num_bvh_models, 4, 1, f);
if (data.bvh_set) {
for (const auto& [model_id, mbvh] : data.bvh_set->models) {
fwrite(&model_id, 4, 1, f);
uint32_t nn = static_cast(mbvh.nodes.size());
fwrite(&nn, 4, 1, f);
if (nn > 0) fwrite(mbvh.nodes.data(), sizeof(BvhNode), nn, f);
uint32_t no = static_cast(mbvh.object_indices.size());
fwrite(&no, 4, 1, f);
if (no > 0) fwrite(mbvh.object_indices.data(), 4, no, f);
}
}
fclose(f);
return true;
}
std::optional readSidecar(const std::string& ifc_path,
uint64_t ifc_file_size) {
std::string path = sidecarPath(ifc_path);
FILE* f = fopen(path.c_str(), "rb");
if (!f) return std::nullopt;
auto fail = [&]() -> std::optional { fclose(f); return std::nullopt; };
// Header
SidecarHeader hdr;
if (fread(&hdr, sizeof(hdr), 1, f) != 1) return fail();
if (hdr.magic != SIDECAR_MAGIC ||
hdr.version != SIDECAR_VERSION ||
hdr.endian != SIDECAR_ENDIAN) return fail();
uint64_t stored_size;
if (fread(&stored_size, 8, 1, f) != 1) return fail();
if (stored_size != ifc_file_size) return fail();
SidecarData data;
// Geometry
if (!readVec(f, data.vertices)) return fail();
if (!readVec(f, data.indices)) return fail();
// Draw info
if (!readVec(f, data.draw_info)) return fail();
// Elements + string table
if (!readVec(f, data.elements)) return fail();
uint32_t stbl_len;
if (fread(&stbl_len, 4, 1, f) != 1) return fail();
data.string_table.resize(stbl_len);
if (stbl_len > 0 && fread(data.string_table.data(), 1, stbl_len, f) != stbl_len)
return fail();
// BVH
uint32_t num_bvh_models;
if (fread(&num_bvh_models, 4, 1, f) != 1) return fail();
if (num_bvh_models > 0) {
data.bvh_set = std::make_shared();
for (uint32_t m = 0; m < num_bvh_models; ++m) {
uint32_t model_id;
if (fread(&model_id, 4, 1, f) != 1) return fail();
ModelBvh mbvh;
mbvh.model_id = model_id;
uint32_t nn;
if (fread(&nn, 4, 1, f) != 1) return fail();
mbvh.nodes.resize(nn);
if (nn > 0 && fread(mbvh.nodes.data(), sizeof(BvhNode), nn, f) != nn)
return fail();
uint32_t no;
if (fread(&no, 4, 1, f) != 1) return fail();
mbvh.object_indices.resize(no);
if (no > 0 && fread(mbvh.object_indices.data(), 4, no, f) != no)
return fail();
data.bvh_set->bvh_model_ids.insert(model_id);
data.bvh_set->models[model_id] = std::move(mbvh);
}
}
fclose(f);
return data;
}