Kaynağa Gözat

Add VirtualCamerasPlugin and examples, fix autoAspect initialization, Add renderCamera in RootScene indicating the current render camera, Add onEnd in popmotion AnimationOptions, support target for every camera in ProgressivePlugin, Add renderToScreen option in RenderManager.render, Add '*' event in ThreeViewer, Add HierarchyUiPlugin in tweakpane-editor

master
Palash Bansal 2 yıl önce
ebeveyn
işleme
d3810941aa
No account linked to committer's email address

+ 196
- 5
README.md Dosyayı Görüntüle

## Table of Contents ## Table of Contents


- [ThreePipe](#threepipe) - [ThreePipe](#threepipe)
- [Examples](#examples)
- [Examples](https://threepipe.org/examples/)
- [Table of Contents](#table-of-contents) - [Table of Contents](#table-of-contents)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [HTML/JS Quickstart (CDN)](#htmljs-quickstart-cdn) - [HTML/JS Quickstart (CDN)](#htmljs-quickstart-cdn)
- [VignettePlugin](#vignetteplugin) - Add Vignette effect by patching the final screen pass - [VignettePlugin](#vignetteplugin) - Add Vignette effect by patching the final screen pass
- [ChromaticAberrationPlugin](#chromaticaberrationplugin) - Add Chromatic Aberration effect by patching the final screen pass - [ChromaticAberrationPlugin](#chromaticaberrationplugin) - Add Chromatic Aberration effect by patching the final screen pass
- [FilmicGrainPlugin](#filmicgrainplugin) - Add Filmic Grain effect by patching the final screen pass - [FilmicGrainPlugin](#filmicgrainplugin) - Add Filmic Grain effect by patching the final screen pass
- [NoiseBumpMaterialPlugin](#noisebumpmaterialplugin) - Sparkle Bump/Noise Bump material extension for PhysicalMaterial
- [CustomBumpMapPlugin](#custombumpmapplugin) - Custom Bump Map material extension for PhysicalMaterial
- [ClearcoatTintPlugin](#clearcoattintplugin) - Clearcoat Tint material extension for PhysicalMaterial
- [FragmentClippingExtensionPlugin](#fragmentclippingextensionplugin) - Fragment/SDF Clipping material extension for PhysicalMaterial
- [HDRiGroundPlugin](#hdrigroundplugin) - Add support for ground projected hdri/skybox to the webgl background shader. - [HDRiGroundPlugin](#hdrigroundplugin) - Add support for ground projected hdri/skybox to the webgl background shader.
- [VirtualCamerasPlugin](#virtualcamerasplugin) - Add support for rendering virtual cameras before the main one every frame.
- [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
}, },
onComplete: () => isMovedUp = true, onComplete: () => isMovedUp = true,
onStop: () => throw(new Error('Animation stopped')), onStop: () => throw(new Error('Animation stopped')),
onEnd: () => console.log('Animation ended'), // This runs after both onComplete and onStop
}) })


// await for animation. This promise will reject only if an exception is thrown in onStop
// await for animation. This promise will reject only if an exception is thrown in onStop or onComplete. onStop rejects if throwOnStop is true
await anim.promise.catch((e)=>{ await anim.promise.catch((e)=>{
console.log(e, 'animation stopped before completion') console.log(e, 'animation stopped before completion')
}); });
filmicGrainPlugin.multiply = false filmicGrainPlugin.multiply = false
``` ```


## NoiseBumpMaterialPlugin

[//]: # (todo: image)

Example: https://threepipe.org/examples/#noise-bump-material-plugin/

Source Code: [src/plugins/material/NoiseBumpMaterialPlugin.ts](./src/plugins/material/NoiseBumpMaterialPlugin.ts)

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

NoiseBumpMaterialPlugin adds a material extension to PhysicalMaterial to add support for sparkle bump / noise bump by creating procedural bump map from noise to simulate sparkle flakes.
It uses voronoise function from blender along with several additions to generate the noise for the generation.
It also adds a UI to the material to edit the settings.
It uses `WEBGI_materials_noise_bump` glTF extension to save the settings in glTF/glb files.

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

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

const noiseBump = viewer.addPluginSync(NoiseBumpMaterialPlugin)

// Add noise bump to a material
NoiseBumpMaterialPlugin.AddNoiseBumpMaterial(material, {
flakeScale: 300,
})

// Change properties with code or use the UI
material.userData._noiseBumpMat!.bumpNoiseParams = [1, 1]
material.setDirty()

// Disable
material.userData._noiseBumpMat!.hasBump = false
material.setDirty()
```

## CustomBumpMapPlugin

[//]: # (todo: image)

Example: https://threepipe.org/examples/#custom-bump-map-plugin/

Source Code: [src/plugins/material/CustomBumpMapPlugin.ts](./src/plugins/material/CustomBumpMapPlugin.ts)

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

CustomBumpMapPlugin adds a material extension to PhysicalMaterial to support custom bump maps.
A Custom bump map is similar to the built-in bump map, but allows using an extra bump map and scale to give a combined effect.
This plugin also has support for bicubic filtering of the custom bump map and is enabled by default.
It also adds a UI to the material to edit the settings.
It uses `WEBGI_materials_custom_bump_map` glTF extension to save the settings in glTF/glb files.

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

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

const customBump = viewer.addPluginSync(CustomBumpMapPlugin)

// Add noise bump to a material
customBump.enableCustomBump(material, bumpMap, 0.2)

// Change properties with code or use the UI
material.userData._customBumpMat = texture
material.setDirty()

// Disable
material.userData._hasCustomBump = false
// or
material.userData._customBumpMat = null
material.setDirty()
```

## ClearcoatTintPlugin

[//]: # (todo: image)

Example: https://threepipe.org/examples/#clearcoat-tint-plugin/

Source Code: [src/plugins/material/ClearcoatTintPlugin.ts](./src/plugins/material/ClearcoatTintPlugin.ts)

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

ClearcoatTintPlugin adds a material extension to PhysicalMaterial which adds tint and thickness to the built-in clearcoat properties.
It also adds a UI to the material to edit the settings.
It uses `WEBGI_materials_clearcoat_tint` glTF extension to save the settings in glTF/glb files.

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

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

const clearcoatTint = viewer.addPluginSync(ClearcoatTintPlugin)

material.clearcoat = 1
// add initial properties
ClearcoatTintPlugin.AddClearcoatTint(material, {
tintColor: '#ff0000',
thickness: 1,
})

// Change properties with code or use the UI
material.userData._clearcoatTint!.tintColor = '#ff0000'
material.setDirty()

// Disable
material.userData._clearcoatTint.enableTint = false
material.setDirty()
```

## FragmentClippingExtensionPlugin

[//]: # (todo: image)

Example: https://threepipe.org/examples/#fragment-clipping-extension-plugin/

Source Code: [src/plugins/materials/FragmentClippingExtensionPlugin.ts](./src/plugins/material/FragmentClippingExtensionPlugin.ts)

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

FragmentClippingExtensionPlugin adds a material extension to PhysicalMaterial to add support for fragment clipping.
Fragment clipping allows to clip fragments of the material in screen space or world space based on a circle, rectangle, plane, sphere, etc.
It uses fixed SDFs with params defined by the user for clipping.
It also adds a UI to the material to edit the settings.
It uses `WEBGI_materials_fragment_clipping_extension` glTF extension to save the settings in glTF/glb files.

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

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

const fragmentClipping = viewer.addPluginSync(FragmentClippingExtensionPlugin)

// add initial properties
FragmentClippingExtensionPlugin.AddFragmentClipping(material, {
clipPosition: new Vector4(0.5, 0.5, 0, 0),
clipParams: new Vector4(0.1, 0.05, 0, 1),
})

// Change properties with code or use the UI
material.userData._fragmentClipping!.clipPosition.set(0, 0, 0, 0)
material.setDirty()

// Disable
material.userData._clearcoatTint.clipEnabled = false
material.setDirty()
```

## HDRiGroundPlugin ## HDRiGroundPlugin


[//]: # (todo: image) [//]: # (todo: image)


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


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


API Reference: [FrameFadePlugin](https://threepipe.org/docs/classes/HDRiGroundPlugin.html)
API Reference: [HDRiGroundPlugin](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. 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.


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. 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 ```typescript
import {ThreeViewer, FrameFadePlugin} from 'threepipe'
import {ThreeViewer, HDRiGrounPlugin} from 'threepipe'


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




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


## VirtualCamerasPlugin

[//]: # (todo: image)

Example: https://threepipe.org/examples/#virtual-cameras-plugin/

Source Code: [src/plugins/rendering/VirtualCamerasPlugin.ts](./src/plugins/rendering/VirtualCamerasPlugin.ts)

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

VirtualCamerasPlugin adds support for rendering to multiple virtual cameras in the viewer. These cameras are rendered in preRender callback just before the main camera is rendered. The virtual cameras can be added to the plugin and removed from it.

The feed to the virtual camera is rendered to a Render Target texture which can be accessed and re-rendered in the scene or used in other plugins.

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

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

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

const camera = new PerspectiveCamera2('orbit', viewer.canvas, false, 45, 1)
camera.name = name
camera.position.set(0, 5, 0)
camera.target.set(0, 0.25, 0)
camera.userData.autoLookAtTarget = true // automatically look at the target (in setDirty)
camera.setDirty()
camera.addEventListener('update', ()=>{
viewer.setDirty() // if the camera is not added to the scene it wont update automatically when camera.setDirty is called(like from the UI)
})

const vCam = virtualCameras.addCamera(camera)
console.log(vCam.target) // target is a WebGLRenderTarget/IRenderTarget
```

Check the [virtual camera](https://threepipe.org/examples/#hdri-ground-plugin/) example for using the texture in the scene.

## Rhino3dmLoadPlugin ## Rhino3dmLoadPlugin


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

+ 9
- 0
examples/index.html Dosyayı Görüntüle

<li><a href="./depth-buffer-plugin/">Depth Buffer Plugin </a></li> <li><a href="./depth-buffer-plugin/">Depth Buffer Plugin </a></li>
<li><a href="./normal-buffer-plugin/">Normal Buffer Plugin </a></li> <li><a href="./normal-buffer-plugin/">Normal Buffer Plugin </a></li>
<li><a href="./custom-pipeline/">Custom Pipeline specification </a></li> <li><a href="./custom-pipeline/">Custom Pipeline specification </a></li>
<li><a href="./virtual-cameras-plugin/">Virtual Cameras Plugin </a></li>
<li><a href="./virtual-camera/">Virtual Camera (Animated) </a></li>
</ul> </ul>
<h2 class="category">Interaction</h2> <h2 class="category">Interaction</h2>
<ul> <ul>
<li><a href="./gltf-camera-animation/">glTF Camera Animation </a></li> <li><a href="./gltf-camera-animation/">glTF Camera Animation </a></li>
<li><a href="./gltf-animation-page-scroll/">glTF Animation Page Scroll </a></li> <li><a href="./gltf-animation-page-scroll/">glTF Animation Page Scroll </a></li>
</ul> </ul>
<h2 class="category">Materials</h2>
<ul>
<li><a href="./clearcoat-tint-plugin/">Clearcoat Tint Plugin</a></li>
<li><a href="./fragment-clipping-extension-plugin/">Fragment Clipping Extension Plugin </a></li>
<li><a href="./noise-bump-material-plugin/">SparkleBump(NoiseBump) Material Plugin </a></li>
<li><a href="./custom-bump-map-plugin/">Custom Bump Map Plugin </a></li>
</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="./hdri-ground-plugin/">HDRi Ground Plugin <br/>(Projected Skybox)</a></li>

+ 14
- 3
examples/tweakpane-editor/script.ts Dosyayı Görüntüle

_testFinish, _testFinish,
CameraViewPlugin, CameraViewPlugin,
ChromaticAberrationPlugin, ChromaticAberrationPlugin,
ClearcoatTintPlugin,
CustomBumpMapPlugin,
DepthBufferPlugin, DepthBufferPlugin,
DropzonePlugin, DropzonePlugin,
FilmicGrainPlugin, FilmicGrainPlugin,
FragmentClippingExtensionPlugin,
FrameFadePlugin, FrameFadePlugin,
FullScreenPlugin, FullScreenPlugin,
GLTFAnimationPlugin, GLTFAnimationPlugin,
HemisphereLight, HemisphereLight,
KTX2LoadPlugin, KTX2LoadPlugin,
KTXLoadPlugin, KTXLoadPlugin,
NoiseBumpMaterialPlugin,
NormalBufferPlugin, NormalBufferPlugin,
PickingPlugin, PickingPlugin,
PLYLoadPlugin, PLYLoadPlugin,
USDZLoadPlugin, USDZLoadPlugin,
ViewerUiConfigPlugin, ViewerUiConfigPlugin,
VignettePlugin, VignettePlugin,
VirtualCamerasPlugin,
} from 'threepipe' } from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'
import {TweakpaneEditorPlugin} from '@threepipe/plugin-tweakpane-editor'
import {HierarchyUiPlugin, TweakpaneEditorPlugin} from '@threepipe/plugin-tweakpane-editor'
import {BlendLoadPlugin} from '@threepipe/plugin-blend-importer' import {BlendLoadPlugin} from '@threepipe/plugin-blend-importer'
import {extraImportPlugins} from '@threepipe/plugin-extra-importers' import {extraImportPlugins} from '@threepipe/plugin-extra-importers'


PickingPlugin, PickingPlugin,
CameraViewPlugin, CameraViewPlugin,
ViewerUiConfigPlugin, ViewerUiConfigPlugin,
ClearcoatTintPlugin,
FragmentClippingExtensionPlugin,
NoiseBumpMaterialPlugin,
CustomBumpMapPlugin,
VirtualCamerasPlugin,
// new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin // new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin
new DepthBufferPlugin(HalfFloatType, true, true), new DepthBufferPlugin(HalfFloatType, true, true),
new NormalBufferPlugin(HalfFloatType, false), new NormalBufferPlugin(HalfFloatType, false),
STLLoadPlugin, STLLoadPlugin,
USDZLoadPlugin, USDZLoadPlugin,
BlendLoadPlugin, BlendLoadPlugin,
HierarchyUiPlugin,
...extraImportPlugins, ...extraImportPlugins,
]) ])




editor.loadPlugins({ editor.loadPlugins({
['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin], ['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin],
['Interaction']: [PickingPlugin],
['Interaction']: [HierarchyUiPlugin, PickingPlugin],
['GBuffer']: [DepthBufferPlugin, NormalBufferPlugin], ['GBuffer']: [DepthBufferPlugin, NormalBufferPlugin],
['Post-processing']: [TonemapPlugin, ProgressivePlugin, FrameFadePlugin, VignettePlugin], ['Post-processing']: [TonemapPlugin, ProgressivePlugin, FrameFadePlugin, VignettePlugin],
['Animation']: [GLTFAnimationPlugin, CameraViewPlugin], ['Animation']: [GLTFAnimationPlugin, CameraViewPlugin],
['Extras']: [HDRiGroundPlugin, Rhino3dmLoadPlugin],
['Extras']: [HDRiGroundPlugin, Rhino3dmLoadPlugin, ClearcoatTintPlugin, FragmentClippingExtensionPlugin, NoiseBumpMaterialPlugin, CustomBumpMapPlugin, VirtualCamerasPlugin],
['Debug']: [RenderTargetPreviewPlugin], ['Debug']: [RenderTargetPreviewPlugin],
}) })



+ 36
- 0
examples/virtual-camera/index.html Dosyayı Görüntüle

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Virtual Camera</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>

+ 69
- 0
examples/virtual-camera/script.ts Dosyayı Görüntüle

import {
_testFinish,
IObject3D,
Mesh,
PerspectiveCamera2,
PhysicalMaterial,
PlaneGeometry,
PopmotionPlugin,
ProgressivePlugin,
Texture,
ThreeViewer,
VirtualCamerasPlugin,
} from 'threepipe'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
debug: true,
plugins: [new ProgressivePlugin(16)],
})
const virtualCameras = viewer.addPluginSync(VirtualCamerasPlugin)
const popmotion = viewer.addPluginSync(PopmotionPlugin)

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

const ground = new Mesh(
new PlaneGeometry(5, 5)
.translate(0, 0, -4),
new PhysicalMaterial({
color: '#ffffff',
})
)
ground.castShadow = false
ground.receiveShadow = true
viewer.scene.addObject(ground)

const camera = new PerspectiveCamera2('', viewer.canvas, false, 45, 1)
camera.position.set(0, 0, 5)
camera.target.set(0, 0.25, 0)
camera.userData.autoLookAtTarget = true
camera.near = 1
camera.far = 10
camera.setDirty()
const vCam = virtualCameras.addCamera(camera)
ground.material.map = vCam.target.texture as Texture

popmotion.animate({
from: 0,
to: 1,
repeat: Infinity,
duration: 6000,
onUpdate: (v)=>{
// Set camera position xz in a circle around the target
const angle = v * Math.PI * 2 + Math.PI / 2
const radius = 5
camera.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius)
camera.setDirty()
viewer.setDirty() // since camera is not in the scene
},
})

}

init().then(_testFinish)

+ 36
- 0
examples/virtual-cameras-plugin/index.html Dosyayı Görüntüle

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Virtual Cameras 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>

+ 69
- 0
examples/virtual-cameras-plugin/script.ts Dosyayı Görüntüle

import {
_testFinish,
HemisphereLight,
IObject3D,
PerspectiveCamera2,
ProgressivePlugin,
RenderTargetPreviewPlugin,
ThreeViewer,
Vector3,
VirtualCamerasPlugin,
} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

async function init() {

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
debug: true,
plugins: [new ProgressivePlugin(16)],
})
const virtualCameras = viewer.addPluginSync(VirtualCamerasPlugin)

viewer.scene.addObject(new HemisphereLight(0xffffff, 0x444444, 10))
await viewer.load<IObject3D>('https://threejs.org/examples/models/fbx/Samba Dancing.fbx', {
autoCenter: true,
autoScale: true,
})

viewer.scene.mainCamera.position.set(5, 5, 5)
viewer.scene.mainCamera.target.set(0, 0.25, 0)
viewer.scene.mainCamera.setDirty()

const views = [
[new Vector3(5, 0, 0), 'right'],
[new Vector3(0, 5, 0), 'top'],
[new Vector3(0, 0, 5), 'front'],
] as const

const rt = viewer.addPluginSync(RenderTargetPreviewPlugin)

const ui = viewer.addPluginSync(TweakpaneUiPlugin, true)
ui.appendChild(viewer.scene.mainCamera.uiConfig)

for (const [view, name] of views) {
const camera = new PerspectiveCamera2('', viewer.canvas, false, 45, 1)
camera.name = name
camera.position.copy(view)
camera.target.set(0, 0.25, 0)
camera.userData.autoLookAtTarget = true
camera.setDirty()
camera.addEventListener('update', ()=>{
viewer.setDirty() // since the camera is not added to the scene it wont update automatically when setDirty is called(like from the UI)
})

viewer.scene.mainCamera.addEventListener('update', ()=>{
camera.target.copy(viewer.scene.mainCamera.target) // sync the lookAt target of all the cameras
camera.setDirty()
})

const vCam = virtualCameras.addCamera(camera)

rt.addTarget(()=>vCam.target, name, false, false, true)

ui.appendChild(camera.uiConfig)
}

}

init().then(_testFinish)

+ 2
- 1
plugins/tweakpane-editor/package.json Dosyayı Görüntüle

}, },
"dependencies": { "dependencies": {
"threepipe": "file:./../../src/", "threepipe": "file:./../../src/",
"@threepipe/plugin-tweakpane": "file:./../tweakpane/src/"
"@threepipe/plugin-tweakpane": "file:./../tweakpane/src/",
"treejs": "git://github.com/repalash/treejs.git#d303016bb74e75725d13e97291ac1d4727985918"
}, },
"clean-package": { "clean-package": {
"remove": [ "remove": [

+ 139
- 0
plugins/tweakpane-editor/src/HierarchyUiPlugin.ts Dosyayı Görüntüle

import {createDiv, createStyles, css, timeout} from 'ts-browser-helpers'
import Tree from 'treejs'
import {AViewerPluginSync, IObject3D, Object3D, ThreeViewer} from 'threepipe'
import {UiObjectConfig} from 'uiconfig.js'

export class HierarchyUiPlugin extends AViewerPluginSync<''> {
enabled = true
public static readonly PluginType = 'HierarchyUiPlugin'

toJSON: any = undefined

treeView: any = undefined

hierarchyDiv = createDiv({
innerHTML: '',
id: 'tpHierarchyContainer',
addToBody: false,
})

constructor(enabled = true) {
super()
this.enabled = enabled
this.reset = this.reset.bind(this)
this._postFrame = this._postFrame.bind(this)
this.uiConfig.domChildren = [this.hierarchyDiv]
createStyles(css`
#tpHierarchyContainer{
width: 100%;
height: auto;
background-color: transparent;
color: #e9e9ed;
margin-top: 0;
}
`)

}

reset(e?: any) {
if (e?.fromHierarchyPlugin) return // for infinite loop
if (!e?.hierarchyChanged) return
this._needsReset = true
}
protected async _reset() {
this._needsReset = false

while (this.hierarchyDiv.firstChild) this.hierarchyDiv.firstChild.remove()

const obj = this._viewer?.scene.modelRoot
if (!obj) return

const data = obj.children.reduce(this._buildData, [])
const visible = obj.children.reduce(this._findVisible, [])
let firstChange = false
return new Promise<void>((resolve) => {
this.treeView = new Tree(this.hierarchyDiv, {
closeDepth: 1,
data,
// values: visible, // uuids of visible nodes
loaded: function() {
this.values = visible
resolve()
},
onChange: () => {
if (!firstChange) { // first time called when loaded
firstChange = true
return
}
timeout(200).then(() => { // wait for the UI to update
if (this.treeView)
this._setVisible(this.treeView.values)
})
},
onItemLabelClick: (item: any) => {
const obj1 = this._viewer?.scene.modelRoot.getObjectByProperty('uuid', item)
if (!obj1 || !obj.visible) return
obj1.dispatchEvent({type: 'select', value: obj1, ui: true})
},
})
})
}

onAdded(viewer: ThreeViewer) {
super.onAdded(viewer)
this.reset()
viewer.scene.addEventListener('sceneUpdate', this.reset)
viewer.addEventListener('postFrame', this._postFrame)
}

onRemove(viewer: ThreeViewer) {
// todo: remove UI element.
viewer.scene.removeEventListener('sceneUpdate', this.reset)
viewer.removeEventListener('postFrame', this._postFrame)
return super.onRemove(viewer)
}

protected _needsReset = false
protected _postFrame() {
if (this._needsReset) this._reset()
}

dispose() {
// todo destroy UI element.
}

uiConfig: UiObjectConfig = {
type: 'folder',
label: 'Hierarchy',
children: [],
}

private _buildData = (data: any[], obj: IObject3D): any[] => {
data.push({
text: obj.name || 'unnamed',
id: obj.uuid,
children: obj.children.reduce(this._buildData, []),
})
return data
}
private _findVisible = (data: any[], obj: Object3D): any[] => { // only leaf.
if (!obj.visible) return data
if (obj.children.length < 1) data.push(obj.uuid)
else data.push(...obj.children.reduce(this._findVisible, []))
return data
}
private _setVisible = (values: string[]): void => {
this._viewer?.doOnce('postFrame', () => {
const obj = this._viewer?.scene.modelRoot
if (!obj || values === undefined || values === null) return
const ancestorSet: Set<Object3D> = new Set()
obj.traverse((o)=>{
if (o === obj) return
o.visible = values.includes(o.uuid)
if (o.visible) o.traverseAncestors(p=>ancestorSet.add(p))
})
ancestorSet.forEach(o=> o.visible = true)
this._viewer?.scene?.setDirty({refreshScene: true, fromHierarchyPlugin: true, updateGround: false})
})
}
}

+ 1
- 0
plugins/tweakpane-editor/src/index.ts Dosyayı Görüntüle

export {TweakpaneEditorPlugin} from './TweakpaneEditorPlugin' export {TweakpaneEditorPlugin} from './TweakpaneEditorPlugin'
export {HierarchyUiPlugin} from './HierarchyUiPlugin'

+ 8
- 1
src/core/IScene.ts Dosyayı Görüntüle



// | string // | string
export type ISceneEventTypes = IObject3DEventTypes | 'sceneUpdate' | 'addSceneObject' | export type ISceneEventTypes = IObject3DEventTypes | 'sceneUpdate' | 'addSceneObject' |
'mainCameraChange' | 'mainCameraUpdate' | 'environmentChanged' | 'backgroundChanged' |
'mainCameraChange' | 'mainCameraUpdate' | 'environmentChanged' | 'backgroundChanged' | 'renderCameraChange' |
'update' | // todo: deprecate, use 'sceneUpdate' instead 'update' | // todo: deprecate, use 'sceneUpdate' instead
'textureAdded' | // todo remove 'textureAdded' | // todo remove
'activeCameraChange' | 'activeCameraUpdate' | // todo: deprecate 'activeCameraChange' | 'activeCameraUpdate' | // todo: deprecate
extends Scene<E, ET>, IObject3D<E, ET>, IShaderPropertiesUpdater { extends Scene<E, ET>, IObject3D<E, ET>, IShaderPropertiesUpdater {
readonly visible: boolean; readonly visible: boolean;
readonly isScene: true; readonly isScene: true;
/**
* Main camera that the user controls
*/
mainCamera: ICamera; mainCamera: ICamera;
/**
* Camera that in currently being rendered.
*/
renderCamera: ICamera;
type: 'Scene'; type: 'Scene';


toJSON(): any; // todo toJSON(): any; // todo

+ 1
- 1
src/core/camera/PerspectiveCamera2.ts Dosyayı Görüntüle

constructor(controlsMode?: TCameraControlsMode, domElement?: HTMLCanvasElement, autoAspect?: boolean, fov?: number, aspect?: number) { constructor(controlsMode?: TCameraControlsMode, domElement?: HTMLCanvasElement, autoAspect?: boolean, fov?: number, aspect?: number) {
super(fov, aspect) super(fov, aspect)
this._canvas = domElement this._canvas = domElement
this.autoAspect = autoAspect || !!domElement
this.autoAspect = autoAspect ?? !!domElement


iCameraCommons.upgradeCamera.call(this) // todo: test if autoUpgrade = false works as expected if we call upgradeObject3D externally after constructor, because we have setDirty, refreshTarget below. iCameraCommons.upgradeCamera.call(this) // todo: test if autoUpgrade = false works as expected if we call upgradeObject3D externally after constructor, because we have setDirty, refreshTarget below.



+ 10
- 0
src/core/object/RootScene.ts Dosyayı Görüntüle

this.setDirty() this.setDirty()
} }


private _renderCamera: ICamera | undefined
get renderCamera() {
return this._renderCamera ?? this.mainCamera
}
set renderCamera(camera: ICamera) {
const cam = this._renderCamera
this._renderCamera = camera
this.dispatchEvent({type: 'renderCameraChange', lastCamera: cam, camera})
}

/** /**
* Create a scene instance. This is done automatically in the {@link ThreeViewer} and must not be created separately. * Create a scene instance. This is done automatically in the {@link ThreeViewer} and must not be created separately.
* @param camera * @param camera

+ 3
- 0
src/plugins/animation/CameraViewPlugin.ts Dosyayı Görüntüle

}) })


this._animating = false this._animating = false

this._viewer?.setDirty()

this.dispatchEvent({type: 'viewChange', view}) this.dispatchEvent({type: 'viewChange', view})


await timeout(10) await timeout(10)

+ 12
- 0
src/plugins/animation/PopmotionPlugin.ts Dosyayı Görüntüle

} }
this.animations[uuid] = a this.animations[uuid] = a
a.promise = new Promise<void>((resolve, reject) => { a.promise = new Promise<void>((resolve, reject) => {
const end2 = ()=>{
try {
options.onEnd && options.onEnd()
} catch (e: any) {
reject(e)
return false
}
return true
}
const opts: AnimationOptions<V> = { const opts: AnimationOptions<V> = {
driver: this.defaultDriver, driver: this.defaultDriver,
...options, ...options,
try { try {
options.onComplete && options.onComplete() options.onComplete && options.onComplete()
} catch (e: any) { } catch (e: any) {
if (!end2()) return
reject(e) reject(e)
return return
} }
if (!end2()) return
resolve() resolve()
}, },
onStop: ()=>{ onStop: ()=>{
try { try {
options.onStop && options.onStop() options.onStop && options.onStop()
} catch (e: any) { } catch (e: any) {
if (!end2()) return
reject(e) reject(e)
return return
} }

+ 3
- 0
src/plugins/index.ts Dosyayı Görüntüle

export {CustomBumpMapPlugin} from './material/CustomBumpMapPlugin' export {CustomBumpMapPlugin} from './material/CustomBumpMapPlugin'
export {FragmentClippingExtensionPlugin, FragmentClippingMode} from './material/FragmentClippingExtensionPlugin' export {FragmentClippingExtensionPlugin, FragmentClippingMode} from './material/FragmentClippingExtensionPlugin'


// extras
export {VirtualCamerasPlugin} from './rendering/VirtualCamerasPlugin'

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

+ 4
- 1
src/plugins/pipeline/FrameFadePlugin.ts Dosyayı Görüntüle

} }


get canFrameFade() { get canFrameFade() {
return this._target && this._pointerEnabled && this.enabled && this.dirty && this._pass && this._pass.fadeTimeState > 0.001
return this._target && this._pointerEnabled &&
this.enabled && this.dirty && this._pass &&
this._pass.fadeTimeState > 0.001 &&
this._viewer && this._viewer.scene.renderCamera === this._viewer.scene.mainCamera
} }


get lastFrame() { get lastFrame() {

+ 56
- 23
src/plugins/pipeline/ProgressivePlugin.ts Dosyayı Görüntüle

import {uiFolderContainer, uiImage, uiInput} from 'uiconfig.js' import {uiFolderContainer, uiImage, uiInput} from 'uiconfig.js'
import {ICamera, IRenderManager, IScene, IWebGLRenderer} from '../../core' import {ICamera, IRenderManager, IScene, IWebGLRenderer} from '../../core'
import {AddBlendTexturePass} from '../../postprocessing/AddBlendTexturePass' import {AddBlendTexturePass} from '../../postprocessing/AddBlendTexturePass'
import {serialize, ValOrFunc} from 'ts-browser-helpers'
import {getOrCall, serialize, ValOrFunc} from 'ts-browser-helpers'


export type ProgressivePluginEventTypes = '' export type ProgressivePluginEventTypes = ''
export type ProgressivePluginTarget = WebGLRenderTarget export type ProgressivePluginTarget = WebGLRenderTarget
readonly passId = 'progressive' readonly passId = 'progressive'
public static readonly PluginType = 'ProgressivePlugin' public static readonly PluginType = 'ProgressivePlugin'


target?: ProgressivePluginTarget
protected _targets = new Map<string, ProgressivePluginTarget>()


@serialize() @uiInput('Frame count') maxFrameCount: number @serialize() @uiInput('Frame count') maxFrameCount: number
// todo: deserialize jitter // todo: deserialize jitter


@uiImage('Last Texture' /* {readOnly: true}*/) texture?: Texture
// @uiImage('Last Texture' /* {readOnly: true}*/) texture?: Texture

get texture(): Texture | undefined {
return this.target?.texture
}

get target(): ProgressivePluginTarget | undefined {
return this._viewer ? this._targets.get(this._viewer.scene.renderCamera.uuid) : undefined
}

getTarget(camera?: ICamera) {
return this._viewer ? this._targets.get((camera ? camera : this._viewer.scene.renderCamera).uuid) : undefined
}

get textures() {
return this._viewer ? Array.from(this._targets.values()).map(t => t.texture) : []
}

@uiImage('Last Texture' /* {readOnly: true}*/)
get mainTexture() {
return this._viewer ? this.getTarget(this._viewer.scene.mainCamera)?.texture : undefined
}


// @onChange2(ProgressivePlugin.prototype._createTarget) // @onChange2(ProgressivePlugin.prototype._createTarget)
// @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig) // @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)
this.bufferType = bufferType this.bufferType = bufferType
} }


protected _createTarget(recreate = true) {
protected _createTarget(camera?: ICamera, recreate = false) {
if (!this._viewer) return if (!this._viewer) return
if (recreate) this._disposeTarget()
if (!this.target) this.target = this._viewer.renderManager.composerTarget.clone(true) as WebGLRenderTarget

this.texture = this.target.texture
this.texture.name = 'progressiveLastBuffer'

if (this._pass) this._pass.target = this.target
camera = camera ?? this._viewer.scene.renderCamera
if (recreate) this._disposeTarget(camera)
if (this._targets.has(camera.uuid)) return this._targets.get(camera.uuid)
const target = this._viewer.renderManager.composerTarget.clone(true) as WebGLRenderTarget
target.texture.name = 'progressiveLastBuffer_' + camera.uuid
// target.texture.type = this.bufferType
this._targets.set(camera.uuid, target)
// if (this._pass) this._pass.target = this.target
return target
} }


protected _disposeTarget() {
protected _disposeTarget(camera?: ICamera) {
if (!this._viewer) return if (!this._viewer) return
if (this.target) {
this._viewer.renderManager.disposeTarget(this.target)
this.target = undefined
if (!camera) {
this._targets.forEach((t) => this._viewer!.renderManager.disposeTarget(t))
this._targets.clear()
} else {
const t = this._targets.get(camera.uuid)
if (t) {
this._viewer!.renderManager.disposeTarget(t)
this._targets.delete(camera.uuid)
}
} }
this.texture = undefined
} }


protected _createPass() { protected _createPass() {
this._createTarget(true)
if (!this.target) throw new Error('ProgressivePlugin: target not created')
const pass = new ProgressiveBlendPass(this.passId, this.target)
// this._createTarget(true)
const pass = new ProgressiveBlendPass(this.passId, ()=>this.target ?? this._createTarget()) // todo: disposeTarget somewhere
pass.dirty = () => (this._viewer?.renderManager.frameCount || 0) < this.maxFrameCount // todo use isConverged function pass.dirty = () => (this._viewer?.renderManager.frameCount || 0) < this.maxFrameCount // todo use isConverged function
return pass return pass
} }
after = ['render'] after = ['render']
required = ['render'] required = ['render']
dirty: ValOrFunc<boolean> = () => false dirty: ValOrFunc<boolean> = () => false
constructor(public readonly passId: IPassID, public target: WebGLRenderTarget) {
constructor(public readonly passId: IPassID, public target?: ValOrFunc<WebGLRenderTarget|undefined>) {
super() super()
} }
render(renderer: IWebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, deltaTime: number, maskActive: boolean) { render(renderer: IWebGLRenderer, writeBuffer: WebGLRenderTarget, readBuffer: WebGLRenderTarget, deltaTime: number, maskActive: boolean) {
if (!this.enabled) return
const target = getOrCall(this.target)
if (!target) {
console.warn('ProgressiveBlendPass: target not defined')
return
}
if (renderer.renderManager.frameCount < 1) { if (renderer.renderManager.frameCount < 1) {
this.needsSwap = false this.needsSwap = false
if (readBuffer?.texture) if (readBuffer?.texture)
renderer.renderManager.blit(this.target, {
renderer.renderManager.blit(target, {
source: readBuffer.texture, source: readBuffer.texture,
respectColorSpace: false, respectColorSpace: false,
}) })
} }
this.needsSwap = true this.needsSwap = true
super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
renderer.renderManager.blit(this.target, {
renderer.renderManager.blit(target, {
source: writeBuffer.texture, source: writeBuffer.texture,
respectColorSpace: false, respectColorSpace: false,
}) })
this.uniforms.weight.value.set(f, f, f, f) this.uniforms.weight.value.set(f, f, f, f)
f = 1. - f f = 1. - f
this.uniforms.weight2.value.set(f, f, f, f) this.uniforms.weight2.value.set(f, f, f, f)
this.uniforms.tDiffuse2.value = this.target.texture
this.uniforms.tDiffuse2.value = getOrCall(this.target)?.texture
this.material.uniformsNeedUpdate = true this.material.uniformsNeedUpdate = true
} }



+ 58
- 0
src/plugins/rendering/VirtualCamerasPlugin.ts Dosyayı Görüntüle

import {AViewerPluginSync} from '../../viewer'
import {IRenderTarget} from '../../rendering'
import {ICamera} from '../../core'
import {uiFolderContainer, uiToggle} from 'uiconfig.js'

export interface VirtualCamera {
camera: ICamera
target: IRenderTarget
enabled: boolean
}
@uiFolderContainer('Virtual Cameras')
export class VirtualCamerasPlugin extends AViewerPluginSync<''> {
public static readonly PluginType = 'VirtualCamerasPlugin'

@uiToggle()
enabled = true

toJSON: any = undefined // disable serialization

constructor(enabled = true) {
super()
this.enabled = enabled
}

cameras: VirtualCamera[] = []

protected _viewerListeners = {
preRender: () => {
if (!this.enabled || !this._viewer) return
const viewer = this._viewer
for (const v of this.cameras) {
if (!v.enabled) continue
const camera = v.camera
try {
viewer.scene.renderCamera = camera
viewer.renderManager.render(viewer.scene, false)
const source = viewer.renderManager.composer.readBuffer.texture
viewer.renderManager.blit(v.target, {source})
} catch (e: any) {
viewer.console.error(e)
v.enabled = false
if (viewer.debug) throw e
}
}
},
}

addCamera(camera: ICamera) {
if (!this._viewer) throw 'Plugin not added to viewer'
const target = this._viewer.renderManager.composerTarget.clone(true)
target.name = camera.name + '_virtualCamTarget'
const vCam: VirtualCamera = {camera, target, enabled: true}
this.cameras.push(vCam)
// todo: track for jitter in progressive or something else for jittering
return vCam
}

}

+ 5
- 1
src/postprocessing/ScreenPass.ts Dosyayı Görüntüle

this.material.addEventListener('materialUpdate', this.setDirty) this.material.addEventListener('materialUpdate', this.setDirty)
} }


/**
* Output Color Space
* Note: this is ignored when renderToScreen is false (it will take the color space of the render target)
*/
@uiDropdown('Output Color Space', threeConstMappings.ColorSpace.uiConfig, (t: ScreenPass)=>({onChange: t.setDirty})) @uiDropdown('Output Color Space', threeConstMappings.ColorSpace.uiConfig, (t: ScreenPass)=>({onChange: t.setDirty}))
outputColorSpace: ColorSpace = SRGBColorSpace outputColorSpace: ColorSpace = SRGBColorSpace


render(renderer: IWebGLRenderer, writeBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget | null, readBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget, deltaTime?: number, maskActive?: boolean) { render(renderer: IWebGLRenderer, writeBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget | null, readBuffer?: WebGLMultipleRenderTargets | WebGLRenderTarget, deltaTime?: number, maskActive?: boolean) {
const colorSpace = renderer.outputColorSpace const colorSpace = renderer.outputColorSpace
if (!writeBuffer || this.renderToScreen) renderer.outputColorSpace = this.outputColorSpace if (!writeBuffer || this.renderToScreen) renderer.outputColorSpace = this.outputColorSpace
else console.warn('ScreenPass: outputColorSpace is ignored when renderToScreen is false')
// else console.warn('ScreenPass: outputColorSpace is ignored when renderToScreen is false')
super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive) super.render(renderer, writeBuffer, readBuffer, deltaTime, maskActive)
this._lastReadBuffer = readBuffer this._lastReadBuffer = readBuffer
renderer.outputColorSpace = colorSpace renderer.outputColorSpace = colorSpace

+ 8
- 4
src/rendering/RenderManager.ts Dosyayı Görüntüle

// // todo gizmos // // todo gizmos
// } // }


render(scene: IScene): void {
render(scene: IScene, renderToScreen = true): void {
if (this._passesNeedsUpdate) { if (this._passesNeedsUpdate) {
this._refreshPipeline() this._refreshPipeline()
this.refreshPasses() this.refreshPasses()
} }
for (const pass of this._passes) { for (const pass of this._passes) {
if (pass.enabled && pass.beforeRender) pass.beforeRender(scene, scene.mainCamera, this)
if (pass.enabled && pass.beforeRender) pass.beforeRender(scene, scene.renderCamera, this)
} }
this._composer.renderToScreen = renderToScreen
this._composer.render() this._composer.render()
this._frameCount += 1
this._totalFrameCount += 1
this._composer.renderToScreen = true
if (renderToScreen) {
this._frameCount += 1
this._totalFrameCount += 1
}
this._dirty = false this._dirty = false
} }



+ 22
- 0
src/utils/animation.ts Dosyayı Görüntüle

import {MathUtils} from 'three' import {MathUtils} from 'three'


export {animate} export {animate}

declare module 'popmotion'{
interface PlaybackOptions<V> {
// throwOnStop?: boolean; // instead of this, user can simply throw an error in onStop.
onEnd?: () => void;
}
}

export type {AnimationOptions, KeyframeOptions, Easing} export type {AnimationOptions, KeyframeOptions, Easing}


function easeInOutSine(x: number): number { function easeInOutSine(x: number): number {
export async function animateAsync<V=number>(options: AnimationOptions<V>, animations?: AnimateResult[]) { export async function animateAsync<V=number>(options: AnimationOptions<V>, animations?: AnimateResult[]) {
const complete = options.onComplete const complete = options.onComplete
const stop = options.onStop const stop = options.onStop
const end = options.onEnd
options = {...options} options = {...options}
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const end2 = ()=>{
try {
end?.()
} catch (e: any) {
reject(e)
return false
}
return true
}
options.onComplete = ()=>{ options.onComplete = ()=>{
try { try {
complete?.() complete?.()
} catch (e: any) { } catch (e: any) {
if (!end2()) return
reject(e) reject(e)
return return
} }
if (!end2()) return
resolve() resolve()
} }
options.onStop = ()=>{ options.onStop = ()=>{
try { try {
stop?.() stop?.()
} catch (e: any) { } catch (e: any) {
if (!end2()) return
reject(e) reject(e)
return return
} }
if (!end2()) return
resolve() resolve()
} }
const an = animate(options) const an = animate(options)

+ 15
- 2
src/viewer/AViewerPlugin.ts Dosyayı Görüntüle

import type {ISerializedConfig, ThreeViewer} from './ThreeViewer'
import type {ISerializedConfig, IViewerEvent, ThreeViewer} from './ThreeViewer'
import {IViewerEventTypes} from './ThreeViewer'
import {Event, EventDispatcher} from 'three' import {Event, EventDispatcher} from 'three'
import {SerializationMetaType, ThreeSerialization} from '../utils'
import {PartialRecord, SerializationMetaType, ThreeSerialization} from '../utils'
import {IViewerPlugin, IViewerPluginAsync} from './IViewerPlugin' import {IViewerPlugin, IViewerPluginAsync} from './IViewerPlugin'
import {UiObjectConfig} from 'uiconfig.js' import {UiObjectConfig} from 'uiconfig.js'


else this.fromJSON?.(state) else this.fromJSON?.(state)
} }


protected _viewerListeners: PartialRecord<IViewerEventTypes, (e: Event)=>void> = {}
protected _onViewerEvent = (e: IViewerEvent)=> {
const et = e.eType
et && this._viewerListeners[et]?.(e)
return e
}


// todo: move to ThreeViewer // todo: move to ThreeViewer
// storeState(prefix?: string, storage?: Storage, data?: any): void { // storeState(prefix?: string, storage?: Storage, data?: any): void {
// storage = storage || (window ? window.localStorage : undefined) // storage = storage || (window ? window.localStorage : undefined)


onAdded(viewer: TViewer): void { onAdded(viewer: TViewer): void {
this._viewer = viewer this._viewer = viewer
this._viewer.addEventListener('*', this._onViewerEvent)
} }
onRemove(viewer: TViewer): void { onRemove(viewer: TViewer): void {
if (this._viewer !== viewer) viewer.console.error('Wrong viewer') if (this._viewer !== viewer) viewer.console.error('Wrong viewer')
this._viewer?.removeEventListener('*', this._onViewerEvent)
this._viewer = undefined this._viewer = undefined
} }




async onAdded(viewer: TViewer): Promise<void> { async onAdded(viewer: TViewer): Promise<void> {
this._viewer = viewer this._viewer = viewer
this._viewer.addEventListener('*', this._onViewerEvent)
} }
async onRemove(viewer: TViewer): Promise<void> { async onRemove(viewer: TViewer): Promise<void> {
if (this._viewer !== viewer) viewer.console.error('Wrong viewer') if (this._viewer !== viewer) viewer.console.error('Wrong viewer')
this._viewer?.removeEventListener('*', this._onViewerEvent)
this._viewer = undefined this._viewer = undefined
} }
} }

+ 15
- 9
src/viewer/ThreeViewer.ts Dosyayı Görüntüle

import {OrbitControls3} from '../three' 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'
eType?: '*'|'update'|'preRender'|'postRender'|'preFrame'|'postFrame'|'dispose'|'addPlugin'|'renderEnabled'|'renderDisabled'
[p: string]: any
} }
export type IViewerEventTypes = IViewerEvent['type'] export type IViewerEventTypes = IViewerEvent['type']
export interface ISerializedConfig { export interface ISerializedConfig {


this.dispatchEvent({type: 'preRender', target: this}) this.dispatchEvent({type: 'preRender', target: this})


if (this.debug) this.renderManager.render(this._scene)
else {
try {
this.renderManager.render(this._scene)
} catch (e) {
this.console.error(e)
// this.enabled = false
}
try {
this._scene.renderCamera = this._scene.mainCamera
this.renderManager.render(this._scene)
} catch (e) {
this.console.error(e)
if (this.debug) throw e
// this.enabled = false
} }


this.dispatchEvent({type: 'postRender', target: this}) this.dispatchEvent({type: 'postRender', target: this})
}) })
} }


dispatchEvent(event: IViewerEvent) {
super.dispatchEvent(event)
super.dispatchEvent({...event, type: '*', eType: event.type})
}

private _setActiveCameraView(event: any = {}): void { private _setActiveCameraView(event: any = {}): void {
if (event.type === 'setView') { if (event.type === 'setView') {
if (!event.camera) { if (!event.camera) {

Loading…
İptal
Kaydet