diff --git a/PLAN.md b/PLAN.md index 01fcc0d..d3a4bd3 100644 --- a/PLAN.md +++ b/PLAN.md @@ -501,9 +501,17 @@ user does.** In rough priority: ### 5.3 Engine features the editor is blocked on - **Render-mesh-to-texture** (the obvious one): real thumbnails for models - and prefabs. The current render-target API is a per-frame override consumed - at end_frame — unusable mid-frame; needs either an immediate mode or a - dedicated `renderModelToTexture(model, camera, size)` call. + and prefabs. Two editor-side approaches are now DISPROVEN with screenshots + (2026-07-17, details in `src/ui/thumbnails.ts`): mid-frame texture mode + renders nothing (the override is per-frame), and dedicated whole frames + engage the pipeline (GI backend re-selects) but the resulting texture still + draws as nothing in the 2D layer — even a bare magenta clear never shows + through `drawTexturePro`. The engine work is now precisely scoped: a + self-contained `renderModelToTexture(model, camera, size)` pass plus a + golden test that round-trips a rendered texture through `drawTexturePro` + (which today has never sampled an RT texture successfully). The editor + keeps its category-colored cells until then; the dedicated-frame burst + scaffolding is in git history, ready to resurrect. - **Text-input completeness**: ~~caret movement~~ DONE 2026-07-17 (click places the caret, Left/Right/Home/End move it, Delete deletes forward, typing inserts at the caret). Still engine-blocked: clipboard paste, diff --git a/src/main.ts b/src/main.ts index 575a9c3..36bc095 100644 --- a/src/main.ts +++ b/src/main.ts @@ -62,7 +62,7 @@ import { drawInspector } from './ui/layouts/inspector'; import { drawOutliner } from './ui/layouts/outliner'; import { drawRecentPanel } from './ui/layouts/recent-panel'; import { drawStatusBar } from './ui/layouts/status-bar'; -import { pumpThumbnails } from './ui/thumbnails'; +import { runThumbnailFrame } from './ui/thumbnails'; import { runSelfTests } from './tests/self-tests'; // ---- self-tests (headless) --------------------------------------------------- @@ -151,6 +151,13 @@ let modelsPending = 1; // ---- main loop ------------------------------------------------------------- while (!windowShouldClose()) { + // Thumbnail render frames are currently a no-op (see ui/thumbnails.ts for + // the two dead ends and the engine work they point to); the hook stays so + // the burst comes back the day the engine can render a model to a texture. + if (modelsPending === 0 && !state.playtesting && runThumbnailFrame(state)) { + continue; + } + const dt = getDeltaTime(); // Auto-save. @@ -274,13 +281,6 @@ while (!windowShouldClose()) { a: 255, }); - // Render missing asset thumbnails, one per frame, once models are in. - // Must run inside the frame but before the main 3D pass (it switches the - // render target and back). - if (modelsPending === 0 && !state.playtesting) { - pumpThumbnails(state, 1); - } - // ---- 3D viewport --------------------------------------------------------- const cam3D = buildCamera3D(state.camera); diff --git a/src/ui/thumbnails.ts b/src/ui/thumbnails.ts index 0c7f1ae..c7aec0b 100644 --- a/src/ui/thumbnails.ts +++ b/src/ui/thumbnails.ts @@ -1,20 +1,30 @@ -// Asset-panel model thumbnails (PLAN §G) — render pass currently DISABLED. +// Asset-panel model thumbnails (PLAN §G) — render pass DISABLED, twice over. +// The asset panel draws category-colored cells instead (visible, clickable, +// labeled); getThumbnail below always returns null and the grid falls back. // -// The first implementation called beginTextureMode/endTextureMode mid-frame, -// raylib-style. The engine's render targets don't work that way: the override -// set by begin_texture_mode is consumed at END_FRAME ("the next end_frame will -// render to this texture view instead of the surface" — renderer/mod.rs), and -// 2D draws batch for the frame. So the thumbnail textures stayed empty and the -// grid drew invisible images over dark panel — screenshot-verified 2026-07-16. +// What was tried, and what each attempt established (2026-07-16/17): // -// Rendering a mesh into a texture on this engine needs either (a) a dedicated -// whole frame per thumbnail with the world's scene nodes hidden, or (b) a real -// engine-side render-mesh-to-texture utility. Until one of those exists, the -// asset panel draws colored placeholder cells (visible, clickable, labeled) — -// see asset-panel.ts. The API here stays so the grid lights up the day the -// engine call exists. - -import { EditorState } from '../state/editor-state'; +// 1. MID-FRAME beginTextureMode/endTextureMode, raylib-style: the engine's +// override is per-frame — begin_texture_mode arms it and only end_frame +// consumes it (renderer/mod.rs), so clearing it mid-frame renders nothing. +// Result: empty textures, invisible cells (screenshot-verified). +// +// 2. DEDICATED FRAMES: hide the world's scene nodes, draw one model, arm the +// override, endDrawing → the whole frame renders into the 128px target +// with no present. The frame pipeline demonstrably ENGAGES (the GI +// backend re-selects during the burst) and the world hide/unhide works — +// but the texture still draws as nothing in the 2D layer: even a bare +// magenta clearBackground never shows up when the texture is drawn via +// drawTexturePro (screenshot-verified). So either the compose pass does +// not write the RT the way begin_texture_mode's docs suggest, or 2D +// sampling of an RT texture is broken (bind group / alpha). +// +// Conclusion: this needs a focused ENGINE session — ideally a purpose-built +// `renderModelToTexture(model, camera, size)` that runs a self-contained +// simple pass, plus a golden test that round-trips a rendered texture +// through drawTexturePro. Editor-side scaffolding (dedicated-frame burst, +// world hide/unhide, framing camera) lives in git history at commit ab43000^ +// ..HEAD and can be resurrected the day the engine call exists. export const THUMB_SIZE = 128; @@ -24,14 +34,14 @@ export interface ThumbnailEntry { height: number; } -// No-op while the render pass is disabled. Returns 0 = "nothing pending" so -// main.ts never waits on it. -export function pumpThumbnails(_state: EditorState, _maxPerFrame: number): number { - return 0; +import { EditorState } from '../state/editor-state'; + +// No-op while the render pass is disabled: nothing pending, ever. +export function runThumbnailFrame(_state: EditorState): boolean { + return false; } -// Always null while the render pass is disabled — the asset panel then draws -// its colored placeholder cell. +// Always null — the asset panel then draws its colored placeholder cell. export function getThumbnail(_relPath: string): ThumbnailEntry | null { return null; }