Sfoglia il codice sorgente

Add react, vue, svelte samples

master
Palash Bansal 2 anni fa
parent
commit
4766d64ca8
Nessun account collegato all'indirizzo email del committer

+ 90
- 1
README.md Vedi File

- [Table of Contents](#table-of-contents) - [Table of Contents](#table-of-contents)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [HTML/JS Quickstart (CDN)](#htmljs-quickstart-cdn) - [HTML/JS Quickstart (CDN)](#htmljs-quickstart-cdn)
- [React](#react)
- [Vue.js](#vuejs)
- [Svelte](#svelte)
- [NPM/YARN Package](#npmyarn) - [NPM/YARN Package](#npmyarn)
- [Installation](#installation) - [Installation](#installation)
- [Loading a 3D Model](#loading-a-3d-model) - [Loading a 3D Model](#loading-a-3d-model)
}) })
</script> </script>
``` ```
Check it in action: https://threepipe.org/examples/#html-sample/
Check it in action: https://threepipe.org/examples/#html-js-sample/


Check out the details about the [ThreeViewer API](#viewer-api) and more [plugins](#threepipe-plugins) below. Check out the details about the [ThreeViewer API](#viewer-api) and more [plugins](#threepipe-plugins) below.


### React

A sample [react](https://react.dev) component in tsx to render a model with an environment map.

```tsx
import React from 'react'
function ThreeViewerComponent({src, env}: {src: string, env: string}) {
const canvasRef = React.useRef(null)
React.useEffect(() => {
const viewer = new ThreeViewer({canvas: canvasRef.current})

const envPromise = viewer.setEnvironmentMap(env)
const modelPromise = viewer.load(src)
Promise.all([envPromise, modelPromise])
return () => {
viewer.dispose()
}
}, [])
return (
<canvas id="three-canvas" style={{width: 800, height: 600}} ref={canvasRef} />
)
}
```

Check it in action: https://threepipe.org/examples/#react-tsx-sample/

Other examples in js: https://threepipe.org/examples/#react-js-sample/ and jsx: https://threepipe.org/examples/#react-jsx-sample/

### Vue.js

A sample [vue.js](https://vuejs.org/) component in js to render a model with an environment map.

```js
const ThreeViewerComponent = {
setup() {
const canvasRef = ref(null);

onMounted(() => {
const viewer = new ThreeViewer({ canvas: canvasRef.value });

const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr');
const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf');

Promise.all([envPromise, modelPromise])

onBeforeUnmount(() => {
viewer.dispose();
});
});

return { canvasRef };
},
};
```

Check it in action: https://threepipe.org/examples/#vue-html-sample/

Another example with Vue SFC(Single file component): https://threepipe.org/examples/#vue-sfc-sample/

### Svelte

A sample [svelte](https://svelte.dev/) component in js to render a model with an environment map.

```html
<script>
import {onDestroy, onMount} from 'svelte';
import {ThreeViewer} from 'threepipe';

let canvasRef;
let viewer;
onMount(() => {
viewer = new ThreeViewer({canvas: canvasRef});

const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr');
const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf');

Promise.all([envPromise, modelPromise])
});
onDestroy(() => viewer.dispose())
</script>

<canvas bind:this={canvasRef} id="three-canvas" style="width: 800px; height: 600px"></canvas>
```

Check it in action: https://threepipe.org/examples/#svelte-sample/

### NPM/YARN ### NPM/YARN


### Installation ### Installation

examples/html-sample/index.html → examples/html-js-sample/index.html Vedi File

<body> <body>
<canvas id="three-canvas" style="width: 800px; height: 600px;"></canvas> <canvas id="three-canvas" style="width: 800px; height: 600px;"></canvas>
<script id="example-script" type="module" data-scripts="./index.html"> <script id="example-script" type="module" data-scripts="./index.html">
import {ThreeViewer} from 'https://threepipe.org/dist/index.mjs'
// import {ThreeViewer} from 'https://threepipe.org/dist/index.mjs'
import {ThreeViewer} from './../../dist/index.mjs'
const viewer = new ThreeViewer({canvas: document.getElementById('three-canvas')}) const viewer = new ThreeViewer({canvas: document.getElementById('three-canvas')})


// Load an environment map // Load an environment map
}) })
</script> </script>
</body> </body>
</html>

+ 56
- 0
examples/react-js-sample/index.html Vedi File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Threepipe React/JS Sample</title>
<style>
html, body{
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>

<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
</head>
<body>
<div id="root"></div>
<script id="example-script" type="module" data-scripts="./index.html">
// import {ThreeViewer} from 'https://threepipe.org/dist/index.mjs'
import {ThreeViewer} from './../../dist/index.mjs'
import React from 'https://esm.sh/react'
import ReactDOM from 'https://esm.sh/react-dom'

function ThreeViewerComponent({ src }) {
const canvasRef = React.useRef(null);
React.useEffect(() => {
const viewer = new ThreeViewer({canvas: canvasRef.current})

// Load an environment map
const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
})

Promise.all([envPromise, modelPromise]).then(([env, model])=>{
console.log('Loaded', model, env, viewer)
})
return () => {
viewer.dispose()
}
}, []);
return React.createElement(
'canvas',
{id: 'three-canvas', style: {width: 800, height: 600}, ref: canvasRef},
)
}

ReactDOM.render(
React.createElement(ThreeViewerComponent),
document.getElementById('root')
)
</script>
</body>
</html>

+ 60
- 0
examples/react-jsx-sample/index.html Vedi File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Threepipe React/JSX Sample</title>
<style>
html, body{
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>

<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
<!-- Include Babel for JSX transformation -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script id="example-script" type="text/babel" data-scripts="./index.html" data-type="module">
// import {ThreeViewer} from 'https://threepipe.org/dist/index.mjs'
import {ThreeViewer} from './../../dist/index.mjs'
import React from 'https://esm.sh/react'
import ReactDOM from 'https://esm.sh/react-dom'

function ThreeViewerComponent({ src, env }) {
const canvasRef = React.useRef(null);
React.useEffect(() => {
const viewer = new ThreeViewer({canvas: canvasRef.current})

// Load an environment map
const envPromise = viewer.setEnvironmentMap(env)
const modelPromise = viewer.load(src, {
autoCenter: true,
autoScale: true,
})

Promise.all([envPromise, modelPromise]).then(([env, model])=>{
console.log('Loaded', model, env, viewer)
})
return () => {
viewer.dispose()
}
}, []);
return (
<canvas id="three-canvas" style={{ width: 800, height: 600 }} ref={canvasRef} />
)
}

ReactDOM.render(
<ThreeViewerComponent
src={'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf'}
env={'https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr'}
/>,
document.getElementById('root')
)
</script>
</body>
</html>

+ 36
- 0
examples/react-tsx-sample/index.html Vedi File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Threepipe React/TSX Sample</title>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs",
"react": "https://esm.sh/react",
"react-dom": "https://esm.sh/react-dom"
}
}

</script>

<style>
html, body{
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.tsx;./script.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>

+ 41
- 0
examples/react-tsx-sample/script.tsx Vedi File

import {_testFinish, ThreeViewer} from 'threepipe'
// @ts-expect-error no need react here
import React from 'react'
// @ts-expect-error no need react-dom here
import ReactDOM from 'react-dom'

function ThreeViewerComponent({src, env}: {src: string, env: string}) {
const canvasRef = React.useRef(null)
React.useEffect(() => {
const viewer = new ThreeViewer({canvas: canvasRef.current})

// Load an environment map
const envPromise = viewer.setEnvironmentMap(env)
const modelPromise = viewer.load(src, {
autoCenter: true,
autoScale: true,
})

Promise.all([envPromise, modelPromise]).then(([env, model])=>{
console.log('Loaded', model, env, viewer)
})
return () => {
viewer.dispose()
}
}, [])
return (
<canvas id="three-canvas" style={{width: 800, height: 600}} ref={canvasRef} />
)
}

async function init() {
ReactDOM.render(
<ThreeViewerComponent
src={'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf'}
env={'https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr'}
/>,
document.getElementById('root')
)
}

init().then(_testFinish)

+ 29
- 0
examples/svelte-sample/App.svelte Vedi File

<script>
import {onDestroy, onMount} from 'svelte';

const {ThreeViewer} = window.threepipe; // umd imported from unpkg in index.html
// or
// import {ThreeViewer} from 'threepipe'; // esm imported from npm

let canvasRef;
let viewer;

onMount(() => {
viewer = new ThreeViewer({canvas: canvasRef});

// Load an environment map
const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr');
const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
});

Promise.all([envPromise, modelPromise]).then(([env, model]) => {
console.log('Loaded', model, env, viewer);
});
});
onDestroy(() => viewer.dispose())

