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

Remove File.__loadedAsset caching, accept File objects while setting env, background maps in viewer, add file-load example

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

+ 18
- 14
examples/dispose-reimport-test/index.html Просмотреть файл

@@ -31,31 +31,33 @@

const viewer = new ThreeViewer({canvas: document.getElementById('mcanvas')})

async function init() {
async function init(loadAsFile = true) {
const url = 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf'
let file
if(loadAsFile){
file = new File([await (await fetch(url)).blob()], url)
}
const load = async ()=> {
return viewer.load(file ?? url, {
autoCenter: true,
autoScale: true,
})
}

await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
viewer.scene.background = viewer.scene.environment
const model = await viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
})
const model = await load()
console.log(model.uuid)
await timeout(500)
viewer.scene.clearSceneModels(false)
await timeout(500)
const model2 = await viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
})
const model2 = await load()
console.log(model2.uuid)
if(model !== model2) throw new Error('Error in Test - Models should be the same after clearing scene models');
await timeout(500)
viewer.scene.disposeSceneModels()
await timeout(500)
const model3 = await viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
})
const model3 = await load()
console.log(model3.uuid)
if(model2 === model3) throw new Error('Error in Test - Models should not be the same after disposing scene models');
await timeout(500)
@@ -64,10 +66,12 @@
viewer.scene.addObject(model.translateX(-0.5)) // add back cleared model
viewer.scene.addObject(model2.translateX(-0.5)) // again, shouldn't change anything
viewer.scene.addObject(model3.translateX(1)) // add back disposed model
await timeout(1000)
viewer.scene.disposeSceneModels()
}

_testStart()
init().finally(_testFinish)
init(false).then(()=>init(true)).finally(_testFinish)
</script>
</head>
<body>

+ 36
- 0
examples/file-load/index.html Просмотреть файл

@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File/Blob Load</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

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

</script>
<style id="example-style">
html, body, #canvas-container, #mcanvas {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>
<script type="module" src="../examples-utils/global-loading.mjs"></script>
<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>

+ 41
- 0
examples/file-load/script.ts Просмотреть файл

@@ -0,0 +1,41 @@
import {_testFinish, _testStart, LoadingScreenPlugin, ThreeViewer} from 'threepipe'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: true,
dropzone: {
addOptions: {
disposeSceneObjects: true,
autoSetEnvironment: true, // when hdr is dropped
autoSetBackground: true,
},
},
})

viewer.addPluginSync(LoadingScreenPlugin)

const env = 'https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr'
const url = 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf'

const responses = await Promise.all([
fetch(env),
fetch(url),
])
const envFile = new File([await responses[0].blob()], 'venice_sunset_1k.hdr')
await viewer.setEnvironmentMap(envFile, {
setBackground: true,
})
const blob = await responses[1].blob()
const file = new File([blob], url) // Set the file name to the URL, so that internal textures can be resolved correctly from the base path
const result = await viewer.load(file, {
autoCenter: true,
autoScale: true,
})
console.log(result)

}

_testStart()
init().finally(_testFinish)

+ 2
- 1
examples/index.html Просмотреть файл

@@ -454,6 +454,7 @@
<li><a href="./slippy-map-tiles/">Slippy Map Tiles Load<br/>(OpenStreetMap ZYX) </a></li>
<li><a href="./ogc-tiles-google-maps/">Google Maps Globe (OGC Tiles)</a></li>
<li><a href="./ogc-tiles-google-maps-3d/">Google Maps 3D (OGC Tiles)</a></li>
<li><a href="./file-load/">File/Blob load</a></li>
</ul>
<h2 class="category">Export</h2>
<ul>
@@ -546,7 +547,7 @@
<li><a href="./uint8-rgbm-hdr-test/">Uint8 RGBM HDR Test </a></li>
<li><a href="./half-float-hdr-test/">Half-float HDR Test </a></li>
<li><a href="./sphere-rgbm-test/">RGBM Test </a></li>
<li><a href="./sphere-half-float-test/">Half Float Test </a></li>
<li><a href="./sphere-half-float-test/">Half-float Test </a></li>
<li><a href="./sphere-msaa-test/">MSAA Test </a></li>
<li><a href="./z-prepass/">Z-Prepass Test </a></li>
<li><a href="./import-test/">Import Test</a></li>

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

