Skip to content

Commit a3b5c97

Browse files
author
DavidQ
committed
Final polish for Level 17 rendering track (samples 1701-1707)
1 parent 0242ca3 commit a3b5c97

13 files changed

Lines changed: 144 additions & 53 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
MODEL: GPT-5.3-codex
22
REASONING: high
3-
COMMAND: Renumber samples 1623-1629 to 1701-1707, update folders and samples/index.html references, ensure no broken links, package to <project folder>/tmp/BUILD_PR_LEVEL_17_35_SAMPLE_RENUMBER_AND_INDEX_ALIGNMENT.zip
3+
COMMAND: Polish rendering samples 1701-1707 for clarity, consistency, labeling, and UX; ensure controls and overlays are consistent; package to <project folder>/tmp/BUILD_PR_LEVEL_17_36_RENDERING_TRACK_FINAL_POLISH.zip

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Renumber rendering samples 1623-1629 to 1701-1707 and align index
1+
Final polish for Level 17 rendering track (samples 1701-1707)
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11

2-
- [ ] all 1701-1707 samples load
3-
- [ ] index updated correctly
4-
- [ ] no broken paths
2+
- [ ] samples consistent
3+
- [ ] labels visible
4+
- [ ] controls uniform
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# BUILD_PR_LEVEL_17_36_RENDERING_TRACK_FINAL_POLISH
2+
3+
Implement:
4+
5+
1. Visual polish:
6+
- consistent camera controls
7+
- consistent UI labels
8+
9+
2. Clarity:
10+
- add on-screen labels (DOOM, Wolf, Unreal, Minecraft)
11+
- add short description overlay
12+
13+
3. UX:
14+
- ensure movement is smooth
15+
- ensure no confusing orientation issues
16+
17+
Validation:
18+
- samples clearly identifiable
19+
- consistent experience
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# PLAN_PR_LEVEL_17_36_RENDERING_TRACK_FINAL_POLISH
2+
3+
Purpose:
4+
Finalize rendering track for Level 17 ensuring consistency, clarity, and visual quality.
5+
6+
Scope:
7+
- polish samples 1701–1707
8+
- ensure visual clarity between techniques
9+
- standardize controls and UI hints
10+
11+
Out of Scope:
12+
- new features

samples/phase-17/1701/RaycastDemoScene.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,22 @@ export default class DoomRaycastSpritesScene extends Scene {
123123

124124
step3DPhysics(dt, engine) {
125125
const input = engine.input;
126-
if (input?.isDown('KeyA')) this.player.angle -= this.player.turnSpeed * dt;
127-
if (input?.isDown('KeyD')) this.player.angle += this.player.turnSpeed * dt;
126+
const step = Math.min(dt, 1 / 30);
127+
const turnLeft = input?.isDown('KeyA') || input?.isDown('ArrowLeft');
128+
const turnRight = input?.isDown('KeyD') || input?.isDown('ArrowRight');
129+
if (turnLeft) this.player.angle -= this.player.turnSpeed * step;
130+
if (turnRight) this.player.angle += this.player.turnSpeed * step;
131+
this.player.angle = normalizeAngle(this.player.angle);
128132

129133
const forwardX = Math.cos(this.player.angle);
130134
const forwardY = Math.sin(this.player.angle);
131-
if (input?.isDown('KeyW')) {
132-
this.tryMove(this.player.x + forwardX * this.player.moveSpeed * dt, this.player.y + forwardY * this.player.moveSpeed * dt);
135+
const moveForward = input?.isDown('KeyW') || input?.isDown('ArrowUp');
136+
const moveBackward = input?.isDown('KeyS') || input?.isDown('ArrowDown');
137+
if (moveForward) {
138+
this.tryMove(this.player.x + forwardX * this.player.moveSpeed * step, this.player.y + forwardY * this.player.moveSpeed * step);
133139
}
134-
if (input?.isDown('KeyS')) {
135-
this.tryMove(this.player.x - forwardX * this.player.moveSpeed * dt, this.player.y - forwardY * this.player.moveSpeed * dt);
140+
if (moveBackward) {
141+
this.tryMove(this.player.x - forwardX * this.player.moveSpeed * step, this.player.y - forwardY * this.player.moveSpeed * step);
136142
}
137143
}
138144

@@ -177,13 +183,15 @@ export default class DoomRaycastSpritesScene extends Scene {
177183
drawFrame(renderer, theme, [
178184
'Sample 1701 - DOOM Raycast + Sprites',
179185
'DOOM-style corridor raycasting with billboard sprites composited into depth-sorted space.',
180-
'Move: W/S | Turn: A/D',
186+
'Controls: W/S or Up/Down move | A/D or Left/Right turn',
181187
]);
182188

183189
const viewport = this.viewport;
184190
renderer.strokeRect(viewport.x, viewport.y, viewport.width, viewport.height, '#d8d5ff', 2);
185191
renderer.drawRect(viewport.x, viewport.y, viewport.width, viewport.height * 0.48, '#334155');
186192
renderer.drawRect(viewport.x, viewport.y + viewport.height * 0.48, viewport.width, viewport.height * 0.52, '#111827');
193+
renderer.drawRect(viewport.x + 10, viewport.y + 8, 230, 20, 'rgba(249, 115, 22, 0.20)');
194+
renderer.drawText('DOOM | Raycast + Sprites', viewport.x + 16, viewport.y + 22, { color: '#fed7aa', font: '12px monospace' });
187195

188196
const columnWidth = 2;
189197
const columns = Math.floor(viewport.width / columnWidth);

samples/phase-17/1702/RaycastDemoScene.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ function clamp(value, min, max) {
1414
return Math.max(min, Math.min(max, value));
1515
}
1616

17+
function normalizeAngle(angle) {
18+
let result = angle;
19+
while (result > Math.PI) result -= Math.PI * 2;
20+
while (result < -Math.PI) result += Math.PI * 2;
21+
return result;
22+
}
23+
1724
export default class WolfGridRaycastScene extends Scene {
1825
constructor() {
1926
super();
@@ -110,26 +117,34 @@ export default class WolfGridRaycastScene extends Scene {
110117

111118
step3DPhysics(dt, engine) {
112119
const input = engine.input;
113-
if (input?.isDown('KeyA')) this.player.angle -= this.player.turnSpeed * dt;
114-
if (input?.isDown('KeyD')) this.player.angle += this.player.turnSpeed * dt;
120+
const step = Math.min(dt, 1 / 30);
121+
const turnLeft = input?.isDown('KeyA') || input?.isDown('ArrowLeft');
122+
const turnRight = input?.isDown('KeyD') || input?.isDown('ArrowRight');
123+
if (turnLeft) this.player.angle -= this.player.turnSpeed * step;
124+
if (turnRight) this.player.angle += this.player.turnSpeed * step;
125+
this.player.angle = normalizeAngle(this.player.angle);
115126

116127
const forwardX = Math.cos(this.player.angle);
117128
const forwardY = Math.sin(this.player.angle);
118-
if (input?.isDown('KeyW')) this.tryMove(this.player.x + forwardX * this.player.moveSpeed * dt, this.player.y + forwardY * this.player.moveSpeed * dt);
119-
if (input?.isDown('KeyS')) this.tryMove(this.player.x - forwardX * this.player.moveSpeed * dt, this.player.y - forwardY * this.player.moveSpeed * dt);
129+
const moveForward = input?.isDown('KeyW') || input?.isDown('ArrowUp');
130+
const moveBackward = input?.isDown('KeyS') || input?.isDown('ArrowDown');
131+
if (moveForward) this.tryMove(this.player.x + forwardX * this.player.moveSpeed * step, this.player.y + forwardY * this.player.moveSpeed * step);
132+
if (moveBackward) this.tryMove(this.player.x - forwardX * this.player.moveSpeed * step, this.player.y - forwardY * this.player.moveSpeed * step);
120133
}
121134

122135
render(renderer) {
123136
drawFrame(renderer, theme, [
124137
'Sample 1702 - Wolf Grid Raycast',
125138
'Pure grid raycasting and flat filled walls with classic corridor readability.',
126-
'Move: W/S | Turn: A/D',
139+
'Controls: W/S or Up/Down move | A/D or Left/Right turn',
127140
]);
128141

129142
const viewport = this.viewport;
130143
renderer.strokeRect(viewport.x, viewport.y, viewport.width, viewport.height, '#d8d5ff', 2);
131144
renderer.drawRect(viewport.x, viewport.y, viewport.width, viewport.height * 0.5, '#1e3a8a');
132145
renderer.drawRect(viewport.x, viewport.y + viewport.height * 0.5, viewport.width, viewport.height * 0.5, '#0f172a');
146+
renderer.drawRect(viewport.x + 10, viewport.y + 8, 198, 20, 'rgba(59, 130, 246, 0.20)');
147+
renderer.drawText('Wolf | Grid Raycast', viewport.x + 16, viewport.y + 22, { color: '#bfdbfe', font: '12px monospace' });
133148

134149
const columnWidth = 2;
135150
const columns = Math.floor(viewport.width / columnWidth);

samples/phase-17/1703/RaycastDemoScene.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ function clamp(value, min, max) {
1414
return Math.max(min, Math.min(max, value));
1515
}
1616

17+
function normalizeAngle(angle) {
18+
let result = angle;
19+
while (result > Math.PI) result -= Math.PI * 2;
20+
while (result < -Math.PI) result += Math.PI * 2;
21+
return result;
22+
}
23+
1724
export default class WolfOptimizedRaycastScene extends Scene {
1825
constructor() {
1926
super();
@@ -113,26 +120,34 @@ export default class WolfOptimizedRaycastScene extends Scene {
113120

114121
step3DPhysics(dt, engine) {
115122
const input = engine.input;
116-
if (input?.isDown('KeyA')) this.player.angle -= this.player.turnSpeed * dt;
117-
if (input?.isDown('KeyD')) this.player.angle += this.player.turnSpeed * dt;
123+
const step = Math.min(dt, 1 / 30);
124+
const turnLeft = input?.isDown('KeyA') || input?.isDown('ArrowLeft');
125+
const turnRight = input?.isDown('KeyD') || input?.isDown('ArrowRight');
126+
if (turnLeft) this.player.angle -= this.player.turnSpeed * step;
127+
if (turnRight) this.player.angle += this.player.turnSpeed * step;
128+
this.player.angle = normalizeAngle(this.player.angle);
118129

119130
const forwardX = Math.cos(this.player.angle);
120131
const forwardY = Math.sin(this.player.angle);
121-
if (input?.isDown('KeyW')) this.tryMove(this.player.x + forwardX * this.player.moveSpeed * dt, this.player.y + forwardY * this.player.moveSpeed * dt);
122-
if (input?.isDown('KeyS')) this.tryMove(this.player.x - forwardX * this.player.moveSpeed * dt, this.player.y - forwardY * this.player.moveSpeed * dt);
132+
const moveForward = input?.isDown('KeyW') || input?.isDown('ArrowUp');
133+
const moveBackward = input?.isDown('KeyS') || input?.isDown('ArrowDown');
134+
if (moveForward) this.tryMove(this.player.x + forwardX * this.player.moveSpeed * step, this.player.y + forwardY * this.player.moveSpeed * step);
135+
if (moveBackward) this.tryMove(this.player.x - forwardX * this.player.moveSpeed * step, this.player.y - forwardY * this.player.moveSpeed * step);
123136
}
124137

125138
render(renderer) {
126139
drawFrame(renderer, theme, [
127140
'Sample 1703 - Wolf Optimized Raycast',
128141
'DDA traversal for ray steps, preserving classic filled corridor rendering.',
129-
'Move: W/S | Turn: A/D',
142+
'Controls: W/S or Up/Down move | A/D or Left/Right turn',
130143
]);
131144

132145
const viewport = this.viewport;
133146
renderer.strokeRect(viewport.x, viewport.y, viewport.width, viewport.height, '#d8d5ff', 2);
134147
renderer.drawRect(viewport.x, viewport.y, viewport.width, viewport.height * 0.5, '#0f766e');
135148
renderer.drawRect(viewport.x, viewport.y + viewport.height * 0.5, viewport.width, viewport.height * 0.5, '#111827');
149+
renderer.drawRect(viewport.x + 10, viewport.y + 8, 188, 20, 'rgba(20, 184, 166, 0.22)');
150+
renderer.drawText('Wolf | DDA Raycast', viewport.x + 16, viewport.y + 22, { color: '#99f6e4', font: '12px monospace' });
136151

137152
const columnWidth = 2;
138153
const columns = Math.floor(viewport.width / columnWidth);
@@ -156,7 +171,7 @@ export default class WolfOptimizedRaycastScene extends Scene {
156171
this.lastFilledColumns += 1;
157172
}
158173

159-
drawPanel(renderer, 620, 34, 300, 182, 'Optimized Runtime', [
174+
drawPanel(renderer, 620, 34, 300, 182, 'Wolf Runtime', [
160175
`Player: x=${this.player.x.toFixed(2)} y=${this.player.y.toFixed(2)}`,
161176
`Angle: ${this.player.angle.toFixed(2)} rad`,
162177
`Filled columns: ${this.lastFilledColumns}`,

samples/phase-17/1704/TextureMaterialDemoScene.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,13 @@ export default class TextureMaterialDemoScene extends Scene {
103103

104104
step3DPhysics(dt, engine) {
105105
const input = engine.input;
106+
const step = Math.min(dt, 1 / 30);
106107
stepPhase16ViewToggles(this.viewState, input);
107108

108-
this.cameraYaw += this.cameraOrbitSpeed * dt;
109-
if (input?.isDown('KeyQ')) this.cameraYaw -= 1.0 * dt;
110-
if (input?.isDown('KeyE')) this.cameraYaw += 1.0 * dt;
111-
this.lightPulse += dt * 2.7;
109+
this.cameraYaw += this.cameraOrbitSpeed * step;
110+
if (input?.isDown('KeyQ') || input?.isDown('ArrowLeft')) this.cameraYaw -= 1.0 * step;
111+
if (input?.isDown('KeyE') || input?.isDown('ArrowRight')) this.cameraYaw += 1.0 * step;
112+
this.lightPulse += step * 2.7;
112113
this.syncCamera();
113114
}
114115

@@ -142,14 +143,14 @@ export default class TextureMaterialDemoScene extends Scene {
142143
drawFrame(renderer, theme, [
143144
'Sample 1704 - Unreal Texture + Lighting',
144145
'Image-backed textures mapped to sample surfaces with simple light pulse response.',
145-
'Camera orbit: auto + Q/E | Camera mode: C | Debug: V',
146+
'Controls: Q/E or Left/Right orbit | C camera mode | V debug',
146147
]);
147148

148149
const viewport = this.viewport;
149150
renderer.strokeRect(viewport.x, viewport.y, viewport.width, viewport.height, '#d8d5ff', 2);
150151
drawDepthBackdrop(renderer, viewport);
151-
renderer.drawRect(viewport.x + 10, viewport.y + 8, 188, 20, 'rgba(56, 189, 248, 0.18)');
152-
renderer.drawText('Image-Backed Material Surfaces', viewport.x + 16, viewport.y + 22, { color: '#bae6fd', font: '12px monospace' });
152+
renderer.drawRect(viewport.x + 10, viewport.y + 8, 262, 20, 'rgba(56, 189, 248, 0.18)');
153+
renderer.drawText('Unreal | Texture + Material Lighting', viewport.x + 16, viewport.y + 22, { color: '#bae6fd', font: '12px monospace' });
153154

154155
const cameraState = this.camera3D?.getState?.() ?? {
155156
position: { x: 8, y: 8, z: 0 },

samples/phase-17/1705/ImageSkinnedCharacterDemoScene.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,12 @@ export default class ImageSkinnedCharacterDemoScene extends Scene {
104104

105105
step3DPhysics(dt, engine) {
106106
const input = engine.input;
107+
const step = Math.min(dt, 1 / 30);
107108
stepPhase16ViewToggles(this.viewState, input);
108109

109110
this.pathTime += dt;
110-
if (input?.isDown('ArrowLeft')) this.cameraYawOffset -= 0.9 * dt;
111-
if (input?.isDown('ArrowRight')) this.cameraYawOffset += 0.9 * dt;
111+
if (input?.isDown('ArrowLeft') || input?.isDown('KeyQ')) this.cameraYawOffset -= 0.9 * step;
112+
if (input?.isDown('ArrowRight') || input?.isDown('KeyE')) this.cameraYawOffset += 0.9 * step;
112113
if (input?.isDown('KeyR')) this.pathTime = 0;
113114

114115
this.character.x = this.centerPath.x + Math.cos(this.pathTime * 0.85) * this.pathRadius;
@@ -149,14 +150,14 @@ export default class ImageSkinnedCharacterDemoScene extends Scene {
149150
drawFrame(renderer, theme, [
150151
'Sample 1705 - Unreal Skinned Mesh Demo',
151152
'Image sprite-sheet frames animate a skinned character moving along a looping path.',
152-
'Camera offset: Left/Right | Reset path: R | Camera mode: C | Debug: V',
153+
'Controls: Left/Right or Q/E orbit | R reset path | C camera mode | V debug',
153154
]);
154155

155156
const viewport = this.viewport;
156157
renderer.strokeRect(viewport.x, viewport.y, viewport.width, viewport.height, '#d8d5ff', 2);
157158
drawDepthBackdrop(renderer, viewport);
158-
renderer.drawRect(viewport.x + 10, viewport.y + 8, 206, 20, 'rgba(99, 102, 241, 0.20)');
159-
renderer.drawText('Image-Skinned Character + Frames', viewport.x + 16, viewport.y + 22, { color: '#ddd6fe', font: '12px monospace' });
159+
renderer.drawRect(viewport.x + 10, viewport.y + 8, 250, 20, 'rgba(99, 102, 241, 0.20)');
160+
renderer.drawText('Unreal | Image-Skinned Character', viewport.x + 16, viewport.y + 22, { color: '#ddd6fe', font: '12px monospace' });
160161

161162
const cameraState = this.camera3D?.getState?.() ?? {
162163
position: { x: 8, y: 8, z: 0 },

0 commit comments

Comments
 (0)