@popaya/pgsg-viewer 0.1.11 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ var PGSGViewer = class {
10
10
  const { PGSGPlayCanvasViewer } = await import("./pgsg-playcanvas-viewer-4WORM4WU.js");
11
11
  this.engine = new PGSGPlayCanvasViewer(this.options);
12
12
  } else {
13
- const { PGSGThreeViewer } = await import("./pgsg-three-viewer-KBOYGHT3.js");
13
+ const { PGSGThreeViewer } = await import("./pgsg-three-viewer-B5AH6DJO.js");
14
14
  this.engine = new PGSGThreeViewer(this.options);
15
15
  }
16
16
  await this.engine.load();
@@ -37,9 +37,12 @@ var PGSGViewer = class {
37
37
  enablePlacementMode(url) {
38
38
  return this.engine?.enablePlacementMode?.(url);
39
39
  }
40
+ enableInput(enabled) {
41
+ return this.engine?.enableInput?.(enabled);
42
+ }
40
43
  };
41
44
 
42
45
  export {
43
46
  PGSGViewer
44
47
  };
45
- //# sourceMappingURL=chunk-Z7DBLRJH.js.map
48
+ //# sourceMappingURL=chunk-Q2W7KVNK.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/viewer/pgsg-viewer.ts"],"sourcesContent":["import * as THREE from \"three\";\n\nimport type {\n ViewerType,\n PGSGViewerEngine,\n CameraMode,\n} from \"../viewers/types\";\nimport type { PGSGViewerOptions } from \"../core/types\";\n\nexport class PGSGViewer {\n private engine!: PGSGViewerEngine;\n\n constructor(\n private options: PGSGViewerOptions & { viewerType?: ViewerType },\n ) {}\n\n async load() {\n const type = this.options.viewerType ?? \"three\";\n\n if (type === \"playcanvas\") {\n const { PGSGPlayCanvasViewer } =\n await import(\"../viewers/playcanvas/pgsg-playcanvas-viewer\");\n this.engine = new PGSGPlayCanvasViewer(this.options);\n } else {\n const { PGSGThreeViewer } =\n await import(\"../viewers/three/pgsg-three-viewer\");\n this.engine = new PGSGThreeViewer(this.options);\n }\n\n await this.engine.load();\n this.engine.start?.();\n }\n\n resize() {\n this.engine?.resize();\n }\n\n destroy() {\n this.engine?.destroy();\n }\n\n setCameraMode(mode: CameraMode) {\n this.engine?.setCameraMode?.(mode);\n }\n\n finalizePolyline() {\n (this.engine as any)?.finalizePolyline?.();\n }\n\n deleteMeasurement(index: number) {\n (this.engine as any)?.deleteMeasurement?.(index);\n }\n\n async addModelFromUrl(url: string, position?: THREE.Vector3) {\n return this.engine?.addModelFromUrl?.(url, position);\n }\n\n enablePlacementMode(url: string) {\n return this.engine?.enablePlacementMode?.(url);\n }\n}\n"],"mappings":";AASO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YACU,SACR;AADQ;AAAA,EACP;AAAA,EAJK;AAAA,EAMR,MAAM,OAAO;AACX,UAAM,OAAO,KAAK,QAAQ,cAAc;AAExC,QAAI,SAAS,cAAc;AACzB,YAAM,EAAE,qBAAqB,IAC3B,MAAM,OAAO,sCAA8C;AAC7D,WAAK,SAAS,IAAI,qBAAqB,KAAK,OAAO;AAAA,IACrD,OAAO;AACL,YAAM,EAAE,gBAAgB,IACtB,MAAM,OAAO,iCAAoC;AACnD,WAAK,SAAS,IAAI,gBAAgB,KAAK,OAAO;AAAA,IAChD;AAEA,UAAM,KAAK,OAAO,KAAK;AACvB,SAAK,OAAO,QAAQ;AAAA,EACtB;AAAA,EAEA,SAAS;AACP,SAAK,QAAQ,OAAO;AAAA,EACtB;AAAA,EAEA,UAAU;AACR,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,cAAc,MAAkB;AAC9B,SAAK,QAAQ,gBAAgB,IAAI;AAAA,EACnC;AAAA,EAEA,mBAAmB;AACjB,IAAC,KAAK,QAAgB,mBAAmB;AAAA,EAC3C;AAAA,EAEA,kBAAkB,OAAe;AAC/B,IAAC,KAAK,QAAgB,oBAAoB,KAAK;AAAA,EACjD;AAAA,EAEA,MAAM,gBAAgB,KAAa,UAA0B;AAC3D,WAAO,KAAK,QAAQ,kBAAkB,KAAK,QAAQ;AAAA,EACrD;AAAA,EAEA,oBAAoB,KAAa;AAC/B,WAAO,KAAK,QAAQ,sBAAsB,GAAG;AAAA,EAC/C;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/viewer/pgsg-viewer.ts"],"sourcesContent":["import * as THREE from \"three\";\n\nimport type {\n ViewerType,\n PGSGViewerEngine,\n CameraMode,\n} from \"../viewers/types\";\nimport type { PGSGViewerOptions } from \"../core/types\";\n\nexport class PGSGViewer {\n private engine!: PGSGViewerEngine;\n\n constructor(\n private options: PGSGViewerOptions & { viewerType?: ViewerType },\n ) {}\n\n async load() {\n const type = this.options.viewerType ?? \"three\";\n\n if (type === \"playcanvas\") {\n const { PGSGPlayCanvasViewer } =\n await import(\"../viewers/playcanvas/pgsg-playcanvas-viewer\");\n this.engine = new PGSGPlayCanvasViewer(this.options);\n } else {\n const { PGSGThreeViewer } =\n await import(\"../viewers/three/pgsg-three-viewer\");\n this.engine = new PGSGThreeViewer(this.options);\n }\n\n await this.engine.load();\n this.engine.start?.();\n }\n\n resize() {\n this.engine?.resize();\n }\n\n destroy() {\n this.engine?.destroy();\n }\n\n setCameraMode(mode: CameraMode) {\n this.engine?.setCameraMode?.(mode);\n }\n\n finalizePolyline() {\n (this.engine as any)?.finalizePolyline?.();\n }\n\n deleteMeasurement(index: number) {\n (this.engine as any)?.deleteMeasurement?.(index);\n }\n\n async addModelFromUrl(url: string, position?: THREE.Vector3) {\n return this.engine?.addModelFromUrl?.(url, position);\n }\n\n enablePlacementMode(url: string) {\n return this.engine?.enablePlacementMode?.(url);\n }\n\n enableInput(enabled: boolean) {\n return this.engine?.enableInput?.(enabled);\n }\n}\n"],"mappings":";AASO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YACU,SACR;AADQ;AAAA,EACP;AAAA,EAJK;AAAA,EAMR,MAAM,OAAO;AACX,UAAM,OAAO,KAAK,QAAQ,cAAc;AAExC,QAAI,SAAS,cAAc;AACzB,YAAM,EAAE,qBAAqB,IAC3B,MAAM,OAAO,sCAA8C;AAC7D,WAAK,SAAS,IAAI,qBAAqB,KAAK,OAAO;AAAA,IACrD,OAAO;AACL,YAAM,EAAE,gBAAgB,IACtB,MAAM,OAAO,iCAAoC;AACnD,WAAK,SAAS,IAAI,gBAAgB,KAAK,OAAO;AAAA,IAChD;AAEA,UAAM,KAAK,OAAO,KAAK;AACvB,SAAK,OAAO,QAAQ;AAAA,EACtB;AAAA,EAEA,SAAS;AACP,SAAK,QAAQ,OAAO;AAAA,EACtB;AAAA,EAEA,UAAU;AACR,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,cAAc,MAAkB;AAC9B,SAAK,QAAQ,gBAAgB,IAAI;AAAA,EACnC;AAAA,EAEA,mBAAmB;AACjB,IAAC,KAAK,QAAgB,mBAAmB;AAAA,EAC3C;AAAA,EAEA,kBAAkB,OAAe;AAC/B,IAAC,KAAK,QAAgB,oBAAoB,KAAK;AAAA,EACjD;AAAA,EAEA,MAAM,gBAAgB,KAAa,UAA0B;AAC3D,WAAO,KAAK,QAAQ,kBAAkB,KAAK,QAAQ;AAAA,EACrD;AAAA,EAEA,oBAAoB,KAAa;AAC/B,WAAO,KAAK,QAAQ,sBAAsB,GAAG;AAAA,EAC/C;AAAA,EAEA,YAAY,SAAkB;AAC5B,WAAO,KAAK,QAAQ,cAAc,OAAO;AAAA,EAC3C;AACF;","names":[]}
@@ -51,6 +51,7 @@ declare class PGSGViewer {
51
51
  deleteMeasurement(index: number): void;
52
52
  addModelFromUrl(url: string, position?: THREE.Vector3): Promise<THREE.Object3D<THREE.Object3DEventMap> | null | undefined>;
53
53
  enablePlacementMode(url: string): void | undefined;
54
+ enableInput(enabled: boolean): void | undefined;
54
55
  }
