@pascal-app/viewer 0.3.2 → 0.3.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ceiling-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/ceiling/ceiling-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAA;AAmChE,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,4CAoC9D,CAAA"}
1
+ {"version":3,"file":"ceiling-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/ceiling/ceiling-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAgC,MAAM,kBAAkB,CAAA;AAkCjF,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,4CA6B9D,CAAA"}
@@ -1,10 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useRegistry } from '@pascal-app/core';
2
+ import { resolveMaterial, useRegistry } from '@pascal-app/core';
3
3
  import { useMemo, useRef } from 'react';
4
4
  import { float, mix, positionWorld, smoothstep } from 'three/tsl';
5
5
  import { BackSide, FrontSide, MeshBasicNodeMaterial } from 'three/webgpu';
6
6
  import { useNodeEvents } from '../../../hooks/use-node-events';
7
- import { DEFAULT_CEILING_MATERIAL } from '../../../lib/materials';
8
7
  import { NodeRenderer } from '../node-renderer';
9
8
  const gridScale = 5;
10
9
  const gridX = positionWorld.x.mul(gridScale).fract();
@@ -34,16 +33,9 @@ export const CeilingRenderer = ({ node }) => {
34
33
  useRegistry(node.id, 'ceiling', ref);
35
34
  const handlers = useNodeEvents(node, 'ceiling');
36
35
  const materials = useMemo(() => {
37
- const mat = node.material;
38
- if (mat) {
39
- const props = mat.properties;
40
- const color = props?.color || '#999999';
41
- return createCeilingMaterials(color);
42
- }
43
- return {
44
- topMaterial: createCeilingMaterials().topMaterial,
45
- bottomMaterial: DEFAULT_CEILING_MATERIAL,
46
- };
36
+ const props = resolveMaterial(node.material);
37
+ const color = props.color || '#999999';
38
+ return createCeilingMaterials(color);
47
39
  }, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
48
40
  return (_jsxs("mesh", { material: materials.bottomMaterial, ref: ref, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("mesh", { material: materials.topMaterial, name: "ceiling-grid", ...handlers, scale: 0, visible: false, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
49
41
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AA0CrC,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,aAAc,SAAQ,kBAAkB,CAAC,OAAO,KAAK,CAAC;KAAG;CACpE;AA+BD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA6DjC,CAAA;AASD,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AA2CrC,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,aAAc,SAAQ,kBAAkB,CAAC,OAAO,KAAK,CAAC;KAAG;CACpE;AA+BD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA8DjC,CAAA;AASD,eAAe,MAAM,CAAA"}
@@ -6,6 +6,7 @@ import { Canvas, extend, useFrame, useThree } from '@react-three/fiber';
6
6
  import { useEffect, useMemo, useRef } from 'react';
7
7
  import * as THREE from 'three/webgpu';
8
8
  import useViewer from '../../store/use-viewer';
9
+ import { ExportSystem } from '../../systems/export/export-system';
9
10
  import { GuideSystem } from '../../systems/guide/guide-system';
10
11
  import { ItemLightSystem } from '../../systems/item-light/item-light-system';
11
12
  import { LevelSystem } from '../../systems/level/level-system';
@@ -74,7 +75,7 @@ const Viewer = ({ children, selectionManager = 'default', perf = false, }) => {
74
75
  }, shadows: {
75
76
  type: THREE.PCFShadowMap,
76
77
  enabled: true,
77
- }, children: [_jsx(ViewerCamera, {}), _jsx(Lights, {}), _jsx(Bvh, { children: _jsx(SceneRenderer, {}) }), _jsx(LevelSystem, {}), _jsx(GuideSystem, {}), _jsx(ScanSystem, {}), _jsx(WallCutout, {}), _jsx(CeilingSystem, {}), _jsx(DoorSystem, {}), _jsx(ItemSystem, {}), _jsx(RoofSystem, {}), _jsx(SlabSystem, {}), _jsx(WallSystem, {}), _jsx(WindowSystem, {}), _jsx(ZoneSystem, {}), _jsx(PostProcessing, {}), _jsx(GPUDeviceWatcher, {}), _jsx(ItemLightSystem, {}), selectionManager === 'default' && _jsx(SelectionManager, {}), perf && _jsx(PerfMonitor, {}), children] }));
78
+ }, children: [_jsx(ViewerCamera, {}), _jsx(Lights, {}), _jsx(Bvh, { children: _jsx(SceneRenderer, {}) }), _jsx(LevelSystem, {}), _jsx(GuideSystem, {}), _jsx(ScanSystem, {}), _jsx(WallCutout, {}), _jsx(CeilingSystem, {}), _jsx(DoorSystem, {}), _jsx(ItemSystem, {}), _jsx(RoofSystem, {}), _jsx(SlabSystem, {}), _jsx(WallSystem, {}), _jsx(WindowSystem, {}), _jsx(ZoneSystem, {}), _jsx(ExportSystem, {}), _jsx(PostProcessing, {}), _jsx(GPUDeviceWatcher, {}), _jsx(ItemLightSystem, {}), selectionManager === 'default' && _jsx(SelectionManager, {}), perf && _jsx(PerfMonitor, {}), children] }));
78
79
  };
