| @@ -51,8 +51,7 @@ module.exports = { | |||
| 'ecmaVersion': 2021, // Allows for the parsing of modern ECMAScript features | |||
| 'sourceType': 'module', // Allows for the use of imports | |||
| 'project': ['./tsconfig.json', './examples/tsconfig.json', | |||
| './plugins/tweakpane-editor/tsconfig.json', | |||
| './plugins/tweakpane/tsconfig.json', | |||
| './plugins/**/tsconfig.json', | |||
| ], | |||
| 'tsconfigRootDir': './', | |||
| }, | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Rhino 3DM To GLB</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Camera UiConfig</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Custom Pipeline</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Depth Buffer Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Directional Light</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>DRACO(DRC) Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Dropzone Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>EXR Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -0,0 +1,48 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Extra importer plugins</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "three": "./../../dist/index.mjs", | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-extra-importers": "./../../plugins/extra-importers/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| p{ | |||
| position: absolute; | |||
| top: 5%; | |||
| left: 50%; | |||
| transform: translate(-50%, -50%); | |||
| font-size: 1.25em; | |||
| color: #8cd55b; | |||
| font-family: sans-serif; | |||
| pointer-events: none; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <p>Drop .3ds .3mf .collada .amf .bvh .vox .gcode .mdd .pcd .tilt .wrl .mpd .vtk .xyz files here</p> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,97 @@ | |||
| import {_testFinish, GLTFAnimationPlugin, HemisphereLight, ImportAddOptions, IObject3D, ThreeViewer} from 'threepipe' | |||
| import { | |||
| AMFLoadPlugin, | |||
| BVHLoadPlugin, | |||
| ColladaLoadPlugin, | |||
| GCodeLoadPlugin, | |||
| LDrawLoadPlugin, | |||
| MDDLoadPlugin, | |||
| PCDLoadPlugin, | |||
| TDSLoadPlugin, | |||
| ThreeMFLoadPlugin, | |||
| TiltLoadPlugin, | |||
| VOXLoadPlugin, | |||
| VRMLLoadPlugin, | |||
| VTKLoadPlugin, | |||
| XYZLoadPlugin, | |||
| } from '@threepipe/plugin-extra-importers' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| dropzone: { | |||
| addOptions: { | |||
| disposeSceneObjects: true, | |||
| }, | |||
| }, | |||
| }) | |||
| viewer.addPluginsSync([ | |||
| GLTFAnimationPlugin, | |||
| TDSLoadPlugin, | |||
| ThreeMFLoadPlugin, | |||
| ColladaLoadPlugin, | |||
| AMFLoadPlugin, | |||
| GCodeLoadPlugin, | |||
| BVHLoadPlugin, | |||
| VOXLoadPlugin, | |||
| MDDLoadPlugin, | |||
| PCDLoadPlugin, | |||
| TiltLoadPlugin, | |||
| VRMLLoadPlugin, | |||
| LDrawLoadPlugin, | |||
| VTKLoadPlugin, | |||
| XYZLoadPlugin, | |||
| ]) | |||
| viewer.getPlugin(GLTFAnimationPlugin)!.autoplayOnLoad = true | |||
| viewer.scene.mainCamera.autoNearFar = false | |||
| viewer.scene.setBackgroundColor('#555555') | |||
| viewer.scene.addObject(new HemisphereLight(0xffffff, 0x444444, 2)) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| const urls = [ | |||
| 'https://threejs.org/examples/models/3ds/portalgun/portalgun.3ds', | |||
| 'https://threejs.org/examples/models/3mf/cube_gears.3mf', | |||
| 'https://threejs.org/examples/models/collada/elf/elf.dae', | |||
| 'https://threejs.org/examples/models/amf/rook.amf', | |||
| 'https://threejs.org/examples/models/gcode/benchy.gcode', | |||
| 'https://threejs.org/examples/models/bvh/pirouette.bvh', | |||
| 'https://threejs.org/examples/models/vox/monu10.vox', | |||
| 'https://threejs.org/examples/models/mdd/cube.mdd', | |||
| 'https://threejs.org/examples/models/pcd/binary/Zaghetto.pcd', | |||
| 'https://threejs.org/examples/models/tilt/BRUSH_DOME.tilt', | |||
| 'https://threejs.org/examples/models/ldraw/officialLibrary/models/car.ldr_Packed.mpd', | |||
| 'https://threejs.org/examples/models/vtk/bunny.vtk', | |||
| 'https://threejs.org/examples/models/vtk/cube_binary.vtp', | |||
| 'https://threejs.org/examples/models/xyz/helix_201.xyz', | |||
| ] | |||
| const options: ImportAddOptions = { | |||
| autoScale: true, | |||
| autoCenter: true, | |||
| autoScaleRadius: 0.5, | |||
| clearSceneObjects: false, | |||
| } | |||
| let i = 0 | |||
| const models = await Promise.allSettled(urls.map(async url => | |||
| viewer.load<IObject3D>(url, options).then(res => { | |||
| if (!res) return | |||
| res.position.set(i % 4 - 1.5, 0, Math.floor(i / 4) - 1.5).multiplyScalar(1) | |||
| res.setDirty() | |||
| i++ | |||
| return res | |||
| }))) | |||
| console.log(models) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>FBX Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Frame Fade Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Fullscreen Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -0,0 +1,35 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Render Target Preview</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,35 @@ | |||
| import {_testFinish, GeometryUVPreviewPlugin, IObject3D, ThreeViewer} from 'threepipe' | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| rgbm: true, | |||
| }) | |||
| async function init() { | |||
| const uvPreview = viewer.addPluginSync(GeometryUVPreviewPlugin) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/IridescentDishWithOlives.glb', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }) | |||
| const added = false | |||
| result?.traverse((obj) => { | |||
| if (obj.geometry && !added) { | |||
| // added = true | |||
| uvPreview.addGeometry(obj.geometry, obj.name) | |||
| } | |||
| }) | |||
| // uvPreview.add(()=>depth.target, 'depth', false, true) | |||
| // uvPreview.add(()=>normal.target, 'normal', false, false) | |||
| // uvPreview.add(()=>viewer.renderManager.composerTarget, 'composer-1', false, false) | |||
| // uvPreview.add(()=>viewer.renderManager.composerTarget2, 'composer-2', false, false) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLB Export</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,7 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLTF Animation Page Scroll</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLTF Animation Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLTF Camera Animation</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLTF Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLTF Transmission Test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Half float HDR Test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>HDR Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>HDR To EXR</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Image Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Image Snapshot Export</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Basic Lib Import Test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -244,10 +244,12 @@ | |||
| <li><a href="./hdr-load/">HDR Load </a></li> | |||
| <li><a href="./exr-load/">EXR Load </a></li> | |||
| <li><a href="./image-load/">Image(png, jpeg, svg, ico, webp, avif) Load </a></li> | |||
| <li><a href="./usdz-load/">USDZ, USDA Load </a></li> | |||
| <li><a href="./ply-load/">PLY Load </a></li> | |||
| <li><a href="./stl-load/">STL Load </a></li> | |||
| <li><a href="./ktx2-load/">KTX2 Load </a></li> | |||
| <li><a href="./ktx-load/">KTX Load </a></li> | |||
| <li><a href="./extra-importer-plugins/">Extra(3ds, 3mf, collada, amf, bvh, vox, gcode, mdd, pcd, tilt, wrl, ldraw, vtk, xyz) Load </a></li> | |||
| </ul> | |||
| <h2 class="category">Export</h2> | |||
| <ul> | |||
| @@ -267,13 +269,14 @@ | |||
| <h2 class="category">Animation</h2> | |||
| <ul> | |||
| <li><a href="./gltf-animation-plugin/">glTF Animation Plugin </a></li> | |||
| <li><a href="./gltf-animation-plugin/">Popmotion Plugin </a></li> | |||
| <li><a href="./popmotion-plugin/">Popmotion Plugin </a></li> | |||
| <li><a href="./gltf-camera-animation/">glTF Camera Animation </a></li> | |||
| <li><a href="./gltf-animation-page-scroll/">glTF Animation Page Scroll </a></li> | |||
| </ul> | |||
| <h2 class="category">Utils</h2> | |||
| <ul> | |||
| <li><a href="./render-target-preview/">Render Target Preview Plugin </a></li> | |||
| <li><a href="./geometry-uv-preview/">Geometry UV Preview Plugin </a></li> | |||
| <li><a href="./parallel-asset-import/">Parallel Asset Import </a></li> | |||
| <li><a href="./obj-to-glb/">Convert OBJ to GLB </a></li> | |||
| <li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>KTX Texture Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>KTX2 Texture Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Material UiConfig</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Depth Buffer Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>OBJ MTL Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>OBJ To GLB</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Object UiConfig</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Parallel Asset Import/Download</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>PLY Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>PMAT(Physical) Material export</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Popmotion Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Progressive Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Image Snapshot Export</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Render Target Preview</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Rhino 3DM Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Scene UiConfig</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Half float render pipeline test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>MSAA Test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>RGBM Render pipeline test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>STL Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Tonemap Plugin</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Tweakpane Editor</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -14,9 +15,11 @@ | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "three": "./../../dist/index.mjs", | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane-editor": "./../../plugins/tweakpane-editor/dist/index.mjs" | |||
| "@threepipe/plugin-tweakpane-editor": "./../../plugins/tweakpane-editor/dist/index.mjs", | |||
| "@threepipe/plugin-extra-importers": "./../../plugins/extra-importers/dist/index.mjs" | |||
| } | |||
| } | |||
| @@ -17,10 +17,12 @@ import { | |||
| STLLoadPlugin, | |||
| ThreeViewer, | |||
| TonemapPlugin, | |||
| USDZLoadPlugin, | |||
| ViewerUiConfigPlugin, | |||
| } from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| import {TweakpaneEditorPlugin} from '@threepipe/plugin-tweakpane-editor' | |||
| import {extraImportPlugins} from '@threepipe/plugin-extra-importers' | |||
| async function init() { | |||
| @@ -49,11 +51,13 @@ async function init() { | |||
| new NormalBufferPlugin(HalfFloatType, false), | |||
| new RenderTargetPreviewPlugin(false), | |||
| new FrameFadePlugin(), | |||
| new KTX2LoadPlugin(), | |||
| new KTXLoadPlugin(), | |||
| new PLYLoadPlugin(), | |||
| new Rhino3dmLoadPlugin(), | |||
| new STLLoadPlugin(), | |||
| KTX2LoadPlugin, | |||
| KTXLoadPlugin, | |||
| PLYLoadPlugin, | |||
| Rhino3dmLoadPlugin, | |||
| STLLoadPlugin, | |||
| USDZLoadPlugin, | |||
| ...extraImportPlugins, | |||
| ]) | |||
| const rt = viewer.getOrAddPluginSync(RenderTargetPreviewPlugin) | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Uint8 RGBM HDR Test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -0,0 +1,35 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>USDZ / USDA Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,31 @@ | |||
| import {_testFinish, IObject3D, ThreeViewer, USDZLoadPlugin} from 'threepipe' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| dropzone: { | |||
| allowedExtensions: ['usdz', 'usda', 'hdr', 'exr'], | |||
| addOptions: { | |||
| disposeSceneObjects: true, | |||
| autoSetEnvironment: true, // when hdr is dropped | |||
| autoSetBackground: true, | |||
| }, | |||
| }, | |||
| }) | |||
| viewer.addPluginSync(USDZLoadPlugin) | |||
| const options = { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| } | |||
| await Promise.all([ | |||
| viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr'), | |||
| viewer.load<IObject3D>('https://threejs.org/examples/models/usdz/saeukkang.usdz', options), | |||
| ]) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Viewer UiConfig</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -3,6 +3,7 @@ | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Z-Prepass test</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| @@ -1,15 +1,15 @@ | |||
| { | |||
| "name": "threepipe", | |||
| "version": "0.0.12", | |||
| "version": "0.0.14", | |||
| "lockfileVersion": 2, | |||
| "requires": true, | |||
| "packages": { | |||
| "": { | |||
| "name": "threepipe", | |||
| "version": "0.0.12", | |||
| "version": "0.0.14", | |||
| "license": "Apache-2.0", | |||
| "dependencies": { | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1012/package.tgz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "@types/webxr": "^0.5.1", | |||
| "@types/wicg-file-system-access": "^2020.9.5", | |||
| "ts-browser-helpers": "^0.8.0" | |||
| @@ -663,9 +663,9 @@ | |||
| "dev": true | |||
| }, | |||
| "node_modules/@types/three": { | |||
| "version": "0.152.1012", | |||
| "resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1012/package.tgz", | |||
| "integrity": "sha512-G3Hyma5qGM+Wc1F2dapnIrdNY2uPr33eNYMzaDPbrvHJMHmoZyshrrl9tAodVpND+BVxXSvGiXpfunRGYPOtJg==", | |||
| "version": "0.152.1014", | |||
| "resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "integrity": "sha512-1sR9iALwIFtfSXxJshAglvMjLy5litWF2hTbh0JQ+44d+21D2t0nppRZBnWtNQP5XsBYdhCNygnDQNeF6kd+NQ==", | |||
| "dependencies": { | |||
| "@tweenjs/tween.js": "~18.6.4", | |||
| "fflate": "~0.6.9", | |||
| @@ -10863,8 +10863,8 @@ | |||
| "dev": true | |||
| }, | |||
| "@types/three": { | |||
| "version": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1012/package.tgz", | |||
| "integrity": "sha512-G3Hyma5qGM+Wc1F2dapnIrdNY2uPr33eNYMzaDPbrvHJMHmoZyshrrl9tAodVpND+BVxXSvGiXpfunRGYPOtJg==", | |||
| "version": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "integrity": "sha512-1sR9iALwIFtfSXxJshAglvMjLy5litWF2hTbh0JQ+44d+21D2t0nppRZBnWtNQP5XsBYdhCNygnDQNeF6kd+NQ==", | |||
| "requires": { | |||
| "@tweenjs/tween.js": "~18.6.4", | |||
| "fflate": "~0.6.9", | |||
| @@ -1,6 +1,6 @@ | |||
| { | |||
| "name": "threepipe", | |||
| "version": "0.0.12", | |||
| "version": "0.0.13", | |||
| "description": "A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.", | |||
| "main": "src/index.ts", | |||
| "module": "dist/index.mjs", | |||
| @@ -102,7 +102,7 @@ | |||
| "popmotion": "^11.0.5" | |||
| }, | |||
| "dependencies": { | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1012/package.tgz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "@types/webxr": "^0.5.1", | |||
| "@types/wicg-file-system-access": "^2020.9.5", | |||
| "ts-browser-helpers": "^0.8.0" | |||
| @@ -113,8 +113,8 @@ | |||
| "ts-browser-helpers": "^0.8.0", | |||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | |||
| "three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2012.tar.gz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1012/package.tgz", | |||
| "@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.152.1012.tar.gz", | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1014/package.tgz", | |||
| "@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.152.1014.tar.gz", | |||
| "@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three" | |||
| }, | |||
| "local_dependencies": { | |||
| @@ -0,0 +1,30 @@ | |||
| { | |||
| "name": "@threepipe/plugins-extra-importers", | |||
| "version": "0.1.0", | |||
| "lockfileVersion": 2, | |||
| "requires": true, | |||
| "packages": { | |||
| "": { | |||
| "name": "@threepipe/plugins-extra-importers", | |||
| "version": "0.1.0", | |||
| "license": "Apache-2.0", | |||
| "dependencies": { | |||
| "threepipe": "file:./../../src/" | |||
| }, | |||
| "devDependencies": {} | |||
| }, | |||
| "../../src": {}, | |||
| "../tweakpane/src": { | |||
| "extraneous": true | |||
| }, | |||
| "node_modules/threepipe": { | |||
| "resolved": "../../src", | |||
| "link": true | |||
| } | |||
| }, | |||
| "dependencies": { | |||
| "threepipe": { | |||
| "version": "file:../../src" | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| { | |||
| "name": "@threepipe/plugins-extra-importers", | |||
| "description": "Extra Threepipe plugins for importing several file types.", | |||
| "version": "0.1.0", | |||
| "devDependencies": { | |||
| }, | |||
| "dependencies": { | |||
| "threepipe": "file:./../../src/" | |||
| }, | |||
| "clean-package": { | |||
| "remove": [ | |||
| "clean-package", | |||
| "scripts", | |||
| "devDependencies", | |||
| "//", | |||
| "markdown-to-html" | |||
| ], | |||
| "replace": { | |||
| "dependencies": { | |||
| "threepipe": "^0.0.14" | |||
| } | |||
| } | |||
| }, | |||
| "type": "module", | |||
| "main": "dist/index.js", | |||
| "module": "dist/index.mjs", | |||
| "types": "dist/index.d.ts", | |||
| "files": [ | |||
| "dist", | |||
| "src" | |||
| ], | |||
| "scripts": { | |||
| "new:pack": "npm run prepare && clean-package && npm pack && clean-package restore", | |||
| "new:publish": "npm run prepare && clean-package && npm publish --access public && clean-package restore", | |||
| "prepare": "npm run build", | |||
| "build": "rimraf dist && NODE_ENV=production rollup -c", | |||
| "dev": "rollup -c -w", | |||
| "docs": "rimraf docs && npx typedoc" | |||
| }, | |||
| "author": "repalash <palash@shaders.app>", | |||
| "license": "Apache-2.0", | |||
| "keywords": [ | |||
| "three", | |||
| "three.js", | |||
| "threepipe", | |||
| "tweakpane", | |||
| "editor", | |||
| "plugin" | |||
| ], | |||
| "bugs": { | |||
| "url": "https://github.com/repalash/threepipe/issues" | |||
| }, | |||
| "homepage": "https://github.com/repalash/threepipe#readme", | |||
| "repository": { | |||
| "type": "git", | |||
| "url": "git://github.com/repalash/threepipe.git" | |||
| } | |||
| } | |||
| @@ -0,0 +1,97 @@ | |||
| // rollup.config.js | |||
| import commonjs from '@rollup/plugin-commonjs'; | |||
| import json from '@rollup/plugin-json'; | |||
| import resolve from '@rollup/plugin-node-resolve'; | |||
| import typescript from '@rollup/plugin-typescript'; | |||
| import license from 'rollup-plugin-license' | |||
| import packageJson from './package.json' assert {type: 'json'}; | |||
| import path from 'path' | |||
| import {fileURLToPath} from 'url'; | |||
| import postcss from 'rollup-plugin-postcss' | |||
| import replace from 'rollup-plugin-replace' | |||
| import terser from "@rollup/plugin-terser"; | |||
| const __filename = fileURLToPath(import.meta.url); | |||
| const __dirname = path.dirname(__filename); | |||
| const {name, version, author} = packageJson | |||
| // const {main, module, browser} = packageJson["clean-package"].replace | |||
| const isProduction = process.env.NODE_ENV === 'production' | |||
| const settings = { | |||
| globals: { | |||
| "threepipe": "threepipe", | |||
| "three": "threepipe" | |||
| }, | |||
| sourcemap: true | |||
| } | |||
| export default { | |||
| input: './src/index.ts', | |||
| output: [ | |||
| // { | |||
| // file: main, | |||
| // name: main, | |||
| // ...settings, | |||
| // format: 'cjs', | |||
| // plugins: [ | |||
| // isProduction && terser() | |||
| // ] | |||
| // }, | |||
| { | |||
| file: './dist/index.mjs', | |||
| ...settings, | |||
| name: name, | |||
| format: 'es', | |||
| plugins: [ | |||
| isProduction && terser() | |||
| ] | |||
| }, | |||
| { | |||
| file: './dist/index.js', | |||
| ...settings, | |||
| name: name, | |||
| format: 'umd', | |||
| plugins: [ | |||
| isProduction && terser() | |||
| ] | |||
| } | |||
| ], | |||
| external: Object.keys(settings.globals), | |||
| plugins: [ | |||
| replace({ | |||
| // If you would like DEV messages, specify 'development' | |||
| // Otherwise use 'production' | |||
| 'process.env.NODE_ENV': JSON.stringify('production') // for tippy.js | |||
| }), | |||
| postcss({ | |||
| modules: false, | |||
| autoModules: true, // todo; issues with typescript import css, because inject is false | |||
| inject: false, | |||
| minimize: isProduction, | |||
| // Or with custom options for `postcss-modules` | |||
| }), | |||
| json(), | |||
| resolve({}), | |||
| typescript({ | |||
| }), | |||
| commonjs({ | |||
| include: 'node_modules/**', | |||
| extensions: ['.js'], | |||
| ignoreGlobal: false, | |||
| sourceMap: false | |||
| }), | |||
| license({ | |||
| banner: ` | |||
| @license | |||
| ${name} v${version} | |||
| Copyright 2022<%= moment().format('YYYY') > 2022 ? '-' + moment().format('YYYY') : null %> ${author} | |||
| ${packageJson.license} License | |||
| `, | |||
| thirdParty: { | |||
| output: path.join(__dirname, 'dist', 'dependencies.txt'), | |||
| includePrivate: true, // Default is false. | |||
| }, | |||
| }) | |||
| ] | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| declare module '*.txt' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.glsl' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.vert' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.frag' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.module.scss' { | |||
| const content: any | |||
| export default content | |||
| export const stylesheet: string | |||
| } | |||
| declare module '*.module.css' { | |||
| const content: any | |||
| export default content | |||
| export const stylesheet: string | |||
| } | |||
| declare module '*.css' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| // export {} | |||
| // hack for typedoc | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| // declare type OffscreenCanvas = HTMLCanvasElement | |||
| @@ -0,0 +1,244 @@ | |||
| import { | |||
| AnyOptions, | |||
| BaseImporterPlugin, | |||
| BoxGeometry, | |||
| BufferGeometry, | |||
| Color, | |||
| Group, | |||
| ILoader, | |||
| Importer, | |||
| Mesh, | |||
| Object3D, | |||
| PhysicalMaterial, | |||
| Points, | |||
| PointsMaterial, | |||
| Scene, | |||
| SkeletonHelper, | |||
| } from 'threepipe' | |||
| import {TDSLoader} from 'three/examples/jsm/loaders/TDSLoader.js' | |||
| import {ThreeMFLoader} from 'three/examples/jsm/loaders/3MFLoader.js' | |||
| import {Collada, ColladaLoader} from 'three/examples/jsm/loaders/ColladaLoader.js' | |||
| import {AMFLoader} from 'three/examples/jsm/loaders/AMFLoader.js' | |||
| import {GCodeLoader} from 'three/examples/jsm/loaders/GCodeLoader.js' | |||
| import {BVH, BVHLoader} from 'three/examples/jsm/loaders/BVHLoader.js' | |||
| import {Chunk, VOXLoader, VOXMesh} from 'three/examples/jsm/loaders/VOXLoader.js' | |||
| import {MDD, MDDLoader} from 'three/examples/jsm/loaders/MDDLoader.js' | |||
| import {PCDLoader} from 'three/examples/jsm/loaders/PCDLoader.js' | |||
| import {TiltLoader} from 'three/examples/jsm/loaders/TiltLoader.js' | |||
| import {VRMLLoader} from 'three/examples/jsm/loaders/VRMLLoader.js' | |||
| import {LDrawLoader} from 'three/examples/jsm/loaders/LDrawLoader.js' | |||
| import {VTKLoader} from 'three/examples/jsm/loaders/VTKLoader.js' | |||
| import {XYZLoader} from 'three/examples/jsm/loaders/XYZLoader.js' | |||
| // 3ds | |||
| /** | |||
| * Adds support for loading Autodesk 3ds `.3ds`, `application/x-3ds` files and data uris | |||
| */ | |||
| export class TDSLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'TDSLoadPlugin' | |||
| protected _importer = new Importer(TDSLoader, ['3ds'], ['image/x-3ds', 'application/x-3ds'], false) | |||
| } | |||
| // 3mf | |||
| /** | |||
| * Adds support for loading `.3mf`, `model/3mf` files and data uris | |||
| */ | |||
| export class ThreeMFLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'ThreeMFLoadPlugin' | |||
| protected _importer = new Importer(ThreeMFLoader, ['3mf'], ['model/3mf'], false) | |||
| } | |||
| // collada | |||
| /** | |||
| * Adds support for loading Collada `.dae`, `model/vnd.collada+xml` files and data uris | |||
| */ | |||
| export class ColladaLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'ColladaLoadPlugin' | |||
| protected _importer = new Importer(class extends ColladaLoader implements ILoader { | |||
| transform(res: Collada, _: AnyOptions): Scene { | |||
| res.scene.userData.kinematics = res.kinematics | |||
| res.scene.userData.library = res.library | |||
| return res.scene | |||
| } | |||
| }, ['dae'], ['model/vnd.collada+xml'], false) | |||
| } | |||
| // amf | |||
| /** | |||
| * Adds support for loading Additive Manufacturing files `.amf`, `application/amf` files and data uris | |||
| */ | |||
| export class AMFLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'AMFLoadPlugin' | |||
| protected _importer = new Importer(AMFLoader, ['amf'], ['application/amf'], false) | |||
| } | |||
| // gcode | |||
| /** | |||
| * Adds support for loading `.gcode`, `application/gcode` files and data uris | |||
| */ | |||
| export class GCodeLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'GCodeLoadPlugin' | |||
| protected _importer = new Importer(GCodeLoader, ['gcode'], ['application/gcode'], false) | |||
| } | |||
| // bvh | |||
| /** | |||
| * Adds support for loading `.bvh`, `application/bvh` files and data uris | |||
| */ | |||
| export class BVHLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'BVHLoadPlugin' | |||
| protected _importer = new Importer(class extends BVHLoader implements ILoader { | |||
| transform(res: BVH, _: AnyOptions): Object3D { | |||
| const obj = new Object3D() | |||
| const helper = new SkeletonHelper(res.skeleton.bones[0]) | |||
| obj.add(res.skeleton.bones[0]) | |||
| obj.add(helper) | |||
| obj.animations = [res.clip] | |||
| obj.scale.set(0.1, 0.1, 0.1) // todo: autoScale and autoCenter not working | |||
| return obj | |||
| } | |||
| }, ['bvh'], ['application/bvh'], false) | |||
| } | |||
| // vox | |||
| /** | |||
| * Adds support for loading Magica Voxel `.vox` files and data uris | |||
| */ | |||
| export class VOXLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'VOXLoadPlugin' | |||
| protected _importer = new Importer(class extends VOXLoader implements ILoader { | |||
| transform(chunks: Chunk[], _: AnyOptions): Object3D { | |||
| const obj = new Object3D() | |||
| for (const chunk of chunks) { | |||
| // displayPalette( chunk.palette ); | |||
| const mesh = new VOXMesh(chunk) | |||
| mesh.scale.setScalar(0.0015) | |||
| obj.add(mesh) | |||
| } | |||
| return obj | |||
| } | |||
| }, ['vox'], [''], false) | |||
| } | |||
| // mdd | |||
| /** | |||
| * Adds support for loading animation `.mdd`, `application/mdd` files and data uris | |||
| */ | |||
| export class MDDLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'MDDLoadPlugin' | |||
| protected _importer = new Importer(class extends MDDLoader implements ILoader { | |||
| transform(res: MDD, _: AnyOptions): Object3D { | |||
| const morphTargets = res.morphTargets | |||
| const geometry = new BoxGeometry() | |||
| geometry.morphAttributes.position = morphTargets // apply morph targets | |||
| const mesh = new Mesh(geometry, new PhysicalMaterial()) | |||
| const obj = new Object3D() | |||
| obj.add(mesh) | |||
| res.clip.tracks.forEach(track=> track.name = mesh.uuid + track.name) | |||
| obj.animations = [res.clip] | |||
| return obj | |||
| } | |||
| }, ['mdd'], ['application/mdd'], false) | |||
| } | |||
| // pcd | |||
| /** | |||
| * Adds support for loading Point cloud data `.pcd`, `application/pcd` files and data uris | |||
| */ | |||
| export class PCDLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'PCDLoadPlugin' | |||
| protected _importer = new Importer(class extends PCDLoader implements ILoader { | |||
| transform(points: Points, options: AnyOptions): any { | |||
| if (options.autoCenter) points.geometry.center() | |||
| points.geometry.rotateX(Math.PI) | |||
| return points | |||
| } | |||
| }, ['pcd'], ['application/pcd'], false) | |||
| } | |||
| // tilt | |||
| /** | |||
| * Adds support for loading Tilt brush `.tilt`, `application/tilt` files and data uris | |||
| */ | |||
| export class TiltLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'TiltLoadPlugin' | |||
| protected _importer = new Importer(TiltLoader, ['tilt'], ['application/tilt'], false) | |||
| } | |||
| // vrml | |||
| /** | |||
| * Adds support for loading VRML `.wrl`, `model/vrml` files and data uris | |||
| */ | |||
| export class VRMLLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'VRMLLoadPlugin' | |||
| protected _importer = new Importer(VRMLLoader, ['wrl'], ['model/vrml'], false) | |||
| } | |||
| // ldraw | |||
| /** | |||
| * Adds support for loading LDraw `.mpd`, `application/mpd` files and data uris. see https://ldraw.org | |||
| */ | |||
| export class LDrawLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'LDrawLoadPlugin' | |||
| protected _importer = new Importer(class extends LDrawLoader implements ILoader { | |||
| transform(res: Group, _: AnyOptions): any { | |||
| // Convert from LDraw coordinates: rotate 180 degrees around OX | |||
| res.rotation.x = Math.PI | |||
| return res | |||
| } | |||
| }, ['mpd'], ['application/ldraw'], false) | |||
| } | |||
| // vtk | |||
| /** | |||
| * Adds support for loading VTK `.vtk`, '.vtp', `application/vtk` files and data uris | |||
| **/ | |||
| export class VTKLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'VTKLoadPlugin' | |||
| protected _importer = new Importer(class extends VTKLoader implements ILoader { | |||
| transform(res: BufferGeometry, _: AnyOptions): Mesh|undefined { | |||
| if (!res.attributes?.normal) res.computeVertexNormals() | |||
| // todo set mesh name from options/path | |||
| return res ? new Mesh(res, new PhysicalMaterial({ | |||
| color: new Color(1, 1, 1), | |||
| vertexColors: res.hasAttribute('color'), | |||
| })) : undefined | |||
| } | |||
| }, ['vtk', 'vtp'], ['application/vtk'], false) | |||
| } | |||
| // xyz | |||
| /** | |||
| * Adds support for loading XYZ `.xyz`, `text/plain+xyz` files and data uris | |||
| */ | |||
| export class XYZLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'XYZLoadPlugin' | |||
| protected _importer = new Importer(class extends XYZLoader implements ILoader { | |||
| transform(res: BufferGeometry, options: AnyOptions): Points|undefined { | |||
| if (!res.attributes?.normal) res.computeVertexNormals() | |||
| if (options.autoCenter) res.center() | |||
| return res ? new Points(res, new PointsMaterial({ | |||
| size: 0.1, | |||
| vertexColors: res.hasAttribute('color'), | |||
| })) : undefined | |||
| } | |||
| }, ['xyz'], ['text/plain+xyz'], false) | |||
| } | |||
| export const extraImportPlugins = [ | |||
| TDSLoadPlugin, | |||
| ThreeMFLoadPlugin, | |||
| ColladaLoadPlugin, | |||
| AMFLoadPlugin, | |||
| GCodeLoadPlugin, | |||
| BVHLoadPlugin, | |||
| VOXLoadPlugin, | |||
| MDDLoadPlugin, | |||
| PCDLoadPlugin, | |||
| TiltLoadPlugin, | |||
| VRMLLoadPlugin, | |||
| LDrawLoadPlugin, | |||
| VTKLoadPlugin, | |||
| XYZLoadPlugin, | |||
| ] as const | |||
| @@ -0,0 +1,41 @@ | |||
| { | |||
| "compilerOptions": { | |||
| "baseUrl": "./src", | |||
| "rootDir": "./src", | |||
| "allowJs": false, | |||
| "checkJs": false, | |||
| "skipLibCheck": true, | |||
| "allowSyntheticDefaultImports": true, | |||
| "experimentalDecorators": true, | |||
| "isolatedModules": true, | |||
| "module": "es2020", | |||
| "noImplicitAny": true, | |||
| "declaration": true, | |||
| "declarationMap": true, | |||
| "declarationDir": "dist", | |||
| "outDir": "dist", | |||
| "noImplicitThis": true, | |||
| "noUnusedLocals": true, | |||
| "noUnusedParameters": true, | |||
| "removeComments": false, | |||
| "preserveConstEnums": true, | |||
| "moduleResolution": "node", | |||
| "emitDecoratorMetadata": false, | |||
| "sourceMap": true, | |||
| "target": "ES2020", | |||
| "strictNullChecks": true, | |||
| "lib": [ | |||
| "es2020", | |||
| "esnext", | |||
| "dom" | |||
| ] | |||
| }, | |||
| "include": [ | |||
| "src/**/*" | |||
| ], | |||
| "exclude": [ | |||
| "node_modules", | |||
| "**/*.spec.ts", | |||
| "dist" | |||
| ] | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| { | |||
| "extends": [ | |||
| "../../typedoc.json" | |||
| ], | |||
| "entryPoints": [ | |||
| "src/index.ts" | |||
| ], | |||
| "name": "Threepipe Extra Importer Plugins", | |||
| "readme": "none" | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| { | |||
| "name": "threepipe-plugin-template-rollup", | |||
| "version": "0.0.1", | |||
| "lockfileVersion": 2, | |||
| "requires": true, | |||
| "packages": { | |||
| "": { | |||
| "name": "threepipe-plugin-template-rollup", | |||
| "version": "0.0.1", | |||
| "license": "Apache-2.0", | |||
| "dependencies": { | |||
| "@threepipe/plugin-tweakpane": "file:./../tweakpane/src/", | |||
| "threepipe": "file:./../../src/" | |||
| }, | |||
| "devDependencies": {} | |||
| }, | |||
| "../../src": {}, | |||
| "../tweakpane/src": {}, | |||
| "node_modules/@threepipe/plugin-tweakpane": { | |||
| "resolved": "../tweakpane/src", | |||
| "link": true | |||
| }, | |||
| "node_modules/threepipe": { | |||
| "resolved": "../../src", | |||
| "link": true | |||
| } | |||
| }, | |||
| "dependencies": { | |||
| "@threepipe/plugin-tweakpane": { | |||
| "version": "file:../tweakpane/src" | |||
| }, | |||
| "threepipe": { | |||
| "version": "file:../../src" | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| { | |||
| "name": "threepipe-plugin-template-rollup", | |||
| "description": "Sample Threepipe plugin using rollup", | |||
| "version": "0.1.0", | |||
| "devDependencies": { | |||
| }, | |||
| "dependencies": { | |||
| "threepipe": "file:./../../src/", | |||
| "@threepipe/plugin-tweakpane": "file:./../tweakpane/src/" | |||
| }, | |||
| "clean-package": { | |||
| "remove": [ | |||
| "clean-package", | |||
| "scripts", | |||
| "devDependencies", | |||
| "//", | |||
| "markdown-to-html" | |||
| ], | |||
| "replace": { | |||
| "dependencies": { | |||
| "threepipe": "^0.0.8", | |||
| "@threepipe/plugin-tweakpane": "^0.1.2" | |||
| } | |||
| } | |||
| }, | |||
| "type": "module", | |||
| "main": "dist/index.js", | |||
| "module": "dist/index.mjs", | |||
| "types": "dist/index.d.ts", | |||
| "files": [ | |||
| "dist", | |||
| "src" | |||
| ], | |||
| "scripts": { | |||
| "new:pack": "npm run prepare && clean-package && npm pack && clean-package restore", | |||
| "new:publish": "npm run prepare && clean-package && npm publish --access public && clean-package restore", | |||
| "prepare": "npm run build", | |||
| "build": "rimraf dist && NODE_ENV=production rollup -c", | |||
| "dev": "rollup -c -w", | |||
| "docs": "rimraf docs && npx typedoc" | |||
| }, | |||
| "author": "repalash <palash@shaders.app>", | |||
| "license": "Apache-2.0", | |||
| "keywords": [ | |||
| "three", | |||
| "three.js", | |||
| "threepipe", | |||
| "tweakpane", | |||
| "editor", | |||
| "plugin" | |||
| ], | |||
| "bugs": { | |||
| "url": "https://github.com/repalash/threepipe/issues" | |||
| }, | |||
| "homepage": "https://github.com/repalash/threepipe#readme", | |||
| "repository": { | |||
| "type": "git", | |||
| "url": "git://github.com/repalash/threepipe.git" | |||
| } | |||
| } | |||
| @@ -0,0 +1,97 @@ | |||
| // rollup.config.js | |||
| import commonjs from '@rollup/plugin-commonjs'; | |||
| import json from '@rollup/plugin-json'; | |||
| import resolve from '@rollup/plugin-node-resolve'; | |||
| import typescript from '@rollup/plugin-typescript'; | |||
| import license from 'rollup-plugin-license' | |||
| import packageJson from './package.json' assert {type: 'json'}; | |||
| import path from 'path' | |||
| import {fileURLToPath} from 'url'; | |||
| import postcss from 'rollup-plugin-postcss' | |||
| import replace from 'rollup-plugin-replace' | |||
| import terser from "@rollup/plugin-terser"; | |||
| const __filename = fileURLToPath(import.meta.url); | |||
| const __dirname = path.dirname(__filename); | |||
| const {name, version, author} = packageJson | |||
| // const {main, module, browser} = packageJson["clean-package"].replace | |||
| const isProduction = process.env.NODE_ENV === 'production' | |||
| const settings = { | |||
| globals: { | |||
| "threepipe": "threepipe", | |||
| "@threepipe/plugin-tweakpane": "@threepipe/plugin-tweakpane" | |||
| }, | |||
| sourcemap: true | |||
| } | |||
| export default { | |||
| input: './src/index.ts', | |||
| output: [ | |||
| // { | |||
| // file: main, | |||
| // name: main, | |||
| // ...settings, | |||
| // format: 'cjs', | |||
| // plugins: [ | |||
| // isProduction && terser() | |||
| // ] | |||
| // }, | |||
| { | |||
| file: './dist/index.mjs', | |||
| ...settings, | |||
| name: name, | |||
| format: 'es', | |||
| plugins: [ | |||
| isProduction && terser() | |||
| ] | |||
| }, | |||
| { | |||
| file: './dist/index.js', | |||
| ...settings, | |||
| name: name, | |||
| format: 'umd', | |||
| plugins: [ | |||
| isProduction && terser() | |||
| ] | |||
| } | |||
| ], | |||
| external: Object.keys(settings.globals), | |||
| plugins: [ | |||
| replace({ | |||
| // If you would like DEV messages, specify 'development' | |||
| // Otherwise use 'production' | |||
| 'process.env.NODE_ENV': JSON.stringify('production') // for tippy.js | |||
| }), | |||
| postcss({ | |||
| modules: false, | |||
| autoModules: true, // todo; issues with typescript import css, because inject is false | |||
| inject: false, | |||
| minimize: isProduction, | |||
| // Or with custom options for `postcss-modules` | |||
| }), | |||
| json(), | |||
| resolve({}), | |||
| typescript({ | |||
| }), | |||
| commonjs({ | |||
| include: 'node_modules/**', | |||
| extensions: ['.js'], | |||
| ignoreGlobal: false, | |||
| sourceMap: false | |||
| }), | |||
| license({ | |||
| banner: ` | |||
| @license | |||
| ${name} v${version} | |||
| Copyright 2022<%= moment().format('YYYY') > 2022 ? '-' + moment().format('YYYY') : null %> ${author} | |||
| ${packageJson.license} License | |||
| `, | |||
| thirdParty: { | |||
| output: path.join(__dirname, 'dist', 'dependencies.txt'), | |||
| includePrivate: true, // Default is false. | |||
| }, | |||
| }) | |||
| ] | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| html{ | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| import {AViewerPluginSync, createStyles, ThreeViewer,} from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| import styles from './SamplePlugin.css' | |||
| console.log(TweakpaneUiPlugin) | |||
| export class SamplePlugin extends AViewerPluginSync<string> { | |||
| public static readonly PluginType: string = 'SamplePlugin' | |||
| enabled = true | |||
| dependencies = [] | |||
| constructor() { | |||
| super() | |||
| } | |||
| onAdded(viewer: ThreeViewer) { | |||
| super.onAdded(viewer) | |||
| createStyles(styles) | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| declare module '*.txt' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.glsl' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.vert' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.frag' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.module.scss' { | |||
| const content: any | |||
| export default content | |||
| export const stylesheet: string | |||
| } | |||
| declare module '*.module.css' { | |||
| const content: any | |||
| export default content | |||
| export const stylesheet: string | |||
| } | |||
| declare module '*.css' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| // export {} | |||
| // hack for typedoc | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| // declare type OffscreenCanvas = HTMLCanvasElement | |||
| @@ -0,0 +1 @@ | |||
| export {SamplePlugin} from './SamplePlugin' | |||
| @@ -0,0 +1,41 @@ | |||
| { | |||
| "compilerOptions": { | |||
| "baseUrl": "./src", | |||
| "rootDir": "./src", | |||
| "allowJs": false, | |||
| "checkJs": false, | |||
| "skipLibCheck": true, | |||
| "allowSyntheticDefaultImports": true, | |||
| "experimentalDecorators": true, | |||
| "isolatedModules": true, | |||
| "module": "es2020", | |||
| "noImplicitAny": true, | |||
| "declaration": true, | |||
| "declarationMap": true, | |||
| "declarationDir": "dist", | |||
| "outDir": "dist", | |||
| "noImplicitThis": true, | |||
| "noUnusedLocals": true, | |||
| "noUnusedParameters": true, | |||
| "removeComments": false, | |||
| "preserveConstEnums": true, | |||
| "moduleResolution": "node", | |||
| "emitDecoratorMetadata": false, | |||
| "sourceMap": true, | |||
| "target": "ES2020", | |||
| "strictNullChecks": true, | |||
| "lib": [ | |||
| "es2020", | |||
| "esnext", | |||
| "dom" | |||
| ] | |||
| }, | |||
| "include": [ | |||
| "src/**/*" | |||
| ], | |||
| "exclude": [ | |||
| "node_modules", | |||
| "**/*.spec.ts", | |||
| "dist" | |||
| ] | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| { | |||
| "extends": [ | |||
| "../../typedoc.json" | |||
| ], | |||
| "entryPoints": [ | |||
| "src/index.ts" | |||
| ], | |||
| "name": "Threepipe Tweakpane Editor Plugin", | |||
| "readme": "none" | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| import { | |||
| CustomContextMenu, | |||
| DataTexture, | |||
| EXRExporter2, | |||
| FloatType, | |||
| generateUUID, | |||
| @@ -20,7 +21,6 @@ import { | |||
| } from 'threepipe' | |||
| import type {UiObjectConfig} from 'uiconfig.js' | |||
| import {TweakpaneUiPlugin} from './TweakpaneUiPlugin' | |||
| import {DataTexture} from 'three' | |||
| const staticData = { | |||
| placeholderVal: 'placeholder', | |||
| @@ -46,6 +46,7 @@ function proxyGetValue(cc: any, viewer: ThreeViewer) { | |||
| // } | |||
| if (cc.isTexture) { | |||
| // console.warn('here') | |||
| // todo: use textureToCanvas for data texture | |||
| if (cc.image && !cc.image.tp_src) { | |||
| if (cc.image instanceof ImageBitmap || cc.image instanceof HTMLImageElement || cc.image instanceof HTMLVideoElement) { // todo: support playback in video | |||
| cc.image.tp_src = imageBitmapToBase64(cc.image, 160) | |||
| @@ -220,6 +221,7 @@ function downloadImage(config: UiObjectConfig, _: TweakpaneUiPlugin, viewer: Thr | |||
| // data texture | |||
| if (!src && tex.isDataTexture) { | |||
| if (tex.type !== HalfFloatType && tex.type !== FloatType) { | |||
| // todo: use textureToCanvas for data texture | |||
| console.error('Only Float and HalfFloat Data texture export is supported', vcv, tex, config) | |||
| return | |||
| } | |||
| @@ -11,13 +11,14 @@ export function loopPluginDirs(callback){ | |||
| const pluginDir = path.join(pluginsDir, pluginFolder) | |||
| const packageJsonPath = path.join(pluginDir, 'package.json') | |||
| if (!fs.existsSync(packageJsonPath)) continue; | |||
| callback(pluginDir) | |||
| callback(pluginDir, pluginFolder) | |||
| } | |||
| } | |||
| export function execEachPlugin(command){ | |||
| loopPluginDirs((pluginDir) => { | |||
| export function execEachPlugin(command, templates = false){ | |||
| loopPluginDirs((pluginDir, pluginFolder) => { | |||
| if(!templates && pluginFolder.startsWith('plugin-template-')) return; | |||
| console.log(`Executing ${command} in ${pluginDir}`) | |||
| execSync(command, {cwd: pluginDir, stdio: 'inherit'}) | |||
| }) | |||
| @@ -74,7 +74,6 @@ export type AddRawOptions = ProcessRawOptions & AddAssetOptions | |||
| * @category Asset Manager | |||
| */ | |||
| export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult}, 'loadAsset'> { | |||
| static readonly PluginType = 'AssetManager' | |||
| readonly viewer: ThreeViewer | |||
| readonly importer: AssetImporter | |||
| readonly exporter: AssetExporter | |||
| @@ -498,5 +497,9 @@ export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult} | |||
| return this.viewer?.loadConfigResources(json, extraResources) | |||
| } | |||
| /** | |||
| * @deprecated not a plugin anymore | |||
| */ | |||
| static readonly PluginType = 'AssetManager' | |||
| // endregion | |||
| } | |||
| @@ -81,6 +81,7 @@ export interface IRenderManagerOptions { | |||
| rgbm?: boolean, | |||
| msaa?: boolean, | |||
| depthBuffer?: boolean, | |||
| renderScale?: number, | |||
| } | |||
| export interface IWebGLRenderer<TManager extends IRenderManager=IRenderManager> extends WebGLRenderer { | |||
| @@ -107,7 +107,7 @@ export interface IScene<E extends ISceneEvent = ISceneEvent, ET extends ISceneEv | |||
| // region deprecated | |||
| /** | |||
| * @deprecated use {@link IObject3D.getObjectByName} instead | |||
| @deprecated use {@link getObjectByName} instead | |||
| * @param name | |||
| * @param parent | |||
| */ | |||
| @@ -36,12 +36,12 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| readonly modelRoot: IObject3D | |||
| @uiColor<RootScene>('Background Color', (s)=>({ | |||
| onChange: ()=>s?._onBackgroundChange(), | |||
| onChange: ()=>s?.onBackgroundChange(), | |||
| })) | |||
| @serialize() @onChange2(RootScene.prototype._onBackgroundChange) | |||
| @serialize() @onChange2(RootScene.prototype.onBackgroundChange) | |||
| backgroundColor: Color | null = null // read in three.js WebGLBackground | |||
| @onChange2(RootScene.prototype._onBackgroundChange) | |||
| @onChange2(RootScene.prototype.onBackgroundChange) | |||
| @serialize() @uiImage('Background Image') | |||
| background: null | Color | ITexture | 'environment' = null | |||
| /** | |||
| @@ -266,7 +266,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| this.refreshUi?.() | |||
| } | |||
| private _onBackgroundChange() { | |||
| onBackgroundChange() { | |||
| this.dispatchEvent({type: 'backgroundChanged', background: this.background, backgroundColor: this.backgroundColor}) | |||
| this.setDirty({refreshScene: true, geometryChanged: false}) | |||
| this.refreshUi?.() | |||
| @@ -358,20 +358,6 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| return | |||
| } | |||
| /** | |||
| * Find objects by name exact match in the complete hierarchy. | |||
| * @param name - name | |||
| * @param parent - optional root node to start search from | |||
| * @returns Array of found objects | |||
| */ | |||
| public findObjectsByName(name: string, parent?: IObject3D): IObject3D[] { | |||
| const o: IObject3D[] = []; | |||
| (parent ?? this).traverse(object => { | |||
| if (object.name === name) o.push(object) | |||
| }) | |||
| return o | |||
| } | |||
| /** | |||
| * Returns the bounding box of the scene model root. | |||
| * @param precise | |||
| @@ -414,36 +400,6 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| return this | |||
| } | |||
| /** | |||
| * @deprecated | |||
| * Sets the camera pointing towards the object at a specific distance. | |||
| * @param rootObject - The object to point at. | |||
| * @param centerOffset - The distance offset from the object to point at. | |||
| * @param targetOffset - The distance offset for the target from the center of object to point at. | |||
| * @param options - Not used yet. | |||
| */ | |||
| resetCamera(rootObject:Object3D|undefined = undefined, centerOffset = new Vector3(1, 1, 1), targetOffset = new Vector3(0, 0, 0)): void { | |||
| if (this._mainCamera) { | |||
| this.matrixWorldNeedsUpdate = true | |||
| this.updateMatrixWorld(true) | |||
| const bounds = rootObject ? new Box3B().expandByObject(rootObject, true, true) : this.getBounds(true) | |||
| const center = bounds.getCenter(new Vector3()) | |||
| const radius = bounds.getSize(new Vector3()).length() * 0.5 | |||
| center.add(targetOffset.clone().multiplyScalar(radius)) | |||
| this._mainCamera.position = new Vector3( // todo: for nested cameras? | |||
| center.x + centerOffset.x * radius, | |||
| center.y + centerOffset.y * radius, | |||
| center.z + centerOffset.z * radius, | |||
| ) | |||
| this._mainCamera.target = center | |||
| // this.scene.mainCamera.controls?.targetOffset.set(0, 0, 0) | |||
| this.setDirty() | |||
| } | |||
| } | |||
| /** | |||
| * Serialize the scene properties | |||
| * @param meta | |||
| @@ -480,49 +436,6 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| super.addEventListener(type, listener) | |||
| } | |||
| /** | |||
| * Minimum Camera near plane | |||
| * @deprecated - use camera.userData.minNearPlane instead | |||
| */ | |||
| get minNearDistance(): number { | |||
| console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead') | |||
| return this.mainCamera.userData.minNearPlane ?? 0.02 | |||
| } | |||
| /** | |||
| * @deprecated - use camera.userData.minNearPlane instead | |||
| */ | |||
| set minNearDistance(value: number) { | |||
| console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead') | |||
| if (this.mainCamera) | |||
| this.mainCamera.userData.minNearPlane = value | |||
| } | |||
| /** | |||
| * @deprecated | |||
| */ | |||
| get activeCamera(): ICamera { | |||
| console.error('activeCamera is deprecated. Use mainCamera instead.') | |||
| return this.mainCamera | |||
| } | |||
| /** | |||
| * @deprecated | |||
| */ | |||
| set activeCamera(camera: ICamera | undefined) { | |||
| console.error('activeCamera is deprecated. Use mainCamera instead.') | |||
| this.mainCamera = camera | |||
| } | |||
| /** | |||
| * Get the threejs scene object | |||
| * @deprecated | |||
| */ | |||
| get modelObject(): this { | |||
| return this as any | |||
| } | |||
| // region inherited type fixes | |||
| // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936 | |||
| @@ -581,6 +494,108 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I | |||
| // } | |||
| /** | |||
| * Find objects by name exact match in the complete hierarchy. | |||
| * @deprecated Use {@link getObjectByName} instead. | |||
| * @param name - name | |||
| * @param parent - optional root node to start search from | |||
| * @returns Array of found objects | |||
| */ | |||
| public findObjectsByName(name: string, parent?: IObject3D): IObject3D[] { | |||
| const o: IObject3D[] = []; | |||
| (parent ?? this).traverse(object => { | |||
| if (object.name === name) o.push(object) | |||
| }) | |||
| return o | |||
| } | |||
| /** | |||
| * @deprecated | |||
| * Sets the camera pointing towards the object at a specific distance. | |||
| * @param rootObject - The object to point at. | |||
| * @param centerOffset - The distance offset from the object to point at. | |||
| * @param targetOffset - The distance offset for the target from the center of object to point at. | |||
| * @param options - Not used yet. | |||
| */ | |||
| resetCamera(rootObject:Object3D|undefined = undefined, centerOffset = new Vector3(1, 1, 1), targetOffset = new Vector3(0, 0, 0)): void { | |||
| if (this._mainCamera) { | |||
| this.matrixWorldNeedsUpdate = true | |||
| this.updateMatrixWorld(true) | |||
| const bounds = rootObject ? new Box3B().expandByObject(rootObject, true, true) : this.getBounds(true) | |||
| const center = bounds.getCenter(new Vector3()) | |||
| const radius = bounds.getSize(new Vector3()).length() * 0.5 | |||
| center.add(targetOffset.clone().multiplyScalar(radius)) | |||
| this._mainCamera.position = new Vector3( // todo: for nested cameras? | |||
| center.x + centerOffset.x * radius, | |||
| center.y + centerOffset.y * radius, | |||
| center.z + centerOffset.z * radius, | |||
| ) | |||
| this._mainCamera.target = center | |||
| // this.scene.mainCamera.controls?.targetOffset.set(0, 0, 0) | |||
| this.setDirty() | |||
| } | |||
| } | |||
| /** | |||
| * Minimum Camera near plane | |||
| * @deprecated - use camera.userData.minNearPlane instead | |||
| */ | |||
| get minNearDistance(): number { | |||
| console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead') | |||
| return this.mainCamera.userData.minNearPlane ?? 0.02 | |||
| } | |||
| /** | |||
| * @deprecated - use camera.userData.minNearPlane instead | |||
| */ | |||
| set minNearDistance(value: number) { | |||
| console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead') | |||
| if (this.mainCamera) | |||
| this.mainCamera.userData.minNearPlane = value | |||
| } | |||
| /** | |||
| * @deprecated | |||
| */ | |||
| get activeCamera(): ICamera { | |||
| console.error('activeCamera is deprecated. Use mainCamera instead.') | |||
| return this.mainCamera | |||
| } | |||
| /** | |||
| * @deprecated | |||
| */ | |||
| set activeCamera(camera: ICamera | undefined) { | |||
| console.error('activeCamera is deprecated. Use mainCamera instead.') | |||
| this.mainCamera = camera | |||
| } | |||
| /** | |||
| * Get the threejs scene object | |||
| * @deprecated | |||
| */ | |||
| get modelObject(): this { | |||
| return this as any | |||
| } | |||
| /** | |||
| * @deprecated use {@link envMapIntensity} instead | |||
| */ | |||
| get environmentIntensity(): number { | |||
| return this.envMapIntensity | |||
| } | |||
| /** | |||
| * @deprecated use {@link envMapIntensity} instead | |||
| */ | |||
| set environmentIntensity(value: number) { | |||
| this.envMapIntensity = value | |||
| } | |||
| /** | |||
| * Add any processed scene object to the scene. | |||
| * @deprecated renamed to {@link addObject} | |||
| @@ -0,0 +1,24 @@ | |||
| import {IViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {Importer} from '../../assetmanager' | |||
| export abstract class BaseImporterPlugin implements IViewerPluginSync { | |||
| declare ['constructor']: typeof BaseImporterPlugin | |||
| public static readonly PluginType: string | |||
| protected abstract _importer: Importer | |||
| toJSON: any = null // disable serialization | |||
| onAdded(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.addImporter(this._importer) | |||
| } | |||
| onRemove(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.removeImporter(this._importer) | |||
| } | |||
| dispose() { | |||
| return | |||
| } | |||
| } | |||
| @@ -1,19 +1,18 @@ | |||
| import {IViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {ThreeViewer} from '../../viewer' | |||
| import {GLTFWriter2, ILoader, Importer, ImportResultExtras} from '../../assetmanager' | |||
| import {KTX2Loader} from 'three/examples/jsm/loaders/KTX2Loader.js' | |||
| import {CompressedTexture} from 'three' | |||
| import {serializeTextureInExtras} from '../../utils' | |||
| import {ITexture} from '../../core' | |||
| import {BaseImporterPlugin} from '../base/BaseImporterPlugin' | |||
| /** | |||
| * Adds support for loading Compressed Textures of format `.ktx2`, `image/ktx2` files and data uris. | |||
| * @category Plugins | |||
| */ | |||
| export class KTX2LoadPlugin implements IViewerPluginSync { | |||
| declare ['constructor']: typeof KTX2LoadPlugin | |||
| export class KTX2LoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'KTX2LoadPlugin' | |||
| private _importer = new Importer(KTX2Loader2, ['ktx2'], ['image/ktx2'], false) | |||
| protected _importer = new Importer(KTX2Loader2, ['ktx2'], ['image/ktx2'], false) | |||
| public static TRANSCODER_LIBRARY_PATH = 'https://cdn.jsdelivr.net/gh/BinomialLLC/basis_universal@1.16.4/webgl/transcoder/build/' | |||
| @@ -21,20 +20,17 @@ export class KTX2LoadPlugin implements IViewerPluginSync { | |||
| this._importer.onCtor = (l: KTX2Loader2) => l | |||
| .setTranscoderPath(KTX2LoadPlugin.TRANSCODER_LIBRARY_PATH) | |||
| .detectSupport(viewer.renderManager.renderer) | |||
| viewer.assetManager.importer.addImporter(this._importer) | |||
| super.onAdded(viewer) | |||
| viewer.assetManager.exporter.getExporter('gltf', 'glb')?.extensions?.push(glTFTextureBasisUExtensionExport) | |||
| } | |||
| onRemove(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.removeImporter(this._importer) | |||
| super.onRemove(viewer) | |||
| const exporter = viewer.assetManager.exporter.getExporter('gltf', 'glb') | |||
| const index = exporter?.extensions?.indexOf(glTFTextureBasisUExtensionExport) | |||
| if (index !== undefined && index !== -1) exporter?.extensions?.splice(index, 1) | |||
| } | |||
| dispose() { | |||
| return | |||
| } | |||
| } | |||
| @@ -1,27 +1,13 @@ | |||
| import {IViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {Importer} from '../../assetmanager' | |||
| import {KTXLoader} from 'three/examples/jsm/loaders/KTXLoader.js' | |||
| import {BaseImporterPlugin} from '../base/BaseImporterPlugin' | |||
| /** | |||
| * Adds support for loading `.ktx`, `image/ktx` files and data uris. | |||
| * @category Plugins | |||
| */ | |||
| export class KTXLoadPlugin implements IViewerPluginSync { | |||
| declare ['constructor']: typeof KTXLoadPlugin | |||
| export class KTXLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'KTXLoadPlugin' | |||
| private _importer = new Importer(KTXLoader, ['ktx'], ['image/ktx'], false) | |||
| onAdded(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.addImporter(this._importer) | |||
| } | |||
| onRemove(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.removeImporter(this._importer) | |||
| } | |||
| dispose() { | |||
| return | |||
| } | |||
| protected _importer = new Importer(KTXLoader, ['ktx'], ['image/ktx'], false) | |||
| } | |||
| @@ -1,36 +1,24 @@ | |||
| import {IViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {ILoader, Importer} from '../../assetmanager' | |||
| import {PLYLoader} from 'three/examples/jsm/loaders/PLYLoader.js' | |||
| import {AnyOptions} from 'ts-browser-helpers' | |||
| import {BufferGeometry, Color, Mesh} from 'three' | |||
| import {PhysicalMaterial} from '../../core' | |||
| import {BaseImporterPlugin} from '../base/BaseImporterPlugin' | |||
| /** | |||
| * Adds support for loading `.ply`, `text/plain+ply` files and data uris | |||
| * @category Plugins | |||
| */ | |||
| export class PLYLoadPlugin implements IViewerPluginSync { | |||
| declare ['constructor']: typeof PLYLoadPlugin | |||
| export class PLYLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'PLYLoadPlugin' | |||
| private _importer = new Importer(class extends PLYLoader implements ILoader { | |||
| protected _importer = new Importer(class extends PLYLoader implements ILoader { | |||
| transform(res: BufferGeometry, _: AnyOptions): Mesh|undefined { | |||
| if (!res.attributes?.normal) res.computeVertexNormals() | |||
| // todo set mesh name from options/path | |||
| return res ? new Mesh(res, new PhysicalMaterial({color: new Color(1, 1, 1)})) : undefined | |||
| return res ? new Mesh(res, new PhysicalMaterial({ | |||
| color: new Color(1, 1, 1), | |||
| vertexColors: res.hasAttribute('color'), | |||
| })) : undefined | |||
| } | |||
| }, ['ply'], ['text/plain+ply'], false) | |||
| onAdded(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.addImporter(this._importer) | |||
| } | |||
| onRemove(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.removeImporter(this._importer) | |||
| } | |||
| dispose() { | |||
| return | |||
| } | |||
| } | |||
| @@ -1,26 +1,11 @@ | |||
| import {IViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {Importer, Rhino3dmLoader2} from '../../assetmanager' | |||
| import {BaseImporterPlugin} from '../base/BaseImporterPlugin' | |||
| /** | |||
| * Adds support for loading Rhino `.3dm`, `model/vnd.3dm`, `model/3dm` files and data uris. | |||
| * @category Plugins | |||
| */ | |||
| export class Rhino3dmLoadPlugin implements IViewerPluginSync { | |||
| declare ['constructor']: typeof Rhino3dmLoadPlugin | |||
| export class Rhino3dmLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'Rhino3dmLoadPlugin' | |||
| private _importer = new Importer(Rhino3dmLoader2, ['3dm'], ['model/vnd.3dm', 'model/3dm'], true) | |||
| onAdded(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.addImporter(this._importer) | |||
| } | |||
| onRemove(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.removeImporter(this._importer) | |||
| } | |||
| dispose() { | |||
| return | |||
| } | |||
| protected _importer = new Importer(Rhino3dmLoader2, ['3dm'], ['model/vnd.3dm', 'model/3dm'], true) | |||
| } | |||
| @@ -1,36 +1,25 @@ | |||
| import {IViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {ILoader, Importer} from '../../assetmanager' | |||
| import {STLLoader} from 'three/examples/jsm/loaders/STLLoader.js' | |||
| import {BufferGeometry, Color, Mesh} from 'three' | |||
| import {AnyOptions} from 'ts-browser-helpers' | |||
| import {PhysicalMaterial} from '../../core' | |||
| import {BaseImporterPlugin} from '../base/BaseImporterPlugin' | |||
| /** | |||
| * Adds support for loading `.stl`, `model/stl` files and data uris. | |||
| * @category Plugins | |||
| */ | |||
| export class STLLoadPlugin implements IViewerPluginSync { | |||
| declare ['constructor']: typeof STLLoadPlugin | |||
| export class STLLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'STLLoadPlugin' | |||
| private _importer = new Importer(class extends STLLoader implements ILoader { | |||
| protected _importer = new Importer(class extends STLLoader implements ILoader { | |||
| transform(res: BufferGeometry, _: AnyOptions): Mesh|undefined { | |||
| if (!res.attributes?.normal) res.computeVertexNormals() | |||
| // todo set mesh name from options/path | |||
| return res ? new Mesh(res, new PhysicalMaterial({color: new Color(1, 1, 1)})) : undefined | |||
| return res ? new Mesh(res, new PhysicalMaterial({ | |||
| color: new Color(1, 1, 1), | |||
| vertexColors: res.hasAttribute('color'), | |||
| })) : undefined | |||
| } | |||
| }, ['stl'], ['model/stl', 'model/x.stl-binary', 'model/x.stl-ascii'], false) | |||
| onAdded(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.addImporter(this._importer) | |||
| } | |||
| onRemove(viewer: ThreeViewer) { | |||
| viewer.assetManager.importer.removeImporter(this._importer) | |||
| } | |||
| dispose() { | |||
| return | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| import {Importer} from '../../assetmanager' | |||
| import {USDZLoader} from 'three/examples/jsm/loaders/USDZLoader.js' | |||
| import {Group, Mesh} from 'three' | |||
| import {Zippable, zipSync} from 'three/examples/jsm/libs/fflate.module.js' | |||
| import {BaseImporterPlugin} from '../base/BaseImporterPlugin' | |||
| /** | |||
| * Adds support for loading `.usdz`, `model/vnd.usd+zip` and `.usda`, `model/vnd.usda` files and data uris | |||
| * @category Plugins | |||
| */ | |||
| export class USDZLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'USDZLoadPlugin' | |||
| protected _importer = new Importer(class extends USDZLoader { | |||
| currentUrl = '' | |||
| async loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<Mesh> { | |||
| this.currentUrl = url | |||
| const res = await super.loadAsync(url, onProgress) | |||
| this.currentUrl = '' | |||
| return res | |||
| } | |||
| parse(buffer: ArrayBuffer|string): Group { | |||
| // todo make changes in three.js to allow passing unzipped buffer directly for usda | |||
| if (this.currentUrl.endsWith('.usda') && typeof buffer !== 'string') { | |||
| const filename = this.currentUrl.split('/').pop() | |||
| if (filename) { | |||
| const zip: Zippable = {} | |||
| zip[filename] = new Uint8Array(buffer) | |||
| buffer = zipSync(zip).buffer | |||
| } | |||
| } | |||
| return super.parse(buffer) | |||
| } | |||
| }, ['usdz', 'usda'], ['model/vnd.usd+zip', 'model/vnd.usdz+zip', 'model/vnd.usda'], false) | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| // base | |||
| export {PipelinePassPlugin} from './base/PipelinePassPlugin' | |||
| export {BaseImporterPlugin} from './base/BaseImporterPlugin' | |||
| // pipeline | |||
| export {ProgressivePlugin} from './pipeline/ProgressivePlugin' | |||
| @@ -12,6 +13,7 @@ export type {NormalBufferPluginEventTypes, NormalBufferPluginPass, NormalBufferP | |||
| // ui | |||
| export {RenderTargetPreviewPlugin} from './ui/RenderTargetPreviewPlugin' | |||
| export {GeometryUVPreviewPlugin} from './ui/GeometryUVPreviewPlugin' | |||
| export {ViewerUiConfigPlugin} from './ui/ViewerUiConfigPlugin' | |||
| export {SceneUiConfigPlugin} from './ui/SceneUiConfigPlugin' | |||
| @@ -21,6 +23,7 @@ export {FullScreenPlugin} from './interaction/FullScreenPlugin' | |||
| // import | |||
| export {Rhino3dmLoadPlugin} from './import/Rhino3dmLoadPlugin' | |||
| export {USDZLoadPlugin} from './import/USDZLoadPlugin' | |||
| export {PLYLoadPlugin} from './import/PLYLoadPlugin' | |||
| export {STLLoadPlugin} from './import/STLLoadPlugin' | |||
| export {KTXLoadPlugin} from './import/KTXLoadPlugin' | |||
| @@ -0,0 +1,49 @@ | |||
| #GeometryUVPreviewPluginContainer{ | |||
| position: absolute; | |||
| left: 0; | |||
| bottom: 0; | |||
| width: 100%; | |||
| z-index: 1000; | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| flex-direction: row; | |||
| gap: 5px; | |||
| padding: 5px; | |||
| pointer-events: none; | |||
| height: auto; | |||
| } | |||
| .GeometryUVPreviewPluginTarget{ | |||
| position: relative; | |||
| width: 200px; | |||
| height: 200px; | |||
| } | |||
| .GeometryUVPreviewPluginCollapsed{ | |||
| height: 25px; | |||
| } | |||
| .GeometryUVPreviewPluginTargetHeader{ | |||
| position: absolute; | |||
| top: 0; | |||
| left: 0; | |||
| right: 0; | |||
| background: rgba(0,0,0,0.5); | |||
| color: white; | |||
| padding: 2px; | |||
| font-size: 16px; | |||
| height: 20px; | |||
| font-family: monospace; | |||
| text-align: center; | |||
| pointer-events: auto; | |||
| cursor: pointer; | |||
| } | |||
| .GeometryUVPreviewPluginTargetHeader::after{ | |||
| content: '-'; | |||
| position: absolute; | |||
| right: 2px; | |||
| width: 20px; | |||
| height: 20px; | |||
| line-height: 16px; | |||
| } | |||
| .GeometryUVPreviewPluginCollapsed .GeometryUVPreviewPluginTargetHeader::after{ | |||
| content: '+'; | |||
| line-height: 20px; | |||
| } | |||
| @@ -0,0 +1,166 @@ | |||
| import {AViewerPluginSync, ThreeViewer} from '../../viewer' | |||
| import {createDiv, createStyles, getOrCall, onChange, ValOrFunc} from 'ts-browser-helpers' | |||
| import styles from './GeometryUVPreviewPlugin.css' | |||
| import {CustomContextMenu} from '../../utils' | |||
| import {uiFolderContainer, uiToggle} from 'uiconfig.js' | |||
| import {IGeometry} from '../../core' | |||
| import {UVsDebug} from 'three/examples/jsm/utils/UVsDebug.js' | |||
| export interface TargetBlock { | |||
| target: ValOrFunc<IGeometry|undefined> | |||
| name: string | |||
| visible: boolean | |||
| div: HTMLDivElement | |||
| uvCanvas?: HTMLCanvasElement | |||
| } | |||
| @uiFolderContainer('Render Target Preview Plugin') | |||
| export class GeometryUVPreviewPlugin<TEvent extends string> extends AViewerPluginSync<TEvent> { | |||
| static readonly PluginType = 'GeometryUVPreviewPlugin' | |||
| @uiToggle('Enabled') | |||
| @onChange(GeometryUVPreviewPlugin.prototype.refreshUi) enabled = true | |||
| toJSON: any = null | |||
| mainDiv: HTMLDivElement = createDiv({id: 'GeometryUVPreviewPluginContainer', addToBody: false}) | |||
| stylesheet?: HTMLStyleElement | |||
| constructor(enabled = true) { | |||
| super() | |||
| this.enabled = enabled | |||
| } | |||
| targetBlocks: TargetBlock[] = [] | |||
| onAdded(viewer: ThreeViewer): void { | |||
| super.onAdded(viewer) | |||
| viewer.addEventListener('postRender', this._postRender) | |||
| this.stylesheet = createStyles(styles, viewer.container) | |||
| this.refreshUi() | |||
| } | |||
| onRemove(viewer: ThreeViewer): void { | |||
| viewer.removeEventListener('postRender', this._postRender) | |||
| this.stylesheet?.remove() | |||
| this.stylesheet = undefined | |||
| this.refreshUi() | |||
| super.onRemove(viewer) | |||
| } | |||
| private _postRender = () => { | |||
| if (!this._viewer) return | |||
| for (const target of this.targetBlocks) { | |||
| if (!target.visible) continue | |||
| const geo = getOrCall(target.target) | |||
| if (!geo?.attributes?.uv) { | |||
| // todo draw white or pink | |||
| continue | |||
| } | |||
| if (!target.uvCanvas) { | |||
| target.uvCanvas = UVsDebug(geo, 1024) | |||
| target.uvCanvas.style.width = '100%' | |||
| target.uvCanvas.style.height = '100%' | |||
| } | |||
| if (target.uvCanvas && target.uvCanvas.parentElement !== target.div) target.div.appendChild(target.uvCanvas) | |||
| // const rect = target.div.getBoundingClientRect() | |||
| // const canvasRect = this._viewer.canvas.getBoundingClientRect() | |||
| // rect.x = rect.x - canvasRect.x | |||
| // rect.y = canvasRect.height + canvasRect.y - rect.y - rect.height | |||
| // if (Array.isArray(tex)) { | |||
| // // todo support multi target | |||
| // this._viewer.console.warn('Multi target preview not supported yet') | |||
| // continue | |||
| // } | |||
| // const outputColorSpace = this._viewer.renderManager.webglRenderer.outputColorSpace | |||
| // if (!target.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace | |||
| // this._viewer.renderManager.blit(null, { | |||
| // source: tex, | |||
| // clear: !target.transparent, | |||
| // respectColorSpace: !target.originalColorSpace, | |||
| // viewport: new Vector4(rect.x, rect.y, rect.width, rect.height), | |||
| // }) | |||
| // this._viewer.renderManager.webglRenderer.outputColorSpace = outputColorSpace | |||
| } | |||
| } | |||
| addGeometry(target: ValOrFunc<IGeometry|undefined>, name: string, visible = true): this { | |||
| if (!target) return this | |||
| const div = document.createElement('div') | |||
| const targetDef: TargetBlock = {target, name, div, visible} | |||
| div.classList.add('GeometryUVPreviewPluginTarget') | |||
| if (!targetDef.visible) div.classList.add('GeometryUVPreviewPluginCollapsed') | |||
| const header = document.createElement('div') | |||
| header.classList.add('GeometryUVPreviewPluginTargetHeader') | |||
| header.innerText = name | |||
| header.onclick = () => { | |||
| targetDef.visible = !targetDef.visible | |||
| if (!targetDef.visible) div.classList.add('GeometryUVPreviewPluginCollapsed') | |||
| else div.classList.remove('GeometryUVPreviewPluginCollapsed') | |||
| this._viewer?.setDirty() | |||
| } | |||
| header.oncontextmenu = (e) => { | |||
| e.preventDefault() | |||
| e.stopPropagation() | |||
| CustomContextMenu.Create({ | |||
| 'Download': () => this.downloadGeometryUV(targetDef), | |||
| 'Remove': () => this.removeGeometry(target), | |||
| }, e.clientX, e.clientY) | |||
| } | |||
| div.appendChild(header) | |||
| this.mainDiv.appendChild(div) | |||
| this.targetBlocks.push(targetDef) | |||
| this.refreshUi() | |||
| return this | |||
| } | |||
| removeGeometry(target: ValOrFunc<IGeometry|undefined>): this { | |||
| const index = this.targetBlocks.findIndex(t => t.target === target) | |||
| if (index >= 0) { | |||
| const t = this.targetBlocks[index] | |||
| this.targetBlocks.splice(index, 1) | |||
| t.div.remove() | |||
| } | |||
| this.refreshUi() | |||
| return this | |||
| } | |||
| downloadGeometryUV(targetDef: TargetBlock): this { | |||
| if (!this._viewer) return this | |||
| if (!targetDef.uvCanvas) return this | |||
| const canvas = targetDef.uvCanvas | |||
| const url = canvas.toDataURL('image/png') | |||
| const link = document.createElement('a') | |||
| document.body.appendChild(link) | |||
| link.style.display = 'none' | |||
| link.href = url | |||
| link.download = 'renderTarget.' + 'png' | |||
| link.click() | |||
| document.body.removeChild(link) | |||
| URL.revokeObjectURL(url) | |||
| return this | |||
| } | |||
| refreshUi(): void { | |||
| if (!this.mainDiv) return | |||
| if (!this._viewer) { | |||
| if (this.mainDiv.parentElement) this.mainDiv.remove() | |||
| this.mainDiv.style.display = 'none' | |||
| this.mainDiv.style.zIndex = '1000' | |||
| return | |||
| } | |||
| if (!this.mainDiv.parentElement) this._viewer.container?.appendChild(this.mainDiv) | |||
| this.mainDiv.style.display = this.enabled ? 'flex' : 'none' | |||
| this.mainDiv.style.zIndex = parseInt(this._viewer.canvas.style.zIndex || '0') + 1 + '' | |||
| this._viewer?.setDirty() | |||
| } | |||
| dispose() { | |||
| for (const target of this.targetBlocks) { | |||
| this.removeGeometry(target.target) | |||
| } | |||
| super.dispose() | |||
| } | |||
| } | |||
| @@ -107,11 +107,12 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| this.dispatchEvent({type: 'animationLoop', deltaTime, time, renderer: this._renderer, xrFrame: frame}) | |||
| } | |||
| constructor({canvas, alpha = true, targetOptions}:IRenderManagerOptions) { | |||
| constructor({canvas, alpha = true, renderScale = 1, targetOptions}:IRenderManagerOptions) { | |||
| super() | |||
| this._animationLoop = this._animationLoop.bind(this) | |||
| // this._xrPreAnimationLoop = this._xrPreAnimationLoop.bind(this) | |||
| this._renderSize = new Vector2(canvas.clientWidth, canvas.clientHeight) | |||
| this._renderScale = renderScale | |||
| this._renderer = this._initWebGLRenderer(canvas, alpha) | |||
| this._context = this._renderer.getContext() | |||
| this._isWebGL2 = this._renderer.capabilities.isWebGL2 | |||
| @@ -346,6 +347,9 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| return this._context | |||
| } | |||
| /** | |||
| * Same as {@link renderer} | |||
| */ | |||
| get webglRenderer(): WebGLRenderer { | |||
| return this._renderer | |||
| } | |||
| @@ -369,7 +373,7 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| // region Utils | |||
| /** | |||
| * | |||
| * blit - blits a texture to the screen or another render target. | |||
| * @param destination - destination target, or screen if undefined or null | |||
| * @param source - source Texture | |||
| * @param viewport - viewport and scissor | |||
| @@ -518,6 +522,10 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| return string | |||
| } | |||
| /** | |||
| * Rend pixels from a render target into a new Uint8Array|Uint16Array|Float32Array buffer | |||
| * @param target | |||
| */ | |||
| renderTargetToBuffer(target: WebGLRenderTarget): Uint8Array|Uint16Array|Float32Array { | |||
| const buffer = | |||
| target.texture.type === HalfFloatType ? | |||
| @@ -529,6 +537,12 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| return buffer | |||
| } | |||
| /** | |||
| * Exports a render target to a blob. The type is automatically picked from exr to png based on the render target. | |||
| * @param target - render target to export | |||
| * @param mimeType - mime type to use. | |||
| * If auto (default), then it will be picked based on the render target type. | |||
| */ | |||
| exportRenderTarget(target: WebGLRenderTarget, mimeType = 'auto'): BlobExt { | |||
| const hdrFormats = ['image/x-exr'] | |||
| let hdr = target.texture.type === HalfFloatType || target.texture.type === FloatType | |||
| @@ -1,9 +1,23 @@ | |||
| import styles from './CustomContextMenu.css' | |||
| /** | |||
| * Represents a custom context menu that can be created and managed dynamically. | |||
| */ | |||
| export class CustomContextMenu { | |||
| /** | |||
| * The HTML element representing the context menu. | |||
| */ | |||
| public static Element: HTMLDivElement | undefined = undefined | |||
| /** | |||
| * Indicates whether the context menu has been initialized. | |||
| */ | |||
| private static _inited = false | |||
| /** | |||
| * Initializes the context menu by adding event listeners. | |||
| * This method should be called before creating a context menu. | |||
| */ | |||
| private static _initialize(): void { | |||
| this._inited = true | |||
| document.addEventListener('pointerdown', (e) => { | |||
| @@ -13,7 +27,23 @@ export class CustomContextMenu { | |||
| }) | |||
| } | |||
| public static Create(items: Record<string, () => void>, x: number, y: number, show = true, removeOnSelect = true): HTMLDivElement { | |||
| /** | |||
| * Creates a custom context menu with specified items and options. | |||
| * | |||
| * @param items - An object containing menu item labels and corresponding callback functions. | |||
| * @param x - The horizontal position of the context menu. | |||
| * @param y - The vertical position of the context menu. | |||
| * @param show - Indicates whether the context menu should be displayed immediately. | |||
| * @param removeOnSelect - Indicates whether the context menu should be removed after an item is selected. | |||
| * @returns The HTML element representing the created context menu. | |||
| */ | |||
| public static Create( | |||
| items: Record<string, () => void>, | |||
| x: number, | |||
| y: number, | |||
| show = true, | |||
| removeOnSelect = true | |||
| ): HTMLDivElement { | |||
| if (!this._inited) this._initialize() | |||
| if (this.Element) this.Remove() | |||
| @@ -40,6 +70,9 @@ export class CustomContextMenu { | |||
| return container | |||
| } | |||
| /** | |||
| * Removes the context menu from the DOM. | |||
| */ | |||
| public static Remove(): void { | |||
| this.Element?.remove() | |||
| this.Element = undefined | |||
| @@ -759,6 +759,7 @@ export function metaToResources(meta?: SerializationMetaType): Partial<Serializa | |||
| if (res._context) delete res._context | |||
| return res | |||
| } | |||
| export function metaFromResources(resources?: Partial<SerializationResourcesType>, viewer?: ThreeViewer): SerializationMetaType { | |||
| return { | |||
| ...resources, | |||
| @@ -1,5 +1,14 @@ | |||
| const warnEnabled = true | |||
| /** | |||
| * Replace a string in a shader function with added options to prepend, append, show warning when not found, and replace all occurrences. | |||
| * @param shader - shader code | |||
| * @param str - string to replace | |||
| * @param newStr - new string to replace with | |||
| * @param replaceAll - replace all occurrences | |||
| * @param prepend - prepend new string to old string | |||
| * @param append - append new string to old string | |||
| */ | |||
| export function shaderReplaceString(shader: string, str: string, newStr: string, { | |||
| replaceAll = false, | |||
| prepend = false, | |||
| @@ -105,7 +105,9 @@ export interface ThreeViewerOptions { | |||
| */ | |||
| msaa?: boolean, | |||
| /** | |||
| * Use RGBM HDR Pipeline | |||
| * Use Uint8 RGBM HDR Render Pipeline. | |||
| * Provides better performance with post-processing. | |||
| * RenderManager Uses Half-float if set to false. | |||
| */ | |||
| rgbm?: boolean | |||
| /** | |||
| @@ -113,6 +115,14 @@ export interface ThreeViewerOptions { | |||
| */ | |||
| zPrepass?: boolean | |||
| /* | |||
| * Render scale, 1 = full resolution, 0.5 = half resolution, 2 = double resolution. | |||
| * Same as pixelRatio in three.js | |||
| * Can be set to `window.devicePixelRatio` to render at device resolution in browsers. | |||
| * An optimal value is `Math.min(2, window.devicePixelRatio)` to prevent issues on mobile. | |||
| */ | |||
| renderScale?: number | |||
| debug?: boolean | |||
| /** | |||
| @@ -173,62 +183,58 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| } | |||
| static Dialog: IDialogWrapper = windowDialogWrapper | |||
| renderStats: GLStatsJS | |||
| readonly assetManager: AssetManager | |||
| get console(): IConsoleWrapper { | |||
| return ThreeViewer.Console | |||
| } | |||
| get dialog(): IDialogWrapper { | |||
| return ThreeViewer.Dialog | |||
| } | |||
| @serialize() readonly type = 'ThreeViewer' | |||
| private readonly _canvas: HTMLCanvasElement | |||
| // this can be used by other plugins to add ui elements alongside the canvas | |||
| private readonly _container: HTMLElement // todo: add a way to move the canvas to a new container... and dispatch event... | |||
| @uiConfig() @serialize('renderManager') | |||
| readonly renderManager: ViewerRenderManager | |||
| /** | |||
| * The Scene attached to the viewer, this cannot be changed. | |||
| * @type {RootScene} | |||
| */ | |||
| @uiConfig() @serialize('scene') | |||
| private readonly _scene: RootScene | |||
| public readonly plugins: Record<string, IViewerPlugin> = {} | |||
| private _needsResize = false | |||
| /** | |||
| * If the viewer is enabled. Set this `false` to disable RAF loop. | |||
| * @type {boolean} | |||
| */ | |||
| enabled = true | |||
| /** | |||
| * Enable or disable all rendering, Animation loop including any frame/render events won't be fired when this is false. | |||
| */ | |||
| @onChange(ThreeViewer.prototype._renderEnabledChanged) | |||
| renderEnabled = true | |||
| private _isRenderingFrame = false | |||
| renderStats: GLStatsJS | |||
| readonly assetManager: AssetManager | |||
| @uiConfig() @serialize('renderManager') | |||
| readonly renderManager: ViewerRenderManager | |||
| public readonly plugins: Record<string, IViewerPlugin> = {} | |||
| /** | |||
| * Scene with object hierarchy used for rendering | |||
| */ | |||
| get scene(): RootScene { | |||
| return this._scene | |||
| } | |||
| /** | |||
| * Specifies how many frames to render in a single request animation frame. Keep to 1 for realtime rendering. | |||
| * Note: should be max (screen refresh rate / animation frame rate) like 60Hz / 30fps | |||
| * @type {number} | |||
| */ | |||
| public maxFramePerLoop = 1 | |||
| readonly debug: boolean | |||
| get scene(): RootScene { | |||
| return this._scene | |||
| /** | |||
| * Get the HTML Element containing the canvas | |||
| * @returns {HTMLElement} | |||
| */ | |||
| get container(): HTMLElement { | |||
| return this._container | |||
| } | |||
| /** | |||
| * Get the HTML Canvas Element where the viewer is rendering | |||
| * @returns {HTMLCanvasElement} | |||
| */ | |||
| get canvas(): HTMLCanvasElement { | |||
| return this._canvas | |||
| } | |||
| get console(): IConsoleWrapper { | |||
| return ThreeViewer.Console | |||
| } | |||
| get dialog(): IDialogWrapper { | |||
| return ThreeViewer.Dialog | |||
| } | |||
| @serialize() readonly type = 'ThreeViewer' | |||
| /** | |||
| * The ResizeObserver observing the canvas element. Add more elements to this observer to resize viewer on their size change. | |||
| @@ -236,13 +242,34 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| */ | |||
| readonly resizeObserver = window?.ResizeObserver ? new window.ResizeObserver(_ => this.resize()) : undefined | |||
| private readonly _canvas: HTMLCanvasElement | |||
| // this can be used by other plugins to add ui elements alongside the canvas | |||
| private readonly _container: HTMLElement // todo: add a way to move the canvas to a new container... and dispatch event... | |||
| /** | |||
| * The Scene attached to the viewer, this cannot be changed. | |||
| * @type {RootScene} | |||
| */ | |||
| @uiConfig() @serialize('scene') | |||
| private readonly _scene: RootScene | |||
| private _needsResize = false | |||
| private _isRenderingFrame = false | |||
| private _objectProcessor: IObjectProcessor = { | |||
| processObject: (object: IObject3D)=>{ | |||
| if (object.material) { | |||
| if (Array.isArray(object.material)) this.assetManager.materials.registerMaterials(object.material) | |||
| else this.assetManager.materials.registerMaterial(object.material) | |||
| } | |||
| }, | |||
| } | |||
| private _needsReset = true // renderer needs reset | |||
| // Helpers for tracking main camera change and setting dirty automatically | |||
| private _lastCameraPosition: Vector3 = new Vector3() | |||
| private _lastCameraQuat: Quaternion = new Quaternion() | |||
| private _lastCameraTarget: Vector3 = new Vector3() | |||
| private _tempVec: Vector3 = new Vector3() | |||
| private _tempQuat: Quaternion = new Quaternion() | |||
| readonly debug: boolean | |||
| /** | |||
| * Create a viewer instance for using the webgi viewer SDK. | |||
| * @param options - {@link ThreeViewerOptions} | |||
| @@ -333,6 +360,7 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| zPrepass: options.zPrepass ?? options.useGBufferDepth ?? false, | |||
| depthBuffer: !(options.zPrepass ?? options.useGBufferDepth ?? false), | |||
| screenShader: options.screenShader, | |||
| renderScale: options.renderScale, | |||
| }) | |||
| this.renderManager.addEventListener('animationLoop', this._animationLoop as any) | |||
| this.renderManager.addEventListener('resize', ()=> this._scene.mainCamera.refreshAspect()) | |||
| @@ -365,61 +393,6 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| } | |||
| private _objectProcessor: IObjectProcessor = { | |||
| processObject: (object: IObject3D)=>{ | |||
| if (object.material) { | |||
| if (Array.isArray(object.material)) this.assetManager.materials.registerMaterials(object.material) | |||
| else this.assetManager.materials.registerMaterial(object.material) | |||
| } | |||
| }, | |||
| } | |||
| // todo: find a better fix for context loss and restore? | |||
| private _lastSize = new Vector2() | |||
| private _onContextRestore = (_: Event) => { | |||
| this.enabled = true | |||
| this._canvas.width = this._lastSize.width | |||
| this._canvas.height = this._lastSize.height | |||
| this.resize() | |||
| this._scene.setDirty({refreshScene: true, frameFade: false}) | |||
| } | |||
| private _onContextLost = (_: Event) => { | |||
| this._lastSize.set(this._canvas.width, this._canvas.height) | |||
| this._canvas.width = 2 | |||
| this._canvas.height = 2 | |||
| this.resize() | |||
| this.enabled = false | |||
| } | |||
| /** | |||
| * Mark that the canvas is resized. If the size is changed, the renderer and all render targets are resized. This happens before the render of the next frame. | |||
| */ | |||
| resize = () => { | |||
| this._needsResize = true | |||
| this.setDirty() | |||
| } | |||
| private _needsReset = true // renderer reset | |||
| /** | |||
| * Set the viewer to dirty and trigger render of the next frame. | |||
| * @param source - The source of the dirty event. like plugin or 3d object | |||
| * @param event - The event that triggered the dirty event. | |||
| */ | |||
| setDirty(source?: any, event?: Event): void { | |||
| this._needsReset = true | |||
| source = source ?? this | |||
| this.dispatchEvent({...event ?? {}, type: 'update', source}) | |||
| } | |||
| /** | |||
| * The renderer for the viewer that's attached to the canvas. This is wrapper around WebGLRenderer and EffectComposer and manages post-processing passes and rendering logic | |||
| * @deprecated - use {@link renderManager} instead | |||
| */ | |||
| get renderer(): ViewerRenderManager { | |||
| this.console.error('renderer is deprecated, use renderManager instead') | |||
| return this.renderManager | |||
| } | |||
| /** | |||
| * Add an object/model/material/viewer-config/plugin-preset/... to the viewer scene from url or an {@link IAsset} object. | |||
| * Same as {@link AssetManager.addAssetSingle} | |||
| @@ -442,19 +415,6 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| return await this.assetManager.importer.importSingle<T>(obj, options) | |||
| } | |||
| /** | |||
| * Exports an object/mesh/material/texture/render-target/plugin-preset/viewer to a blob. | |||
| * If no object is given, a glb is exported with the current viewer state. | |||
| * @param obj | |||
| * @param options | |||
| */ | |||
| async export(obj?: IObject3D|IMaterial|ITexture|IRenderTarget|IViewerPlugin|(typeof this), options?: ExportFileOptions) { | |||
| if (!obj) obj = this._scene // this will export the glb with the scene and viewer config | |||
| if ((<typeof this>obj).type === this.type) return jsonToBlob((<typeof this>obj).exportConfig()) | |||
| if ((<IViewerPlugin>obj).constructor?.PluginType) return jsonToBlob(this.exportPluginConfig(<IViewerPlugin>obj)) | |||
| return await this.assetManager.exporter.exportObject(<IObject3D|IMaterial|ITexture|IRenderTarget>obj, options) | |||
| } | |||
| /** | |||
| * Set the environment map of the scene from url or an {@link IAsset} object. | |||
| * @param map | |||
| @@ -479,6 +439,47 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| return this._scene.background | |||
| } | |||
| /** | |||
| * Exports an object/mesh/material/texture/render-target/plugin-preset/viewer to a blob. | |||
| * If no object is given, a glb is exported with the current viewer state. | |||
| * @param obj | |||
| * @param options | |||
| */ | |||
| async export(obj?: IObject3D|IMaterial|ITexture|IRenderTarget|IViewerPlugin|(typeof this), options?: ExportFileOptions) { | |||
| if (!obj) obj = this._scene // this will export the glb with the scene and viewer config | |||
| if ((<typeof this>obj).type === this.type) return jsonToBlob((<typeof this>obj).exportConfig()) | |||
| if ((<IViewerPlugin>obj).constructor?.PluginType) return jsonToBlob(this.exportPluginConfig(<IViewerPlugin>obj)) | |||
| return await this.assetManager.exporter.exportObject(<IObject3D|IMaterial|ITexture|IRenderTarget>obj, options) | |||
| } | |||
| /** | |||
| * Export the scene to a file (default: glb with viewer config) and return a blob | |||
| * @param options | |||
| */ | |||
| async exportScene(options?: ExportFileOptions): Promise<BlobExt | undefined> { | |||
| return this.assetManager.exporter.exportObject(this._scene.modelRoot, options) | |||
| } | |||
| async getScreenshotBlob({mimeType = 'image/jpeg', quality = 90} = {}): Promise<Blob | null> { | |||
| const blobPromise = async()=> new Promise<Blob|null>((resolve) => { | |||
| this._canvas.toBlob((blob) => { | |||
| resolve(blob) | |||
| }, mimeType, quality) | |||
| }) | |||
| if (!this.renderEnabled) return blobPromise() | |||
| return await this.doOnce('postFrame', async() => { | |||
| this.renderEnabled = false | |||
| const blob = await blobPromise() | |||
| this.renderEnabled = true | |||
| return blob | |||
| }) | |||
| } | |||
| async getScreenshotDataUrl({mimeType = 'image/jpeg', quality = 90} = {}): Promise<string | null> { | |||
| if (!this.renderEnabled) return this._canvas.toDataURL(mimeType, quality) | |||
| return await this.doOnce('postFrame', () => this._canvas.toDataURL(mimeType, quality)) | |||
| } | |||
| /** | |||
| * Disposes the viewer and frees up all resource and events. Do not use the viewer after calling dispose. | |||
| * @note - If you want to reuse the viewer, set viewer.enabled to false instead, then set it to true again when required. To dispose all the objects, materials in the scene use `viewer.scene.disposeSceneModels()` | |||
| @@ -505,7 +506,26 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| this.dispatchEvent({type: 'dispose'}) | |||
| } | |||
| private _animationLoop(event: IAnimationLoopEvent): void { | |||
| /** | |||
| * Mark that the canvas is resized. If the size is changed, the renderer and all render targets are resized. This happens before the render of the next frame. | |||
| */ | |||
| resize = () => { | |||
| this._needsResize = true | |||
| this.setDirty() | |||
| } | |||
| /** | |||
| * Set the viewer to dirty and trigger render of the next frame. | |||
| * @param source - The source of the dirty event. like plugin or 3d object | |||
| * @param event - The event that triggered the dirty event. | |||
| */ | |||
| setDirty(source?: any, event?: Event): void { | |||
| this._needsReset = true | |||
| source = source ?? this | |||
| this.dispatchEvent({...event ?? {}, type: 'update', source}) | |||
| } | |||
| protected _animationLoop(event: IAnimationLoopEvent): void { | |||
| if (!this.enabled || !this.renderEnabled) return | |||
| if (this._isRenderingFrame) { | |||
| this.console.warn('animation loop: frame skip') // not possible actually, since this is not async | |||
| @@ -590,23 +610,6 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| } | |||
| /** | |||
| * Get the HTML Element containing the canvas | |||
| * @returns {HTMLElement} | |||
| */ | |||
| get container(): HTMLElement { | |||
| return this._container | |||
| } | |||
| /** | |||
| * Get the HTML Canvas Element where the viewer is rendering | |||
| * @returns {HTMLCanvasElement} | |||
| */ | |||
| get canvas(): HTMLCanvasElement { | |||
| return this._canvas | |||
| } | |||
| /** | |||
| * Get the Plugin by a constructor type or by the string type. | |||
| * Use string type if the plugin is not a dependency and you don't want to bundle the plugin. | |||
| @@ -618,21 +621,21 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| } | |||
| /** | |||
| * Get the Plugin by the string type. | |||
| * @deprecated - Use {@link getPlugin} instead. | |||
| * Get the Plugin by a constructor type or add a new plugin of the specified type if it doesn't exist. | |||
| * @param type | |||
| * @returns {T | undefined} | |||
| * @param args - arguments for the constructor of the plugin, used when a new plugin is created. | |||
| */ | |||
| getPluginByType<T extends IViewerPlugin>(type: string): T | undefined { | |||
| return this.plugins[type] as T | undefined | |||
| } | |||
| async getOrAddPlugin<T extends IViewerPlugin>(type: Class<T>, ...args: ConstructorParameters<Class<T>>): Promise<T> { | |||
| const plugin = this.getPlugin(type) | |||
| if (plugin) return plugin | |||
| return this.addPlugin(type, ...args) | |||
| } | |||
| /** | |||
| * Get the Plugin by a constructor type or add a new plugin to the viewer of the specified type if it doesn't exist(sync). | |||
| * @param type | |||
| * @param args - arguments for the constructor of the plugin, used when a new plugin is created. | |||
| */ | |||
| getOrAddPluginSync<T extends IViewerPluginSync>(type: Class<T>, ...args: ConstructorParameters<Class<T>>): T { | |||
| const plugin = this.getPlugin(type) | |||
| if (plugin) return plugin | |||
| @@ -695,10 +698,18 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| return p | |||
| } | |||
| /** | |||
| * Add multiple plugins to the viewer. | |||
| * @param plugins - List of plugin instances or classes | |||
| */ | |||
| async addPlugins(plugins: (IViewerPlugin | Class<IViewerPlugin>)[]): Promise<void> { | |||
| for (const p of plugins) await this.addPlugin(p) | |||
| } | |||
| /** | |||
| * Add multiple plugins to the viewer(sync). | |||
| * @param plugins - List of plugin instances or classes | |||
| */ | |||
| async addPluginsSync(plugins: (IViewerPluginSync | Class<IViewerPluginSync>)[]): Promise<void> { | |||
| for (const p of plugins) this.addPluginSync(p) | |||
| } | |||
| @@ -718,6 +729,11 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| this.setDirty(p) | |||
| } | |||
| /** | |||
| * Remove a plugin instance or a plugin class(sync). Works similar to {@link ThreeViewer.addPluginSync} | |||
| * @param p | |||
| * @param dispose | |||
| */ | |||
| removePluginSync(p: IViewerPluginSync, dispose = true): void { | |||
| const type = p.constructor.PluginType | |||
| if (!this.plugins[type]) return | |||
| @@ -740,32 +756,6 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| this.resize() | |||
| } | |||
| // private _addSceneObject = (e: IEvent<any>) => { | |||
| // if (!e || !e.object) return | |||
| // const config = e.object.__importedViewerConfig // this is set in gltf.ts when gltf file is imported. This is done here so that scene settings are applied whenever the imported object is added to scene. | |||
| // if (!config) return | |||
| // this.fromJSON(config, config.resources) | |||
| // } | |||
| /** | |||
| * @deprecated use {@link assetManager} instead. | |||
| * Gets the Asset manager, contains useful functions for managing, loading and inserting assets. | |||
| */ | |||
| getManager(): AssetManager|undefined { | |||
| return this.assetManager | |||
| } | |||
| // todo | |||
| // public async fitToView(selected?: Object3D, distanceMultiplier = 1.5, duration?: number, ease?: Easing|EasingFunctionType) { | |||
| // const camViews = this.getPluginByType<CameraViewPlugin>('CameraViews') | |||
| // if (!camViews) { | |||
| // this.console.error('CameraViews plugin is required for fitToView to work') | |||
| // return | |||
| // } | |||
| // await camViews?.animateToFitObject(selected, distanceMultiplier, duration, ease, {min: (this.scene.activeCamera.getControls<OrbitControls3>()?.minDistance ?? 0.5) + 0.5, max: 1000.0}) | |||
| // } | |||
| /** | |||
| * Traverse all objects in scene model root. | |||
| * @param callback | |||
| @@ -774,7 +764,23 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| this._scene.modelRoot.traverse(callback) | |||
| } | |||
| // todo: create/load texture utils | |||
| /** | |||
| * Add an object to the scene model root. | |||
| * If an imported scene model root is passed, it will be loaded with viewer configuration, unless importConfig is false | |||
| * @param imported | |||
| * @param options | |||
| */ | |||
| async addSceneObject<T extends IObject3D|Object3D|RootSceneImportResult = RootSceneImportResult>(imported: T, options?: AddObjectOptions): Promise<T> { | |||
| if (imported.userData?.rootSceneModelRoot) { | |||
| const obj = <RootSceneImportResult>imported | |||
| if (obj.importedViewerConfig && options?.importConfig !== false) await this.importConfig(obj.importedViewerConfig) | |||
| this._scene.loadModelRoot(obj, options) | |||
| return this._scene.modelRoot as T | |||
| } | |||
| this._scene.addObject(imported, options) | |||
| return imported | |||
| } | |||
| /** | |||
| * Serialize all the plugins and their settings to save or create presets. Used in {@link toJSON}. | |||
| @@ -913,7 +919,6 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| return data | |||
| } | |||
| /** | |||
| * Deserialize all the viewer and plugin settings. | |||
| * @note use async {@link ThreeViewer.importConfig} to import a json/config exported with {@link ThreeViewer.exportConfig} or {@link ThreeViewer.toJSON}. | |||
| @@ -1004,15 +1009,14 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| return await MetaImporter.ImportMeta(meta, extraResources) | |||
| } | |||
| async addSceneObject<T extends IObject3D|Object3D|RootSceneImportResult = RootSceneImportResult>(imported: T, options?: AddObjectOptions): Promise<T> { | |||
| if (imported.userData?.rootSceneModelRoot) { | |||
| const obj = <RootSceneImportResult>imported | |||
| if (obj.importedViewerConfig && options?.importConfig !== false) await this.importConfig(obj.importedViewerConfig) | |||
| this._scene.loadModelRoot(obj, options) | |||
| return this._scene.modelRoot as T | |||
| } | |||
| this._scene.addObject(imported, options) | |||
| return imported | |||
| async doOnce<TRet>(event: IViewerEventTypes, func: (...args: any[]) => TRet): Promise<TRet> { | |||
| return new Promise((resolve) => { | |||
| const listener = async(...args: any[]) => { | |||
| this.removeEventListener(event, listener) | |||
| resolve(await func(...args)) | |||
| } | |||
| this.addEventListener(event, listener) | |||
| }) | |||
| } | |||
| private _setActiveCameraView(event: any = {}): void { | |||
| @@ -1048,45 +1052,6 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| return p | |||
| } | |||
| async doOnce<TRet>(event: IViewerEventTypes, func: (...args: any[]) => TRet): Promise<TRet> { | |||
| return new Promise((resolve) => { | |||
| const listener = async(...args: any[]) => { | |||
| this.removeEventListener(event, listener) | |||
| resolve(await func(...args)) | |||
| } | |||
| this.addEventListener(event, listener) | |||
| }) | |||
| } | |||
| /** | |||
| * Export the scene to a file (default: glb with viewer config) and return a blob | |||
| * @param options | |||
| */ | |||
| async exportScene(options?: ExportFileOptions): Promise<BlobExt | undefined> { | |||
| return this.assetManager.exporter.exportObject(this._scene.modelRoot, options) | |||
| } | |||
| async getScreenshotBlob({mimeType = 'image/jpeg', quality = 90} = {}): Promise<Blob | null> { | |||
| const blobPromise = async()=> new Promise<Blob|null>((resolve) => { | |||
| this._canvas.toBlob((blob) => { | |||
| resolve(blob) | |||
| }, mimeType, quality) | |||
| }) | |||
| if (!this.renderEnabled) return blobPromise() | |||
| return await this.doOnce('postFrame', async() => { | |||
| this.renderEnabled = false | |||
| const blob = await blobPromise() | |||
| this.renderEnabled = true | |||
| return blob | |||
| }) | |||
| } | |||
| async getScreenshotDataUrl({mimeType = 'image/jpeg', quality = 90} = {}): Promise<string | null> { | |||
| if (!this.renderEnabled) return this._canvas.toDataURL(mimeType, quality) | |||
| return await this.doOnce('postFrame', () => this._canvas.toDataURL(mimeType, quality)) | |||
| } | |||
| private _renderEnabledChanged(): void { | |||
| this.dispatchEvent({type: this.renderEnabled ? 'renderEnabled' : 'renderDisabled'}) | |||
| } | |||
| @@ -1102,6 +1067,42 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| plugins: [], | |||
| } | |||
| // todo: find a better fix for context loss and restore? | |||
| private _lastSize = new Vector2() | |||
| private _onContextRestore = (_: Event) => { | |||
| this.enabled = true | |||
| this._canvas.width = this._lastSize.width | |||
| this._canvas.height = this._lastSize.height | |||
| this.resize() | |||
| this._scene.setDirty({refreshScene: true, frameFade: false}) | |||
| } | |||
| private _onContextLost = (_: Event) => { | |||
| this._lastSize.set(this._canvas.width, this._canvas.height) | |||
| this._canvas.width = 2 | |||
| this._canvas.height = 2 | |||
| this.resize() | |||
| this.enabled = false | |||
| } | |||
| // private _addSceneObject = (e: IEvent<any>) => { | |||
| // if (!e || !e.object) return | |||
| // const config = e.object.__importedViewerConfig // this is set in gltf.ts when gltf file is imported. This is done here so that scene settings are applied whenever the imported object is added to scene. | |||
| // if (!config) return | |||
| // this.fromJSON(config, config.resources) | |||
| // } | |||
| // todo | |||
| // public async fitToView(selected?: Object3D, distanceMultiplier = 1.5, duration?: number, ease?: Easing|EasingFunctionType) { | |||
| // const camViews = this.getPluginByType<CameraViewPlugin>('CameraViews') | |||
| // if (!camViews) { | |||
| // this.console.error('CameraViews plugin is required for fitToView to work') | |||
| // return | |||
| // } | |||
| // await camViews?.animateToFitObject(selected, distanceMultiplier, duration, ease, {min: (this.scene.activeCamera.getControls<OrbitControls3>()?.minDistance ?? 0.5) + 0.5, max: 1000.0}) | |||
| // } | |||
| // todo: create/load texture utils | |||
| // region legacy creation functions | |||
| // /** | |||
| @@ -1154,4 +1155,31 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| // endregion | |||
| /** | |||
| * The renderer for the viewer that's attached to the canvas. This is wrapper around WebGLRenderer and EffectComposer and manages post-processing passes and rendering logic | |||
| * @deprecated - use {@link renderManager} instead | |||
| */ | |||
| get renderer(): ViewerRenderManager { | |||
| this.console.error('renderer is deprecated, use renderManager instead') | |||
| return this.renderManager | |||
| } | |||
| /** | |||
| * @deprecated use {@link assetManager} instead. | |||
| * Gets the Asset manager, contains useful functions for managing, loading and inserting assets. | |||
| */ | |||
| getManager(): AssetManager|undefined { | |||
| return this.assetManager | |||
| } | |||
| /** | |||
| * Get the Plugin by the string type. | |||
| * @deprecated - Use {@link getPlugin} instead. | |||
| * @param type | |||
| * @returns {T | undefined} | |||
| */ | |||
| getPluginByType<T extends IViewerPlugin>(type: string): T | undefined { | |||
| return this.plugins[type] as T | undefined | |||
| } | |||
| } | |||
| @@ -1 +1 @@ | |||
| export const VERSION = '0.0.12' | |||
| export const VERSION = '0.0.14' | |||