Skip to content

Commit c2933ae

Browse files
author
DavidQ
committed
Add Advanced 3D Samples batch 4 (1618-1620)
1 parent f1cb1e2 commit c2933ae

16 files changed

Lines changed: 906 additions & 7 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: Fix Sample 1610 hybrid demo by correcting mini map Y-axis inversion and adding clear visual distinction between 2D overlay and 3D world. Package results to <project folder>/tmp/BUILD_PR_LEVEL_17_29_SAMPLE_1610_HYBRID_FIX.zip
3+
COMMAND: Implement samples 1618-1620, integrate into index, ensure visible camera movement and UI clarity, package to <project folder>/tmp/BUILD_PR_LEVEL_17_30_ADVANCED_3D_SAMPLES_BATCH_4.zip

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Fix Sample 1610 mini map orientation and clarify hybrid 2D vs 3D visualization
1+
Add Advanced 3D Samples batch 4 (1618-1620)
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
- [ ] mini map orientation correct (forward = up)
3-
- [ ] no inversion artifacts
4-
- [ ] 2D vs 3D clearly labeled or visually distinct
5-
- [ ] sample remains simple and functional
1+
- [ ] samples load
2+
- [ ] UI visible
3+
- [ ] no errors
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# BUILD_PR_LEVEL_17_30_ADVANCED_3D_SAMPLES_BATCH_4
2+
3+
Implement:
4+
- samples/1618-lighting-materials
5+
- samples/1619-debug-hud
6+
- samples/1620-mini-game
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# PLAN_PR_LEVEL_17_30_ADVANCED_3D_SAMPLES_BATCH_4
2+
3+
Purpose:
4+
Continue Advanced 3D Samples.
5+
6+
Scope:
7+
- 1618 Lighting & Materials Lab
8+
- 1619 Debug HUD Sample
9+
- 1620 Mini Game (3D Reference)

