diff --git a/Cargo.lock b/Cargo.lock index 8402d71..05cf264 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -181,12 +190,31 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "critical-section" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -208,6 +236,16 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "disqualified" version = "1.0.0" @@ -288,6 +326,27 @@ dependencies = [ "slab", ] +[[package]] +name = "gamecult-geometry-csg" +version = "0.1.0" +dependencies = [ + "approx", + "bevy_math", + "rmp-serde", + "serde", + "sha2", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -597,6 +656,25 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -667,6 +745,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "slab" version = "0.4.12" @@ -764,6 +853,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -800,12 +895,10 @@ dependencies = [ ] [[package]] -name = "vg_csg" -version = "0.1.0" -dependencies = [ - "approx", - "bevy_math", -] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasip2" diff --git a/Cargo.toml b/Cargo.toml index f3a85e8..25ce772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "vg_csg" +name = "gamecult-geometry-csg" version = "0.1.0" edition = "2024" -description = "Lean Bevy-math constructive geometry assembler for VibeGeometry blockouts" +description = "CultGeometry constructive-solid-geometry kernel" license = "MIT" -repository = "https://github.com/GameCult/vg-csg" +repository = "https://github.com/GameCult/GameCult.Geometry.Csg" readme = "README.md" [dependencies] diff --git a/README.md b/README.md index 7b994fd..fa21c06 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ -# vg-csg +# GameCult.Geometry.Csg + +The project is known as **CultGeometry CSG**. Its Rust package is +`gamecult-geometry-csg`, exposed to Rust code as `gamecult_geometry_csg`. Lean constructive geometry tooling for Rust/Bevy-math scene blockouts. -`vg-csg` is the extracted CSG kernel from VibeGeometry. It is not trying to be +CultGeometry CSG is the extracted CSG kernel from VibeGeometry. It is not trying to be a full CAD kernel. It is a small, inspectable machine for agent-authored level and habitat geometry: @@ -39,7 +42,7 @@ experiments/generated/ ```rust use bevy_math::{Quat, Vec3}; -use vg_csg::{LevelDsl, MaterialId}; +use gamecult_geometry_csg::{LevelDsl, MaterialId}; let mut level = LevelDsl::new(); level.solid_box( diff --git a/docs/csg-performance-fixtures.md b/docs/csg-performance-fixtures.md index 9cc0399..667f061 100644 --- a/docs/csg-performance-fixtures.md +++ b/docs/csg-performance-fixtures.md @@ -8,7 +8,7 @@ Run our current kernel: .\tools\run_csg_perf.ps1 ``` -The script builds `vg_csg` in release mode and writes JSONL to +The script builds `gamecult-geometry-csg` in release mode and writes JSONL to `experiments/generated/csg-perf-latest.jsonl`. Each record carries: - `kernel` @@ -19,7 +19,7 @@ The script builds `vg_csg` in release mode and writes JSONL to - emitted triangle, fragment, and warning counts - demand-frontier `candidate_pairs` and `rejected_pairs` -The `vg_csg` fixture measures: +The CultGeometry CSG fixture measures: - `stable`: cached output after warmup - `dirty`: full rebuild through the current ordered assembler @@ -47,7 +47,7 @@ still rebuild. The same wrapper can append reference timings: ```powershell -$env:VIBEGEOMETRY_REFERENCE_CSG_PERF = "path\to\reference-perf.exe" +$env:GAMECULT_GEOMETRY_CSG_REFERENCE_PERF = "path\to\reference-perf.exe" .\tools\run_csg_perf.ps1 ``` @@ -94,7 +94,7 @@ inside/outside/aligned/reverse-aligned categories, crossing polygon splits, and logical routing tables. Use it as readable doctrine. Use the native bridge as the performance target. -`vg_csg` now reports the first demand-frontier counters. These are not yet a +CultGeometry CSG reports the first demand-frontier counters. These are not yet a new kernel path; they expose the ordered brush stream as affected source/operator pairs and rejected bounds pairs so future router/index work can prove it is refusing real work rather than merely rearranging it. diff --git a/docs/realtime-csg-bevy-assembler.md b/docs/realtime-csg-bevy-assembler.md index 0a0fc2c..5e840b8 100644 --- a/docs/realtime-csg-bevy-assembler.md +++ b/docs/realtime-csg-bevy-assembler.md @@ -41,14 +41,14 @@ to Bevy render assets. ## Current Port Shape -`vg-csg` is the first lean Rust organ. +CultGeometry CSG is the lean Rust CSG organ. It uses `bevy_math` for `Vec3`, `Vec2`, and Bevy-compatible spatial types while keeping output as simple arrays: ```rust use bevy_math::Vec3; -use vg_csg::{LevelDsl, MaterialId}; +use gamecult_geometry_csg::{LevelDsl, MaterialId}; let mut level = LevelDsl::new(); level.solid_box( @@ -109,7 +109,7 @@ level script can mix hard architectural cuts with habitat-specific forms: ```rust level.dome_cap_z( "city dome", - vg_csg::DomeCapZSpec { + gamecult_geometry_csg::DomeCapZSpec { center: Vec3::ZERO, radius: 20.0, height: 8.0, @@ -120,7 +120,7 @@ level.dome_cap_z( ); level.floret_arm( "radiator petal", - vg_csg::FloretArmSpec { + gamecult_geometry_csg::FloretArmSpec { anchor: Vec3::new(20.0, 0.0, 0.2), direction: Vec3::X, length: 120.0, @@ -188,7 +188,7 @@ slot for a public-demo/reference harness when one is available. ## Limits -`vg_csg` is not yet a full RealtimeCSG replacement. +CultGeometry CSG is not yet a full RealtimeCSG replacement. - subtractive convex support currently covers axis-aligned and oriented boxes - non-box subtractors are reported and ignored diff --git a/docs/realtime-csg-doctrine.md b/docs/realtime-csg-doctrine.md index c01f0c8..d4f60fb 100644 --- a/docs/realtime-csg-doctrine.md +++ b/docs/realtime-csg-doctrine.md @@ -27,7 +27,7 @@ CSG tree, and emits the pieces that remain visible. It does not treat the final mesh as the authority. The brush tree is the authority; the mesh is cache and evidence. -The highest-value shift for `vg_csg` is therefore: +The highest-value shift for CultGeometry CSG is therefore: ```text from: subtract cutter -> produce new convex fragments -> triangulate fragments @@ -63,7 +63,7 @@ A brush should be authored and cached as a set of planes, not merely as a triangle soup. Brush mesh creation is the act of finding valid intersections between those planes and linking the resulting topology. -Implication for `vg_csg`: add a `ConvexBrush` authored by planes, then derive +Implication for CultGeometry CSG: add a `ConvexBrush` authored by planes, then derive polygons, half-edges, bounds, and render buffers from that. Boxes, cylinders, wedges, stairs, panels, and dome approximations should become constructors for plane sets where possible. @@ -120,7 +120,7 @@ Top-level output policy: - overlapping coplanar surfaces are resolved by tree/order policy so duplicate faces do not z-fight -Implication for `vg_csg`: material propagation should be natural if source +Implication for CultGeometry CSG: material propagation should be natural if source polygons remain source polygons. Generated cut faces need explicit policy: inherit cutter material, source material, or a domain material such as "raw concrete cut." Do not leave that as accidental array order. @@ -138,7 +138,7 @@ allow early-outs before cutting: The optimization post reports a major speedup from branch-specific bounds checks alone. This is not a garnish. Bounds are semantic gates. -Implication for `vg_csg`: every brush, polygon batch, and branch needs cheap +Implication for CultGeometry CSG: every brush, polygon batch, and branch needs cheap bounds. Later BVH, hashed grid, or sweep-and-prune should be measured against the built-in tree culling instead of assumed superior by vibes in a lab coat. @@ -150,7 +150,7 @@ sampled many times. Dreams' prototype refined frustum cells, shortened object lists as cells split, then switched to per-pixel sorted lists that could truncate at the first solid hit. -For `vg_csg`, that translates to consumer-shaped query frontiers: +For CultGeometry CSG, that translates to consumer-shaped query frontiers: - dirty brush movement asks for affected brush pairs, not a world rebuild; - branch evaluation asks for left/right bounds gates before polygon tests; @@ -242,7 +242,7 @@ Use procedural fields and meshes for: The level DSL should let these coexist. Boolean nodes cut space; procedural nodes grow matter and detail around that space. -## Implementation Target For `vg_csg` +## Implementation Target For CultGeometry CSG Near-term kernel shape: @@ -269,7 +269,7 @@ Process brush: emit visible pieces into render mesh ``` -`vg_csg` now exposes the first version of that public tree surface: +CultGeometry CSG exposes the first version of that public tree surface: - `CsgOperationType::{Additive, Subtractive, Intersecting}` with parity-tested ordinals matching the public API @@ -285,7 +285,7 @@ backend grows into the category router. Algorithm migration steps: -1. Add polygon categories and visible/reversed flags to `vg_csg`. +1. Add polygon categories and visible/reversed flags to CultGeometry CSG. 2. Add plane-set brush construction independent of `Aabb`. 3. Add a topology arena with vertices, directed edges, polygons, and polygon plane/material ids. @@ -303,7 +303,7 @@ Algorithm migration steps: ## Acceptance Tests To Add -- Where `vg_csg` exposes the same behavior as public RealtimeCSG/demo APIs, +- Where CultGeometry CSG exposes the same behavior as public RealtimeCSG/demo APIs, maintain exact observable parity fixtures. These should compare public behavior such as category vocabulary, brush plane/polygon counts, split counts, visible/reversed semantics, bounds early-outs, and mesh output diff --git a/examples/csg_perf_fixture.rs b/examples/csg_perf_fixture.rs index bae6a45..2d05b6d 100644 --- a/examples/csg_perf_fixture.rs +++ b/examples/csg_perf_fixture.rs @@ -4,7 +4,7 @@ use std::{ }; use bevy_math::{Quat, Vec3}; -use vg_csg::{ +use gamecult_geometry_csg::{ Aabb, Assembler, BrushOp, DomainQuery, MaterialId, Primitive, lower_selected_cut_chunks, ragnarok_column_fixture, select_domain_cut, }; @@ -86,7 +86,11 @@ impl PerfMode { } } - fn build(self, assembler: &mut Assembler, iteration: usize) -> vg_csg::BuildOutput { + fn build( + self, + assembler: &mut Assembler, + iteration: usize, + ) -> gamecult_geometry_csg::BuildOutput { match self { Self::Stable => assembler.build(), Self::Dirty => { @@ -168,7 +172,7 @@ fn run_case(case: PerfCase, mode: PerfMode) { let p95_ns = percentile_ns(&timings, 95); println!( - "{{\"kernel\":\"vg_csg\",\"mode\":\"{}\",\"scenario\":\"{}\",\"brushes\":{},\"iterations\":{},\"warmup_iterations\":{},\"mean_ns\":{},\"min_ns\":{},\"p50_ns\":{},\"p95_ns\":{},\"max_ns\":{},\"triangles\":{},\"fragments\":{},\"warnings\":{},\"candidate_pairs\":{},\"rejected_pairs\":{},\"reused_mesh\":{},\"used_checkpoint\":{},\"checkpoint_rebuild_from\":{},\"direct_dirty_rebuild\":{}}}", + "{{\"kernel\":\"gamecult_geometry_csg\",\"mode\":\"{}\",\"scenario\":\"{}\",\"brushes\":{},\"iterations\":{},\"warmup_iterations\":{},\"mean_ns\":{},\"min_ns\":{},\"p50_ns\":{},\"p95_ns\":{},\"max_ns\":{},\"triangles\":{},\"fragments\":{},\"warnings\":{},\"candidate_pairs\":{},\"rejected_pairs\":{},\"reused_mesh\":{},\"used_checkpoint\":{},\"checkpoint_rebuild_from\":{},\"direct_dirty_rebuild\":{}}}", mode.as_str(), case.name, case.brushes, @@ -314,7 +318,7 @@ fn run_lod_case(name: &'static str, query: DomainQuery) { let p95_ns = percentile_ns(&timings, 95); println!( - "{{\"kernel\":\"vg_csg\",\"mode\":\"lod_query\",\"scenario\":\"{}\",\"selected_nodes\":{},\"emitted_claims\":{},\"brushes\":{},\"iterations\":{},\"warmup_iterations\":{},\"mean_ns\":{},\"min_ns\":{},\"p50_ns\":{},\"p95_ns\":{},\"max_ns\":{},\"triangles\":{},\"collider_triangles\":{},\"candidate_pairs\":{},\"rejected_pairs\":{}}}", + "{{\"kernel\":\"gamecult_geometry_csg\",\"mode\":\"lod_query\",\"scenario\":\"{}\",\"selected_nodes\":{},\"emitted_claims\":{},\"brushes\":{},\"iterations\":{},\"warmup_iterations\":{},\"mean_ns\":{},\"min_ns\":{},\"p50_ns\":{},\"p95_ns\":{},\"max_ns\":{},\"triangles\":{},\"collider_triangles\":{},\"candidate_pairs\":{},\"rejected_pairs\":{}}}", name, selected_nodes, emitted_claims, diff --git a/examples/csg_room.rs b/examples/csg_room.rs index 9b5e6f2..43253a4 100644 --- a/examples/csg_room.rs +++ b/examples/csg_room.rs @@ -1,5 +1,5 @@ use bevy_math::Vec3; -use vg_csg::{DomeCapZSpec, FloretArmSpec, LevelDsl, MaterialId}; +use gamecult_geometry_csg::{DomeCapZSpec, FloretArmSpec, LevelDsl, MaterialId}; fn main() { let mut level = LevelDsl::new(); diff --git a/examples/dream_machine_level.rs b/examples/dream_machine_level.rs index fe8fc9f..3d0999f 100644 --- a/examples/dream_machine_level.rs +++ b/examples/dream_machine_level.rs @@ -5,7 +5,7 @@ use std::{ }; use bevy_math::{Quat, Vec3}; -use vg_csg::{DomeCapZSpec, LevelDsl, MaterialId, TriangleMesh}; +use gamecult_geometry_csg::{DomeCapZSpec, LevelDsl, MaterialId, TriangleMesh}; const FLOOR: MaterialId = MaterialId(1); const WALL: MaterialId = MaterialId(2); diff --git a/examples/industrial_maintenance_loop.rs b/examples/industrial_maintenance_loop.rs index 44a2fec..6cb5ec0 100644 --- a/examples/industrial_maintenance_loop.rs +++ b/examples/industrial_maintenance_loop.rs @@ -5,7 +5,7 @@ use std::{ }; use bevy_math::Vec3; -use vg_csg::{LevelDsl, MaterialId, TriangleMesh}; +use gamecult_geometry_csg::{LevelDsl, MaterialId, TriangleMesh}; const DECK: MaterialId = MaterialId(21); const SHELL: MaterialId = MaterialId(22); diff --git a/examples/ragnarok_cultcache_export.rs b/examples/ragnarok_cultcache_export.rs index 89aeedf..f63847b 100644 --- a/examples/ragnarok_cultcache_export.rs +++ b/examples/ragnarok_cultcache_export.rs @@ -4,7 +4,7 @@ use std::{ }; use bevy_math::Vec3; -use vg_csg::{ +use gamecult_geometry_csg::{ Aabb, CultGeometryBuildRequest, CultGeometryChunkArtifact, CultGeometryDomainDocument, CultGeometrySelectedCutManifest, DomainQuery, GEOMETRY_BUILD_REQUEST_SCHEMA, GEOMETRY_CHUNK_ARTIFACT_SCHEMA, GEOMETRY_DOMAIN_SCHEMA, GEOMETRY_SELECTED_CUT_SCHEMA, @@ -21,8 +21,11 @@ fn main() -> Result<(), Box> { let spec = ragnarok_column_spec(); let root = spec.compile_root(); let query = high_lod_query(); - let domain = - CultGeometryDomainDocument::from_spec(&spec, "vg-csg", "2026-05-29T00:00:00.0000000Z"); + let domain = CultGeometryDomainDocument::from_spec( + &spec, + "gamecult.geometry.csg", + "2026-05-29T00:00:00.0000000Z", + ); let domain_key = domain.record_key(); let request = CultGeometryBuildRequest::from_query( "ragnarok-column-high", diff --git a/src/cult_geometry.rs b/src/cult_geometry.rs index dfbe75a..09b3517 100644 --- a/src/cult_geometry.rs +++ b/src/cult_geometry.rs @@ -535,8 +535,11 @@ mod tests { #[test] fn domain_document_emits_msgpack_payload_and_stable_key() { let spec = ragnarok_column_spec(); - let document = - CultGeometryDomainDocument::from_spec(&spec, "vg-csg", "2026-05-29T00:00:00Z"); + let document = CultGeometryDomainDocument::from_spec( + &spec, + "gamecult.geometry.csg", + "2026-05-29T00:00:00Z", + ); let payload = document.to_msgpack().unwrap(); let decoded = CultGeometryDomainDocument::from_msgpack(&payload).unwrap(); assert_eq!(document.record_key(), decoded.record_key()); diff --git a/src/lib.rs b/src/lib.rs index 8fb7eda..fdd578f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! Lean constructive geometry tools for VibeGeometry. +//! Constructive-solid-geometry tools for CultGeometry. //! //! This crate borrows RealtimeCSG's useful public shape: ordered brushes, //! operation types, dirtied generations, prefix checkpoints, and rebuildable diff --git a/tests/realtime_csg_parity.rs b/tests/realtime_csg_parity.rs index fde5893..f28d2be 100644 --- a/tests/realtime_csg_parity.rs +++ b/tests/realtime_csg_parity.rs @@ -1,5 +1,5 @@ use bevy_math::{Quat, Vec3}; -use vg_csg::{ +use gamecult_geometry_csg::{ Aabb, Assembler, ConvexSolid, CsgBranchOp, CsgOperationType, CsgTreeArena, MaterialId, PolygonCategory, Primitive, }; diff --git a/tools/run_csg_perf.ps1 b/tools/run_csg_perf.ps1 index ca594bc..04a359b 100644 --- a/tools/run_csg_perf.ps1 +++ b/tools/run_csg_perf.ps1 @@ -1,6 +1,6 @@ param( [string]$OutputPath = ".\experiments\generated\csg-perf-latest.jsonl", - [string]$ReferenceCommand = $env:VIBEGEOMETRY_REFERENCE_CSG_PERF, + [string]$ReferenceCommand = $env:GAMECULT_GEOMETRY_CSG_REFERENCE_PERF, [switch]$UseRealtimeCsgCpp ) @@ -15,17 +15,17 @@ Push-Location $root try { cargo build --release --example csg_perf_fixture | Out-Host if ($LASTEXITCODE -ne 0) { - throw "vg_csg perf fixture build failed with exit code $LASTEXITCODE" + throw "CultGeometry CSG perf fixture build failed with exit code $LASTEXITCODE" } $exe = Join-Path $root "target\release\examples\csg_perf_fixture.exe" if (!(Test-Path $exe)) { - throw "Missing vg_csg perf fixture executable: $exe" + throw "Missing CultGeometry CSG perf fixture executable: $exe" } & $exe | Set-Content -Path $resolvedOutput -Encoding utf8 if ($LASTEXITCODE -ne 0) { - throw "vg_csg perf fixture failed with exit code $LASTEXITCODE" + throw "CultGeometry CSG perf fixture failed with exit code $LASTEXITCODE" } if ($UseRealtimeCsgCpp) { @@ -44,7 +44,7 @@ try { $missing = @{ kernel = "reference" status = "missing" - reason = "Set VIBEGEOMETRY_REFERENCE_CSG_PERF or pass -ReferenceCommand with an executable that emits the same JSONL scenario records." + reason = "Set GAMECULT_GEOMETRY_CSG_REFERENCE_PERF or pass -ReferenceCommand with an executable that emits the same JSONL scenario records." } | ConvertTo-Json -Compress Add-Content -Path $resolvedOutput -Value $missing -Encoding utf8 }