@@ -6,14 +6,14 @@ import {
ImportFilesOptions,
ImportResult,
LoadFileOptions,
ProcessRawOptions, RootSceneImportResult,
ProcessRawOptions,
RootSceneImportResult,
} from './IAssetImporter'
import {IAsset, IFile} from './IAsset'
import {IImporter, ILoader} from './IImporter'
import {Importer} from './Importer'
import {SimpleJSONLoader} from './import'
import {parseFileExtension} from 'ts-browser-helpers'
import {IObject3D} from '../core'

// export type IAssetImporterEvent = Event&{
// type: IAssetImporterEventTypes,
@@ -126,7 +126,7 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple
console.error('AssetImporter: Invalid asset or path', assetOrPath)
return []
}
async importSingle<T extends ImportResult|undefined = ImportResult>(asset?: IAsset | string, options?: ImportAssetOptions): Promise<T|undefined> {
async importSingle<T extends ImportResult|undefined = ImportResult>(asset?: string | IAsset | File, options?: ImportAssetOptions): Promise<T|undefined> {
return (await this.import<T>(asset, options))?.[0]
}

@@ -283,7 +283,7 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple

// load a single file
private async _loadFile(path: string, file?: IFile, options: LoadFileOptions = {}, onDownloadProgress?: (e: ProgressEvent)=>void): Promise<ImportResult | ImportResult[] | undefined> {
if (file?.__loadedAsset) return file.__loadedAsset
// if (file?.__loadedAsset) return file.__loadedAsset

this.dispatchEvent({type: 'importFile', path, state:'downloading', progress: 0})
let res: ImportResult | ImportResult[] | undefined
@@ -343,21 +343,21 @@ export class AssetImporter extends EventDispatcher<IAssetImporterEventMap> imple
return []
}
this.dispatchEvent({type: 'importFile', path, state: 'done'}) // todo: do this after processing?
if (file) {
file.__loadedAsset = res
// todo: recheck below code after dispose logic change
// Clear the reference __loadedAsset when any one asset is disposed.
// it's a bit hacky to do this here, but it works for now. todo: move to a better place
let ress: any[] = []
if (Array.isArray(res)) ress = res.flat(2)
else if ((<RootSceneImportResult>res)?.userData?.rootSceneModelRoot) ress.push(...(<IObject3D>res).children)
else ress.push(res)
for (const r of ress) r?.addEventListener?.('dispose', () => file.__loadedAsset = undefined)
}
// if (file) {
// file.__loadedAsset = res
//
//
// // todo: recheck below code after dispose logic change
//
// // Clear the reference __loadedAsset when any one asset is disposed.
// // it's a bit hacky to do this here, but it works for now. todo: move to a better place
// let ress: any[] = []
// if (Array.isArray(res)) ress = res.flat(2)
// else if ((<RootSceneImportResult>res)?.userData?.rootSceneModelRoot) ress.push(...(<IObject3D>res).children)
// else ress.push(res)
// for (const r of ress) r?.addEventListener?.('dispose', () => file.__loadedAsset = undefined)
//
// }
if (res && typeof res === 'object' && !Array.isArray(res)) {
res.__rootPath = path
const f = file || this._fileDatabase.get(path)

+ 4
- 4
src/viewer/ThreeViewer.ts Просмотреть файл

@@ -513,7 +513,7 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView
* @param obj
* @param options
*/
async import<T extends ImportResult = ImportResult>(obj: string | IAsset | null, options?: ImportAddOptions) {
async import<T extends ImportResult = ImportResult>(obj: string | IAsset | File | null, options?: ImportAddOptions) {
if (!obj) return
return await this.assetManager.importer.importSingle<T>(obj, options)
}
@@ -524,8 +524,8 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView
* @param setBackground - Set the background image of the scene from the same map.
* @param options - Options for importing the asset. See {@link ImportAssetOptions}
*/
async setEnvironmentMap(map: string | IAsset | null | ITexture | undefined, {setBackground = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<ITexture | null> {
this._scene.environment = map && !(<ITexture>map).isTexture ? await this.assetManager.importer.importSingle<ITexture>(map as string|IAsset, options) || null : <ITexture>map || null
async setEnvironmentMap(map: string | IAsset | null | ITexture | File | undefined, {setBackground = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<ITexture | null> {
this._scene.environment = map && !(<ITexture>map).isTexture ? await this.assetManager.importer.importSingle<ITexture>(map as string|IAsset|File, options) || null : <ITexture>map || null
if (setBackground) return this.setBackgroundMap(this._scene.environment)
return this._scene.environment
}
@@ -536,7 +536,7 @@ export class ThreeViewer extends EventDispatcher<Record<IViewerEventTypes, IView
* @param setEnvironment - Set the environment map of the scene from the same map.
* @param options - Options for importing the asset. See {@link ImportAssetOptions}
*/
async setBackgroundMap(map: string | IAsset | null | ITexture | undefined, {setEnvironment = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<ITexture | null> {
async setBackgroundMap(map: string | IAsset | null | ITexture | File | undefined, {setEnvironment = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<ITexture | null> {
this._scene.background = map && !(<ITexture>map).isTexture ? await this.assetManager.importer.importSingle<ITexture>(map as string|IAsset, options) || null : <ITexture>map || null
if (setEnvironment) return this.setEnvironmentMap(this._scene.background)
return this._scene.background

+ 24
- 3
website/guide/loading-files.md Просмотреть файл

@@ -14,8 +14,12 @@ ThreePipe uses the [AssetManager](https://threepipe.org/docs/classes/AssetManage
The AssetManager has support for loading files from URLs, local files and data URLs.
The AssetManager also adds support for loading files from a zip archive. The zip files are automatically unzipped, and the files are loaded from the zip archive.

[`viewer.load()`](https://threepipe.org/docs/classes/ThreeViewer.html#load) is a simple wrapper for loading files from the AssetManager.
It automatically adds the loaded object to the scene(if possible) and returns a promise that resolves to the loaded object, the materials are also automatically registered to the material manager.
[`viewer.load()`](https://threepipe.org/docs/classes/ThreeViewer.html#load) is a simple helper for loading files from the AssetManager. It accepts urls, local `File`/`Blob`, data URLs, zip files, `IAsset`.
It automatically adds the loaded object to the scene(if possible) and returns a promise that resolves to the loaded object/texture/material/etc, the materials are also automatically registered to the material manager.

```typescript
const object = await viewer.load<IObject3D>('https://example.com/file.glb')
```

::: details AssetManager
AssetManager internally uses [AssetImporter](https://threepipe.org/docs/classes/AssetImporter.html), which provides an API for managing three.js [LoadingManager](https://threejs.org/docs/#api/en/loaders/LoadingManager) and adding and registering loaders for different file types.
@@ -185,7 +189,8 @@ Local files can be loaded using the `viewer.load` method by passing a [IAsset](h

```typescript
const file: File|Blob = fileObject // create a new file, blob or get from input element
const text = await viewer.load<IObject>({
const res = await viewer.load(file)
const res2 = await viewer.load<IObject>({
// a path/name is required to determine the proper importer by extension. `file.name` can also be used if available
path: 'file.glb',
file
@@ -195,6 +200,22 @@ The same can be done for any file type.

To load a `Map` of files(like when multiple files are dragged and dropped on the webpage) with internal references to other files, use `viewer.assetManager.importer.importFiles` method. Check the source for [DropzonePlugin](../plugin/DropzonePlugin) for an example.

## File/Blob with URL

Files with references can be loaded with path, by setting the path as the name of the `File` object, or by specifying the `path` parameter.

```typescript
const url = 'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf'
const blob = await fetch(url).then(res => res.blob())
const file = new File([blob], url) // Set the file name to the URL, so that internal textures can be resolved correctly from the base path
const result = await viewer.load(file, {
autoCenter: true,
autoScale: true,
})
```

Check the complete example - [file-load](https://threepipe.org/examples/#file-load/)

## Background, Environment maps

The background and environment maps can be set using the `viewer.setBackgroundMap` and `viewer.setEnvironmentMap` methods respectively. These accept both loaded textures from `viewer.load` and direct URLs. Files can be of any image format including hdr, exr.

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