samples/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,9 @@ <h2>Phase 16 - 3D Capability Track</h2>
490490
<a class="live" href="./phase-16/1615/index.html" title="Assembles one 3D actor from modular parts to demonstrate composition and local transforms." data-tags="camera3d, composition3d, entity3d, render3d, scene, themetokens" data-primary="entity-composition">Sample 1615 - Entity Composition</a>
491491
<a class="live" href="./phase-16/1616/index.html" title="Streams nearby chunks around a moving anchor to visualize real-time world activation." data-tags="camera3d, streaming3d, world3d, scene, themetokens" data-primary="world-streaming">Sample 1616 - World Streaming</a>
492492
<a class="live" href="./phase-16/1617/index.html" title="Demonstrates capped streaming windows for a larger world grid with movement and visibility budgeting." data-tags="camera3d, streaming3d, largeworld3d, scene, themetokens" data-primary="large-world-streaming">Sample 1617 - Large World Streaming</a>
493+
<a class="live" href="./phase-16/1618/index.html" title="Examines moving light behavior against multiple material swatches with visible camera orbit motion." data-tags="camera3d, lighting3d, materials3d, render3d, scene, themetokens" data-primary="lighting-materials-lab">Sample 1618 - Lighting & Materials Lab</a>
494+
<a class="live" href="./phase-16/1619/index.html" title="Demonstrates a readable debug HUD layered above an active 3D scene with explicit world/UI labels." data-tags="camera3d, debughud3d, ui-overlay, render3d, scene, themetokens" data-primary="debug-hud-sample">Sample 1619 - Debug HUD Sample</a>
495+
<a class="live" href="./phase-16/1620/index.html" title="Provides a compact 3D pickup mini game reference with follow camera movement and clear HUD scoring." data-tags="camera3d, minigame3d, pickups, hud, scene, themetokens" data-primary="mini-game-reference">Sample 1620 - Mini Game (3D Reference)</a>
493496
</div>
494497
</section>
495498
</div>
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
04/16/2026
5+
LightingMaterialsLab3DScene.js
6+
*/
7+
import { Scene } from '/src/engine/scene/index.js';
8+
import { Theme, ThemeTokens } from '/src/engine/theme/index.js';
9+
import { drawFrame, drawPanel } from '/src/engine/debug/index.js';
10+
import {
11+
applyPhase16CameraMode,
12+
createPhase16ViewState,
13+
createProjectionViewport,
14+
drawDepthBackdrop,
15+
drawGroundGrid,
16+
drawPhase16DebugOverlay,
17+
drawWireBox,
18+
stepPhase16ViewToggles,
19+
} from '../shared/threeDWireframe.js';
20+
21+
const theme = new Theme(ThemeTokens);
22+
23+
function clamp(value, min, max) {
24+
return Math.max(min, Math.min(max, value));
25+
}
26+
27+
function parseHexColor(color) {
28+
if (typeof color !== 'string' || !color.startsWith('#') || color.length !== 7) {
29+
return { r: 255, g: 255, b: 255 };
30+
}
31+
return {
32+
r: Number.parseInt(color.slice(1, 3), 16),
33+
g: Number.parseInt(color.slice(3, 5), 16),
34+
b: Number.parseInt(color.slice(5, 7), 16),
35+
};
36+
}
37+
38+
function shadeColor(color, factor) {
39+
const rgb = parseHexColor(color);
40+
const scale = clamp(factor, 0.25, 1.35);
41+
const r = clamp(Math.round(rgb.r * scale), 0, 255);
42+
const g = clamp(Math.round(rgb.g * scale), 0, 255);
43+
const b = clamp(Math.round(rgb.b * scale), 0, 255);
44+
return `rgb(${r}, ${g}, ${b})`;
45+
}
46+
47+
export default class LightingMaterialsLab3DScene extends Scene {
48+
constructor() {
49+
super();
50+
this.viewState = createPhase16ViewState();
51+
this.viewport = {
52+
x: 40,
53+
y: 170,
54+
width: 860,
55+
height: 320,
56+
focalLength: 450,
57+
};
58+
this.cameraYaw = 0.52;
59+
this.autoOrbitSpeed = 0.36;
60+
this.lightYaw = 0.8;
61+
this.lightHeight = 3.2;
62+
this.lightDistance = 8.4;
63+
this.time = 0;
64+
this.materialBlocks = [
65+
{ x: -6.8, z: 9.0, baseColor: '#f97316', label: 'warm-matte' },
66+
{ x: -2.8, z: 11.5, baseColor: '#60a5fa', label: 'cool-gloss' },
67+
{ x: 1.6, z: 8.5, baseColor: '#a78bfa', label: 'violet-diffuse' },
68+
{ x: 5.2, z: 12.2, baseColor: '#34d399', label: 'emerald-semi' },
69+
];
70+
}
71+
72+
setCamera3D(camera3D) {
73+
this.camera3D = camera3D;
74+
this.syncCamera();
75+
}
76+
77+
getLightPosition() {
78+
return {
79+
x: Math.cos(this.lightYaw) * this.lightDistance,
80+
y: this.lightHeight,
81+
z: 10 + Math.sin(this.lightYaw) * this.lightDistance,
82+
};
83+
}
84+
85+
syncCamera() {
86+
if (!this.camera3D) {
87+
return;
88+
}
89+
const focusPoint = { x: 0.5, y: 1, z: 10.6 };
90+
const basePose = {
91+
position: {
92+
x: Math.sin(this.cameraYaw) * 12,
93+
y: 8.8,
94+
z: focusPoint.z - Math.cos(this.cameraYaw) * 12,
95+
},
96+
rotation: {
97+
x: -0.52,
98+
y: this.cameraYaw,
99+
z: 0,
100+
},
101+
};
102+
applyPhase16CameraMode(this.camera3D, this.viewState, basePose, focusPoint);
103+
}
104+
105+
step3DPhysics(dt, engine) {
106+
const input = engine.input;
107+
stepPhase16ViewToggles(this.viewState, input);
108+
109+
this.time += dt;
110+
this.cameraYaw += this.autoOrbitSpeed * dt;
111+
if (input?.isDown('KeyQ')) this.cameraYaw -= 1.05 * dt;
112+
if (input?.isDown('KeyE')) this.cameraYaw += 1.05 * dt;
113+
if (input?.isDown('ArrowLeft')) this.lightYaw -= 1.15 * dt;
114+
if (input?.isDown('ArrowRight')) this.lightYaw += 1.15 * dt;
115+
if (input?.isDown('ArrowUp')) this.lightHeight = clamp(this.lightHeight + 3.6 * dt, 1.4, 6.8);
116+
if (input?.isDown('ArrowDown')) this.lightHeight = clamp(this.lightHeight - 3.6 * dt, 1.4, 6.8);
117+
118+
this.syncCamera();
119+
}
120+
121+
computeLightingFactor(blockPosition, lightPosition) {
122+
const dx = blockPosition.x - lightPosition.x;
123+
const dy = 0.8 - lightPosition.y;
124+
const dz = blockPosition.z - lightPosition.z;
125+
const distance = Math.hypot(dx, dy, dz);
126+
return 1.25 - distance * 0.08;
127+
}
128+
129+
render(renderer) {
130+
drawFrame(renderer, theme, [
131+
'Sample 1618 - 3D Lighting & Materials Lab',
132+
'Material swatches react to moving light while camera orbit keeps depth changes visible.',
133+
'Camera orbit: auto + Q/E | Light yaw: Left/Right | Light height: Up/Down | Camera mode: C | Debug: V',
134+
]);
135+
136+
const viewport = this.viewport;
137+
renderer.strokeRect(viewport.x, viewport.y, viewport.width, viewport.height, '#d8d5ff', 2);
138+
drawDepthBackdrop(renderer, viewport);
139+
renderer.drawRect(viewport.x + 10, viewport.y + 8, 160, 20, 'rgba(56, 189, 248, 0.20)');
140+
renderer.drawText('3D World - Lighting Stage', viewport.x + 16, viewport.y + 22, { color: '#bae6fd', font: '12px monospace' });
141+
142+
const cameraState = this.camera3D?.getState?.() ?? {
143+
position: { x: 8, y: 9, z: 0 },
144+
rotation: { x: -0.52, y: this.cameraYaw, z: 0 },
145+
};
146+
const projectionViewport = createProjectionViewport(viewport);
147+
drawGroundGrid(renderer, { minX: -10, maxX: 10, minZ: 4, maxZ: 18, y: -0.3, step: 2 }, cameraState, projectionViewport);
148+
149+
const lightPosition = this.getLightPosition();
150+
for (let i = 0; i < this.materialBlocks.length; i += 1) {
151+
const block = this.materialBlocks[i];
152+
const blockPosition = { x: block.x, y: 0, z: block.z };
153+
const lighting = this.computeLightingFactor(blockPosition, lightPosition);
154+
drawWireBox(
155+
renderer,
156+
{ x: block.x, y: -0.2, z: block.z },
157+
{ width: 2.2, height: 2.2, depth: 2.2 },
158+
cameraState,
159+
projectionViewport,
160+
shadeColor(block.baseColor, lighting),
161+
{ lineWidth: 2.1, depthCueEnabled: true }
162+
);
163+
}
164+
165+
drawWireBox(
166+
renderer,
167+
{ x: lightPosition.x - 0.25, y: lightPosition.y - 0.25, z: lightPosition.z - 0.25 },
168+
{ width: 0.5, height: 0.5, depth: 0.5 },
169+
cameraState,
170+
projectionViewport,
171+
'#fde047',
172+
{ lineWidth: 2.5, depthCueEnabled: false }
173+
);
174+
175+
renderer.drawRect(620, 30, 305, 194, 'rgba(8, 47, 73, 0.55)');
176+
renderer.drawText('UI Layer - Lighting HUD', 634, 48, { color: '#a5f3fc', font: '12px monospace' });
177+
drawPanel(renderer, 620, 34, 300, 188, 'Lighting Runtime', [
178+
`Camera yaw: ${this.cameraYaw.toFixed(2)}`,
179+
`Light yaw: ${this.lightYaw.toFixed(2)}`,
180+
`Light height: ${this.lightHeight.toFixed(2)}`,
181+
`Light distance: ${this.lightDistance.toFixed(2)}`,
182+
`Materials: ${this.materialBlocks.length}`,
183+
`Camera mode: ${this.viewState.cameraMode}`,
184+
]);
185+
186+
drawPhase16DebugOverlay(renderer, viewport, this.viewState, [
187+
'HUD and world labels indicate UI-vs-scene layering',
188+
'Light marker cube tracks active light source',
189+
]);
190+
}
191+
}