55
56
 
56
57
  export { type CameraOptions, type ControlOptions, type PGSGSource, PGSGViewer, type PGSGViewerOptions, type PLYSource, type SourceType, type ViewerSource };
@@ -1,7 +1,7 @@
1
1
  import "../chunk-PYYLHUV6.js";
2
2
  import {
3
3
  PGSGViewer
4
- } from "../chunk-Z7DBLRJH.js";
4
+ } from "../chunk-Q2W7KVNK.js";
5
5
  export {
6
6
  PGSGViewer
7
7
  };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./chunk-PYYLHUV6.js";
2
2
  import {
3
3
  PGSGViewer
4
- } from "./chunk-Z7DBLRJH.js";
4
+ } from "./chunk-Q2W7KVNK.js";
5
5
  export {
6
6
  PGSGViewer
7
7
  };
@@ -1,7 +1,11 @@
1
1
  // src/viewers/three/pgsg-three-viewer.ts
2
2
  import * as THREE5 from "three";
3
- import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
4
- import { TransformControls } from "three/addons/controls/TransformControls.js";
3
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
4
+ import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
5
+ import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
6
+ import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
7
+ import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
8
+ import { OutputPass } from "three/examples/jsm/postprocessing/OutputPass.js";
5
9
  import * as RAPIER2 from "@dimforge/rapier3d-compat";
