bilca commited on
Commit
4d5ccce
·
verified ·
1 Parent(s): 7beb25d

Update points.js

Browse files
Files changed (1) hide show
  1. points.js +107 -80
points.js CHANGED
@@ -1,3 +1,5 @@
 
 
1
  /**
2
  * initializePoints(options)
3
  *
@@ -37,7 +39,7 @@ export async function initializePoints(options) {
37
  return;
38
  }
39
 
40
- const pointEntities = []; // store created sphere entities
41
 
42
  // Create a material for info-point spheres
43
  const mat = new pc.StandardMaterial();
@@ -46,53 +48,50 @@ export async function initializePoints(options) {
46
  mat.shininess = 20;
47
  mat.update();
48
 
49
- // Create each sphere + attach custom data
50
  for (let i = 0; i < pointsData.length; i++) {
51
  const pt = pointsData[i];
52
  const { x, y, z, text, imageUrl } = pt;
53
 
54
- // Create the sphere entity
55
  const sphere = new pc.Entity("point-" + i);
56
- sphere.addComponent("model", {
57
- type: "sphere"
58
- });
59
  sphere.model.material = mat;
60
 
61
- // Scale it small (primitive sphere has radius=0.5 by default)
62
  sphere.setLocalScale(0.05, 0.05, 0.05);
63
-
64
- // Position is absolute world coordinates
65
  sphere.setLocalPosition(x, y, z);
66
-
67
- // Attach custom data on entity for event details
68
  sphere.pointData = { text, imageUrl };
69
-
70
- // Add to scene
71
  app.root.addChild(sphere);
72
  pointEntities.push(sphere);
73
  }
74
 
75
- // Function to show/hide all point spheres
76
  function setPointsVisibility(visible) {
77
  pointEntities.forEach(ent => {
78
  ent.enabled = visible;
79
  });
80
  }
81
- // Initial visibility
82
  setPointsVisibility(!!defaultVisible);
83
 
84
- // Listen for toggle-points event from interface.js
85
  document.addEventListener("toggle-points", (evt) => {
86
  const { visible } = evt.detail;
87
  setPointsVisibility(!!visible);
88
  });
89
 
90
- // On mouse down, do a manual ray-sphere intersection test
 
 
 
91
  app.mouse.on(pc.EVENT_MOUSEDOWN, (event) => {
 
 
 
 
 
 
92
  const x = event.x;
93
  const y = event.y;
94
-
95
- // Build world-space ray from camera through screen pixel (x,y)
96
  const from = new pc.Vec3();
97
  const to = new pc.Vec3();
98
  const camera = cameraEntity.camera;
@@ -100,29 +99,24 @@ export async function initializePoints(options) {
100
  cameraEntity.camera.screenToWorld(x, y, camera.nearClip, from);
101
  cameraEntity.camera.screenToWorld(x, y, camera.farClip, to);
102
 
103
- // Compute ray direction
104
  const dir = new pc.Vec3().sub2(to, from).normalize();
105
 
106
- // Ray-sphere intersection for each point; track closest hit
107
  let closestT = Infinity;
108
  let pickedEntity = null;
109
 
110
  for (const ent of pointEntities) {
111
  if (!ent.enabled) continue;
112
 
113
- const center = ent.getPosition(); // world position of sphere center
114
- // Primitive sphere radius = 0.5, scaled by ent.getLocalScale().x
115
  const worldRadius = 0.5 * ent.getLocalScale().x;
116
 
117
- // Compute vector from ray origin to sphere center
118
  const oc = new pc.Vec3().sub2(center, from);
119
  const tca = oc.dot(dir);
120
- if (tca < 0) continue; // sphere is behind ray origin
121
 
122
  const d2 = oc.lengthSq() - (tca * tca);
123
- if (d2 > worldRadius * worldRadius) continue; // miss
124
 
125
- // Compute half-chord distance
126
  const thc = Math.sqrt(worldRadius * worldRadius - d2);
127
  const t0 = tca - thc;
128
  if (t0 < closestT && t0 >= 0) {
@@ -132,76 +126,109 @@ export async function initializePoints(options) {
132
  }
133
 
134
  if (pickedEntity) {
135
- // We clicked on a point sphere
136
  const { text, imageUrl } = pickedEntity.pointData;
137
-
138
- // Dispatch an event so interface.js can show tooltip
139
  document.dispatchEvent(new CustomEvent("point-selected", {
140
  detail: { text, imageUrl }
141
  }));
142
-
143
- // Smoothly move camera to face that point
144
- moveCameraToPoint(pickedEntity.getPosition(), moveDuration);
145
  }
146
  });
147
 
148
- // Helper: smoothly move cameraEntity to look at `targetPos`
149
- function moveCameraToPoint(targetPos, duration) {
150
- const camStartPos = cameraEntity.getPosition().clone();
151
- const camTargetPos = new pc.Vec3();
152
- // Compute direction from camera to target, then back off by same distance
153
- const dir = new pc.Vec3().sub2(camStartPos, targetPos).normalize();
154
- const dist = camStartPos.distance(targetPos);
155
- camTargetPos.copy(targetPos).add(dir.scale(dist));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  let elapsed = 0;
158
- const orgPos = camStartPos.clone();
 
 
 
 
 
 
 
 
 
159
 
160
- // On update, interpolate
161
  function lerpUpdate(dt) {
162
  elapsed += dt;
163
  const t = Math.min(elapsed / duration, 1);
164
 
165
- // Linear interpolation of position
166
- const newPos = new pc.Vec3().lerp(orgPos, camTargetPos, t);
167
- cameraEntity.setPosition(newPos);
168
- cameraEntity.lookAt(targetPos);
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
  if (t >= 1) {
171
- // Finalize pivot and distance in orbitCamera
172
- const orbitCam = cameraEntity.script.orbitCamera;
173
- if (orbitCam) {
174
- const newDistance = cameraEntity.getPosition().distance(targetPos);
175
- orbitCam.pivotPoint.copy(targetPos);
176
- orbitCam._targetDistance = newDistance;
177
- orbitCam._distance = newDistance;
178
-
179
- // Recompute yaw & pitch exactly as resetViewerCamera does
180
- const tempEnt = new pc.Entity();
181
- tempEnt.setPosition(newPos);
182
- tempEnt.lookAt(targetPos);
183
- const rotation = tempEnt.getRotation();
184
- const tempForward = new pc.Vec3();
185
- rotation.transformVector(pc.Vec3.FORWARD, tempForward);
186
-
187
- const yaw = Math.atan2(-tempForward.x, -tempForward.z) * pc.math.RAD_TO_DEG;
188
- const yawQuat = new pc.Quat().setFromEulerAngles(0, -yaw, 0);
189
- const rotWithoutYaw = new pc.Quat().mul2(yawQuat, rotation);
190
- const forwardWithoutYaw = new pc.Vec3();
191
- rotWithoutYaw.transformVector(pc.Vec3.FORWARD, forwardWithoutYaw);
192
- const pitch = Math.atan2(forwardWithoutYaw.y, -forwardWithoutYaw.z) * pc.math.RAD_TO_DEG;
193
-
194
- orbitCam._targetYaw = yaw;
195
- orbitCam._yaw = yaw;
196
- orbitCam._targetPitch = pitch;
197
- orbitCam._pitch = pitch;
198
- orbitCam._updatePosition();
199
-
200
- tempEnt.destroy();
201
- }
202
- app.off('update', lerpUpdate);
203
  }
204
  }
205
- app.on('update', lerpUpdate);
 
 
206
  }
207
  }
 
1
+ //points.js
2
+
3
  /**
4
  * initializePoints(options)
5
  *
 
39
  return;
40
  }
41
 
42
+ const pointEntities = [];
43
 
44
  // Create a material for info-point spheres
45
  const mat = new pc.StandardMaterial();
 
48
  mat.shininess = 20;
49
  mat.update();
50
 
51
+ // Build each sphere + attach custom data
52
  for (let i = 0; i < pointsData.length; i++) {
53
  const pt = pointsData[i];
54
  const { x, y, z, text, imageUrl } = pt;
55
 
 
56
  const sphere = new pc.Entity("point-" + i);
57
+ sphere.addComponent("model", { type: "sphere" });
 
 
58
  sphere.model.material = mat;
59
 
60
+ // Scale small (primitive sphere radius = 0.5)
61
  sphere.setLocalScale(0.05, 0.05, 0.05);
 
 
62
  sphere.setLocalPosition(x, y, z);
 
 
63
  sphere.pointData = { text, imageUrl };
 
 
64
  app.root.addChild(sphere);
65
  pointEntities.push(sphere);
66
  }
67
 
68
+ // Show/hide all point spheres
69
  function setPointsVisibility(visible) {
70
  pointEntities.forEach(ent => {
71
  ent.enabled = visible;
72
  });
73
  }
 
74
  setPointsVisibility(!!defaultVisible);
75
 
76
+ // Respond to toggle-points event from interface.js
77
  document.addEventListener("toggle-points", (evt) => {
78
  const { visible } = evt.detail;
79
  setPointsVisibility(!!visible);
80
  });
81
 
82
+ // Keep track of any in-flight camera tween so we can cancel it
83
+ let currentTween = null;
84
+
85
+ // On mouse down (or touch equivalent), perform manual ray‐sphere intersection
86
  app.mouse.on(pc.EVENT_MOUSEDOWN, (event) => {
87
+ // If a tween is running, cancel it immediately
88
+ if (currentTween) {
89
+ app.off("update", currentTween);
90
+ currentTween = null;
91
+ }
92
+
93
  const x = event.x;
94
  const y = event.y;
 
 
95
  const from = new pc.Vec3();
96
  const to = new pc.Vec3();
97
  const camera = cameraEntity.camera;
 
99
  cameraEntity.camera.screenToWorld(x, y, camera.nearClip, from);
100
  cameraEntity.camera.screenToWorld(x, y, camera.farClip, to);
101
 
 
102
  const dir = new pc.Vec3().sub2(to, from).normalize();
103
 
 
104
  let closestT = Infinity;
105
  let pickedEntity = null;
106
 
107
  for (const ent of pointEntities) {
108
  if (!ent.enabled) continue;
109
 
110
+ const center = ent.getPosition();
 
111
  const worldRadius = 0.5 * ent.getLocalScale().x;
112
 
 
113
  const oc = new pc.Vec3().sub2(center, from);
114
  const tca = oc.dot(dir);
115
+ if (tca < 0) continue;
116
 
117
  const d2 = oc.lengthSq() - (tca * tca);
118
+ if (d2 > worldRadius * worldRadius) continue;
119
 
 
120
  const thc = Math.sqrt(worldRadius * worldRadius - d2);
121
  const t0 = tca - thc;
122
  if (t0 < closestT && t0 >= 0) {
 
126
  }
127
 
128
  if (pickedEntity) {
 
129
  const { text, imageUrl } = pickedEntity.pointData;
 
 
130
  document.dispatchEvent(new CustomEvent("point-selected", {
131
  detail: { text, imageUrl }
132
  }));
133
+ tweenCameraToPoint(pickedEntity, moveDuration);
 
 
134
  }
135
  });
136
 
137
+ // Also close tooltip if user interacts (mouse or touch) on the canvas
138
+ const canvasId = app.graphicsDevice.canvas.id;
139
+ const htmlCanvas = document.getElementById(canvasId);
140
+ if (htmlCanvas) {
141
+ htmlCanvas.addEventListener("mousedown", () => {
142
+ document.dispatchEvent(new CustomEvent("hide-tooltip"));
143
+ });
144
+ htmlCanvas.addEventListener("touchstart", () => {
145
+ document.dispatchEvent(new CustomEvent("hide-tooltip"));
146
+ });
147
+ }
148
+
149
+ // Tween helper: smoothly move and reorient camera to focus the chosen point entity
150
+ function tweenCameraToPoint(pointEnt, duration) {
151
+ const orbitCam = cameraEntity.script.orbitCamera;
152
+ if (!orbitCam) return;
153
+
154
+ // Compute target pivot exactly at the sphere center
155
+ const targetPos = pointEnt.getPosition().clone();
156
+ // Compute current state
157
+ const startPivot = orbitCam.pivotPoint.clone();
158
+ const startYaw = orbitCam._yaw;
159
+ const startPitch = orbitCam._pitch;
160
+ const startDist = orbitCam._distance;
161
+
162
+ // Compute direction & candidate distance:
163
+ const worldRadius = 0.5 * pointEnt.getLocalScale().x;
164
+ const minZoom = orbitCam.distanceMin;
165
+ const desiredDistance = Math.max(minZoom * 1.2, worldRadius * 4);
166
+
167
+ // Compute target yaw/pitch from camera pointing at targetPos
168
+ // Reuse reset logic: place a temp entity at camera’s current position, have it look at target
169
+ const camWorldPos = cameraEntity.getPosition().clone();
170
+ const tempEnt = new pc.Entity();
171
+ tempEnt.setPosition(camWorldPos);
172
+ tempEnt.lookAt(targetPos);
173
+ const rotation = tempEnt.getRotation();
174
+ const forward = new pc.Vec3();
175
+ rotation.transformVector(pc.Vec3.FORWARD, forward);
176
+ const tgtYaw = Math.atan2(-forward.x, -forward.z) * pc.math.RAD_TO_DEG;
177
+ const yawQuat = new pc.Quat().setFromEulerAngles(0, -tgtYaw, 0);
178
+ const rotNoYaw = new pc.Quat().mul2(yawQuat, rotation);
179
+ const fNoYaw = new pc.Vec3();
180
+ rotNoYaw.transformVector(pc.Vec3.FORWARD, fNoYaw);
181
+ const tgtPitch = Math.atan2(fNoYaw.y, -fNoYaw.z) * pc.math.RAD_TO_DEG;
182
+ tempEnt.destroy();
183
+
184
+ // Target state:
185
+ const endPivot = targetPos.clone();
186
+ const endYaw = tgtYaw;
187
+ const endPitch = tgtPitch;
188
+ const endDist = desiredDistance;
189
 
190
  let elapsed = 0;
191
+ const orgPivot = startPivot.clone();
192
+ const orgYaw = startYaw;
193
+ const orgPitch = startPitch;
194
+ const orgDist = startDist;
195
+
196
+ // If another tween is running, cancel it
197
+ if (currentTween) {
198
+ app.off("update", currentTween);
199
+ currentTween = null;
200
+ }
201
 
202
+ // Per-frame update
203
  function lerpUpdate(dt) {
204
  elapsed += dt;
205
  const t = Math.min(elapsed / duration, 1);
206
 
207
+ // Interpolate pivot (vector lerp)
208
+ const newPivot = new pc.Vec3().lerp(orgPivot, endPivot, t);
209
+ orbitCam.pivotPoint.copy(newPivot);
210
+
211
+ // Interpolate yaw/pitch/distance (simple lerp)
212
+ const newYaw = pc.math.lerp(orgYaw, endYaw, t);
213
+ const newPitch = pc.math.lerp(orgPitch, endPitch, t);
214
+ const newDist = pc.math.lerp(orgDist, endDist, t);
215
+
216
+ orbitCam._targetYaw = newYaw;
217
+ orbitCam._yaw = newYaw;
218
+ orbitCam._targetPitch = newPitch;
219
+ orbitCam._pitch = newPitch;
220
+ orbitCam._targetDistance = newDist;
221
+ orbitCam._distance = newDist;
222
+
223
+ orbitCam._updatePosition();
224
 
225
  if (t >= 1) {
226
+ app.off("update", lerpUpdate);
227
+ currentTween = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  }
229
  }
230
+
231
+ currentTween = lerpUpdate;
232
+ app.on("update", lerpUpdate);
233
  }
234
  }