samples/phase-16/1618/index.html

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!--
2+
Toolbox Aid
3+
David Quesenberry
4+
04/16/2026
5+
index.html
6+
-->
7+
<!DOCTYPE html>
8+
<html lang="en">
9+
<head>
10+
<meta charset="UTF-8" />
11+
<title>Sample 1618 - 3D Lighting & Materials Lab</title>
12+
<link rel="stylesheet" href="../../../src/engine/ui/baseLayout.css" />
13+
</head>
14+
<body>
15+
<main>
16+
<h1>Sample 1618 - 3D Lighting & Materials Lab</h1>
17+
<p>Demonstrates moving light controls, material color response, and camera orbit readability in a 3D lab.</p>
18+
<canvas id="game" width="960" height="540"></canvas>
19+
20+
<section>
21+
<h3>Engine Classes Used</h3>
22+
<ul>
23+
<li>Engine</li>
24+
<li>Scene</li>
25+
<li>Camera3D</li>
26+
</ul>
27+
</section>
28+
</main>
29+
30+
<script type="module" src="/samples/shared/sampleDetailPageEnhancement.js"></script>
31+
<script type="module" src="./main.js"></script>
32+
</body>
33+
</html>

samples/phase-16/1618/main.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
04/16/2026
5+
main.js
6+
*/
7+
import Engine from '/src/engine/core/Engine.js';
8+
import { InputService } from '/src/engine/input/index.js';
9+
import { Theme, ThemeTokens } from '/src/engine/theme/index.js';
10+
import LightingMaterialsLab3DScene from './LightingMaterialsLab3DScene.js';
11+
12+
const theme = new Theme(ThemeTokens);
13+
theme.applyDocumentTheme();
14+
15+
const canvas = document.getElementById('game');
16+
const input = new InputService();
17+
18+
const engine = new Engine({
19+
canvas,
20+
width: 960,
21+
height: 540,
22+
fixedStepMs: 1000 / 60,
23+
input,
24+
});
25+
26+
engine.setScene(new LightingMaterialsLab3DScene());
27+
engine.start();

0 commit comments

Comments
 (0)