Skip to content

Release 0.4.0: Blender parity P1-P15 - #52

Merged
its-janghoon merged 29 commits into
mainfrom
develop
Oct 15, 2026
Merged

its-janghoon merged 29 commits into
mainfrom
develop

Conversation

@its-janghoon

Copy link
Copy Markdown
Member

Summary

Blender parity stages P1–P15 (behaviour port, see ROADMAP.md "Blender parity"), version 0.4.0.

Highlights: Layout/edit-mode/modifiers/outliner parity, textures and shader node editor, shading popover, sculpt, UV editing, animation, armatures/pose/weight paint, render DOF + denoiser, curves/text/metaballs, COLLADA/USDA/ASCII FBX, Geometry Nodes modifier + editor, compositor chain, Preferences with rebindable keymap, Shape Script console and add-ons.

Tested

  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test --workspace (216 tests), release build.
  • On-screen checks on :0 for P13 (Collada import), P14 (Geometry Nodes editor), P15 (Preferences, Scripting console).
  • Blender 5.3 round trip: Shape USDA imports into Blender; Blender's USDA (Z-up) imports into Shape.

Not included

  • Alembic (no Ogawa reader), binary FBX/USD, Rhai scripting (not in the offline registry).

its-janghoon and others added 29 commits October 15, 2026 02:06
…lease job

macOS: packaging/macos/bundle.sh signs the bundle inside-out with the Developer ID identity that
import-cert.sh puts in a temporary keychain (hardened runtime, secure timestamp, empty
entitlements), notarizes and staples it through the App Store Connect API key (Apple-ID fallback),
and ships a drag-to-Applications disk image. Without the secrets it falls back to ad-hoc signing
with a warning, as before.

Windows: the archive is `redrob-shape-<version>-windows-<arch>-portable.zip`, the one zip name
Redrob Launcher installs. scripts/check-asset-names.py refuses any name the launcher's parser
would skip; every packaging job runs it, and the CDN job runs it over the whole set.

release.yml: the CDN job publishes every artifact to cdn.redrob.ai/shape/ (versioned path read
back and compared, then the latest/ alias; a missing bucket uploads nothing) and the publish job
is the organization's release-publish.yml, which writes SHA256SUMS.txt and redrob-launcher.json
and publishes the release as latest.
…ifier

shape_ops::bevel_edges pushes every face corner inward along each of its beveled edges, inserts a
corner that slid along an unbeveled edge into the face across it (no T-junctions), then adds one
quad per beveled edge and one polygon per touched vertex. A strip that ends inside a face (one
beveled edge at a 3-valent vertex) hands its end edge to that face instead of a flat triangle.
Beveling every edge of a cube gives the 26-face chamfered cube; results are checked closed,
oriented outward and Euler-consistent.

UI: Edit > Mesh > Bevel All Edges, Ctrl+B modal over the selected faces' edges, Bevel modifier
with an offset; agent mesh_op `bevel` and add_modifier `bevel`.
macOS Developer ID signing and notarization, launcher-readable asset names, CDN and shared
publish job, bevel.
…gions, tabbed Properties)

Measured against a 1280x800 Blender 5.3 window: top bar 26, header 24, right column 228 with
the Outliner 208 over Properties and its 28px tab strip, status bar 20, toolbar 50; colours
#181818 / #303030 / #3f3f3f / #282828 / #3d3d3d / #1d1d1d, accent #4772b3. Only tabs and
workspace tabs with content are offered. New keys: T, N, Ctrl+Space, Shift+D, H, Alt+H, Alt+A,
Ctrl+I. Properties moved to properties.rs, the layout to layout.rs, the theme to theme.rs.
Local gitflow rules replaced by the org caller; types come from the one org-wide list.
…pad views, Ctrl+J, P

