An effect's context menu copies, cuts and pastes it, and Ctrl+C,
Ctrl+X and Ctrl+V act on a selected effect (pasting a copied one above
the selected effect, or on top of the selected content or mask).
Painted strokes come along, resampled to the target set; generators
and other one-stack effects go to their stack, and a fill reading an
anchor point needs one below its new layer. effect.copy, effect.cut
and effect.paste share the layer clipboard.
The History panel lists every undone step too (dimmed, next first) and
a click on any step goes back or forward to it, the current one
highlighted. edit.undo and edit.redo take {steps?} for the same jump.
Each mesh map slot of the Texture Set Settings takes an image resource:
drop one from the Assets panel on the slot, or pick it from the slot's
menu, which also clears the slot. The image is resampled to the size of
the set's other mesh maps (or its bake size), its values read as data,
a normal map in the project's normal format.
textureSet.meshMap.set {ts?, map, image} and
textureSet.meshMap.clear {ts?, map} give agents the same.
Effect rows in the layer stack can be dragged within their stack (a
drop lands above the row, or below it from its lower half), and their
context menu duplicates an effect just above itself, painted pixels
included. effect.move {effect, index} and effect.duplicate {effect}
give agents the same.
The Levels effect's properties now show a histogram of its input, the
mask below it or (on layer content) a channel picked above it, with the
input range marked. effect.histogram {effect, channel?, bins?} gives
agents the same counts. Large sets are sampled (at most 256 tiles), so
the cost stays flat up to 8K.
The baked maps are most of a sample project's size, so a 2048 sample is
as large as a 1024 one; the usage and the control protocol now say so.
Fixes#66.
resource.text draws one or more lines of text, shaped with harfrust (kerning,
ligatures, joined scripts) from the font's outlines (skrifa) and filled by a
non-zero scanline rasterizer, cropped to the ink, as an image resource: white
for the brush alpha, or a color for a decal. text.fonts lists the installed
families and styles, read from each font file's name table only (246 families in
80 ms here); Ubuntu Light and Hack from the UI's built-in fonts are always there
and are the fallback in the browser.
- Properties > Alpha > Text: the text, a font and style, Use as brush alpha and
Place as decal (in the tool's base color).
- Image alphas keep their proportions (their longer side spans the stamp), so a
line of text stays a line; image thumbnails are letterboxed and show alpha.
- harfrust 0.12 and skrifa 0.44 are the versions egui already builds, so the
lockfile gains no packages.
Fixes#40
The desktop app wrote its control token to <temp>/texturecraft-control-<port>.token with
default permissions (world-readable in a shared /tmp), from a hash-based generator, and
compared it non-constant-time with no connection limit. Any local user could read the token
and drive the app, whose command.run takes host paths.
- Token files are created new (remove, then O_EXCL) with mode 0600, so a planted file or
symlink can neither keep its permissions nor redirect the write; one that cannot be
replaced disables the server with a hint to set TEXTURECRAFT_CONTROL_TOKEN.
- Clients only read a token file that is a regular file owned by them with no group or
other permissions, opened without following symlinks or blocking on a FIFO.
- The app's control server uses the shared security module: OS-random 256-bit token,
constant-time compare, validated and trimmed env token, bounded lines and at most 16
connections.
- Connections must authenticate within 10 s (AuthDeadline) and get one attempt.
- The app and `serve --port` bind before writing the token file, so a server whose port is
busy no longer replaces the running server's token.
Regression tests cover owner-only files over an existing open file, symlink and FIFO
handling, the auth deadline, the connection limit and the app's token checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the crate and material-ball renders in Made in TextureCraft with two
original props, modelled procedurally and textured with TextureCraft's own
materials, masks and mesh-map generators, path traced at 2048 samples.
texturecraft-cli render gains --camera <name> to look through a camera saved
in the project (camera.add), so a render can use an art-directed framing
(distance, target, field of view) rather than only yaw and pitch.
The top menu strip is now the window's title bar wherever the app draws the
frame (crates/ui-egui/src/titlebar.rs), using craft-window for the decision
and for the draggable empty area:
- Windows: no OS frame. App icon at the very left, the menus, empty space
that moves the window (double-click maximizes), the mode label, then
minimize, maximize/restore and close (46 pt wide, crisp pixel-snapped
glyphs, close turns red on hover, dimmed while inactive). Invisible edge
and corner zones resize the window. A per-frame guard ported from
VectorCraft keeps the undecorated window borderless and a maximized one
clear of the taskbar (winsafe, no unsafe), and the taskbar gets the big
app icon.
- macOS: the traffic lights over the strip (full-size content view, hidden
title), centred on it with eframe's traffic-light API, the app icon after
their inset, and AppKit's movable flag synced by craft-window.
- Linux and the BSDs: craft-window's probe decides; where the compositor
leaves the frame to the app (GNOME) the strip draws the buttons in the
desktop's button layout.
- Wherever the system draws the frame (and on the web) the strip stays the
menu bar, with the app icon at its left. No line under the strip.
TEXTURECRAFT_TITLEBAR=app|system overrides the decision. Settings ›
Interface › System title bar (applies on restart) brings the OS frame back;
it is read from the saved settings before the window opens and is settable
over the control channel as prefs.system_title_bar. ui.inspect reports
titleBar. New strings are translated in every language, and the offscreen
snapshots render the app-drawn and macOS variants.
egui, eframe and the craft-ui pin go to the suite's egui 0.37 forks and craft-libs f8f8829c.
The panels dock through craft_ui::docking (native floating windows on desktop) in place of
egui_dock, which has no egui 0.37 release. The viewport is the protected central canvas.
Layouts saved before load into the new workspace; an unreadable one becomes the default.
egui-phosphor has no egui 0.37 release, so a copy with the egui requirement raised is vendored.
Agents in a workspace can now do everything the file menu and bake panel do with mesh files:
add high polys and the bake cage, export the mesh with its textures, and reimport the
project's mesh. Paths stay below the read and write roots, as for the other workspace tools.
- MCP tools bake_mesh_add {path, cage?}, mesh_export {path, format?} and
mesh_reimport {path?, preserveStrokes?}; control-channel methods bake.mesh.add, mesh.export
and mesh.reimport (headless); bridge mode reimports the app's own copy
- engine: Session::add_bake_mesh, Session::mesh_export and Session::reimport_mesh, shared by
bake.highPoly.add, bake.cage.set, file.exportMesh and file.reimportMesh (the cage now logs
textures it could not read, like high polys)
- docs/control-protocol.md lists the new tools and methods
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four brightness levels (Medium dark by default) and seven accent colours (Copper, from the app
icon, by default) in Settings › Interface, saved in the UI preferences and settable over the
control channel. Panels, dock groups and bars are rounded cards; tabs are pills, buttons flat and
rounded, popups shadowed; spacing follows VectorCraft. The 3D and 2D views keep a neutral studio
grey with overlay colours that stay readable on it. Widget colours come from craft-ui.
Also: `ui.resize` sets the window size for recordings and screenshots, and TEXTURECRAFT_NO_PREFS
keeps scripted runs off the user's saved layout and settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Strokes pre-blend their commit while painting and recompose committed tiles at low
priority: the 8K stroke-end frame drops from 30-42 ms to 12-19 ms
- Texture set resize runs in the background (8K: 3 s to 0.5 s, UV padding map from island
borders only)
- Decal move/scale/rotate handles; FBX and USD high-poly textures for the color map;
viewport displacement from the height channel; screen-space refraction of the scene
- CI pinned to Rust 1.96.0; FreeBSD VM sized for hosted runners; release builds embed
craft-fonts; file associations for FBX, COLLADA, USD and Alembic on every platform
- README, release checklist, NOTICE and attribution brought up to date
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Baking: per-map ray settings (AO, thickness, bent normals), GPU ray-traced baking with CPU
fallback (~12x), color map from textured glTF high polys
- Files: Alembic (Ogawa) and FBX 6 import, per-tile UDIM resolution, converting a set to UV
tiles, project templates, preserving painted strokes on mesh reimport
- UI: translations (French, German, Spanish, Portuguese, Italian, Japanese, Chinese, Korean)
with a live language picker and CJK fonts, closed panels reopen where they were, viewport
texture resolution setting, frame-budgeted scene sync for 8K sets
- Web: loading screen, WebGL2 notice, app-captured shortcuts, touch navigation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rendering mode traces on the GPU (compute shaders, ~25x the CPU tracer) with CPU fallback;
CLI/MCP render --samples uses the GPU unless --cpu
- Depth of field in the viewport and the path tracer; multiple cameras with glTF import;
transmission/IOR, coat normal and screen-space subsurface blur; exact 8-bit normal decode
- Filter library (18 filters incl. warp, color balance, gradient map, posterize, MatFinish)
with a Filters shelf tab; geometry decals; clone from the current layer; anchors inside a
content stack; instances across texture-set resolutions; drag saved smart materials
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- UV tiles: auto-detected UDIM texture sets as one atlas per set, padding kept inside tiles,
one file per tile with the $udim export token, builtin:udim_plane
- USD: usda/usdc/usdz import (Crate reader with LZ4) and export with UsdPreviewSurface
- Shader lobes: clear coat, sheen, anisotropy and subsurface scattering channels in the
viewport and the path tracer; car paint, velvet, brushed steel, skin and wax materials;
glTF clearcoat/sheen export
- Color swatch popups no longer stay open
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Compositor caches the stack below and replays what lies above the layer being painted,
also inside folders: painting under a smart material is ~10-25x faster per frame and the
stroke commit 12-30x faster, bit-identical to a full composite
- Paint shows in the frame it is drawn; parallel stamp rasterizing and stroke commit;
camera visibility reused between strokes
- 67 procedural brush alphas with an Alphas shelf tab; drag materials, smart masks and
alphas from the shelf onto the layer stack, layers and viewport
- Export output template editor: user presets with per-channel packing, converted maps,
import/export as JSON, validated at load
- Shift+right drag turns the environment; S+drag no longer orbits while moving the stencil
- Frame profiler and scripted paint test on the control channel (debug.frames, debug.paintTest)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An open-source, clean-room implementation of a Substance 3D Painter-style texturing
workflow, written in pure Rust with egui and wgpu.
- Layer stack with 32 blend modes, masks, generators, content effects and anchor points
- Painting, projection, smudge, clone, path, lasso fill, particle brush and quick mask
- 43 procedural materials, 17 smart materials and 10 smart masks
- Mesh map baking with high-poly, cage and match-by-name support
- PBR viewport with HDR environments, TAA and a path-traced Rendering mode
- glTF, FBX, COLLADA, OBJ, PLY and STL import; texture export presets; OBJ and GLB export
- Dockable Painter-style UI, shortcut editor, web build, MCP server and CLI
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>