A Windows release build has no console, so when the GPU could not be
set up or the app panicked, the window never showed or vanished without
a word. Such errors now also go to errors.log beside the settings and,
on the main thread, to a message box that names the log file.
Fixes#106.
The browser build threw unsaved changes away on New project, Open,
Close, Open sample project or a dropped file, and when the tab closed.
They now wait on the desktop's Save / Don't save / Cancel dialog (Save
downloads the project), and closing or reloading the tab with unsaved
changes makes the browser ask first.
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.
The app opens on the sample project, which left no obvious place to pick a
project. A start window now offers New project, Open, the recent projects and
Continue with the sample project, with Show at startup (also Settings >
Interface > Start window). It opens at launch in the desktop and web apps, but
not when a file is given on the command line or the app runs with --control.
Labels translated into the other nine UI languages; a UI test covers New
project, Continue and the startup setting.
A malformed first line got a "bad JSON" reply but kept the connection open until
the auth deadline, unlike `texturecraft-cli serve` and the documented
one-attempt rule. It now closes the connection while unauthenticated; after
auth a malformed line still only gets the error reply. Adds a regression test
and doc comments on the touched functions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
A .gltf whose buffers or images are separate files, and a loose USD layer whose
UsdPreviewSurface textures sit next to it, now open, reimport and bake as high polys with
those files, and projects keep them: `self_contained_mesh` folds a .gltf's files in as data
URIs and packages a loose layer with its textures as a .usdz, so the stored mesh imports
from its bytes alone. Only files inside the mesh's folder are read (no absolute paths, URLs
or `..` above it); missing ones are named in the log.
- formats: `self_contained_mesh`, `read_mesh_file` and `MeshFile`; `import_mesh` uses them;
the glTF reader no longer reads the disk itself; USD texture lookups go through one
`Assets` source (usdz package or loose files)
- engine: file.open, file.new, file.reimportMesh, bake.highPoly.add and bake.cage.set read
meshes this way; a failed reimport no longer leaves the session pointing at the new file
- automation: mesh_import / mesh.import read the files under the same workspace rules
- CLI `info` reports a .gltf with external buffers
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>
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>