One operator registry (keymap::Op) drives F3 search, the pointer popups and the keymap table.
Box select projects object origins (faces in edit mode) into the drag rectangle. Axis views go
orthographic and orbiting restores perspective, as Blender does. Key matching now requires the
exact modifiers: egui consume_key let plain A swallow Shift+A.
EditSelection now holds vertices, edges and faces and flushes between them as Blender does.
shape-ops gains delete/dissolve, fill, merge, linked/grow/shrink and edge/face loop walks (the
loop follows 4-valent vertices and goes around an n-gon cap at 3-valent ones). The GPU draws
vertex dots as screen-space squares through the line pipeline. A headless wgpu test renders a
frame and reads the pixels back. Key matching: 1/3 are select modes in edit mode, views in object
mode; popup clicks give keyboard focus back so hotkeys keep working.
…n, Ctrl+V/E/F menus

bevel_edges_segments splits every strip into n quads along a quadratic profile through the
original edge and splices the rows into the vertex polygons (and into the host face when a strip
ends inside one). edge_loops orders a selection into chains; bridge_edge_loops pairs two loops by
nearest start and rung length and orients against existing half-edges. spin sweeps edges around an
axis. knife_cut splits a face between two edge points, inserting the points into the neighbours.
The viewport reports whole wheel notches to a running modal when the camera is off.
…Wireframe, Triangulate, Screw, Simple Deform, Displace, Weld

Boolean is BSP clipping of the two triangle soups (volumes of two offset cubes check out: 15 / 7 /
1), stitched by position; coplanar leftovers fall back to a triangle soup. Target modifiers get
the scene through evaluate_in / apply_in and read the target object base mesh in this object
space. Spin now only sweeps open edges, so Screw on a closed mesh is a no-op instead of an error.
…ting, parenting, Set Origin, collections

Modal::transform_with takes TransformSettings (pivot, orientation, proportional) and a camera for
the View orientation; Individual Origins keeps one pivot per object. Modal::edit_transform moves,
rotates or scales the selected vertices with a smooth proportional falloff computed at start and
recomputed on wheel. Scene gains collections, object.collection, cursor and is_visible(), which
the viewport, picking, renderer and exporters now consult.
scene::Texture holds decoded linear pixels (serde-skipped, reloaded from path on open) and samples
with wrap and v-up. The GPU vertex gains a UV and a textured flag; textured triangles are grouped
per texture, uploaded as Rgba8UnormSrgb and bound at group 1 (a 1x1 white texture for the rest).
ShadingMode gains MaterialPreview; Solid no longer reads materials, as in Blender. The path tracer
carries corner UVs per triangle and multiplies the base colour by the texture at the hit.
Headless render tests cover both modes.
scene::nodes holds the NodeTree (typed sockets, one link per input, cycle check) and evaluates it
into the flat Material, so the renderers stay unchanged. ui-egui::node_editor draws the canvas with
the egui painter (nodes, bezier links, sockets) and places egui widgets for unlinked constants.
Viewport hotkeys stand down while the pointer is over the editor, as in Blender. Shading workspace
switches the viewport to Material Preview and shows the editor in place of the agent panel.
SolidOptions on ViewOptions feed a new Globals.shading vec4 and the view matrix; the shader
branches on lighting (studio / procedural matcap / flat) and scales by a derivative-based cavity
term. Four solid pipelines cover cull x depth-write (x-ray). Material gains viewport_color, which
Solid "Material" colour shows, keeping Base Color for Material Preview as Blender does.
ops::sculpt holds the brush maths (falloff, area plane, per-brush dab, symmetry mirroring) on the
half-edge mesh positions; ui-egui::sculpt_mode maps the pointer through pick_detail (or the first
hit depth for Grab) into mesh space, scales the pixel radius by the hit depth, spaces dabs and
records the stroke as one snapshot. Sculpt keys replace the object/modal keys while active.
ops::unwrap splits islands at seams (vertex-pair keys on MeshData), unfolds each island by
rigidly placing faces across shared edges from a seed, groups faces by normal for Smart UV Project
and shelf-packs island boxes into the unit square. ui-egui::uv_editor adds the U popup, Ctrl+E
seam items, the UV Editing workspace and a painter-drawn UV editor.
scene::anim adds FCurve/Action/Timeline with Hermite auto-clamped Bezier evaluation and keying
sets; Scene::insert_keyframe/delete_keyframe/evaluate_animation. ui-egui::timeline draws the
ruler, dope-sheet rows and playhead with the egui painter, drives playback from stable_dt, and
binds Blender's playback keys; object-mode I opens the Insert Keyframe popup.
scene::armature holds Bone/Armature (rest and pose matrix chains, skin matrices), VertexGroup,
automatic_weights (inverse-square distance to bone segments, normalised, 10% cutoff) and skin.
Modifier::Armature evaluates through evaluate_in with the armature object. ui-egui::pose_mode adds
bone picking by projected segment distance, pose drags, the Ctrl+P Set Parent menu and header
widgets; gpu draws octahedral bones. Outliner rows no longer indent inside a horizontal layout.
anim gains BoneLocation/BoneRotation channels evaluated into bone poses; Pose Mode I keys the
selected bones. ui-egui::bone_edit adds Edit Mode for armatures (pick, E, G, X, A) over the rest
bones (Armature.edit_rest). Picks tolerate the octahedron width. Tab moves Pose -> Edit. A status
trace env var makes on-screen driving verifiable.
ViewOptions.weight_paint names the object and group whose weights colour the frame's vertices
(gpu::weight_color ramp); ui-egui::weight_paint paints through pick_detail on the evaluated mesh
with the sculpt falloff and records one snapshot per stroke.
RenderCamera gets a thin lens (with_dof from f-stop + 36 mm sensor), Renderer samples it; a
noise-adaptive bilateral denoiser runs on the final linear frame when Settings.denoise is set, with
PSNR tests against a 256-spp reference. RenderPanel renders animations frame by frame to PNGs and
scales output by Resolution %. Camera DOF props and output fields in the properties tabs.
…mesh