</script>

<canvas bind:this={canvasRef} id="three-canvas" style="width: 800px; height: 600px"></canvas>

+ 47
- 0
examples/svelte-sample/index.html Vedi File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Threepipe Svelte Sample</title>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs"
}
}

</script>

<style>
html, body{
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>

<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
<script src="./../../dist/index.js"></script>
<!-- <script src="https://unpkg.com/threepipe"></script>-->
<script src="https://unpkg.com/svelte-browser-import"></script>

</head>
<body>
<div id="app">
</div>
<script type="module" data-scripts="./App.svelte" id="example-script">
window["svelte-browser-import"].importSvelte('./App.svelte').then(App=> {
const app = new App({
target: document.getElementById('app'),
})
// to destroy the app
// app.$destroy()
})
</script>
</body>
</html>

+ 4
- 2
examples/tsconfig.json Vedi File

"es2020", "es2020",
"esnext", "esnext",
"dom" "dom"
]
],
"jsx": "react"
}, },
"include": [ "include": [
"./**/*.ts"
"./**/*.ts",
"./**/*.tsx"
], ],
"exclude": [ "exclude": [
"node_modules", "node_modules",

+ 57
- 0
examples/vue-html-sample/index.html Vedi File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Threepipe Vue/HTML Sample</title>
<style>
html, body{
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>

<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
</head>
<body>
<div id="app">
<canvas id="three-canvas" style="width: 800px; height: 600px" ref="canvasRef"></canvas>
</div>
<script id="example-script" type="module" data-scripts="./index.html">
// import { ThreeViewer } from 'https://threepipe.org/dist/index.mjs'
import { ThreeViewer } from './../../dist/index.mjs'
import { createApp, ref, onMounted, onBeforeUnmount } from "https://unpkg.com/vue@3/dist/vue.esm-browser.prod.js";

const ThreeViewerComponent = {
setup() {
const canvasRef = ref(null);

onMounted(() => {
const viewer = new ThreeViewer({ canvas: canvasRef.value });

// Load an environment map
const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr');
const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
});

Promise.all([envPromise, modelPromise]).then(([env, model]) => {
console.log('Loaded', model, env, viewer);
});

onBeforeUnmount(() => {
viewer.dispose();
});
});

return { canvasRef };
},
};

const app = createApp(ThreeViewerComponent);
app.mount('#app');
</script>
</body>
</html>

+ 48
- 0
examples/vue-sfc-sample/index.html Vedi File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Threepipe React/TSX Sample</title>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs",
"vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.prod.js",
"vue-import": "https://unpkg.com/vue-import/dist/vue-import.esm-browser.js"
}
}

