Skip to content

Commit 7a76599

Browse files
author
DavidQ
committed
Add advanced readability and camera enhancements to Phase 16 samples<BUILD_PR_LEVEL_17_15_ADVANCED_3D_SAMPLE_EXTENSIONS>
1 parent 3a7608f commit 7a76599

15 files changed

Lines changed: 545 additions & 91 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ MODEL: GPT-5.3-codex
33
REASONING: high
44

55
COMMAND:
6-
Implement Sample 1608 dungeon crawler core loop.
6+
Add advanced but safe enhancements to Phase 16 samples.
7+
8+
Focus:
9+
- camera mode toggle
10+
- simple shading/depth cues
11+
- debug overlay
712

813
Rules:
914
- smallest scoped change
15+
- sample-only
1016
- no zip output
11-
- sample only
1217

1318
Validate:
14-
- movement works
15-
- collision blocks
16-
- interaction visible
19+
- features visibly work
20+
- no regressions
1721
- sanity + smoke pass

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Add core dungeon crawler loop to Sample 1608<BUILD_PR_LEVEL_17_14_SAMPLE_1608_DUNGEON_CRAWLER_CORE_LOOP>
1+
Add advanced readability and camera enhancements to Phase 16 samples<BUILD_PR_LEVEL_17_15_ADVANCED_3D_SAMPLE_EXTENSIONS>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1608 now has a readable dungeon loop with movement, blocking collision, and interaction.
1+
Adds camera modes, shading cues, and debug overlays to improve 3D sample clarity.
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
# Launch Smoke Report
22

3-
Generated: 2026-04-15T21:47:58.859Z
3+
Generated: 2026-04-15T21:59:22.383Z
44

5-
Filters: games=false, samples=true, tools=false, sampleRange=1608-1608
5+
Filters: games=false, samples=true, tools=false, sampleRange=1601-1608
66

77
| Status | Type | Label | Path | Notes | Steps |
88
| --- | --- | --- | --- | --- | --- |
9+
| PASS | sample | 1601 | samples\phase-16\1601\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
10+
| PASS | sample | 1602 | samples\phase-16\1602\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
11+
| PASS | sample | 1603 | samples\phase-16\1603\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
12+
| PASS | sample | 1604 | samples\phase-16\1604\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
13+
| PASS | sample | 1605 | samples\phase-16\1605\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
14+
| PASS | sample | 1606 | samples\phase-16\1606\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
15+
| PASS | sample | 1607 | samples\phase-16\1607\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
916
| PASS | sample | 1608 | samples\phase-16\1608\index.html | | npm install --prefix ./tmp ws → npm run test:launch-smoke |
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

2-
[ ] movement works
3-
[ ] walls block
4-
[ ] interaction visible
5-
[ ] loop understandable
2+
[ ] camera toggle works
3+
[ ] shading improves readability
4+
[ ] debug overlay visible
65
[ ] sanity pass
76
[ ] smoke pass
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
# BUILD PR: 17.15 Advanced 3D Sample Extensions
3+
4+
## Purpose
5+
Extend Phase 16 samples with advanced but still safe, testable capabilities.
6+
7+
## Scope
8+
- camera modes (follow, free look toggle)
9+
- simple lighting/shading hint (fake shading ok)
10+
- depth ordering clarity improvements
11+
- optional debug overlay (position, velocity)
12+
13+
## Constraints
14+
- no engine changes
15+
- sample-only enhancements
16+
- no networking or 2D impact
17+
18+
## Acceptance
19+
- camera mode toggle works
20+
- shading/depth improves readability
21+
- debug overlay optional and useful
22+
- sanity + smoke pass

samples/phase-16/1601/CubeExplorer3DScene.js

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import { Theme, ThemeTokens } from '/src/engine/theme/index.js';
99
import { drawFrame, drawPanel } from '/src/engine/debug/index.js';
1010
import { World } from '/src/engine/ecs/index.js';
1111
import { stepWorldPhysics3D } from '/src/engine/systems/index.js';
12+
import {
13+
applyPhase16CameraMode,
14+
createPhase16ViewState,
15+
drawDepthBackdrop,
16+
drawPhase16DebugOverlay,
17+
stepPhase16ViewToggles,
18+
} from '../shared/threeDWireframe.js';
1219