scene::curve holds splines of Bezier points with Blender's handle types, evaluation and a
parallel-transport tube sweep; evaluated_mesh serves the tube to viewport/export. ui-egui::curve_edit
adds the edit mode (pick, G, E, X, V, A) and the Data tab. Tab focus is returned to the viewport at
the end of the frame so hotkeys survive mode toggles.
ops::text parses the bundled UFL font with ttf-parser, flattens outlines and ear-clips glyphs with
holes, extruding with walls wound against the cap boundary; ops::metaball polygonises a Wyvill
field with Kuhn marching tetrahedra and a consistent-orientation pass. Scene stores Text/Metaball
data plus a cached MeshId regenerated on edit; UI adds the Add entries, Data panels and conversion.
format::interchange shares the export records and the Z-up/Y-up basis change; collada.rs builds a
small DOM with quick-xml and reads polylist/triangles/polygons with input offsets; usd.rs parses
nested def blocks and xformOps; fbx.rs parses the generic ASCII node tree and rebuilds
Geometry/Model/Material through OO connections. Alembic is recorded as deferred.
scene::geonodes holds the tree (links replace inputs, cycles refused); ops::geonodes evaluates it by
pulling from Group Output with per-node caching; a Geometry Nodes workspace adds a canvas editor.
scene::compositor + render::composite apply Blur/Bright-Contrast/Glare/Gamma/HSV/Mix/Invert to the
render window image and animation frames, edited in the Render window.
agent::script runs line-per-tool-call scripts over the agent tools; ui-egui prefs.rs holds the
Preferences window (Interface / Keymap / Add-ons) saved under the config dir, with object-mode
hotkeys read through Prefs; console.rs adds the Scripting workspace. Add-ons are *.shape files
with header comments, toggled in Preferences and exposed under the Scripts menu.
Collada raw-string delimiter, quick-xml normalized_value, geonodes BooleanOp conversion, composite
as_chunks, render_panel borrow, serde dep for prefs, geonodes remove_node test.
CHANGELOG 0.4.0 section, workspace version bump, Preferences window centred and non-collapsible,
kittest coverage for Preferences / Geometry Nodes / Scripting workspaces.
@its-janghoon
its-janghoon merged commit 36cfa5b into main Oct 15, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant