Commit Graph

68 Commits

Author SHA1 Message Date
bflatastic ec30388787 Settings: a data folder on any drive
Autosaves, smart materials, brush and export presets, project templates
and UI layouts always went to Documents/TextureCraft, which fills the
system drive. Settings > Folders > Data folder points them anywhere;
the choice applies on restart, and an export folder still at its
default follows it.

Refs #105.
2026-11-02 14:06:51 +07:00
bflatastic 8c94d053b4 Merge remote-tracking branch 'origin/main' into effects/copy-paste 2026-11-02 12:20:06 +07:00
BFlat e9409b696b Merge pull request #126 from storytold/history/jump-to-step
History: click a step to return to it
2026-11-02 12:19:57 +07:00
bflatastic fbceb95242 Effects: Delete and Ctrl+D act on a selected effect
With an effect selected, Delete removed its whole layer and Ctrl+D
duplicated the layer. Both now act on the effect, like the clipboard
shortcuts; effect commands default to the selected effect.
2026-11-02 12:19:21 +07:00
BFlat 73043fd4b3 Merge pull request #125 from storytold/brush/ctrl-drag
Brush: Ctrl+drag sets size, hardness, flow and angle
2026-11-02 12:14:05 +07:00
bflatastic 4ca9f424f4 Effects: copy, cut and paste between layers
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.
2026-11-02 12:13:39 +07:00
bflatastic f8f241e804 Merge remote-tracking branch 'origin/main' into history/jump-to-step 2026-11-02 12:00:46 +07:00
BFlat 18b1969712 Merge pull request #122 from storytold/effects/reorder-duplicate
Effects: drag to reorder, duplicate
2026-11-02 12:00:32 +07:00
bflatastic b133186641 History: click a step to return to it
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.
2026-11-02 12:00:09 +07:00
bflatastic 410cf3a14f Brush: Ctrl+drag sets size, hardness, flow and angle
Ctrl (Cmd on macOS) + right-drag in the 3D or 2D view changes the
brush size (left/right) and hardness (up/down), Ctrl + left-drag its
flow and angle, instead of orbiting or painting. The brush cursor stays
where the drag began, with the values beside it, and the Brush
properties name the gesture.
2026-11-02 11:48:32 +07:00
bflatastic f6bd07e4d6 Mesh maps: use an image instead of a bake
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.
2026-11-02 11:32:20 +07:00
bflatastic 0d2f1873cd Effects: drag to reorder, duplicate
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.
2026-11-02 11:13:00 +07:00
BFlat b906f41f2e Merge pull request #118 from storytold/filters/histogram-scan-range-shift
Filters: histogram scan, range and shift
2026-11-02 10:38:29 +07:00
BFlat 78e6d7b334 Merge pull request #119 from storytold/materials/ground-and-metals
Materials: platinum, zinc, cobalt, bark, sand, mud, ice, snow and bone
2026-11-02 10:34:38 +07:00
bflatastic 39a2dd9d27 Merge main into filters/histogram-scan-range-shift 2026-11-02 10:27:48 +07:00
BFlat cb1501318f Merge pull request #114 from storytold/levels/histogram
Levels: a histogram of what the effect reads
2026-11-02 10:19:27 +07:00
bflatastic a4cebb790d Merge main into filters/histogram-scan-range-shift 2026-11-02 10:10:15 +07:00
bflatastic 4913131cf7 Materials: platinum, zinc, cobalt, bark, sand, mud, ice, snow and bone
Nine procedural materials for the shelf: three metals from measured
reflectance (zinc with crystalline spangles), bark with deep furrows,
fine-grained sand, mud with glossy wet patches, ice with cloudy air
pockets under transmission, and snow and bone with a little subsurface
scattering.
2026-11-02 10:09:29 +07:00
bflatastic a185e5f951 Filters: histogram scan, range and shift
Three adjustment filters for masks and content: Histogram Scan slides a
ramp through the values (contrast narrows it to a step), Histogram Range
squeezes them towards a position, and Histogram Shift offsets them with
wrap-around. Names and parameters are translated into all nine other UI
languages.
2026-11-02 09:52:09 +07:00
bflatastic 5f890e584e Web: ask to save before replacing the project or closing the tab
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.
2026-11-02 09:32:25 +07:00
bflatastic d736ffa9af Levels: a histogram of what the effect reads
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.
2026-11-02 09:23:59 +07:00
bflatastic 9e65464654 Merge main into docs/roadmap-2026-11-02c 2026-11-02 08:42:02 +07:00
bflatastic 4f8a2f0828 Docs: roadmap after the issue fixes 2026-11-02 08:28:30 +07:00
bflatastic be1e095157 Merge main into fix/ask-to-save-before-replacing
# Conflicts:
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/de.rs
#	crates/ui-egui/src/i18n/es.rs
#	crates/ui-egui/src/i18n/fr.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/ko.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/zh_hans.rs
2026-11-02 08:26:03 +07:00
bflatastic f48d5b6ba4 Ask to save before opening, starting or closing another project
New project, Open, a recent project, a dropped file, Open sample
project and Close replaced a project with unsaved changes without a
word. They now wait on the same Save / Don't save / Cancel dialog as
quitting, and go ahead once the changes are saved or let go.
2026-11-02 08:24:29 +07:00
bflatastic ad8c650c09 Merge main into fix/fabric-materials
# Conflicts:
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/de.rs
#	crates/ui-egui/src/i18n/es.rs
#	crates/ui-egui/src/i18n/fr.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/ko.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/zh_hans.rs
2026-11-02 08:01:39 +07:00
bflatastic 3fea0f6c62 Materials: twill for denim and carbon fiber, a pile for velvet
Carbon Fiber, Fabric Cotton, Denim and Velvet were all the same plain
weave in different colours. A new Twill weave pattern (2/2: diagonal
ribs, the weft darker than the warp) now gives Denim its diagonal and
Carbon Fiber its light and dark tows under a clear coat; Velvet is a
soft pile with no weave under its sheen; Fabric Cotton keeps the plain
weave. Twill weave is also a fill pattern, translated into all nine
other UI languages.

Fixes #97.
2026-11-02 07:53:46 +07:00
bflatastic 85c749bb1b Ask to save unsaved changes when the app closes
Closing the window (or File > Quit) with unsaved changes now keeps the
app open and asks: Save, Don't save or Cancel. A cancelled or failed
save keeps the app and the dialog open. Strings translated into all
nine other UI languages.

Fixes #100.
2026-11-02 07:41:01 +07:00
bflatastic 2d8c34c055 Features: the texture set visibility controls 2026-11-02 07:07:45 +07:00
bflatastic 7e78a2cba6 Material channels: Alt+click keeps one channel
Alt+click on a channel of a fill's or the brush's Material section turns
that channel on and every other one off (one undo step for a fill).
tool.material and layer.fill.set take solo: true for the same.

Fixes #74.
2026-11-02 06:37:04 +07:00
bflatastic e03a9d6c71 Merge remote-tracking branch 'origin/main' into assets/brush-presets
# Conflicts:
#	ROADMAP.md
#	crates/engine/tests/session.rs
#	crates/paint/src/lib.rs
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/de.rs
#	crates/ui-egui/src/i18n/es.rs
#	crates/ui-egui/src/i18n/fr.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/ko.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/zh_hans.rs
2026-11-02 04:48:21 +07:00
bflatastic f4baf8140a Merge remote-tracking branch 'origin/main' into ui/start-window
# Conflicts:
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/de.rs
#	crates/ui-egui/src/i18n/es.rs
#	crates/ui-egui/src/i18n/fr.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/ko.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/zh_hans.rs
2026-11-02 04:29:43 +07:00
bflatastic 1d885c8df1 Brushes: 18 brush presets in the shelf, and your own
The Assets shelf has a Brushes tab: presets grouped as Basic (hard, soft,
airbrush, square, dots), Wear and grunge (dry brush, chalk, spray, grunge,
splatter, scratches, chips, cracks, drips) and Details (stars, stitches,
rivets, fur), each with a preview of its stroke. A click sets the stroke (alpha,
spacing, flow, hardness, angle, path following, jitters, particles) and keeps
the brush's size, alignment, culling, symmetry and lazy mouse. My brushes saves
the current brush as a .tcbrush file in the brushes folder and lists them.

Commands: brush.preset.list, brush.preset.apply {preset}, brush.preset.save
{path} and brush.preset.load {path} (an image alpha from another project is
dropped). Labels translated into the other nine UI languages.
2026-11-02 04:28:17 +07:00
bflatastic 96b179ccfc Start window: new, open or a recent project at launch
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.
2026-11-02 04:15:55 +07:00
bflatastic 0c9627573e Merge remote-tracking branch 'origin/main' into tools/text-alpha 2026-11-02 04:07:38 +07:00
bflatastic 7fdd9be0a3 Text: draw text in any installed font as a brush alpha or a decal
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
2026-11-02 04:07:37 +07:00
BFlat 0788317deb Merge pull request #47 from test-bit-sys/feat/arabic-localization
Add complete Arabic (ar) UI localization
2026-11-02 04:03:28 +07:00
bflatastic eb812f90d4 Arabic: UI font fallback, and the docs
Arabic text needs a font with Arabic glyphs; egui already shapes it and orders
right-to-left runs. The script joins the CJK ones in the lazy fallback fonts:
Noto Sans Arabic from craft-fonts (desktop and web, 0.8 MB), else Segoe UI,
Tahoma or Arial on Windows, SF Arabic or Geeza Pro on macOS, and Noto Sans
Arabic or DejaVu Sans on Linux and FreeBSD. Without any, the picker lists Arabic
by its English name, as for CJK.

ATTRIBUTION, the localization table (panels keep the left-to-right layout),
features, README and roadmap count ten languages.
2026-11-02 03:28:59 +07:00
bflatastic bd128ab181 Wireframes show the mesh's polygons, not their triangles
Importers fan-triangulate quads and n-gons, and the 3D and UV wireframes drew
every triangle edge, so a quad mesh showed its triangulation. Meshes now carry a
per-triangle mask of the diagonals the importer added (Mesh::diagonals), and the
wireframe leaves them out (Mesh::wire_edges), unless a UV seam split one from its
twin.

