research(intervals): OBJ export switch and a D3D11 fly-through viewer for the spike
Viewer generates the world at startup with the spike's code (timing shown), free-fly camera, wireframe, near/far toggles, seed cycling. Benchmark hashes unchanged (e5ad62303de5f641). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
61b1c9c329
commit
8d29c09512
2 files changed
+190
-2
No files matched your search
@@ -10,6 +10,9 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
@@ -1323,11 +1326,104 @@ void print_axis_stats(const char* name,const AxisStats& s) {
|
||||
s.runsP50,s.runsP95,s.runsMax);
|
||||
}
|
||||
|
||||
bool write_obj(const std::filesystem::path& path, const std::vector<Vertex>& vertices,
|
||||
const std::vector<uint32_t>& indices, const std::vector<PageRange>* pages = nullptr) {
|
||||
std::ofstream file(path, std::ios::binary | std::ios::trunc);
|
||||
if (!file) return false;
|
||||
file << "# VoxelForge interval spike; Unreal centimetres, Z up, relative to spike world origin\n";
|
||||
std::vector<uint32_t> remap;
|
||||
std::vector<uint8_t> used;
|
||||
if (pages) {
|
||||
remap.assign(vertices.size(), 0);
|
||||
used.assign(vertices.size(), 0);
|
||||
for (const PageRange& page : *pages) {
|
||||
if (static_cast<uint64_t>(page.indexStart) + page.indexCount > indices.size()) return false;
|
||||
for (uint32_t i = 0; i < page.indexCount; ++i) {
|
||||
const uint32_t index = indices[page.indexStart + i];
|
||||
if (index >= vertices.size()) return false;
|
||||
used[index] = 1;
|
||||
}
|
||||
}
|
||||
uint32_t next = 1;
|
||||
for (size_t i = 0; i < vertices.size(); ++i) if (used[i]) remap[i] = next++;
|
||||
}
|
||||
file << std::fixed << std::setprecision(6);
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
if (pages && !used[i]) continue;
|
||||
file << "v " << vertices[i].x * 100.0f << ' ' << vertices[i].y * 100.0f << ' '
|
||||
<< vertices[i].z * 100.0f << '\n';
|
||||
}
|
||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
||||
if (pages && !used[i]) continue;
|
||||
file << "vn " << vertices[i].nx << ' ' << vertices[i].ny << ' ' << vertices[i].nz << '\n';
|
||||
}
|
||||
auto emit_face = [&](uint32_t a, uint32_t b, uint32_t c) {
|
||||
if (pages) { a = remap[a]; b = remap[b]; c = remap[c]; }
|
||||
file << "f " << a << "//" << a << ' ' << b << "//" << b << ' ' << c << "//" << c << '\n';
|
||||
};
|
||||
if (pages) {
|
||||
for (const PageRange& page : *pages)
|
||||
for (uint32_t i = 0; i < page.indexCount; i += 3)
|
||||
emit_face(indices[page.indexStart + i], indices[page.indexStart + i + 1], indices[page.indexStart + i + 2]);
|
||||
} else {
|
||||
if (indices.size() % 3 != 0) return false;
|
||||
for (size_t i = 0; i < indices.size(); i += 3) {
|
||||
if (indices[i] >= vertices.size() || indices[i + 1] >= vertices.size() || indices[i + 2] >= vertices.size()) return false;
|
||||
emit_face(indices[i] + 1, indices[i + 1] + 1, indices[i + 2] + 1);
|
||||
}
|
||||
}
|
||||
file.flush();
|
||||
return file.good();
|
||||
}
|
||||
|
||||
bool export_obj_files(const std::filesystem::path& directory, const MeshOutput& nearMesh, const FarOutput& farMesh) {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(directory, ec);
|
||||
if (ec) { std::fprintf(stderr, "OBJ export: cannot create %s: %s\n", directory.string().c_str(), ec.message().c_str()); return false; }
|
||||
std::vector<Vertex> nearVertices;
|
||||
nearVertices.reserve(nearMesh.vertices.size());
|
||||
for (const TempVertex& v : nearMesh.vertices) {
|
||||
Vertex local = v.v;
|
||||
local.x -= kWorldOriginCm[0] * 0.01f;
|
||||
local.y -= kWorldOriginCm[1] * 0.01f;
|
||||
local.z -= kWorldOriginCm[2] * 0.01f;
|
||||
nearVertices.push_back(local);
|
||||
}
|
||||
if (!write_obj(directory / "near.obj", nearVertices, nearMesh.indices)) {
|
||||
std::fprintf(stderr, "OBJ export: failed writing %s\n", (directory / "near.obj").string().c_str()); return false;
|
||||
}
|
||||
std::vector<Vertex> farVertices = farMesh.vertices;
|
||||
for (Vertex& v : farVertices) {
|
||||
v.x -= kWorldOriginCm[0] * 0.01f;
|
||||
v.y -= kWorldOriginCm[1] * 0.01f;
|
||||
v.z -= kWorldOriginCm[2] * 0.01f;
|
||||
}
|
||||
if (!write_obj(directory / "far.obj", farVertices, farMesh.indices)) {
|
||||
std::fprintf(stderr, "OBJ export: failed writing %s\n", (directory / "far.obj").string().c_str()); return false;
|
||||
}
|
||||
for (uint32_t pageId = 0; pageId < static_cast<uint32_t>(kPagesPerAxis*kPagesPerAxis*kPagesPerAxis); ++pageId) {
|
||||
std::vector<PageRange> ranges;
|
||||
for (const PageRange& range : nearMesh.pages) if (range.page == pageId) ranges.push_back(range);
|
||||
if (std::none_of(ranges.begin(), ranges.end(), [](const PageRange& range) { return range.indexCount != 0; })) continue;
|
||||
char filename[32]; std::snprintf(filename, sizeof(filename), "near_page_%02u.obj", pageId);
|
||||
if (!write_obj(directory / filename, nearVertices, nearMesh.indices, &ranges)) {
|
||||
std::fprintf(stderr, "OBJ export: failed writing %s\n", (directory / filename).string().c_str()); return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc,char** argv) {
|
||||
const uint64_t seed=argc>1?std::strtoull(argv[1],nullptr,10):1;
|
||||
const int workers=argc>2?std::clamp(std::atoi(argv[2]),1,8):1;
|
||||
uint64_t seed=1; int workers=1; std::filesystem::path exportDirectory; bool exportObj=false;
|
||||
int positional=0;
|
||||
for (int i=1;i<argc;++i) {
|
||||
if (std::strcmp(argv[i], "--export-obj") == 0 && i+1<argc) { exportObj=true; exportDirectory=argv[++i]; }
|
||||
else if (positional++ == 0) seed=std::strtoull(argv[i],nullptr,10);
|
||||
else if (positional == 2) workers=std::clamp(std::atoi(argv[i]),1,8);
|
||||
else { std::fprintf(stderr,"Usage: interval_spike.exe [seed] [workers] [--export-obj directory]\n"); return 1; }
|
||||
}
|
||||
double corpusMs=0,intervalMs=0,activeMs=0,meshMs=0,farMs=0,validationMs=0;
|
||||
Clock::time_point t0=Clock::now();
|
||||
Corpus corpus;
|
||||
@@ -1387,5 +1483,6 @@ int main(int argc,char** argv) {
|
||||
std::printf("\"output_bytes_total\":%llu,\"combined_hash\":\"%016llx\",\"working_set_bytes\":%llu,\"validation_pass\":%s}\n",
|
||||
static_cast<unsigned long long>(totalBytes),static_cast<unsigned long long>(combinedHash),
|
||||
static_cast<unsigned long long>(process_working_set()),validationPass?"true":"false");
|
||||
if (exportObj && !export_obj_files(exportDirectory, mesh, farOutput)) return 3;
|
||||
return validationPass?0:2;
|
||||
}
|
||||
Reference in new issue
Block a user