Przeglądaj źródła

Merge remote-tracking branch 'origin/master'

master
Palash Bansal 2 lat temu
rodzic
commit
7ddc4d6026
No account linked to committer's email address
98 zmienionych plików z 3754 dodań i 628 usunięć
  1. 1
    2
      .eslintrc.cjs
  2. 1854
    214
      README.md
  3. 1
    0
      examples/3dm-to-glb/index.html
  4. 1
    0
      examples/camera-uiconfig/index.html
  5. 1
    0
      examples/custom-pipeline/index.html
  6. 1
    0
      examples/depth-buffer-plugin/index.html
  7. 1
    0
      examples/directional-light/index.html
  8. 1
    0
      examples/drc-load/index.html
  9. 1
    0
      examples/dropzone-plugin/index.html
  10. 1
    0
      examples/exr-load/index.html
  11. 48
    0
      examples/extra-importer-plugins/index.html
  12. 97
    0
      examples/extra-importer-plugins/script.ts
  13. 1
    0
      examples/fbx-load/index.html
  14. 1
    0
      examples/frame-fade-plugin/index.html
  15. 1
    0
      examples/fullscreen-plugin/index.html
  16. 35
    0
      examples/geometry-uv-preview/index.html
  17. 35
    0
      examples/geometry-uv-preview/script.ts
  18. 1
    0
      examples/glb-export/index.html
  19. 1
    1
      examples/gltf-animation-page-scroll/index.html
  20. 1
    0
      examples/gltf-animation-plugin/index.html
  21. 1
    0
      examples/gltf-camera-animation/index.html
  22. 1
    0
      examples/gltf-load/index.html
  23. 1
    0
      examples/gltf-transmission-test/index.html
  24. 1
    0
      examples/half-float-hdr-test/index.html
  25. 1
    0
      examples/hdr-load/index.html
  26. 1
    0
      examples/hdr-to-exr/index.html
  27. 1
    0
      examples/image-load/index.html
  28. 1
    0
      examples/image-snapshot-export/index.html
  29. 1
    0
      examples/import-test/index.html
  30. 4
    1
      examples/index.html
  31. 1
    0
      examples/ktx-load/index.html
  32. 1
    0
      examples/ktx2-load/index.html
  33. 1
    0
      examples/material-uiconfig/index.html
  34. 1
    0
      examples/normal-buffer-plugin/index.html
  35. 1
    0
      examples/obj-mtl-load/index.html
  36. 1
    0
      examples/obj-to-glb/index.html
  37. 1
    0
      examples/object-uiconfig/index.html
  38. 1
    0
      examples/parallel-asset-import/index.html
  39. 1
    0
      examples/ply-load/index.html
  40. 1
    0
      examples/pmat-material-export/index.html
  41. 1
    0
      examples/popmotion-plugin/index.html
  42. 1
    0
      examples/progressive-plugin/index.html
  43. 1
    0
      examples/render-target-export/index.html
  44. 1
    0
      examples/render-target-preview/index.html
  45. 1
    0
      examples/rhino3dm-load/index.html
  46. 1
    0
      examples/scene-uiconfig/index.html
  47. 1
    0
      examples/sphere-half-float-test/index.html
  48. 1
    0
      examples/sphere-msaa-test/index.html
  49. 1
    0
      examples/sphere-rgbm-test/index.html
  50. 1
    0
      examples/stl-load/index.html
  51. 1
    0
      examples/tonemap-plugin/index.html
  52. 4
    1
      examples/tweakpane-editor/index.html
  53. 9
    5
      examples/tweakpane-editor/script.ts
  54. 1
    0
      examples/uint8-rgbm-hdr-test/index.html
  55. 35
    0
      examples/usdz-load/index.html
  56. 31
    0
      examples/usdz-load/script.ts
  57. 1
    0
      examples/viewer-uiconfig/index.html
  58. 1
    0
      examples/z-prepass/index.html
  59. 8
    8
      package-lock.json
  60. 4
    4
      package.json
  61. 30
    0
      plugins/extra-importers/package-lock.json
  62. 58
    0
      plugins/extra-importers/package.json
  63. 97
    0
      plugins/extra-importers/rollup.config.mjs
  64. 36
    0
      plugins/extra-importers/src/global.d.ts
  65. 244
    0
      plugins/extra-importers/src/index.ts
  66. 41
    0
      plugins/extra-importers/tsconfig.json
  67. 10
    0
      plugins/extra-importers/typedoc.json
  68. 36
    0
      plugins/plugin-template-rollup/package-lock.json
  69. 60
    0
      plugins/plugin-template-rollup/package.json
  70. 97
    0
      plugins/plugin-template-rollup/rollup.config.mjs
  71. 3
    0
      plugins/plugin-template-rollup/src/SamplePlugin.css
  72. 19
    0
      plugins/plugin-template-rollup/src/SamplePlugin.ts
  73. 36
    0
      plugins/plugin-template-rollup/src/global.d.ts
  74. 1
    0
      plugins/plugin-template-rollup/src/index.ts
  75. 41
    0
      plugins/plugin-template-rollup/tsconfig.json
  76. 10
    0
      plugins/plugin-template-rollup/typedoc.json
  77. 3
    1
      plugins/tweakpane/src/tpImageInputGenerator.ts
  78. 4
    3
      scripts/utils.mjs
  79. 4
    1
      src/assetmanager/AssetManager.ts
  80. 1
    0
      src/core/IRenderer.ts
  81. 1
    1
      src/core/IScene.ts
  82. 106
    91
      src/core/object/RootScene.ts
  83. 24
    0
      src/plugins/base/BaseImporterPlugin.ts
  84. 6
    10
      src/plugins/import/KTX2LoadPlugin.ts
  85. 3
    17
      src/plugins/import/KTXLoadPlugin.ts
  86. 7
    19
      src/plugins/import/PLYLoadPlugin.ts
  87. 3
    18
      src/plugins/import/Rhino3dmLoadPlugin.ts
  88. 7
    18
      src/plugins/import/STLLoadPlugin.ts
  89. 38
    0
      src/plugins/import/USDZLoadPlugin.ts
  90. 3
    0
      src/plugins/index.ts
  91. 49
    0
      src/plugins/ui/GeometryUVPreviewPlugin.css
  92. 166
    0
      src/plugins/ui/GeometryUVPreviewPlugin.ts
  93. 16
    2
      src/rendering/RenderManager.ts
  94. 34
    1
      src/utils/CustomContextMenu.ts
  95. 1
    0
      src/utils/serialization.ts
  96. 9
    0
      src/utils/shader-helpers.ts
  97. 237
    209
      src/viewer/ThreeViewer.ts
  98. 1
    1
      src/viewer/version.ts

