Bladeren bron

Add GBufferPlugin, update ts-browser-helpers and uiconfig.js, add support to render and export MRT, add center all geometries in RootScene, other minor changes and refactor.

master
Palash Bansal 2 jaren geleden
bovenliggende
commit
fd88dc852f
No account linked to committer's email address
38 gewijzigde bestanden met toevoegingen van 996 en 156 verwijderingen
  1. 21
    3
      README.md
  2. 35
    0
      examples/gbuffer-plugin/index.html
  3. 80
    0
      examples/gbuffer-plugin/script.ts
  4. 2
    1
      examples/index.html
  5. 7
    3
      examples/tweakpane-editor/script.ts
  6. 28
    28
      package-lock.json
  7. 11
    11
      package.json
  8. 2
    2
      plugins/tweakpane/package-lock.json
  9. 2
    2
      plugins/tweakpane/package.json
  10. 5
    4
      plugins/tweakpane/src/tpImageInputGenerator.ts
  11. 2
    1
      src/assetmanager/export/EXRExporter2.ts
  12. 20
    0
      src/core/IMaterial.ts
  13. 1
    1
      src/core/material/LineMaterial2.ts
  14. 9
    4
      src/core/object/RootScene.ts
  15. 7
    4
      src/plugins/base/PipelinePassPlugin.ts
  16. 2
    0
      src/plugins/index.ts
  17. 7
    2
      src/plugins/interaction/TransformControlsPlugin.ts
  18. 2
    2
      src/plugins/pipeline/DepthBufferPlugin.ts
  19. 431
    0
      src/plugins/pipeline/GBufferPlugin.ts
  20. 1
    1
      src/plugins/pipeline/NormalBufferPlugin.ts
  21. 2
    1
      src/plugins/pipeline/ProgressivePlugin.ts
  22. 119
    0
      src/plugins/pipeline/shaders/GBufferPlugin.mat.frag.glsl
  23. 45
    0
      src/plugins/pipeline/shaders/GBufferPlugin.mat.vert.glsl
  24. 77
    0
      src/plugins/pipeline/shaders/GBufferPlugin.unpack.glsl
  25. 4
    13
      src/plugins/postprocessing/AScreenPassExtensionPlugin.ts
  26. 5
    3
      src/plugins/postprocessing/TonemapPlugin.ts
  27. 0
    18
      src/plugins/ui/GeometryUVPreviewPlugin.ts
  28. 25
    23
      src/plugins/ui/RenderTargetPreviewPlugin.ts
  29. 0
    1
      src/postprocessing/ExtendedRenderPass.ts
  30. 2
    2
      src/postprocessing/ExtendedShaderPass.ts
  31. 1
    1
      src/postprocessing/ScreenPass.ts
  32. 23
    16
      src/rendering/RenderManager.ts
  33. 7
    5
      src/utils/browser-helpers.ts
  34. 1
    1
      src/utils/shader-helpers.ts
  35. 1
    1
      src/viewer/AViewerPlugin.ts
  36. 7
    0
      src/viewer/ThreeViewer.ts
  37. 1
    1
      src/viewer/ViewerRenderManager.ts
  38. 1
    1
      src/viewer/version.ts

+ 21
- 3
README.md Bestand weergeven

