Переглянути джерело

Serialization fixes, other minor fixes.

master
Palash Bansal 2 роки тому
джерело
коміт
09a80866da
Аккаунт користувача з таким Email не знайдено

+ 118
- 0
README.md Переглянути файл

@@ -33,6 +33,10 @@ Code samples and demos covering various usecases and test are present in the [ex

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

@@ -75,6 +79,8 @@ The viewer initializes with a Scene, Camera, Camera controls(Orbit Controls), se

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

@@ -98,3 +104,115 @@ Check out WebGi - Premium Photo-realistic 3D rendering framework and tools for w

## Contributing
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 Переглянути файл

@@ -69,8 +69,11 @@ export class AssetExporter extends EventDispatcher<BaseEvent, 'exporterCreate' |
this.dispatchEvent({type: 'exportFile', obj, state:'processing', exportOptions: 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
else {
const parser = this._getParser(ext)
@@ -128,7 +131,9 @@ export class AssetExporter extends EventDispatcher<BaseEvent, 'exporterCreate' |
if (obj.isWebGLMultipleRenderTargets) console.error('AssetExporter: WebGLMultipleRenderTargets export not supported')
else if (!obj.renderManager) return {obj, ext: 'exr'}
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 {
obj, ext: blob.ext, blob,
}

+ 11
- 3
src/assetmanager/AssetManager.ts Переглянути файл

@@ -198,9 +198,15 @@ export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult}

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]
let ret: T = Array.isArray(imported) ? [] : undefined as any

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

let r = obj

switch (obj.assetType) {
case 'material':
@@ -215,7 +221,7 @@ export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult}
case 'model':
case 'light':
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
case 'config':
if (options?.importConfig !== false) await this.viewer.importConfig(<ISerializedConfig>obj)
@@ -230,9 +236,11 @@ export class AssetManager extends EventDispatcher<BaseEvent&{data: ImportResult}
break
}
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 Переглянути файл

@@ -84,6 +84,9 @@ export interface ICamera<E extends ICameraEvent = ICameraEvent, ET extends ICame
removeControlsCtor(key: string): void;
refreshCameraControls(setDirty?: boolean): void

updateProjectionMatrix(): void
fov?: number

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


+ 4
- 1
src/core/IMaterial.ts Переглянути файл

@@ -1,4 +1,4 @@
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 {MaterialExtension} from '../materials'
import type {ChangeEvent, IUiConfigContainer} from 'uiconfig.js'
@@ -129,6 +129,9 @@ export interface IMaterial<E extends IMaterialEvent = IMaterialEvent, ET = IMate
transmissionMap?: ITexture | null
transmission?: number

color?: Color
wireframe?: boolean


isRawShaderMaterial?: boolean
isPhysicalMaterial?: boolean

+ 1
- 1
src/core/object/RootScene.ts Переглянути файл

@@ -213,7 +213,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I
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 {

+ 4
- 2
src/rendering/RenderManager.ts Переглянути файл

@@ -31,7 +31,7 @@ import {
IWebGLRenderer,
upgradeWebGLRenderer,
} 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 {generateUUID, textureDataToImageData} from '../three'
import {BlobExt, EXRExporter2} from '../assetmanager'
@@ -476,6 +476,7 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen

/**
* 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 mimeType
* @param quality
@@ -496,7 +497,8 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen
}

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()
return string
}

+ 1
- 1
src/three/utils/index.ts Переглянути файл

@@ -4,6 +4,6 @@ export {dataTextureFromColor, dataTextureFromVec4, halfFloatToRgbe} from './conv
export {uniform, matDefine} from './decorators'
export {getEncodingComponents, getTexelEncoding, getTexelDecoding, getTexelDecoding2, getTexelDecodingFunction, getTexelEncodingFunction, getTextureColorSpaceFromMap} from './encoding'
export {generateUUID, toIndexedGeometry} from './misc'
export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDataUrl, imageToCanvas} from './texture'
export {getTextureDataType, textureToCanvas, textureDataToImageData, textureToDataUrl, texImageToCanvas} from './texture'

// export {} from './constants'

+ 9
- 7
src/three/utils/texture.ts Переглянути файл

@@ -11,7 +11,7 @@ import {
WebGLRenderer,
} from 'three'
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 {
if (!renderer) return UnsignedByteType
@@ -51,15 +51,15 @@ export function textureDataToImageData(imgData: TextureImageData | ImageData | {
* @param flipY
* @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
if ((texture as DataTexture).isDataTexture) img = textureDataToImageData(texture.image, texture.colorSpace)
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
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))
@@ -77,6 +77,7 @@ export function imageToCanvas(image: TexImageSource, maxWidth: number, flipY = f

}

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

@@ -89,17 +90,18 @@ export function imageToCanvas(image: TexImageSource, maxWidth: number, flipY = f
console.error('textureToDataUrl: could not get temp canvas context')
ctx.putImageData(imageData, 0, 0)
} else {
tempCtx.putImageData(imageData, 0, 0)
tempCtx.putImageData(imageData, 0, 0) // for resize
ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height)
}
} else {
ctx.putImageData(imageData, 0, 0)
if (flipY) needsFlipY = true // because of putImageData
}

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

+ 28
- 1
src/utils/browser-helpers.ts Переглянути файл

@@ -1 +1,28 @@
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 Переглянути файл

@@ -7,4 +7,3 @@ export {ThreeSerialization, type SerializationMetaType, type SerializationResour
export {shaderReplaceString} from './shader-helpers'
export {makeGLBFile} from './gltf'

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

+ 10
- 10
src/utils/serialization.ts Переглянути файл

@@ -538,16 +538,16 @@ export function convertStringsToArrayBuffersInMeta(meta: SerializationMetaType)
})
}

export function getEmptyMeta(): SerializationMetaType {
export function getEmptyMeta(res?: Partial<SerializationResourcesType>): SerializationMetaType {
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: {},
}
}
@@ -761,8 +761,8 @@ export function metaToResources(meta?: SerializationMetaType): Partial<Serializa
}
export function metaFromResources(resources?: Partial<SerializationResourcesType>, viewer?: ThreeViewer): SerializationMetaType {
return {
...getEmptyMeta(),
...resources,
...getEmptyMeta(resources),
_context: {
assetManager: viewer?.assetManager,
assetImporter: viewer?.assetManager.importer,

+ 1
- 1
src/viewer/AViewerPlugin.ts Переглянути файл

@@ -79,7 +79,7 @@ export abstract class AViewerPlugin<T extends string = string, TViewer extends T
// }

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

set dirty(value: boolean) {

+ 1
- 1
src/viewer/ThreeViewer.ts Переглянути файл

@@ -955,7 +955,7 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
const obj = <RootSceneImportResult>imported
if (obj.importedViewerConfig && options?.importConfig !== false) await this.importConfig(obj.importedViewerConfig)
this._scene.loadModelRoot(obj, options)
return imported
return this._scene.modelRoot as T
}
this._scene.addObject(imported, options)
return imported

Завантаження…
Відмінити
Зберегти