+ 1
- 2
.eslintrc.cjs Wyświetl plik

@@ -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': './',
},

+ 1854
- 214
README.md
Plik diff jest za duży
Wyświetl plik


+ 1
- 0
examples/3dm-to-glb/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/camera-uiconfig/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/custom-pipeline/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/depth-buffer-plugin/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/directional-light/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/drc-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/dropzone-plugin/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/exr-load/index.html Wyświetl plik

@@ -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>

+ 48
- 0
examples/extra-importer-plugins/index.html Wyświetl plik

@@ -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>

+ 97
- 0
examples/extra-importer-plugins/script.ts Wyświetl plik

@@ -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)


+ 1
- 0
examples/fbx-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/frame-fade-plugin/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/fullscreen-plugin/index.html Wyświetl plik

@@ -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>

+ 35
- 0
examples/geometry-uv-preview/index.html Wyświetl plik

@@ -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>

+ 35
- 0
examples/geometry-uv-preview/script.ts Wyświetl plik

@@ -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)

+ 1
- 0
examples/glb-export/index.html Wyświetl plik

@@ -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>

+ 1
- 1
examples/gltf-animation-page-scroll/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/gltf-animation-plugin/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/gltf-camera-animation/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/gltf-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/gltf-transmission-test/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/half-float-hdr-test/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/hdr-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/hdr-to-exr/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/image-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/image-snapshot-export/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/import-test/index.html Wyświetl plik

@@ -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>

+ 4
- 1
examples/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/ktx-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/ktx2-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/material-uiconfig/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/normal-buffer-plugin/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/obj-mtl-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/obj-to-glb/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/object-uiconfig/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/parallel-asset-import/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/ply-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/pmat-material-export/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/popmotion-plugin/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/progressive-plugin/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/render-target-export/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/render-target-preview/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/rhino3dm-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/scene-uiconfig/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/sphere-half-float-test/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/sphere-msaa-test/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/sphere-rgbm-test/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/stl-load/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/tonemap-plugin/index.html Wyświetl plik

@@ -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>

+ 4
- 1
examples/tweakpane-editor/index.html Wyświetl plik

@@ -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"
}
}


+ 9
- 5
examples/tweakpane-editor/script.ts Wyświetl plik

@@ -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)

+ 1
- 0
examples/uint8-rgbm-hdr-test/index.html Wyświetl plik

@@ -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>

+ 35
- 0
examples/usdz-load/index.html Wyświetl plik

@@ -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>

+ 31
- 0
examples/usdz-load/script.ts Wyświetl plik

@@ -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)

+ 1
- 0
examples/viewer-uiconfig/index.html Wyświetl plik

@@ -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>

+ 1
- 0
examples/z-prepass/index.html Wyświetl plik

@@ -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>

+ 8
- 8
package-lock.json Wyświetl plik

@@ -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",

+ 4
- 4
package.json Wyświetl plik

@@ -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": {

+ 30
- 0
plugins/extra-importers/package-lock.json Wyświetl plik

@@ -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"
}
}
}

+ 58
- 0
plugins/extra-importers/package.json Wyświetl plik

@@ -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"
}
}