- [ProgressivePlugin](#progressiveplugin) - Post-render pass to blend the last frame with the current frame - [ProgressivePlugin](#progressiveplugin) - Post-render pass to blend the last frame with the current frame
- [DepthBufferPlugin](#depthbufferplugin) - Pre-rendering of depth buffer - [DepthBufferPlugin](#depthbufferplugin) - Pre-rendering of depth buffer
- [NormalBufferPlugin](#normalbufferplugin) - Pre-rendering of normal buffer - [NormalBufferPlugin](#normalbufferplugin) - Pre-rendering of normal buffer
- [GBufferPlugin](#depthnormalbufferplugin) - Pre-rendering of depth and normal buffers in a single pass buffer
- [GBufferPlugin](#gbufferplugin) - Pre-rendering of depth-normal and flags buffers in a single pass
- [PickingPlugin](#pickingplugin) - Adds support for selecting objects in the viewer with user interactions and selection widgets - [PickingPlugin](#pickingplugin) - Adds support for selecting objects in the viewer with user interactions and selection widgets
- [TransformControlsPlugin](#transformcontrolsplugin) - Adds support for moving, rotating and scaling objects in the viewer with interactive widgets - [TransformControlsPlugin](#transformcontrolsplugin) - Adds support for moving, rotating and scaling objects in the viewer with interactive widgets
- [GLTFAnimationPlugin](#gltfanimationplugin) - Add support for playing and seeking gltf animations - [GLTFAnimationPlugin](#gltfanimationplugin) - Add support for playing and seeking gltf animations


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. 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.
Note: Use [`GBufferPlugin`](#GBufferPlugin) if using both `DepthBufferPlugin` and `NormalBufferPlugin` to render both depth and normal buffers in a single pass.


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


## GBufferPlugin ## GBufferPlugin


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

[Example](https://threepipe.org/examples/#gbuffer-plugin/) —
[Source Code](./src/plugins/pipeline/GBufferPlugin.ts) —
[API Reference](https://threepipe.org/docs/classes/GBufferPlugin.html)

GBuffer Plugin adds a pre-render pass to the render manager and renders depth+normals to a target and some customizable flags to another. The multiple render target and textures can be accessed by other plugins throughout the rendering pipeline to create effects like SSAO, SSR, etc.

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

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

const gBufferPlugin = viewer.addPluginSync(new GBufferPlugin())

const gBuffer = gBufferPlugin.target;
const normalDepth = gBufferPlugin.normalDepthTexture;
const gBufferFlags = gBufferPlugin.flagsTexture;
```


## PickingPlugin ## PickingPlugin



+ 35
- 0
examples/gbuffer-plugin/index.html Bestand weergeven

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

</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>

+ 80
- 0
examples/gbuffer-plugin/script.ts Bestand weergeven

import {
_testFinish,
downloadBlob,
GBufferPlugin,
HalfFloatType,
IObject3D,
RenderTargetPreviewPlugin,
ThreeViewer,
} from 'threepipe'
import {createSimpleButtons} from '../examples-utils/simple-bottom-buttons.js'

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
msaa: true,
})

async function init() {

const gbufferPlugin = viewer.addPluginSync(new GBufferPlugin(HalfFloatType))

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

let id = 1
model?.traverse((o) => {
o.materials?.forEach(m=>{
if (!m.userData.gBufferData) m.userData.gBufferData = {}
if (!m.userData.gBufferData.materialId) m.userData.gBufferData.materialId = id += 10
})
})

// Disable automatic near/far plane calculation based on scene bounding box
viewer.scene.mainCamera.userData.autoNearFar = false
viewer.scene.mainCamera.userData.minNearPlane = 1
viewer.scene.mainCamera.userData.maxFarPlane = 10
viewer.scene.refreshScene()

const gbufferTarget = gbufferPlugin.target
if (!gbufferTarget) {
throw new Error('gbufferPlugin.target returned undefined')
}

// to render depth buffer to screen, uncomment this line:
// viewer.renderManager.screenPass.overrideReadBuffer = gbufferTarget

const getNormalDepth = ()=>({texture: gbufferPlugin.normalDepthTexture})
const getFlags = ()=>({texture: gbufferPlugin.flagsTexture})

const targetPreview = await viewer.addPlugin(RenderTargetPreviewPlugin)
targetPreview.addTarget(getNormalDepth, 'normalDepth')
targetPreview.addTarget(getFlags, 'gBufferFlags')

const screenPass = viewer.renderManager.screenPass

createSimpleButtons({
['Toggle Normal+Depth']: () => {
const rt = getNormalDepth()
screenPass.overrideReadBuffer = screenPass.overrideReadBuffer?.texture === rt.texture ? null : rt
viewer.setDirty()
},
['Toggle Gbuffer Flags']: () => {
const rt = getFlags()
screenPass.overrideReadBuffer = screenPass.overrideReadBuffer?.texture === rt.texture ? null : rt
viewer.setDirty()
},
['Download snapshot']: async(btn: HTMLButtonElement) => {
btn.disabled = true
const blob = await viewer.getScreenshotBlob({mimeType: 'image/png'})
if (blob) downloadBlob(blob, 'file.png')
else console.error('Unable to get screenshot')
btn.disabled = false
},
})

}

init().then(_testFinish)

+ 2
- 1
examples/index.html Bestand weergeven

<h2 class="category">Rendering</h2> <h2 class="category">Rendering</h2>
<ul> <ul>
<li><a href="./progressive-plugin/">Progressive Plugin </a></li> <li><a href="./progressive-plugin/">Progressive Plugin </a></li>
<li><a href="./custom-pipeline/">Custom Pipeline specification </a></li>
<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="./gbuffer-plugin/">GBuffer Plugin <br/>(NormalDepth+Flags) </a></li>
<li><a href="./virtual-cameras-plugin/">Virtual Cameras Plugin </a></li> <li><a href="./virtual-cameras-plugin/">Virtual Cameras Plugin </a></li>
<li><a href="./virtual-camera/">Virtual Camera (Animated) </a></li> <li><a href="./virtual-camera/">Virtual Camera (Animated) </a></li>
</ul> </ul>

+ 7
- 3
examples/tweakpane-editor/script.ts Bestand weergeven

FragmentClippingExtensionPlugin, FragmentClippingExtensionPlugin,
FrameFadePlugin, FrameFadePlugin,
FullScreenPlugin, FullScreenPlugin,
GBufferPlugin,
GLTFAnimationPlugin, GLTFAnimationPlugin,
HalfFloatType, HalfFloatType,
HDRiGroundPlugin, HDRiGroundPlugin,
new ProgressivePlugin(), new ProgressivePlugin(),
GLTFAnimationPlugin, GLTFAnimationPlugin,
PickingPlugin, PickingPlugin,
TransformControlsPlugin,
new TransformControlsPlugin(false),
EditorViewWidgetPlugin, EditorViewWidgetPlugin,
CameraViewPlugin, CameraViewPlugin,
ViewerUiConfigPlugin, ViewerUiConfigPlugin,
CustomBumpMapPlugin, CustomBumpMapPlugin,
VirtualCamerasPlugin, VirtualCamerasPlugin,
// new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin // new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin
new DepthBufferPlugin(HalfFloatType, true, true),
new GBufferPlugin(HalfFloatType, true, true, true),
new DepthBufferPlugin(HalfFloatType, false, false),
new NormalBufferPlugin(HalfFloatType, false), new NormalBufferPlugin(HalfFloatType, false),
new RenderTargetPreviewPlugin(false), new RenderTargetPreviewPlugin(false),
new FrameFadePlugin(), new FrameFadePlugin(),
]) ])


const rt = viewer.getOrAddPluginSync(RenderTargetPreviewPlugin) const rt = viewer.getOrAddPluginSync(RenderTargetPreviewPlugin)
rt.addTarget({texture: viewer.getPlugin(GBufferPlugin)?.normalDepthTexture}, 'normalDepth')
rt.addTarget({texture: viewer.getPlugin(GBufferPlugin)?.flagsTexture}, 'gBufferFlags')
rt.addTarget(viewer.getPlugin(DepthBufferPlugin)?.target, 'depth', false, false, false) rt.addTarget(viewer.getPlugin(DepthBufferPlugin)?.target, 'depth', false, false, false)
rt.addTarget(viewer.getPlugin(NormalBufferPlugin)?.target, 'normal', false, true, false) rt.addTarget(viewer.getPlugin(NormalBufferPlugin)?.target, 'normal', false, true, false)


editor.loadPlugins({ editor.loadPlugins({
['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin, TweakpaneUiPlugin], ['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin, TweakpaneUiPlugin],
['Interaction']: [HierarchyUiPlugin, TransformControlsPlugin, PickingPlugin, Object3DGeneratorPlugin, GeometryGeneratorPlugin, EditorViewWidgetPlugin, Object3DWidgetsPlugin], ['Interaction']: [HierarchyUiPlugin, TransformControlsPlugin, PickingPlugin, Object3DGeneratorPlugin, GeometryGeneratorPlugin, EditorViewWidgetPlugin, Object3DWidgetsPlugin],
['GBuffer']: [DepthBufferPlugin, NormalBufferPlugin],
['GBuffer']: [GBufferPlugin, DepthBufferPlugin, NormalBufferPlugin],
['Post-processing']: [TonemapPlugin, ProgressivePlugin, FrameFadePlugin, VignettePlugin, ChromaticAberrationPlugin, FilmicGrainPlugin], ['Post-processing']: [TonemapPlugin, ProgressivePlugin, FrameFadePlugin, VignettePlugin, ChromaticAberrationPlugin, FilmicGrainPlugin],
['Animation']: [GLTFAnimationPlugin, CameraViewPlugin], ['Animation']: [GLTFAnimationPlugin, CameraViewPlugin],
['Extras']: [HDRiGroundPlugin, Rhino3dmLoadPlugin, ClearcoatTintPlugin, FragmentClippingExtensionPlugin, NoiseBumpMaterialPlugin, CustomBumpMapPlugin, VirtualCamerasPlugin], ['Extras']: [HDRiGroundPlugin, Rhino3dmLoadPlugin, ClearcoatTintPlugin, FragmentClippingExtensionPlugin, NoiseBumpMaterialPlugin, CustomBumpMapPlugin, VirtualCamerasPlugin],

+ 28
- 28
package-lock.json Bestand weergeven

{ {
"name": "threepipe", "name": "threepipe",
"version": "0.0.20-dev.1",
"version": "0.0.20",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "threepipe", "name": "threepipe",
"version": "0.0.20-dev.1",
"version": "0.0.20",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1019/package.tgz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1020/package.tgz",
"@types/webxr": "^0.5.1", "@types/webxr": "^0.5.1",
"@types/wicg-file-system-access": "^2020.9.5", "@types/wicg-file-system-access": "^2020.9.5",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"ts-browser-helpers": "^0.9.0",
"uiconfig.js": "^0.0.9"
"ts-browser-helpers": "^0.12.0",
"uiconfig.js": "^0.0.12"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^25.0.0", "@rollup/plugin-commonjs": "^25.0.0",
"rollup-plugin-glsl": "^1.3.0", "rollup-plugin-glsl": "^1.3.0",
"rollup-plugin-license": "^3.0.1", "rollup-plugin-license": "^3.0.1",
"rollup-plugin-postcss": "^4.0.2", "rollup-plugin-postcss": "^4.0.2",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2019/package.tgz",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2020/package.tgz",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"typedoc": "^0.25.6", "typedoc": "^0.25.6",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"dev": true "dev": true
}, },
"node_modules/@types/three": { "node_modules/@types/three": {
"version": "0.152.1019",
"resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1019/package.tgz",
"integrity": "sha512-blLZGZVAkpv7HpbUkQZhU8bsVfEKzpSa1qHoey3MkgW+FNiaxFqf2V16tK7etcEF7YK46czMvDGMGc/oOvBoKg==",
"version": "0.152.1020",
"resolved": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1020/package.tgz",
"integrity": "sha512-Xnfma6acJylMQEBYiEOBAUifspGy2ao1UQ2ecYDKAFMwC04fDhTnO2LELluk3j/o/8qaYadOW4OcjvMKLOxTdg==",
"dependencies": { "dependencies": {
"@tweenjs/tween.js": "~18.6.4", "@tweenjs/tween.js": "~18.6.4",
"fflate": "~0.6.9", "fflate": "~0.6.9",
} }
}, },
"node_modules/three": { "node_modules/three": {
"version": "0.152.2019",
"resolved": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2019/package.tgz",
"integrity": "sha512-aszR8Ae48vf9pRqmFzvC6IG7o1yJcEyOsuEEkQL2z9mlTqOHl342cQe9ob8Bp2gN6QJGsUnea/sI0z7j+Xao0g==",
"version": "0.152.2020",
"resolved": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2020/package.tgz",
"integrity": "sha512-XpB018kSE/FZG5xmh6XG22lXTS9bParloATQ952tZaY3p7PsdiP3skXR49GYiLjG2Lui46wA18aq5aXJ9oabGA==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
} }
}, },
"node_modules/ts-browser-helpers": { "node_modules/ts-browser-helpers": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/ts-browser-helpers/-/ts-browser-helpers-0.9.0.tgz",
"integrity": "sha512-8ViPxn9X/K+is+4i8x9H1MB0RmPYFqkrrX2v9EtVZxGbYXLaKmkiL+kfzISUGNOQdzu4E93KjluT9S7cxegs0g==",
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/ts-browser-helpers/-/ts-browser-helpers-0.12.0.tgz",
"integrity": "sha512-lrXA3VZd9OvheHec+fQ3hVeSyLzQI0Bu3fH20/fQXH7Q4wYhZ2IwererEix/mn3j7Apo/W1XgYS8s8Xu7cCypQ==",
"dependencies": { "dependencies": {
"@types/wicg-file-system-access": "^2020.9.5" "@types/wicg-file-system-access": "^2020.9.5"
} }
} }
}, },
"node_modules/uiconfig.js": { "node_modules/uiconfig.js": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.9.tgz",
"integrity": "sha512-jB2LIUTBA7gHegrFr62J7xOEkyXmb+h0/5FczD9qzcBjCbb2R8UsdaxOyVoa+9ecfc2NAaeboTzJK8n8ji1cbw=="
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.12.tgz",
"integrity": "sha512-OnEkWuVYHFQRM9s+SYPvDCFG4NbMcR2vOt5JXt6h91P/PbbJVSWeyfo3ka+KqkuXmJe+x2IJUHqAlbv0VgmbhQ=="
}, },
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.0.2", "version": "1.0.2",
"dev": true "dev": true
}, },
"@types/three": { "@types/three": {
"version": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1019/package.tgz",
"integrity": "sha512-blLZGZVAkpv7HpbUkQZhU8bsVfEKzpSa1qHoey3MkgW+FNiaxFqf2V16tK7etcEF7YK46czMvDGMGc/oOvBoKg==",
"version": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1020/package.tgz",
"integrity": "sha512-Xnfma6acJylMQEBYiEOBAUifspGy2ao1UQ2ecYDKAFMwC04fDhTnO2LELluk3j/o/8qaYadOW4OcjvMKLOxTdg==",
"requires": { "requires": {
"@tweenjs/tween.js": "~18.6.4", "@tweenjs/tween.js": "~18.6.4",
"fflate": "~0.6.9", "fflate": "~0.6.9",
} }
}, },
"three": { "three": {
"version": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2019/package.tgz",
"integrity": "sha512-aszR8Ae48vf9pRqmFzvC6IG7o1yJcEyOsuEEkQL2z9mlTqOHl342cQe9ob8Bp2gN6QJGsUnea/sI0z7j+Xao0g==",
"version": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2020/package.tgz",
"integrity": "sha512-XpB018kSE/FZG5xmh6XG22lXTS9bParloATQ952tZaY3p7PsdiP3skXR49GYiLjG2Lui46wA18aq5aXJ9oabGA==",
"dev": true "dev": true
}, },
"through": { "through": {
"dev": true "dev": true
}, },
"ts-browser-helpers": { "ts-browser-helpers": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/ts-browser-helpers/-/ts-browser-helpers-0.9.0.tgz",
"integrity": "sha512-8ViPxn9X/K+is+4i8x9H1MB0RmPYFqkrrX2v9EtVZxGbYXLaKmkiL+kfzISUGNOQdzu4E93KjluT9S7cxegs0g==",
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/ts-browser-helpers/-/ts-browser-helpers-0.12.0.tgz",
"integrity": "sha512-lrXA3VZd9OvheHec+fQ3hVeSyLzQI0Bu3fH20/fQXH7Q4wYhZ2IwererEix/mn3j7Apo/W1XgYS8s8Xu7cCypQ==",
"requires": { "requires": {
"@types/wicg-file-system-access": "^2020.9.5" "@types/wicg-file-system-access": "^2020.9.5"
} }
"dev": true "dev": true
}, },
"uiconfig.js": { "uiconfig.js": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.9.tgz",
"integrity": "sha512-jB2LIUTBA7gHegrFr62J7xOEkyXmb+h0/5FczD9qzcBjCbb2R8UsdaxOyVoa+9ecfc2NAaeboTzJK8n8ji1cbw=="
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/uiconfig.js/-/uiconfig.js-0.0.12.tgz",
"integrity": "sha512-OnEkWuVYHFQRM9s+SYPvDCFG4NbMcR2vOt5JXt6h91P/PbbJVSWeyfo3ka+KqkuXmJe+x2IJUHqAlbv0VgmbhQ=="
}, },
"unbox-primitive": { "unbox-primitive": {
"version": "1.0.2", "version": "1.0.2",

+ 11
- 11
package.json Bestand weergeven

{ {
"name": "threepipe", "name": "threepipe",
"version": "0.0.20",
"version": "0.0.21",
"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": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.mjs", "module": "dist/index.mjs",
"rollup-plugin-glsl": "^1.3.0", "rollup-plugin-glsl": "^1.3.0",
"rollup-plugin-license": "^3.0.1", "rollup-plugin-license": "^3.0.1",
"rollup-plugin-postcss": "^4.0.2", "rollup-plugin-postcss": "^4.0.2",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2019/package.tgz",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2020/package.tgz",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"typedoc": "^0.25.6", "typedoc": "^0.25.6",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite-plugin-dts": "^3.7.0" "vite-plugin-dts": "^3.7.0"
}, },
"dependencies": { "dependencies": {
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1019/package.tgz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1020/package.tgz",
"@types/webxr": "^0.5.1", "@types/webxr": "^0.5.1",
"@types/wicg-file-system-access": "^2020.9.5", "@types/wicg-file-system-access": "^2020.9.5",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"ts-browser-helpers": "^0.9.0",
"uiconfig.js": "^0.0.9"
"ts-browser-helpers": "^0.12.0",
"uiconfig.js": "^0.0.12"
}, },
"//": { "//": {
"dependencies": { "dependencies": {
"uiconfig.js": "^0.0.9",
"ts-browser-helpers": "^0.8.0",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2019/package.tgz",
"three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2019.tar.gz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1019/package.tgz",
"@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.152.1019.tar.gz",
"uiconfig.js": "^0.0.12",
"ts-browser-helpers": "^0.12.0",
"three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2020/package.tgz",
"three-f": "https://github.com/repalash/three.js-modded/archive/refs/tags/v0.152.2020.tar.gz",
"@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1020/package.tgz",
"@types/three-f": "https://github.com/repalash/three-ts-types/archive/refs/tags/v0.152.1020.tar.gz",
"@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three" "@types/three-pkg": "https://gitpkg.now.sh/repalash/three-ts-types/types/three?modded_three"
}, },
"local_dependencies": { "local_dependencies": {

+ 2
- 2
plugins/tweakpane/package-lock.json Bestand weergeven

{ {
"name": "@threepipe/plugin-tweakpane", "name": "@threepipe/plugin-tweakpane",
"version": "0.2.0",
"version": "0.2.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@threepipe/plugin-tweakpane", "name": "@threepipe/plugin-tweakpane",
"version": "0.2.0",
"version": "0.2.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"threepipe": "file:./../../src/" "threepipe": "file:./../../src/"

+ 2
- 2
plugins/tweakpane/package.json Bestand weergeven

{ {
"name": "@threepipe/plugin-tweakpane", "name": "@threepipe/plugin-tweakpane",
"description": "Tweakpane UI Plugin for ThreePipe", "description": "Tweakpane UI Plugin for ThreePipe",
"version": "0.2.0",
"version": "0.2.1",
"devDependencies": { "devDependencies": {
"tweakpane-image-plugin": "https://github.com/repalash/tweakpane-image-plugin/releases/download/v1.1.404/package.tgz", "tweakpane-image-plugin": "https://github.com/repalash/tweakpane-image-plugin/releases/download/v1.1.404/package.tgz",
"uiconfig-tweakpane": "^0.0.8" "uiconfig-tweakpane": "^0.0.8"
], ],
"replace": { "replace": {
"dependencies": { "dependencies": {
"threepipe": "^0.0.18"
"threepipe": "^0.0.21"
} }
} }
}, },

+ 5
- 4
plugins/tweakpane/src/tpImageInputGenerator.ts Bestand weergeven

cc.image.tp_src = imageBitmapToBase64(cc.image, 160) cc.image.tp_src = imageBitmapToBase64(cc.image, 160)
} else if (cc.isRenderTargetTexture) { } else if (cc.isRenderTargetTexture) {
if (cc._target) { if (cc._target) {
cc.image.tp_src = viewer.renderManager.renderTargetToDataUrl(cc._target)
setTimeout(()=>cc.image.tp_src && delete cc.image.tp_src, 1000) // clear after 1 second so it refreshes on next render
// here we are not doing cc.image.tp_src because cc.image can be shared across multiple textures in MRT
cc.tp_src = viewer.renderManager.renderTargetToDataUrl(cc._target, undefined, undefined, Array.isArray(cc._target.texture) ? cc._target.texture.indexOf(cc) : undefined)
setTimeout(()=>cc.tp_src && delete cc.tp_src, 1000) // clear after 1 second so it refreshes on next render
} }
} else { } else {
cc.image.tp_src = textureToDataUrl(cc, 160, false, 'image/png', 90) // this supports DataTexture also cc.image.tp_src = textureToDataUrl(cc, 160, false, 'image/png', 90) // this supports DataTexture also
} }


if (!cc.image.tp_src) {
if (!cc.image.tp_src && !cc.tp_src) {
if (cc.isRenderTargetTexture) cc.image.tp_src = staticData.renderTarImage if (cc.isRenderTargetTexture) cc.image.tp_src = staticData.renderTarImage
else if (cc.isDataTexture) cc.image.tp_src = staticData.dataTexImage else if (cc.isDataTexture) cc.image.tp_src = staticData.dataTexImage
} }
ret = ret ? staticData.imageMap[ret] : undefined ret = ret ? staticData.imageMap[ret] : undefined
if (!ret) ret = cc.image.tp_src || cc.image.src if (!ret) ret = cc.image.tp_src || cc.image.src
} }
if (cc.tp_src) ret = cc.tp_src
} else if (typeof cc === 'string') { } else if (typeof cc === 'string') {
ret = cc ret = cc
} else if (cc.domainMin) { // for lut CUBE files. } else if (cc.domainMin) { // for lut CUBE files.
cc.image.tp_src_uuid = uuid cc.image.tp_src_uuid = uuid
staticData.tempMap[ret] = uuid staticData.tempMap[ret] = uuid
} }
// console.log(ret, cc, tar, key)
if (typeof ret === 'string') if (typeof ret === 'string')
ret = staticData.imageMap[ret] ?? ret // Note: this will be a bottleneck if the length of src is too long. ret = staticData.imageMap[ret] ?? ret // Note: this will be a bottleneck if the length of src is too long.
return ret return ret

+ 2
- 1
src/assetmanager/export/EXRExporter2.ts Bestand weergeven

const target = <IRenderTarget>obj const target = <IRenderTarget>obj
if (target.isWebGLRenderTarget && !target.renderManager) throw new Error('No renderManager on renderTarget') if (target.isWebGLRenderTarget && !target.renderManager) throw new Error('No renderManager on renderTarget')
if (!target.isWebGLRenderTarget && !(<DataTexture>obj).isDataTexture) throw new Error('Invalid object type') if (!target.isWebGLRenderTarget && !(<DataTexture>obj).isDataTexture) throw new Error('Invalid object type')
if (target.isWebGLMultipleRenderTargets) throw new Error('WebGLMultipleRenderTargets not supported')
if (target.isWebGLMultipleRenderTargets && options.textureIndex === undefined)
console.warn('No textureIndex specified for WebGLMultipleRenderTargets')
const res = target.isWebGLRenderTarget ? const res = target.isWebGLRenderTarget ?
this.parse(target.renderManager!.webglRenderer, <WebGLRenderTarget>target, options) : this.parse(target.renderManager!.webglRenderer, <WebGLRenderTarget>target, options) :
this.parse(undefined, <DataTexture>obj, options) this.parse(undefined, <DataTexture>obj, options)

+ 20
- 0
src/core/IMaterial.ts Bestand weergeven



inverseAlphaMap?: boolean // only for physical material right now inverseAlphaMap?: boolean // only for physical material right now


/**
* See {@link GBufferPlugin}
*/
gBufferData?: {
materialId?: number
/**
* @default true
*/
tonemapEnabled?: boolean
}


[key: string]: any [key: string]: any




// legacy, to be removed // legacy, to be removed
/**
* @deprecated
*/
setDirty?: (options?: IMaterialSetDirtyOptions) => void setDirty?: (options?: IMaterialSetDirtyOptions) => void
/**
* @deprecated
*/
postTonemap?: boolean
} }


export interface IMaterial<E extends IMaterialEvent = IMaterialEvent, ET = IMaterialEventTypes> extends Material<E, ET>, IJSONSerializable, IDisposable, IUiConfigContainer { export interface IMaterial<E extends IMaterialEvent = IMaterialEvent, ET = IMaterialEventTypes> extends Material<E, ET>, IJSONSerializable, IDisposable, IUiConfigContainer {
lightMap?: ITexture | null lightMap?: ITexture | null
normalMap?: ITexture | null normalMap?: ITexture | null
bumpMap?: ITexture | null bumpMap?: ITexture | null
displacementMap?: ITexture | null
aoMapIntensity?: number aoMapIntensity?: number
lightMapIntensity?: number lightMapIntensity?: number
roughnessMap?: ITexture | null roughnessMap?: ITexture | null

+ 1
- 1
src/core/material/LineMaterial2.ts Bestand weergeven

this.setDirty({uiChangeEvent: ev, needsUpdate: false, refreshUi: true}) this.setDirty({uiChangeEvent: ev, needsUpdate: false, refreshUi: true})
}, },
children: [ children: [
...generateUiConfig(this),
...generateUiConfig(this) || [],
iMaterialUI.blending(this), iMaterialUI.blending(this),
iMaterialUI.polygonOffset(this), iMaterialUI.polygonOffset(this),
...iMaterialUI.misc(this), ...iMaterialUI.misc(this),

+ 9
- 4
src/core/object/RootScene.ts Bestand weergeven

import {AddObjectOptions, IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions, IWidget} from '../IScene' import {AddObjectOptions, IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions, IWidget} from '../IScene'
import {iObjectCommons} from './iObjectCommons' import {iObjectCommons} from './iObjectCommons'
import {RootSceneImportResult} from '../../assetmanager' import {RootSceneImportResult} from '../../assetmanager'
import {uiColor, uiConfig, uiFolderContainer, uiImage, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js'
import {uiButton, uiColor, uiConfig, uiFolderContainer, uiImage, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js'
import {IGeometry} from '../IGeometry' import {IGeometry} from '../IGeometry'


@uiFolderContainer('Root Scene') @uiFolderContainer('Root Scene')
obj.userData.autoScaled = true // mark as auto-scaled, so that autoScale is not called again when file is reloaded. obj.userData.autoScaled = true // mark as auto-scaled, so that autoScale is not called again when file is reloaded.
} }
if (centerGeometries && !obj.userData.geometriesCentered) { if (centerGeometries && !obj.userData.geometriesCentered) {
const geoms = new Set<IGeometry>()
obj.traverse((o)=> o.geometry && geoms.add(o.geometry))
geoms.forEach(g=>g.center(undefined, centerGeometriesKeepPosition))
this.centerAllGeometries(centerGeometriesKeepPosition, obj)
obj.userData.geometriesCentered = true obj.userData.geometriesCentered = true
} else { } else {
obj.userData.geometriesCentered = true // mark as centered, so that geometry center is not called again when file is reloaded. obj.userData.geometriesCentered = true // mark as centered, so that geometry center is not called again when file is reloaded.
this.setDirty({refreshScene: true}) this.setDirty({refreshScene: true})
} }


@uiButton()
centerAllGeometries(keepPosition = true, obj?: IObject3D) {
const geoms = new Set<IGeometry>()
;(obj ?? this.modelRoot).traverse((o) => o.geometry && geoms.add(o.geometry))
geoms.forEach(g => g.center(undefined, keepPosition))
}

clearSceneModels(dispose = false, setDirty = true): void { clearSceneModels(dispose = false, setDirty = true): void {
if (dispose) return this.disposeSceneModels(setDirty) if (dispose) return this.disposeSceneModels(setDirty)
this.modelRoot.clear() this.modelRoot.clear()

+ 7
- 4
src/plugins/base/PipelinePassPlugin.ts Bestand weergeven

import {IPassID, IPipelinePass} from '../../postprocessing' import {IPassID, IPipelinePass} from '../../postprocessing'
import {AViewerPluginSync, ISerializedConfig, ThreeViewer} from '../../viewer' import {AViewerPluginSync, ISerializedConfig, ThreeViewer} from '../../viewer'
import {onChange, serialize, wrapThisFunction} from 'ts-browser-helpers'
import {SerializationMetaType} from '../../utils'
import {onChange, serialize} from 'ts-browser-helpers'
import {SerializationMetaType, wrapThisFunction2} from '../../utils'
import {uiToggle} from 'uiconfig.js' import {uiToggle} from 'uiconfig.js'
import {ICamera, IRenderManager, IScene} from '../../core'


export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extends IPassID, TEvent extends string, TViewer extends ThreeViewer=ThreeViewer> extends AViewerPluginSync<TEvent, TViewer> { export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extends IPassID, TEvent extends string, TViewer extends ThreeViewer=ThreeViewer> extends AViewerPluginSync<TEvent, TViewer> {
abstract passId: TPassId abstract passId: TPassId
/** /**
* This function is called every frame before composer render, if this pass is being used in the pipeline * This function is called every frame before composer render, if this pass is being used in the pipeline
* @param _ * @param _
* @param _1
* @param _2
*/ */
protected _beforeRender(): boolean {
protected _beforeRender(_?: IScene, _1?: ICamera, _2?: IRenderManager): boolean {
if (!this._pass) return false if (!this._pass) return false
this._pass.enabled = !this.isDisabled() this._pass.enabled = !this.isDisabled()
return this._pass.enabled return this._pass.enabled


this._pass = this._createPass() this._pass = this._createPass()
this._pass.onDirty?.push(viewer.setDirty) this._pass.onDirty?.push(viewer.setDirty)
this._pass.beforeRender = wrapThisFunction(this._beforeRender, this._pass.beforeRender)
this._pass.beforeRender = wrapThisFunction2(this._beforeRender, this._pass.beforeRender)
viewer.renderManager.registerPass(this._pass) viewer.renderManager.registerPass(this._pass)
} }



+ 2
- 0
src/plugins/index.ts Bestand weergeven



// pipeline // pipeline
export {ProgressivePlugin} from './pipeline/ProgressivePlugin' export {ProgressivePlugin} from './pipeline/ProgressivePlugin'
export {GBufferPlugin, GBufferMaterial, DepthNormalMaterial} from './pipeline/GBufferPlugin'
export {DepthBufferPlugin} from './pipeline/DepthBufferPlugin' export {DepthBufferPlugin} from './pipeline/DepthBufferPlugin'
export {NormalBufferPlugin} from './pipeline/NormalBufferPlugin' export {NormalBufferPlugin} from './pipeline/NormalBufferPlugin'
export {FrameFadePlugin, type FrameFadePluginEventTypes} from './pipeline/FrameFadePlugin' export {FrameFadePlugin, type FrameFadePluginEventTypes} from './pipeline/FrameFadePlugin'
export type {ProgressivePluginEventTypes, ProgressivePluginTarget} from './pipeline/ProgressivePlugin' export type {ProgressivePluginEventTypes, ProgressivePluginTarget} from './pipeline/ProgressivePlugin'
export type {GBufferPluginEventTypes, GBufferPluginPass, GBufferUpdater, GBufferUpdaterContext} from './pipeline/GBufferPlugin'
export type {DepthBufferPluginEventTypes, DepthBufferPluginPass, DepthBufferPluginTarget} from './pipeline/DepthBufferPlugin' export type {DepthBufferPluginEventTypes, DepthBufferPluginPass, DepthBufferPluginTarget} from './pipeline/DepthBufferPlugin'
export type {NormalBufferPluginEventTypes, NormalBufferPluginPass, NormalBufferPluginTarget} from './pipeline/NormalBufferPlugin' export type {NormalBufferPluginEventTypes, NormalBufferPluginPass, NormalBufferPluginTarget} from './pipeline/NormalBufferPlugin'



+ 7
- 2
src/plugins/interaction/TransformControlsPlugin.ts Bestand weergeven

import {uiConfig, uiPanelContainer, uiToggle} from 'uiconfig.js'
import {uiButton, uiConfig, uiPanelContainer, uiToggle} from 'uiconfig.js'
import {AViewerPluginSync, ThreeViewer} from '../../viewer' import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {OrbitControls3, TransformControls2} from '../../three' import {OrbitControls3, TransformControls2} from '../../three'
import {PickingPlugin} from './PickingPlugin' import {PickingPlugin} from './PickingPlugin'
this._viewer.setDirty() this._viewer.setDirty()
} }


constructor() {
constructor(enabled: boolean) {
super() super()
TransformControls.ObjectConstructors.MeshBasicMaterial = UnlitMaterial as any TransformControls.ObjectConstructors.MeshBasicMaterial = UnlitMaterial as any
TransformControls.ObjectConstructors.LineBasicMaterial = UnlitLineMaterial as any TransformControls.ObjectConstructors.LineBasicMaterial = UnlitLineMaterial as any
this.enabled = enabled
} }


toJSON: any = undefined toJSON: any = undefined
this.transformControls.camera = this._viewer.scene.mainCamera this.transformControls.camera = this._viewer.scene.mainCamera
} }


@uiButton('Center All Meshes')
centerAllMeshes() {
this._viewer?.scene.centerAllGeometries(true)
}


} }

+ 2
- 2
src/plugins/pipeline/DepthBufferPlugin.ts Bestand weergeven

this._createTarget(true) this._createTarget(true)
if (!this.target) throw new Error('DepthBufferPlugin: target not created') if (!this.target) throw new Error('DepthBufferPlugin: target not created')
this.material.userData.isGBufferMaterial = true this.material.userData.isGBufferMaterial = true
const pass = new GBufferRenderPass('depth', this.target, this.material, new Color(0, 0, 0), 1)
const pass = new GBufferRenderPass(this.passId, this.target, this.material, new Color(0, 0, 0), 1)
const preprocessMaterial = pass.preprocessMaterial const preprocessMaterial = pass.preprocessMaterial
pass.preprocessMaterial = (m) => preprocessMaterial(m, m.userData.renderToDepth)
pass.preprocessMaterial = (m) => preprocessMaterial(m, m.userData.renderToDepth) // if renderToDepth is undefined then renderToGbuffer is taken internally
pass.before = ['render'] pass.before = ['render']
pass.after = [] pass.after = []
pass.required = ['render'] pass.required = ['render']

+ 431
- 0
src/plugins/pipeline/GBufferPlugin.ts Bestand weergeven

import {
BufferGeometry,
Camera,
Color,
DoubleSide,
GLSL1,
GLSL3,
IUniform,
NoBlending,
NormalMapTypes,
Object3D,
Scene,
ShaderMaterialParameters,
TangentSpaceNormalMap,
Texture,
TextureDataType,
UniformsLib,
UniformsUtils,
UnsignedByteType,
Vector2,
Vector4,
WebGLMultipleRenderTargets,
WebGLRenderer,
WebGLRenderTarget,
} from 'three'
import {GBufferRenderPass} from '../../postprocessing'
import {ThreeViewer} from '../../viewer'
import {MaterialExtension, updateMaterialDefines} from '../../materials'
import {PipelinePassPlugin} from '../base/PipelinePassPlugin'
import {uiFolderContainer, uiImage} from 'uiconfig.js'
import {shaderReplaceString} from '../../utils'
import GBufferUnpack from './shaders/GBufferPlugin.unpack.glsl'
import GBufferMatVert from './shaders/GBufferPlugin.mat.vert.glsl'
import GBufferMatFrag from './shaders/GBufferPlugin.mat.frag.glsl'
import {
ICamera,
IMaterial,
IMaterialParameters,
IRenderManager,
IScene,
ITexture,
PhysicalMaterial,
ShaderMaterial2,
} from '../../core'

export type GBufferPluginEventTypes = ''
type GBufferPluginTarget = WebGLMultipleRenderTargets | WebGLRenderTarget
// export type GBufferPluginTarget = WebGLRenderTarget
export type GBufferPluginPass = GBufferRenderPass<'gbuffer', GBufferPluginTarget>

export interface GBufferUpdaterContext {
material: IMaterial, renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D
}
export interface GBufferUpdater {
updateGBufferFlags: (data: Vector4, context: GBufferUpdaterContext) => void
}

/**
* G-Buffer Plugin
*
* Adds a pre-render pass to render the g-buffer(depth+normal+flags) to render target(s) that can be used as gbuffer and for postprocessing.
* @category Plugins
*/
@uiFolderContainer('G-Buffer Plugin')
export class GBufferPlugin
extends PipelinePassPlugin<GBufferPluginPass, 'gbuffer', GBufferPluginEventTypes> {

readonly passId = 'gbuffer'
public static readonly PluginType = 'GBuffer'

target?: GBufferPluginTarget

// @uiConfig(/* {readOnly: true}*/) // todo: fix bug in uiconfig or tpImageGenerator because of which 0 index is not showing in the UI, when we uncomment this
textures: Texture[] = []

@uiImage(/* {readOnly: true}*/)
get normalDepthTexture(): ITexture|undefined {
return this.textures[0]
}

@uiImage(/* {readOnly: true}*/)
get flagsTexture(): ITexture|undefined {
return this.textures[1]
}
// @uiConfig() // not supported in this material yet
material?: GBufferMaterial

// @onChange(GBufferPlugin.prototype._depthPackingChanged)
// @uiDropdown('Depth Packing', threeConstMappings.DepthPackingStrategies.uiConfig) packing: DepthPackingStrategies

// @onChange2(GBufferPlugin.prototype._createTargetAndMaterial)
// @uiDropdown('Buffer Type', threeConstMappings.TextureDataType.uiConfig)
readonly bufferType: TextureDataType // cannot be changed after creation (for now)

// @uiToggle()
// @onChange2(GBufferPlugin.prototype._createTargetAndMaterial)
readonly isPrimaryGBuffer: boolean // cannot be changed after creation (for now)

// protected _depthPackingChanged() {
// this.material.depthPacking = this.depthPacking
// this.material.needsUpdate = true
// if (this.unpackExtension && this.unpackExtension.extraDefines) {
// this.unpackExtension.extraDefines.DEPTH_PACKING = this.depthPacking
// this.unpackExtension.setDirty?.()
// }
// this.setDirty()
// }

unpackExtension: MaterialExtension = {
shaderExtender: (shader)=>{
shader.fragmentShader = shaderReplaceString(shader.fragmentShader,
'#include <packing>',
'\n' + GBufferUnpack + '\n', {append: true})
},
extraUniforms: {
tNormalDepth: ()=>({value: this.normalDepthTexture}),
tGBufferFlags: ()=>({value: this.flagsTexture}),
},
extraDefines: {
// ['GBUFFER_PACKING']: BasicDepthPacking,
['HAS_NORMAL_DEPTH_BUFFER']: ()=>this.normalDepthTexture ? 1 : undefined,
// ['HAS_FLAGS_BUFFER']: ()=>this.flagsTexture ? 1 : undefined,
['GBUFFER_HAS_FLAGS']: ()=>this.flagsTexture ? 1 : undefined,
['HAS_GBUFFER']: ()=>this.isPrimaryGBuffer && this.normalDepthTexture ? 1 : undefined,
},
priority: 100,
isCompatible: () => true,
}

private _isPrimaryGBufferSet = false
protected _createTargetAndMaterial(recreateTarget = true) {
if (!this._viewer) return
if (recreateTarget) this._disposeTarget()
const useMultiple = this._viewer?.renderManager.isWebGL2 && this.renderFlagsBuffer
if (!this.target) {
this.target = this._viewer.renderManager.createTarget<GBufferPluginTarget>(
{
depthBuffer: true,
samples: this._viewer.renderManager.composerTarget.samples || 0,
type: this.bufferType,
textureCount: useMultiple ? 2 : 1,
// magFilter: NearestFilter,
// minFilter: NearestFilter,
// generateMipmaps: false,
// encoding: LinearEncoding,
})
if (Array.isArray(this.target.texture)) {
this.target.texture[0].name = 'gbufferDepthNormal'
this.target.texture[1].name = 'gbufferFlags'
this.textures = this.target.texture
} else {
this.target.texture.name = 'gbufferDepthNormal'
this.textures.push(this.target.texture)
}
}

if (!this.material) {
this.material = new GBufferMaterial(useMultiple, {
blending: NoBlending,
transparent: true,
})
}

if (this._pass) this._pass.target = this.target

if (this.isPrimaryGBuffer) {
this._viewer.renderManager.gbufferTarget = this.target
this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension])
this._isPrimaryGBufferSet = true
}
}

protected _disposeTarget() {
if (!this._viewer) return
if (this.target) {
this._viewer.renderManager.disposeTarget(this.target)
this.target = undefined
}
this.textures = []
if (this._isPrimaryGBufferSet) { // using a separate flag as when isPrimaryGBuffer is changed, we cannot check it.
this._viewer.renderManager.gbufferTarget = undefined
// this._viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this.unpackExtension]) // todo
this._isPrimaryGBufferSet = false
}
}

protected _createPass() {
this._createTargetAndMaterial(true)
if (!this.target) throw new Error('GBufferPlugin: target not created')
if (!this.material) throw new Error('GBufferPlugin: material not created')
this.material.userData.isGBufferMaterial = true
const pass = new GBufferRenderPass(this.passId, this.target, this.material, new Color(1, 1, 1), 1)
const preprocessMaterial = pass.preprocessMaterial
pass.preprocessMaterial = (m) => preprocessMaterial(m, m.userData.renderToDepth) // if renderToDepth is undefined then renderToGbuffer is taken internally
pass.before = ['render']
pass.after = []
pass.required = ['render']
return pass
}

protected _beforeRender(scene: IScene, camera: ICamera, renderManager: IRenderManager): boolean {
if (!super._beforeRender(scene, camera, renderManager) || !this.material) return false
camera.updateShaderProperties(this.material)
return true
}

constructor(
bufferType: TextureDataType = UnsignedByteType,
isPrimaryGBuffer = true,
enabled = true,
public renderFlagsBuffer: boolean = true
// packing: DepthPackingStrategies = BasicDepthPacking,
) {
super()
this.enabled = enabled
this.bufferType = bufferType
this.isPrimaryGBuffer = isPrimaryGBuffer
// this.depthPacking = depthPacking
}

registerGBufferUpdater(key: string, updater: GBufferUpdater['updateGBufferFlags']): void {
if (this.material) this.material.flagUpdaters.set(key, updater)
}

unregisterGBufferUpdater(key: string): void {
if (this.material) this.material.flagUpdaters.delete(key)
}

onRemove(viewer: ThreeViewer): void {
this._disposeTarget()
this.material?.dispose()
this.material = undefined
return super.onRemove(viewer)
}

/**
* @deprecated use {@link normalDepthTexture} instead
*/
getDepthNormal() {
return this.textures.length > 0 ? this.textures[0] : undefined
}
/**
* @deprecated use {@link flagsTexture} instead
*/
getFlagsTexture() {
return this.textures.length > 1 ? this.textures[1] : undefined
}

/**
* @deprecated use {@link target} instead
*/
getTarget() {
return this.target
}

/**
* @deprecated use {@link unpackExtension} instead
*/
getUnpackSnippet(): string {
return GBufferUnpack
}

/**
* @deprecated use {@link unpackExtension} instead, it adds the same uniforms and defines
* @param material
*/
updateShaderProperties(material: {defines: Record<string, string | number | undefined>; uniforms: {[p: string]: IUniform}, needsUpdate?: boolean}): this {
if (material.uniforms.tNormalDepth) material.uniforms.tNormalDepth.value = this.normalDepthTexture ?? undefined
else this._viewer?.console.warn('BaseRenderer: no uniform: tNormalDepth')
if (material.uniforms.tGBufferFlags) {
material.uniforms.tGBufferFlags.value = this.flagsTexture ?? undefined
const t = material.uniforms.tGBufferFlags.value ? 1 : 0
if (t !== material.defines.GBUFFER_HAS_FLAGS) {
material.defines.GBUFFER_HAS_FLAGS = t
material.needsUpdate = true
}
}
return this
}

}

/**
* Renders DepthNormal to a texture and flags to another
*/
export class GBufferMaterial extends ShaderMaterial2 {

constructor(multipleRT = true, parameters?: ShaderMaterialParameters & IMaterialParameters) {
super({
vertexShader: GBufferMatVert,
fragmentShader: GBufferMatFrag,
uniforms: UniformsUtils.merge([
UniformsLib.common,
UniformsLib.bumpmap,
UniformsLib.normalmap,
UniformsLib.displacementmap,
{
cameraNearFar: {value: new Vector2(0.1, 1000)}, // this has to be set from outside
flags: {value: new Vector4(255, 255, 255, 255)},
},
]),
defines: {
// eslint-disable-next-line @typescript-eslint/naming-convention
IS_GLSL3: multipleRT ? '1' : '0',
},
glslVersion: multipleRT ? GLSL3 : GLSL1,
...parameters,
})
this.reset()
}

flagUpdaters: Map<string, GBufferUpdater['updateGBufferFlags']> = new Map()
normalMapType: NormalMapTypes = TangentSpaceNormalMap
flatShading = false

onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) {
super.onBeforeRender(renderer, scene, camera, geometry, object)

let material = (object as any).material as IMaterial & Partial<PhysicalMaterial>
if (Array.isArray(material)) { // todo: add support for multi materials.
material = material[0]
}
if (!material) return

const setMap = (key: keyof IMaterial)=>{
const map = material[key]
if (!map) return
this.uniforms[key].value = map
if (!this.uniforms[key + 'Transform']) console.error('GBufferMaterial: ' + key + 'Transform is not defined in uniform')
else renderer.materials.refreshTransformUniform(map, this.uniforms[key + 'Transform'])
}

setMap('map')

if (material.side !== undefined) this.side = material.side ?? DoubleSide
setMap('alphaMap')
if (material.alphaTest !== undefined) this.alphaTest = material.alphaTest < 1e-4 ? 1e-4 : material.alphaTest

setMap('bumpMap')
if (material.bumpScale !== undefined) this.uniforms.bumpScale.value = material.bumpScale

setMap('normalMap')
if (material.normalScale !== undefined) this.uniforms.normalScale.value.copy(material.normalScale)
if (material.normalMapType !== undefined) this.normalMapType = material.normalMapType
if (material.flatShading !== undefined) this.flatShading = material.flatShading

setMap('displacementMap')
if (material.displacementScale !== undefined) this.uniforms.displacementScale.value = material.displacementScale
if (material.displacementBias !== undefined) this.uniforms.displacementBias.value = material.displacementBias

if (material.wireframe !== undefined) this.wireframe = material.wireframe
if (material.wireframeLinewidth !== undefined) this.wireframeLinewidth = material.wireframeLinewidth

/*
GBuffer Flags has the following data
1st Rendertarget has Depth and Normal buffers
2nd Render Target::
x : Empty
y : first 3 bits lut index, second 5 bits bevel radius
z : material id (userData.gBufferData?.materialId, userData.matId)
w : this field is for setting bits - lutEnable-0, tonemap-1, bloom-2
*/

this.uniforms.flags.value.set(255, 255, 255, 255)

const materialId = material.userData.gBufferData?.materialId ?? material.userData.matId // matId for backward compatibility
this.uniforms.flags.value.z = materialId || 0

this.flagUpdaters.forEach((updater)=> updater(this.uniforms.flags.value, {material, renderer, scene, camera, geometry, object}))

this.uniforms.flags.value.x /= 255
this.uniforms.flags.value.y /= 255
this.uniforms.flags.value.z /= 255
this.uniforms.flags.value.w /= 255

this.uniformsNeedUpdate = true

updateMaterialDefines({
['USE_ALPHAMAP']: this.uniforms.alphaMap.value ? 1 : undefined,
['ALPHAMAP_UV']: this.uniforms.alphaMap.value ? 'uv' : undefined, // todo use getChannel, see WebGLPrograms.js
['USE_DISPLACEMENTMAP']: this.uniforms.displacementMap.value ? 1 : undefined,
['DISPLACEMENTMAP_UV']: this.uniforms.displacementMap.value ? 'uv' : undefined, // todo use getChannel, see WebGLPrograms.js
['ALPHA_I_RGBA_PACKING']: material.userData.ALPHA_I_RGBA_PACKING ? 1 : undefined,
['FORCED_LINEAR_DEPTH']: material.userData.forcedLinearDepth ?? undefined,
}, material)

// this.transparent = true
this.needsUpdate = true

}

onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) {
super.onAfterRender(renderer, scene, camera, geometry, object)

this.reset()
}

reset() {
this.uniforms.map.value = null
this.side = DoubleSide
this.uniforms.alphaMap.value = null
this.alphaTest = 0.001

this.uniforms.bumpMap.value = null
this.uniforms.bumpScale.value = 1

this.uniforms.normalMap.value = null
this.uniforms.normalScale.value.set(1, 1)
this.normalMapType = TangentSpaceNormalMap
this.flatShading = false

this.uniforms.displacementMap.value = null
this.uniforms.displacementScale.value = 1
this.uniforms.displacementBias.value = 0

this.uniforms.flags.value.set(255, 255, 255, 255)

this.wireframe = false
this.wireframeLinewidth = 1
}
}

/**
* @deprecated use GBufferMaterial instead
*/
export class DepthNormalMaterial extends GBufferMaterial {
constructor(multipleRT: boolean, parameters?: ShaderMaterialParameters & IMaterialParameters) {
super(multipleRT, parameters)
console.warn('DepthNormalMaterial is deprecated, use GBufferMaterial instead')
}
}

+ 1
- 1
src/plugins/pipeline/NormalBufferPlugin.ts Bestand weergeven

this._createTarget(true) this._createTarget(true)
if (!this.target) throw new Error('NormalBufferPlugin: target not created') if (!this.target) throw new Error('NormalBufferPlugin: target not created')
this.material.userData.isGBufferMaterial = true this.material.userData.isGBufferMaterial = true
const pass = new GBufferRenderPass('normal', this.target, this.material, new Color(0, 0, 0), 1)
const pass = new GBufferRenderPass(this.passId, this.target, this.material, new Color(0, 0, 0), 1)
const preprocessMaterial = pass.preprocessMaterial const preprocessMaterial = pass.preprocessMaterial
pass.preprocessMaterial = (m) => preprocessMaterial(m, true) pass.preprocessMaterial = (m) => preprocessMaterial(m, true)
pass.before = ['render'] pass.before = ['render']

+ 2
- 1
src/plugins/pipeline/ProgressivePlugin.ts Bestand weergeven

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 {getOrCall, serialize, ValOrFunc} from 'ts-browser-helpers' import {getOrCall, serialize, ValOrFunc} from 'ts-browser-helpers'
import {IShaderPropertiesUpdater} from '../../materials'


export type ProgressivePluginEventTypes = '' export type ProgressivePluginEventTypes = ''
export type ProgressivePluginTarget = WebGLRenderTarget export type ProgressivePluginTarget = WebGLRenderTarget
*/ */
@uiFolderContainer('Progressive Plugin') @uiFolderContainer('Progressive Plugin')
export class ProgressivePlugin export class ProgressivePlugin
extends PipelinePassPlugin<ProgressiveBlendPass, 'progressive', ProgressivePluginEventTypes> {
extends PipelinePassPlugin<ProgressiveBlendPass, 'progressive', ProgressivePluginEventTypes> implements IShaderPropertiesUpdater {


readonly passId = 'progressive' readonly passId = 'progressive'
public static readonly PluginType = 'ProgressivePlugin' public static readonly PluginType = 'ProgressivePlugin'

+ 119
- 0
src/plugins/pipeline/shaders/GBufferPlugin.mat.frag.glsl Bestand weergeven

// Similar to meshnormal.glsl.js in three.js, check for ref

#define DEPTH_NORMAL

//uniform float opacity;

#if IS_GLSL3 > 0
in vec3 vViewPosition;
#else
varying vec3 vViewPosition;
#endif

#ifdef USE_ALPHAMAP
#define USE_UV

#include <packing>

#endif

#if IS_GLSL3 > 0

#ifndef gl_FragColor // webgl2 with glsl3
layout(location = 0) out vec4 gDepthNormal;
layout(location = 1) out vec4 gFlags;
#endif

#endif

#include <uv_pars_fragment>
#include <normal_pars_fragment>
#include <map_pars_fragment>
#include <alphamap_pars_fragment>
#include <alphatest_pars_fragment>
#include <bumpmap_pars_fragment>
#include <normalmap_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>

uniform vec2 cameraNearFar;
uniform vec4 flags;

//vec2 pack16(float value) {
// float sMax = 65535.0;
// int v = int(clamp(value, 0.0, 1.0)*sMax+0.5);
// int digit0 = v/256;
// int digit1 = v-digit0*256;
// return vec2(float(digit0)/255.0, float(digit1)/255.0);
//}
vec2 pack16(float value){
float f = clamp(value, 0.0, 1.0)*255.0;
float digitLow = fract(f);
float digitHigh = floor(f)/255.0;
return vec2(digitHigh, digitLow);
}
//float unpack16(vec2 value){
// return value.x+value.y/255.0;
//}

vec2 packNormal(vec3 n){
float p = sqrt(n.z*8.0+8.0);
return vec2(n.xy/p + 0.5);
}

float linstep(float edge0, float edge1, float value) {
return clamp((value-edge0)/(edge1-edge0), 0.0, 1.0);
}

void main() {
#glMarker mainStart

#include <clipping_planes_fragment>

vec4 diffuseColor = vec4( 1.0 );

#include <map_fragment>

//#/include <alphamap_fragment> // changed for ALPHA_I_RGBA_PACKING
#ifdef USE_ALPHAMAP

float alphaMapValue =
#ifdef ALPHA_I_RGBA_PACKING
1. - unpackRGBAToDepth( texture2D( alphaMap, vAlphaMapUv ) );
#else
texture2D( alphaMap, vAlphaMapUv ).g;
#endif

#if defined(INVERSE_ALPHAMAP) && INVERSE_ALPHAMAP >= 1
diffuseColor.a *= 1.0 - alphaMapValue;
#else
diffuseColor.a *= alphaMapValue;
#endif

#endif

#include <alphatest_fragment>

#include <logdepthbuf_fragment>
#include <normal_fragment_begin>
#include <normal_fragment_maps>

#ifdef FORCED_LINEAR_DEPTH
float linearZ = float(FORCED_LINEAR_DEPTH);
#else
float linearZ = linstep(-cameraNearFar.x, -cameraNearFar.y, -vViewPosition.z);
#endif
vec2 packedZ = pack16(pow(linearZ, 0.5));
vec2 packedNormal = packNormal(normal);

#if IS_GLSL3 > 0
#ifndef gl_FragColor // webgl2 with glsl3
gDepthNormal = vec4(packedZ.x, packedZ.y, packedNormal.x, packedNormal.y);
gFlags = flags;
#else
gl_FragColor = vec4(packedZ.x, packedZ.y, packedNormal.x, packedNormal.y);
#endif
#else
gl_FragColor = vec4(packedZ.x, packedZ.y, packedNormal.x, packedNormal.y);
#endif
}

+ 45
- 0
src/plugins/pipeline/shaders/GBufferPlugin.mat.vert.glsl Bestand weergeven

// Pretty much the same as meshnormal.glsl.js in three.js with minor changes.

#define DEPTH_NORMAL

#if IS_GLSL3 > 0
out vec3 vViewPosition;
#else
varying vec3 vViewPosition;
#endif

#ifdef USE_ALPHAMAP
#define USE_UV // see todo in GBufferMaterialOverride updateMaterialDefines
#endif

//#/include <common>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <normal_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>

void main() {

#include <uv_vertex>

#include <beginnormal_vertex>
#include <morphnormal_vertex>
#include <skinbase_vertex>
#include <skinnormal_vertex>
#include <defaultnormal_vertex>
#include <normal_vertex>

#include <begin_vertex>
#include <morphtarget_vertex>
#include <skinning_vertex>
#include <displacementmap_vertex>
#include <project_vertex>
#include <logdepthbuf_vertex>
#include <clipping_planes_vertex>

vViewPosition = - mvPosition.xyz;

}

+ 77
- 0
src/plugins/pipeline/shaders/GBufferPlugin.unpack.glsl Bestand weergeven

#ifndef UNPACK_GBUFFER_SNIPPET
#define UNPACK_GBUFFER_SNIPPET

//precision highp usampler2D;

uniform sampler2D tNormalDepth;
//float unpack16(vec2 value) {
// return (
// value.x*0.996108949416342426275150501169264316558837890625 +
// value.y*0.00389105058365758760263730664519243873655796051025390625
// );
//}
float unpack16(vec2 value){
return value.x+value.y/255.0;
}

vec3 unpackNormal(vec2 enc) {
vec2 fenc = enc*4.0-2.0;
float f = dot(fenc, fenc);
float g = sqrt(1.0-f/4.0);
return vec3(fenc*g, 1.0-f/2.0);
}
float unpackDepth(vec2 uncodedDepth) {
float x = unpack16(uncodedDepth.xy);
return x*x;
}
#define getDepth(uv) unpackDepth(texture2D(tNormalDepth, uv).xy)

void getDepthNormal(const in vec2 uv, out float depth, out vec3 normal){
vec4 uncodedDepth = texture2D(tNormalDepth, uv);
depth = unpackDepth(uncodedDepth.xy);
normal = unpackNormal(uncodedDepth.zw);
}
vec3 getViewNormal(const in vec2 uv ) {
// #if DEPTH_NORMAL_TEXTURE == 1
return unpackNormal( texture2D( tNormalDepth, uv ).zw );
// #else
// return unpackRGBToNormal( texture2D( tNormal, uv ).xyz );
// #endif
}
#if defined(GBUFFER_HAS_FLAGS) && GBUFFER_HAS_FLAGS == 1
uniform sampler2D tGBufferFlags;
#endif
ivec4 getGBufferFlags(const in vec2 uv){
#if defined(GBUFFER_HAS_FLAGS) && GBUFFER_HAS_FLAGS == 1
return ivec4(texture2D(tGBufferFlags, uv) * 255.);
#else
return ivec4(1);
#endif
}
//#if DEPTH_NORMAL_TEXTURE == 1
//uniform sampler2D tNormalDepth;
//#else
//uniform sampler2D tNormal;
//#endif

//float decodeDepth( const in vec2 uv ) {
// vec4 uncodedDepth;
// #if DEPTH_PACKING_MODE == 2
// uncodedDepth = texture2D( tNormalDepth, uv );
// #else
// uncodedDepth = texture2D( tDepth, uv );
// #endif
//
// #if DEPTH_PACKING_MODE == 0
// return uncodedDepth.x;
// #elif DEPTH_PACKING_MODE == 1
// #if LINEAR_DEPTH == 1
// return pow2(unpackRGBAToDepth(uncodedDepth));
// #else
// return unpackRGBAToDepth( uncodedDepth );
// #endif
// #else
// return pow2(unpack16(uncodedDepth.xy));
// #endif
//}
#endif

+ 4
- 13
src/plugins/postprocessing/AScreenPassExtensionPlugin.ts Bestand weergeven

import {Shader, Vector4, WebGLRenderer} from 'three' import {Shader, Vector4, WebGLRenderer} from 'three'
import {IMaterial} from '../../core' import {IMaterial} from '../../core'
import {shaderReplaceString} from '../../utils' import {shaderReplaceString} from '../../utils'

// todo move
export interface GBufferUpdater {
updateGBufferFlags: (material: IMaterial, data: Vector4) => void
}
import {GBufferPlugin, GBufferUpdater, GBufferUpdaterContext} from '../pipeline/GBufferPlugin'


/** /**
* Base Screen Pass Extension Plugin * Base Screen Pass Extension Plugin


onAdded(viewer: ThreeViewer) { onAdded(viewer: ThreeViewer) {
super.onAdded(viewer) super.onAdded(viewer)
// viewer.getPlugin(GBufferPlugin)?.registerGBufferUpdater(this.updateGBufferFlags) // todo
viewer.getPlugin(GBufferPlugin)?.registerGBufferUpdater(this.constructor.PluginType, this.updateGBufferFlags)
viewer.renderManager.screenPass.material.registerMaterialExtensions([this]) viewer.renderManager.screenPass.material.registerMaterialExtensions([this])
} }


onRemove(viewer: ThreeViewer) { onRemove(viewer: ThreeViewer) {
// viewer.getPlugin(GBufferPlugin)?.unregisterGBufferUpdater(this.updateGBufferFlags)
viewer.getPlugin(GBufferPlugin)?.unregisterGBufferUpdater(this.constructor.PluginType)
viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this]) viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this])
super.onRemove(viewer) super.onRemove(viewer)
} }


// updateGBufferFlags(material: IMaterial, data: Vector4): void {
// const x = material?.userData.postTonemap === false ? 0 : 1
// data.w = updateBit(data.w, 1, x) // 2nd Bit
// }

// for typescript // for typescript
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
__setDirty?: () => void __setDirty?: () => void


updateGBufferFlags(_: IMaterial, _1: Vector4): void {
updateGBufferFlags(_: Vector4, _1: GBufferUpdaterContext): void {
return return
} }



+ 5
- 3
src/plugins/postprocessing/TonemapPlugin.ts Bestand weergeven

import TonemapShader from './shaders/TonemapPlugin.pars.glsl' import TonemapShader from './shaders/TonemapPlugin.pars.glsl'
import TonemapShaderPatch from './shaders/TonemapPlugin.patch.glsl' import TonemapShaderPatch from './shaders/TonemapPlugin.patch.glsl'
import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin' import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin'
import {GBufferUpdaterContext} from '../pipeline/GBufferPlugin'


// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping
return super.fromJSON(data, meta) return super.fromJSON(data, meta)
} }


updateGBufferFlags(material: IMaterial, data: Vector4): void {
const x = material?.userData.postTonemap === false ? 0 : 1
// TODO: add gBufferData or just tonemapEnabled to the scene material UI with an extension
updateGBufferFlags(data: Vector4, c: GBufferUpdaterContext): void {
const x = (c.material.userData.gBufferData?.tonemapEnabled ?? c.material?.userData.postTonemap) === false ? 0 : 1
data.w = updateBit(data.w, 1, x) // 2nd Bit data.w = updateBit(data.w, 1, x) // 2nd Bit
super.updateGBufferFlags(material, data)
super.updateGBufferFlags(data, c)
} }


static { static {

+ 0
- 18
src/plugins/ui/GeometryUVPreviewPlugin.ts Bestand weergeven

target.uvCanvas.style.height = '100%' target.uvCanvas.style.height = '100%'
} }
if (target.uvCanvas && target.uvCanvas.parentElement !== target.div) target.div.appendChild(target.uvCanvas) if (target.uvCanvas && target.uvCanvas.parentElement !== target.div) target.div.appendChild(target.uvCanvas)
// const rect = target.div.getBoundingClientRect()
// const canvasRect = this._viewer.canvas.getBoundingClientRect()
// rect.x = rect.x - canvasRect.x
// rect.y = canvasRect.height + canvasRect.y - rect.y - rect.height
// if (Array.isArray(tex)) {
// // todo support multi target
// this._viewer.console.warn('Multi target preview not supported yet')
// continue
// }
// const outputColorSpace = this._viewer.renderManager.webglRenderer.outputColorSpace
// if (!target.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace
// this._viewer.renderManager.blit(null, {
// source: tex,
// clear: !target.transparent,
// respectColorSpace: !target.originalColorSpace,
// viewport: new Vector4(rect.x, rect.y, rect.width, rect.height),
// })
// this._viewer.renderManager.webglRenderer.outputColorSpace = outputColorSpace
} }
} }



+ 25
- 23
src/plugins/ui/RenderTargetPreviewPlugin.ts Bestand weergeven

import {AViewerPluginSync, ThreeViewer} from '../../viewer' import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {IRenderTarget} from '../../rendering' import {IRenderTarget} from '../../rendering'
import {createDiv, createStyles, getOrCall, onChange, ValOrFunc} from 'ts-browser-helpers'
import {createDiv, createStyles, getOrCall, onChange, ValOrArr, ValOrFunc} from 'ts-browser-helpers'
import {SRGBColorSpace, Vector4, WebGLRenderTarget} from 'three' import {SRGBColorSpace, Vector4, WebGLRenderTarget} from 'three'
import styles from './RenderTargetPreviewPlugin.css?inline' import styles from './RenderTargetPreviewPlugin.css?inline'
import {CustomContextMenu} from '../../utils' import {CustomContextMenu} from '../../utils'
import {uiFolderContainer, uiToggle} from 'uiconfig.js' import {uiFolderContainer, uiToggle} from 'uiconfig.js'

import {ITexture} from '../../core'

export interface RenderTargetBlock {
target: ValOrFunc<IRenderTarget|{texture?: ValOrArr<ITexture>}|undefined>
name: string
visible: boolean
transparent: boolean
originalColorSpace: boolean
div: HTMLDivElement
}
@uiFolderContainer('Render Target Preview Plugin') @uiFolderContainer('Render Target Preview Plugin')
export class RenderTargetPreviewPlugin<TEvent extends string> extends AViewerPluginSync<TEvent> { export class RenderTargetPreviewPlugin<TEvent extends string> extends AViewerPluginSync<TEvent> {
static readonly PluginType = 'RenderTargetPreviewPlugin' static readonly PluginType = 'RenderTargetPreviewPlugin'
this.enabled = enabled this.enabled = enabled
} }


targetBlocks: {
target: ValOrFunc<IRenderTarget|undefined>
name: string
visible: boolean
transparent: boolean
originalColorSpace: boolean
div: HTMLDivElement
}[] = []
targetBlocks: RenderTargetBlock[] = []


onAdded(viewer: ThreeViewer): void { onAdded(viewer: ThreeViewer): void {
super.onAdded(viewer) super.onAdded(viewer)
private _postRender = () => { private _postRender = () => {
if (!this._viewer) return if (!this._viewer) return


for (const target of this.targetBlocks) {
if (!target.visible) continue
const rt = getOrCall(target.target)
for (const targetBlock of this.targetBlocks) {
if (!targetBlock.visible) continue
const rt = getOrCall(targetBlock.target)
if (!rt) { if (!rt) {
// todo draw white or pink // todo draw white or pink
continue continue
} }
const rect = target.div.getBoundingClientRect()
const tex = rt.texture
const rect = targetBlock.div.getBoundingClientRect()
let tex = rt.texture
const canvasRect = this._viewer.canvas.getBoundingClientRect() const canvasRect = this._viewer.canvas.getBoundingClientRect()
rect.x = rect.x - canvasRect.x rect.x = rect.x - canvasRect.x
rect.y = canvasRect.height + canvasRect.y - rect.y - rect.height rect.y = canvasRect.height + canvasRect.y - rect.y - rect.height
if (Array.isArray(tex)) { if (Array.isArray(tex)) {
// todo support multi target // todo support multi target
this._viewer.console.warn('Multi target preview not supported yet')
continue
this._viewer.console.warn('Multi target preview not supported yet, rendering just the first one')
tex = tex[0]
} }
const outputColorSpace = this._viewer.renderManager.webglRenderer.outputColorSpace const outputColorSpace = this._viewer.renderManager.webglRenderer.outputColorSpace
if (!target.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace
if (!targetBlock.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace
this._viewer.renderManager.blit(null, { this._viewer.renderManager.blit(null, {
source: tex, source: tex,
clear: !target.transparent,
respectColorSpace: !target.originalColorSpace,
clear: !targetBlock.transparent,
respectColorSpace: !targetBlock.originalColorSpace,
viewport: new Vector4(rect.x, rect.y, rect.width, rect.height), viewport: new Vector4(rect.x, rect.y, rect.width, rect.height),
}) })
this._viewer.renderManager.webglRenderer.outputColorSpace = outputColorSpace this._viewer.renderManager.webglRenderer.outputColorSpace = outputColorSpace
} }
} }


addTarget(target: ValOrFunc<IRenderTarget|undefined>, name: string, transparent = false, originalColorSpace = false, visible = true): this {
addTarget(target: RenderTargetBlock['target'], name: string, transparent = false, originalColorSpace = false, visible = true): this {
if (!target) return this if (!target) return this
const div = document.createElement('div') const div = document.createElement('div')
const targetDef = {target, name, transparent, div, originalColorSpace, visible} const targetDef = {target, name, transparent, div, originalColorSpace, visible}
return this return this
} }


removeTarget(target: ValOrFunc<IRenderTarget|undefined>): this {
removeTarget(target: RenderTargetBlock['target']): this {
const index = this.targetBlocks.findIndex(t => t.target === target) const index = this.targetBlocks.findIndex(t => t.target === target)
if (index >= 0) { if (index >= 0) {
const t = this.targetBlocks[index] const t = this.targetBlocks[index]
this.refreshUi() this.refreshUi()
return this return this
} }
downloadTarget(target1: ValOrFunc<IRenderTarget|undefined>): this {
downloadTarget(target1: RenderTargetBlock['target']): this {
if (!this._viewer) return this if (!this._viewer) return this
const target = getOrCall(target1) const target = getOrCall(target1)
if (!target) return this if (!target) return this

+ 0
- 1
src/postprocessing/ExtendedRenderPass.ts Bestand weergeven

this._transparentTarget = undefined this._transparentTarget = undefined
} }



constructor(renderManager: ViewerRenderManager, overrideMaterial?: Material, clearColor = new Color(0, 0, 0), clearAlpha = 0) { constructor(renderManager: ViewerRenderManager, overrideMaterial?: Material, clearColor = new Color(0, 0, 0), clearAlpha = 0) {
super(undefined, undefined, overrideMaterial, clearColor, clearAlpha) super(undefined, undefined, overrideMaterial, clearColor, clearAlpha)
this.renderManager = renderManager this.renderManager = renderManager

+ 2
- 2
src/postprocessing/ExtendedShaderPass.ts Bestand weergeven

public static readonly DEFAULT_TEX_ID = 'tDiffuse' public static readonly DEFAULT_TEX_ID = 'tDiffuse'


material!: ShaderMaterial2 material!: ShaderMaterial2
overrideReadBuffer: WebGLRenderTarget|null = null
overrideReadBuffer: {texture?: WebGLRenderTarget['texture']}|null = null


readonly isExtendedShaderPass = true readonly isExtendedShaderPass = true
// private _textureIDs: string[] // private _textureIDs: string[]
renderer.renderWithModes({ renderer.renderWithModes({
backgroundRender: false, backgroundRender: false,
}, ()=>{ }, ()=>{
super.render(renderer, writeBuffer || null, this.overrideReadBuffer || readBuffer, deltaTime, maskActive)
super.render(renderer, writeBuffer || null, (this.overrideReadBuffer as WebGLRenderTarget) || readBuffer, deltaTime, maskActive)
}) })
} }



+ 1
- 1
src/postprocessing/ScreenPass.ts Bestand weergeven

super( super(
(<any>shader)?.fragmentShader || (<ShaderMaterial2>shader)?.isShaderMaterial ? <ShaderMaterialParameters|ShaderMaterial2>shader : (<any>shader)?.fragmentShader || (<ShaderMaterial2>shader)?.isShaderMaterial ? <ShaderMaterialParameters|ShaderMaterial2>shader :
makeScreenShader(shader), makeScreenShader(shader),
...textureID.length ? textureID : ['tDiffuse', 'tTransparent', 'tGBuffer'])
...textureID.length ? textureID : ['tDiffuse', 'tTransparent'])
this.material.addEventListener('materialUpdate', this.setDirty) this.material.addEventListener('materialUpdate', this.setDirty)
} }



+ 23
- 16
src/rendering/RenderManager.ts Bestand weergeven

Texture, Texture,
Vector2, Vector2,
Vector4, Vector4,
WebGLMultipleRenderTargets,
WebGLRenderer, WebGLRenderer,
WebGLRenderTarget, WebGLRenderTarget,
WebGLRenderTargetOptions, WebGLRenderTargetOptions,
* @param target * @param target
* @param mimeType * @param mimeType
* @param quality * @param quality
* @param textureIndex - index of the texture to use in the render target (only in case of multiple render target)
*/ */
renderTargetToDataUrl(target: WebGLRenderTarget, mimeType = 'image/png', quality = 90): string {
renderTargetToDataUrl(target: WebGLMultipleRenderTargets|WebGLRenderTarget, mimeType = 'image/png', quality = 90, textureIndex = 0): string {
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
canvas.width = target.width canvas.width = target.width
canvas.height = target.height canvas.height = target.height
const ctx = canvas.getContext('2d') const ctx = canvas.getContext('2d')
if (!ctx) throw new Error('Unable to get 2d context') if (!ctx) throw new Error('Unable to get 2d context')
const imageData = ctx.createImageData(target.width, target.height, {colorSpace: ['display-p3', 'srgb'].includes(target.texture.colorSpace) ? <PredefinedColorSpace>target.texture.colorSpace : undefined})
if (target.texture.type === HalfFloatType || target.texture.type === FloatType) {
const buffer = this.renderTargetToBuffer(target)
textureDataToImageData({data: buffer, width: target.width, height: target.height}, target.texture.colorSpace, imageData) // this handles converting to srgb
const texture = Array.isArray(target.texture) ? target.texture[textureIndex] : target.texture
const imageData = ctx.createImageData(target.width, target.height, {colorSpace: ['display-p3', 'srgb'].includes(texture.colorSpace) ? <PredefinedColorSpace>texture.colorSpace : undefined})
if (texture.type === HalfFloatType || texture.type === FloatType) {
const buffer = this.renderTargetToBuffer(target, textureIndex)
textureDataToImageData({data: buffer, width: target.width, height: target.height}, texture.colorSpace, imageData) // this handles converting to srgb
} else { } else {
// todo: handle rgbm to srgb conversion? // todo: handle rgbm to srgb conversion?
this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, imageData.data)
this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, imageData.data, undefined, textureIndex)
} }


ctx.putImageData(imageData, 0, 0) ctx.putImageData(imageData, 0, 0)


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


/** /**
* Rend pixels from a render target into a new Uint8Array|Uint16Array|Float32Array buffer * Rend pixels from a render target into a new Uint8Array|Uint16Array|Float32Array buffer
* @param target
* @param target - render target to read from
* @param textureIndex - index of the texture to use in the render target (only in case of multiple render target)
*/ */
renderTargetToBuffer(target: WebGLRenderTarget): Uint8Array|Uint16Array|Float32Array {
renderTargetToBuffer(target: WebGLMultipleRenderTargets|WebGLRenderTarget, textureIndex = 0): Uint8Array|Uint16Array|Float32Array {
const texture = Array.isArray(target.texture) ? target.texture[textureIndex] : target.texture
const buffer = const buffer =
target.texture.type === HalfFloatType ?
texture.type === HalfFloatType ?
new Uint16Array(target.width * target.height * 4) : new Uint16Array(target.width * target.height * 4) :
target.texture.type === FloatType ?
texture.type === FloatType ?
new Float32Array(target.width * target.height * 4) : new Float32Array(target.width * target.height * 4) :
new Uint8Array(target.width * target.height * 4) new Uint8Array(target.width * target.height * 4)
this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, buffer)
this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, buffer, undefined, textureIndex)
return buffer return buffer
} }


* @param target - render target to export * @param target - render target to export
* @param mimeType - mime type to use. * @param mimeType - mime type to use.
* If auto (default), then it will be picked based on the render target type. * If auto (default), then it will be picked based on the render target type.
* @param textureIndex - index of the texture to use in the render target (only in case of multiple render target)
*/ */
exportRenderTarget(target: WebGLRenderTarget, mimeType = 'auto'): BlobExt {
exportRenderTarget(target: WebGLMultipleRenderTargets|WebGLRenderTarget, mimeType = 'auto', textureIndex = 0): BlobExt {
const hdrFormats = ['image/x-exr'] const hdrFormats = ['image/x-exr']
let hdr = target.texture.type === HalfFloatType || target.texture.type === FloatType
const texture = Array.isArray(target.texture) ? target.texture[textureIndex] : target.texture
let hdr = texture.type === HalfFloatType || texture.type === FloatType
if (mimeType === 'auto') { if (mimeType === 'auto') {
mimeType = hdr ? 'image/x-exr' : 'image/png' mimeType = hdr ? 'image/x-exr' : 'image/png'
} }
if (!hdrFormats.includes(mimeType)) hdr = false if (!hdrFormats.includes(mimeType)) hdr = false
let buffer: ArrayBufferLike let buffer: ArrayBufferLike
if (!hdr) { if (!hdr) {
const url = this.renderTargetToDataUrl(target, mimeType === 'auto' ? undefined : mimeType)
const url = this.renderTargetToDataUrl(target, mimeType === 'auto' ? undefined : mimeType, 90, textureIndex)
buffer = base64ToArrayBuffer(url.split(',')[1]) buffer = base64ToArrayBuffer(url.split(',')[1])
mimeType = url.split(';')[0].split(':')[1] mimeType = url.split(';')[0].split(':')[1]
} else { } else {
mimeType = 'image/x-exr' mimeType = 'image/x-exr'
} }
const exporter = new EXRExporter2() const exporter = new EXRExporter2()
buffer = exporter.parse(this._renderer, target)
buffer = exporter.parse(this._renderer, target, {textureIndex})
} }
const b = new Blob([buffer], {type: mimeType}) as BlobExt const b = new Blob([buffer], {type: mimeType}) as BlobExt
b.ext = mimeType === 'image/x-exr' ? 'exr' : mimeType.split('/')[1] b.ext = mimeType === 'image/x-exr' ? 'exr' : mimeType.split('/')[1]

+ 7
- 5
src/utils/browser-helpers.ts Bestand weergeven

export type {ImageCanvasOptions} from 'ts-browser-helpers' export type {ImageCanvasOptions} from 'ts-browser-helpers'
export type {AnyFunction, AnyOptions, Class, IDisposable, IJSONSerializable, PartialPick, PartialRecord, StringKeyOf, Fof, ValOrFunc, ValOrArr, ValOrFuncOp, ValOrArrOp} from 'ts-browser-helpers' export type {AnyFunction, AnyOptions, Class, IDisposable, IJSONSerializable, PartialPick, PartialRecord, StringKeyOf, Fof, ValOrFunc, ValOrArr, ValOrFuncOp, ValOrArrOp} from 'ts-browser-helpers'
export type {Serializer} from 'ts-browser-helpers' export type {Serializer} from 'ts-browser-helpers'

export {PointerDragHelper} from 'ts-browser-helpers' export {PointerDragHelper} from 'ts-browser-helpers'
export {Damper} from 'ts-browser-helpers' export {Damper} from 'ts-browser-helpers'
export {SimpleEventDispatcher} from 'ts-browser-helpers' export {SimpleEventDispatcher} from 'ts-browser-helpers'
export {createCanvasElement, createDiv, createImage, createStyles, createScriptFromURL} from 'ts-browser-helpers'

export {createCanvasElement, createDiv, createImage, createStyles, createScriptFromURL, cloneScriptTag, setInnerHTMLWithScripts} from 'ts-browser-helpers'
export {TYPED_ARRAYS, arrayBufferToBase64, base64ToArrayBuffer, getTypedArray} 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 {escapeRegExp, getFilenameFromPath, parseFileExtension, replaceAll, toTitleCase, longestCommonPrefix, toCamelCase, safeReplaceString} from 'ts-browser-helpers'
export {prettyScrollbar} from 'ts-browser-helpers' export {prettyScrollbar} from 'ts-browser-helpers'
export {blobToDataURL, downloadBlob, downloadFile, uploadFile, mobileAndTabletCheck} from 'ts-browser-helpers' export {blobToDataURL, downloadBlob, downloadFile, uploadFile, mobileAndTabletCheck} from 'ts-browser-helpers'
export {LinearToSRGB, SRGBToLinear, colorToDataUrl} from 'ts-browser-helpers' export {LinearToSRGB, SRGBToLinear, colorToDataUrl} from 'ts-browser-helpers'
export {aesGcmDecrypt, aesGcmEncrypt} from 'ts-browser-helpers' export {aesGcmDecrypt, aesGcmEncrypt} from 'ts-browser-helpers'
export {verifyPermission, writeFile, getFileHandle, getNewFileHandle, readFile} from 'ts-browser-helpers' export {verifyPermission, writeFile, getFileHandle, getNewFileHandle, readFile} from 'ts-browser-helpers'
export {embedUrlRefs, htmlToCanvas, htmlToPng, htmlToSvg} 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, wrapThisFunction, findLastIndex} from 'ts-browser-helpers'
export {imageToCanvas, imageBitmapToBase64, imageUrlToImageData, imageDataToCanvas, canvasFlipY, isWebpExportSupported, imageBitmapToBlob, imageBitmapToCanvas, blobToImage} from 'ts-browser-helpers'
export {absMax, clearBit, updateBit, uuidV4} from 'ts-browser-helpers'
export {includesAll, wrapThisFunction, wrapThisFunction2, findLastIndex} from 'ts-browser-helpers'
export {copyProps, getOrCall, getPropertyDescriptor, isPropertyWritable, safeSetProperty} from 'ts-browser-helpers' export {copyProps, getOrCall, getPropertyDescriptor, isPropertyWritable, safeSetProperty} from 'ts-browser-helpers'
export {deepAccessObject, getKeyByValue, objectHasOwn, objectMap2, objectMap} from 'ts-browser-helpers' export {deepAccessObject, getKeyByValue, objectHasOwn, objectMap2, objectMap} from 'ts-browser-helpers'
export {makeColorSvg, makeTextSvg, makeColorSvgCircle, svgToCanvas, svgToPng} from 'ts-browser-helpers' export {makeColorSvg, makeTextSvg, makeColorSvgCircle, svgToCanvas, svgToPng} from 'ts-browser-helpers'

+ 1
- 1
src/utils/shader-helpers.ts Bestand weergeven

append = false, append = false,
} = {}) { } = {}) {
// todo: use safeReplaceString from ts-browser-helpers // todo: use safeReplaceString from ts-browser-helpers
if (warnEnabled) {
if (warnEnabled /* && ThreeViewer.ViewerDebugging */) {
if (!shader.includes(str)) { if (!shader.includes(str)) {
console.error(`${str} not found in shader`) console.error(`${str} not found in shader`)
return shader return shader

+ 1
- 1
src/viewer/AViewerPlugin.ts Bestand weergeven



toJSON(meta?: SerializationMetaType): ISerializedConfig { toJSON(meta?: SerializationMetaType): ISerializedConfig {
const data: any = ThreeSerialization.Serialize(this, meta, true) const data: any = ThreeSerialization.Serialize(this, meta, true)
data.type = (this as any).constructor.PluginType
data.type = this.constructor.PluginType
data.assetType = 'config' data.assetType = 'config'
this.dispatchEvent({type: 'serialize', data}) this.dispatchEvent({type: 'serialize', data})
return data return data

+ 7
- 0
src/viewer/ThreeViewer.ts Bestand weergeven

private _tempVec: Vector3 = new Vector3() private _tempVec: Vector3 = new Vector3()
private _tempQuat: Quaternion = new Quaternion() private _tempQuat: Quaternion = new Quaternion()


/**
* If any of the viewers are in debug mode, this will be true.
* This is required for debugging/logging in some cases.
*/
public static ViewerDebugging = false // todo use in shaderReplaceString

/** /**
* Create a viewer instance for using the webgi viewer SDK. * Create a viewer instance for using the webgi viewer SDK.
* @param options - {@link ThreeViewerOptions} * @param options - {@link ThreeViewerOptions}
constructor({debug = false, ...options}: ThreeViewerOptions) { constructor({debug = false, ...options}: ThreeViewerOptions) {
super() super()
this.debug = debug this.debug = debug
if (debug) ThreeViewer.ViewerDebugging = true
this._canvas = options.canvas || createCanvasElement() this._canvas = options.canvas || createCanvasElement()
let container = options.container let container = options.container
if (container && !options.canvas) container.appendChild(this._canvas) if (container && !options.canvas) container.appendChild(this._canvas)

+ 1
- 1
src/viewer/ViewerRenderManager.ts Bestand weergeven

} }


/** /**
* Reference to the gbuffer target, if it exists. This can be set by plugins like {@link DepthBufferPlugin}
* Reference to the gbuffer target, if it exists. This can be set by plugins like {@link DepthBufferPlugin}, {@link GBufferPlugin}
*/ */
gbufferTarget: IRenderTarget | undefined gbufferTarget: IRenderTarget | undefined



+ 1
- 1
src/viewer/version.ts Bestand weergeven

export const VERSION = '0.0.20'
export const VERSION = '0.0.21'

Laden…
Annuleren
Opslaan