79
80
  const DebugRenderer = () => {
80
81
  useFrame(({ gl, scene, camera }) => {
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url'
3
3
  export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
4
4
  export { clearMaterialCache, createDefaultMaterial, createMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, } from './lib/materials';
5
5
  export { default as useViewer } from './store/use-viewer';
6
+ export { ExportSystem } from './systems/export/export-system';
6
7
  export { InteractiveSystem } from './systems/interactive/interactive-system';
7
8
  export { snapLevelsToTruePositions } from './systems/level/level-utils';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,GAChB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,GAChB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA"}
package/dist/index.js CHANGED
@@ -3,5 +3,6 @@ export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url'
3
3
  export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
4
4
  export { clearMaterialCache, createDefaultMaterial, createMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, } from './lib/materials';
5
5
  export { default as useViewer } from './store/use-viewer';
6
+ export { ExportSystem } from './systems/export/export-system';
6
7
  export { InteractiveSystem } from './systems/interactive/interactive-system';
7
8
  export { snapLevelsToTruePositions } from './systems/level/level-utils';
@@ -0,0 +1,2 @@
1
+ export declare const ExportSystem: () => null;
2
+ //# sourceMappingURL=export-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export-system.d.ts","sourceRoot":"","sources":["../../../src/systems/export/export-system.tsx"],"names":[],"mappings":"AAwBA,eAAO,MAAM,YAAY,YAgDxB,CAAA"}
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+ import { useThree } from '@react-three/fiber';
3
+ import { useEffect } from 'react';
4
+ import * as THREE from 'three';
5
+ import { STLExporter } from 'three/examples/jsm/exporters/STLExporter.js';
6
+ import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
7
+ import { OBJExporter } from 'three/examples/jsm/exporters/OBJExporter.js';
8
+ import useViewer from '../../store/use-viewer';
9
+ const EDITOR_LAYER = 1; // same constant used across the editor
10
+ function downloadBlob(blob, filename) {
11
+ const url = URL.createObjectURL(blob);
12
+ const a = document.createElement('a');
13
+ a.href = url;
14
+ a.download = filename;
15
+ document.body.appendChild(a);
16
+ a.click();
17
+ document.body.removeChild(a);
18
+ URL.revokeObjectURL(url);
19
+ }
20
+ export const ExportSystem = () => {
21
+ const { scene } = useThree();
22
+ const setExportScene = useViewer((state) => state.setExportScene);
23
+ useEffect(() => {
24
+ const exportFn = async (format = 'glb') => {
25
+ const date = new Date().toISOString().split('T')[0];
26
+ const filename = `pascal-export-${date}`;
27
+ // Clone scene and strip editor-only objects (layer 1 = EDITOR_LAYER)
28
+ const exportRoot = scene.clone(true);
29
+ const toRemove = [];
30
+ exportRoot.traverse((obj) => {
31
+ if (obj.layers.isEnabled(EDITOR_LAYER)) {
32
+ toRemove.push(obj);
33
+ }
34
+ });
35
+ for (const obj of toRemove) {
36
+ obj.parent?.remove(obj);
37
+ }
38
+ if (format === 'glb') {
39
+ const exporter = new GLTFExporter();
40
+ const result = await new Promise((resolve, reject) => {
41
+ exporter.parse(exportRoot, (output) => resolve(output), (err) => reject(err), { binary: true });
42
+ });
43
+ downloadBlob(new Blob([result], { type: 'model/gltf-binary' }), `${filename}.glb`);
44
+ }
45
+ else if (format === 'stl') {
46
+ const exporter = new STLExporter();
47
+ const result = exporter.parse(exportRoot, { binary: true });
48
+ downloadBlob(new Blob([result.buffer], { type: 'model/stl' }), `${filename}.stl`);
49
+ }
50
+ else if (format === 'obj') {
51
+ const exporter = new OBJExporter();
52
+ const result = exporter.parse(exportRoot);
53
+ downloadBlob(new Blob([result], { type: 'model/obj' }), `${filename}.obj`);
54
+ }
55
+ };
56
+ setExportScene(exportFn);
57
+ return () => setExportScene(null);
58
+ }, [scene, setExportScene]);
59
+ return null;
60
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pascal-app/viewer",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "3D viewer component for Pascal building editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",