mirror of
https://github.com/isledecomp/isle.pizza.git
synced 2026-03-01 06:17:38 +00:00
Add drag-to-orbit controls to vehicle and actor editors
Use Three.js OrbitControls in BaseRenderer for rotation-only orbiting with damping. Vehicle editor auto-rotates and resets on part switch. Actor editor uses orbit without auto-rotate (has skeletal animations). Drag vs click detection uses pointermove threshold to avoid false positives from autoRotate damping.
This commit is contained in:
parent
f796ea4299
commit
fe9117dd5e
@ -94,6 +94,9 @@ export class ActorRenderer extends BaseRenderer {
|
||||
this.camera.position.set(2, 0.8, 3.5);
|
||||
this.camera.lookAt(0, 0.2, 0);
|
||||
|
||||
this.setupControls(new THREE.Vector3(0, 0.2, 0));
|
||||
this.controls.autoRotate = false;
|
||||
|
||||
this.raycaster = new THREE.Raycaster();
|
||||
}
|
||||
|
||||
@ -887,17 +890,13 @@ export class ActorRenderer extends BaseRenderer {
|
||||
|
||||
if (this.mixer) {
|
||||
this.mixer.update(delta);
|
||||
} else if (this.modelGroup) {
|
||||
// Fallback: rotate if no animation loaded
|
||||
this.modelGroup.rotation.y += 0.01;
|
||||
}
|
||||
this.controls?.update();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.animating = false;
|
||||
this.stopAnimation();
|
||||
this.clearModel();
|
||||
this.renderer?.dispose();
|
||||
super.dispose();
|
||||
this.animationCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
|
||||
/**
|
||||
* Base renderer providing shared Three.js setup, lighting, texture creation,
|
||||
@ -24,6 +25,9 @@ export class BaseRenderer {
|
||||
this.renderer.setClearColor(0x000000, 0);
|
||||
|
||||
this.setupLighting();
|
||||
|
||||
this.controls = null;
|
||||
this._didDrag = false;
|
||||
}
|
||||
|
||||
setupLighting() {
|
||||
@ -35,6 +39,39 @@ export class BaseRenderer {
|
||||
this.scene.add(sunLight);
|
||||
}
|
||||
|
||||
setupControls(target) {
|
||||
this.controls = new OrbitControls(this.camera, this.canvas);
|
||||
this.controls.enableZoom = false;
|
||||
this.controls.enablePan = false;
|
||||
this.controls.enableDamping = true;
|
||||
this.controls.dampingFactor = 0.1;
|
||||
this.controls.autoRotate = true;
|
||||
this.controls.autoRotateSpeed = 4.0;
|
||||
this.controls.target.copy(target);
|
||||
|
||||
this.controls.addEventListener('start', () => {
|
||||
this.controls.autoRotate = false;
|
||||
});
|
||||
|
||||
this._onPointerDown = (e) => {
|
||||
this._didDrag = false;
|
||||
this._pointerStart = { x: e.clientX, y: e.clientY };
|
||||
};
|
||||
this._onPointerMove = (e) => {
|
||||
if (!this._pointerStart) return;
|
||||
const dx = e.clientX - this._pointerStart.x;
|
||||
const dy = e.clientY - this._pointerStart.y;
|
||||
if (dx * dx + dy * dy > 9) this._didDrag = true;
|
||||
};
|
||||
|
||||
this.canvas.addEventListener('pointerdown', this._onPointerDown);
|
||||
this.canvas.addEventListener('pointermove', this._onPointerMove);
|
||||
}
|
||||
|
||||
wasDragged() {
|
||||
return this._didDrag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Three.js texture from parsed texture data
|
||||
*/
|
||||
@ -190,9 +227,7 @@ export class BaseRenderer {
|
||||
* Called each frame before rendering.
|
||||
*/
|
||||
updateAnimation() {
|
||||
if (this.modelGroup) {
|
||||
this.modelGroup.rotation.y += 0.01;
|
||||
}
|
||||
this.controls?.update();
|
||||
}
|
||||
|
||||
resize(width, height) {
|
||||
@ -203,6 +238,11 @@ export class BaseRenderer {
|
||||
|
||||
dispose() {
|
||||
this.animating = false;
|
||||
if (this.controls) {
|
||||
this.controls.dispose();
|
||||
this.canvas.removeEventListener('pointerdown', this._onPointerDown);
|
||||
this.canvas.removeEventListener('pointermove', this._onPointerMove);
|
||||
}
|
||||
this.clearModel();
|
||||
this.renderer?.dispose();
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ export class VehiclePartRenderer extends BaseRenderer {
|
||||
|
||||
this.camera.position.set(0, 0, 3);
|
||||
this.camera.lookAt(0, 0, 0);
|
||||
|
||||
this.setupControls(new THREE.Vector3(0, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,6 +58,7 @@ export class VehiclePartRenderer extends BaseRenderer {
|
||||
this.centerAndScaleModel(1.5);
|
||||
|
||||
this.scene.add(this.modelGroup);
|
||||
this.controls.autoRotate = true;
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
}
|
||||
|
||||
|
||||
@ -203,6 +203,7 @@
|
||||
|
||||
function handleCanvasClick(event) {
|
||||
if (!renderer || !slot?.characters || !charState) return;
|
||||
if (renderer.wasDragged()) return;
|
||||
|
||||
const playerId = slot.header?.actorId;
|
||||
let acted = false;
|
||||
@ -393,10 +394,14 @@
|
||||
canvas {
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
cursor: grab;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
canvas:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
canvas:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -251,6 +251,7 @@
|
||||
|
||||
function cycleColor() {
|
||||
if (!currentPart || partError) return;
|
||||
if (renderer?.wasDragged()) return;
|
||||
|
||||
// Find current color index and cycle to next
|
||||
const currentIdx = LegoColorNames.indexOf(currentColorValue);
|
||||
@ -398,10 +399,14 @@
|
||||
canvas {
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
cursor: grab;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
canvas:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
canvas:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user