1320
const theme = new Theme(ThemeTokens);
1421
const BOX_EDGES = [
@@ -91,7 +98,10 @@ function drawWireBox(renderer, transform3D, size3D, cameraState, viewport, color
9198
if (!start || !end) {
9299
continue;
93100
}
94-
renderer.drawLine(start.x, start.y, end.x, end.y, color, 2);
101+
const edgeDepth = (start.depth + end.depth) * 0.5;
102+
const depthT = clamp((edgeDepth - 3) / 40, 0, 1);
103+
const edgeWidth = 2.2 - depthT * 1.1;
104+
renderer.drawLine(start.x, start.y, end.x, end.y, color, Math.max(1, edgeWidth));
95105
}
96106
}
97107

@@ -120,6 +130,7 @@ export default class CubeExplorer3DScene extends Scene {
120130
depth: 22,
121131
};
122132
this.lastPhysicsSummary = { movedEntities: 0, collisionCount: 0 };
133+
this.viewState = createPhase16ViewState();
123134

124135
this.playerId = this.world.createEntity();
125136
this.world.addComponent(this.playerId, 'transform3D', {
@@ -175,21 +186,29 @@ export default class CubeExplorer3DScene extends Scene {
175186
const cameraZ = playerTransform.z - Math.cos(this.cameraYaw) * orbitDistance;
176187
const cameraY = playerTransform.y + 4.8;
177188

178-
this.camera3D.setPosition({
179-
x: cameraX,
180-
y: cameraY,
181-
z: cameraZ,
182-
});
183-
this.camera3D.setRotation({
184-
x: this.cameraPitch,
185-
y: this.cameraYaw,
186-
z: 0,
189+
const basePose = {
190+
position: {
191+
x: cameraX,
192+
y: cameraY,
193+
z: cameraZ,
194+
},
195+
rotation: {
196+
x: this.cameraPitch,
197+
y: this.cameraYaw,
198+
z: 0,
199+
},
200+
};
201+
applyPhase16CameraMode(this.camera3D, this.viewState, basePose, {
202+
x: playerTransform.x + 0.8,
203+
y: playerTransform.y + 0.8,
204+
z: playerTransform.z + 0.8,
187205
});
188206
}
189207

190208
step3DPhysics(dt, engine) {
191209
const velocity = this.world.requireComponent(this.playerId, 'velocity3D');
192210
const input = engine.input;
211+
stepPhase16ViewToggles(this.viewState, input);
193212

194213
velocity.x = 0;
195214
velocity.y = 0;
@@ -217,7 +236,7 @@ export default class CubeExplorer3DScene extends Scene {
217236
drawFrame(renderer, theme, [
218237
'Sample 1601 - 3D Cube Explorer',
219238
'Minimal 3D movement + AABB collision using an isolated physics loop',
220-
'Move: W A S D | Vertical: R/F | Camera orbit: Arrow keys',
239+
'Move: W A S D | Vertical: R/F | Camera orbit: Arrow keys | Camera: C | Debug: V',
221240
'Goal: navigate around blocking boxes while remaining inside world bounds',
222241
]);
223242

@@ -229,6 +248,7 @@ export default class CubeExplorer3DScene extends Scene {
229248
'#d8d5ff',
230249
2,
231250
);
251+
drawDepthBackdrop(renderer, this.viewport);
232252

233253
const cameraState = this.camera3D?.getState?.() ?? {
234254
position: { x: 0, y: 3, z: -8 },
@@ -277,12 +297,17 @@ export default class CubeExplorer3DScene extends Scene {
277297
});
278298

279299
const player = this.world.requireComponent(this.playerId, 'transform3D');
280-
drawPanel(renderer, 620, 34, 300, 126, '3D Runtime', [
300+
drawPanel(renderer, 620, 34, 300, 156, '3D Runtime', [
281301
`Cube: x=${player.x.toFixed(2)} y=${player.y.toFixed(2)} z=${player.z.toFixed(2)}`,
282302
`Camera yaw: ${this.cameraYaw.toFixed(2)} pitch: ${this.cameraPitch.toFixed(2)}`,
283303
`Moved entities: ${this.lastPhysicsSummary.movedEntities}`,
284304
`Resolved collisions: ${this.lastPhysicsSummary.collisionCount}`,
285305
'Physics loop: stepWorldPhysics3D (MovementSystem + AABB)',
286306
]);
307+
308+
drawPhase16DebugOverlay(renderer, this.viewport, this.viewState, [
309+
`Yaw: ${this.cameraYaw.toFixed(2)} | Pitch: ${this.cameraPitch.toFixed(2)}`,
310+
`Collisions/frame: ${this.lastPhysicsSummary.collisionCount}`,
311+
]);
287312
}
288313
}

samples/phase-16/1602/MazeRunner3DScene.js

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,16 @@ import { drawFrame, drawPanel } from '/src/engine/debug/index.js';
1010
import { World } from '/src/engine/ecs/index.js';
1111
import { isAabbColliding3D } from '/src/engine/physics/index.js';
1212
import { stepWorldPhysics3D } from '/src/engine/systems/index.js';
13-
import { createProjectionViewport, drawGroundGrid, drawWireBox } from '../shared/threeDWireframe.js';
13+
import {
14+
applyPhase16CameraMode,
15+
createPhase16ViewState,
16+
createProjectionViewport,
17+
drawDepthBackdrop,
18+
drawGroundGrid,
19+
drawPhase16DebugOverlay,
20+
drawWireBox,
21+
stepPhase16ViewToggles,
22+
} from '../shared/threeDWireframe.js';
1423

1524
const theme = new Theme(ThemeTokens);
1625

@@ -44,6 +53,7 @@ export default class MazeRunner3DScene extends Scene {
4453
};
4554
this.goalReached = false;
4655
this.lastPhysicsSummary = { movedEntities: 0, collisionCount: 0 };
56+
this.viewState = createPhase16ViewState();
4757

4858
this.playerId = this.world.createEntity();
4959
this.world.addComponent(this.playerId, 'transform3D', {
@@ -109,20 +119,28 @@ export default class MazeRunner3DScene extends Scene {
109119
}
110120

111121
const player = this.world.requireComponent(this.playerId, 'transform3D');
112-
this.camera3D.setPosition({
113-
x: player.x,
114-
y: 10.2,
115-
z: player.z - 10.6,
116-
});
117-
this.camera3D.setRotation({
118-
x: -0.5,
119-
y: 0,
120-
z: 0,
122+
const basePose = {
123+
position: {
124+
x: player.x,
125+
y: 10.2,
126+
z: player.z - 10.6,
127+
},
128+
rotation: {
129+
x: -0.5,
130+
y: 0,
131+
z: 0,
132+
},
133+
};
134+
applyPhase16CameraMode(this.camera3D, this.viewState, basePose, {
135+
x: player.x + 0.55,
136+
y: player.y + 0.55,
137+
z: player.z + 0.55,
121138
});
122139
}
123140

124141
step3DPhysics(dt, engine) {
125142
const input = engine.input;
143+
stepPhase16ViewToggles(this.viewState, input);
126144
const velocity = this.world.requireComponent(this.playerId, 'velocity3D');
127145

128146
const axisX = (input?.isDown('KeyD') ? 1 : 0) - (input?.isDown('KeyA') ? 1 : 0);
@@ -157,11 +175,12 @@ export default class MazeRunner3DScene extends Scene {
157175
drawFrame(renderer, theme, [
158176
'Sample 1602 - 3D Maze Runner',
159177
'Navigate through a 3D maze while physics keeps the cube outside wall bounds.',
160-
'Move: W A S D',
178+
'Move: W A S D | Camera: C | Debug: V',
161179
this.goalReached ? 'Goal reached: route solved.' : 'Find the green goal cube near the far-right corner.',
162180
]);
163181

164182
renderer.strokeRect(this.viewport.x, this.viewport.y, this.viewport.width, this.viewport.height, '#d8d5ff', 2);
183+
drawDepthBackdrop(renderer, this.viewport);
165184

166185
const cameraState = this.camera3D?.getState?.() ?? {
167186
position: { x: 0, y: 10.2, z: -7.4 },
@@ -211,5 +230,10 @@ export default class MazeRunner3DScene extends Scene {
211230
`Goal: ${this.goalReached ? 'reached' : 'in progress'}`,
212231
'Pathing: stepWorldPhysics3D (AABB walls)',
213232
]);
233+
234+
drawPhase16DebugOverlay(renderer, this.viewport, this.viewState, [
235+
`Goal status: ${this.goalReached ? 'reached' : 'pending'}`,
236+
`Collisions/frame: ${this.lastPhysicsSummary.collisionCount}`,
237+
]);
214238
}
215239
}

samples/phase-16/1603/FirstPersonWalkthroughScene.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@ import { Theme, ThemeTokens } from '/src/engine/theme/index.js';
99
import { drawFrame, drawPanel } from '/src/engine/debug/index.js';
1010
import { World } from '/src/engine/ecs/index.js';
1111
import { stepWorldPhysics3D } from '/src/engine/systems/index.js';
12-
import { createProjectionViewport, drawGroundGrid, drawWireBox } from '../shared/threeDWireframe.js';
12+
import {
13+
applyPhase16CameraMode,
14+
createPhase16ViewState,
15+
createProjectionViewport,
16+
drawDepthBackdrop,
17+
drawGroundGrid,
18+
drawPhase16DebugOverlay,
19+
drawWireBox,
20+
stepPhase16ViewToggles,
21+
} from '../shared/threeDWireframe.js';
1322

1423
const theme = new Theme(ThemeTokens);
1524

@@ -42,6 +51,7 @@ export default class FirstPersonWalkthroughScene extends Scene {
4251
depth: 26,
4352
};
4453
this.lastPhysicsSummary = { movedEntities: 0, collisionCount: 0 };
54+
this.viewState = createPhase16ViewState();
4555

4656
this.playerId = this.world.createEntity();
4757
this.world.addComponent(this.playerId, 'transform3D', {
@@ -106,20 +116,28 @@ export default class FirstPersonWalkthroughScene extends Scene {
106116
}
107117

108118
const player = this.world.requireComponent(this.playerId, 'transform3D');
109-
this.camera3D.setPosition({
119+
const basePose = {
120+
position: {
121+
x: player.x + 0.45,
122+
y: player.y + 1.2,
123+
z: player.z + 0.45,
124+
},
125+
rotation: {
126+
x: this.pitch,
127+
y: -this.yaw,
128+
z: 0,
129+
},
130+
};
131+
applyPhase16CameraMode(this.camera3D, this.viewState, basePose, {
110132
x: player.x + 0.45,
111-
y: player.y + 1.2,
133+
y: player.y + 0.9,
112134
z: player.z + 0.45,
113135
});
114-
this.camera3D.setRotation({
115-
x: this.pitch,
116-
y: -this.yaw,
117-
z: 0,
118-
});
119136
}
120137

121138
step3DPhysics(dt, engine) {
122139
const input = engine.input;
140+
stepPhase16ViewToggles(this.viewState, input);
123141
const velocity = this.world.requireComponent(this.playerId, 'velocity3D');
124142

125143
const lookX = (input?.isDown('ArrowRight') ? 1 : 0) - (input?.isDown('ArrowLeft') ? 1 : 0);
@@ -153,11 +171,12 @@ export default class FirstPersonWalkthroughScene extends Scene {
153171
drawFrame(renderer, theme, [
154172
'Sample 1603 - First Person Walkthrough',
155173
'Navigate from a first-person camera while AABB collisions keep movement grounded.',
156-
'Move: W A S D | Look: Arrow Keys',
174+
'Move: W A S D | Look: Arrow Keys | Camera: C | Debug: V',
157175
'Stay centered in the lane and navigate around interior blockers.',
158176
]);
159177

160178
renderer.strokeRect(this.viewport.x, this.viewport.y, this.viewport.width, this.viewport.height, '#d8d5ff', 2);
179+
drawDepthBackdrop(renderer, this.viewport);
161180

162181
const cameraState = this.camera3D?.getState?.() ?? {
163182
position: { x: 0, y: 1.2, z: 2.5 },
@@ -202,5 +221,10 @@ export default class FirstPersonWalkthroughScene extends Scene {
202221
`Resolved collisions: ${this.lastPhysicsSummary.collisionCount}`,
203222
'View mode: first person + collision-aware movement',
204223
]);
224+
225+
drawPhase16DebugOverlay(renderer, this.viewport, this.viewState, [
226+
`Yaw: ${this.yaw.toFixed(2)} | Pitch: ${this.pitch.toFixed(2)}`,
227+
`Collisions/frame: ${this.lastPhysicsSummary.collisionCount}`,
228+
]);
205229
}
206230
}

0 commit comments

Comments
 (0)