@popaya/pgsg-viewer 0.2.0 → 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-5BUDBGSE.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();
@@ -45,4 +45,4 @@ var PGSGViewer = class {
45
45
  export {
46
46
  PGSGViewer
47
47
  };
48
- //# sourceMappingURL=chunk-LFICZTG7.js.map
48
+ //# sourceMappingURL=chunk-Q2W7KVNK.js.map
@@ -1,7 +1,7 @@
1
1
  import "../chunk-PYYLHUV6.js";
2
2
  import {
3
3
  PGSGViewer
4
- } from "../chunk-LFICZTG7.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-LFICZTG7.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;
@@ -1006,6 +1016,14 @@ var PGSGThreeViewer = class {
1006
1016
  placementRotation = 0;
1007
1017
  transformControls;
1008
1018
  inputEnabled = true;
1019
+ placementMouseMoved = false;
1020
+ placedObjects = [];
1021
+ ghostYOffset = 0;
1022
+ composer;
1023
+ hoverOutline;
1024
+ selectOutline;
1025
+ selectedObject;
1026
+ hoveredObject;
1009
1027
  container;
1010
1028
  resizeObserver = null;
1011
1029
  controls;
@@ -1032,21 +1050,28 @@ var PGSGThreeViewer = class {
1032
1050
  this.initRenderer();
1033
1051
  this.initCamera();
1034
1052
  this.initScene();
1053
+ this.initPostProcessing();
1035
1054
  this.bindResize();
1036
1055
  this.resize();
1037
1056
  this.transformControls = new TransformControls(
1038
1057
  this.camera,
1039
1058
  this.renderer.domElement
1040
1059
  );
1041
- this.scene.add(this.transformControls);
1042
- console.log(this.transformControls instanceof THREE5.Object3D);
1060
+ this.scene.add(this.transformControls.getHelper());
1061
+ console.log(this.transformControls.getHelper() instanceof THREE5.Object3D);
1043
1062
  this.transformControls.addEventListener("dragging-changed", (event) => {
1044
1063
  if (!this.controls) return;
1045
1064
  this.controls.enabled = !event.value;
1046
1065
  });
1047
1066
  this.transformControls.addEventListener("mouseDown", () => {
1048
1067
  document.exitPointerLock?.();
1068
+ this.controls.enabled = false;
1049
1069
  });
1070
+ this.transformControls.addEventListener("mouseUp", () => {
1071
+ this.controls.enabled = true;
1072
+ });
1073
+ this.transformControls.setSize(0.8);
1074
+ this.transformControls.setSpace("world");
1050
1075
  await RAPIER2.init();
1051
1076
  this.world = new RAPIER2.World({
1052
1077
  x: 0,
@@ -1080,7 +1105,20 @@ var PGSGThreeViewer = class {
1080
1105
  if (e.key === "t") this.transformControls.setMode("translate");
1081
1106
  if (e.key === "s") this.transformControls.setMode("scale");
1082
1107
  });
1083
- 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);
1084
1122
  }
1085
1123
  start() {
1086
1124
  if (this.running) return;
@@ -1096,7 +1134,7 @@ var PGSGThreeViewer = class {
1096
1134
  this.acc -= this.fixedDt;
1097
1135
  }
1098
1136
  this.controls?.update?.(delta);
1099
- this.renderer.render(this.scene, this.camera);
1137
+ this.composer.render();
1100
1138
  });
1101
1139
  }
1102
1140
  pause() {
@@ -1107,6 +1145,9 @@ var PGSGThreeViewer = class {
1107
1145
  const w = Math.max(1, this.container.clientWidth);
1108
1146
  const h = Math.max(1, this.container.clientHeight);
1109
1147
  this.renderer.setSize(w, h);
1148
+ this.composer?.setSize(w, h);
1149
+ this.hoverOutline?.setSize(w, h);
1150
+ this.selectOutline?.setSize(w, h);
1110
1151
  this.camera.aspect = w / h;
1111
1152
  this.camera.updateProjectionMatrix();
1112
1153
  }
@@ -1168,6 +1209,7 @@ var PGSGThreeViewer = class {
1168
1209
  this.pendingPlacementUrl = url;
1169
1210
  this.placementRotation = 0;
1170
1211
  console.log("[PGSG Viewer] Placement mode enabled");
1212
+ this.controls.enabled = false;
1171
1213
  const gltf = await this.gltfLoader.loadAsync(url);
1172
1214
  this.ghostModel = gltf.scene;
1173
1215
  const box = new THREE5.Box3().setFromObject(this.ghostModel);
@@ -1188,8 +1230,17 @@ var PGSGThreeViewer = class {
1188
1230
  this.onPlacementMove
1189
1231
  );
1190
1232
  this.renderer.domElement.addEventListener("wheel", this.onPlacementWheel);
1191
- this.renderer.domElement.addEventListener("click", this.onSceneClick);
1192
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
+ }
1193
1244
  }
