| const exampleStyle = document.querySelector('#example-style') | const exampleStyle = document.querySelector('#example-style') | ||||
| const css = exampleStyle ? exampleStyle.textContent : '' | const css = exampleStyle ? exampleStyle.textContent : '' | ||||
| const importMap = document.querySelector('script[type="importmap"]') | const importMap = document.querySelector('script[type="importmap"]') | ||||
| const imports = JSON.parse(importMap.textContent||'{}').imports||{} | |||||
| const imports = importMap ? JSON.parse(importMap.textContent||'{}').imports||{} : {} | |||||
| Object.entries(imports).forEach(([k,v])=>imports[k] = v.replace(/^\.\/\.\.\/\.\.\//, rootPath)) // ./../../ -> rootPath | Object.entries(imports).forEach(([k,v])=>imports[k] = v.replace(/^\.\/\.\.\/\.\.\//, rootPath)) // ./../../ -> rootPath | ||||
| function replaceImports(code) { | function replaceImports(code) { | ||||
| for (const [name, link] of Object.entries(imports)) code = code.replaceAll(` from '${name}'`, ` from '${link}'`) | for (const [name, link] of Object.entries(imports)) code = code.replaceAll(` from '${name}'`, ` from '${link}'`) | ||||
| .replaceAll(` from '../`, ` from '${rootPath+examplePath}`) | .replaceAll(` from '../`, ` from '${rootPath+examplePath}`) | ||||
| } | } | ||||
| setupCodePreview( | setupCodePreview( | ||||
| document.getElementById('canvas-container') || document.querySelector('.code-preview-container'), | |||||
| document.getElementById('canvas-container') || document.querySelector('.code-preview-container') || document.body, | |||||
| scripts, | scripts, | ||||
| scripts.map(s=>s.textContent ? 'js' : s.split('.').pop()), // title | scripts.map(s=>s.textContent ? 'js' : s.split('.').pop()), // title | ||||
| scripts.map(s=>(typeof s === 'string' && s.endsWith('.js')) ? s : (codePath+examplePath+window.location.pathname.split('/examples/').pop().replace('index.html', '')+(s.textContent ? 'index.html' : s))), // todo: github link | scripts.map(s=>(typeof s === 'string' && s.endsWith('.js')) ? s : (codePath+examplePath+window.location.pathname.split('/examples/').pop().replace('index.html', '')+(s.textContent ? 'index.html' : s))), // todo: github link |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>Threepipe HTML/JS Sample</title> | |||||
| <style> | |||||
| html, body{ | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| margin: 0; | |||||
| overflow: hidden; | |||||
| } | |||||
| </style> | |||||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||||
| </head> | |||||
| <body> | |||||
| <canvas id="three-canvas" style="width: 800px; height: 600px;"></canvas> | |||||
| <script id="example-script" type="module" data-scripts="./index.html"> | |||||
| import {ThreeViewer} from 'https://threepipe.org/dist/index.mjs' | |||||
| const viewer = new ThreeViewer({canvas: document.getElementById('three-canvas')}) | |||||
| // Load an environment map | |||||
| const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||||
| const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', { | |||||
| autoCenter: true, | |||||
| autoScale: true, | |||||
| }) | |||||
| Promise.all([envPromise, modelPromise]).then(([env, model])=>{ | |||||
| console.log('Loaded', model, env, viewer) | |||||
| }) | |||||
| </script> | |||||
| </body> |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>Image Load</title> | |||||
| <!-- 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; | |||||
| } | |||||
| 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 .png, .jpeg, .jpg, .svg, .ico, .avif, .webp files here</p> | |||||
| <canvas id="mcanvas"></canvas> | |||||
| </div> | |||||
| </body> |
| import {_testFinish, ITexture, Mesh, PlaneGeometry, SRGBColorSpace, ThreeViewer, UnlitMaterial} from 'threepipe' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: true, | |||||
| dropzone: { | |||||
| allowedExtensions: ['png', 'jpg', 'jpeg', 'svg', 'webp', 'avif', 'ico'], | |||||
| addOptions: { | |||||
| disposeSceneObjects: false, | |||||
| autoSetEnvironment: false, // when hdr is dropped | |||||
| autoSetBackground: false, | |||||
| }, | |||||
| }, | |||||
| }) | |||||
| viewer.scene.setBackgroundColor('#555555') | |||||
| const urls = [ | |||||
| 'https://threejs.org/examples/textures/sprite0.png', | |||||
| 'https://threejs.org/examples/textures/uv_grid_opengl.jpg', | |||||
| 'https://threejs.org/examples/models/svg/style-css-inside-defs.svg', | |||||
| 'https://threejs.org/examples/textures/tiltbrush/Light.webp', | |||||
| // todo: avif | |||||
| 'https://threejs.org/favicon.ico', | |||||
| ] | |||||
| const geometry = new PlaneGeometry(1, 1) | |||||
| let i = 0 | |||||
| for (const url of urls) { | |||||
| // Load the url as a Texture | |||||
| const texture = await viewer.load<ITexture>(url) | |||||
| if (!texture) continue | |||||
| texture.colorSpace = SRGBColorSpace | |||||
| const material = new UnlitMaterial({ | |||||
| map: texture, | |||||
| transparent: true, | |||||
| }) | |||||
| const plane = new Mesh(geometry, material) | |||||
| plane.position.set(i % 3 - 1, -Math.floor(i / 3) + 1, 0) | |||||
| viewer.scene.addObject(plane) | |||||
| i++ | |||||
| } | |||||
| // Listen to when a file is dropped | |||||
| viewer.assetManager.addEventListener('loadAsset', (e)=>{ | |||||
| if (!e.data.isTexture) return | |||||
| const texture = e.data as ITexture | |||||
| texture.colorSpace = SRGBColorSpace | |||||
| const material = new UnlitMaterial({ | |||||
| map: texture, | |||||
| transparent: true, | |||||
| }) | |||||
| const plane = new Mesh(geometry, material) | |||||
| plane.position.set(i % 3 - 1, -Math.floor(i / 3) + 1, 0) | |||||
| viewer.scene.addObject(plane) | |||||
| i++ | |||||
| }) | |||||
| } | |||||
| init().then(_testFinish) |
| <li><a href="./drc-load/">DRACO(DRC) Load </a></li> | <li><a href="./drc-load/">DRACO(DRC) Load </a></li> | ||||
| <li><a href="./hdr-load/">HDR Load </a></li> | <li><a href="./hdr-load/">HDR Load </a></li> | ||||
| <li><a href="./exr-load/">EXR 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="./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> | |||||
| </ul> | </ul> | ||||
| <h2 class="category">Export</h2> | <h2 class="category">Export</h2> | ||||
| <ul> | <ul> | ||||
| <li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li> | <li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li> | ||||
| <li><a href="./hdr-to-exr/">Convert HDR to EXR </a></li> | <li><a href="./hdr-to-exr/">Convert HDR to EXR </a></li> | ||||
| </ul> | </ul> | ||||
| <h2 class="category">Samples</h2> | |||||
| <ul> | |||||
| <li><a href="./html-sample/">HTML/JS Sample </a></li> | |||||
| </ul> | |||||
| <h2 class="category">Tests</h2> | <h2 class="category">Tests</h2> | ||||
| <ul> | <ul> | ||||
| <li><a href="./gltf-transmission-test/">GLTF Transmission Test </a></li> | <li><a href="./gltf-transmission-test/">GLTF Transmission Test </a></li> |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>KTX Texture Load</title> | |||||
| <!-- 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; | |||||
| } | |||||
| 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 .ktx files here</p> | |||||
| <canvas id="mcanvas"></canvas> | |||||
| </div> | |||||
| </body> |
| import { | |||||
| _testFinish, | |||||
| ITexture, | |||||
| KTXLoadPlugin, | |||||
| Mesh, | |||||
| PlaneGeometry, | |||||
| SRGBColorSpace, | |||||
| ThreeViewer, | |||||
| UnlitMaterial, | |||||
| } from 'threepipe' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: true, | |||||
| dropzone: { | |||||
| allowedExtensions: ['ktx'], | |||||
| }, | |||||
| }) | |||||
| viewer.addPluginSync(KTXLoadPlugin) | |||||
| viewer.scene.setBackgroundColor('#555555') | |||||
| const urls = [] | |||||
| // Checking which ktx formats are supported by the browser | |||||
| const formats = { | |||||
| astc: viewer.renderManager.renderer.extensions.has('WEBGL_compressed_texture_astc'), | |||||
| etc1: viewer.renderManager.renderer.extensions.has('WEBGL_compressed_texture_etc1'), | |||||
| s3tc: viewer.renderManager.renderer.extensions.has('WEBGL_compressed_texture_s3tc'), | |||||
| pvrtc: viewer.renderManager.renderer.extensions.has('WEBGL_compressed_texture_pvrtc'), | |||||
| } | |||||
| if (formats.pvrtc) urls.push( | |||||
| 'https://threejs.org/examples/textures/compressed/disturb_PVR2bpp.ktx', | |||||
| 'https://threejs.org/examples/textures/compressed/lensflare_PVR4bpp.ktx' | |||||
| ) | |||||
| if (formats.s3tc) urls.push( | |||||
| 'https://threejs.org/examples/textures/compressed/disturb_BC1.ktx', | |||||
| 'https://threejs.org/examples/textures/compressed/lensflare_BC3.ktx' | |||||
| ) | |||||
| if (formats.etc1) urls.push( | |||||
| 'https://threejs.org/examples/textures/compressed/disturb_ETC1.ktx' | |||||
| ) | |||||
| if (formats.astc) urls.push( | |||||
| 'https://threejs.org/examples/textures/compressed/disturb_ASTC4x4.ktx', | |||||
| 'https://threejs.org/examples/textures/compressed/lensflare_ASTC8x8.ktx' | |||||
| ) | |||||
| const geometry = new PlaneGeometry(1, 1) | |||||
| let i = 0 | |||||
| for (const url of urls) { | |||||
| // Load the url as a Texture | |||||
| const texture = await viewer.load<ITexture>(url) | |||||
| if (!texture) continue | |||||
| texture.colorSpace = SRGBColorSpace | |||||
| const material = new UnlitMaterial({ | |||||
| map: texture, | |||||
| transparent: true, | |||||
| }) | |||||
| const plane = new Mesh(geometry, material) | |||||
| plane.position.set(i % 3 - 1, -Math.floor(i / 3) + 1, 0) | |||||
| viewer.scene.addObject(plane) | |||||
| i++ | |||||
| } | |||||
| // Listen to when a file is dropped | |||||
| viewer.assetManager.addEventListener('loadAsset', (e)=>{ | |||||
| if (!e.data.isTexture) return | |||||
| const texture = e.data as ITexture | |||||
| texture.colorSpace = SRGBColorSpace | |||||
| const material = new UnlitMaterial({ | |||||
| map: texture, | |||||
| transparent: true, | |||||
| }) | |||||
| const plane = new Mesh(geometry, material) | |||||
| plane.position.set(i % 3 - 1, -Math.floor(i / 3) + 1, 0) | |||||
| viewer.scene.addObject(plane) | |||||
| i++ | |||||
| }) | |||||
| } | |||||
| init().then(_testFinish) |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>KTX2 Texture Load</title> | |||||
| <!-- 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; | |||||
| } | |||||
| 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 .ktx2 files here</p> | |||||
| <canvas id="mcanvas"></canvas> | |||||
| </div> | |||||
| </body> |
| import { | |||||
| _testFinish, | |||||
| ITexture, | |||||
| KTX2LoadPlugin, | |||||
| Mesh, | |||||
| PlaneGeometry, | |||||
| SRGBColorSpace, | |||||
| ThreeViewer, | |||||
| UnlitMaterial, | |||||
| } from 'threepipe' | |||||
| import {BufferGeometry} from 'three' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: true, | |||||
| dropzone: { | |||||
| allowedExtensions: ['ktx2'], | |||||
| }, | |||||
| }) | |||||
| viewer.addPluginSync(KTX2LoadPlugin) | |||||
| viewer.scene.setBackgroundColor('#555555') | |||||
| const urls = [ | |||||
| 'https://threejs.org/examples/textures/compressed/sample_etc1s.ktx2', | |||||
| 'https://threejs.org/examples/textures/compressed/sample_uastc.ktx2', | |||||
| 'https://threejs.org/examples/textures/compressed/sample_uastc_zstd.ktx2', | |||||
| ] | |||||
| // PlaneGeometry UVs assume flipY=true, which compressed textures don't support. | |||||
| const geometry = flipY(new PlaneGeometry(1, 1)) | |||||
| let i = 0 | |||||
| for (const url of urls) { | |||||
| // Load the url as a Texture | |||||
| const texture = await viewer.load<ITexture>(url) | |||||
| if (!texture) continue | |||||
| texture.colorSpace = SRGBColorSpace | |||||
| const material = new UnlitMaterial({ | |||||
| map: texture, | |||||
| transparent: true, | |||||
| }) | |||||
| const plane = new Mesh(geometry, material) | |||||
| plane.position.set(i % 3 - 1, -Math.floor(i / 3) + 1, 0) | |||||
| viewer.scene.addObject(plane) | |||||
| i++ | |||||
| } | |||||
| // Listen to when a file is dropped | |||||
| viewer.assetManager.addEventListener('loadAsset', (e)=>{ | |||||
| if (!e.data.isTexture) return | |||||
| const texture = e.data as ITexture | |||||
| texture.colorSpace = SRGBColorSpace | |||||
| const material = new UnlitMaterial({ | |||||
| map: texture, | |||||
| transparent: true, | |||||
| }) | |||||
| const plane = new Mesh(geometry, material) | |||||
| plane.position.set(i % 3 - 1, -Math.floor(i / 3) + 1, 0) | |||||
| viewer.scene.addObject(plane) | |||||
| i++ | |||||
| }) | |||||
| } | |||||
| init().then(_testFinish) | |||||
| /** Correct UVs to be compatible with `flipY=false` textures. */ | |||||
| function flipY(geometry: BufferGeometry) { | |||||
| const uv = geometry.attributes.uv | |||||
| for (let i = 0; i < uv.count; i++) { | |||||
| uv.setY(i, 1 - uv.getY(i)) | |||||
| } | |||||
| return geometry | |||||
| } |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>PLY Load</title> | |||||
| <!-- 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> |
| import {_testFinish, IObject3D, PLYLoadPlugin, ThreeViewer} from 'threepipe' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: true, | |||||
| dropzone: { | |||||
| allowedExtensions: ['ply', 'hdr'], | |||||
| addOptions: { | |||||
| disposeSceneObjects: true, | |||||
| autoSetEnvironment: true, // when hdr is dropped | |||||
| autoSetBackground: true, | |||||
| }, | |||||
| }, | |||||
| }) | |||||
| viewer.addPluginSync(PLYLoadPlugin) | |||||
| 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/ply/ascii/dolphins_colored.ply', options), | |||||
| viewer.load<IObject3D>('https://threejs.org/examples/models/ply/binary/Lucy100k.ply', options), | |||||
| ]) | |||||
| } | |||||
| init().then(_testFinish) |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>STL Load</title> | |||||
| <!-- 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> |
| import {_testFinish, IObject3D, STLLoadPlugin, ThreeViewer} from 'threepipe' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: true, | |||||
| dropzone: { | |||||
| allowedExtensions: ['stl', 'hdr'], | |||||
| addOptions: { | |||||
| disposeSceneObjects: true, | |||||
| autoSetEnvironment: true, // when hdr is dropped | |||||
| autoSetBackground: true, | |||||
| }, | |||||
| }, | |||||
| }) | |||||
| viewer.addPluginSync(STLLoadPlugin) | |||||
| const options = { | |||||
| autoCenter: true, | |||||
| autoScale: true, | |||||
| } | |||||
| const res = await Promise.all([ | |||||
| viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr'), | |||||
| viewer.load<IObject3D>('https://threejs.org/examples/models/stl/ascii/slotted_disk.stl', options), | |||||
| viewer.load<IObject3D>('https://threejs.org/examples/models/stl/binary/pr2_head_pan.stl', options), | |||||
| ]) | |||||
| console.log(res) | |||||
| } | |||||
| init().then(_testFinish) |
| DropzonePlugin, | DropzonePlugin, | ||||
| FullScreenPlugin, | FullScreenPlugin, | ||||
| HalfFloatType, | HalfFloatType, | ||||
| IObject3D, | |||||
| KTX2LoadPlugin, | |||||
| KTXLoadPlugin, | |||||
| NormalBufferPlugin, | NormalBufferPlugin, | ||||
| PLYLoadPlugin, | |||||
| RenderTargetPreviewPlugin, | RenderTargetPreviewPlugin, | ||||
| Rhino3dmLoadPlugin, | |||||
| SceneUiConfigPlugin, | SceneUiConfigPlugin, | ||||
| STLLoadPlugin, | |||||
| ThreeViewer, | ThreeViewer, | ||||
| TonemapPlugin, | TonemapPlugin, | ||||
| ViewerUiConfigPlugin, | ViewerUiConfigPlugin, | ||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | ||||
| msaa: true, | msaa: true, | ||||
| rgbm: true, | rgbm: true, | ||||
| zPrepass: false, // set it to true if you only have opaque objects in the scene to get better performance. | |||||
| dropzone: { | dropzone: { | ||||
| addOptions: { | addOptions: { | ||||
| clearSceneObjects: false, // clear the scene before adding new objects on drop. | clearSceneObjects: false, // clear the scene before adding new objects on drop. | ||||
| new DepthBufferPlugin(HalfFloatType, true, true), | new DepthBufferPlugin(HalfFloatType, true, true), | ||||
| new NormalBufferPlugin(HalfFloatType, false), | new NormalBufferPlugin(HalfFloatType, false), | ||||
| new RenderTargetPreviewPlugin(false), | new RenderTargetPreviewPlugin(false), | ||||
| new KTX2LoadPlugin(), | |||||
| new KTXLoadPlugin(), | |||||
| new PLYLoadPlugin(), | |||||
| new Rhino3dmLoadPlugin(), | |||||
| new STLLoadPlugin(), | |||||
| ]) | ]) | ||||
| const rt = viewer.getOrAddPluginSync(RenderTargetPreviewPlugin) | const rt = viewer.getOrAddPluginSync(RenderTargetPreviewPlugin) | ||||
| ['Debug']: [RenderTargetPreviewPlugin], | ['Debug']: [RenderTargetPreviewPlugin], | ||||
| }) | }) | ||||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr', { | |||||
| setBackground: true, | |||||
| }) | |||||
| await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', { | |||||
| autoCenter: true, | |||||
| autoScale: true, | |||||
| }) | |||||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||||
| // await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', { | |||||
| // autoCenter: true, | |||||
| // autoScale: true, | |||||
| // }) | |||||
| // const model = result?.getObjectByName('node_damagedHelmet_-6514') | // const model = result?.getObjectByName('node_damagedHelmet_-6514') | ||||
| // const config = model?.uiConfig | // const config = model?.uiConfig |
| export interface ILoader<T = any, T2 = any> extends Loader, Partial<IDisposable> { | export interface ILoader<T = any, T2 = any> extends Loader, Partial<IDisposable> { | ||||
| loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<any>; | loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<any>; | ||||
| /** | /** | ||||
| * Transform after load, like convert geometry to mesh, etc. for reference see {@link DRACOLoader2} | |||||
| * Transform after load, like convert geometry to mesh, etc. for reference see {@link DRACOLoader2} or {@link PLYLoadPlugin} | |||||
| * @param res - result of load | * @param res - result of load | ||||
| * @param options | * @param options | ||||
| */ | */ |
| import {IViewerPluginSync, 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' | |||||
| /** | |||||
| * 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 | |||||
| public static readonly PluginType = 'KTX2LoadPlugin' | |||||
| private _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/' | |||||
| onAdded(viewer: ThreeViewer) { | |||||
| this._importer.onCtor = (l: KTX2Loader2) => l | |||||
| .setTranscoderPath(KTX2LoadPlugin.TRANSCODER_LIBRARY_PATH) | |||||
| .detectSupport(viewer.renderManager.renderer) | |||||
| viewer.assetManager.importer.addImporter(this._importer) | |||||
| viewer.assetManager.exporter.getExporter('gltf', 'glb')?.extensions?.push(glTFTextureBasisUExtensionExport) | |||||
| } | |||||
| onRemove(viewer: ThreeViewer) { | |||||
| viewer.assetManager.importer.removeImporter(this._importer) | |||||
| 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 | |||||
| } | |||||
| } | |||||
| export class KTX2Loader2 extends KTX2Loader implements ILoader { | |||||
| async createTexture(buffer: ArrayBuffer, config: any): Promise<CompressedTexture> { | |||||
| const buffer2 = new Uint8Array(buffer.slice(0)) // clones the buffer | |||||
| const texture = (await super.createTexture(buffer, config)) as CompressedTexture & ITexture | |||||
| texture.source._sourceImgBuffer = buffer2 // keep the same buffer when cloned and all, used in serializeTextureInExtras | |||||
| texture.userData.mimeType = 'image/ktx2' | |||||
| texture.toJSON = (meta?: any)=>{ | |||||
| return serializeTextureInExtras(texture, meta, texture.name, 'image/ktx2') | |||||
| } | |||||
| texture.clone = ()=>{ | |||||
| throw new Error('ktx2 texture cloning not supported') | |||||
| } | |||||
| return texture | |||||
| } | |||||
| } | |||||
| export const KHR_TEXTURE_BASISU = 'KHR_texture_basisu' | |||||
| const glTFTextureBasisUExtensionExport = (w: GLTFWriter2)=> ({ | |||||
| writeTexture: (texture: ITexture&ImportResultExtras, textureDef: any) => { | |||||
| // if (!w.options.embedImages) return // option is removed. | |||||
| if (texture.userData.mimeType !== 'image/ktx2') return | |||||
| if (textureDef.source !== undefined && textureDef.source !== null) { | |||||
| console.warn('ktx2 export: source already set') | |||||
| return | |||||
| } | |||||
| const sourceBuffer = texture.source._sourceImgBuffer || texture.__sourceBuffer // todo do this for all images that have a __sourceBuffer (in GLTFExporter.processImage or GLTFWriter2.processTexture) | |||||
| if (!sourceBuffer) { | |||||
| console.warn('ktx2 export: no source buffer for ktx2') | |||||
| return | |||||
| } | |||||
| textureDef.extensions = textureDef.extensions || {} | |||||
| const extensionDef: any = {} | |||||
| const blob = new Blob([sourceBuffer], {type: 'image/ktx2'}) | |||||
| extensionDef.source = w.processImageBlob(blob, texture) | |||||
| textureDef.extensions[ KHR_TEXTURE_BASISU ] = extensionDef | |||||
| w.extensionsUsed[ KHR_TEXTURE_BASISU ] = true | |||||
| }, | |||||
| }) | |||||
| import {IViewerPluginSync, ThreeViewer} from '../../viewer' | |||||
| import {Importer} from '../../assetmanager' | |||||
| import {KTXLoader} from 'three/examples/jsm/loaders/KTXLoader.js' | |||||
| /** | |||||
| * Adds support for loading `.ktx`, `image/ktx` files and data uris. | |||||
| * @category Plugins | |||||
| */ | |||||
| export class KTXLoadPlugin implements IViewerPluginSync { | |||||
| declare ['constructor']: typeof KTXLoadPlugin | |||||
| 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 | |||||
| } | |||||
| } |
| 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' | |||||
| /** | |||||
| * Adds support for loading `.ply`, `text/plain+ply` files and data uris | |||||
| * @category Plugins | |||||
| */ | |||||
| export class PLYLoadPlugin implements IViewerPluginSync { | |||||
| declare ['constructor']: typeof PLYLoadPlugin | |||||
| public static readonly PluginType = 'PLYLoadPlugin' | |||||
| private _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 | |||||
| } | |||||
| }, ['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 | |||||
| } | |||||
| } |
| 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' | |||||
| /** | |||||
| * Adds support for loading `.stl`, `model/stl` files and data uris. | |||||
| * @category Plugins | |||||
| */ | |||||
| export class STLLoadPlugin implements IViewerPluginSync { | |||||
| declare ['constructor']: typeof STLLoadPlugin | |||||
| public static readonly PluginType = 'STLLoadPlugin' | |||||
| private _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 | |||||
| } | |||||
| }, ['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 | |||||
| } | |||||
| } |
| // import | // import | ||||
| export {Rhino3dmLoadPlugin} from './import/Rhino3dmLoadPlugin' | export {Rhino3dmLoadPlugin} from './import/Rhino3dmLoadPlugin' | ||||
| export {PLYLoadPlugin} from './import/PLYLoadPlugin' | |||||
| export {STLLoadPlugin} from './import/STLLoadPlugin' | |||||
| export {KTXLoadPlugin} from './import/KTXLoadPlugin' | |||||
| export {KTX2LoadPlugin} from './import/KTX2LoadPlugin' | |||||
| // postprocessing | // postprocessing | ||||
| export {TonemapPlugin} from './postprocessing/TonemapPlugin' | export {TonemapPlugin} from './postprocessing/TonemapPlugin' |
| export {GLStatsJS} from './GLStatsJS' | export {GLStatsJS} from './GLStatsJS' | ||||
| export {CustomContextMenu} from './CustomContextMenu' | export {CustomContextMenu} from './CustomContextMenu' | ||||
| export {Dropzone, type DropFile, type ListenerCallback, type DropEventType} from './Dropzone' | export {Dropzone, type DropFile, type ListenerCallback, type DropEventType} from './Dropzone' | ||||
| export {ThreeSerialization, type SerializationMetaType, type SerializationResourcesType, MetaImporter, metaToResources, getEmptyMeta, metaFromResources, convertArrayBufferToStringsInMeta, convertStringsToArrayBuffersInMeta, copyMaterialUserData, copyObject3DUserData, copyUserData, copyTextureUserData, jsonToBlob} from './serialization' | |||||
| export {ThreeSerialization, type SerializationMetaType, type SerializationResourcesType, MetaImporter, metaToResources, getEmptyMeta, metaFromResources, convertArrayBufferToStringsInMeta, convertStringsToArrayBuffersInMeta, copyMaterialUserData, copyObject3DUserData, copyUserData, copyTextureUserData, jsonToBlob, serializeTextureInExtras} from './serialization' | |||||
| export {shaderReplaceString} from './shader-helpers' | export {shaderReplaceString} from './shader-helpers' | ||||
| export {makeGLBFile} from './gltf' | export {makeGLBFile} from './gltf' | ||||
| Vector4, | Vector4, | ||||
| } from 'three' | } from 'three' | ||||
| import type {AssetImporter, AssetManager, MaterialManager} from '../assetmanager' | import type {AssetImporter, AssetManager, MaterialManager} from '../assetmanager' | ||||
| import {BlobExt, IAssetImporter} from '../assetmanager' | |||||
| import {BlobExt, IAssetImporter, ImportResultExtras} from '../assetmanager' | |||||
| import {ThreeViewer} from '../viewer' | import {ThreeViewer} from '../viewer' | ||||
| import {ITexture} from '../core' | import {ITexture} from '../core' | ||||
| import {IRenderTarget, RenderManager} from '../rendering' | import {IRenderTarget, RenderManager} from '../rendering' | ||||
| b.ext = 'json' | b.ext = 'json' | ||||
| return b | return b | ||||
| } | } | ||||
| /** | |||||
| * Used in {@link LUTCubeTextureWrapper} and {@link KTX2LoadPlugin} and imported in {@link loadConfigResources} | |||||
| * @param texture | |||||
| * @param meta | |||||
| * @param name | |||||
| * @param mime | |||||
| */ | |||||
| export function serializeTextureInExtras(texture: ITexture & ImportResultExtras, meta: any, name?: string, mime?: string) { | |||||
| if (meta?.extras[texture.uuid]) return {uuid: texture.uuid, resource: 'extras'} | |||||
| let url: any = '' | |||||
| if (texture.source?._sourceImgBuffer || texture.__sourceBuffer) { | |||||
| // serialize blob to data in image. | |||||
| // Note: do not change to Uint16Array because it's encoded to rgbe in `processViewer` | |||||
| const data = new Uint8Array(texture.source?._sourceImgBuffer || texture.__sourceBuffer as ArrayBuffer) | |||||
| const mimeType = mime || texture.userData.mimeType || '' | |||||
| url = { | |||||
| data: Array.from(data), // texture need to be a normal array, not a typed array. | |||||
| type: data.constructor.name, | |||||
| path: texture.userData.__sourceBlob?.name || texture.userData.rootPath || 'file.' + mimeType.split('/')[1], | |||||
| } | |||||
| if (mimeType) url.mimeType = mimeType | |||||
| } else if (texture.userData.rootPath) { | |||||
| url = texture.userData.rootPath | |||||
| } else { | |||||
| console.error('Unable to serialize LUT texture, not loaded through asset manager.') | |||||
| } | |||||
| const tex = { | |||||
| uuid: texture.uuid, | |||||
| url, | |||||
| userData: copyTextureUserData({}, texture.userData), | |||||
| type: texture.type, | |||||
| name: name || texture.name, | |||||
| } | |||||
| if (meta?.extras) { | |||||
| meta.extras[texture.uuid] = tex | |||||
| return {uuid: texture.uuid, resource: 'extras'} | |||||
| } | |||||
| return tex | |||||
| } |