</script>
<!-- "vue-import": "./vue-import/dist/vue-import.esm-browser.prod.js"-->

<style>
html, body{
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>

<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
</head>
<body>
<div id="app">
<three-viewer></three-viewer>
</div>
<script type="module" data-scripts="./script.vue" id="example-script">
import { createApp } from 'vue';
import vueImport from 'vue-import';

(async ()=>{
const app = createApp();
app.component('three-viewer', await vueImport('script.vue', {}))
app.mount('#app');
})()
</script>
</body>
</html>

+ 36
- 0
examples/vue-sfc-sample/script.vue Vedi File

<template>
<canvas id="three-canvas" style="width: 800px; height: 600px" ref="canvasRef"></canvas>
</template>

<script>
import {ThreeViewer} from "threepipe";
import {onBeforeUnmount, onMounted, ref} from "vue"

export default {
setup() {

const canvasRef = ref(null);

onMounted(() => {
const viewer = new ThreeViewer({canvas: canvasRef.value});

// Load an environment map
const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr');
const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
});

Promise.all([envPromise, modelPromise]).then(([env, model]) => {
console.log('Loaded', model, env, viewer);
});

onBeforeUnmount(() => {
viewer.dispose();
});
});

return {canvasRef};
},
};
</script>

+ 7
- 7
package-lock.json Vedi File

