ソースを参照

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年前
コミット
fd88dc852f
コミッターのメールアドレスに関連付けられたアカウントが存在しません
38個のファイルの変更996行の追加156行の削除
  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 ファイルの表示

@@ -94,7 +94,7 @@ To make changes and run the example, click on the CodePen button on the top righ
- [ProgressivePlugin](#progressiveplugin) - Post-render pass to blend the last frame with the current frame
- [DepthBufferPlugin](#depthbufferplugin) - Pre-rendering of depth 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
- [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
@@ -2212,7 +2212,7 @@ The depth values are based on camera near far values, which are controlled autom

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
import {ThreeViewer, NormalBufferPlugin} from 'threepipe'
@@ -2229,7 +2229,25 @@ const normalTarget = normalPlugin.target;

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


+ 35
- 0
examples/gbuffer-plugin/index.html ファイルの表示

@@ -0,0 +1,35 @@
<!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 ファイルの表示

@@ -0,0 +1,80 @@
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 ファイルの表示

@@ -228,9 +228,10 @@
<h2 class="category">Rendering</h2>
<ul>
<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="./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-camera/">Virtual Camera (Animated) </a></li>
</ul>

+ 7
- 3
examples/tweakpane-editor/script.ts ファイルの表示

@@ -11,6 +11,7 @@ import {
FragmentClippingExtensionPlugin,
FrameFadePlugin,
FullScreenPlugin,
GBufferPlugin,
GLTFAnimationPlugin,
HalfFloatType,
HDRiGroundPlugin,
@@ -64,7 +65,7 @@ async function init() {
new ProgressivePlugin(),
GLTFAnimationPlugin,
PickingPlugin,
TransformControlsPlugin,
new TransformControlsPlugin(false),
EditorViewWidgetPlugin,
CameraViewPlugin,
ViewerUiConfigPlugin,
@@ -74,7 +75,8 @@ async function init() {
CustomBumpMapPlugin,
VirtualCamerasPlugin,
// 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 RenderTargetPreviewPlugin(false),
new FrameFadePlugin(),
@@ -97,13 +99,15 @@ async function init() {
])

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(NormalBufferPlugin)?.target, 'normal', false, true, false)

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

+ 28
- 28
package-lock.json ファイルの表示

@@ -1,20 +1,20 @@
{
"name": "threepipe",
"version": "0.0.20-dev.1",
"version": "0.0.20",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "threepipe",
"version": "0.0.20-dev.1",
"version": "0.0.20",
"license": "Apache-2.0",
"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/wicg-file-system-access": "^2020.9.5",
"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": {
"@rollup/plugin-commonjs": "^25.0.0",
@@ -41,7 +41,7 @@
"rollup-plugin-glsl": "^1.3.0",
"rollup-plugin-license": "^3.0.1",
"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",
"typedoc": "^0.25.6",
"typescript": "^5.3.3",
@@ -1393,9 +1393,9 @@
"dev": true
},
"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": {
"@tweenjs/tween.js": "~18.6.4",
"fflate": "~0.6.9",
@@ -10614,9 +10614,9 @@
}
},
"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,
"license": "MIT"
},
@@ -10690,9 +10690,9 @@
}
},
"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": {
"@types/wicg-file-system-access": "^2020.9.5"
}
@@ -10917,9 +10917,9 @@
}
},
"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": {
"version": "1.0.2",
@@ -12609,8 +12609,8 @@
"dev": true
},
"@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": {
"@tweenjs/tween.js": "~18.6.4",
"fflate": "~0.6.9",
@@ -19342,8 +19342,8 @@
}
},
"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
},
"through": {
@@ -19396,9 +19396,9 @@
"dev": true
},
"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": {
"@types/wicg-file-system-access": "^2020.9.5"
}
@@ -19568,9 +19568,9 @@
"dev": true
},
"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": {
"version": "1.0.2",

+ 11
- 11
package.json ファイルの表示

@@ -1,6 +1,6 @@
{
"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.",
"main": "dist/index.js",
"module": "dist/index.mjs",
@@ -100,7 +100,7 @@
"rollup-plugin-glsl": "^1.3.0",
"rollup-plugin-license": "^3.0.1",
"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",
"typedoc": "^0.25.6",
"typescript": "^5.3.3",
@@ -109,21 +109,21 @@
"vite-plugin-dts": "^3.7.0"
},
"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/wicg-file-system-access": "^2020.9.5",
"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": {
"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"
},
"local_dependencies": {

+ 2
- 2
plugins/tweakpane/package-lock.json ファイルの表示

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

+ 2
- 2
plugins/tweakpane/package.json ファイルの表示

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

+ 5
- 4
plugins/tweakpane/src/tpImageInputGenerator.ts ファイルの表示

@@ -52,14 +52,15 @@ function proxyGetValue(cc: any, viewer: ThreeViewer) {
cc.image.tp_src = imageBitmapToBase64(cc.image, 160)
} else if (cc.isRenderTargetTexture) {
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 {
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
else if (cc.isDataTexture) cc.image.tp_src = staticData.dataTexImage
}
@@ -69,6 +70,7 @@ function proxyGetValue(cc: any, viewer: ThreeViewer) {
ret = ret ? staticData.imageMap[ret] : undefined
if (!ret) ret = cc.image.tp_src || cc.image.src
}
if (cc.tp_src) ret = cc.tp_src
} else if (typeof cc === 'string') {
ret = cc
} else if (cc.domainMin) { // for lut CUBE files.
@@ -90,7 +92,6 @@ function proxyGetValue(cc: any, viewer: ThreeViewer) {
cc.image.tp_src_uuid = uuid
staticData.tempMap[ret] = uuid
}
// console.log(ret, cc, tar, key)
if (typeof ret === 'string')
ret = staticData.imageMap[ret] ?? ret // Note: this will be a bottleneck if the length of src is too long.
return ret

+ 2
- 1
src/assetmanager/export/EXRExporter2.ts ファイルの表示

@@ -8,7 +8,8 @@ export class EXRExporter2 extends EXRExporter implements IExportParser {
const target = <IRenderTarget>obj
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.isWebGLMultipleRenderTargets) throw new Error('WebGLMultipleRenderTargets not supported')
if (target.isWebGLMultipleRenderTargets && options.textureIndex === undefined)
console.warn('No textureIndex specified for WebGLMultipleRenderTargets')
const res = target.isWebGLRenderTarget ?
this.parse(target.renderManager!.webglRenderer, <WebGLRenderTarget>target, options) :
this.parse(undefined, <DataTexture>obj, options)

+ 20
- 0
src/core/IMaterial.ts ファイルの表示

@@ -58,11 +58,30 @@ export interface IMaterialUserData extends IImportResultUserData{

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

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


[key: string]: any


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

export interface IMaterial<E extends IMaterialEvent = IMaterialEvent, ET = IMaterialEventTypes> extends Material<E, ET>, IJSONSerializable, IDisposable, IUiConfigContainer {
@@ -125,6 +144,7 @@ export interface IMaterial<E extends IMaterialEvent = IMaterialEvent, ET = IMate
lightMap?: ITexture | null
normalMap?: ITexture | null
bumpMap?: ITexture | null
displacementMap?: ITexture | null
aoMapIntensity?: number
lightMapIntensity?: number
roughnessMap?: ITexture | null

+ 1
- 1
src/core/material/LineMaterial2.ts ファイルの表示

@@ -101,7 +101,7 @@ export class LineMaterial2 extends LineMaterial<IMaterialEvent, LineMaterial2Eve
this.setDirty({uiChangeEvent: ev, needsUpdate: false, refreshUi: true})
},
children: [
...generateUiConfig(this),
...generateUiConfig(this) || [],
iMaterialUI.blending(this),
iMaterialUI.polygonOffset(this),
...iMaterialUI.misc(this),

+ 9
- 4
src/core/object/RootScene.ts ファイルの表示

@@ -18,7 +18,7 @@ import {ITexture} from '../ITexture'
import {AddObjectOptions, IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions, IWidget} from '../IScene'
import {iObjectCommons} from './iObjectCommons'
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'

@uiFolderContainer('Root Scene')
@@ -249,9 +249,7 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I
obj.userData.autoScaled = true // mark as auto-scaled, so that autoScale is not called again when file is reloaded.
}
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
} else {
obj.userData.geometriesCentered = true // mark as centered, so that geometry center is not called again when file is reloaded.
@@ -262,6 +260,13 @@ export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements I
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 {
if (dispose) return this.disposeSceneModels(setDirty)
this.modelRoot.clear()

+ 7
- 4
src/plugins/base/PipelinePassPlugin.ts ファイルの表示

@@ -1,8 +1,9 @@
import {IPassID, IPipelinePass} from '../../postprocessing'
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 {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> {
abstract passId: TPassId
@@ -20,8 +21,10 @@ export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extend
/**
* This function is called every frame before composer render, if this pass is being used in the pipeline
* @param _
* @param _1
* @param _2
*/
protected _beforeRender(): boolean {
protected _beforeRender(_?: IScene, _1?: ICamera, _2?: IRenderManager): boolean {
if (!this._pass) return false
this._pass.enabled = !this.isDisabled()
return this._pass.enabled
@@ -36,7 +39,7 @@ export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extend

this._pass = this._createPass()
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)
}


+ 2
- 0
src/plugins/index.ts ファイルの表示

@@ -4,10 +4,12 @@ export {BaseImporterPlugin} from './base/BaseImporterPlugin'

// pipeline
export {ProgressivePlugin} from './pipeline/ProgressivePlugin'
export {GBufferPlugin, GBufferMaterial, DepthNormalMaterial} from './pipeline/GBufferPlugin'
export {DepthBufferPlugin} from './pipeline/DepthBufferPlugin'
export {NormalBufferPlugin} from './pipeline/NormalBufferPlugin'
export {FrameFadePlugin, type FrameFadePluginEventTypes} from './pipeline/FrameFadePlugin'
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 {NormalBufferPluginEventTypes, NormalBufferPluginPass, NormalBufferPluginTarget} from './pipeline/NormalBufferPlugin'


+ 7
- 2
src/plugins/interaction/TransformControlsPlugin.ts ファイルの表示

@@ -1,4 +1,4 @@
import {uiConfig, uiPanelContainer, uiToggle} from 'uiconfig.js'
import {uiButton, uiConfig, uiPanelContainer, uiToggle} from 'uiconfig.js'
import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {OrbitControls3, TransformControls2} from '../../three'
import {PickingPlugin} from './PickingPlugin'
@@ -33,10 +33,11 @@ export class TransformControlsPlugin extends AViewerPluginSync<''> {
this._viewer.setDirty()
}

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

toJSON: any = undefined
@@ -103,5 +104,9 @@ export class TransformControlsPlugin extends AViewerPluginSync<''> {
this.transformControls.camera = this._viewer.scene.mainCamera
}

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

}

+ 2
- 2
src/plugins/pipeline/DepthBufferPlugin.ts ファイルの表示

@@ -142,9 +142,9 @@ export class DepthBufferPlugin
this._createTarget(true)
if (!this.target) throw new Error('DepthBufferPlugin: target not created')
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
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.after = []
pass.required = ['render']

+ 431
- 0
src/plugins/pipeline/GBufferPlugin.ts ファイルの表示

@@ -0,0 +1,431 @@
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 ファイルの表示

@@ -83,7 +83,7 @@ export class NormalBufferPlugin
this._createTarget(true)
if (!this.target) throw new Error('NormalBufferPlugin: target not created')
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
pass.preprocessMaterial = (m) => preprocessMaterial(m, true)
pass.before = ['render']

+ 2
- 1
src/plugins/pipeline/ProgressivePlugin.ts ファイルの表示

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

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

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

+ 119
- 0
src/plugins/pipeline/shaders/GBufferPlugin.mat.frag.glsl ファイルの表示

@@ -0,0 +1,119 @@
// 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 ファイルの表示

@@ -0,0 +1,45 @@
// 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 ファイルの表示

@@ -0,0 +1,77 @@
#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 ファイルの表示

@@ -4,11 +4,7 @@ import {MaterialExtension} from '../../materials'
import {Shader, Vector4, WebGLRenderer} from 'three'
import {IMaterial} from '../../core'
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
@@ -84,26 +80,21 @@ export abstract class AScreenPassExtensionPlugin<T extends string> extends AView

onAdded(viewer: ThreeViewer) {
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])
}

onRemove(viewer: ThreeViewer) {
// viewer.getPlugin(GBufferPlugin)?.unregisterGBufferUpdater(this.updateGBufferFlags)
viewer.getPlugin(GBufferPlugin)?.unregisterGBufferUpdater(this.constructor.PluginType)
viewer.renderManager.screenPass.material.unregisterMaterialExtensions([this])
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
// eslint-disable-next-line @typescript-eslint/naming-convention
__setDirty?: () => void

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


+ 5
- 3
src/plugins/postprocessing/TonemapPlugin.ts ファイルの表示

@@ -20,6 +20,7 @@ import Uncharted2ToneMappingShader from './shaders/Uncharted2ToneMapping.glsl'
import TonemapShader from './shaders/TonemapPlugin.pars.glsl'
import TonemapShaderPatch from './shaders/TonemapPlugin.patch.glsl'
import {AScreenPassExtensionPlugin} from './AScreenPassExtensionPlugin'
import {GBufferUpdaterContext} from '../pipeline/GBufferPlugin'

// eslint-disable-next-line @typescript-eslint/naming-convention
export const Uncharted2Tonemapping: ToneMapping = CustomToneMapping
@@ -131,10 +132,11 @@ export class TonemapPlugin extends AScreenPassExtensionPlugin<''> {
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
super.updateGBufferFlags(material, data)
super.updateGBufferFlags(data, c)
}

static {

+ 0
- 18
src/plugins/ui/GeometryUVPreviewPlugin.ts ファイルの表示

@@ -64,24 +64,6 @@ export class GeometryUVPreviewPlugin<TEvent extends string> extends AViewerPlugi
target.uvCanvas.style.height = '100%'
}
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 ファイルの表示

@@ -1,11 +1,20 @@
import {AViewerPluginSync, ThreeViewer} from '../../viewer'
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 styles from './RenderTargetPreviewPlugin.css?inline'
import {CustomContextMenu} from '../../utils'
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')
export class RenderTargetPreviewPlugin<TEvent extends string> extends AViewerPluginSync<TEvent> {
static readonly PluginType = 'RenderTargetPreviewPlugin'
@@ -22,14 +31,7 @@ export class RenderTargetPreviewPlugin<TEvent extends string> extends AViewerPlu
this.enabled = enabled
}

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

onAdded(viewer: ThreeViewer): void {
super.onAdded(viewer)
@@ -50,36 +52,36 @@ export class RenderTargetPreviewPlugin<TEvent extends string> extends AViewerPlu
private _postRender = () => {
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) {
// todo draw white or pink
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()
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
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
if (!target.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace
if (!targetBlock.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace
this._viewer.renderManager.blit(null, {
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),
})
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
const div = document.createElement('div')
const targetDef = {target, name, transparent, div, originalColorSpace, visible}
@@ -109,7 +111,7 @@ export class RenderTargetPreviewPlugin<TEvent extends string> extends AViewerPlu
return this
}

removeTarget(target: ValOrFunc<IRenderTarget|undefined>): this {
removeTarget(target: RenderTargetBlock['target']): this {
const index = this.targetBlocks.findIndex(t => t.target === target)
if (index >= 0) {
const t = this.targetBlocks[index]
@@ -119,7 +121,7 @@ export class RenderTargetPreviewPlugin<TEvent extends string> extends AViewerPlu
this.refreshUi()
return this
}
downloadTarget(target1: ValOrFunc<IRenderTarget|undefined>): this {
downloadTarget(target1: RenderTargetBlock['target']): this {
if (!this._viewer) return this
const target = getOrCall(target1)
if (!target) return this

+ 0
- 1
src/postprocessing/ExtendedRenderPass.ts ファイルの表示

@@ -52,7 +52,6 @@ export class ExtendedRenderPass extends RenderPass implements IPipelinePass<'ren
this._transparentTarget = undefined
}


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

+ 2
- 2
src/postprocessing/ExtendedShaderPass.ts ファイルの表示

@@ -10,7 +10,7 @@ export class ExtendedShaderPass extends ShaderPass implements IPass {
public static readonly DEFAULT_TEX_ID = 'tDiffuse'

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

readonly isExtendedShaderPass = true
// private _textureIDs: string[]
@@ -29,7 +29,7 @@ export class ExtendedShaderPass extends ShaderPass implements IPass {
renderer.renderWithModes({
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 ファイルの表示

@@ -31,7 +31,7 @@ export class ScreenPass extends ExtendedShaderPass implements IPipelinePass<'scr
super(
(<any>shader)?.fragmentShader || (<ShaderMaterial2>shader)?.isShaderMaterial ? <ShaderMaterialParameters|ShaderMaterial2>shader :
makeScreenShader(shader),
...textureID.length ? textureID : ['tDiffuse', 'tTransparent', 'tGBuffer'])
...textureID.length ? textureID : ['tDiffuse', 'tTransparent'])
this.material.addEventListener('materialUpdate', this.setDirty)
}


+ 23
- 16
src/rendering/RenderManager.ts ファイルの表示

@@ -11,6 +11,7 @@ import {
Texture,
Vector2,
Vector4,
WebGLMultipleRenderTargets,
WebGLRenderer,
WebGLRenderTarget,
WebGLRenderTargetOptions,
@@ -504,41 +505,45 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen
* @param target
* @param mimeType
* @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')
canvas.width = target.width
canvas.height = target.height
const ctx = canvas.getContext('2d')
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 {
// 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)

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

/**
* 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 =
target.texture.type === HalfFloatType ?
texture.type === HalfFloatType ?
new Uint16Array(target.width * target.height * 4) :
target.texture.type === FloatType ?
texture.type === FloatType ?
new Float32Array(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
}

@@ -547,17 +552,19 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen
* @param target - render target to export
* @param mimeType - mime type to use.
* 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']
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') {
mimeType = hdr ? 'image/x-exr' : 'image/png'
}
if (!hdrFormats.includes(mimeType)) hdr = false
let buffer: ArrayBufferLike
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])
mimeType = url.split(';')[0].split(':')[1]
} else {
@@ -566,7 +573,7 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen
mimeType = 'image/x-exr'
}
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
b.ext = mimeType === 'image/x-exr' ? 'exr' : mimeType.split('/')[1]

+ 7
- 5
src/utils/browser-helpers.ts ファイルの表示

@@ -2,12 +2,14 @@ export type {IEvent, IEventDispatcher} from 'ts-browser-helpers'
export type {ImageCanvasOptions} from 'ts-browser-helpers'
export type {AnyFunction, AnyOptions, Class, IDisposable, IJSONSerializable, PartialPick, PartialRecord, StringKeyOf, Fof, ValOrFunc, ValOrArr, ValOrFuncOp, ValOrArrOp} from 'ts-browser-helpers'
export type {Serializer} from 'ts-browser-helpers'

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

export {createCanvasElement, createDiv, createImage, createStyles, createScriptFromURL, cloneScriptTag, setInnerHTMLWithScripts} 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 {blobToDataURL, downloadBlob, downloadFile, uploadFile, mobileAndTabletCheck} from 'ts-browser-helpers'
export {LinearToSRGB, SRGBToLinear, colorToDataUrl} from 'ts-browser-helpers'
@@ -15,9 +17,9 @@ export {onChange, onChange2, onChange3, onChangeDispatchEvent, serialize, serial
export {aesGcmDecrypt, aesGcmEncrypt} from 'ts-browser-helpers'
export {verifyPermission, writeFile, getFileHandle, getNewFileHandle, readFile} from 'ts-browser-helpers'
export {embedUrlRefs, htmlToCanvas, htmlToPng, htmlToSvg} from 'ts-browser-helpers'
export {imageToCanvas, imageBitmapToBase64, imageUrlToImageData, imageDataToCanvas, isWebpExportSupported, canvasFlipY} from 'ts-browser-helpers'
export {absMax, clearBit, updateBit} from 'ts-browser-helpers'
export {includesAll, 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 {deepAccessObject, getKeyByValue, objectHasOwn, objectMap2, objectMap} from 'ts-browser-helpers'
export {makeColorSvg, makeTextSvg, makeColorSvgCircle, svgToCanvas, svgToPng} from 'ts-browser-helpers'

+ 1
- 1
src/utils/shader-helpers.ts ファイルの表示

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

+ 1
- 1
src/viewer/AViewerPlugin.ts ファイルの表示

@@ -31,7 +31,7 @@ export abstract class AViewerPlugin<T extends string = string, TViewer extends T

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

+ 7
- 0
src/viewer/ThreeViewer.ts ファイルの表示

@@ -297,6 +297,12 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
private _tempVec: Vector3 = new Vector3()
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.
* @param options - {@link ThreeViewerOptions}
@@ -304,6 +310,7 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes
constructor({debug = false, ...options}: ThreeViewerOptions) {
super()
this.debug = debug
if (debug) ThreeViewer.ViewerDebugging = true
this._canvas = options.canvas || createCanvasElement()
let container = options.container
if (container && !options.canvas) container.appendChild(this._canvas)

+ 1
- 1
src/viewer/ViewerRenderManager.ts ファイルの表示

@@ -52,7 +52,7 @@ export class ViewerRenderManager extends RenderManager {
}

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


+ 1
- 1
src/viewer/version.ts ファイルの表示

@@ -1 +1 @@
export const VERSION = '0.0.20'
export const VERSION = '0.0.21'

読み込み中…
キャンセル
保存