Parcourir la source

Add HDRiGroundPlugin and example, add CameraViewPluginOptions for CameraViewPlugin constructor options, implement ThreeViewer.fitToView, PickingPlugin.focusObject, add some three addons exports, Add plugins, load, onLoad in ThreeViewer constructor options. minor changes

master
Palash Bansal il y a 2 ans
Parent
révision
362007f9bb
Aucun compte lié à l'adresse e-mail de l'auteur

+ 57
- 5
README.md Voir le fichier

- [RenderTargetPreviewPlugin](#rendertargetpreviewplugin) - Preview any render target in a UI panel over the canvas - [RenderTargetPreviewPlugin](#rendertargetpreviewplugin) - Preview any render target in a UI panel over the canvas
- [GeometryUVPreviewPlugin](#geometryuvpreviewplugin) - Preview UVs of any geometry in a UI panel over the canvas - [GeometryUVPreviewPlugin](#geometryuvpreviewplugin) - Preview UVs of any geometry in a UI panel over the canvas
- [FrameFadePlugin](#framefadeplugin) - Post-render pass to smoothly fade to a new rendered frame over time - [FrameFadePlugin](#framefadeplugin) - Post-render pass to smoothly fade to a new rendered frame over time
- [HDRiGroundPlugin](#hdrigroundplugin) - Add support for ground projected hdri/skybox to the webgl background shader.
- [Rhino3dmLoadPlugin](#rhino3dmloadplugin) - Add support for loading .3dm files - [Rhino3dmLoadPlugin](#rhino3dmloadplugin) - Add support for loading .3dm files
- [PLYLoadPlugin](#plyloadplugin) - Add support for loading .ply files - [PLYLoadPlugin](#plyloadplugin) - Add support for loading .ply files
- [STLLoadPlugin](#stlloadplugin) - Add support for loading .stl files - [STLLoadPlugin](#stlloadplugin) - Add support for loading .stl files
### Constructor ### Constructor


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


// Create a viewer. All options except canvas/container are optional
const viewer = new ThreeViewer({ const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement, canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
// or a container like: // or a container like:
// domElement: document.body, // domElement: document.body,
// addOptions: { ... } // addOptions: { ... }
// importOptions: { ... } // importOptions: { ... }
}
},
// By default its false // By default its false
// dropzone: false, // dropzone: false,
// To Enable without options // To Enable without options
// dropzone: true // dropzone: true
// Add some plugins after viewer creation.
plugins: [CameraViewPlugin, new CustomPlugin()],
// Shorthand to load files immediately after viewer initialization
load: {
src: 'https://example.com/file.glb',
environment: 'https://example.com/file.hdr',
background: 'https://example.com/file.png',
},
onLoad: (viewer) => {
// Called when all the files are loaded
},
}) })
``` ```


// Add a plugin // Add a plugin
const plugin = viewer.addPluginSync(new TonemapPlugin()) const plugin = viewer.addPluginSync(new TonemapPlugin())
// plugins can be added with just the class also // plugins can be added with just the class also
const plugin = viewer.addPluginSync(TonemapPlugin)
const plugin2 = viewer.addPluginSync(TonemapPlugin)


// Add multiple plugins at once // Add multiple plugins at once
viewer.addPluginsSync([ viewer.addPluginsSync([
]) ])


// Get a plugin // Get a plugin
const plugin = viewer.getPlugin(TonemapPlugin)
const plugin3 = viewer.getPlugin(TonemapPlugin)
// Get or add a plugin, when not sure if the plugin is already added // Get or add a plugin, when not sure if the plugin is already added
const plugin = viewer.getOrAddPluginSync(TonemapPlugin)
const plugin4 = viewer.getOrAddPluginSync(TonemapPlugin)


// Remove a plugin // Remove a plugin
viewer.removePluginSync(TonemapPlugin) viewer.removePluginSync(TonemapPlugin)


The plugin automatically tracks `setDirty()` function calls in objects, materials and the scene. It can be triggerred by calling `setDirty` on any material or object in the scene. Check the [example](https://threepipe.org/examples/#frame-fade-plugin/) for a demo. This can be disabled by options in the plugin. The plugin automatically tracks `setDirty()` function calls in objects, materials and the scene. It can be triggerred by calling `setDirty` on any material or object in the scene. Check the [example](https://threepipe.org/examples/#frame-fade-plugin/) for a demo. This can be disabled by options in the plugin.


## HDRiGroundPlugin

[//]: # (todo: image)

Example: https://threepipe.org/examples/#hdri-ground-plugin/

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

API Reference: [FrameFadePlugin](https://threepipe.org/docs/classes/HDRiGroundPlugin.html)

HDRiGroundPlugin patches the background shader in the renderer to add support for ground projected environment map/skybox. Works simply by setting the background same as the environemnt and enabling the plugin.

The world radius, tripod height, and origin position(center offset) can be set in the plugin.

The plugin is disabled by default when added. Set `.enabled` to enable it or pass `true` in the constructor.
If the background is not the same as the environment when enabled, the user will be prompted for this, unless `promptOnBackgroundMismatch` is set to `false` in the plugin.

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

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

const hdriGround = viewer.addPluginSync(new HDRiGrounPlugin())

// Load an hdr environment map
await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
// set background to environment
viewer.scene.background = 'environment'
// or
// viewer.scene.background = viewer.scene.environemnt

// enable the plugin
hdriGround.enabled = true
```

Check the [example](https://threepipe.org/examples/#hdri-ground-plugin/) for a demo.

## Rhino3dmLoadPlugin ## Rhino3dmLoadPlugin


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

+ 36
- 0
examples/hdri-ground-plugin/index.html Voir le fichier

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HDRi Ground Plugin</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",
"@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/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/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>

+ 50
- 0
examples/hdri-ground-plugin/script.ts Voir le fichier

import {_testFinish, CameraViewPlugin, HDRiGroundPlugin, ThreeViewer} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: true,
dropzone: {
allowedExtensions: ['gltf', 'glb', 'hdr', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr'],
addOptions: {
disposeSceneObjects: true,
autoSetEnvironment: true, // when hdr is dropped
autoSetBackground: true,
},
},
plugins: [CameraViewPlugin],
})
const hdriGround = viewer.addPluginSync(HDRiGroundPlugin)

await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr', {
setBackground: true,
})
await viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
autoCenter: true,
autoScale: true,
autoScaleRadius: 10,
})

viewer.scene.background = 'environment' // this is not really required since setBackground is also set to true above

hdriGround.worldRadius = 50
hdriGround.tripodHeight = 10

const bounds = viewer.scene.getBounds(true, true)
bounds.getCenter(hdriGround.originPosition)
hdriGround.originPosition.y -= (bounds.max.y - bounds.min.y) / 2

hdriGround.enabled = true

console.log(hdriGround)

const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true))
ui.setupPluginUi(HDRiGroundPlugin)

await viewer.fitToView(undefined, 2.5)

}

init().then(_testFinish)

+ 1
- 0
examples/index.html Voir le fichier

</ul> </ul>
<h2 class="category">Utils</h2> <h2 class="category">Utils</h2>
<ul> <ul>
<li><a href="./hdri-ground-plugin/">HDRi Ground Plugin <br/>(Projected Skybox)</a></li>
<li><a href="./render-target-preview/">Render Target Preview Plugin </a></li> <li><a href="./render-target-preview/">Render Target Preview Plugin </a></li>
<li><a href="./geometry-uv-preview/">Geometry UV Preview Plugin </a></li> <li><a href="./geometry-uv-preview/">Geometry UV Preview Plugin </a></li>
<li><a href="./parallel-asset-import/">Parallel Asset Import </a></li> <li><a href="./parallel-asset-import/">Parallel Asset Import </a></li>

+ 2
- 2
package-lock.json Voir le fichier

{ {
"name": "threepipe", "name": "threepipe",
"version": "0.0.14",
"version": "0.0.16",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "threepipe", "name": "threepipe",
"version": "0.0.14",
"version": "0.0.16",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1017/package.tgz", "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1017/package.tgz",

+ 1
- 1
package.json Voir le fichier

{ {
"name": "threepipe", "name": "threepipe",
"version": "0.0.15",
"version": "0.0.16",
"description": "A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.", "description": "A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.",
"main": "src/index.ts", "main": "src/index.ts",
"module": "dist/index.mjs", "module": "dist/index.mjs",

+ 15
- 2
src/plugins/animation/CameraViewPlugin.ts Voir le fichier

import {CameraView, ICamera, ICameraView, PerspectiveCamera2} from '../../core' import {CameraView, ICamera, ICameraView, PerspectiveCamera2} from '../../core'
import {AnimationResult, PopmotionPlugin} from './PopmotionPlugin' import {AnimationResult, PopmotionPlugin} from './PopmotionPlugin'


export interface CameraViewPluginOptions{duration?: number, ease?: EasingFunctionType, interpolateMode?: 'spherical'|'linear'}

/** /**
* Camera View Plugin * Camera View Plugin
* *
// return this._animating // return this._animating
// } // }


constructor() {
constructor(options: CameraViewPluginOptions = {}) {
super() super()
this.addCurrentView = this.addCurrentView.bind(this) this.addCurrentView = this.addCurrentView.bind(this)
this.resetToFirstView = this.resetToFirstView.bind(this) this.resetToFirstView = this.resetToFirstView.bind(this)
// this._wheel = this._wheel.bind(this) // this._wheel = this._wheel.bind(this)
// this._pointerMove = this._pointerMove.bind(this) // this._pointerMove = this._pointerMove.bind(this)
// this._postFrame = this._postFrame.bind(this) // this._postFrame = this._postFrame.bind(this)

this.animDuration = options.duration ?? this.animDuration
this.animEase = options.ease ?? this.animEase
this.interpolateMode = options.interpolateMode ?? this.interpolateMode
} }




@serialize('cameraViews') @serialize('cameraViews')
private _cameraViews: CameraView[] = [] private _cameraViews: CameraView[] = []
get cameraViews(): CameraView[] { get cameraViews(): CameraView[] {
*/ */
@serialize() @uiDropdown('Ease', Object.keys(EasingFunctions).map((label:string)=>({label}))) animEase: EasingFunctionType = 'easeInOutSine' // ms @serialize() @uiDropdown('Ease', Object.keys(EasingFunctions).map((label:string)=>({label}))) animEase: EasingFunctionType = 'easeInOutSine' // ms
@serialize() @uiSlider('Duration', [10, 10000], 10) animDuration = 1000 // ms @serialize() @uiSlider('Duration', [10, 10000], 10) animDuration = 1000 // ms
@serialize() @uiSlider('RotationOffset', [0.2, 0.75], 0.01) rotationOffset = 0.25
@serialize() @uiDropdown('Interpolation', ['spherical', 'linear'].map((label:string)=>({label}))) @serialize() @uiDropdown('Interpolation', ['spherical', 'linear'].map((label:string)=>({label})))
interpolateMode: 'spherical'|'linear' = 'spherical' interpolateMode: 'spherical'|'linear' = 'spherical'



// not used
@serialize()
// @uiSlider('RotationOffset', [0.2, 0.75], 0.01)
rotationOffset = 0.25

private _animating = false private _animating = false
get animating(): boolean { get animating(): boolean {
return this._animating return this._animating

+ 58
- 0
src/plugins/extras/HDRiGroundPlugin.glsl Voir le fichier


#ifdef HDRi_GROUND_PROJ
// assuming vectors are all normalized
float intersectPlane1(const in vec3 r0, const in vec3 rd, const in vec3 n, const in vec3 p0)
{
float t = dot(p0 - r0, n) / (dot(n, rd)+1e-6);
return t < 0. ? 1000. : t;
}
// slightly modified version
float intersectSphere1(in vec3 ro, in vec3 rd, in vec3 sph, in float rad) {
vec3 oc = ro - sph;
float b = dot(oc, rd);
float c = dot(oc, oc) - rad*rad;
float t = b*b - c;
return t < 0.0 ? t : -b + sqrt(t);
}

#define PI_HALF 1.5707963267948966
uniform float worldRadius;
uniform float tripodHeight;
uniform vec3 originPosition;

vec3 hdriProject(){
vec3 p = normalize( vWorldDirection );
vec3 camPos = cameraPosition;
camPos.y -= tripodHeight;
float t = intersectSphere1(camPos, p, originPosition, worldRadius);
if(t>0.0) {
float t2 = intersectPlane1(camPos, p, vec3(0,-1,0), originPosition + vec3(0.0, -tripodHeight, 0.0));
p = (camPos + min(t, t2)*p)/worldRadius;
/*
if(t2 < t && tripodHeight < 0.001){
// float h = dot(p.xz, p.xz);
//vertical
// p.y = sqrt(1.-h);

//sterographic // https://math.stackexchange.com/questions/1729012/mapping-the-unit-disc-to-the-hemisphere
// p.x = p.x/(h+1.0);
// p.z = p.z/(h+1.0);
// p.y = (h-1.0)/(h+1.0);

// polar
float phi = atan(p.z, p.x);
float p1 = 0.4; // lens projection fix // experimental for hdrihaven
float l = length(p.xz);
p1 = (1.-p1*l)/(1.-p1);
float theta = sin(l*PI_HALF)*PI_HALF; // cancel out projection, map [0,1] to [0, PI/2]

p.x = sin(theta)*cos(phi)*p1;
p.y = -cos(theta);
p.z = sin(theta)*sin(phi)*p1;
}
*/
}
else p = vec3(0.0, 1.0, 0.0);
return p;
}
#endif

+ 117
- 0
src/plugins/extras/HDRiGroundPlugin.ts Voir le fichier

import {DataTexture, EquirectangularReflectionMapping, ShaderLib, Vector3} from 'three'
import {onChange, serialize} from 'ts-browser-helpers'
import hdriGroundProj from './HDRiGroundPlugin.glsl'
import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {shaderReplaceString} from '../../utils'
import {uiPanelContainer, uiSlider, uiToggle, uiVector} from 'uiconfig.js'

@uiPanelContainer('HDRi Ground')
export class HDRiGroundPlugin extends AViewerPluginSync<'', ThreeViewer> {
static readonly PluginType = 'HDRiGroundPlugin'

@serialize()
@onChange(HDRiGroundPlugin.prototype._paramsChanged)
@uiToggle('Enabled')
enabled = false

@serialize()
@onChange(HDRiGroundPlugin.prototype._paramsChanged)
@uiSlider('World Radius', [1, 1000], 0.01)
worldRadius = 100

@serialize()
@onChange(HDRiGroundPlugin.prototype._paramsChanged)
@uiSlider('Tripod height', [0, 50], 0.01)
tripodHeight = 10

@serialize()
@onChange(HDRiGroundPlugin.prototype._paramsChanged)
@uiVector('Origin Position', undefined, 0.001, (t: HDRiGroundPlugin)=>({
onChange: t._paramsChanged, // this is for x, y, z values.
}))
originPosition = new Vector3(0, 0, 0)

@serialize()
@onChange(HDRiGroundPlugin.prototype._paramsChanged)
promptOnBackgroundMismatch = true

// todo
// /**
// * Automatically set the origin position based on the ground position in GroundPlugin
// */
// @serialize()
// @onChange(HDRiGroundPlugin.prototype._paramsChanged)
// @uiToggle('Auto Ground Position')
// autoGroundPosition = false

constructor(enabled = false, promptOnBackgroundMismatch = true) {
super()
this._paramsChanged = this._paramsChanged.bind(this)
this.enabled = enabled
this.promptOnBackgroundMismatch = promptOnBackgroundMismatch

this.addEventListener('deserialize', this._paramsChanged)
}

private _paramsChanged() {
if (!this._viewer) return
const bg = this._viewer.scene.background
if (this.enabled && bg !== this._viewer.scene.environment && bg !== 'environment') {
if (bg && (bg as any).isDataTexture) (bg as DataTexture).mapping = EquirectangularReflectionMapping
else {
const change = this.promptOnBackgroundMismatch ? this._viewer.dialog.confirmSync('Background must be same as environment, do you want to change it?') : true
if (change) {
// const bgui = this._viewer.getPlugin<SimpleBackgroundEnvUiPlugin>('SimpleBackgroundEnvUiPlugin1')
// if (bgui) {
// bgui.envmapBg = true
// bgui.uiConfig.uiRefresh?.('postFrame', true)
// } else
this._viewer.scene.background = 'environment'
} else this.enabled = false
}
}

const cubeMat = this._viewer.renderManager.renderer.background.getBoxMesh2()?.material
const unif = cubeMat?.uniforms ?? ShaderLib.backgroundCube.uniforms
if (!unif.tripodHeight) unif.tripodHeight = {value: 1.0}
if (!unif.worldRadius) unif.worldRadius = {value: 1.0}
if (!unif.originPosition) unif.originPosition = {value: new Vector3()}
unif.tripodHeight.value = this.tripodHeight
unif.worldRadius.value = this.worldRadius
unif.originPosition.value.copy(this.originPosition)
if (cubeMat) {
if (!this.enabled && cubeMat.defines.HDRi_GROUND_PROJ)
delete cubeMat.defines.HDRi_GROUND_PROJ
else if (this.enabled)
cubeMat.defines.HDRi_GROUND_PROJ = '1'
cubeMat.needsUpdate = true
}
this._viewer.setDirty()
// const m = this._viewer?.scene.modelRoot.children ?? []
// for (const m1 of m) {
// m1.position.y = -this.tripodHeight + new Box3B().expandByObject(m1, true, true).getSize(new Vector3()).y / 2
// }
}

onAdded(viewer: ThreeViewer): void {
super.onAdded(viewer)
if (this._viewer?.renderManager.webglRenderer?.background.getBoxMesh())
viewer.console.error('HDRi Ground Plugin must be added before setting any cube or env map')

if (!ShaderLib.backgroundCube.fragmentShader.includes('#ifdef HDRi_GROUND_PROJ')) {
ShaderLib.backgroundCube.fragmentShader = shaderReplaceString(ShaderLib.backgroundCube.fragmentShader, 'void main() {', hdriGroundProj, {prepend: true})
ShaderLib.backgroundCube.fragmentShader = shaderReplaceString(ShaderLib.backgroundCube.fragmentShader, 'vec3 vReflect = vWorldDirection;', `
vec3 vReflect =
#ifdef HDRi_GROUND_PROJ
hdriProject()
#else
vWorldDirection
#endif
;
`)
}

viewer.scene.addEventListener('environmentChanged', this._paramsChanged)
}

}

+ 4
- 1
src/plugins/index.ts Voir le fichier

// animation // animation
export {GLTFAnimationPlugin} from './animation/GLTFAnimationPlugin' export {GLTFAnimationPlugin} from './animation/GLTFAnimationPlugin'
export {PopmotionPlugin} from './animation/PopmotionPlugin' export {PopmotionPlugin} from './animation/PopmotionPlugin'
export {CameraViewPlugin} from './animation/CameraViewPlugin'
export {CameraViewPlugin, type CameraViewPluginOptions} from './animation/CameraViewPlugin'

// extras
export {HDRiGroundPlugin} from './extras/HDRiGroundPlugin'

+ 2
- 5
src/plugins/interaction/PickingPlugin.ts Voir le fichier

import {Object3D} from 'three' import {Object3D} from 'three'
import {Class, serialize} from 'ts-browser-helpers' import {Class, serialize} from 'ts-browser-helpers'
import {AViewerPluginSync, ThreeViewer} from '../../viewer' import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {ObjectPicker} from '../../three/utils/ObjectPicker'
import {BoxSelectionWidget, ObjectPicker, SelectionWidget} from '../../three'
import {IObject3D, IObject3DEvent, ISceneEvent} from '../../core' import {IObject3D, IObject3DEvent, ISceneEvent} from '../../core'
import {IUiConfigContainer, UiObjectConfig} from 'uiconfig.js' import {IUiConfigContainer, UiObjectConfig} from 'uiconfig.js'
import {BoxSelectionWidget, SelectionWidget} from '../../three/utils/SelectionWidget'


export class PickingPlugin extends AViewerPluginSync<'selectedObjectChanged'|'hoverObjectChanged'|'hitObject'> { export class PickingPlugin extends AViewerPluginSync<'selectedObjectChanged'|'hoverObjectChanged'|'hitObject'> {
@serialize() enabled = true @serialize() enabled = true
this.dispatchEvent(e) this.dispatchEvent(e)
} }


// @ts-expect-error temporary
public async focusObject(selected?: Object3D) { public async focusObject(selected?: Object3D) {
// const camViews = this._viewer?.getPluginByType<CameraViewPlugin>('CameraViews')
// await camViews?.animateToFitObject(selected, 1.25, 1000, 'easeOut', {min: (this._viewer?.scene.activeCamera.getControls<OrbitControls3>()?.minDistance ?? 0.5) + 0.5, max: 50.0})
this._viewer?.fitToView(selected, 1.25, 1000, 'easeOut')
} }


public enableWidget(enable: boolean): void { public enableWidget(enable: boolean): void {

+ 7
- 0
src/three/Threejs.ts Voir le fichier


export {WebGLArrayRenderTarget} from 'three' export {WebGLArrayRenderTarget} from 'three'
export {WebGL3DRenderTarget} from 'three' export {WebGL3DRenderTarget} from 'three'
export {WebGLMultipleRenderTargets} from 'three' export {WebGLMultipleRenderTargets} from 'three'
export * from 'three/examples/jsm/libs/fflate.module.js' export * from 'three/examples/jsm/libs/fflate.module.js'


export {CopyShader} from 'three/examples/jsm/shaders/CopyShader.js' export {CopyShader} from 'three/examples/jsm/shaders/CopyShader.js'
export {Pass, FullScreenQuad} from 'three/examples/jsm/postprocessing/Pass.js'
export {RenderPass} from 'three/examples/jsm/postprocessing/RenderPass.js'
export {ShaderPass} from 'three/examples/jsm/postprocessing/ShaderPass.js'
export {EffectComposer} from 'three/examples/jsm/postprocessing/EffectComposer.js'

export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'

+ 4
- 2
src/utils/DialogWrapper.ts Voir le fichier

export interface IDialogWrapper { export interface IDialogWrapper {
alert: (message?: string) => Promise<void> alert: (message?: string) => Promise<void>
confirm: (message?: string) => Promise<boolean>
prompt: (message?: string, _default?: string, cancel?: boolean) => Promise<string | null> prompt: (message?: string, _default?: string, cancel?: boolean) => Promise<string | null>
confirm: (message?: string) => Promise<boolean>
confirmSync: (message?: string) => boolean
} }
export const windowDialogWrapper: IDialogWrapper = { export const windowDialogWrapper: IDialogWrapper = {
alert: async(message?: string) => window.alert(message), alert: async(message?: string) => window.alert(message),
confirm: async(message?: string) => window.confirm(message),
prompt: async(message?: string, _default?: string, _?: boolean) => window.prompt(message, _default), prompt: async(message?: string, _default?: string, _?: boolean) => window.prompt(message, _default),
confirm: async(message?: string) => window.confirm(message),
confirmSync: (message?: string) => window.confirm(message),
} }

+ 46
- 12
src/viewer/ThreeViewer.ts Voir le fichier

Vector2, Vector2,
Vector3, Vector3,
} from 'three' } from 'three'
import {Class, createCanvasElement, onChange, serialize} from 'ts-browser-helpers'
import {Class, createCanvasElement, onChange, serialize, ValOrArr} from 'ts-browser-helpers'
import {TViewerScreenShader} from '../postprocessing' import {TViewerScreenShader} from '../postprocessing'
import { import {
AddObjectOptions, AddObjectOptions,
import {ViewerRenderManager} from './ViewerRenderManager' import {ViewerRenderManager} from './ViewerRenderManager'
import { import {
convertArrayBufferToStringsInMeta, convertArrayBufferToStringsInMeta,
EasingFunctionType,
getEmptyMeta, getEmptyMeta,
GLStatsJS, GLStatsJS,
IDialogWrapper, IDialogWrapper,
import {IViewerPlugin, IViewerPluginSync} from './IViewerPlugin' import {IViewerPlugin, IViewerPluginSync} from './IViewerPlugin'
import {uiConfig, uiFolderContainer, UiObjectConfig} from 'uiconfig.js' import {uiConfig, uiFolderContainer, UiObjectConfig} from 'uiconfig.js'
import {IRenderTarget} from '../rendering' import {IRenderTarget} from '../rendering'
import type {ProgressivePlugin} from '../plugins'
import type {CameraViewPlugin, ProgressivePlugin} from '../plugins'
// noinspection ES6PreferShortImport // noinspection ES6PreferShortImport
import {DropzonePlugin, DropzonePluginOptions} from '../plugins/interaction/DropzonePlugin' import {DropzonePlugin, DropzonePluginOptions} from '../plugins/interaction/DropzonePlugin'
// noinspection ES6PreferShortImport // noinspection ES6PreferShortImport
import {TonemapPlugin} from '../plugins/postprocessing/TonemapPlugin' import {TonemapPlugin} from '../plugins/postprocessing/TonemapPlugin'
import {VERSION} from './version' import {VERSION} from './version'
import {Easing} from 'popmotion'
import {OrbitControls3} from '../three'


export type IViewerEvent = BaseEvent & { export type IViewerEvent = BaseEvent & {
type: 'update'|'preRender'|'postRender'|'preFrame'|'postFrame'|'dispose'|'addPlugin'|'renderEnabled'|'renderDisabled' type: 'update'|'preRender'|'postRender'|'preFrame'|'postFrame'|'dispose'|'addPlugin'|'renderEnabled'|'renderDisabled'


debug?: boolean debug?: boolean


/**
* Add initial plugins.
*/
plugins?: (IViewerPluginSync | Class<IViewerPluginSync>)[]

load?: {
/**
* Load one or more source files
*/
src?: ValOrArr<string | IAsset | null>

/**
* Load environment map
*/
environment?: string | IAsset | ITexture | undefined | null

/**
* Load background map
*/
background?: string | IAsset | ITexture | undefined | null

}
onLoad?: (results: any) => void


/** /**
* TonemapPlugin is added to the viewer if this is true. * TonemapPlugin is added to the viewer if this is true.
* @default true * @default true
if (options.tonemap !== false) { if (options.tonemap !== false) {
this.addPluginSync(new TonemapPlugin()) this.addPluginSync(new TonemapPlugin())
} }
for (const p of options.plugins ?? []) this.addPluginSync(p)


this.console.log('ThreePipe Viewer instance initialized, version: ', ThreeViewer.VERSION) this.console.log('ThreePipe Viewer instance initialized, version: ', ThreeViewer.VERSION)



if (options.load) {
const sources = [options.load.src].flat().filter(s=> s)
const promises: Promise<any>[] = sources.map(async s=> s && this.load(s))
if (options.load.environment) promises.push(this.setEnvironmentMap(options.load.environment))
if (options.load.background) promises.push(this.setBackgroundMap(options.load.background))
Promise.all(promises).then(options.onLoad)
}
} }


/** /**
// this.fromJSON(config, config.resources) // this.fromJSON(config, config.resources)
// } // }


// todo
// public async fitToView(selected?: Object3D, distanceMultiplier = 1.5, duration?: number, ease?: Easing|EasingFunctionType) {
// const camViews = this.getPluginByType<CameraViewPlugin>('CameraViews')
// if (!camViews) {
// this.console.error('CameraViews plugin is required for fitToView to work')
// return
// }
// await camViews?.animateToFitObject(selected, distanceMultiplier, duration, ease, {min: (this.scene.activeCamera.getControls<OrbitControls3>()?.minDistance ?? 0.5) + 0.5, max: 1000.0})
// }
public async fitToView(selected?: Object3D, distanceMultiplier = 1.5, duration?: number, ease?: Easing|EasingFunctionType) {
const camViews = this.getPlugin<CameraViewPlugin>('CameraViews')
if (!camViews) {
this.console.error('CameraViewPlugin (CameraViews) is required for fitToView to work')
return
}
await camViews?.animateToFitObject(selected, distanceMultiplier, duration, ease, {min: ((<OrbitControls3> this.scene.mainCamera.controls)?.minDistance ?? 0.5) + 0.5, max: 1000.0})
}


// todo: create/load texture utils // todo: create/load texture utils



+ 1
- 1
src/viewer/version.ts Voir le fichier

export const VERSION = '0.0.15'
export const VERSION = '0.0.16'

Chargement…
Annuler
Enregistrer