6
10
  import nipplejs from "nipplejs";
7
11
 
@@ -294,6 +298,7 @@ var OrbitCameraController = class {
294
298
  lastTouchY = 0;
295
299
  externalMove = new THREE2.Vector2(0, 0);
296
300
  moveSpeed = 5;
301
+ enabled = true;
297
302
  constructor(camera, dom, opts) {
298
303
  this.camera = camera;
299
304
  this.dom = dom;
@@ -371,6 +376,7 @@ var OrbitCameraController = class {
371
376
  // Input
372
377
  // -----------------------------
373
378
  onMouseDown = (e) => {
379
+ if (!this.enabled) return;
374
380
  if (e.button === 0) {
375
381
  this.dragging = true;
376
382
  } else if (e.button === 2 || e.button === 1) {
@@ -384,6 +390,7 @@ var OrbitCameraController = class {
384
390
  this.panning = false;
385
391
  };
386
392
  onMouseMove = (e) => {
393
+ if (!this.enabled) return;
387
394
  if (!this.dragging && !this.panning) return;
388
395
  const dx = e.clientX - this.lastX;
389
396
  const dy = e.clientY - this.lastY;
@@ -412,6 +419,7 @@ var OrbitCameraController = class {
412
419
  }
413
420
  };
414
421
  onWheel = (e) => {
422
+ if (!this.enabled) return;
415
423
  e.preventDefault();
416
424
  const zoomFactor = Math.pow(this.zoomSpeed, e.deltaY / 100);
417
425
  this.distance *= zoomFactor;
@@ -423,11 +431,13 @@ var OrbitCameraController = class {
423
431
  this.updateCamera();
424
432
  };
425
433
  onTouchStart = (e) => {
434
+ if (!this.enabled) return;
426
435
  const t = e.touches[0];
427
436
  this.lastTouchX = t.clientX;
428
437
  this.lastTouchY = t.clientY;
429
438
  };
430
439
  onTouchMove = (e) => {
440
+ if (!this.enabled) return;
431
441
  const t = e.touches[0];
432
442
  const deltaX = t.clientX - this.lastTouchX;
433
443
  const deltaY = t.clientY - this.lastTouchY;
@@ -1005,6 +1015,15 @@ var PGSGThreeViewer = class {
1005
1015
  mouse = new THREE5.Vector2();
1006
1016
  placementRotation = 0;
1007
1017
  transformControls;
1018
+ inputEnabled = true;
1019
+ placementMouseMoved = false;
1020
+ placedObjects = [];
1021
+ ghostYOffset = 0;
1022
+ composer;
1023
+ hoverOutline;
1024
+ selectOutline;
1025
+ selectedObject;
1026
+ hoveredObject;
1008
1027
  container;
1009
1028
  resizeObserver = null;
1010
1029
  controls;
@@ -1031,21 +1050,28 @@ var PGSGThreeViewer = class {
1031
1050
  this.initRenderer();
1032
1051
  this.initCamera();
1033
1052
  this.initScene();
1053
+ this.initPostProcessing();
1034
1054
  this.bindResize();
1035
1055
  this.resize();
1036
1056
  this.transformControls = new TransformControls(
1037
1057
  this.camera,
1038
1058
  this.renderer.domElement
1039
1059
  );
1040
- this.scene.add(this.transformControls);
1041
- console.log(this.transformControls instanceof THREE5.Object3D);
1060
+ this.scene.add(this.transformControls.getHelper());
1061
+ console.log(this.transformControls.getHelper() instanceof THREE5.Object3D);
1042
1062
  this.transformControls.addEventListener("dragging-changed", (event) => {
1043
1063
  if (!this.controls) return;
1044
1064
  this.controls.enabled = !event.value;
1045
1065
  });
1046
1066
  this.transformControls.addEventListener("mouseDown", () => {
1047
1067
  document.exitPointerLock?.();
1068
+ this.controls.enabled = false;
1048
1069
  });
1070
+ this.transformControls.addEventListener("mouseUp", () => {
1071
+ this.controls.enabled = true;
1072
+ });
1073
+ this.transformControls.setSize(0.8);
1074
+ this.transformControls.setSpace("world");
1049
1075
  await RAPIER2.init();
1050
1076
  this.world = new RAPIER2.World({
1051
1077
  x: 0,
@@ -1073,12 +1099,26 @@ var PGSGThreeViewer = class {
1073
1099
  }
1074
1100
  window.addEventListener("keydown", this.onKeyToggle);
1075
1101
  window.addEventListener("keydown", (e) => {
1102
+ if (!this.inputEnabled) return;
1076
1103
  if (!this.transformControls) return;
1077
1104
  if (e.key === "r") this.transformControls.setMode("rotate");
1078
1105
  if (e.key === "t") this.transformControls.setMode("translate");
1079
1106
  if (e.key === "s") this.transformControls.setMode("scale");
1080
1107
  });
1081
- this.renderer.domElement.addEventListener("click", this.onSceneClick);
1108
+ this.renderer.domElement.addEventListener("mousedown", () => {
1109
+ this.placementMouseMoved = false;
1110
+ });
1111
+ this.renderer.domElement.addEventListener("mousemove", () => {
1112
+ this.placementMouseMoved = true;
1113
+ });
1114
+ this.renderer.domElement.addEventListener("mouseup", (e) => {
1115
+ if (!this.placementMouseMoved) {
1116
+ this.onSceneClick(e);
1117
+ }
1118
+ });
1119
+ this.renderer.domElement.addEventListener("click", this.onSelectObject);
1120
+ this.renderer.domElement.addEventListener("mousemove", this.onHoverObject);
1121
+ window.addEventListener("keydown", this.onDeleteObject);
1082
1122
  }
1083
1123
  start() {
1084
1124
  if (this.running) return;
@@ -1094,7 +1134,7 @@ var PGSGThreeViewer = class {
1094
1134
  this.acc -= this.fixedDt;
1095
1135
  }
1096
1136
  this.controls?.update?.(delta);
1097
- this.renderer.render(this.scene, this.camera);
1137
+ this.composer.render();
1098
1138
  });
1099
1139
  }
1100
1140
  pause() {
@@ -1105,6 +1145,9 @@ var PGSGThreeViewer = class {
1105
1145
  const w = Math.max(1, this.container.clientWidth);
1106
1146
  const h = Math.max(1, this.container.clientHeight);
1107
1147
  this.renderer.setSize(w, h);
1148
+ this.composer?.setSize(w, h);
1149
+ this.hoverOutline?.setSize(w, h);
1150
+ this.selectOutline?.setSize(w, h);
1108
1151
  this.camera.aspect = w / h;
1109
1152
  this.camera.updateProjectionMatrix();
1110
1153
  }
@@ -1166,6 +1209,7 @@ var PGSGThreeViewer = class {
1166
1209
  this.pendingPlacementUrl = url;
1167
1210
  this.placementRotation = 0;
1168
1211
  console.log("[PGSG Viewer] Placement mode enabled");
1212
+ this.controls.enabled = false;
1169
1213
  const gltf = await this.gltfLoader.loadAsync(url);
1170
1214
  this.ghostModel = gltf.scene;
1171
1215
  const box = new THREE5.Box3().setFromObject(this.ghostModel);
@@ -1186,8 +1230,17 @@ var PGSGThreeViewer = class {
1186
1230
  this.onPlacementMove
1187
1231
  );
1188
1232
  this.renderer.domElement.addEventListener("wheel", this.onPlacementWheel);
1189
- this.renderer.domElement.addEventListener("click", this.onSceneClick);
1190
1233
  window.addEventListener("keydown", this.onPlacementCancel);
1234
+ const rect = this.renderer.domElement.getBoundingClientRect();
1235
+ this.mouse.set(0, 0);
1236
+ this.raycaster.setFromCamera(this.mouse, this.camera);
1237
+ const intersects = this.raycaster.intersectObjects(
1238
+ this.colliderMeshes,
1239
+ true
1240
+ );
1241
+ if (intersects.length) {
1242
+ this.ghostModel.position.copy(intersects[0].point);
1243
+ }
1191
1244
  }
1192
1245
  async addModelFromUrl(url, position) {
1193
1246
  return new Promise((resolve, reject) => {
@@ -1214,6 +1267,9 @@ var PGSGThreeViewer = class {
1214
1267
  );
1215
1268
  });
1216
1269
  }
1270
+ enableInput(enabled) {
1271
+ this.inputEnabled = enabled;
1272
+ }
1217
1273
  // ---------------------------
1218
1274
  // Init
1219
1275
  // ---------------------------
@@ -1233,14 +1289,21 @@ var PGSGThreeViewer = class {
1233
1289
  this.renderer.setPixelRatio(
1234
1290
  this.options.renderer?.pixelRatio ?? window.devicePixelRatio
1235
1291
  );
1292
+ this.renderer.toneMapping = THREE5.ACESFilmicToneMapping;
1293
+ this.renderer.toneMappingExposure = 1.2;
1236
1294
  this.renderer.localClippingEnabled = true;
1237
1295
  this.container.appendChild(this.renderer.domElement);
1238
1296
  }
1239
1297
  initScene() {
1240
1298
  this.scene = new THREE5.Scene();
1241
1299
  this.scene.background = new THREE5.Color(1118481);
1242
- const light = new THREE5.HemisphereLight(16777215, 2236962, 1);
1243
- this.scene.add(light);
1300
+ const hemi = new THREE5.HemisphereLight(16777215, 4473924, 1.2);
1301
+ this.scene.add(hemi);
1302
+ const dir = new THREE5.DirectionalLight(16777215, 2.5);
1303
+ dir.position.set(5, 10, 5);
1304
+ this.scene.add(dir);
1305
+ const amb = new THREE5.AmbientLight(16777215, 0.35);
1306
+ this.scene.add(amb);
1244
1307
  }
1245
1308
  initCamera() {
1246
1309
  this.camera = new THREE5.PerspectiveCamera(
@@ -1252,6 +1315,31 @@ var PGSGThreeViewer = class {
1252
1315
  this.camera.position.set(2, 2, 5);
1253
1316
  this.camera.lookAt(0, 0, 0);
1254
1317
  }
1318
+ initPostProcessing() {
1319
+ this.composer = new EffectComposer(this.renderer);
1320
+ this.composer.addPass(new RenderPass(this.scene, this.camera));
1321
+ const size = new THREE5.Vector2(
1322
+ this.container.clientWidth,
1323
+ this.container.clientHeight
1324
+ );
1325
+ this.hoverOutline = new OutlinePass(size, this.scene, this.camera);
1326
+ this.hoverOutline.edgeStrength = 6;
1327
+ this.hoverOutline.edgeGlow = 1;
1328
+ this.hoverOutline.edgeThickness = 2;
1329
+ this.hoverOutline.pulsePeriod = 0;
1330
+ this.hoverOutline.visibleEdgeColor.set("#00e5ff");
1331
+ this.hoverOutline.hiddenEdgeColor.set("#00e5ff");
1332
+ this.composer.addPass(this.hoverOutline);
1333
+ this.selectOutline = new OutlinePass(size, this.scene, this.camera);
1334
+ this.selectOutline.edgeStrength = 8;
1335
+ this.selectOutline.edgeGlow = 1.2;
1336
+ this.selectOutline.edgeThickness = 2.5;
1337
+ this.selectOutline.pulsePeriod = 0;
1338
+ this.selectOutline.visibleEdgeColor.set("#ffd400");
1339
+ this.selectOutline.hiddenEdgeColor.set("#ffd400");
1340
+ this.composer.addPass(this.selectOutline);
1341
+ this.composer.addPass(new OutputPass());
1342
+ }
1255
1343
  bindResize() {
1256
1344
  this.resizeObserver?.disconnect();
1257
1345
  this.resizeObserver = new ResizeObserver(() => this.resize());
@@ -1293,6 +1381,7 @@ var PGSGThreeViewer = class {
1293
1381
  cd.setFriction(0.9);
1294
1382
  cd.setRestitution(0);
1295
1383
  this.world.createCollider(cd, rb);
1384
+ child.visible = false;
1296
1385
  this.colliderMeshes.push(child);
1297
1386
  });
1298
1387
  const rawBox = new THREE5.Box3().setFromObject(colliderRoot);
@@ -1303,6 +1392,7 @@ var PGSGThreeViewer = class {
1303
1392
  // Mode switching
1304
1393
  // ---------------------------
1305
1394
  onKeyToggle = (e) => {
1395
+ if (!this.inputEnabled) return;
1306
1396
  if (e.key !== "v") return;
1307
1397
  if (this.controls instanceof RapierWalkController) this.switchToOrbit();
1308
1398
  else this.switchToWalk();
@@ -1482,16 +1572,21 @@ var PGSGThreeViewer = class {
1482
1572
  return hits;
1483
1573
  }
1484
1574
  onSceneClick = async (event) => {
1575
+ if (this.placementMouseMoved) return;
1485
1576
  if (!this.pendingPlacementUrl || !this.ghostModel) return;
1486
1577
  const position = this.ghostModel.position.clone();
1487
1578
  const placed = await this.addModelFromUrl(
1488
1579
  this.pendingPlacementUrl,
1489
1580
  position
1490
1581
  );
1582
+ if (placed) {
1583
+ this.placedObjects.push(placed);
1584
+ }
1491
1585
  if (placed && this.transformControls) {
1492
1586
  placed.rotation.y = this.placementRotation;
1493
1587
  this.transformControls.attach(placed);
1494
1588
  }
1589
+ this.controls.enabled = true;
1495
1590
  this.scene.remove(this.ghostModel);
1496
1591
  this.ghostModel = void 0;
1497
1592
  this.pendingPlacementUrl = void 0;
@@ -1503,7 +1598,6 @@ var PGSGThreeViewer = class {
1503
1598
  "wheel",
1504
1599
  this.onPlacementWheel
1505
1600
  );
1506
- this.renderer.domElement.removeEventListener("click", this.onSceneClick);
1507
1601
  window.removeEventListener("keydown", this.onPlacementCancel);
1508
1602
  };
1509
1603
  onPlacementMove = (event) => {
@@ -1519,10 +1613,23 @@ var PGSGThreeViewer = class {
1519
1613
  true
1520
1614
  );
1521
1615
  if (!intersects.length) return;
1522
- const point = intersects[0].point;
1523
- this.ghostModel.position.copy(point);
1616
+ const hit = intersects[0];
1617
+ const box = new THREE5.Box3().setFromObject(this.ghostModel);
1618
+ this.ghostYOffset = -box.min.y;
1619
+ this.ghostModel.position.copy(hit.point);
1620
+ this.ghostModel.position.y += this.ghostYOffset;
1621
+ if (hit.face) {
1622
+ const normal = this.getWorldNormalSafe(hit);
1623
+ if (normal) {
1624
+ const up = new THREE5.Vector3(0, 1, 0);
1625
+ const quat = new THREE5.Quaternion().setFromUnitVectors(up, normal);
1626
+ this.ghostModel.quaternion.copy(quat);
1627
+ }
1628
+ }
1524
1629
  };
1525
1630
  onPlacementWheel = (event) => {
1631
+ event.preventDefault();
1632
+ event.stopPropagation();
1526
1633
  if (!this.ghostModel) return;
1527
1634
  event.preventDefault();
1528
1635
  const step = Math.PI / 12;
@@ -1552,8 +1659,71 @@ var PGSGThreeViewer = class {
1552
1659
  );
1553
1660
  window.removeEventListener("keydown", this.onPlacementCancel);
1554
1661
  };
1662
+ onSelectObject = (event) => {
1663
+ if (this.pendingPlacementUrl) return;
1664
+ if (this.transformControls?.dragging) return;
1665
+ const rect = this.renderer.domElement.getBoundingClientRect();
1666
+ this.mouse.set(
1667
+ (event.clientX - rect.left) / rect.width * 2 - 1,
1668
+ -((event.clientY - rect.top) / rect.height) * 2 + 1
1669
+ );
1670
+ this.raycaster.setFromCamera(this.mouse, this.camera);
1671
+ const hits = this.raycaster.intersectObjects(this.placedObjects, true);
1672
+ if (!hits.length) {
1673
+ this.selectedObject = void 0;
1674
+ this.transformControls?.detach();
1675
+ this.selectOutline.selectedObjects = [];
1676
+ return;
1677
+ }
1678
+ let root = hits[0].object;
1679
+ while (root && !this.placedObjects.includes(root)) root = root.parent;
1680
+ if (!root) return;
1681
+ this.selectedObject = root;
1682
+ this.transformControls?.attach(root);
1683
+ this.selectOutline.selectedObjects = [root];
1684
+ };
1685
+ onHoverObject = (event) => {
1686
+ if (this.pendingPlacementUrl) return;
1687
+ if (this.transformControls?.dragging) return;
1688
+ const rect = this.renderer.domElement.getBoundingClientRect();
1689
+ this.mouse.set(
1690
+ (event.clientX - rect.left) / rect.width * 2 - 1,
1691
+ -((event.clientY - rect.top) / rect.height) * 2 + 1
1692
+ );
1693
+ this.raycaster.setFromCamera(this.mouse, this.camera);
1694
+ const hits = this.raycaster.intersectObjects(this.placedObjects, true);
1695
+ if (!hits.length) {
1696
+ this.hoveredObject = void 0;
1697
+ this.hoverOutline.selectedObjects = [];
1698
+ this.renderer.domElement.style.cursor = "default";
1699
+ return;
1700
+ }
1701
+ let root = hits[0].object;
1702
+ while (root && !this.placedObjects.includes(root)) root = root.parent;
1703
+ if (!root) return;
1704
+ if (root !== this.hoveredObject) {
1705
+ this.hoveredObject = root;
1706
+ this.hoverOutline.selectedObjects = [root];
1707
+ this.renderer.domElement.style.cursor = "pointer";
1708
+ }
1709
+ };
1710
+ onDeleteObject = (event) => {
1711
+ const tag = event.target?.tagName?.toLowerCase();
1712
+ if (tag === "input" || tag === "textarea") return;
1713
+ if (event.key !== "Delete" && event.key !== "Backspace") return;
1714
+ if (!this.selectedObject) return;
1715
+ event.preventDefault();
1716
+ this.selectedObject.parent?.remove(this.selectedObject);
1717
+ this.placedObjects = this.placedObjects.filter(
1718
+ (o) => o !== this.selectedObject
1719
+ );
1720
+ this.transformControls?.detach();
1721
+ this.selectOutline.selectedObjects = [];
1722
+ this.hoverOutline.selectedObjects = [];
1723
+ this.selectedObject = void 0;
1724
+ };
1555
1725
  };
1556
1726
  export {
1557
1727
  PGSGThreeViewer
1558
1728
  };
1559
- //# sourceMappingURL=pgsg-three-viewer-KBOYGHT3.js.map
1729
+ //# sourceMappingURL=pgsg-three-viewer-B5AH6DJO.js.map