"typedoc": "^0.24.7", "typedoc": "^0.24.7",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"typescript-plugin-css-modules": "^5.0.1", "typescript-plugin-css-modules": "^5.0.1",
"uiconfig.js": "^0.0.7"
"uiconfig.js": "^0.0.8"
}, },
"optionalDependencies": { "optionalDependencies": {
"win-node-env": "^0.6.1" "win-node-env": "^0.6.1"
} }
}, },
"node_modules/uiconfig.js": { "node_modules/uiconfig.js": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.7.tgz",
"integrity": "sha512-PNZkeNd52ETa5UQRu5XLXqJZhAUoUSzCiBfYVbl+7GdRIt65XTmPgNIjqeZXL59g6zhaGrkWTLa0AVyeZzVJZQ==",
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.8.tgz",
"integrity": "sha512-0H1OO4CNHP5O0LBy82YWWFCzDK+Yf/GtXnR3i968FkMkf0+3/JsW7MC8ea2CcPtsi8ni4TA1FrMOC+KrYmMnCQ==",
"dev": true "dev": true
}, },
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"dev": true "dev": true
}, },
"uiconfig.js": { "uiconfig.js": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.7.tgz",
"integrity": "sha512-PNZkeNd52ETa5UQRu5XLXqJZhAUoUSzCiBfYVbl+7GdRIt65XTmPgNIjqeZXL59g6zhaGrkWTLa0AVyeZzVJZQ==",
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.8.tgz",
"integrity": "sha512-0H1OO4CNHP5O0LBy82YWWFCzDK+Yf/GtXnR3i968FkMkf0+3/JsW7MC8ea2CcPtsi8ni4TA1FrMOC+KrYmMnCQ==",
"dev": true "dev": true
}, },
"unbox-primitive": { "unbox-primitive": {

+ 3
- 2
package.json Vedi File

"module": "dist/index.mjs", "module": "dist/index.mjs",
"types": "src/index.ts", "types": "src/index.ts",
"sources": "src/index.ts", "sources": "src/index.ts",
"browser": "dist/index.js",
"type": "module", "type": "module",
"scripts": { "scripts": {
"new:pack": "npm run prepare && clean-package && npm pack && clean-package restore", "new:pack": "npm run prepare && clean-package && npm pack && clean-package restore",
"typedoc": "^0.24.7", "typedoc": "^0.24.7",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"typescript-plugin-css-modules": "^5.0.1", "typescript-plugin-css-modules": "^5.0.1",
"uiconfig.js": "^0.0.7",
"uiconfig.js": "^0.0.8",
"@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-replace": "^5.0.2",
"popmotion": "^11.0.5" "popmotion": "^11.0.5"
}, },
}, },
"//": { "//": {
"dependencies": { "dependencies": {
"uiconfig.js": "^0.0.7",
"uiconfig.js": "^0.0.8",
"ts-browser-helpers": "^0.8.0", "ts-browser-helpers": "^0.8.0",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2018/package.tgz", "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2018/package.tgz",
"three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2018.tar.gz", "three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2018.tar.gz",

+ 10
- 9
rollup.config.mjs Vedi File

import postcss from 'rollup-plugin-postcss' import postcss from 'rollup-plugin-postcss'
import glsl from "rollup-plugin-glsl" import glsl from "rollup-plugin-glsl"
import replace from "@rollup/plugin-replace"; import replace from "@rollup/plugin-replace";
import terser from "@rollup/plugin-terser";


const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
// preserveModulesRoot: 'src', // optional but useful to create a more plain folder structure // preserveModulesRoot: 'src', // optional but useful to create a more plain folder structure
format: 'es' format: 'es'
}, },
// {
// file: browser,
// ...settings,
// name: name,
// format: 'umd',
// plugins: [
// isProduction && terser()
// ]
// }
{
file: browser,
...settings,
name: name,
format: 'umd',
plugins: [
isProduction && terser()
]
}
], ],
external: [], external: [],
plugins: [ plugins: [

+ 13
- 1
src/assetmanager/AssetImporter.ts Vedi File



// region import functions // region import functions


async import<T extends ImportResult|undefined = ImportResult>(assetOrPath?: string | IAsset | IAsset[], options?: ImportAssetOptions): Promise<(T|undefined)[]> {
async import<T extends ImportResult|undefined = ImportResult>(assetOrPath?: string | IAsset | IAsset[] | File | File[], options?: ImportAssetOptions): Promise<(T|undefined)[]> {
if (!assetOrPath) return [] if (!assetOrPath) return []
if (Array.isArray(assetOrPath)) return (await Promise.all(assetOrPath.map(async a => this.import<T>(a, options)))).flat(1) if (Array.isArray(assetOrPath)) return (await Promise.all(assetOrPath.map(async a => this.import<T>(a, options)))).flat(1)
if (assetOrPath instanceof File) return await this.importFile<T>(assetOrPath, options)
if (typeof assetOrPath === 'object') return await this.importAsset<T>(assetOrPath, options) if (typeof assetOrPath === 'object') return await this.importAsset<T>(assetOrPath, options)
if (typeof assetOrPath === 'string') return await this.importPath<T>(assetOrPath, options) if (typeof assetOrPath === 'string') return await this.importPath<T>(assetOrPath, options)
console.error('AssetImporter: Invalid asset or path', assetOrPath) console.error('AssetImporter: Invalid asset or path', assetOrPath)
return result return result
} }


async importFile<T extends ImportResult|undefined = ImportResult|undefined>(file?: File, options: ImportAssetOptions = {}, onDownloadProgress?: (e:ProgressEvent)=>void): Promise<T[]> {
if (!file) return []
if (!(file instanceof File)) {
console.error('AssetImporter: Invalid file', file)
return []
}
return this.importAsset(this._cachedAssets.find(a=>a.file === file) ?? {
path: file.name || file.webkitRelativePath, file,
}, options, onDownloadProgress)
}

/** /**
* Import multiple local files/blobs from a map of files, like when a local folder is loaded, or when multiple files are dropped. * Import multiple local files/blobs from a map of files, like when a local folder is loaded, or when multiple files are dropped.
* @param files * @param files

+ 2
- 2
src/assetmanager/AssetManager.ts Vedi File



} }


async addAsset<T extends ImportResult = ImportResult>(assetOrPath?: string | IAsset | IAsset[], options?: ImportAddOptions): Promise<(T|undefined)[]> {
async addAsset<T extends ImportResult = ImportResult>(assetOrPath?: string | IAsset | IAsset[] | File | File[], options?: ImportAddOptions): Promise<(T|undefined)[]> {
if (!this.importer || !this.viewer) return [] if (!this.importer || !this.viewer) return []
const imported = await this.importer.import<T>(assetOrPath, options) const imported = await this.importer.import<T>(assetOrPath, options)
if (!imported) { if (!imported) {
return this.loadImported(imported, options) return this.loadImported(imported, options)
} }


async addAssetSingle<T extends ImportResult = ImportResult>(asset?: IAsset | string, options?: ImportAssetOptions): Promise<T|undefined> {
async addAssetSingle<T extends ImportResult = ImportResult>(asset?: string | IAsset | File, options?: ImportAssetOptions): Promise<T|undefined> {
return !asset ? undefined : (await this.addAsset<T>(asset, options))?.[0] return !asset ? undefined : (await this.addAsset<T>(asset, options))?.[0]
} }



+ 0
- 1
src/core/material/IMaterialUi.ts Vedi File

type: 'slider', type: 'slider',
bounds: [0, 1], bounds: [0, 1],
property: [material, 'transmission'], property: [material, 'transmission'],
limitedUi: true,
}, },
{ {
type: 'slider', type: 'slider',

+ 6
- 13
src/core/object/IObjectUi.ts Vedi File

type: 'folder', type: 'folder',
label: ()=>this.name || 'unnamed', label: ()=>this.name || 'unnamed',
expanded: true, expanded: true,
limitedUi: true,
onChange: (ev)=>{
if (!ev.config || ev.config.onChange) return
this.setDirty({uiChangeEvent: ev, needsUpdate: false, refreshUi: true})
},
children: [ children: [
{ {
type: 'checkbox', type: 'checkbox',
label: 'Visible', label: 'Visible',
property: [this, 'visible'], property: [this, 'visible'],
limitedUi: true,
}, },
{ {
type: 'button', type: 'button',
label: 'Pick/Focus',
label: 'Pick/Focus', // todo: move to the plugin that does the picking
value: ()=>{ value: ()=>{
// todo instead of dispatching, make a IObject3D.select function
this.dispatchEvent({type: 'select', ui: true, object: this, bubbleToParent: true, focusCamera: true}) this.dispatchEvent({type: 'select', ui: true, object: this, bubbleToParent: true, focusCamera: true})
}, },
}, },
{ {
type: 'button', type: 'button',
label: 'Pick Parent',
label: 'Pick Parent', // todo: move to the plugin that does the picking
hidden: ()=>!this.parent, hidden: ()=>!this.parent,
value: ()=>{ value: ()=>{
const parent = this.parent const parent = this.parent
label: 'Casts Shadow', label: 'Casts Shadow',
hidden: () => !(this as any).isMesh, hidden: () => !(this as any).isMesh,
property: [this, 'castShadow'], property: [this, 'castShadow'],
// onChange: this.setDirty,
}, },
{ {
type: 'checkbox', type: 'checkbox',
label: 'Receive Shadow', label: 'Receive Shadow',
hidden: () => !(this as any).isMesh, hidden: () => !(this as any).isMesh,
property: [this, 'receiveShadow'], property: [this, 'receiveShadow'],
// onChange: this.setDirty,
}, },
{ {
type: 'checkbox', type: 'checkbox',
label: 'Frustum culled', label: 'Frustum culled',
property: [this, 'frustumCulled'], property: [this, 'frustumCulled'],
// onChange: this.setDirty,
}, },
{ {
type: 'vec3', type: 'vec3',
label: 'Position', label: 'Position',
property: [this, 'position'], property: [this, 'position'],
limitedUi: true,
}, },
{ {
type: 'vec3', type: 'vec3',
label: 'Rotation', label: 'Rotation',
property: [this, 'rotation'], property: [this, 'rotation'],
limitedUi: true,
}, },
{ {
type: 'vec3', type: 'vec3',
type: 'input', type: 'input',
label: 'License/Credits', label: 'License/Credits',
property: [this.userData, 'license'], property: [this.userData, 'license'],
limitedUi: true,
} : {}, } : {},
], ],
} }
// if (object.children.length === 0) return { // if (object.children.length === 0) return {
// type: 'button', // type: 'button',
// label: 'Select ' + (object.name || 'unnamed'), // label: 'Select ' + (object.name || 'unnamed'),
// // limitedUi: true,
// value: dispatch, // value: dispatch,
// } // }
// return { // return {
// type: 'folder', // type: 'folder',
// label: 'Select ' + (object.name || 'unnamed'), // label: 'Select ' + (object.name || 'unnamed'),
// // limitedUi: true,
// children: object.children.map((child)=>makeHierarchyUi(child, root || object)), // children: object.children.map((child)=>makeHierarchyUi(child, root || object)),
// value: dispatch, // value: dispatch,
// onExpand: dispatch, // onExpand: dispatch,

+ 0
- 1
src/plugins/animation/GLTFAnimationPlugin.ts Vedi File



@uiButton('Play/Pause', (that: GLTFAnimationPlugin)=>({ @uiButton('Play/Pause', (that: GLTFAnimationPlugin)=>({
label:()=> that.animationState === 'playing' ? 'Pause' : 'Play', label:()=> that.animationState === 'playing' ? 'Pause' : 'Play',
limitedUi: true,
})) }))
playPauseAnimation() { playPauseAnimation() {
this._animationState === 'playing' ? this.pauseAnimation() : this.playAnimation() this._animationState === 'playing' ? this.pauseAnimation() : this.playAnimation()

+ 1
- 1
src/three/utils/index.ts Vedi File

export {uniform, matDefine} from './decorators' export {uniform, matDefine} from './decorators'
export {getEncodingComponents, getTexelEncoding, getTexelDecoding, getTexelDecoding2, getTexelDecodingFunction, getTexelEncodingFunction, getTextureColorSpaceFromMap} from './encoding' export {getEncodingComponents, getTexelEncoding, getTexelDecoding, getTexelDecoding2, getTexelDecodingFunction, getTexelEncodingFunction, getTextureColorSpaceFromMap} from './encoding'
export {generateUUID, toIndexedGeometry, isInScene, localToWorldQuaternion, worldToLocalQuaternion} from './misc' export {generateUUID, toIndexedGeometry, isInScene, localToWorldQuaternion, worldToLocalQuaternion} from './misc'
export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDataUrl, texImageToCanvas} from './texture'
export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDataUrl, textureToBlob, texImageToCanvas} from './texture'
export {threeConstMappings} from './const-mappings' export {threeConstMappings} from './const-mappings'
export {ObjectPicker} from './ObjectPicker' export {ObjectPicker} from './ObjectPicker'
export {SelectionWidget, BoxSelectionWidget} from './SelectionWidget' export {SelectionWidget, BoxSelectionWidget} from './SelectionWidget'

+ 9
- 0
src/three/utils/texture.ts Vedi File

export function textureToDataUrl(texture: Texture|DataTexture, maxWidth: number, flipY: boolean, mimeType?: string, quality?: number) { export function textureToDataUrl(texture: Texture|DataTexture, maxWidth: number, flipY: boolean, mimeType?: string, quality?: number) {
return textureToCanvas(texture, maxWidth, flipY).toDataURL(mimeType, quality) return textureToCanvas(texture, maxWidth, flipY).toDataURL(mimeType, quality)
} }
export async function textureToBlob(texture: Texture|DataTexture, maxWidth: number, flipY: boolean, mimeType?: string, quality?: number) {
const canvas = textureToCanvas(texture, maxWidth, flipY)
return new Promise<Blob>((resolve, reject) => {
canvas.toBlob(blob => {
if (blob) resolve(blob)
else reject(new Error('Failed to create blob'))
}, mimeType, quality)
})
}

+ 2
- 2
src/viewer/ThreeViewer.ts Vedi File

let container = options.container let container = options.container
if (container && !options.canvas) container.appendChild(this._canvas) if (container && !options.canvas) container.appendChild(this._canvas)
if (!container) container = this._canvas.parentElement ?? undefined if (!container) container = this._canvas.parentElement ?? undefined
if (!container) throw new Error('No container.')
if (!container) throw new Error('No container(or canvas).')
this._container = container this._container = container
this.setDirty = this.setDirty.bind(this) this.setDirty = this.setDirty.bind(this)
this._animationLoop = this._animationLoop.bind(this) this._animationLoop = this._animationLoop.bind(this)
* @param obj * @param obj
* @param options * @param options
*/ */
async load<T extends ImportResult = ImportResult>(obj: string | IAsset | null, options?: ImportAddOptions) {
async load<T extends ImportResult = ImportResult>(obj: string | IAsset | File | null, options?: ImportAddOptions) {
if (!obj) return if (!obj) return
return await this.assetManager.addAssetSingle<T>(obj, options) return await this.assetManager.addAssetSingle<T>(obj, options)
} }

Loading…
Annulla
Salva