v0.4.16: PDF-aanzichten (vlakke render) met legenda afwerking en afmetingen, zaaglijst/zaagmachine eigen blad, infopaneel afmetingen, lopen (Shift+T) met camera-menu, zachter licht, naadwanden volgen koppeling, boeideel over de gevel (gevel tot bovenkant dak), thumbnails riet/vloer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
12c2579471
commit
23bfe153a2
@@ -0,0 +1,102 @@
|
||||
/*!
|
||||
* Tectum
|
||||
* Copyright © 2026 Machiel Heinen.
|
||||
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
||||
* Zie LICENTIE.md.
|
||||
*/
|
||||
// ============================================================
|
||||
// core/walk-controls.js — lopen in de 3D-weergave (first person).
|
||||
// W/S of pijl omhoog/omlaag vooruit/achteruit
|
||||
// A/D opzij stappen
|
||||
// pijl links/rechts draaien
|
||||
// Shift rennen
|
||||
// slepen met de muis rondkijken
|
||||
// Botsen (optioneel) en vloerhoogte komen van de aanroeper:
|
||||
// opts.blocked(x, z) -> true als je daar niet kunt staan (m); standaard nooit
|
||||
// opts.floorAt(x, z) -> hoogte van de ondergrond (m)
|
||||
// opts.eye -> ooghoogte boven de ondergrond (m, standaard 1,65)
|
||||
// Plain script (geen ES-module); gebruikt THREE.
|
||||
//
|
||||
// const walk = createWalkControls(camera, domElement, opts);
|
||||
// walk.start({ x, z, yaw }); walk.update(dt); walk.stop();
|
||||
// ============================================================
|
||||
|
||||
function createWalkControls(camera, domElement, opts) {
|
||||
const o = Object.assign({ eye: 1.65, speed: 1.68, run: 3.84, turn: Math.PI / 2, blocked: () => false, floorAt: () => 0 }, opts || {});
|
||||
const keys = new Set();
|
||||
let active = false;
|
||||
let x = 0, z = 0, yaw = 0, pitch = 0;
|
||||
let drag = null;
|
||||
|
||||
const inField = () => ['INPUT', 'SELECT', 'TEXTAREA'].includes(document.activeElement && document.activeElement.tagName);
|
||||
const MOVE_KEYS = ['w', 'a', 's', 'd', 'arrowup', 'arrowdown', 'arrowleft', 'arrowright', 'shift'];
|
||||
function onKeyDown(e) {
|
||||
if (!active || inField() || e.ctrlKey || e.metaKey || e.altKey) return;
|
||||
const k = e.key.toLowerCase();
|
||||
if (!MOVE_KEYS.includes(k)) return;
|
||||
keys.add(k);
|
||||
e.preventDefault();
|
||||
}
|
||||
function onKeyUp(e) { keys.delete(e.key.toLowerCase()); }
|
||||
function onDown(e) { if (active && e.button === 0) drag = { x: e.clientX, y: e.clientY }; }
|
||||
function onMove(e) {
|
||||
if (!active || !drag) return;
|
||||
const w = domElement.clientWidth || 1;
|
||||
yaw -= (e.clientX - drag.x) / w * Math.PI;
|
||||
pitch = Math.max(-1.2, Math.min(1.2, pitch - (e.clientY - drag.y) / w * Math.PI));
|
||||
drag = { x: e.clientX, y: e.clientY };
|
||||
}
|
||||
function onUp() { drag = null; }
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
window.addEventListener('keyup', onKeyUp);
|
||||
domElement.addEventListener('pointerdown', onDown);
|
||||
window.addEventListener('pointermove', onMove);
|
||||
window.addEventListener('pointerup', onUp);
|
||||
|
||||
function apply() {
|
||||
camera.position.set(x, o.floorAt(x, z) + o.eye, z);
|
||||
// yaw 0 = kijkrichting +Z; pitch > 0 = omhoog.
|
||||
const dir = new THREE.Vector3(Math.sin(yaw) * Math.cos(pitch), Math.sin(pitch), Math.cos(yaw) * Math.cos(pitch));
|
||||
camera.lookAt(camera.position.clone().add(dir));
|
||||
}
|
||||
|
||||
// Stap met botsen: eerst de hele stap, anders per as (langs een muur glijden).
|
||||
function step(dx, dz) {
|
||||
if (!o.blocked(x + dx, z + dz)) { x += dx; z += dz; return; }
|
||||
if (!o.blocked(x + dx, z)) x += dx;
|
||||
else if (!o.blocked(x, z + dz)) z += dz;
|
||||
}
|
||||
|
||||
return {
|
||||
get active() { return active; },
|
||||
start(pose) {
|
||||
x = pose.x; z = pose.z; yaw = pose.yaw || 0; pitch = 0;
|
||||
keys.clear();
|
||||
active = true;
|
||||
apply();
|
||||
},
|
||||
stop() { active = false; keys.clear(); drag = null; },
|
||||
update(dt) {
|
||||
if (!active) return;
|
||||
const t = Math.min(dt, 0.1);
|
||||
if (keys.has('arrowleft')) yaw += o.turn * t;
|
||||
if (keys.has('arrowright')) yaw -= o.turn * t;
|
||||
const f = (keys.has('w') || keys.has('arrowup') ? 1 : 0) - (keys.has('s') || keys.has('arrowdown') ? 1 : 0);
|
||||
const r = (keys.has('d') ? 1 : 0) - (keys.has('a') ? 1 : 0);
|
||||
if (f || r) {
|
||||
const v = (keys.has('shift') ? o.run : o.speed) * t / Math.hypot(f, r);
|
||||
const fx = Math.sin(yaw), fz = Math.cos(yaw); // vooruit
|
||||
const rx = -fz, rz = fx; // rechts (kijkend langs +Z is rechts -X)
|
||||
step((fx * f + rx * r) * v, (fz * f + rz * r) * v);
|
||||
}
|
||||
apply();
|
||||
},
|
||||
dispose() {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
window.removeEventListener('keyup', onKeyUp);
|
||||
domElement.removeEventListener('pointerdown', onDown);
|
||||
window.removeEventListener('pointermove', onMove);
|
||||
window.removeEventListener('pointerup', onUp);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user