1194
1245
  async addModelFromUrl(url, position) {
1195
1246
  return new Promise((resolve, reject) => {
@@ -1238,14 +1289,21 @@ var PGSGThreeViewer = class {
1238
1289
  this.renderer.setPixelRatio(
1239
1290
  this.options.renderer?.pixelRatio ?? window.devicePixelRatio
1240
1291
  );
1292
+ this.renderer.toneMapping = THREE5.ACESFilmicToneMapping;
1293
+ this.renderer.toneMappingExposure = 1.2;
1241
1294
  this.renderer.localClippingEnabled = true;
1242
1295
  this.container.appendChild(this.renderer.domElement);
1243
1296
  }
1244
1297
  initScene() {
1245
1298
  this.scene = new THREE5.Scene();
1246
1299
  this.scene.background = new THREE5.Color(1118481);
1247
- const light = new THREE5.HemisphereLight(16777215, 2236962, 1);
1248
- 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);
1249
1307
  }
1250
1308
  initCamera() {
1251
1309
  this.camera = new THREE5.PerspectiveCamera(
@@ -1257,6 +1315,31 @@ var PGSGThreeViewer = class {
1257
1315
  this.camera.position.set(2, 2, 5);
1258
1316
  this.camera.lookAt(0, 0, 0);
1259
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
+ }
1260
1343
  bindResize() {
1261
1344
  this.resizeObserver?.disconnect();
1262
1345
  this.resizeObserver = new ResizeObserver(() => this.resize());
@@ -1489,16 +1572,21 @@ var PGSGThreeViewer = class {
1489
1572
  return hits;
1490
1573
  }
1491
1574
  onSceneClick = async (event) => {
1575
+ if (this.placementMouseMoved) return;
1492
1576
  if (!this.pendingPlacementUrl || !this.ghostModel) return;
1493
1577
  const position = this.ghostModel.position.clone();
1494
1578
  const placed = await this.addModelFromUrl(
1495
1579
  this.pendingPlacementUrl,
1496
1580
  position
1497
1581
  );
1582
+ if (placed) {
1583
+ this.placedObjects.push(placed);
1584
+ }
1498
1585
  if (placed && this.transformControls) {
1499
1586
  placed.rotation.y = this.placementRotation;
1500
1587
  this.transformControls.attach(placed);
1501
1588
  }
1589
+ this.controls.enabled = true;
1502
1590
  this.scene.remove(this.ghostModel);
1503
1591
  this.ghostModel = void 0;
1504
1592
  this.pendingPlacementUrl = void 0;
@@ -1510,7 +1598,6 @@ var PGSGThreeViewer = class {
1510
1598
  "wheel",
1511
1599
  this.onPlacementWheel
1512
1600
  );
1513
- this.renderer.domElement.removeEventListener("click", this.onSceneClick);
1514
1601
  window.removeEventListener("keydown", this.onPlacementCancel);
1515
1602
  };
1516
1603
  onPlacementMove = (event) => {
@@ -1527,7 +1614,10 @@ var PGSGThreeViewer = class {
1527
1614
  );
1528
1615
  if (!intersects.length) return;
1529
1616
  const hit = intersects[0];
1617
+ const box = new THREE5.Box3().setFromObject(this.ghostModel);
1618
+ this.ghostYOffset = -box.min.y;
1530
1619
  this.ghostModel.position.copy(hit.point);
1620
+ this.ghostModel.position.y += this.ghostYOffset;
1531
1621
  if (hit.face) {
1532
1622
  const normal = this.getWorldNormalSafe(hit);
1533
1623
  if (normal) {
@@ -1538,6 +1628,8 @@ var PGSGThreeViewer = class {
1538
1628
  }
1539
1629
  };
1540
1630
  onPlacementWheel = (event) => {
1631
+ event.preventDefault();
1632
+ event.stopPropagation();
1541
1633
  if (!this.ghostModel) return;
1542
1634
  event.preventDefault();
1543
1635
  const step = Math.PI / 12;
@@ -1567,8 +1659,71 @@ var PGSGThreeViewer = class {
1567
1659
  );
1568
1660
  window.removeEventListener("keydown", this.onPlacementCancel);
1569
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
+ };
1570
1725
  };
1571
1726
  export {
1572
1727
  PGSGThreeViewer
1573
1728
  };
1574
- //# sourceMappingURL=pgsg-three-viewer-5BUDBGSE.js.map
1729
+ //# sourceMappingURL=pgsg-three-viewer-B5AH6DJO.js.map