2026-04-12 09:09:32 +10:00
|
|
|
/********************************************************************************
|
|
|
|
|
* *
|
|
|
|
|
* 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 <http://www.gnu.org/licenses/>. *
|
|
|
|
|
* *
|
|
|
|
|
********************************************************************************/
|
|
|
|
|
|
2026-05-19 18:58:17 +10:00
|
|
|
// v13 layout (all multi-byte fields native-endian; endianness marker in header).
|
2026-04-12 20:10:20 +10:00
|
|
|
//
|
2026-04-28 19:40:00 +10:00
|
|
|
// SidecarHeader (12 bytes)
|
2026-04-12 20:10:20 +10:00
|
|
|
//
|
2026-04-14 21:54:15 +10:00
|
|
|
// uint32_t num_vertex_bytes
|
2026-04-22 11:58:04 +10:00
|
|
|
// uint8_t[] vertex data (12 B/vertex: pos u16x3 + oct-normal i8x2 + rgba8)
|
2026-04-12 20:10:20 +10:00
|
|
|
// uint32_t num_indices
|
|
|
|
|
// uint32_t[] index data (mesh-local indices; base_vertex applied at draw time)
|
|
|
|
|
//
|
|
|
|
|
// uint32_t num_meshes
|
|
|
|
|
// MeshInfo[num_meshes]
|
|
|
|
|
//
|
|
|
|
|
// uint32_t num_instances
|
2026-05-19 18:58:17 +10:00
|
|
|
// InstanceCpu[num_instances] (already sorted by mesh_id; v13 layout)
|
2026-05-02 19:08:45 +10:00
|
|
|
//
|
|
|
|
|
// uint32_t has_coordinate_operation (v11+)
|
|
|
|
|
// double[16] coordinate_operation_meters (v11+; column-major)
|
|
|
|
|
// double project_length_to_meters (v11+)
|
|
|
|
|
// double map_unit_to_meters (v11+)
|
2026-04-12 20:10:20 +10:00
|
|
|
//
|
|
|
|
|
// uint32_t num_elements
|
|
|
|
|
// PackedElementInfo[num_elements]
|
|
|
|
|
// uint32_t string_table_bytes
|
|
|
|
|
// char[string_table_bytes]
|
2026-04-12 09:09:32 +10:00
|
|
|
|
2026-04-12 19:53:06 +10:00
|
|
|
#include "SidecarCache.h"
|
2026-04-12 09:09:32 +10:00
|
|
|
|
2026-04-12 20:10:20 +10:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
|
|
struct SidecarHeader {
|
|
|
|
|
uint32_t magic;
|
|
|
|
|
uint32_t version;
|
|
|
|
|
uint32_t endian;
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-22 11:58:04 +10:00
|
|
|
// foo.ifc -> foo.ifcview
|
|
|
|
|
// foo.ifcdb/ -> foo.ifcview
|
|
|
|
|
// foo.ifcdb -> foo.ifcview
|
|
|
|
|
// foo (no ext) -> foo.ifcview
|
2026-04-12 20:10:20 +10:00
|
|
|
static std::string sidecarPath(const std::string& ifc_path) {
|
2026-04-22 11:58:04 +10:00
|
|
|
std::string p = ifc_path;
|
|
|
|
|
while (!p.empty() && (p.back() == '/' || p.back() == '\\')) p.pop_back();
|
|
|
|
|
auto slash = p.find_last_of("/\\");
|
|
|
|
|
auto dot = p.find_last_of('.');
|
|
|
|
|
std::string stem = (dot != std::string::npos &&
|
|
|
|
|
(slash == std::string::npos || dot > slash))
|
|
|
|
|
? p.substr(0, dot)
|
|
|
|
|
: p;
|
|
|
|
|
return stem + ".ifcview";
|
2026-04-12 20:10:20 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
static bool writeVec(FILE* f, const std::vector<T>& v) {
|
|
|
|
|
uint32_t n = static_cast<uint32_t>(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<typename T>
|
|
|
|
|
static bool readVec(FILE* f, std::vector<T>& 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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 11:58:04 +10:00
|
|
|
bool writeSidecar(const std::string& ifc_path, const SidecarData& data) {
|
2026-04-12 20:10:20 +10:00
|
|
|
std::string path = sidecarPath(ifc_path);
|
|
|
|
|
FILE* f = fopen(path.c_str(), "wb");
|
|
|
|
|
if (!f) return false;
|
|
|
|
|
|
2026-04-28 19:40:00 +10:00
|
|
|
SidecarHeader hdr = { SIDECAR_MAGIC, SIDECAR_VERSION, SIDECAR_ENDIAN };
|
2026-04-12 20:10:20 +10:00
|
|
|
if (fwrite(&hdr, sizeof(hdr), 1, f) != 1) { fclose(f); return false; }
|
|
|
|
|
|
|
|
|
|
if (!writeVec(f, data.vertices)) { fclose(f); return false; }
|
|
|
|
|
if (!writeVec(f, data.indices)) { fclose(f); return false; }
|
|
|
|
|
if (!writeVec(f, data.meshes)) { fclose(f); return false; }
|
|
|
|
|
if (!writeVec(f, data.instances)) { fclose(f); return false; }
|
2026-05-02 19:08:45 +10:00
|
|
|
|
|
|
|
|
// v11 georef block (148 B).
|
|
|
|
|
if (fwrite(&data.has_coordinate_operation, 4, 1, f) != 1) { fclose(f); return false; }
|
|
|
|
|
if (fwrite(data.coordinate_operation_meters,
|
|
|
|
|
sizeof(double), 16, f) != 16) { fclose(f); return false; }
|
|
|
|
|
if (fwrite(&data.project_length_to_meters,
|
|
|
|
|
sizeof(double), 1, f) != 1) { fclose(f); return false; }
|
|
|
|
|
if (fwrite(&data.map_unit_to_meters,
|
|
|
|
|
sizeof(double), 1, f) != 1) { fclose(f); return false; }
|
|
|
|
|
|
2026-04-12 20:10:20 +10:00
|
|
|
if (!writeVec(f, data.elements)) { fclose(f); return false; }
|
|
|
|
|
|
|
|
|
|
uint32_t stbl_len = static_cast<uint32_t>(data.string_table.size());
|
|
|
|
|
if (fwrite(&stbl_len, 4, 1, f) != 1) { fclose(f); return false; }
|
|
|
|
|
if (stbl_len > 0 && fwrite(data.string_table.data(), 1, stbl_len, f) != stbl_len) {
|
|
|
|
|
fclose(f); return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
2026-04-12 09:09:32 +10:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 11:58:04 +10:00
|
|
|
std::optional<SidecarData> readSidecar(const std::string& ifc_path) {
|
2026-04-12 20:10:20 +10:00
|
|
|
std::string path = sidecarPath(ifc_path);
|
|
|
|
|
FILE* f = fopen(path.c_str(), "rb");
|
|
|
|
|
if (!f) return std::nullopt;
|
|
|
|
|
|
|
|
|
|
auto fail = [&]() -> std::optional<SidecarData> { fclose(f); return std::nullopt; };
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
SidecarData data;
|
|
|
|
|
if (!readVec(f, data.vertices)) return fail();
|
|
|
|
|
if (!readVec(f, data.indices)) return fail();
|
|
|
|
|
if (!readVec(f, data.meshes)) return fail();
|
|
|
|
|
if (!readVec(f, data.instances)) return fail();
|
2026-05-02 19:08:45 +10:00
|
|
|
|
|
|
|
|
// v11 georef block.
|
|
|
|
|
if (fread(&data.has_coordinate_operation, 4, 1, f) != 1) return fail();
|
|
|
|
|
if (fread(data.coordinate_operation_meters,
|
|
|
|
|
sizeof(double), 16, f) != 16) return fail();
|
|
|
|
|
if (fread(&data.project_length_to_meters,
|
|
|
|
|
sizeof(double), 1, f) != 1) return fail();
|
|
|
|
|
if (fread(&data.map_unit_to_meters,
|
|
|
|
|
sizeof(double), 1, f) != 1) return fail();
|
|
|
|
|
|
2026-04-12 20:10:20 +10:00
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
return data;
|
2026-04-12 09:09:32 +10:00
|
|
|
}
|