@takram/three-geospatial 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/build/index.cjs +1 -1
  3. package/build/index.cjs.map +1 -1
  4. package/build/index.js +1109 -2108
  5. package/build/index.js.map +1 -1
  6. package/build/r3f.cjs +1 -1
  7. package/build/r3f.cjs.map +1 -1
  8. package/build/r3f.js +1 -1
  9. package/build/r3f.js.map +1 -1
  10. package/build/shaders.cjs +4 -4
  11. package/build/shaders.js +7 -7
  12. package/build/shared.cjs +1 -1
  13. package/build/shared.cjs.map +1 -1
  14. package/build/shared.js +8 -206
  15. package/build/shared.js.map +1 -1
  16. package/build/shared2.cjs +2 -0
  17. package/build/shared2.cjs.map +1 -0
  18. package/build/shared2.js +224 -0
  19. package/build/shared2.js.map +1 -0
  20. package/build/shared3.cjs +2 -0
  21. package/build/shared3.cjs.map +1 -0
  22. package/build/shared3.js +13 -0
  23. package/build/shared3.js.map +1 -0
  24. package/build/webgpu.cjs +2 -0
  25. package/build/webgpu.cjs.map +1 -0
  26. package/build/webgpu.js +1336 -0
  27. package/build/webgpu.js.map +1 -0
  28. package/package.json +14 -9
  29. package/src/Ellipsoid.ts +25 -0
  30. package/src/EllipsoidGeometry.ts +2 -2
  31. package/src/QuadGeometry.ts +14 -0
  32. package/src/bufferGeometry.ts +1 -1
  33. package/src/capabilities.ts +12 -3
  34. package/src/decorators.ts +11 -6
  35. package/src/index.ts +2 -2
  36. package/src/shaders/depth.glsl +3 -3
  37. package/src/types.ts +2 -0
  38. package/src/unrollLoops.ts +1 -1
  39. package/src/webgpu/DownsampleThresholdNode.ts +40 -0
  40. package/src/webgpu/DualMipmapFilterNode.ts +130 -0
  41. package/src/webgpu/FilterNode.ts +93 -0
  42. package/src/webgpu/FnLayout.ts +86 -0
  43. package/src/webgpu/FnVar.ts +26 -0
  44. package/src/webgpu/GaussianBlurNode.ts +129 -0
  45. package/src/webgpu/HighpVelocityNode.ts +115 -0
  46. package/src/webgpu/KawaseBlurNode.ts +76 -0
  47. package/src/webgpu/LensFlareNode.ts +128 -0
  48. package/src/webgpu/LensGhostNode.ts +62 -0
  49. package/src/webgpu/LensGlareNode.ts +318 -0
  50. package/src/webgpu/LensHaloNode.ts +99 -0
  51. package/src/webgpu/MipmapBlurNode.ts +113 -0
  52. package/src/webgpu/MipmapSurfaceBlurNode.ts +140 -0
  53. package/src/webgpu/OutputTexture3DNode.ts +34 -0
  54. package/src/webgpu/OutputTextureNode.ts +33 -0
  55. package/src/webgpu/RTTextureNode.ts +132 -0
  56. package/src/webgpu/SeparableFilterNode.ts +98 -0
  57. package/src/webgpu/SingleFilterNode.ts +80 -0
  58. package/src/webgpu/TemporalAntialiasNode.ts +571 -0
  59. package/src/webgpu/accessors.ts +67 -0
  60. package/src/webgpu/debug.ts +86 -0
  61. package/src/webgpu/generators.ts +40 -0
  62. package/src/webgpu/index.ts +21 -0
  63. package/src/webgpu/internals.ts +37 -0
  64. package/src/webgpu/math.ts +81 -0
  65. package/src/webgpu/node.ts +100 -0
  66. package/src/webgpu/sampling.ts +103 -0
  67. package/src/webgpu/transformations.ts +106 -0
  68. package/src/webgpu/utils.ts +13 -0
  69. package/types/Ellipsoid.d.ts +4 -0
  70. package/types/QuadGeometry.d.ts +4 -0
  71. package/types/bufferGeometry.d.ts +2 -2
  72. package/types/capabilities.d.ts +2 -1
  73. package/types/decorators.d.ts +8 -5
  74. package/types/index.d.ts +2 -2
  75. package/types/types.d.ts +1 -0
  76. package/types/webgpu/DownsampleThresholdNode.d.ts +11 -0
  77. package/types/webgpu/DualMipmapFilterNode.d.ts +21 -0
  78. package/types/webgpu/FilterNode.d.ts +17 -0
  79. package/types/webgpu/FnLayout.d.ts +21 -0
  80. package/types/webgpu/FnVar.d.ts +6 -0
  81. package/types/webgpu/GaussianBlurNode.d.ts +10 -0
  82. package/types/webgpu/HighpVelocityNode.d.ts +18 -0
  83. package/types/webgpu/KawaseBlurNode.d.ts +10 -0
  84. package/types/webgpu/LensFlareNode.d.ts +25 -0
  85. package/types/webgpu/LensGhostNode.d.ts +8 -0
  86. package/types/webgpu/LensGlareNode.d.ts +32 -0
  87. package/types/webgpu/LensHaloNode.d.ts +11 -0
  88. package/types/webgpu/MipmapBlurNode.d.ts +12 -0
  89. package/types/webgpu/MipmapSurfaceBlurNode.d.ts +11 -0
  90. package/types/webgpu/OutputTexture3DNode.d.ts +11 -0
  91. package/types/webgpu/OutputTextureNode.d.ts +11 -0
  92. package/types/webgpu/RTTextureNode.d.ts +23 -0
  93. package/types/webgpu/SeparableFilterNode.d.ts +20 -0
  94. package/types/webgpu/SingleFilterNode.d.ts +17 -0
  95. package/types/webgpu/TemporalAntialiasNode.d.ts +56 -0
  96. package/types/webgpu/accessors.d.ts +10 -0
  97. package/types/webgpu/debug.d.ts +7 -0
  98. package/types/webgpu/generators.d.ts +4 -0
  99. package/types/webgpu/index.d.ts +21 -0
  100. package/types/webgpu/internals.d.ts +3 -0
  101. package/types/webgpu/math.d.ts +4 -0
  102. package/types/webgpu/node.d.ts +33 -0
  103. package/types/webgpu/sampling.d.ts +2 -0
  104. package/types/webgpu/transformations.d.ts +12 -0
  105. package/types/webgpu/utils.d.ts +3 -0
  106. package/src/assertions.ts +0 -1
  107. package/types/assertions.d.ts +0 -1
