| @@ -141,6 +141,7 @@ To make changes and run the example, click on the CodePen button on the top righ | |||
| - [@threepipe/plugin-blueprintjs](#threepipeplugin-blueprintjs) BlueprintJs UI Plugin | |||
| - [@threepipe/plugin-tweakpane-editor](#threepipeplugin-tweakpane-editor) - Tweakpane Editor Plugin | |||
| - [@threepipe/plugin-configurator](#threepipeplugin-configurator) - Provides Material Configurator and Switch Node Plugin to allow users to select variations | |||
| - [@threepipe/plugin-gltf-transform](#threepipeplugin-gltf-transform) - Plugin to transform gltf models (draco compression) | |||
| - [@threepipe/plugins-extra-importers](#threepipeplugins-extra-importers) - Plugin for loading more file types supported by loaders in three.js | |||
| - [@threepipe/plugin-blend-importer](#threepipeplugin-blend-importer) - Blender to add support for loading .blend file | |||
| - [@threepipe/plugin-geometry-generator](#threepipeplugin-geometry-generator) - Generate parametric geometry types that can be re-generated from UI/API. | |||
| @@ -3753,6 +3754,41 @@ To create a custom configurator UI, use the `SwitchNodeBasePlugin` directly and | |||
| [//]: # (TODO Add Example for custom UI) | |||
| ## @threepipe/plugin-gltf-transform | |||
| Exports [GLTFDracoExportPlugin](https://threepipe.org/plugins/gltf-transform/docs/classes/GLTFDracoExportPlugin.html) that extends the default gltf exporter to compress the file after export. | |||
| [Example](https://threepipe.org/examples/#glb-draco-export/) — | |||
| [Source Code](plugins/gltf-transform/src/index.ts) — | |||
| [API Reference](https://threepipe.org/plugins/gltf-transform/docs) | |||
| NPM: `npm install @threepipe/plugin-gltf-transform` | |||
| To use, simply add the plugin to the viewer and export using the `viewer.export` or `viewer.exportScene` functions. This also adds UI options to `AssetExporterPlugin` which are used when exporting using the plugin or using `viewer.exportScene` | |||
| The plugin overloads the default gltf exporter in the asset manager with `GLTFDracoExporter`. Using the [gltf-transform](https://gltf-transform.donmccurdy.com/) library, it compresses the exported gltf file using the [khr_draco_mesh_compression](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md) extension. | |||
| Note - Only `glb` export supported right now. | |||
| Sample Usage: | |||
| ```typescript | |||
| import {ThreeViewer, downloadBlob} from 'threepipe' | |||
| import {GLTFDracoExportPlugin} from '@threepipe/plugin-gltf-transform' | |||
| const viewer = new ThreeViewer({...}) | |||
| viewer.addPluginSync(GLTFDracoExportPlugin) | |||
| await viewer.load('file.glb') | |||
| const blob = await viewer.exportScene({ | |||
| compress: true, // this must be specified, by default it's false. | |||
| viewerConfig: true, // to export with viewer, scene and plugin settings | |||
| }) | |||
| // download the file | |||
| downloadBlob(blob, 'scene.glb') | |||
| ``` | |||
| ## @threepipe/plugin-network | |||
| Network/Cloud related plugin implementations for Threepipe. | |||
| @@ -3941,6 +3977,7 @@ const model1 = await viewer.load<IObject3D>('data:application/x-blender;base64,. | |||
| [//]: # ( TODO: The plugin should parse and references to other assets and find them relative to the .blend file or the current location.) | |||
| ## @threepipe/plugin-geometry-generator | |||
| Exports [GeometryGeneratorPlugin](https://threepipe.org/plugins/geometry-generator/docs/classes/BlendLoadPlugin.html) with several Geometry generators to create parametric and updatable geometries like plane, circle, sphere, box, torus, cylinder, cone etc. | |||
| @@ -12,6 +12,7 @@ | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-gltf-transform": "./../../plugins/gltf-transform/dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs" | |||
| } | |||
| } | |||
| @@ -7,13 +7,14 @@ import { | |||
| ThreeViewer, | |||
| } from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| import {GLTFDracoExportPlugin} from '@threepipe/plugin-gltf-transform' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| plugins: [LoadingScreenPlugin, AssetExporterPlugin, SceneUiConfigPlugin], | |||
| plugins: [LoadingScreenPlugin, AssetExporterPlugin, SceneUiConfigPlugin, GLTFDracoExportPlugin], | |||
| }) | |||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true)) | |||
| @@ -0,0 +1,36 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLB Draco 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> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-gltf-transform": "./../../plugins/gltf-transform/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -0,0 +1,63 @@ | |||
| import {_testFinish, downloadBlob, IObject3D, LoadingScreenPlugin, ThreeViewer} from 'threepipe' | |||
| import {createSimpleButtons} from '../examples-utils/simple-bottom-buttons.js' | |||
| import {GLTFDracoExportPlugin} from '@threepipe/plugin-gltf-transform' | |||
| const viewer = new ThreeViewer({canvas: document.getElementById('mcanvas') as HTMLCanvasElement, msaa: true}) | |||
| async function init() { | |||
| viewer.addPluginSync(LoadingScreenPlugin) | |||
| viewer.addPluginSync(GLTFDracoExportPlugin) | |||
| // Note: see asset-exporter-plugin example as well | |||
| // load obj + mtl | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| const helmet = await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }) | |||
| if (!helmet) { | |||
| console.error('Unable to load model') | |||
| return | |||
| } | |||
| const mesh = helmet.getObjectByName('node_damagedHelmet_-6514')! | |||
| // const blob = await viewer.export(helmetObject, {exportExt: 'glb'}) | |||
| // const blob = await viewer.exportScene({viewerConfig: false}) // export scene without viewer config | |||
| // const blob = await viewer.exportScene() // export scene with viewer config and default settings. | |||
| createSimpleButtons({ | |||
| ['Download Helmet Object GLB + DRACO']: async() => { | |||
| const blob = await viewer.export(mesh, { | |||
| exportExt: 'glb', | |||
| embedUrlImages: true, // embed images in glb even when url is available. | |||
| compress: true, | |||
| }) | |||
| if (!blob) { | |||
| alert('Unable to export helmet object') | |||
| return | |||
| } | |||
| downloadBlob(blob, 'helmet.' + blob.ext) | |||
| }, | |||
| ['Download Scene GLB (Without Viewer Config) + DRACO']: async() => { | |||
| const blob = await viewer.exportScene({viewerConfig: false, compress: true}) | |||
| if (!blob || blob.ext !== 'glb') { | |||
| alert('Unable to export scene') | |||
| return | |||
| } | |||
| downloadBlob(blob, 'scene.glb') | |||
| }, | |||
| ['Download Scene GLB (With Viewer Config) + DRACO']: async() => { | |||
| const blob = await viewer.exportScene({viewerConfig: true, compress: true}) | |||
| if (!blob || blob.ext !== 'glb') { | |||
| alert('Unable to export scene') | |||
| return | |||
| } | |||
| downloadBlob(blob, 'scene_with_config.glb') | |||
| }, | |||
| }) | |||
| } | |||
| init().finally(_testFinish) | |||
| @@ -383,6 +383,7 @@ | |||
| <li><a href="./image-snapshot-export/">PNG, JPEG, WEBP Export<br/>(Image Snapshot) </a></li> | |||
| <li><a href="./render-target-export/">EXR, PNG, JPEG, WEBP Export<br/>(Render Target Export) </a></li> | |||
| <li><a href="./glb-export/">GLB Export </a></li> | |||
| <li><a href="./glb-draco-export/">GLB (+DRACO) Export </a></li> | |||
| <li><a href="./pmat-material-export/">PMAT Material Export </a></li> | |||
| <li><a href="./transfr-share-plugin/">Transfr.one Share Plugin<br/>(Upload, share link) </a></li> | |||
| <li><a href="./svg-geometry-playground/">SVG Geometry Playground </a></li> | |||
| @@ -468,6 +469,7 @@ | |||
| </ul> | |||
| </div> | |||
| <div class="iframe-container"> | |||
| <!-- TODO: allow only threepipe and localhost domains --> | |||
| <iframe id="example-iframe" src="./tweakpane-editor/" frameborder="0" allowfullscreen="allowfullscreen" | |||
| allow="accelerometer *; ambient-light-sensor *; autoplay *; camera *; clipboard-read *; clipboard-write *; encrypted-media *; fullscreen *; geolocation *; gyroscope *; magnetometer *; microphone *; midi *; payment *; picture-in-picture *; screen-wake-lock *; speaker *; sync-xhr *; usb *; web-share *; vibrate *; vr *"> | |||
| </iframe> | |||
| @@ -24,6 +24,7 @@ | |||
| "@threepipe/plugin-geometry-generator": "./../../plugins/geometry-generator/dist/index.mjs", | |||
| "@threepipe/plugin-configurator": "./../../plugins/configurator/dist/index.mjs", | |||
| "@threepipe/plugin-network": "./../../plugins/network/dist/index.mjs", | |||
| "@threepipe/plugin-gltf-transform": "./../../plugins/gltf-transform/dist/index.mjs" | |||
| "@threepipe/plugin-gaussian-splatting": "./../../plugins/gaussian-splatting/dist/index.mjs" | |||
| } | |||
| } | |||
| @@ -61,6 +61,7 @@ import {GeometryGeneratorPlugin} from '@threepipe/plugin-geometry-generator' | |||
| import {GaussianSplattingPlugin} from '@threepipe/plugin-gaussian-splatting' | |||
| import {MaterialConfiguratorPlugin, SwitchNodePlugin} from '@threepipe/plugin-configurator' | |||
| import {AWSClientPlugin, TransfrSharePlugin} from '@threepipe/plugin-network' | |||
| import {GLTFDracoExportPlugin} from '@threepipe/plugin-gltf-transform' | |||
| async function init() { | |||
| @@ -84,6 +85,7 @@ async function init() { | |||
| await viewer.addPlugins([ | |||
| LoadingScreenPlugin, | |||
| AssetExporterPlugin, | |||
| GLTFDracoExportPlugin, | |||
| new ProgressivePlugin(), | |||
| new SSAAPlugin(), | |||
| GLTFAnimationPlugin, | |||
| @@ -2244,9 +2244,9 @@ | |||
| } | |||
| }, | |||
| "node_modules/caniuse-lite": { | |||
| "version": "1.0.30001583", | |||
| "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz", | |||
| "integrity": "sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==", | |||
| "version": "1.0.30001642", | |||
| "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", | |||
| "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", | |||
| "dev": true, | |||
| "funding": [ | |||
| { | |||
| @@ -1,6 +1,6 @@ | |||
| { | |||
| "name": "threepipe", | |||
| "version": "0.0.31", | |||
| "version": "0.0.32", | |||
| "description": "A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.", | |||
| "main": "dist/index.js", | |||
| "module": "dist/index.mjs", | |||
| @@ -27,7 +27,7 @@ | |||
| "docs-all": "npm run docs && npm run docs-plugins", | |||
| "build-plugins": "node scripts/each-plugin.mjs install", | |||
| "prepare": "npm run build && npm run build-plugins && npm run build-examples", | |||
| "update-version": "node scripts/update-version.mjs" | |||
| "update-version": "node scripts/update-version.mjs" | |||
| }, | |||
| "clean-package": { | |||
| "remove": [ | |||
| @@ -0,0 +1,59 @@ | |||
| { | |||
| "name": "@threepipe/plugin-gltf-transform", | |||
| "version": "0.1.0", | |||
| "lockfileVersion": 3, | |||
| "requires": true, | |||
| "packages": { | |||
| "": { | |||
| "name": "@threepipe/plugin-gltf-transform", | |||
| "version": "0.1.0", | |||
| "license": "Apache-2.0", | |||
| "dependencies": { | |||
| "threepipe": "file:./../../src/" | |||
| }, | |||
| "devDependencies": { | |||
| "@gltf-transform/core": "3.2.1", | |||
| "@gltf-transform/extensions": "3.2.1" | |||
| } | |||
| }, | |||
| "../../src": {}, | |||
| "../tweakpane/src": { | |||
| "extraneous": true | |||
| }, | |||
| "node_modules/@gltf-transform/core": { | |||
| "version": "3.2.1", | |||
| "resolved": "https://registry.npmjs.org/@gltf-transform/core/-/core-3.2.1.tgz", | |||
| "integrity": "sha512-EE4AXJsu1jsSvcTnzk+mCu/VgLldPsb0gGhOV7onRlHM4DYh8m9aCCjGdLJf1uNJi+KUP5hPmOrUteXuopFq2A==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "property-graph": "^1.2.0" | |||
| } | |||
| }, | |||
| "node_modules/@gltf-transform/extensions": { | |||
| "version": "3.2.1", | |||
| "resolved": "https://registry.npmjs.org/@gltf-transform/extensions/-/extensions-3.2.1.tgz", | |||
| "integrity": "sha512-7uiCXDV7/o2pnuXM9z/URdP9RR3baOaVo3f3eo65WtoUBIRKO6I/WHJzwGrRdWIWKQQWfxwZLTzYYaAgprnxDg==", | |||
| "dev": true, | |||
| "dependencies": { | |||
| "@gltf-transform/core": "^3.2.1", | |||
| "ktx-parse": "^0.5.0" | |||
| } | |||
| }, | |||
| "node_modules/ktx-parse": { | |||
| "version": "0.5.0", | |||
| "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.5.0.tgz", | |||
| "integrity": "sha512-5IZrv5s1byUeDTIee1jjJQBiD5LPDB0w9pJJ0oT9BCKKJf16Tuj123vm1Ps0GOHSHmeWPgKM0zuViCVuTRpqaA==", | |||
| "dev": true | |||
| }, | |||
| "node_modules/property-graph": { | |||
| "version": "1.3.1", | |||
| "resolved": "https://registry.npmjs.org/property-graph/-/property-graph-1.3.1.tgz", | |||
| "integrity": "sha512-gei3N/bHWJdCItJ4blnlGWd9iauEZI+JZYj/A0D177XSI01+QhiJGAVscYBhe3Yywow3A2QJzVtsO2P+UgrRRQ==", | |||
| "dev": true | |||
| }, | |||
| "node_modules/threepipe": { | |||
| "resolved": "../../src", | |||
| "link": true | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| { | |||
| "name": "@threepipe/plugin-gltf-transform", | |||
| "description": "Utility plugins for threepipe using gltf-transform to optimize/compress glTF files.", | |||
| "version": "0.1.0", | |||
| "devDependencies": { | |||
| "@gltf-transform/core": "3.2.1", | |||
| "@gltf-transform/extensions": "3.2.1" | |||
| }, | |||
| "dependencies": { | |||
| "threepipe": "file:./../../src/" | |||
| }, | |||
| "clean-package": { | |||
| "remove": [ | |||
| "clean-package", | |||
| "scripts", | |||
| "devDependencies", | |||
| "//", | |||
| "markdown-to-html" | |||
| ], | |||
| "replace": { | |||
| "dependencies": {}, | |||
| "peerDependencies": { | |||
| "threepipe": "^0.0.32" | |||
| } | |||
| } | |||
| }, | |||
| "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 && npm run docs", | |||
| "build": "rimraf dist && vite build", | |||
| "dev": "NODE_ENV=development vite build --watch", | |||
| "docs": "rimraf docs && npx typedoc" | |||
| }, | |||
| "author": "repalash <palash@shaders.app>", | |||
| "license": "Apache-2.0", | |||
| "keywords": [ | |||
| "three", | |||
| "three.js", | |||
| "threepipe", | |||
| "vite", | |||
| "plugin" | |||
| ], | |||
| "bugs": { | |||
| "url": "https://github.com/repalash/threepipe/issues" | |||
| }, | |||
| "homepage": "https://github.com/repalash/threepipe#readme", | |||
| "repository": { | |||
| "type": "git", | |||
| "url": "git://github.com/repalash/threepipe.git" | |||
| } | |||
| } | |||
| @@ -0,0 +1,161 @@ | |||
| import { | |||
| AssetExporterPlugin, | |||
| AViewerPluginSync, | |||
| ClearcoatTintPlugin, | |||
| CustomBumpMapPlugin, | |||
| DRACOLoader2, | |||
| generateUUID, | |||
| GLTFLightExtrasExtension, | |||
| GLTFMaterialExtrasExtension, | |||
| GLTFMaterialsAlphaMapExtension, | |||
| GLTFMaterialsBumpMapExtension, | |||
| GLTFMaterialsDisplacementMapExtension, | |||
| GLTFMaterialsLightMapExtension, | |||
| GLTFObject3DExtrasExtension, | |||
| NoiseBumpMaterialPlugin, | |||
| ThreeViewer, | |||
| } from 'threepipe' | |||
| import {GLTFDracoExporter} from './GLTFDracoExporter' | |||
| import {UiObjectConfig} from 'uiconfig.js' | |||
| import {EncoderOptions} from '@gltf-transform/extensions/dist/khr-draco-mesh-compression/encoder' | |||
| export enum EncoderMethod { | |||
| EDGEBREAKER = 1, | |||
| SEQUENTIAL = 0 | |||
| } | |||
| /** | |||
| * GLTF Draco Export Plugin | |||
| * | |||
| * Overloads the default gltf exporter in the asset manager with GLTFDracoExporter. When exporting with compress = true, the output will be compressed. | |||
| * Note - Only `glb` supported right now. | |||
| * | |||
| * @category Plugins | |||
| */ | |||
| export class GLTFDracoExportPlugin extends AViewerPluginSync<''> { | |||
| public static readonly PluginType = 'GLTFDracoExportPlugin' | |||
| enabled = true | |||
| onAdded(viewer: ThreeViewer): void { | |||
| super.onAdded(viewer) | |||
| const importer = viewer.assetManager.importer | |||
| const exporter = viewer.assetManager.exporter | |||
| const glbExporter = exporter.getExporter('glb') | |||
| if (glbExporter) exporter.removeExporter(glbExporter) | |||
| // todo remove exporter and add back the old one on plugin remove. | |||
| exporter.addExporter({ | |||
| ...glbExporter || { | |||
| ext: ['glb', 'gltf'], | |||
| extensions: [], | |||
| }, // for extensions | |||
| ctor: (_, _exporter) => { | |||
| const tempFile = generateUUID() + '.drc' // dummy | |||
| const ex = new GLTFDracoExporter({}, | |||
| // todo unregister on dispose | |||
| importer.registerFile(tempFile) as DRACOLoader2) | |||
| ex.setup(viewer, _exporter.extensions) | |||
| ex.createAndAddExtension(GLTFMaterialsBumpMapExtension.WebGiMaterialsBumpMapExtension, { | |||
| bumpTexture: 'R', | |||
| }) | |||
| ex.createAndAddExtension(GLTFMaterialsLightMapExtension.WebGiMaterialsLightMapExtension, { | |||
| lightMapTexture: 'RGB', | |||
| }) | |||
| ex.createAndAddExtension(GLTFMaterialsAlphaMapExtension.WebGiMaterialsAlphaMapExtension, { | |||
| alphaTexture: 'G', | |||
| }) | |||
| ex.createAndAddExtension(GLTFMaterialsDisplacementMapExtension.WebGiMaterialsDisplacementMapExtension, { | |||
| displacementTexture: 'R', | |||
| }) | |||
| ex.createAndAddExtension(CustomBumpMapPlugin.CUSTOM_BUMP_MAP_GLTF_EXTENSION, { | |||
| customBumpMap: 'RGB', | |||
| }) | |||
| ex.createAndAddExtension(GLTFLightExtrasExtension.WebGiLightExtrasExtension) | |||
| ex.createAndAddExtension(GLTFObject3DExtrasExtension.WebGiObject3DExtrasExtension) | |||
| ex.createAndAddExtension(GLTFMaterialExtrasExtension.WebGiMaterialExtrasExtension) | |||
| ex.createAndAddExtension(ClearcoatTintPlugin.CLEARCOAT_TINT_GLTF_EXTENSION) | |||
| ex.createAndAddExtension(NoiseBumpMaterialPlugin.NOISE_BUMP_MATERIAL_GLTF_EXTENSION) | |||
| // todo port | |||
| // DiamondMaterialExtension | |||
| // AnimationMarkersExtension | |||
| // AnisotropyMaterialExtension | |||
| // ThinFilmLayerMaterialExtension | |||
| // TriplanarMappingMaterialExtension | |||
| // SSBevelMaterialExtension | |||
| return ex | |||
| }, | |||
| }) | |||
| // for ui | |||
| const exportPlugin = viewer.getPlugin(AssetExporterPlugin) | |||
| if (exportPlugin) { | |||
| Object.assign(exportPlugin.exportOptions, { | |||
| compress: false, | |||
| dracoOptions: { | |||
| encodeSpeed: 5, | |||
| method: EncoderMethod.EDGEBREAKER, | |||
| quantizationVolume: 'mesh', | |||
| quantizationBits: { | |||
| ['POSITION']: 14, | |||
| ['NORMAL']: 10, | |||
| ['COLOR']: 8, | |||
| ['TEX_COORD']: 12, | |||
| ['GENERIC']: 12, | |||
| }, | |||
| } as EncoderOptions, | |||
| }) | |||
| const exportOptions = exportPlugin.uiConfig.children?.find(c => (c as UiObjectConfig).label === 'GLB Export') as UiObjectConfig | |||
| if (exportOptions) { | |||
| exportOptions.children = [this._makeUi(exportPlugin), ...exportOptions.children || []] | |||
| } else { | |||
| console.warn('GLTFDracoExportPlugin: Unable to setup UI') | |||
| } | |||
| } | |||
| } | |||
| protected _makeUi = (exporter: AssetExporterPlugin)=>[ | |||
| { | |||
| type: 'checkbox', | |||
| label: 'DRACO Compress', | |||
| property: [exporter.exportOptions, 'compress'], | |||
| onChange: ()=>exporter.uiConfig.uiRefresh?.(true), | |||
| }, | |||
| { | |||
| type: 'folder', | |||
| hidden: ()=>!exporter.exportOptions.compress, | |||
| label: 'DRACO Options', | |||
| children: [ | |||
| { | |||
| type: 'slider', | |||
| label: 'Encode Speed', | |||
| bounds: [1, 10], | |||
| property: [exporter.exportOptions.dracoOptions, 'encodeSpeed'], | |||
| }, | |||
| { | |||
| type: 'dropdown', | |||
| label: 'Encoder Method', | |||
| property: [exporter.exportOptions.dracoOptions, 'method'], | |||
| children: Object.entries(EncoderMethod).map(([k, v]) => ({label: k, value: v})), | |||
| }, | |||
| { | |||
| type: 'dropdown', | |||
| label: 'Quantization Volume', | |||
| property: [exporter.exportOptions.dracoOptions, 'quantizationVolume'], | |||
| children: ['mesh', 'scene', 'bbox'].map(v => ({label: v})), | |||
| }, | |||
| { | |||
| type: 'folder', | |||
| label: 'Quantization Bits', | |||
| children: Object.keys(exporter.exportOptions.dracoOptions?.quantizationBits || {}).map(k => ({ | |||
| type: 'slider', | |||
| label: k, | |||
| bounds: [1, 16], | |||
| stepSize: 1, | |||
| property: [exporter.exportOptions.dracoOptions?.quantizationBits, k], | |||
| })), | |||
| }, | |||
| ], | |||
| }, | |||
| ] | |||
| } | |||
| @@ -0,0 +1,418 @@ | |||
| import { | |||
| Extension, | |||
| ExtensionProperty, | |||
| GLTF, | |||
| Graph, | |||
| Property, | |||
| PropertyType, | |||
| ReaderContext, | |||
| Texture, | |||
| TextureChannel, | |||
| TextureInfo, | |||
| WebIO, | |||
| WriterContext, | |||
| } from '@gltf-transform/core' | |||
| import {EncoderOptions} from '@gltf-transform/extensions/dist/khr-draco-mesh-compression/encoder' | |||
| import {ALL_EXTENSIONS, KHRDracoMeshCompression} from '@gltf-transform/extensions' | |||
| import {DRACOLoader2, GLTFExporter2, GLTFExporter2Options, GLTFViewerConfigExtension, IExportParser} from 'threepipe' | |||
| /** | |||
| * GLTF Draco Exporter | |||
| * | |||
| * Extension of GLTFExporter2 that runs the output through gltf-transform for draco compression. | |||
| */ | |||
| export class GLTFDracoExporter extends GLTFExporter2 implements IExportParser { | |||
| public loader?: DRACOLoader2 // required for loading draco libs. | |||
| private _io: WebIO | |||
| private _loadedLibs = false | |||
| private _encoderOptions: EncoderOptions | |||
| constructor(encoderOptions?: EncoderOptions, loader?: DRACOLoader2) { | |||
| super() | |||
| encoderOptions = encoderOptions || { | |||
| method: KHRDracoMeshCompression.EncoderMethod.EDGEBREAKER, | |||
| encodeSpeed: 5, | |||
| } | |||
| this._io = new WebIO().registerExtensions(ALL_EXTENSIONS) | |||
| .registerExtensions([ | |||
| GLTFViewerConfigExtensionGP, | |||
| ]) | |||
| this._encoderOptions = encoderOptions | |||
| if (loader) { | |||
| this.loader = loader | |||
| this.loader.setDecoderConfig({type: 'js'}) // todo: hack for now. | |||
| this.loader.preload(true, true) | |||
| } | |||
| } | |||
| preload(): this { | |||
| this._loadLibs() | |||
| return this | |||
| } | |||
| private async _loadLibs() { | |||
| if (this._loadedLibs || !this.loader) return | |||
| const libs = await Promise.all([ | |||
| this.loader.initEncoder(), | |||
| this.loader.initDecoder(), | |||
| ]) | |||
| this._io.registerDependencies({ | |||
| ['draco3d.encoder']: libs[0], | |||
| ['draco3d.decoder']: libs[1], // only required if we are loading a draco compressed gltf | |||
| }) | |||
| this._loadedLibs = true | |||
| } | |||
| async parseAsync(obj: any, {compress = false, dracoOptions, ...options}: {compress: boolean, dracoOptions?: EncoderOptions} & GLTFExporter2Options): Promise<Blob> { | |||
| if (!this.loader) { | |||
| console.error('GLTFDracoExporter: No DRACOLoader2 instance provided') | |||
| return super.parseAsync(obj, options) | |||
| } | |||
| await this._loadLibs() | |||
| const ops = {...options} | |||
| if (compress) { | |||
| // externalImagesInExtras: this is required because gltf-transform doesn't support external images in glb | |||
| // see https://github.com/donmccurdy/glTF-Transform/discussions/644 | |||
| ops.externalImagesInExtras = true | |||
| } | |||
| const uncompressed = await new Promise((resolve, reject) => this.parse(obj, resolve, reject, ops)) as any | |||
| const uncompressedBlob = await super.parseAsync(uncompressed, ops) | |||
| if (!compress) return uncompressedBlob | |||
| if (!uncompressed) throw new Error('GLTFDracoExporter: gltf is null') | |||
| let gltf = uncompressed | |||
| const bytes = (gltf as ArrayBuffer).byteLength || Infinity | |||
| const iDocument = await (typeof gltf === 'object' && !(gltf as any).byteLength ? this._io.readJSON({ | |||
| json: gltf as GLTF.IGLTF, | |||
| resources: {}, | |||
| }) : this._io.readBinary(new Uint8Array(gltf as ArrayBuffer))) | |||
| // iDocument.createExtension(GLTFViewerConfigExtensionGP) | |||
| iDocument.createExtension(KHRDracoMeshCompression) | |||
| .setRequired(true) | |||
| .setEncoderOptions({...this._encoderOptions, ...dracoOptions ?? {}}) | |||
| if (ops.exportExt === 'glb') { | |||
| gltf = await this._io.writeBinary(iDocument) | |||
| if (isFinite(bytes)) { | |||
| console.log('DRACO Compression ratio: ' + ((gltf as ArrayBuffer).byteLength / bytes).toFixed(5)) | |||
| } | |||
| } else { | |||
| const jDoc = await this._io.writeJSON(iDocument) | |||
| gltf = jDoc.json | |||
| if (Object.values(jDoc.resources).filter(v => v).length > 0) { | |||
| console.warn('DRACOExporter: extra resources in resources not supported properly') | |||
| ;(gltf as any).resources = jDoc.resources | |||
| } | |||
| } | |||
| gltf.__isGLTFOutput = true | |||
| const blob = await super.parseAsync(gltf, ops) as any // this will just convert it to blob because __isGLTFOutput is set (checked in GLTFExporter2) | |||
| if (!blob) throw new Error('GLTFDracoExporter: blob is null') | |||
| blob.ext = 'glb' | |||
| ;(blob as any).__uncompressed = uncompressedBlob | |||
| return blob | |||
| } | |||
| addExtension(extension: typeof Extension): this { | |||
| this._io.registerExtensions([extension]) | |||
| return this | |||
| } | |||
| createAndAddExtension(name: string, textures?: Record<string, string|number>): this { | |||
| return this.addExtension(createGenericExtensionClass(name, textures)) | |||
| } | |||
| } | |||
| declare module 'threepipe'{ | |||
| interface GLTFExporter2Options { | |||
| compress?: boolean | |||
| dracoOptions?: EncoderOptions | |||
| } | |||
| } | |||
| // for @gltf-transform/core | |||
| class ViewerJSONExtensionProperty extends ExtensionProperty { | |||
| readonly extensionName: string = GLTFViewerConfigExtension.ViewerConfigGLTFExtension | |||
| readonly parentTypes: string[] = [PropertyType.SCENE] | |||
| readonly propertyType: string = 'ViewerJSON' | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| protected init(): void {return} | |||
| } | |||
| class GLTFViewerConfigExtensionGP extends Extension { | |||
| public readonly extensionName = GLTFViewerConfigExtension.ViewerConfigGLTFExtension | |||
| public static readonly EXTENSION_NAME = GLTFViewerConfigExtension.ViewerConfigGLTFExtension | |||
| private _viewerConfig: any = {} | |||
| // private _texturesRef: [any, Texture][] = [] | |||
| read(context: ReaderContext): this { | |||
| this._viewerConfig = {} | |||
| context.jsonDoc.json.scenes?.forEach((sceneDef, sceneIndex)=>{ | |||
| if (sceneDef.extensions && sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension]) { | |||
| const prop = new ViewerJSONExtensionProperty(this.document.getGraph()) | |||
| context.scenes[sceneIndex].setExtension(GLTFViewerConfigExtension.ViewerConfigGLTFExtension, prop) | |||
| this._viewerConfig = sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension] as any | |||
| // prop.setExtras() | |||
| /* | |||
| const buffers = [] as any[] | |||
| Object.values(viewerConfig.resources).forEach((res: any) => { | |||
| Object.values(res).forEach((item: any) => { | |||
| if (!item.url) return | |||
| if (item.url.data?.image !== null) { | |||
| buffers.push(item.url) | |||
| } | |||
| }) | |||
| }) | |||
| const jsonDoc = context.jsonDoc | |||
| console.log(buffers) | |||
| for (const buffer of buffers) { | |||
| const img = buffer.data.image as number | |||
| const imageDef = jsonDoc.json.images![img] | |||
| const bufferViewDef = jsonDoc.json.bufferViews![imageDef.bufferView!] | |||
| const bufferDef = jsonDoc.json.buffers![bufferViewDef.buffer] | |||
| const bufferData = bufferDef.uri ? jsonDoc.resources[bufferDef.uri] : jsonDoc.resources[GLB_BUFFER] | |||
| const byteOffset = bufferViewDef.byteOffset || 0 | |||
| const byteLength = bufferViewDef.byteLength | |||
| const imageData = bufferData.slice(byteOffset, byteOffset + byteLength) | |||
| const texture = this.document.createTexture(imageDef.name) | |||
| texture.setImage(imageData) | |||
| this._texturesRef.push([buffer, texture]) | |||
| } | |||
| */ | |||
| } | |||
| }) | |||
| return this | |||
| } | |||
| write(context: WriterContext): this { | |||
| this.document.getRoot().listScenes().forEach((scene)=>{ | |||
| const prop = scene.getExtension(GLTFViewerConfigExtension.ViewerConfigGLTFExtension) | |||
| if (prop) { | |||
| const sceneDef = context.jsonDoc.json.scenes?.[context.jsonDoc.json.scene || 0] // todo: get proper scene index, if working with multiple scenes | |||
| if (sceneDef && Object.keys(this._viewerConfig).length > 0) { | |||
| sceneDef.extensions = sceneDef.extensions || {} | |||
| /* | |||
| console.log(context.jsonDoc.json.images) | |||
| for (const [buffer, texture] of this._texturesRef) { | |||
| const imageDef = context.createPropertyDef(texture) as GLTF.IImage | |||
| context.createImageData(imageDef, texture.getImage()!, texture) | |||
| buffer.data.image = context.jsonDoc.json.images!.push(imageDef) - 1 | |||
| context.imageIndexMap.set(texture, buffer.data.image) | |||
| } | |||
| console.log(context.jsonDoc.json) | |||
| */ | |||
| sceneDef.extensions[GLTFViewerConfigExtension.ViewerConfigGLTFExtension] = this._viewerConfig | |||
| // this._texturesRef = [] | |||
| this._viewerConfig = {} | |||
| } | |||
| } | |||
| }) | |||
| return this | |||
| } | |||
| required = true | |||
| } | |||
| class GenericExtensionProperty extends ExtensionProperty<any> { | |||
| readonly extensionName: string | |||
| readonly parentTypes: string[] = [PropertyType.MATERIAL, PropertyType.MESH, PropertyType.NODE, PropertyType.SCENE] | |||
| readonly propertyType: string = 'GenericExtension' | |||
| textures: Record<string, [TextureInfo, Texture|null]> = {} | |||
| addTexture(key: string, texInfo: TextureInfo, texture: Texture | null, channels = 0x1111) { | |||
| this.setRef(key, texture, {channels}) | |||
| this.textures[key] = [texInfo, texture] | |||
| } | |||
| constructor(graph: Graph<Property>, name: string, extensionName: string) { | |||
| super(graph, name) | |||
| this.extensionName = extensionName | |||
| } | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| protected init(): void {return} | |||
| } | |||
| // see transmission extension for reference | |||
| abstract class GenericExtension extends Extension { | |||
| abstract readonly extensionName: string | |||
| textureChannels: Record<string, number> = {} | |||
| read(context: ReaderContext): this { | |||
| const jsonDoc = context.jsonDoc | |||
| // console.log(jsonDoc) | |||
| const materialDefs = jsonDoc.json.materials || [] | |||
| const textureDefs = jsonDoc.json.textures || [] | |||
| materialDefs.forEach((materialDef, materialIndex) => { | |||
| if (materialDef.extensions && materialDef.extensions[this.extensionName]) { | |||
| const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName) | |||
| context.materials[materialIndex].setExtension(this.extensionName, paramsExt) | |||
| const paramsExtDef = materialDef.extensions[this.extensionName] as Record<string, any> | |||
| const paramsExtDef2 = {...paramsExtDef} | |||
| for (const [key, value] of Object.entries(paramsExtDef2)) { | |||
| if (typeof value?.index === 'number') { // this is a texture... | |||
| const textureInfoDef = value | |||
| const source = textureDefs[textureInfoDef.index]?.source | |||
| if (typeof source !== 'number') { | |||
| console.warn('GLTF Pipeline: source texture not found for texture info', textureInfoDef) | |||
| continue | |||
| } | |||
| const texture = context.textures[source] | |||
| const texInfo = new TextureInfo(this.document.getGraph()) | |||
| const channels = this.textureChannels[key] ?? 0x1111 | |||
| paramsExt.addTexture(key, texInfo, texture, channels) | |||
| context.setTextureInfo(texInfo, textureInfoDef) | |||
| delete paramsExtDef2[key] | |||
| } | |||
| } | |||
| paramsExt.setExtras(paramsExtDef2) | |||
| // console.log({...paramsExtDef}) | |||
| } | |||
| }) | |||
| const meshDefs = jsonDoc.json.meshes || [] | |||
| meshDefs.forEach((meshDef, meshIndex) => { | |||
| if (meshDef.extensions && meshDef.extensions[this.extensionName]) { | |||
| const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName) | |||
| context.meshes[meshIndex].setExtension(this.extensionName, paramsExt) | |||
| const paramsExtDef = meshDef.extensions[this.extensionName] as Record<string, any> | |||
| paramsExt.setExtras(paramsExtDef) | |||
| } | |||
| }) | |||
| const nodeDefs = jsonDoc.json.nodes || [] | |||
| nodeDefs.forEach((nodeDef, nodeIndex) => { | |||
| if (nodeDef.extensions && nodeDef.extensions[this.extensionName]) { | |||
| const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName) | |||
| context.nodes[nodeIndex].setExtension(this.extensionName, paramsExt) | |||
| const paramsExtDef = nodeDef.extensions[this.extensionName] as Record<string, any> | |||
| paramsExt.setExtras(paramsExtDef) | |||
| // console.log(paramsExtDef) | |||
| } | |||
| }) | |||
| const sceneDefs = jsonDoc.json.scenes || [] | |||
| sceneDefs.forEach((sceneDef, sceneIndex) => { | |||
| if (sceneDef.extensions && sceneDef.extensions[this.extensionName]) { | |||
| const paramsExt = new GenericExtensionProperty(this.document.getGraph(), '', this.extensionName) | |||
| context.scenes[sceneIndex].setExtension(this.extensionName, paramsExt) | |||
| const paramsExtDef = sceneDef.extensions[this.extensionName] as Record<string, any> | |||
| paramsExt.setExtras(paramsExtDef) | |||
| // console.log(paramsExtDef) | |||
| } | |||
| }) | |||
| return this | |||
| } | |||
| write(context: WriterContext): this { | |||
| const jsonDoc = context.jsonDoc | |||
| this.document.getRoot() | |||
| .listMaterials() | |||
| .forEach((material) => { | |||
| const paramsExt = material.getExtension<GenericExtensionProperty>(this.extensionName) | |||
| // console.log(paramsExt) | |||
| if (paramsExt) { | |||
| const materialIndex = context.materialIndexMap.get(material)! | |||
| const materialDef = jsonDoc.json.materials![materialIndex] | |||
| materialDef.extensions = materialDef.extensions || {} | |||
| const extensionDef = paramsExt.getExtras() | |||
| const extensionDef2 = {...extensionDef} | |||
| // console.log(paramsExt.textures) | |||
| for (const [key, value] of Object.entries(paramsExt.textures)) { | |||
| const textureInfo = value[0] | |||
| const textureLink = value[1] | |||
| const texture = textureLink | |||
| if (texture) | |||
| extensionDef2[key] = context.createTextureInfoDef(texture, textureInfo) | |||
| // console.log(texture) | |||
| } | |||
| // console.log(extensionDef2) | |||
| materialDef.extensions[this.extensionName] = extensionDef2 | |||
| } | |||
| }) | |||
| this.document.getRoot() | |||
| .listMeshes() | |||
| .forEach((mesh) => { | |||
| const paramsExt = mesh.getExtension<GenericExtensionProperty>(this.extensionName) | |||
| if (paramsExt) { | |||
| const meshIndex = context.meshIndexMap.get(mesh)! | |||
| const meshDef = jsonDoc.json.meshes![meshIndex] | |||
| meshDef.extensions = meshDef.extensions || {} | |||
| meshDef.extensions[this.extensionName] = paramsExt.getExtras() | |||
| } | |||
| }) | |||
| this.document.getRoot() | |||
| .listNodes() | |||
| .forEach((node) => { | |||
| const paramsExt = node.getExtension<GenericExtensionProperty>(this.extensionName) | |||
| if (paramsExt) { | |||
| const nodeIndex = context.nodeIndexMap.get(node)! | |||
| const nodeDef = jsonDoc.json.nodes![nodeIndex] | |||
| nodeDef.extensions = nodeDef.extensions || {} | |||
| nodeDef.extensions[this.extensionName] = paramsExt.getExtras() | |||
| } | |||
| }) | |||
| this.document.getRoot() | |||
| .listScenes() | |||
| .forEach((scene) => { | |||
| const paramsExt = scene.getExtension<GenericExtensionProperty>(this.extensionName) | |||
| if (paramsExt) { | |||
| const sceneIndex = context.jsonDoc.json.scene || 0 // todo: get proper scene index, if working with multiple scenes, this will do the default one. | |||
| const sceneDef = jsonDoc.json.scenes![sceneIndex] | |||
| if (!sceneDef) return | |||
| sceneDef.extensions = sceneDef.extensions || {} | |||
| sceneDef.extensions[this.extensionName] = paramsExt.getExtras() | |||
| } | |||
| }) | |||
| return this | |||
| } | |||
| } | |||
| function stringToChannel(s: string) { | |||
| let r = 0 | |||
| if (s.includes('R')) r |= TextureChannel.R | |||
| if (s.includes('G')) r |= TextureChannel.G | |||
| if (s.includes('B')) r |= TextureChannel.B | |||
| if (s.includes('A')) r |= TextureChannel.A | |||
| return r | |||
| } | |||
| export function createGenericExtensionClass(name: string, textures?: Record<string, string|number>): typeof GenericExtension { | |||
| return class extends GenericExtension { | |||
| public static readonly EXTENSION_NAME = name | |||
| readonly extensionName = name | |||
| textureChannels: Record<string, number> = !textures ? {} : Object.fromEntries( | |||
| Object.entries(textures) | |||
| .map(([k, v])=> | |||
| [k, typeof v === 'number' ? v : stringToChannel(v)]) | |||
| ) | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| 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 | |||
| } | |||
| declare module '*.css?inline' { // for vite | |||
| const content: string | |||
| export default content | |||
| } | |||
| // export {} | |||
| // hack for typedoc | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| // declare type OffscreenCanvas = HTMLCanvasElement | |||
| @@ -0,0 +1,2 @@ | |||
| export {GLTFDracoExporter, createGenericExtensionClass} from './GLTFDracoExporter' | |||
| export {GLTFDracoExportPlugin} from './GLTFDracoExportPlugin' | |||
| @@ -0,0 +1,41 @@ | |||
| { | |||
| "compilerOptions": { | |||
| "baseUrl": "./src", | |||
| "rootDir": "./src", | |||
| "allowJs": true, | |||
| "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": "ES2021", | |||
| "strictNullChecks": true, | |||
| "lib": [ | |||
| "es2020", | |||
| "esnext", | |||
| "dom" | |||
| ] | |||
| }, | |||
| "include": [ | |||
| "src/**/*" | |||
| ], | |||
| "exclude": [ | |||
| "node_modules", | |||
| "**/*.spec.ts", | |||
| "dist" | |||
| ] | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| { | |||
| "extends": [ | |||
| "../../typedoc.json" | |||
| ], | |||
| "entryPoints": [ | |||
| "src/index.ts" | |||
| ], | |||
| "name": "Threepipe gltf-transform Plugins", | |||
| "readme": "none" | |||
| } | |||
| @@ -0,0 +1,90 @@ | |||
| import {defineConfig} from 'vite' | |||
| import json from '@rollup/plugin-json'; | |||
| import dts from 'vite-plugin-dts' | |||
| import packageJson from './package.json'; | |||
| import license from 'rollup-plugin-license'; | |||
| import replace from '@rollup/plugin-replace'; | |||
| import glsl from 'rollup-plugin-glsl'; | |||
| import path from 'node:path'; | |||
| const isProd = process.env.NODE_ENV === 'production' | |||
| const { name, version, author } = packageJson | |||
| const {main, module, browser} = packageJson | |||
| const globals = { | |||
| 'three': 'threepipe', // just incase someone uses three | |||
| 'threepipe': 'threepipe', | |||
| } | |||
| export default defineConfig({ | |||
| optimizeDeps: { | |||
| exclude: ['uiconfig.js', 'ts-browser-helpers'], | |||
| }, | |||
| base: '', | |||
| // define: { | |||
| // 'process.env': process.env | |||
| // }, | |||
| build: { | |||
| sourcemap: true, | |||
| minify: false, | |||
| cssMinify: isProd, | |||
| cssCodeSplit: false, | |||
| watch: !isProd ? { | |||
| buildDelay: 1000, | |||
| } : null, | |||
| lib: { | |||
| entry: 'src/index.ts', | |||
| formats: isProd ? ['es', 'umd'] : ['es'], | |||
| name: name, | |||
| fileName: (format) => (format === 'umd' ? main : module).replace('dist/', ''), | |||
| }, | |||
| outDir: 'dist', | |||
| emptyOutDir: isProd, | |||
| commonjsOptions: { | |||
| exclude: [/uiconfig.js/, /ts-browser-helpers/], | |||
| }, | |||
| rollupOptions: { | |||
| output: { | |||
| // inlineDynamicImports: false, | |||
| globals, | |||
| }, | |||
| external: Object.keys(globals), | |||
| }, | |||
| }, | |||
| plugins: [ | |||
| isProd ? dts({tsconfigPath: './tsconfig.json'}) : null, | |||
| replace({ | |||
| 'from \'three\'': 'from \'threepipe\'', | |||
| delimiters: ['', ''], | |||
| }), | |||
| replace({ | |||
| 'process.env.NODE_ENV': JSON.stringify(isProd ? 'production' : 'development'), | |||
| preventAssignment: true, | |||
| }), | |||
| glsl({ // todo: minify glsl. | |||
| include: 'src/**/*.glsl', | |||
| }), | |||
| json(), | |||
| // 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` | |||
| // }), | |||
| license({ | |||
| banner: ` | |||
| @license | |||
| ${name} v${version} | |||
| Copyright 2022<%= moment().format('YYYY') > 2022 ? '-' + moment().format('YYYY') : null %> ${author} | |||
| ${packageJson.license} License | |||
| See ./dependencies.txt for any bundled third-party dependencies and licenses. | |||
| `, | |||
| thirdParty: { | |||
| output: path.join(__dirname, 'dist', 'dependencies.txt'), | |||
| includePrivate: true, // Default is false. | |||
| }, | |||
| }), | |||
| ], | |||
| }) | |||