+ 97
- 0
plugins/extra-importers/rollup.config.mjs Wyświetl plik

@@ -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.
},
})
]
}

+ 36
- 0
plugins/extra-importers/src/global.d.ts Wyświetl plik

@@ -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

+ 244
- 0
plugins/extra-importers/src/index.ts Wyświetl plik

@@ -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

+ 41
- 0
plugins/extra-importers/tsconfig.json Wyświetl plik

@@ -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"
]
}

+ 10
- 0
plugins/extra-importers/typedoc.json Wyświetl plik

@@ -0,0 +1,10 @@
{
"extends": [
"../../typedoc.json"
],
"entryPoints": [
"src/index.ts"
],
"name": "Threepipe Extra Importer Plugins",
"readme": "none"
}

+ 36
- 0
plugins/plugin-template-rollup/package-lock.json Wyświetl plik

@@ -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"
}
}
}

+ 60
- 0
plugins/plugin-template-rollup/package.json Wyświetl plik

@@ -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"
}
}

+ 97
- 0
plugins/plugin-template-rollup/rollup.config.mjs Wyświetl plik

@@ -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.
},
})
]
}

+ 3
- 0
plugins/plugin-template-rollup/src/SamplePlugin.css Wyświetl plik

@@ -0,0 +1,3 @@
html{

}

+ 19
- 0
plugins/plugin-template-rollup/src/SamplePlugin.ts Wyświetl plik

@@ -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)
}
}

+ 36
- 0
plugins/plugin-template-rollup/src/global.d.ts Wyświetl plik

@@ -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

+ 1
- 0
plugins/plugin-template-rollup/src/index.ts Wyświetl plik

@@ -0,0 +1 @@
export {SamplePlugin} from './SamplePlugin'

+ 41
- 0
plugins/plugin-template-rollup/tsconfig.json Wyświetl plik

@@ -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"
]
}

+ 10
- 0
plugins/plugin-template-rollup/typedoc.json Wyświetl plik

@@ -0,0 +1,10 @@
{
"extends": [
"../../typedoc.json"
],
"entryPoints": [
"src/index.ts"
],
"name": "Threepipe Tweakpane Editor Plugin",
"readme": "none"
}

+ 3
- 1
plugins/tweakpane/src/tpImageInputGenerator.ts Wyświetl plik

@@ -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
}

+ 4
- 3
scripts/utils.mjs Wyświetl plik

@@ -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'})
})

+ 4
- 1
src/assetmanager/AssetManager.ts Wyświetl plik

@@ -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
}

+ 1
- 0
src/core/IRenderer.ts Wyświetl plik

@@ -81,6 +81,7 @@ export interface IRenderManagerOptions {
rgbm?: boolean,
msaa?: boolean,
depthBuffer?: boolean,
renderScale?: number,
}

export interface IWebGLRenderer<TManager extends IRenderManager=IRenderManager> extends WebGLRenderer {

+ 1
- 1
src/core/IScene.ts Wyświetl plik

@@ -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
*/

+ 106
- 91
src/core/object/RootScene.ts Wyświetl plik

@@ -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}

+ 24
- 0
src/plugins/base/BaseImporterPlugin.ts Wyświetl plik

@@ -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
}

}

+ 6
- 10
src/plugins/import/KTX2LoadPlugin.ts Wyświetl plik

@@ -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
}

}


+ 3
- 17
src/plugins/import/KTXLoadPlugin.ts Wyświetl plik

@@ -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)

}

+ 7
- 19
src/plugins/import/PLYLoadPlugin.ts Wyświetl plik

@@ -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
}

}

+ 3
- 18
src/plugins/import/Rhino3dmLoadPlugin.ts Wyświetl plik

@@ -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)
}

+ 7
- 18
src/plugins/import/STLLoadPlugin.ts Wyświetl plik

@@ -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
}

}

+ 38
- 0
src/plugins/import/USDZLoadPlugin.ts Wyświetl plik

@@ -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)

}

+ 3
- 0
src/plugins/index.ts Wyświetl plik

@@ -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'

+ 49
- 0
src/plugins/ui/GeometryUVPreviewPlugin.css Wyświetl plik

@@ -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;
}

+ 166
- 0
src/plugins/ui/GeometryUVPreviewPlugin.ts Wyświetl plik

@@ -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()
}

}

+ 16
- 2
src/rendering/RenderManager.ts Wyświetl plik

@@ -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

+ 34
- 1
src/utils/CustomContextMenu.ts Wyświetl plik

@@ -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

+ 1
- 0
src/utils/serialization.ts Wyświetl plik

@@ -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,

+ 9
- 0
src/utils/shader-helpers.ts Wyświetl plik

@@ -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,

+ 237
- 209
src/viewer/ThreeViewer.ts Wyświetl plik

@@ -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
src/viewer/version.ts Wyświetl plik

@@ -1 +1 @@
export const VERSION = '0.0.12'
export const VERSION = '0.0.14'

Ładowanie…
Anuluj
Zapisz