@@ -0,0 +1,140 @@
1
+ import { add, Fn, mix, nodeObject, uniform, uv, vec2, vec4 } from 'three/tsl'
2
+ import type { NodeBuilder, TextureNode } from 'three/webgpu'
3
+ import invariant from 'tiny-invariant'
4
+
5
+ import { DualMipmapFilterNode } from './DualMipmapFilterNode'
6
+ import type { Node, NodeObject } from './node'
7
+
8
+ const clampToBorder = (uv: NodeObject<'vec2'>): NodeObject<'float'> => {
9
+ return uv.greaterThanEqual(0).all().and(uv.lessThanEqual(1).all()).toFloat()
10
+ }
11
+
12
+ // Implementation of Lena Piquet's bloom filter.
13
+ // Reference: https://www.froyok.fr/blog/2021-12-ue4-custom-bloom
14
+ export class MipmapSurfaceBlurNode extends DualMipmapFilterNode {
15
+ static override get type(): string {
16
+ return 'MipmapSurfaceBlurNode'
17
+ }
18
+
19
+ blendAmount = uniform(0.85)
20
+
21
+ constructor(inputNode?: TextureNode | null, levels = 4) {
22
+ super(inputNode, levels)
23
+ this.resolutionScale = 0.5
24
+ }
25
+
26
+ protected override setupDownsampleNode(builder: NodeBuilder): Node {
27
+ const { inputNode, inputTexelSize } = this
28
+ invariant(inputNode != null)
29
+
30
+ return Fn(() => {
31
+ const center = uv()
32
+ const offset1 = vec4(1, 1, -1, -1)
33
+ .mul(inputTexelSize.xyxy)
34
+ .add(center.xyxy)
35
+ const offset2 = vec4(2, 2, -2, -2)
36
+ .mul(inputTexelSize.xyxy)
37
+ .add(center.xyxy)
38
+ const uv01 = offset1.zy.toVertexStage() // -1, 1
39
+ const uv02 = offset1.xy.toVertexStage() // 1, 1
40
+ const uv03 = offset1.zw.toVertexStage() // -1, -1
41
+ const uv04 = offset1.xw.toVertexStage() // 1, -1
42
+ const uv05 = offset2.zy.toVertexStage() // -2, 2
43
+ const uv06 = offset2.xy.toVertexStage() // 2, 2
44
+ const uv07 = offset2.zw.toVertexStage() // -2, -2
45
+ const uv08 = offset2.xw.toVertexStage() // 2, -2
46
+ const uv09 = vec2(center.x, offset2.y).toVertexStage() // 0, 2
47
+ const uv10 = vec2(offset2.z, center.y).toVertexStage() // -2, 0
48
+ const uv11 = vec2(offset2.x, center.y).toVertexStage() // 2, 0
49
+ const uv12 = vec2(center.x, offset2.w).toVertexStage() // 0, -2
50
+
51
+ const innerWeight = 1 / 4 / 2
52
+ const outerWeight = 1 / 9 / 2
53
+
54
+ const output = inputNode.sample(center).mul(outerWeight)
55
+
56
+ let weight: NodeObject
57
+ weight = vec4(
58
+ clampToBorder(uv01),
59
+ clampToBorder(uv02),
60
+ clampToBorder(uv03),
61
+ clampToBorder(uv04)
62
+ ).mul(innerWeight)
63
+
64
+ output.addAssign(
65
+ inputNode.sample(uv01).mul(weight.x),
66
+ inputNode.sample(uv02).mul(weight.y),
67
+ inputNode.sample(uv03).mul(weight.z),
68
+ inputNode.sample(uv04).mul(weight.w)
69
+ )
70
+
71
+ weight = vec4(
72
+ clampToBorder(uv05),
73
+ clampToBorder(uv06),
74
+ clampToBorder(uv07),
75
+ clampToBorder(uv08)
76
+ ).mul(outerWeight)
77
+
78
+ output.addAssign(
79
+ inputNode.sample(uv05).mul(weight.x),
80
+ inputNode.sample(uv06).mul(weight.y),
81
+ inputNode.sample(uv07).mul(weight.z),
82
+ inputNode.sample(uv08).mul(weight.w)
83
+ )
84
+
85
+ weight = vec4(
86
+ clampToBorder(uv09),
87
+ clampToBorder(uv10),
88
+ clampToBorder(uv11),
89
+ clampToBorder(uv12)
90
+ ).mul(outerWeight)
91
+
92
+ output.addAssign(
93
+ inputNode.sample(uv09).mul(weight.x),
94
+ inputNode.sample(uv10).mul(weight.y),
95
+ inputNode.sample(uv11).mul(weight.z),
96
+ inputNode.sample(uv12).mul(weight.w)
97
+ )
98
+
99
+ return output
100
+ })()
101
+ }
102
+
103
+ protected override setupUpsampleNode(builder: NodeBuilder): Node {
104
+ const { inputNode, inputTexelSize, downsampleNode } = this
105
+ invariant(inputNode != null)
106
+
107
+ const center = uv()
108
+ const offset = vec4(1, 1, -1, -1).mul(inputTexelSize.xyxy).add(center.xyxy)
109
+ const uv1 = vec2(center.x, offset.y).toVertexStage() // 0, 1
110
+ const uv2 = vec2(offset.z, center.y).toVertexStage() // -1, 0
111
+ const uv3 = vec2(offset.x, center.y).toVertexStage() // 1, 0
112
+ const uv4 = vec2(center.x, offset.w).toVertexStage() // 0, -1
113
+ const uv5 = offset.zy.toVertexStage() // -1, 1
114
+ const uv6 = offset.xy.toVertexStage() // 1, 1
115
+ const uv7 = offset.zw.toVertexStage() // -1, -1
116
+ const uv8 = offset.xw.toVertexStage() // 1, -1
117
+
118
+ const output = add(
119
+ inputNode.sample(center).mul(1 / 4),
120
+ add(
121
+ inputNode.sample(uv1),
122
+ inputNode.sample(uv2),
123
+ inputNode.sample(uv3),
124
+ inputNode.sample(uv4)
125
+ ).mul(1 / 8),
126
+ add(
127
+ inputNode.sample(uv5),
128
+ inputNode.sample(uv6),
129
+ inputNode.sample(uv7),
130
+ inputNode.sample(uv8)
131
+ ).mul(1 / 16)
132
+ )
133
+ return mix(downsampleNode.sample(center), output, this.blendAmount)
134
+ }
135
+ }
136
+
137
+ export const mipmapSurfaceBlur = (
138
+ ...args: ConstructorParameters<typeof MipmapSurfaceBlurNode>
139
+ ): NodeObject<MipmapSurfaceBlurNode> =>
140
+ nodeObject(new MipmapSurfaceBlurNode(...args))
@@ -0,0 +1,34 @@
1
+ import type { Texture } from 'three'
2
+ import { nodeObject } from 'three/tsl'
3
+ import { Texture3DNode, type Node, type NodeBuilder } from 'three/webgpu'
4
+
5
+ import type { NodeObject } from './node'
6
+
7
+ export class OutputTexture3DNode extends Texture3DNode {
8
+ static override get type(): string {
9
+ return 'OutputTexture3DNode'
10
+ }
11
+
12
+ owner: Node
13
+
14
+ constructor(owner: Node, texture: Texture) {
15
+ super(texture)
16
+ this.owner = owner
17
+ this.setUpdateMatrix(false)
18
+ }
19
+
20
+ override setup(builder: NodeBuilder): unknown {
21
+ this.owner.build(builder)
22
+ return super.setup(builder)
23
+ }
24
+
25
+ override clone(): this {
26
+ // @ts-expect-error Ignore
27
+ return new this.constructor(this.owner, this.value)
28
+ }
29
+ }
30
+
31
+ export const outputTexture3D = (
32
+ ...args: ConstructorParameters<typeof OutputTexture3DNode>
33
+ ): NodeObject<OutputTexture3DNode> =>
34
+ nodeObject(new OutputTexture3DNode(...args))
@@ -0,0 +1,33 @@
1
+ import type { Texture } from 'three'
2
+ import { nodeObject } from 'three/tsl'
3
+ import { TextureNode, type Node, type NodeBuilder } from 'three/webgpu'
4
+
5
+ import type { NodeObject } from './node'
6
+
7
+ export class OutputTextureNode extends TextureNode {
8
+ static override get type(): string {
9
+ return 'OutputTextureNode'
10
+ }
11
+
12
+ owner: Node
13
+
14
+ constructor(owner: Node, texture: Texture) {
15
+ super(texture)
16
+ this.owner = owner
17
+ this.setUpdateMatrix(false)
18
+ }
19
+
20
+ override setup(builder: NodeBuilder): unknown {
21
+ this.owner.build(builder)
22
+ return super.setup(builder)
23
+ }
24
+
25
+ override clone(): this {
26
+ // @ts-expect-error Ignore
27
+ return new this.constructor(this.owner, this.value)
28
+ }
29
+ }
30
+
31
+ export const outputTexture = (
32
+ ...args: ConstructorParameters<typeof OutputTextureNode>
33
+ ): NodeObject<OutputTextureNode> => nodeObject(new OutputTextureNode(...args))
@@ -0,0 +1,132 @@
1
+ import { nodeObject, uv } from 'three/tsl'
2
+ import {
3
+ HalfFloatType,
4
+ LinearFilter,
5
+ NodeMaterial,
6
+ NodeUpdateType,
7
+ QuadMesh,
8
+ RendererUtils,
9
+ RenderTarget,
10
+ RGBAFormat,
11
+ TextureNode,
12
+ Vector2,
13
+ type Node,
14
+ type NodeBuilder,
15
+ type NodeFrame
16
+ } from 'three/webgpu'
17
+
18
+ import type { NodeObject } from './node'
19
+
20
+ const { resetRendererState, restoreRendererState } = RendererUtils
21
+
22
+ function createRenderTarget(): RenderTarget {
23
+ const renderTarget = new RenderTarget(1, 1, {
24
+ depthBuffer: false,
25
+ type: HalfFloatType,
26
+ format: RGBAFormat
27
+ })
28
+ const texture = renderTarget.texture
29
+ texture.minFilter = LinearFilter
30
+ texture.magFilter = LinearFilter
31
+ texture.generateMipmaps = false
32
+ return renderTarget
33
+ }
34
+
35
+ const sizeScratch = /*#__PURE__*/ new Vector2()
36
+
37
+ // Similar to RTTNode, which is a bit finicky to handle.
38
+ export class RTTextureNode extends TextureNode {
39
+ static override get type(): string {
40
+ return 'RTTextureNode'
41
+ }
42
+
43
+ node: Node
44
+ resolutionScale = 1
45
+
46
+ private readonly renderTarget: RenderTarget
47
+ private readonly material = new NodeMaterial()
48
+ private readonly mesh = new QuadMesh(this.material)
49
+ private rendererState?: RendererUtils.RendererState
50
+
51
+ constructor(node: Node, uvNode?: Node) {
52
+ const renderTarget = createRenderTarget()
53
+ super(renderTarget.texture, uvNode != null ? nodeObject(uvNode) : uv())
54
+ this.node = node
55
+ this.renderTarget = renderTarget
56
+ this.updateBeforeType = NodeUpdateType.FRAME
57
+ }
58
+
59
+ setSize(width: number, height: number): this {
60
+ const { resolutionScale } = this
61
+ const w = Math.max(Math.round(width * resolutionScale), 1)
62
+ const h = Math.max(Math.round(height * resolutionScale), 1)
63
+ this.renderTarget.setSize(w, h)
64
+ return this
65
+ }
66
+
67
+ override updateBefore({ renderer }: NodeFrame): void {
68
+ if (renderer == null) {
69
+ return
70
+ }
71
+
72
+ const size = renderer.getDrawingBufferSize(sizeScratch)
73
+ this.setSize(size.x, size.y)
74
+
75
+ this.rendererState = resetRendererState(renderer, this.rendererState)
76
+
77
+ renderer.setRenderTarget(this.renderTarget)
78
+ this.mesh.render(renderer)
79
+
80
+ restoreRendererState(renderer, this.rendererState)
81
+ }
82
+
83
+ override setup(builder: NodeBuilder): unknown {
84
+ const { material } = this
85
+ // I don't fully understand why, but updates on "node" doesn't propagate
86
+ // unless giving the builder context.
87
+ material.fragmentNode = nodeObject(this.node).context(builder.getContext())
88
+ material.needsUpdate = true
89
+ return super.setup(builder)
90
+ }
91
+
92
+ override dispose(): void {
93
+ this.renderTarget.dispose()
94
+ this.material.dispose()
95
+ this.mesh.geometry.dispose()
96
+ super.dispose()
97
+ }
98
+
99
+ // @ts-expect-error Ignore
100
+ override clone(): TextureNode {
101
+ const result = new TextureNode(this.value, this.uvNode, this.levelNode)
102
+ result.sampler = this.sampler
103
+ result.referenceNode = this
104
+ return result
105
+ }
106
+ }
107
+
108
+ export const rtTexture = (
109
+ ...args: ConstructorParameters<typeof RTTextureNode>
110
+ ): NodeObject<RTTextureNode> => nodeObject(new RTTextureNode(...args))
111
+
112
+ export const convertToTexture = (
113
+ node: Node & {
114
+ isTextureNode?: boolean
115
+ isSampleNode?: boolean
116
+ getTextureNode?: () => TextureNode
117
+ },
118
+ name?: string
119
+ ): NodeObject<TextureNode> => {
120
+ let textureNode: TextureNode
121
+ if (node.isTextureNode === true || node.isSampleNode === true) {
122
+ textureNode = node as TextureNode
123
+ } else if (node.getTextureNode != null) {
124
+ textureNode = node.getTextureNode()
125
+ } else {
126
+ textureNode = new RTTextureNode(node)
127
+ if (name != null) {
128
+ textureNode.value.name = name
129
+ }
130
+ }
131
+ return nodeObject(textureNode)
132
+ }
@@ -0,0 +1,98 @@
1
+ import { Vector2, type RenderTarget } from 'three'
2
+ import { uniform } from 'three/tsl'
3
+ import {
4
+ NodeMaterial,
5
+ QuadMesh,
6
+ RendererUtils,
7
+ type NodeBuilder,
8
+ type NodeFrame,
9
+ type TextureNode
10
+ } from 'three/webgpu'
11
+ import invariant from 'tiny-invariant'
12
+
13
+ import { FilterNode } from './FilterNode'
14
+ import type { Node } from './node'
15
+
16
+ const { resetRendererState, restoreRendererState } = RendererUtils
17
+
18
+ export abstract class SeparableFilterNode extends FilterNode {
19
+ iterations = 1
20
+
21
+ private readonly horizontalRT: RenderTarget
22
+ private readonly verticalRT: RenderTarget
23
+ private readonly material = new NodeMaterial()
24
+ private readonly mesh = new QuadMesh(this.material)
25
+ private rendererState?: RendererUtils.RendererState
26
+
27
+ protected readonly inputTexelSize = uniform(new Vector2())
28
+ protected readonly direction = uniform(new Vector2())
29
+
30
+ constructor(inputNode?: TextureNode | null) {
31
+ super(inputNode)
32
+
33
+ this.horizontalRT = this.createRenderTarget('Horizontal')
34
+ this.verticalRT = this.createRenderTarget('Vertical')
35
+ this.outputTexture = this.verticalRT.texture
36
+ }
37
+
38
+ setSize(width: number, height: number): this {
39
+ const { resolutionScale } = this
40
+ const w = Math.max(Math.round(width * resolutionScale), 1)
41
+ const h = Math.max(Math.round(height * resolutionScale), 1)
42
+ this.horizontalRT.setSize(w, h)
43
+ this.verticalRT.setSize(w, h)
44
+ return this
45
+ }
46
+
47
+ override updateBefore({ renderer }: NodeFrame): void {
48
+ if (renderer == null) {
49
+ return
50
+ }
51
+
52
+ const { horizontalRT, verticalRT, mesh, inputNode, direction } = this
53
+ invariant(inputNode != null)
54
+
55
+ const { width, height } = inputNode.value
56
+ this.setSize(width, height)
57
+ this.inputTexelSize.value.set(1 / width, 1 / height)
58
+
59
+ const originalTexture = inputNode.value
60
+ this.rendererState = resetRendererState(renderer, this.rendererState)
61
+
62
+ for (let i = 0; i < this.iterations; ++i) {
63
+ direction.value.set(1, 0)
64
+ renderer.setRenderTarget(horizontalRT)
65
+ mesh.render(renderer)
66
+ inputNode.value = horizontalRT.texture
67
+
68
+ direction.value.set(0, 1)
69
+ renderer.setRenderTarget(verticalRT)
70
+ mesh.render(renderer)
71
+ inputNode.value = verticalRT.texture
72
+ }
73
+
74
+ restoreRendererState(renderer, this.rendererState)
75
+ inputNode.value = originalTexture
76
+ }
77
+
78
+ protected abstract setupOutputNode(builder: NodeBuilder): Node
79
+
80
+ override setup(builder: NodeBuilder): unknown {
81
+ const { inputNode } = this
82
+ invariant(inputNode != null)
83
+
84
+ const { material } = this
85
+ material.fragmentNode = this.setupOutputNode(builder)
86
+ material.needsUpdate = true
87
+
88
+ return super.setup(builder)
89
+ }
90
+
91
+ override dispose(): void {
92
+ this.horizontalRT.dispose()
93
+ this.verticalRT.dispose()
94
+ this.material.dispose()
95
+ this.mesh.geometry.dispose()
96
+ super.dispose()
97
+ }
98
+ }
@@ -0,0 +1,80 @@
1
+ import { Vector2, type RenderTarget } from 'three'
2
+ import { uniform } from 'three/tsl'
3
+ import {
4
+ NodeMaterial,
5
+ QuadMesh,
6
+ RendererUtils,
7
+ type NodeBuilder,
8
+ type NodeFrame,
9
+ type TextureNode
10
+ } from 'three/webgpu'
11
+ import invariant from 'tiny-invariant'
12
+
13
+ import { FilterNode } from './FilterNode'
14
+ import type { Node } from './node'
15
+
16
+ const { resetRendererState, restoreRendererState } = RendererUtils
17
+
18
+ export abstract class SingleFilterNode extends FilterNode {
19
+ private readonly renderTarget: RenderTarget
20
+ private readonly material = new NodeMaterial()
21
+ private readonly mesh = new QuadMesh(this.material)
22
+ private rendererState?: RendererUtils.RendererState
23
+
24
+ protected readonly inputTexelSize = uniform(new Vector2())
25
+
26
+ constructor(inputNode?: TextureNode | null) {
27
+ super(inputNode)
28
+
29
+ this.renderTarget = this.createRenderTarget()
30
+ this.outputTexture = this.renderTarget.texture
31
+ }
32
+
33
+ override setSize(width: number, height: number): this {
34
+ const { resolutionScale } = this
35
+ const w = Math.max(Math.round(width * resolutionScale), 1)
36
+ const h = Math.max(Math.round(height * resolutionScale), 1)
37
+ this.renderTarget.setSize(w, h)
38
+ return this
39
+ }
40
+
41
+ override updateBefore({ renderer }: NodeFrame): void {
42
+ if (renderer == null) {
43
+ return
44
+ }
45
+
46
+ const { inputNode } = this
47
+ invariant(inputNode != null)
48
+
49
+ const { width, height } = inputNode.value
50
+ this.setSize(width, height)
51
+ this.inputTexelSize.value.set(1 / width, 1 / height)
52
+
53
+ this.rendererState = resetRendererState(renderer, this.rendererState)
54
+
55
+ renderer.setRenderTarget(this.renderTarget)
56
+ this.mesh.render(renderer)
57
+
58
+ restoreRendererState(renderer, this.rendererState)
59
+ }
60
+
61
+ protected abstract setupOutputNode(builder: NodeBuilder): Node
62
+
63
+ override setup(builder: NodeBuilder): unknown {
64
+ const { inputNode } = this
65
+ invariant(inputNode != null)
66
+
67
+ const { material } = this
68
+ material.fragmentNode = this.setupOutputNode(builder)
69
+ material.needsUpdate = true
70
+
71
+ return super.setup(builder)
72
+ }
73
+
74
+ override dispose(): void {
75
+ this.renderTarget.dispose()
76
+ this.material.dispose()
77
+ this.mesh.geometry.dispose()
78
+ super.dispose()
79
+ }
80
+ }