optimize raycasting
All checks were successful
Deploy to GitHub Pages / deploy (push) Has been skipped
All checks were successful
Deploy to GitHub Pages / deploy (push) Has been skipped
This commit is contained in:
@@ -15,17 +15,27 @@ export class InputManager {
|
|||||||
this.dragObject = null;
|
this.dragObject = null;
|
||||||
this.isPanning = false;
|
this.isPanning = false;
|
||||||
|
|
||||||
|
// Optimization: Cache the ground mesh so we don't search for it every frame
|
||||||
|
this.groundMesh = null;
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
this.onClick = null; // (point, object) -> void
|
this.onClick = null;
|
||||||
this.onDrag = null; // (object, newPoint) -> void
|
this.onDrag = null;
|
||||||
this.onDragEnd = null; // () -> void
|
this.onDragEnd = null;
|
||||||
this.onHover = null; // (point) -> void <-- NEW
|
this.onHover = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this));
|
this.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this));
|
||||||
this.domElement.addEventListener('pointermove', this.onPointerMove.bind(this));
|
this.domElement.addEventListener('pointermove', this.onPointerMove.bind(this));
|
||||||
this.domElement.addEventListener('pointerup', this.onPointerUp.bind(this));
|
this.domElement.addEventListener('pointerup', this.onPointerUp.bind(this));
|
||||||
|
|
||||||
|
// OPTIMIZATION 1: Find and cache the ground mesh once.
|
||||||
|
// We assume the object is named "GROUND" as per main.js
|
||||||
|
this.groundMesh = this.scene.getObjectByName("GROUND");
|
||||||
|
if (!this.groundMesh) {
|
||||||
|
console.warn("InputManager: Ground mesh not found during init. Raycasting may fail.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPointerDown(event) {
|
onPointerDown(event) {
|
||||||
@@ -48,6 +58,7 @@ export class InputManager {
|
|||||||
onPointerMove(event) {
|
onPointerMove(event) {
|
||||||
// Case A: Dragging a Marker
|
// Case A: Dragging a Marker
|
||||||
if (this.dragObject) {
|
if (this.dragObject) {
|
||||||
|
// Use the optimized ground check
|
||||||
const hit = this.raycastGround(event);
|
const hit = this.raycastGround(event);
|
||||||
if (hit && this.onDrag) {
|
if (hit && this.onDrag) {
|
||||||
this.onDrag(this.dragObject, hit.point);
|
this.onDrag(this.dragObject, hit.point);
|
||||||
@@ -55,8 +66,8 @@ export class InputManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case B: Hovering (Ghost Marker Logic) <-- NEW
|
// Case B: Hovering (Ghost Marker Logic)
|
||||||
// We only care about hovering the ground for placing new nodes
|
// OPTIMIZATION: This runs every frame. It must be blazing fast.
|
||||||
const hit = this.raycastGround(event);
|
const hit = this.raycastGround(event);
|
||||||
if (hit && this.onHover) {
|
if (hit && this.onHover) {
|
||||||
this.onHover(hit.point);
|
this.onHover(hit.point);
|
||||||
@@ -94,19 +105,47 @@ export class InputManager {
|
|||||||
return new THREE.Vector2(x, y);
|
return new THREE.Vector2(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OPTIMIZATION 2: Filtered Raycast
|
||||||
|
* Instead of checking scene.children (which checks 300k triangles in the city mesh),
|
||||||
|
* we construct a small list of interactable objects: [Ground, ...Markers].
|
||||||
|
*/
|
||||||
raycast(event) {
|
raycast(event) {
|
||||||
|
if (!this.groundMesh) this.groundMesh = this.scene.getObjectByName("GROUND");
|
||||||
|
|
||||||
this.raycaster.setFromCamera(this.getMouse(event), this.camera);
|
this.raycaster.setFromCamera(this.getMouse(event), this.camera);
|
||||||
// Ignore Ghost Marker in standard raycast interaction
|
|
||||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true);
|
// 1. Gather interactables
|
||||||
return intersects.find(obj =>
|
const interactables = [this.groundMesh];
|
||||||
(obj.object.name === "GROUND" || obj.object.userData.isMarker) &&
|
|
||||||
obj.object.name !== "GHOST_MARKER"
|
// Efficiently gather markers without traversing deep hierarchies
|
||||||
);
|
// We scan the top level children. If markers are grouped, update this loop.
|
||||||
|
for (let i = 0; i < this.scene.children.length; i++) {
|
||||||
|
const child = this.scene.children[i];
|
||||||
|
if (child.userData.isMarker && child.name !== "GHOST_MARKER") {
|
||||||
|
interactables.push(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Raycast against ONLY these objects
|
||||||
|
// Recursive = false (we assume markers and ground are simple meshes)
|
||||||
|
const intersects = this.raycaster.intersectObjects(interactables, false);
|
||||||
|
|
||||||
|
return intersects.length > 0 ? intersects[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OPTIMIZATION 3: Dedicated Ground Raycast
|
||||||
|
* Only checks the ground plane.
|
||||||
|
* Used heavily in onPointerMove.
|
||||||
|
*/
|
||||||
raycastGround(event) {
|
raycastGround(event) {
|
||||||
|
if (!this.groundMesh) return null;
|
||||||
|
|
||||||
this.raycaster.setFromCamera(this.getMouse(event), this.camera);
|
this.raycaster.setFromCamera(this.getMouse(event), this.camera);
|
||||||
const intersects = this.raycaster.intersectObjects(this.scene.children, true);
|
|
||||||
return intersects.find(obj => obj.object.name === "GROUND");
|
// intersectObject (singular), recursive = false
|
||||||
|
const intersects = this.raycaster.intersectObject(this.groundMesh, false);
|
||||||
|
return intersects.length > 0 ? intersects[0] : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ let currentViewMode = 'none'; // 'none', 'zoning', 'approval'
|
|||||||
function init() {
|
function init() {
|
||||||
setupScene();
|
setupScene();
|
||||||
|
|
||||||
|
|
||||||
// 1. Core Systems
|
// 1. Core Systems
|
||||||
routeManager = new RouteManager(scene, SETTINGS);
|
routeManager = new RouteManager(scene, SETTINGS);
|
||||||
uiManager = new UIManager(routeManager);
|
uiManager = new UIManager(routeManager);
|
||||||
@@ -381,7 +382,6 @@ function renderCity(data) {
|
|||||||
// 7. Material Setup
|
// 7. Material Setup
|
||||||
const mat = new THREE.MeshLambertMaterial({
|
const mat = new THREE.MeshLambertMaterial({
|
||||||
vertexColors: true, // IMPORTANT
|
vertexColors: true, // IMPORTANT
|
||||||
roughness: 0.6,
|
|
||||||
shadowSide: THREE.BackSide
|
shadowSide: THREE.BackSide
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user