Преглед изворни кода

Serialization fixes, other minor fixes.

master
Palash Bansal пре 3 година
родитељ
комит
09a80866da
No account linked to committer's email address

+ 118
- 0
README.md Прегледај датотеку



Try them: https://threepipe.org/examples/ Try them: https://threepipe.org/examples/


View the source code by pressing the code button on the top left of the example page.

To make changes and run the example, click on the CodePen button on the top right of the source code.



## Getting Started ## Getting Started




Check out the GLTF Load example to see it in action or to check the JS equivalent code: https://threepipe.org/examples/gltf-load/ Check out the GLTF Load example to see it in action or to check the JS equivalent code: https://threepipe.org/examples/gltf-load/


Check out the [Plugins](#plugins) section below to learn how to add additional functionality to the viewer.

## License ## License
The core framework([src](https://github.com/repalash/threepipe/tree/master/src), [dist](https://github.com/repalash/threepipe/tree/master/dist), [examples](https://github.com/repalash/threepipe/tree/master/examples) folders) and any [plugins](https://github.com/repalash/threepipe/tree/master/plugins) without a separate license are under the [Apache 2.0 license](https://github.com/repalash/threepipe/tree/master/LICENSE). The core framework([src](https://github.com/repalash/threepipe/tree/master/src), [dist](https://github.com/repalash/threepipe/tree/master/dist), [examples](https://github.com/repalash/threepipe/tree/master/examples) folders) and any [plugins](https://github.com/repalash/threepipe/tree/master/plugins) without a separate license are under the [Apache 2.0 license](https://github.com/repalash/threepipe/tree/master/LICENSE).




## Contributing ## Contributing
Contributions to ThreePipe are welcome and encouraged! Feel free to open issues and pull requests on the GitHub repository. Contributions to ThreePipe are welcome and encouraged! Feel free to open issues and pull requests on the GitHub repository.

## File Formats

ThreePipe Asset Manager supports the import of following file formats out of the box:
* Models:
* gltf, glb
* obj, mtl
* fbx
* drc
* Materials
* mat, pmat, bmat (json based), registered material template slugs
* Images
* webp, png, jpeg, jpg, svg, ico
* hdr, exr
* ktx2, ktx, dds, pvr
* Misc
* json, vjson
* zip
* txt

Additional formats can be added by plugins:
* Models
* 3dm - Using [Rhino3dmLoadPlugin](#Rhino3dmLoadPlugin)

## Plugins

ThreePipe has a simple plugin system that allows you to easily add new features to the viewer. Plugins can be added to the viewer using the `addPlugin` and `addPluginSync` methods. The plugin system is designed to be modular and extensible. Plugins can be added to the viewer at any time and can be removed using the `removePlugin` and `removePluginSync` methods.

### DepthBufferPlugin

todo: image

Example: https://threepipe.org/examples/#depth-buffer-plugin/

Source Code: [src/plugins/pipeline/DepthBufferPlugin.ts](./src/plugins/pipeline/DepthBufferPlugin.ts)

Depth Buffer Plugin adds a pre-render pass to the render manager and renders a depth buffer to a target. The render target can be accessed by other plugins throughout the rendering pipeline to create effects like depth of field, SSAO, SSR, etc.

```typescript
import {ThreeViewer, DepthBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const depthPlugin = viewer.addPluginSync(new DepthBufferPlugin(HalfFloatType))

const depthTarget = depthPlugin.target;

// Use the depth target by accesing `depthTarget.texture`.
```

The depth values are based on camera near far values, which are controlled automatically by the viewer. To manually specify near, far values and limits, it can be set in the camera userData. Check the [example](https://threepipe.org/examples/#depth-buffer-plugin/) for more details.

### NormalBufferPlugin

todo: image

Example: https://threepipe.org/examples/#normal-buffer-plugin/

Source Code: [src/plugins/pipeline/NormalBufferPlugin.ts](./src/plugins/pipeline/NormalBufferPlugin.ts)

Normal Buffer Plugin adds a pre-render pass to the render manager and renders a normal buffer to a target. The render target can be accessed by other plugins throughout the rendering pipeline to create effects like SSAO, SSR, etc.

Note: Use [`DepthNormalBufferPlugin`](#DepthNormalBufferPlugin) if using both `DepthBufferPlugin` and `NormalBufferPlugin` to render both depth and normal buffers in a single pass.

```typescript
import {ThreeViewer, NormalBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const normalPlugin = viewer.addPluginSync(new NormalBufferPlugin())

const normalTarget = normalPlugin.target;

// Use the normal target by accessing `normalTarget.texture`.
```


### DepthNormalBufferPlugin

todo


### RenderTargetPreviewPlugin

todo: image

Example: https://threepipe.org/examples/#render-target-preview/

Source Code: [src/plugins/ui/RenderTargetPreviewPlugin.ts](./src/plugins/ui/RenderTargetPreviewPlugin.ts)

RenderTargetPreviewPlugin is a useful development and debugging plugin that renders any registered render-target to the screen in small collapsable panels.

```typescript
import {ThreeViewer, RenderTargetPreviewPlugin, NormalBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const normalPlugin = viewer.addPluginSync(new NormalBufferPlugin(HalfFloatType))

const previewPlugin = viewer.addPluginSync(new RenderTargetPreviewPlugin())

// Show the normal buffer in a panel
previewPlugin.addTarget(()=>normalPlugin.target, 'normal', false, false)
```

### Rhino3dmLoadPlugin

Example: https://threepipe.org/examples/#rhino3dm-load/

Source Code: [src/plugins/import/Rhino3dmLoadPlugin.ts](./src/plugins/import/Rhino3dmLoadPlugin.ts)

Adds support for loading .3dm files generated by [Rhino 3D](https://www.rhino3d.com/). This plugin includes some changes with how 3dm files are loaded in three.js. The changes are around loading layer and primitive properties when set as reference in the 3dm files.

+ 8
- 3
src/assetmanager/AssetExporter.ts Прегледај датотеку

this.dispatchEvent({type: 'exportFile', obj, state:'processing', exportOptions: options}) this.dispatchEvent({type: 'exportFile', obj, state:'processing', exportOptions: options})


const processed = await this.processBeforeExport(obj, options) const processed = await this.processBeforeExport(obj, options)
const ext = options.exportExt ?? processed?.typeExt ?? processed?.ext
if (!processed || !ext) throw new Error(`Unable to preprocess before export ${ext}`)
const ext = options.exportExt || processed?.typeExt || processed?.ext
if (!processed || !ext) {
console.error(processed, options, obj)
throw new Error(`Unable to preprocess before export ${ext}`)
}
if (processed.blob) res = processed.blob if (processed.blob) res = processed.blob
else { else {
const parser = this._getParser(ext) const parser = this._getParser(ext)
if (obj.isWebGLMultipleRenderTargets) console.error('AssetExporter: WebGLMultipleRenderTargets export not supported') if (obj.isWebGLMultipleRenderTargets) console.error('AssetExporter: WebGLMultipleRenderTargets export not supported')
else if (!obj.renderManager) return {obj, ext: 'exr'} else if (!obj.renderManager) return {obj, ext: 'exr'}
else { else {
const blob = obj.renderManager.exportRenderTarget(obj as WebGLRenderTarget, 'auto')
const blob = obj.renderManager.exportRenderTarget(obj as WebGLRenderTarget,
(options.exportExt || '' !== '') && options.exportExt !== 'auto' ?
options.exportExt === 'exr' ? 'image/x-exr' : 'image/' + options.exportExt : 'auto')
return { return {
obj, ext: blob.ext, blob, obj, ext: blob.ext, blob,
} }

+ 11
- 3
src/assetmanager/AssetManager.ts Прегледај датотеку



async loadImported<T extends ValOrArr<ImportResult|undefined> = ImportResult>(imported: T, {autoSetEnvironment = true, autoSetBackground = false, ...options}: AddAssetOptions = {}): Promise<T | never[]> { async loadImported<T extends ValOrArr<ImportResult|undefined> = ImportResult>(imported: T, {autoSetEnvironment = true, autoSetBackground = false, ...options}: AddAssetOptions = {}): Promise<T | never[]> {
const arr: (ImportResult|undefined)[] = Array.isArray(imported) ? imported : [imported] const arr: (ImportResult|undefined)[] = Array.isArray(imported) ? imported : [imported]
let ret: T = Array.isArray(imported) ? [] : undefined as any


for (const obj of arr) { for (const obj of arr) {
if (!obj) continue
if (!obj) {
if (Array.isArray(ret)) ret.push(undefined)
continue
}

let r = obj


switch (obj.assetType) { switch (obj.assetType) {
case 'material': case 'material':
case 'model': case 'model':
case 'light': case 'light':
case 'camera': case 'camera':
await this.viewer.addSceneObject(<IObject3D|RootSceneImportResult>obj, options) // todo update references in scene update event
r = await this.viewer.addSceneObject(<IObject3D|RootSceneImportResult>obj, options) // todo update references in scene update event
break break
case 'config': case 'config':
if (options?.importConfig !== false) await this.viewer.importConfig(<ISerializedConfig>obj) if (options?.importConfig !== false) await this.viewer.importConfig(<ISerializedConfig>obj)
break break
} }
this.dispatchEvent({type: 'loadAsset', data: obj}) this.dispatchEvent({type: 'loadAsset', data: obj})
if (Array.isArray(ret)) ret.push(r)
else ret = r as T
} }


return imported || []
return ret || []
} }


/** /**

+ 3
- 0
src/core/ICamera.ts Прегледај датотеку

removeControlsCtor(key: string): void; removeControlsCtor(key: string): void;
refreshCameraControls(setDirty?: boolean): void refreshCameraControls(setDirty?: boolean): void


updateProjectionMatrix(): void
fov?: number

// region inherited type fixes // region inherited type fixes
// re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936 // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936



+ 4
- 1
src/core/IMaterial.ts Прегледај датотеку

import type {Event, IUniform, Material, MaterialParameters, Shader} from 'three'
import type {Color, Event, IUniform, Material, MaterialParameters, Shader} from 'three'
import type {IDisposable, IJSONSerializable} from 'ts-browser-helpers' import type {IDisposable, IJSONSerializable} from 'ts-browser-helpers'
import type {MaterialExtension} from '../materials' import type {MaterialExtension} from '../materials'
import type {ChangeEvent, IUiConfigContainer} from 'uiconfig.js' import type {ChangeEvent, IUiConfigContainer} from 'uiconfig.js'
transmissionMap?: ITexture | null transmissionMap?: ITexture | null
transmission?: number transmission?: number


color?: Color
wireframe?: boolean



isRawShaderMaterial?: boolean isRawShaderMaterial?: boolean
isPhysicalMaterial?: boolean isPhysicalMaterial?: boolean

+ 1
- 1
src/core/object/RootScene.ts Прегледај датотеку

this.modelRoot.animations.push(animation) this.modelRoot.animations.push(animation)
} }
} }
obj.children.forEach(c=>this.addObject(c, options))
return obj.children.map(c=>this.addObject(c, options))
} }


private _addObject3D(model: IObject3D|null, {autoCenter = false, autoScale = false, autoScaleRadius = 2., addToRoot = false, license}: AddObjectOptions = {}): void { private _addObject3D(model: IObject3D|null, {autoCenter = false, autoScale = false, autoScaleRadius = 2., addToRoot = false, license}: AddObjectOptions = {}): void {

+ 4
- 2
src/rendering/RenderManager.ts Прегледај датотеку

IWebGLRenderer, IWebGLRenderer,
upgradeWebGLRenderer, upgradeWebGLRenderer,
} from '../core' } from '../core'
import {base64ToArrayBuffer, Class, onChange2, serializable, serialize, ValOrArr} from 'ts-browser-helpers'
import {base64ToArrayBuffer, canvasFlipY, Class, onChange2, serializable, serialize, ValOrArr} from 'ts-browser-helpers'
import {uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' import {uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js'
import {generateUUID, textureDataToImageData} from '../three' import {generateUUID, textureDataToImageData} from '../three'
import {BlobExt, EXRExporter2} from '../assetmanager' import {BlobExt, EXRExporter2} from '../assetmanager'


/** /**
* Converts a render target to a png/jpeg data url string. * Converts a render target to a png/jpeg data url string.
* Note: this will clamp the values to [0, 1] and converts to srgb for float and half-float render targets.
* @param target * @param target
* @param mimeType * @param mimeType
* @param quality * @param quality
} }


ctx.putImageData(imageData, 0, 0) ctx.putImageData(imageData, 0, 0)
const string = canvas.toDataURL(mimeType, quality)

const string = (target.texture.flipY ? canvas : canvasFlipY(canvas)).toDataURL(mimeType, quality) // intentionally inverted ternary
canvas.remove() canvas.remove()
return string return string
} }

+ 1
- 1
src/three/utils/index.ts Прегледај датотеку

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} from './misc' export {generateUUID, toIndexedGeometry} from './misc'
export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDataUrl, imageToCanvas} from './texture'
export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDataUrl, texImageToCanvas} from './texture'


// export {} from './constants' // export {} from './constants'

+ 9
- 7
src/three/utils/texture.ts Прегледај датотеку

WebGLRenderer, WebGLRenderer,
} from 'three' } from 'three'
import {TextureImageData} from 'three/src/textures/types' import {TextureImageData} from 'three/src/textures/types'
import {LinearToSRGB} from 'ts-browser-helpers'
import {canvasFlipY, LinearToSRGB} from 'ts-browser-helpers'


export function getTextureDataType(renderer?: WebGLRenderer): TextureDataType { export function getTextureDataType(renderer?: WebGLRenderer): TextureDataType {
if (!renderer) return UnsignedByteType if (!renderer) return UnsignedByteType
* @param flipY * @param flipY
* @param canvas * @param canvas
*/ */
export function textureToCanvas(texture: Texture|DataTexture, maxWidth: number, flipY = false, canvas?: HTMLCanvasElement) {
export function textureToCanvas(texture: Texture|DataTexture, maxWidth: number, flipY = false) {
let img let img
if ((texture as DataTexture).isDataTexture) img = textureDataToImageData(texture.image, texture.colorSpace) if ((texture as DataTexture).isDataTexture) img = textureDataToImageData(texture.image, texture.colorSpace)
else img = texture.image else img = texture.image
return imageToCanvas(img, maxWidth, flipY, canvas)
return texImageToCanvas(img, maxWidth, flipY)
} }


export function imageToCanvas(image: TexImageSource, maxWidth: number, flipY = false, canvas?: HTMLCanvasElement) {
canvas = canvas || document.createElement('canvas')
export function texImageToCanvas(image: TexImageSource, maxWidth: number, flipY = false) {
const canvas = document.createElement('canvas')
// resize it to the size of our image // resize it to the size of our image
canvas.width = Math.min(maxWidth, image.width as number) canvas.width = Math.min(maxWidth, image.width as number)
canvas.height = Math.floor(1.0 + canvas.width * (image.height as number) / (image.width as number)) canvas.height = Math.floor(1.0 + canvas.width * (image.height as number) / (image.width as number))


} }


let needsFlipY = false
if ((image as ImageData).data !== undefined) { // THREE.DataTexture if ((image as ImageData).data !== undefined) { // THREE.DataTexture
const imageData = image as ImageData const imageData = image as ImageData


console.error('textureToDataUrl: could not get temp canvas context') console.error('textureToDataUrl: could not get temp canvas context')
ctx.putImageData(imageData, 0, 0) ctx.putImageData(imageData, 0, 0)
} else { } else {
tempCtx.putImageData(imageData, 0, 0)
tempCtx.putImageData(imageData, 0, 0) // for resize
ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height) ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height)
} }
} else { } else {
ctx.putImageData(imageData, 0, 0) ctx.putImageData(imageData, 0, 0)
if (flipY) needsFlipY = true // because of putImageData
} }


} else { } else {
ctx.drawImage(image as any, 0, 0, canvas.width, canvas.height) ctx.drawImage(image as any, 0, 0, canvas.width, canvas.height)
} }
return canvas
return !needsFlipY ? canvas : canvasFlipY(canvas)
} }


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

+ 28
- 1
src/utils/browser-helpers.ts Прегледај датотеку

export {downloadBlob, blobToDataURL} from 'ts-browser-helpers'
export type {IEvent, IEventDispatcher} from 'ts-browser-helpers'
export type {ImageCanvasOptions} from 'ts-browser-helpers'
export type {AnyFunction, AnyOptions, Class, IDisposable, IJSONSerializable, PartialPick, PartialRecord, StringKeyOf, Fof, ValOrFunc, ValOrArr, ValOrArrOp} from 'ts-browser-helpers'
export type {Serializer} from 'ts-browser-helpers'
export {PointerDragHelper} from 'ts-browser-helpers'
export {Damper} from 'ts-browser-helpers'
export {SimpleEventDispatcher} from 'ts-browser-helpers'
export {createCanvasElement, createDiv, createImage, createStyles, createScriptFromURL} from 'ts-browser-helpers'
export {TYPED_ARRAYS, arrayBufferToBase64, base64ToArrayBuffer, getTypedArray} from 'ts-browser-helpers'
export {escapeRegExp, getFilenameFromPath, parseFileExtension, replaceAll, toTitleCase, longestCommonPrefix} from 'ts-browser-helpers'
export {prettyScrollbar} from 'ts-browser-helpers'
export {blobToDataURL, downloadBlob, downloadFile, uploadFile, mobileAndTabletCheck} from 'ts-browser-helpers'
export {LinearToSRGB, SRGBToLinear, colorToDataUrl} from 'ts-browser-helpers'
export {onChange, onChange2, onChange3, serialize, serializable} from 'ts-browser-helpers'
export {aesGcmDecrypt, aesGcmEncrypt} from 'ts-browser-helpers'
export {verifyPermission, writeFile, getFileHandle, getNewFileHandle, readFile} from 'ts-browser-helpers'
export {embedUrlRefs, htmlToCanvas, htmlToPng, htmlToSvg} from 'ts-browser-helpers'
export {imageToCanvas, imageBitmapToBase64, imageUrlToImageData, imageDataToCanvas, isWebpExportSupported, canvasFlipY} from 'ts-browser-helpers'
export {absMax, clearBit, updateBit} from 'ts-browser-helpers'
export {includesAll} from 'ts-browser-helpers'
export {copyProps, getOrCall, getPropertyDescriptor, isPropertyWritable, safeSetProperty} from 'ts-browser-helpers'
export {deepAccessObject, getKeyByValue, objectHasOwn} from 'ts-browser-helpers'
export {makeColorSvg, makeTextSvg, makeColorSvgCircle, svgToCanvas, svgToPng} from 'ts-browser-helpers'
export {timeout, now} from 'ts-browser-helpers'
export {pathJoin, getUrlQueryParam, setUrlQueryParam, remoteWorkerURL} from 'ts-browser-helpers'
export {css, glsl, html, svgUrl} from 'ts-browser-helpers'
export {Serialization} from 'ts-browser-helpers'


+ 0
- 1
src/utils/index.ts Прегледај датотеку

export {shaderReplaceString} from './shader-helpers' export {shaderReplaceString} from './shader-helpers'
export {makeGLBFile} from './gltf' export {makeGLBFile} from './gltf'


export {serialize, serializable, Serialization} from 'ts-browser-helpers'

+ 10
- 10
src/utils/serialization.ts Прегледај датотеку

}) })
} }