- OBJ reads polygons whole (tobj without triangulation) and fans them itself.
- OBJ, PLY and the polygon sink (FBX, COLLADA, USD, Alembic) share one fan,
  RawMesh::polygon, which records the diagonals.
- The built-in meshes mark their grid quads.
- glTF and STL store triangles only, so they keep every edge.

Fixes #41
2026-11-02 03:20:57 +07:00
bflatastic bdac5a22d2 Custom title bar: app icon, menus and window buttons in one strip
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.
2026-11-01 19:46:06 +07:00
BFlat 155fa7343b Merge pull request #28 from storytold/filters/replace-color-filter
Replace Color filter
2026-11-01 17:37:28 +07:00
bflatastic 56a3a655ee Replace Color filter
Colors near a chosen color shift towards a new one, keeping their shading and texture.
27 -> 28 library filters.

- Filter::ReplaceColor {from, to, tolerance, softness}: each color within the tolerance (RGB
  distance 0..1) moves by to - from, fading out over the softness (smoothstep); far colors and
  scalar channels are untouched; content only (Adjustments)
- effect.add.filter / effect.set take from and to as linear RGB or "#rrggbb"; Properties has a
  swatch per color and the two sliders; translations in all eight UI languages
- tests: near, far and halfway colors, scalars; the engine filter library test adds it
2026-11-01 16:57:50 +07:00
bflatastic 12fa192373 Six more materials: Rust, Slate, Sandstone, Terrazzo, Cork, Paper
48 -> 54 procedural materials on the shelf and through material.apply:
- Rust (Metal): pitted, blotchy orange-brown iron oxide, a rough dielectric
- Slate, Sandstone, Terrazzo (Stone & Masonry): layered dark stone, fine warm grains, pale
  binder with polished stone chips
- Cork and Paper (Organic): granular pored cork, off-white paper with a faint tooth
The existing test applies every material on the sample and checks it changes the composite;
each was also checked on the material ball.
2026-11-01 16:47:49 +07:00
bflatastic c8e26549b0 Geometry mask tool: click mesh parts
A Geometry mask tool (key 8) shows or hides the selected layer on the mesh part under a click,
so geometry masks can be set on the model instead of from the part list.

- Tool::GeometryMask; tool.geometryMask selects it and tool.click toggles the part under the
  point ({layer, part, shown, geometryMask}); showing every part again clears the mask; one
  undo step per click; refused on empty space, without a selected layer, or on a part of
  another texture set
- the tool shelf, the tool bar hint and Properties (the layer's parts, shown or hidden, and Show
  on all parts); the hint is translated in all eight UI languages
- test: clicks on a two-object OBJ hide, show and clear, undo, and a click on empty space
2026-11-01 15:53:49 +07:00
bflatastic afdea4a6dd Radial symmetry for brushes
Strokes can repeat around an axis through the mesh center, 2 to 32 copies, on their own or with
the X/Y/Z mirrors (each mirror is spread too).

- Symmetry gains radial (copies) and axis (X/Y/Z, default Y), serde defaults; sanitized clamps
- symmetry copies are marked and paint where symmetry puts them: the camera visibility and
  back-face tests apply to the stroke itself only, so copies on the far side of the model paint
  (mirror Z included); they still only paint surfaces facing the copy
- tool.brush {symmetryRadial, symmetryAxis}, shown in its result as radialSymmetry; Properties >
  Lazy mouse and symmetry: Radial copies slider and axis; translations in all eight languages
- tests: copies a quarter turn apart, with mirrors, clamping; an engine stroke paints the back of
  a sphere through radial symmetry
2026-11-01 15:04:23 +07:00
BFlat 27a695648d Merge pull request #22 from storytold/docs/roadmap-2026-11
Docs: roadmap and status after the November features
2026-11-01 15:03:36 +07:00
BFlat ff0b439912 Merge pull request #21 from storytold/export/more-presets
Export presets: Godot 4 (ORM) and OpenPBR
2026-11-01 14:45:20 +07:00
bflatastic 5fe56c8000 Docs: rewrap the shading and displacement paragraphs 2026-11-01 14:36:04 +07:00
bflatastic 052c8b2cff Docs: roadmap and status after the November features
- ROADMAP: parity estimate re-scored on 2026-11-01 (about 93%): layer stack (27 filters,
  geometry masks), materials and assets (21 smart materials, 18 smart masks), baking (FBX
  texture files), viewport (cameras from FBX, COLLADA and USD; displacement in shadows and the
  path tracers), shader, export (Godot 4 and OpenPBR presets, clear coat in USD); remaining gaps
  and current focus updated
- README: status date and estimate
- architecture: geometry masks in compositing, displacement in rendering and the path tracers
- features: filter list wrapped
2026-11-01 14:35:18 +07:00
BFlat 61ed084c2e Merge pull request #20 from storytold/assets/more-smart-masks
Eight smart masks and four smart materials
2026-11-01 14:34:36 +07:00