diff --git a/src/domain.rs b/src/domain.rs index 7b3cbe0..369e16e 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -286,6 +286,96 @@ pub struct DomainSpecDocument { pub root: DomainNodeDocument, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DomainBuildRequestDocument { + pub schema_version: u32, + pub domain: DomainSpecDocument, + pub query: DomainQueryDocument, +} + +impl DomainBuildRequestDocument { + pub const CURRENT_SCHEMA_VERSION: u32 = 1; + + pub fn build(&self) -> DomainChunkBuild { + build_domain_chunks(&self.domain.compile_root(), &self.query.to_query()) + } + + pub fn to_json_pretty(&self) -> Result { + serde_json::to_string_pretty(self) + } + + pub fn from_json(source: &str) -> Result { + serde_json::from_str(source) + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DomainQueryDocument { + pub camera_position: [f32; 3], + pub frustum_min: [f32; 3], + pub frustum_max: [f32; 3], + pub viewport_height_px: f32, + pub vertical_fov_radians: f32, + pub target_error: f32, + pub triangle_budget: usize, + pub collider_budget: usize, + pub semantic_filter: Vec, + pub requested_chunk_keys: Vec, + pub dirty_domain_keys: Vec, +} + +impl DomainQueryDocument { + pub fn from_query(query: &DomainQuery) -> Self { + Self { + camera_position: vec3_to_array(query.camera_position), + frustum_min: vec3_to_array(query.frustum.min), + frustum_max: vec3_to_array(query.frustum.max), + viewport_height_px: query.viewport_height_px, + vertical_fov_radians: query.vertical_fov_radians, + target_error: query.target_error, + triangle_budget: query.triangle_budget, + collider_budget: query.collider_budget, + semantic_filter: query.semantic_filter.clone(), + requested_chunk_keys: query + .requested_chunk_keys + .iter() + .map(|key| key.0.clone()) + .collect(), + dirty_domain_keys: query + .dirty_domain_keys + .iter() + .map(|key| key.0.clone()) + .collect(), + } + } + + pub fn to_query(&self) -> DomainQuery { + DomainQuery { + camera_position: array_to_vec3(self.camera_position), + frustum: Aabb::new( + array_to_vec3(self.frustum_min), + array_to_vec3(self.frustum_max), + ), + viewport_height_px: self.viewport_height_px, + vertical_fov_radians: self.vertical_fov_radians, + target_error: self.target_error, + triangle_budget: self.triangle_budget, + collider_budget: self.collider_budget, + semantic_filter: self.semantic_filter.clone(), + requested_chunk_keys: self + .requested_chunk_keys + .iter() + .map(|key| DomainKey::new(key.clone())) + .collect(), + dirty_domain_keys: self + .dirty_domain_keys + .iter() + .map(|key| DomainKey::new(key.clone())) + .collect(), + } + } +} + impl DomainSpecDocument { pub const CURRENT_SCHEMA_VERSION: u32 = 1; @@ -1696,6 +1786,24 @@ mod tests { ); } + #[test] + fn domain_build_request_document_round_trips_worker_input() { + let request = DomainBuildRequestDocument { + schema_version: DomainBuildRequestDocument::CURRENT_SCHEMA_VERSION, + domain: DomainSpecDocument::from_spec(&ragnarok_column_spec()), + query: DomainQueryDocument::from_query(&query(0.01, 10_000)), + }; + let json = request.to_json_pretty().unwrap(); + let decoded = DomainBuildRequestDocument::from_json(&json).unwrap(); + let build = decoded.build(); + assert_eq!( + decoded.schema_version, + DomainBuildRequestDocument::CURRENT_SCHEMA_VERSION + ); + assert!(!build.chunks.is_empty()); + assert_eq!(build.manifest.chunks.len(), build.chunks.len()); + } + #[test] fn requested_chunk_bypasses_frustum_and_budget() { let fixture = ragnarok_column_fixture(); diff --git a/src/lib.rs b/src/lib.rs index 826d2d8..e335620 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,13 +21,14 @@ pub use assembler::{Assembler, BuildOutput, BuildReport, BuildWarning}; pub use brush::{Aabb, Brush, BrushId, BrushOp, MaterialId, PolygonCategory, Primitive}; pub use convex::{CategorizedPolygons, ConvexPolygon, ConvexSolid, Plane, PolygonRouteScratch}; pub use domain::{ - ClaimLoweringTarget, ContributionManifest, ContributionRow, CsgClaimLowering, DomainChunkBuild, - DomainFrame, DomainKey, DomainKind, DomainNode, DomainNodeDocument, DomainNodeSpec, - DomainQuery, DomainSpecDocument, DomainSummary, FeatureClaim, FeatureClaimDocument, - FeatureClaimKind, FeatureClaimSpec, FeatureLoweringPolicy, FieldEncoding, FieldLayer, - SelectedCut, SelectedCutManifest, TriangleChunk, TriangleChunkDocument, TriangleChunkManifest, - build_domain_chunks, lower_feature_claims_to_csg_tree, lower_selected_cut, - lower_selected_cut_chunks, ragnarok_column_fixture, ragnarok_column_spec, select_domain_cut, + ClaimLoweringTarget, ContributionManifest, ContributionRow, CsgClaimLowering, + DomainBuildRequestDocument, DomainChunkBuild, DomainFrame, DomainKey, DomainKind, DomainNode, + DomainNodeDocument, DomainNodeSpec, DomainQuery, DomainQueryDocument, DomainSpecDocument, + DomainSummary, FeatureClaim, FeatureClaimDocument, FeatureClaimKind, FeatureClaimSpec, + FeatureLoweringPolicy, FieldEncoding, FieldLayer, SelectedCut, SelectedCutManifest, + TriangleChunk, TriangleChunkDocument, TriangleChunkManifest, build_domain_chunks, + lower_feature_claims_to_csg_tree, lower_selected_cut, lower_selected_cut_chunks, + ragnarok_column_fixture, ragnarok_column_spec, select_domain_cut, }; pub use dsl::LevelDsl; pub use frontier::{DemandFrontier, DemandPair, DirtyDemandFrontier};