export function getEmptyMeta(): SerializationMetaType {
export function getEmptyMeta(res?: Partial<SerializationResourcesType>): SerializationMetaType {
return { // see Object3D.js toJSON for more details return { // see Object3D.js toJSON for more details
geometries: {},
materials: {},
textures: {},
images: {},
shapes: [],
skeletons: {},
animations: [],
extras: {},
geometries: {...res?.geometries},
materials: {...res?.materials},
textures: {...res?.textures},
images: {...res?.images},
shapes: {...res?.shapes},
skeletons: {...res?.skeletons},
animations: {...res?.animations},
extras: {...res?.extras},
_context: {}, _context: {},
} }
} }
} }
export function metaFromResources(resources?: Partial<SerializationResourcesType>, viewer?: ThreeViewer): SerializationMetaType { export function metaFromResources(resources?: Partial<SerializationResourcesType>, viewer?: ThreeViewer): SerializationMetaType {
return { return {
...getEmptyMeta(),
...resources, ...resources,
...getEmptyMeta(resources),
_context: { _context: {
assetManager: viewer?.assetManager, assetManager: viewer?.assetManager,
assetImporter: viewer?.assetManager.importer, assetImporter: viewer?.assetManager.importer,

+ 1
- 1
src/viewer/AViewerPlugin.ts Прегледај датотеку

// } // }


get dirty(): boolean { get dirty(): boolean {
return this._dirty
return this.enabled && this._dirty
} }


set dirty(value: boolean) { set dirty(value: boolean) {

+ 1
- 1
src/viewer/ThreeViewer.ts Прегледај датотеку

const obj = <RootSceneImportResult>imported const obj = <RootSceneImportResult>imported
if (obj.importedViewerConfig && options?.importConfig !== false) await this.importConfig(obj.importedViewerConfig) if (obj.importedViewerConfig && options?.importConfig !== false) await this.importConfig(obj.importedViewerConfig)
this._scene.loadModelRoot(obj, options) this._scene.loadModelRoot(obj, options)
return imported
return this._scene.modelRoot as T
} }
this._scene.addObject(imported, options) this._scene.addObject(imported, options)
return imported return imported

Loading…
Откажи
Сачувај