소스 검색

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
No account linked to committer's email address
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'

Loading…
취소
저장