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 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.
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>
- 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>
- 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>