Просмотр исходного кода

Add react, vue, svelte samples

master
Palash Bansal 2 лет назад
Родитель
Сommit
4766d64ca8
Аккаунт пользователя с таким Email не найден

+ 90
- 1
README.md Просмотреть файл

@@ -44,6 +44,9 @@ To make changes and run the example, click on the CodePen button on the top righ
- [Table of Contents](#table-of-contents)
- [Getting Started](#getting-started)
- [HTML/JS Quickstart (CDN)](#htmljs-quickstart-cdn)
- [React](#react)
- [Vue.js](#vuejs)
- [Svelte](#svelte)
- [NPM/YARN Package](#npmyarn)
- [Installation](#installation)
- [Loading a 3D Model](#loading-a-3d-model)
@@ -145,10 +148,96 @@ To make changes and run the example, click on the CodePen button on the top righ
})
</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.

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

### Installation

examples/html-sample/index.html → examples/html-js-sample/index.html Просмотреть файл

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

// Load an environment map
@@ -32,3 +33,4 @@
})
</script>
</body>
</html>

+ 56
- 0
examples/react-js-sample/index.html Просмотреть файл

@@ -0,0 +1,56 @@
<!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 Просмотреть файл

@@ -0,0 +1,60 @@
<!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 Просмотреть файл

@@ -0,0 +1,36 @@
<!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 Просмотреть файл

@@ -0,0 +1,41 @@
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 Просмотреть файл

@@ -0,0 +1,29 @@
<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 Просмотреть файл

@@ -0,0 +1,47 @@
<!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 Просмотреть файл

@@ -28,10 +28,12 @@
"es2020",
"esnext",
"dom"
]
],
"jsx": "react"
},
"include": [
"./**/*.ts"
"./**/*.ts",
"./**/*.tsx"
],
"exclude": [
"node_modules",

+ 57
- 0
examples/vue-html-sample/index.html Просмотреть файл

@@ -0,0 +1,57 @@
<!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 Просмотреть файл

@@ -0,0 +1,48 @@
<!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 Просмотреть файл

@@ -0,0 +1,36 @@
<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 Просмотреть файл

@@ -44,7 +44,7 @@
"typedoc": "^0.24.7",
"typescript": "^5.0.4",
"typescript-plugin-css-modules": "^5.0.1",
"uiconfig.js": "^0.0.7"
"uiconfig.js": "^0.0.8"
},
"optionalDependencies": {
"win-node-env": "^0.6.1"
@@ -9826,9 +9826,9 @@
}
},
"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
},
"node_modules/unbox-primitive": {
@@ -17501,9 +17501,9 @@
"dev": true
},
"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
},
"unbox-primitive": {

+ 3
- 2
package.json Просмотреть файл

@@ -6,6 +6,7 @@
"module": "dist/index.mjs",
"types": "src/index.ts",
"sources": "src/index.ts",
"browser": "dist/index.js",
"type": "module",
"scripts": {
"new:pack": "npm run prepare && clean-package && npm pack && clean-package restore",
@@ -97,7 +98,7 @@
"typedoc": "^0.24.7",
"typescript": "^5.0.4",
"typescript-plugin-css-modules": "^5.0.1",
"uiconfig.js": "^0.0.7",
"uiconfig.js": "^0.0.8",
"@rollup/plugin-replace": "^5.0.2",
"popmotion": "^11.0.5"
},
@@ -109,7 +110,7 @@
},
"//": {
"dependencies": {
"uiconfig.js": "^0.0.7",
"uiconfig.js": "^0.0.8",
"ts-browser-helpers": "^0.8.0",
"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",

+ 10
- 9
rollup.config.mjs Просмотреть файл

@@ -10,6 +10,7 @@ import {fileURLToPath} from 'url';
import postcss from 'rollup-plugin-postcss'
import glsl from "rollup-plugin-glsl"
import replace from "@rollup/plugin-replace";
import terser from "@rollup/plugin-terser";

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

+ 13
- 1
src/assetmanager/AssetImporter.ts Просмотреть файл

@@ -89,9 +89,10 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEvent, IAssetIm

// 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 (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 === 'string') return await this.importPath<T>(assetOrPath, options)
console.error('AssetImporter: Invalid asset or path', assetOrPath)
@@ -185,6 +186,17 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEvent, IAssetIm
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.
* @param files

+ 2
- 2
src/assetmanager/AssetManager.ts Просмотреть файл

@@ -192,7 +192,7 @@ export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult}

}

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 []
const imported = await this.importer.import<T>(assetOrPath, options)
if (!imported) {
@@ -261,7 +261,7 @@ export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult}
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]
}


+ 0
- 1
src/core/material/IMaterialUi.ts Просмотреть файл

@@ -385,7 +385,6 @@ export const iMaterialUI = {
type: 'slider',
bounds: [0, 1],
property: [material, 'transmission'],
limitedUi: true,
},
{
type: 'slider',

+ 6
- 13
src/core/object/IObjectUi.ts Просмотреть файл

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

+ 0
- 1
src/plugins/animation/GLTFAnimationPlugin.ts Просмотреть файл

@@ -145,7 +145,6 @@ export class GLTFAnimationPlugin extends AViewerPluginSync<'checkpointEnd'|'chec

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

+ 1
- 1
src/three/utils/index.ts Просмотреть файл

@@ -4,7 +4,7 @@ export {dataTextureFromColor, dataTextureFromVec4, halfFloatToRgbe} from './conv
export {uniform, matDefine} from './decorators'
export {getEncodingComponents, getTexelEncoding, getTexelDecoding, getTexelDecoding2, getTexelDecodingFunction, getTexelEncodingFunction, getTextureColorSpaceFromMap} from './encoding'
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 {ObjectPicker} from './ObjectPicker'
export {SelectionWidget, BoxSelectionWidget} from './SelectionWidget'

+ 9
- 0
src/three/utils/texture.ts Просмотреть файл

@@ -107,3 +107,12 @@ export function texImageToCanvas(image: TexImageSource, maxWidth: number, flipY
export function textureToDataUrl(texture: Texture|DataTexture, maxWidth: number, flipY: boolean, mimeType?: string, quality?: number) {
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 Просмотреть файл

@@ -308,7 +308,7 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
let container = options.container
if (container && !options.canvas) container.appendChild(this._canvas)
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.setDirty = this.setDirty.bind(this)
this._animationLoop = this._animationLoop.bind(this)
@@ -431,7 +431,7 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
* @param obj
* @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
return await this.assetManager.addAssetSingle<T>(obj, options)
}

Загрузка…
Отмена
Сохранить