From c16e794ba3faf1a3ba8b00ff582a9f0d865e6795 Mon Sep 17 00:00:00 2001 From: irev-dev Date: Mon, 20 Apr 2026 03:45:16 +1000 Subject: [PATCH 01/10] firts pass at sending spline to the enigine properly, i.e. not approximating with a series of stright lines, with region selection working using arc approximation as well --- modeling-cmds/openapi/api.json | 52 ++++++++++++++++++++++++++++------ modeling-cmds/src/shared.rs | 16 ++++++++++- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/modeling-cmds/openapi/api.json b/modeling-cmds/openapi/api.json index 1b08c872..9ea01bcf 100644 --- a/modeling-cmds/openapi/api.json +++ b/modeling-cmds/openapi/api.json @@ -8730,6 +8730,46 @@ "type" ] }, + { + "description": "A general exact curve segment. Start at the current path \"pen\" and end at the final control point. In the first pass this is used for non-rational open-uniform spline curves.", + "type": "object", + "properties": { + "degree": { + "description": "Degree of the curve.", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "points": { + "description": "Ordered control points for the curve. The final 3D point becomes the new path \"pen\" position.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Point4d" + } + }, + "rational": { + "description": "Whether to use the homogeneous `w` component as a rational weight.", + "type": "boolean" + }, + "relative": { + "description": "Whether or not this curve is a relative offset", + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "curve" + ] + } + }, + "required": [ + "degree", + "points", + "rational", + "relative", + "type" + ] + }, { "description": "Adds a tangent arc from current pen position with the given radius and angle.", "type": "object", @@ -9090,20 +9130,16 @@ "type": "object", "properties": { "w": { - "type": "number", - "format": "float" + "$ref": "#/components/schemas/LengthUnit" }, "x": { - "type": "number", - "format": "float" + "$ref": "#/components/schemas/LengthUnit" }, "y": { - "type": "number", - "format": "float" + "$ref": "#/components/schemas/LengthUnit" }, "z": { - "type": "number", - "format": "float" + "$ref": "#/components/schemas/LengthUnit" } }, "required": [ diff --git a/modeling-cmds/src/shared.rs b/modeling-cmds/src/shared.rs index 91cf8a22..6b7cc77f 100644 --- a/modeling-cmds/src/shared.rs +++ b/modeling-cmds/src/shared.rs @@ -845,7 +845,7 @@ pub enum CameraDragInteractionType { /// A segment of a path. /// Paths are composed of many segments. -#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] #[serde(rename_all = "snake_case", tag = "type")] #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] @@ -887,6 +887,20 @@ pub enum PathSegment { ///Whether or not this bezier is a relative offset relative: bool, }, + /// A general exact curve segment. + /// Start at the current path "pen" and end at the final control point. + /// In the first pass this is used for non-rational open-uniform spline curves. + Curve { + /// Degree of the curve. + degree: u32, + /// Whether to use the homogeneous `w` component as a rational weight. + rational: bool, + /// Ordered control points for the curve. + /// The final 3D point becomes the new path "pen" position. + points: Vec>, + ///Whether or not this curve is a relative offset + relative: bool, + }, /// Adds a tangent arc from current pen position with the given radius and angle. TangentialArc { /// Radius of the arc. From e3ae6ecfdf34f8de52d657ea2f22fa33d7ff22e4 Mon Sep 17 00:00:00 2001 From: irev-dev Date: Tue, 28 Apr 2026 09:30:10 +1000 Subject: [PATCH 02/10] Implement local WebGPU render packet pipeline and interaction proxy --- modeling-cmds/src/format/mod.rs | 5 ++ modeling-cmds/src/format/render_packet.rs | 60 +++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 modeling-cmds/src/format/render_packet.rs diff --git a/modeling-cmds/src/format/mod.rs b/modeling-cmds/src/format/mod.rs index 1bc48903..cd8324d0 100644 --- a/modeling-cmds/src/format/mod.rs +++ b/modeling-cmds/src/format/mod.rs @@ -17,6 +17,8 @@ pub mod gltf; pub mod obj; /// The PLY Polygon File Format. pub mod ply; +/// KittyCAD render packet format for browser-side rendering. +pub mod render_packet; /// ISO 10303-21 (STEP) format. pub mod step; /// **ST**ereo**L**ithography format. @@ -57,6 +59,8 @@ pub enum OutputFormat3d { Obj(obj::export::Options), /// The PLY Polygon File Format. Ply(ply::export::Options), + /// KittyCAD render packet format for browser-side rendering. + RenderPacket(render_packet::export::Options), /// ISO 10303-21 (STEP) format. Step(step::export::Options), /// **ST**ereo**L**ithography format. @@ -209,6 +213,7 @@ impl From for FileExportFormat { OutputFormat3d::Gltf(_) => Self::Gltf, OutputFormat3d::Obj(_) => Self::Obj, OutputFormat3d::Ply(_) => Self::Ply, + OutputFormat3d::RenderPacket(_) => Self::Glb, OutputFormat3d::Step(_) => Self::Step, OutputFormat3d::Stl(_) => Self::Stl, } diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs new file mode 100644 index 00000000..a2fdc916 --- /dev/null +++ b/modeling-cmds/src/format/render_packet.rs @@ -0,0 +1,60 @@ +use bon::Builder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Export models as a KittyCAD render packet for browser-side rendering. +pub mod export { + use super::*; + + /// Options for exporting a render packet. + #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, Builder)] + #[serde(rename = "RenderPacketExportOptions")] + #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] + #[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] + #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] + pub struct Options {} +} + +/// A render packet that can be consumed directly by the browser renderer. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacket { + /// Individual renderable face primitives with stable engine metadata. + pub primitives: Vec, +} + +/// A single renderable primitive in a render packet. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketPrimitive { + /// Packed xyz positions in OpenGL/glTF coordinates and meters. + pub positions: Vec, + + /// Packed xyz normals in OpenGL/glTF coordinates. + pub normals: Vec, + + /// Triangle indices into the primitive-local position buffer. + pub indices: Vec, + + /// Stable engine object UUID for the parent solid. + pub object_id: uuid::Uuid, + + /// Stable engine body UUID for the parent solid. + pub body_id: uuid::Uuid, + + /// Stable engine face UUID. + pub face_id: uuid::Uuid, + + /// The face index within the solid at export time. + pub face_index: u32, + + /// The primitive index within the generated packet. + pub primitive_index: u32, +} From 4e4f02e3a2041e88eb9c7c2ee8f97e220706960a Mon Sep 17 00:00:00 2001 From: irev-dev Date: Tue, 28 Apr 2026 12:29:52 +1000 Subject: [PATCH 03/10] Add render-packet edges and trim-aware face rendering --- modeling-cmds/src/format/render_packet.rs | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs index a2fdc916..fa8e6473 100644 --- a/modeling-cmds/src/format/render_packet.rs +++ b/modeling-cmds/src/format/render_packet.rs @@ -25,6 +25,9 @@ pub mod export { pub struct RenderPacket { /// Individual renderable face primitives with stable engine metadata. pub primitives: Vec, + + /// Explicit engine-authored edge polylines with stable engine metadata. + pub edges: Vec, } /// A single renderable primitive in a render packet. @@ -40,9 +43,15 @@ pub struct RenderPacketPrimitive { /// Packed xyz normals in OpenGL/glTF coordinates. pub normals: Vec, + /// Packed uv coordinates in normalized face-local trim space. + pub uvs: Vec, + /// Triangle indices into the primitive-local position buffer. pub indices: Vec, + /// Trim loops in the same normalized face-local uv space as `uvs`. + pub trim_loops: Vec, + /// Stable engine object UUID for the parent solid. pub object_id: uuid::Uuid, @@ -58,3 +67,37 @@ pub struct RenderPacketPrimitive { /// The primitive index within the generated packet. pub primitive_index: u32, } + +/// A single trim loop in normalized face-local uv space. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketTrimLoop { + /// Packed uv positions for a closed trim loop in normalized face-local space. + pub positions: Vec, +} + +/// A single renderable edge polyline in a render packet. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketEdge { + /// Packed xyz positions in OpenGL/glTF coordinates and meters. + pub positions: Vec, + + /// Stable engine object UUID for the parent solid. + pub object_id: uuid::Uuid, + + /// Stable engine body UUID for the parent solid. + pub body_id: uuid::Uuid, + + /// Stable engine edge UUID. + pub edge_id: uuid::Uuid, + + /// The edge index within the solid at export time. + pub edge_index: u32, +} From ceb02899715823565568593c6837e6cb2c82503c Mon Sep 17 00:00:00 2001 From: irev-dev Date: Wed, 29 Apr 2026 20:53:40 +1000 Subject: [PATCH 04/10] Add sketch segments and trim-aware rendering to WebGPU render packets --- modeling-cmds/src/format/render_packet.rs | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs index fa8e6473..7d2e4549 100644 --- a/modeling-cmds/src/format/render_packet.rs +++ b/modeling-cmds/src/format/render_packet.rs @@ -28,6 +28,9 @@ pub struct RenderPacket { /// Explicit engine-authored edge polylines with stable engine metadata. pub edges: Vec, + + /// Explicit engine-authored sketch/wire polylines with sketch-local metadata. + pub sketches: Vec, } /// A single renderable primitive in a render packet. @@ -101,3 +104,29 @@ pub struct RenderPacketEdge { /// The edge index within the solid at export time. pub edge_index: u32, } + +/// A single renderable sketch/wire polyline in a render packet. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketSketchSegment { + /// Packed xyz positions in OpenGL/glTF coordinates and meters. + pub positions: Vec, + + /// Stable engine scene object UUID for the sketch owner. + pub sketch_id: uuid::Uuid, + + /// Stable artifact/entity UUID for the underlying sketch segment, when available. + pub segment_id: Option, + + /// Curve index within the sketch path or hole loop. + pub segment_index: u32, + + /// Hole index when this segment belongs to a hole loop. + pub hole_index: Option, + + /// Whether the underlying curve is closed. + pub closed: bool, +} From e7d6fe15f6f0d94a1152d2dddfae14e7544c355e Mon Sep 17 00:00:00 2001 From: irev-dev Date: Mon, 6 Jul 2026 16:32:59 +1000 Subject: [PATCH 05/10] Add sketch-region render packets and local WebGPU region selection Extend the render-packet pipeline to export implicit sketch regions from the engine and consume them in the local WebGPU scene. Engine-side: - add sketch-region loop/query accessors to the C++/Rust bridge - export sketch regions in render packets with plane basis, loops, holes, region IDs, parent IDs, and query points Modeling-app side: - add frontend render-packet bindings for regions and enriched sketch segments - render regions as invisible planar meshes that highlight on hover/selection - resolve region selection through the local proxy and answer region_get_query_point locally - update region selection mapping so command flows like extrude treat local region picks the same way as streamed engine selection This continues the render-packet path beyond faces, edges, and sketch segments so sketch-region interactions work in local WebGPU mode. --- modeling-cmds/src/format/render_packet.rs | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs index 7d2e4549..cb23ac29 100644 --- a/modeling-cmds/src/format/render_packet.rs +++ b/modeling-cmds/src/format/render_packet.rs @@ -2,6 +2,8 @@ use bon::Builder; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use crate::shared::{Point2d, Point3d}; + /// Export models as a KittyCAD render packet for browser-side rendering. pub mod export { use super::*; @@ -31,6 +33,9 @@ pub struct RenderPacket { /// Explicit engine-authored sketch/wire polylines with sketch-local metadata. pub sketches: Vec, + + /// Explicit engine-authored sketch regions with stable engine metadata. + pub regions: Vec, } /// A single renderable primitive in a render packet. @@ -130,3 +135,49 @@ pub struct RenderPacketSketchSegment { /// Whether the underlying curve is closed. pub closed: bool, } + +/// A single implicit sketch region in a render packet. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketRegion { + /// The sketch plane origin in OpenGL/glTF world coordinates and meters. + pub plane_origin: Point3d, + + /// The sketch plane x axis in OpenGL/glTF world coordinates. + pub plane_x_axis: Point3d, + + /// The sketch plane y axis in OpenGL/glTF world coordinates. + pub plane_y_axis: Point3d, + + /// The explicit outer loop for this region in sketch-plane local meters. + pub outer_loop: RenderPacketRegionLoop, + + /// Hole loops for this region in sketch-plane local meters. + pub hole_loops: Vec, + + /// Stable engine scene object UUID for the sketch owner. + pub sketch_id: uuid::Uuid, + + /// Stable engine region UUID. + pub region_id: uuid::Uuid, + + /// Stable engine parent path UUID. This mirrors `entity_get_parent_id`. + pub parent_id: uuid::Uuid, + + /// A point guaranteed to be inside the region, in engine millimeters. + pub query_point: Point2d, +} + +/// A single 2D loop in sketch-plane local meters. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketRegionLoop { + /// Packed xy positions in sketch-plane local meters. + pub positions: Vec, +} From 32a38f711ca5b8ff118d758e7ce539a17617f09d Mon Sep 17 00:00:00 2001 From: Andrew Varga Date: Wed, 26 Aug 2026 17:54:03 +0200 Subject: [PATCH 06/10] add boyd materials to render packet --- modeling-cmds/src/format/render_packet.rs | 26 ++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs index cb23ac29..78af730d 100644 --- a/modeling-cmds/src/format/render_packet.rs +++ b/modeling-cmds/src/format/render_packet.rs @@ -2,7 +2,7 @@ use bon::Builder; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::shared::{Point2d, Point3d}; +use crate::shared::{Color, Point2d, Point3d}; /// Export models as a KittyCAD render packet for browser-side rendering. pub mod export { @@ -25,6 +25,10 @@ pub mod export { #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] #[serde(rename_all = "camelCase")] pub struct RenderPacket { + /// PBR materials keyed by the body IDs referenced by renderable primitives. + #[serde(default)] + pub body_materials: Vec, + /// Individual renderable face primitives with stable engine metadata. pub primitives: Vec, @@ -38,6 +42,26 @@ pub struct RenderPacket { pub regions: Vec, } +/// The PBR material assigned to a body in a render packet. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketBodyMaterial { + /// Stable engine body UUID used by renderable primitives. + pub body_id: uuid::Uuid, + + /// Front-face PBR base color, including opacity in the alpha channel. + pub base_color: Color, + + /// PBR metallic factor in the range 0 to 1. + pub metalness: f32, + + /// PBR roughness factor in the range 0 to 1. + pub roughness: f32, +} + /// A single renderable primitive in a render packet. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] From bacd58d7262ec4b0f95e0eb01cf2f1be67a09532 Mon Sep 17 00:00:00 2001 From: Andrew Varga Date: Fri, 28 Aug 2026 10:17:36 +0200 Subject: [PATCH 07/10] binary render packet --- modeling-cmds/src/format/render_packet.rs | 122 ++++++++++++++++++---- 1 file changed, 104 insertions(+), 18 deletions(-) diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs index 78af730d..95cab94d 100644 --- a/modeling-cmds/src/format/render_packet.rs +++ b/modeling-cmds/src/format/render_packet.rs @@ -4,6 +4,18 @@ use serde::{Deserialize, Serialize}; use crate::shared::{Color, Point2d, Point3d}; +/// Magic bytes at the start of every binary render packet. +pub const RENDER_PACKET_MAGIC: [u8; 8] = *b"ZOORPKT\0"; + +/// Current binary render packet version. +pub const RENDER_PACKET_VERSION: u32 = 1; + +/// Size of the fixed binary render packet header in bytes. +pub const RENDER_PACKET_HEADER_SIZE: usize = 16; + +/// Number of bytes in one interleaved surface vertex. +pub const RENDER_PACKET_VERTEX_STRIDE: u32 = 32; + /// Export models as a KittyCAD render packet for browser-side rendering. pub mod export { use super::*; @@ -18,13 +30,22 @@ pub mod export { pub struct Options {} } -/// A render packet that can be consumed directly by the browser renderer. +/// Metadata for a binary render packet consumed by the browser renderer. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] #[serde(rename_all = "camelCase")] pub struct RenderPacket { + /// Binary render packet format version. + pub version: u32, + + /// Layout of the interleaved surface vertex buffer. + pub vertex_layout: RenderPacketVertexLayout, + + /// Byte ranges relative to the beginning of the packet's binary payload. + pub sections: RenderPacketBinarySections, + /// PBR materials keyed by the body IDs referenced by renderable primitives. #[serde(default)] pub body_materials: Vec, @@ -42,6 +63,59 @@ pub struct RenderPacket { pub regions: Vec, } +/// Layout of a surface vertex in the interleaved vertex section. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketVertexLayout { + /// Distance in bytes between consecutive vertices. + pub stride: u32, + /// Byte offset of the float32x3 position. + pub position_offset: u32, + /// Byte offset of the float32x3 normal. + pub normal_offset: u32, + /// Byte offset of the float32x2 UV coordinate. + pub uv_offset: u32, +} + +/// A byte range within the binary payload following the JSON metadata. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketBinarySection { + /// Byte offset relative to the beginning of the binary payload. + pub byte_offset: u32, + /// Length of the section in bytes. + pub byte_length: u32, +} + +/// Packed numerical sections stored after the JSON metadata. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub struct RenderPacketBinarySections { + /// Interleaved surface vertices. + pub vertices: RenderPacketBinarySection, + /// Packet-wide uint32 primitive index for each surface vertex. + pub primitive_indices: RenderPacketBinarySection, + /// Global uint32 triangle indices. + pub indices: RenderPacketBinarySection, + /// Packed float32x2 trim-loop points. + pub trim_points: RenderPacketBinarySection, + /// Packed float32x3 edge-polyline points. + pub edge_points: RenderPacketBinarySection, + /// Packed float32x3 sketch-segment points. + pub sketch_points: RenderPacketBinarySection, + /// Packed float32x2 sketch-region points. + pub region_points: RenderPacketBinarySection, +} + /// The PBR material assigned to a body in a render packet. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] @@ -62,24 +136,24 @@ pub struct RenderPacketBodyMaterial { pub roughness: f32, } -/// A single renderable primitive in a render packet. +/// A single renderable face range in a render packet. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] #[serde(rename_all = "camelCase")] pub struct RenderPacketPrimitive { - /// Packed xyz positions in OpenGL/glTF coordinates and meters. - pub positions: Vec, + /// First vertex in the packet-wide interleaved vertex section. + pub first_vertex: u32, - /// Packed xyz normals in OpenGL/glTF coordinates. - pub normals: Vec, + /// Number of vertices belonging to this face. + pub vertex_count: u32, - /// Packed uv coordinates in normalized face-local trim space. - pub uvs: Vec, + /// First index in the packet-wide index section. + pub first_index: u32, - /// Triangle indices into the primitive-local position buffer. - pub indices: Vec, + /// Number of indices belonging to this face. + pub index_count: u32, /// Trim loops in the same normalized face-local uv space as `uvs`. pub trim_loops: Vec, @@ -107,8 +181,11 @@ pub struct RenderPacketPrimitive { #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] #[serde(rename_all = "camelCase")] pub struct RenderPacketTrimLoop { - /// Packed uv positions for a closed trim loop in normalized face-local space. - pub positions: Vec, + /// First float32x2 point in the packet-wide trim-point section. + pub first_point: u32, + + /// Number of points in this closed trim loop. + pub point_count: u32, } /// A single renderable edge polyline in a render packet. @@ -118,8 +195,11 @@ pub struct RenderPacketTrimLoop { #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] #[serde(rename_all = "camelCase")] pub struct RenderPacketEdge { - /// Packed xyz positions in OpenGL/glTF coordinates and meters. - pub positions: Vec, + /// First float32x3 point in the packet-wide edge-point section. + pub first_point: u32, + + /// Number of points in this edge polyline. + pub point_count: u32, /// Stable engine object UUID for the parent solid. pub object_id: uuid::Uuid, @@ -141,8 +221,11 @@ pub struct RenderPacketEdge { #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] #[serde(rename_all = "camelCase")] pub struct RenderPacketSketchSegment { - /// Packed xyz positions in OpenGL/glTF coordinates and meters. - pub positions: Vec, + /// First float32x3 point in the packet-wide sketch-point section. + pub first_point: u32, + + /// Number of points in this sketch segment. + pub point_count: u32, /// Stable engine scene object UUID for the sketch owner. pub sketch_id: uuid::Uuid, @@ -202,6 +285,9 @@ pub struct RenderPacketRegion { #[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] #[serde(rename_all = "camelCase")] pub struct RenderPacketRegionLoop { - /// Packed xy positions in sketch-plane local meters. - pub positions: Vec, + /// First float32x2 point in the packet-wide region-point section. + pub first_point: u32, + + /// Number of points in this loop. + pub point_count: u32, } From 7be51941c6d71b37ec8fee271ce6f47c51b5a0ef Mon Sep 17 00:00:00 2001 From: Andrew Varga Date: Sat, 29 Aug 2026 20:58:34 +0200 Subject: [PATCH 08/10] implement trim mode options --- modeling-cmds/src/format/render_packet.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs index 95cab94d..cca57e04 100644 --- a/modeling-cmds/src/format/render_packet.rs +++ b/modeling-cmds/src/format/render_packet.rs @@ -8,7 +8,7 @@ use crate::shared::{Color, Point2d, Point3d}; pub const RENDER_PACKET_MAGIC: [u8; 8] = *b"ZOORPKT\0"; /// Current binary render packet version. -pub const RENDER_PACKET_VERSION: u32 = 1; +pub const RENDER_PACKET_VERSION: u32 = 2; /// Size of the fixed binary render packet header in bytes. pub const RENDER_PACKET_HEADER_SIZE: usize = 16; @@ -136,6 +136,21 @@ pub struct RenderPacketBodyMaterial { pub roughness: f32, } +/// How the browser renderer should evaluate a face's trim loops. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] +#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))] +#[cfg_attr(not(feature = "unstable_exhaustive"), non_exhaustive)] +#[serde(rename_all = "camelCase")] +pub enum RenderPacketTrimMode { + /// The face has no trim loops and needs no fragment clipping. + None, + /// Use a small classifier mask and evaluate uncertain pixels analytically. + Hybrid, + /// Use a high-resolution binary mask without analytical fallback. + ComplexTexture, +} + /// A single renderable face range in a render packet. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] @@ -155,6 +170,12 @@ pub struct RenderPacketPrimitive { /// Number of indices belonging to this face. pub index_count: u32, + /// Index of this face's material in `RenderPacket::body_materials`. + pub material_index: u32, + + /// Trim implementation selected by the geometry engine. + pub trim_mode: RenderPacketTrimMode, + /// Trim loops in the same normalized face-local uv space as `uvs`. pub trim_loops: Vec, From 317da830f48ffa403d2ff7ae6cea34cf345bd10b Mon Sep 17 00:00:00 2001 From: Andrew Varga Date: Sat, 29 Aug 2026 21:03:51 +0200 Subject: [PATCH 09/10] version increment not needed --- modeling-cmds/src/format/render_packet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modeling-cmds/src/format/render_packet.rs b/modeling-cmds/src/format/render_packet.rs index cca57e04..8ceee899 100644 --- a/modeling-cmds/src/format/render_packet.rs +++ b/modeling-cmds/src/format/render_packet.rs @@ -8,7 +8,7 @@ use crate::shared::{Color, Point2d, Point3d}; pub const RENDER_PACKET_MAGIC: [u8; 8] = *b"ZOORPKT\0"; /// Current binary render packet version. -pub const RENDER_PACKET_VERSION: u32 = 2; +pub const RENDER_PACKET_VERSION: u32 = 1; /// Size of the fixed binary render packet header in bytes. pub const RENDER_PACKET_HEADER_SIZE: usize = 16; From ce8564b6a7a3b7d7671780f4123d719843a27897 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Sep 2026 14:12:14 +0000 Subject: [PATCH 10/10] Update OpenAPI specification --- modeling-cmds/openapi/api.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/modeling-cmds/openapi/api.json b/modeling-cmds/openapi/api.json index 375da24f..82b92b75 100644 --- a/modeling-cmds/openapi/api.json +++ b/modeling-cmds/openapi/api.json @@ -8463,6 +8463,21 @@ "units" ] }, + { + "description": "KittyCAD render packet format for browser-side rendering.", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "render_packet" + ] + } + }, + "required": [ + "type" + ] + }, { "description": "ISO 10303-21 (STEP) format.", "type": "object",