@octanejs/drei 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/UPSTREAM.md +92 -0
- package/package.json +96 -0
- package/src/config.ts +14 -0
- package/src/core/AccumulativeShadows.three.tsrx +313 -0
- package/src/core/AdaptivePerformance.three.tsrx +49 -0
- package/src/core/AsciiRenderer.three.tsrx +61 -0
- package/src/core/Bounds.three.tsrx +323 -0
- package/src/core/Bvh.three.tsrx +116 -0
- package/src/core/CameraControls.three.tsrx +181 -0
- package/src/core/CameraShake.three.tsrx +104 -0
- package/src/core/Caustics.three.tsrx +107 -0
- package/src/core/Center.three.tsrx +145 -0
- package/src/core/Clone.three.tsrx +167 -0
- package/src/core/Cloud.three.tsrx +314 -0
- package/src/core/ComputedAttribute.three.tsrx +27 -0
- package/src/core/ContactShadows.three.tsrx +106 -0
- package/src/core/CubeTexture.three.tsrx +32 -0
- package/src/core/CurveModifier.three.tsrx +37 -0
- package/src/core/Decal.three.tsrx +132 -0
- package/src/core/Detailed.three.tsrx +28 -0
- package/src/core/DetectGPU.three.tsrx +22 -0
- package/src/core/Effects.three.tsrx +123 -0
- package/src/core/Environment.three.tsrx +220 -0
- package/src/core/Example.three.tsrx +56 -0
- package/src/core/Fbo.three.tsrx +70 -0
- package/src/core/Fbx.three.tsrx +25 -0
- package/src/core/Fisheye.three.tsrx +82 -0
- package/src/core/GizmoHelper.three.tsrx +115 -0
- package/src/core/GizmoViewcube.three.tsrx +82 -0
- package/src/core/GizmoViewport.three.tsrx +122 -0
- package/src/core/Gltf.three.tsrx +62 -0
- package/src/core/GradientTexture.three.tsrx +66 -0
- package/src/core/Grid.three.tsrx +170 -0
- package/src/core/Helper.three.tsrx +56 -0
- package/src/core/Hud.three.tsrx +52 -0
- package/src/core/Image.three.tsrx +146 -0
- package/src/core/Instances.three.tsrx +332 -0
- package/src/core/Ktx2.three.tsrx +59 -0
- package/src/core/Lightformer.three.tsrx +75 -0
- package/src/core/MarchingCubes.three.tsrx +125 -0
- package/src/core/MeshPortalMaterial.three.tsrx +103 -0
- package/src/core/MeshReflectorMaterial.three.tsrx +67 -0
- package/src/core/MeshRefractionMaterial.three.tsrx +70 -0
- package/src/core/MeshTransmissionMaterial.three.tsrx +71 -0
- package/src/core/MotionPathControls.three.tsrx +139 -0
- package/src/core/MultiMaterial.three.tsrx +32 -0
- package/src/core/Outlines.three.tsrx +162 -0
- package/src/core/PerformanceMonitor.three.tsrx +144 -0
- package/src/core/PointerLockControls.three.tsrx +84 -0
- package/src/core/Points.three.tsrx +242 -0
- package/src/core/PositionalAudio.three.tsrx +76 -0
- package/src/core/Preload.three.tsrx +40 -0
- package/src/core/Progress.three.tsrx +47 -0
- package/src/core/RenderCubeTexture.three.tsrx +111 -0
- package/src/core/RenderTexture.three.tsrx +102 -0
- package/src/core/Resize.three.tsrx +59 -0
- package/src/core/RoundedBox.three.tsrx +106 -0
- package/src/core/Sampler.three.tsrx +116 -0
- package/src/core/ScreenSizer.three.tsrx +43 -0
- package/src/core/ScreenSpace.three.tsrx +21 -0
- package/src/core/Segments.three.tsrx +119 -0
- package/src/core/Sky.three.tsrx +63 -0
- package/src/core/Sparkles.three.tsrx +145 -0
- package/src/core/Splat.three.tsrx +52 -0
- package/src/core/SpotLight.three.tsrx +350 -0
- package/src/core/SpriteAnimator.three.tsrx +338 -0
- package/src/core/Stage.three.tsrx +112 -0
- package/src/core/Stars.three.tsrx +103 -0
- package/src/core/Stats.three.tsrx +35 -0
- package/src/core/StatsGl.three.tsrx +60 -0
- package/src/core/Svg.three.tsrx +82 -0
- package/src/core/Text.three.tsrx +96 -0
- package/src/core/Text3D.three.tsrx +90 -0
- package/src/core/Texture.three.tsrx +72 -0
- package/src/core/Trail.three.tsrx +167 -0
- package/src/core/TrailTexture.three.tsrx +171 -0
- package/src/core/TransformControls.three.tsrx +155 -0
- package/src/core/VideoTexture.three.tsrx +116 -0
- package/src/core/Wireframe.three.tsrx +92 -0
- package/src/core/basic-controls.three.tsrx +353 -0
- package/src/core/calculateScaleFactor.ts +42 -0
- package/src/core/cameras.three.tsrx +230 -0
- package/src/core/catalog-resource.ts +12 -0
- package/src/core/catalog-textures.three.tsrx +105 -0
- package/src/core/compiler-slot.ts +9 -0
- package/src/core/effects-utils.ts +8 -0
- package/src/core/environment-loader.ts +87 -0
- package/src/core/font-resource.ts +31 -0
- package/src/core/gltf-loader.ts +33 -0
- package/src/core/gradient-types.ts +4 -0
- package/src/core/index.ts +295 -0
- package/src/core/lines.three.tsrx +329 -0
- package/src/core/meshBounds.ts +26 -0
- package/src/core/motion-materials.three.tsrx +93 -0
- package/src/core/octane-children.ts +18 -0
- package/src/core/octane-element.ts +16 -0
- package/src/core/resource.ts +29 -0
- package/src/core/scene-motion.three.tsrx +183 -0
- package/src/core/shaderMaterial.ts +50 -0
- package/src/core/shadows.three.tsrx +164 -0
- package/src/core/shapes.three.tsrx +170 -0
- package/src/core/simple-materials.three.tsrx +124 -0
- package/src/core/softShadows.three.tsrx +151 -0
- package/src/core/splat-resource.ts +598 -0
- package/src/core/useAnimations.three.tsrx +57 -0
- package/src/core/useAspect.three.tsrx +10 -0
- package/src/core/useBoxProjectedEnv.three.tsrx +113 -0
- package/src/core/useCamera.three.tsrx +26 -0
- package/src/core/useContextBridge.three.tsrx +25 -0
- package/src/core/useDepthBuffer.three.tsrx +35 -0
- package/src/core/useEnvironment.three.tsrx +81 -0
- package/src/core/useFont.three.tsrx +12 -0
- package/src/core/useIntersect.three.tsrx +38 -0
- package/src/core/useSpriteLoader.three.tsrx +249 -0
- package/src/core/video-texture-resource.ts +14 -0
- package/src/index.ts +2 -0
- package/src/materials/BlurPass.ts +94 -0
- package/src/materials/CausticsMaterials.ts +197 -0
- package/src/materials/ConvolutionMaterial.ts +65 -0
- package/src/materials/MeshReflectorMaterial.ts +180 -0
- package/src/materials/MeshRefractionMaterial.ts +131 -0
- package/src/materials/MeshTransmissionMaterial.ts +182 -0
- package/src/materials/PortalMaterial.ts +38 -0
- package/src/materials/WireframeMaterial.ts +241 -0
- package/src/materials/makeSDFGenerator.ts +155 -0
- package/src/troika-three-text.d.ts +19 -0
- package/src/web/CycleRaycast.three.tsrx +81 -0
- package/src/web/DragControls.three.tsrx +82 -0
- package/src/web/FaceControls.three.tsrx +178 -0
- package/src/web/FaceLandmarker.three.tsrx +48 -0
- package/src/web/Facemesh.three.tsrx +464 -0
- package/src/web/Html.three.tsrx +390 -0
- package/src/web/KeyboardControls.three.tsrx +98 -0
- package/src/web/Loader.tsrx +61 -0
- package/src/web/PresentationControls.three.tsrx +63 -0
- package/src/web/ScrollControls.three.tsrx +120 -0
- package/src/web/Select.three.tsrx +155 -0
- package/src/web/View.three.tsrx +46 -0
- package/src/web/face-landmarker-resource.ts +18 -0
- package/src/web/facemesh-data.ts +54 -0
- package/src/web/index.ts +41 -0
- package/src/web/keyboard-controls-types.ts +33 -0
- package/src/web/media-textures.three.tsrx +50 -0
- package/src/web/pivotControls/annotation.tsrx +17 -0
- package/src/web/pivotControls/context.ts +38 -0
- package/src/web/pivotControls/index.three.tsrx +877 -0
- package/src/web/scroll-html-root.tsrx +27 -0
- package/src/web/useCursor.three.tsrx +19 -0
- package/src/web/useGesture.ts +27 -0
- package/status.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Paul Henschel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @octanejs/drei
|
|
2
|
+
|
|
3
|
+
An Octane port of the complete pinned `@react-three/drei@10.7.7` public web API.
|
|
4
|
+
|
|
5
|
+
This package is under active parity development. Compatibility claims are tied
|
|
6
|
+
to the executable upstream crosswalk and React/Octane evidence described in
|
|
7
|
+
[UPSTREAM.md](./UPSTREAM.md); no unsupported export is exposed as a stub.
|
|
8
|
+
|
|
9
|
+
Compiler configuration must use `dreiRenderers` from `@octanejs/drei/config`.
|
|
10
|
+
The preset includes the Three renderer and the Three-to-DOM child boundary used
|
|
11
|
+
by `Html` and PivotControls annotations.
|
package/UPSTREAM.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Upstream Drei audit
|
|
2
|
+
|
|
3
|
+
This port targets the immutable `@react-three/drei@10.7.7` release:
|
|
4
|
+
|
|
5
|
+
- repository: `https://github.com/pmndrs/drei`;
|
|
6
|
+
- tag: `v10.7.7`;
|
|
7
|
+
- tag commit: `b8b99fd4ca1dfb8d821335671320512daa6efea4`;
|
|
8
|
+
- package: `@react-three/drei@10.7.7`;
|
|
9
|
+
- React oracle: React 19 with `@react-three/fiber@9.6.1`;
|
|
10
|
+
- Three compatibility range: `three >=0.159`.
|
|
11
|
+
|
|
12
|
+
## Source and evidence boundary
|
|
13
|
+
|
|
14
|
+
`upstream/` is a byte-exact development-only snapshot of the release's `src/`,
|
|
15
|
+
`test/`, package manifest, TypeScript configuration, and MIT license. It is not
|
|
16
|
+
included in the published package. The registry artifact supplies the executable
|
|
17
|
+
React oracle and declaration surface; the tagged repository supplies authored
|
|
18
|
+
source, stories, test configuration, and the sole upstream end-to-end suite.
|
|
19
|
+
|
|
20
|
+
Drei has no focused unit-test corpus at this pin. Its observable component
|
|
21
|
+
contracts are primarily expressed through Storybook stories. Those artifacts are
|
|
22
|
+
therefore inventoried as upstream evidence, then exercised through paired React
|
|
23
|
+
and Octane characterization fixtures at the appropriate DOM, Three-scene, or
|
|
24
|
+
real-browser observation boundary.
|
|
25
|
+
|
|
26
|
+
The complete tagged test tree contains exactly four artifacts, all under
|
|
27
|
+
`test/e2e/`: `App.tsx`, `e2e.sh`, `snapshot.test.ts`, and its Linux PNG snapshot.
|
|
28
|
+
They form one whole-gallery Playwright screenshot case. Their byte hashes and
|
|
29
|
+
dispositions are recorded in `audit/upstream-test-artifacts.json`.
|
|
30
|
+
|
|
31
|
+
The full tarball/Vite/Next workflow in `e2e.sh` remains out of scope. The
|
|
32
|
+
vendored `snapshot.test.ts` case now runs unchanged in Chromium through the
|
|
33
|
+
`playwright-full` lane, which creates the small Vite React app it expects from
|
|
34
|
+
the byte-exact vendored `App.tsx`. The separate `drei-adapted-browser` Vitest
|
|
35
|
+
project ports that scene to Octane and uses the same screenshot oracle; it is
|
|
36
|
+
adapted evidence, never a claim that the upstream test ran there. The upstream
|
|
37
|
+
runtime suite remains `insufficient`: its one case is genuine pristine evidence,
|
|
38
|
+
but an incomplete suite cannot replace the required upstream-suite lanes and
|
|
39
|
+
repo-authored differential evidence.
|
|
40
|
+
|
|
41
|
+
The tag contains no upstream type-test suite. The upstream `tsconfig.json`
|
|
42
|
+
compiles package source, while the `@ts-expect-error` comments in that source are
|
|
43
|
+
inventoried separately so removing one makes the audit fail. The port's
|
|
44
|
+
`typetests/` remain repository-authored API checks. Paired pristine/adapted
|
|
45
|
+
public-surface type lanes under `typetests/{pristine,adapted}/` run through
|
|
46
|
+
`react-parity:check`; the broader `typetests/*.test-d.ts` suite continues in
|
|
47
|
+
package typecheck.
|
|
48
|
+
|
|
49
|
+
## Executable parity evidence
|
|
50
|
+
|
|
51
|
+
`audit/react-parity.json` registers the pristine upstream Playwright lane, the
|
|
52
|
+
separate adapted upstream browser lane, the `drei-differential` Vitest project
|
|
53
|
+
(all paired React/Octane characterization files, including the View canary), and
|
|
54
|
+
repo-authored pristine/adapted type lanes with the global `react-parity:check`
|
|
55
|
+
harness. The View renderer-boundary divergence cites an ordinary
|
|
56
|
+
`ordinary:view-renderer-boundary` audit identity (not a React-parity lane) so
|
|
57
|
+
authentication stays in the ordinary `drei-guards` project.
|
|
58
|
+
`audit/test-classifications.json` gives every port-authored runtime and type
|
|
59
|
+
test file exactly one disposition. Paired files import the pinned React Drei
|
|
60
|
+
oracle in the test body; `config.test.ts`, `crosswalk-guard.test.ts`,
|
|
61
|
+
`react-parity-guard.test.ts`, and `view-renderer-boundary.test.ts` are
|
|
62
|
+
Octane-only and execute in the ordinary `drei-guards` project outside
|
|
63
|
+
`testExecution`. Ordinary `typetests/*.test-d.ts` files are classified
|
|
64
|
+
Octane-only and stay outside parity evidence; only the public-API pair under
|
|
65
|
+
`typetests/{pristine,adapted}/` is parity type evidence.
|
|
66
|
+
|
|
67
|
+
`audit/runtime-evidence.json` hashes every test file and every collected assertion
|
|
68
|
+
inventory. `audit/upstream-test-artifacts.json` records the executed screenshot
|
|
69
|
+
case and its supporting artifacts. `typetests/assertions.md` defines the
|
|
70
|
+
permitted import/comment transformations and shared assertion groups. The audit
|
|
71
|
+
guard includes negative controls for a dropped differential inventory file, deleted
|
|
72
|
+
assertion, removed upstream `@ts-expect-error` inventory entry, and fabricated
|
|
73
|
+
upstream type suite.
|
|
74
|
+
|
|
75
|
+
## Completeness contract
|
|
76
|
+
|
|
77
|
+
`audit/upstream-crosswalk.json` is generated from the pinned public runtime and
|
|
78
|
+
type surfaces. Every public web export must have an Octane implementation and
|
|
79
|
+
executable evidence before this package claims parity. Missing, placeholder, or
|
|
80
|
+
unclassified entries fail validation; intentional Octane differences require a
|
|
81
|
+
consumer-visible rationale and evidence from both implementations.
|
|
82
|
+
|
|
83
|
+
## Intentional renderer divergence
|
|
84
|
+
|
|
85
|
+
`View` supports Drei's inline Canvas form, including tracked rectangles, portal
|
|
86
|
+
scenes, scissor rendering, event computation, and render ordering. React Drei
|
|
87
|
+
also lets the same component render in a DOM root and transports its Three
|
|
88
|
+
children through `View.Port` using `tunnel-rat`. Octane components are
|
|
89
|
+
statically owned by one renderer, so a Three component cannot switch to the DOM
|
|
90
|
+
renderer or move authored children between independent DOM and Three roots.
|
|
91
|
+
Calling `View` from a DOM root therefore raises Octane's renderer-boundary
|
|
92
|
+
diagnostic, and `View.Port` remains a callable, type-compatible no-op.
|
package/package.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octanejs/drei",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Octane port of @react-three/drei",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"main": "./src/index.ts",
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./src/index.ts",
|
|
14
|
+
"./web": "./src/web/index.ts",
|
|
15
|
+
"./core": "./src/core/index.ts",
|
|
16
|
+
"./config": "./src/config.ts",
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"src",
|
|
21
|
+
"README.md",
|
|
22
|
+
"UPSTREAM.md",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"status.json"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@mediapipe/tasks-vision": "0.10.17",
|
|
28
|
+
"@monogrid/gainmap-js": "^3.0.6",
|
|
29
|
+
"@use-gesture/core": "^10.3.1",
|
|
30
|
+
"camera-controls": "^3.1.0",
|
|
31
|
+
"detect-gpu": "^5.0.56",
|
|
32
|
+
"hls.js": "^1.5.17",
|
|
33
|
+
"maath": "^0.10.8",
|
|
34
|
+
"meshline": "^3.3.1",
|
|
35
|
+
"stats-gl": "^2.2.8",
|
|
36
|
+
"stats.js": "^0.17.0",
|
|
37
|
+
"three-mesh-bvh": "^0.8.3",
|
|
38
|
+
"three-stdlib": "^2.35.6",
|
|
39
|
+
"troika-three-text": "^0.52.4",
|
|
40
|
+
"@octanejs/zustand": "0.1.34"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22.22.2"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@types/three": ">=0.159",
|
|
47
|
+
"three": ">=0.159",
|
|
48
|
+
"@octanejs/three": "0.1.28",
|
|
49
|
+
"octane": "0.1.35"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"@types/three": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/octanejs/octane.git",
|
|
60
|
+
"directory": "packages/drei"
|
|
61
|
+
},
|
|
62
|
+
"octane": {
|
|
63
|
+
"hookSlots": {
|
|
64
|
+
"manual": [
|
|
65
|
+
"src"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@react-three/drei": "10.7.7",
|
|
71
|
+
"@react-three/fiber": "9.6.1",
|
|
72
|
+
"@tsrx/react": "^0.2.56",
|
|
73
|
+
"@types/react": "^19.2.17",
|
|
74
|
+
"@types/stats.js": "^0.17.4",
|
|
75
|
+
"@types/three": "0.172.0",
|
|
76
|
+
"playwright": "^1.61.1",
|
|
77
|
+
"react": "^19.2.7",
|
|
78
|
+
"react-dom": "^19.2.7",
|
|
79
|
+
"three": "0.172.0",
|
|
80
|
+
"typescript": "^5.9.3",
|
|
81
|
+
"vite": "^8.1.5",
|
|
82
|
+
"vitest": "^4.1.10",
|
|
83
|
+
"@octanejs/three": "0.1.28",
|
|
84
|
+
"@octanejs/zustand": "0.1.34",
|
|
85
|
+
"octane": "0.1.35"
|
|
86
|
+
},
|
|
87
|
+
"scripts": {
|
|
88
|
+
"upstream:generate": "node scripts/generate-upstream-crosswalk.mjs",
|
|
89
|
+
"upstream:check": "node scripts/check-upstream-crosswalk.mjs",
|
|
90
|
+
"upstream:ready": "node scripts/check-upstream-crosswalk.mjs --ready",
|
|
91
|
+
"react-parity:generate": "node scripts/generate-react-parity.mjs",
|
|
92
|
+
"react-parity:check": "node scripts/check-react-parity.mjs",
|
|
93
|
+
"test": "node scripts/check-react-parity.mjs && vitest run --root ../.. --project drei-differential --project drei-guards --project drei-adapted-browser",
|
|
94
|
+
"typecheck": "tsrx-tsc --noEmit -p tsconfig.json && tsrx-tsc --noEmit -p typetests/tsconfig.json"
|
|
95
|
+
}
|
|
96
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { threeRenderers } from '@octanejs/three/config';
|
|
2
|
+
|
|
3
|
+
export const dreiRenderers = {
|
|
4
|
+
...threeRenderers,
|
|
5
|
+
boundaries: {
|
|
6
|
+
...threeRenderers.boundaries,
|
|
7
|
+
'@octanejs/drei': {
|
|
8
|
+
Html: { ownerRenderer: 'three', childRenderer: 'dom', prop: 'children' },
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
export const renderers = dreiRenderers;
|
|
14
|
+
export default dreiRenderers;
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/** @jsxImportSource @octanejs/three/intrinsics */
|
|
2
|
+
import { extend, type ThreeElements, type ThreeRef, useFrame, useThree } from '@octanejs/three';
|
|
3
|
+
import { useCallback, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from '@octanejs/three/renderer';
|
|
4
|
+
import { createContext, type OctaneNode, useContext } from 'octane';
|
|
5
|
+
import * as THREE from 'three';
|
|
6
|
+
import { shaderMaterial } from './shaderMaterial.js';
|
|
7
|
+
|
|
8
|
+
function isLight(object: any): object is THREE.Light {
|
|
9
|
+
return Boolean(object.isLight);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isGeometry(object: any): object is THREE.Mesh {
|
|
13
|
+
return Boolean(object.geometry);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type AccumulativeShadowsProps = Omit<ThreeElements['group'], 'ref'> & {
|
|
17
|
+
frames?: number;
|
|
18
|
+
blend?: number;
|
|
19
|
+
limit?: number;
|
|
20
|
+
scale?: number;
|
|
21
|
+
temporal?: boolean;
|
|
22
|
+
opacity?: number;
|
|
23
|
+
alphaTest?: number;
|
|
24
|
+
color?: string;
|
|
25
|
+
colorBlend?: number;
|
|
26
|
+
resolution?: number;
|
|
27
|
+
toneMapped?: boolean;
|
|
28
|
+
children?: OctaneNode;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface AccumulativeContext {
|
|
32
|
+
lights: Map<any, AccumulativeLightContext>;
|
|
33
|
+
temporal: boolean;
|
|
34
|
+
frames: number;
|
|
35
|
+
blend: number;
|
|
36
|
+
count: number;
|
|
37
|
+
getMesh: () => THREE.Mesh<THREE.PlaneGeometry, SoftShadowMaterial>;
|
|
38
|
+
reset: () => void;
|
|
39
|
+
update: (frames?: number) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface AccumulativeLightContext {
|
|
43
|
+
update: () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type SoftShadowMaterial = THREE.ShaderMaterial & {
|
|
47
|
+
map: THREE.Texture;
|
|
48
|
+
color: THREE.Color;
|
|
49
|
+
alphaTest: number;
|
|
50
|
+
blend: number;
|
|
51
|
+
opacity: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const accumulativeContext = createContext<AccumulativeContext>(null!);
|
|
55
|
+
|
|
56
|
+
const SoftShadowMaterialImpl = shaderMaterial(
|
|
57
|
+
{ color: new THREE.Color(), blend: 2, alphaTest: 0.75, opacity: 0, map: null },
|
|
58
|
+
`varying vec2 vUv;
|
|
59
|
+
void main() {
|
|
60
|
+
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.);
|
|
61
|
+
vUv = uv;
|
|
62
|
+
}`,
|
|
63
|
+
`varying vec2 vUv;
|
|
64
|
+
uniform sampler2D map;
|
|
65
|
+
uniform vec3 color;
|
|
66
|
+
uniform float opacity;
|
|
67
|
+
uniform float alphaTest;
|
|
68
|
+
uniform float blend;
|
|
69
|
+
void main() {
|
|
70
|
+
vec4 sampledDiffuseColor = texture2D(map, vUv);
|
|
71
|
+
gl_FragColor = vec4(color * sampledDiffuseColor.r * blend, max(0.0, (1.0 - (sampledDiffuseColor.r + sampledDiffuseColor.g + sampledDiffuseColor.b) / alphaTest)) * opacity);
|
|
72
|
+
#include <tonemapping_fragment>
|
|
73
|
+
#include <colorspace_fragment>
|
|
74
|
+
}`,
|
|
75
|
+
);
|
|
76
|
+
const SoftShadowMaterialElement = extend(SoftShadowMaterialImpl);
|
|
77
|
+
|
|
78
|
+
const DiscardMaterial = shaderMaterial(
|
|
79
|
+
{},
|
|
80
|
+
'void main() { }',
|
|
81
|
+
'void main() { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); discard; }',
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
class ProgressiveLightMap {
|
|
85
|
+
renderer: any;
|
|
86
|
+
res: number;
|
|
87
|
+
scene: THREE.Scene;
|
|
88
|
+
object: THREE.Mesh | null = null;
|
|
89
|
+
buffer1Active = false;
|
|
90
|
+
progressiveLightMap1: THREE.WebGLRenderTarget;
|
|
91
|
+
progressiveLightMap2: THREE.WebGLRenderTarget;
|
|
92
|
+
discardMat = new DiscardMaterial();
|
|
93
|
+
targetMat = new THREE.MeshLambertMaterial({ fog: false });
|
|
94
|
+
previousShadowMap: { value: THREE.Texture };
|
|
95
|
+
averagingWindow = { value: 100 };
|
|
96
|
+
clearColor = new THREE.Color();
|
|
97
|
+
clearAlpha = 0;
|
|
98
|
+
lights: Array<{ object: THREE.Light; intensity: number }> = [];
|
|
99
|
+
meshes: Array<{ object: THREE.Mesh; material: THREE.Material | THREE.Material[] }> = [];
|
|
100
|
+
|
|
101
|
+
constructor(renderer: any, scene: THREE.Scene, res = 1024) {
|
|
102
|
+
this.renderer = renderer;
|
|
103
|
+
this.res = res;
|
|
104
|
+
this.scene = scene;
|
|
105
|
+
const textureParams = { type: THREE.HalfFloatType, magFilter: THREE.NearestFilter, minFilter: THREE.NearestFilter };
|
|
106
|
+
this.progressiveLightMap1 = new THREE.WebGLRenderTarget(res, res, textureParams);
|
|
107
|
+
this.progressiveLightMap2 = new THREE.WebGLRenderTarget(res, res, textureParams);
|
|
108
|
+
this.previousShadowMap = { value: this.progressiveLightMap1.texture };
|
|
109
|
+
this.targetMat.onBeforeCompile = (shader) => {
|
|
110
|
+
shader.vertexShader = `varying vec2 vUv;\n${shader.vertexShader.slice(0, -1)}vUv = uv; gl_Position = vec4((uv - 0.5) * 2.0, 1.0, 1.0); }`;
|
|
111
|
+
const bodyStart = shader.fragmentShader.indexOf('void main() {');
|
|
112
|
+
shader.fragmentShader = `varying vec2 vUv;\n${shader.fragmentShader.slice(0, bodyStart)}uniform sampler2D previousShadowMap;\nuniform float averagingWindow;\n${shader.fragmentShader.slice(bodyStart - 1, -1)}\nvec3 texelOld = texture2D(previousShadowMap, vUv).rgb;\ngl_FragColor.rgb = mix(texelOld, gl_FragColor.rgb, 1.0 / averagingWindow);\n}`;
|
|
113
|
+
shader.uniforms.previousShadowMap = this.previousShadowMap;
|
|
114
|
+
shader.uniforms.averagingWindow = this.averagingWindow;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
clear() {
|
|
119
|
+
this.renderer.getClearColor(this.clearColor);
|
|
120
|
+
this.clearAlpha = this.renderer.getClearAlpha();
|
|
121
|
+
this.renderer.setClearColor('black', 1);
|
|
122
|
+
this.renderer.setRenderTarget(this.progressiveLightMap1);
|
|
123
|
+
this.renderer.clear();
|
|
124
|
+
this.renderer.setRenderTarget(this.progressiveLightMap2);
|
|
125
|
+
this.renderer.clear();
|
|
126
|
+
this.renderer.setRenderTarget(null);
|
|
127
|
+
this.renderer.setClearColor(this.clearColor, this.clearAlpha);
|
|
128
|
+
this.lights = [];
|
|
129
|
+
this.meshes = [];
|
|
130
|
+
this.scene.traverse((object) => {
|
|
131
|
+
if (isGeometry(object)) this.meshes.push({ object, material: object.material });
|
|
132
|
+
else if (isLight(object)) this.lights.push({ object, intensity: object.intensity });
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
prepare() {
|
|
137
|
+
this.lights.forEach((light) => (light.object.intensity = 0));
|
|
138
|
+
this.meshes.forEach((mesh) => (mesh.object.material = this.discardMat));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
finish() {
|
|
142
|
+
this.lights.forEach((light) => (light.object.intensity = light.intensity));
|
|
143
|
+
this.meshes.forEach((mesh) => (mesh.object.material = mesh.material));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
configure(object: THREE.Mesh) {
|
|
147
|
+
this.object = object;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
update(camera: THREE.Camera, blendWindow = 100) {
|
|
151
|
+
if (!this.object) return;
|
|
152
|
+
this.averagingWindow.value = blendWindow;
|
|
153
|
+
this.object.material = this.targetMat;
|
|
154
|
+
const activeMap = this.buffer1Active ? this.progressiveLightMap1 : this.progressiveLightMap2;
|
|
155
|
+
const inactiveMap = this.buffer1Active ? this.progressiveLightMap2 : this.progressiveLightMap1;
|
|
156
|
+
const oldBackground = this.scene.background;
|
|
157
|
+
this.scene.background = null;
|
|
158
|
+
this.renderer.setRenderTarget(activeMap);
|
|
159
|
+
this.previousShadowMap.value = inactiveMap.texture;
|
|
160
|
+
this.buffer1Active = !this.buffer1Active;
|
|
161
|
+
this.renderer.render(this.scene, camera);
|
|
162
|
+
this.renderer.setRenderTarget(null);
|
|
163
|
+
this.scene.background = oldBackground;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function AccumulativeShadows({
|
|
168
|
+
children,
|
|
169
|
+
temporal,
|
|
170
|
+
frames = 40,
|
|
171
|
+
limit = Infinity,
|
|
172
|
+
blend = 20,
|
|
173
|
+
scale = 10,
|
|
174
|
+
opacity = 1,
|
|
175
|
+
alphaTest = 0.75,
|
|
176
|
+
color = 'black',
|
|
177
|
+
colorBlend = 2,
|
|
178
|
+
resolution = 1024,
|
|
179
|
+
toneMapped = true,
|
|
180
|
+
ref,
|
|
181
|
+
...props
|
|
182
|
+
}: AccumulativeShadowsProps & { ref?: ThreeRef<AccumulativeContext> }) @{
|
|
183
|
+
const gl = useThree((state) => state.gl);
|
|
184
|
+
const scene = useThree((state) => state.scene);
|
|
185
|
+
const camera = useThree((state) => state.camera);
|
|
186
|
+
const invalidate = useThree((state) => state.invalidate);
|
|
187
|
+
const plane = useRef<THREE.Mesh<THREE.PlaneGeometry, SoftShadowMaterial> | null>(null);
|
|
188
|
+
const lights = useRef<THREE.Group | null>(null);
|
|
189
|
+
const [lightMap] = useState(() => new ProgressiveLightMap(gl, scene, resolution));
|
|
190
|
+
useLayoutEffect(() => lightMap.configure(plane.current!), []);
|
|
191
|
+
const api = useMemo<AccumulativeContext>(() => ({
|
|
192
|
+
lights: new Map(),
|
|
193
|
+
temporal: Boolean(temporal),
|
|
194
|
+
frames: Math.max(2, frames),
|
|
195
|
+
blend: Math.max(2, frames === Infinity ? blend : frames),
|
|
196
|
+
count: 0,
|
|
197
|
+
getMesh: () => plane.current!,
|
|
198
|
+
reset: () => {
|
|
199
|
+
lightMap.clear();
|
|
200
|
+
plane.current!.material.opacity = 0;
|
|
201
|
+
plane.current!.material.alphaTest = 0;
|
|
202
|
+
api.count = 0;
|
|
203
|
+
},
|
|
204
|
+
update: (count = 1) => {
|
|
205
|
+
const material = plane.current!.material;
|
|
206
|
+
if (!api.temporal) {
|
|
207
|
+
material.opacity = opacity;
|
|
208
|
+
material.alphaTest = alphaTest;
|
|
209
|
+
} else {
|
|
210
|
+
material.opacity = Math.min(opacity, material.opacity + opacity / api.blend);
|
|
211
|
+
material.alphaTest = Math.min(alphaTest, material.alphaTest + alphaTest / api.blend);
|
|
212
|
+
}
|
|
213
|
+
lights.current!.visible = true;
|
|
214
|
+
lightMap.prepare();
|
|
215
|
+
for (let index = 0; index < count; index++) {
|
|
216
|
+
api.lights.forEach((light) => light.update());
|
|
217
|
+
lightMap.update(camera, api.blend);
|
|
218
|
+
}
|
|
219
|
+
lights.current!.visible = false;
|
|
220
|
+
lightMap.finish();
|
|
221
|
+
},
|
|
222
|
+
}), [lightMap, camera, scene, temporal, frames, blend, opacity, alphaTest]);
|
|
223
|
+
useLayoutEffect(() => {
|
|
224
|
+
api.reset();
|
|
225
|
+
if (!api.temporal && api.frames !== Infinity) api.update(api.blend);
|
|
226
|
+
});
|
|
227
|
+
useImperativeHandle((ref ?? null) as any, () => api, [api]);
|
|
228
|
+
useFrame(() => {
|
|
229
|
+
if ((api.temporal || api.frames === Infinity) && api.count < api.frames && api.count < limit) {
|
|
230
|
+
invalidate();
|
|
231
|
+
api.update();
|
|
232
|
+
api.count++;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
<group {...props}>
|
|
236
|
+
<group {...({ traverse: () => null } as any)} ref={lights}>
|
|
237
|
+
<accumulativeContext.Provider value={api}>{children}</accumulativeContext.Provider>
|
|
238
|
+
</group>
|
|
239
|
+
<mesh receiveShadow ref={plane as any} scale={scale} rotation={[-Math.PI / 2, 0, 0]}>
|
|
240
|
+
<planeGeometry />
|
|
241
|
+
<SoftShadowMaterialElement transparent depthWrite={false} toneMapped={toneMapped} color={color} blend={colorBlend} map={lightMap.progressiveLightMap2.texture as any} />
|
|
242
|
+
</mesh>
|
|
243
|
+
</group>
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export type RandomizedLightProps = Omit<ThreeElements['group'], 'ref'> & {
|
|
247
|
+
frames?: number;
|
|
248
|
+
position?: [x: number, y: number, z: number];
|
|
249
|
+
radius?: number;
|
|
250
|
+
amount?: number;
|
|
251
|
+
intensity?: number;
|
|
252
|
+
ambient?: number;
|
|
253
|
+
castShadow?: boolean;
|
|
254
|
+
bias?: number;
|
|
255
|
+
mapSize?: number;
|
|
256
|
+
size?: number;
|
|
257
|
+
near?: number;
|
|
258
|
+
far?: number;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export function RandomizedLight({
|
|
262
|
+
castShadow = true,
|
|
263
|
+
bias = 0.001,
|
|
264
|
+
mapSize = 512,
|
|
265
|
+
size = 5,
|
|
266
|
+
near = 0.5,
|
|
267
|
+
far = 500,
|
|
268
|
+
frames = 1,
|
|
269
|
+
position = [0, 0, 0],
|
|
270
|
+
radius = 1,
|
|
271
|
+
amount = 8,
|
|
272
|
+
intensity = Math.PI,
|
|
273
|
+
ambient = 0.5,
|
|
274
|
+
ref,
|
|
275
|
+
...props
|
|
276
|
+
}: RandomizedLightProps & { ref?: ThreeRef<AccumulativeLightContext> }) @{
|
|
277
|
+
const group = useRef<THREE.Group | null>(null);
|
|
278
|
+
const length = new THREE.Vector3(...position).length();
|
|
279
|
+
const parent = useContext(accumulativeContext);
|
|
280
|
+
const update = useCallback(() => {
|
|
281
|
+
if (!group.current) return;
|
|
282
|
+
for (const light of group.current.children) {
|
|
283
|
+
if (Math.random() > ambient) {
|
|
284
|
+
light.position.set(
|
|
285
|
+
position[0] + THREE.MathUtils.randFloatSpread(radius),
|
|
286
|
+
position[1] + THREE.MathUtils.randFloatSpread(radius),
|
|
287
|
+
position[2] + THREE.MathUtils.randFloatSpread(radius),
|
|
288
|
+
);
|
|
289
|
+
} else {
|
|
290
|
+
const lambda = Math.acos(2 * Math.random() - 1) - Math.PI / 2;
|
|
291
|
+
const phi = 2 * Math.PI * Math.random();
|
|
292
|
+
light.position.set(
|
|
293
|
+
Math.cos(lambda) * Math.cos(phi) * length,
|
|
294
|
+
Math.abs(Math.cos(lambda) * Math.sin(phi) * length),
|
|
295
|
+
Math.sin(lambda) * length,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}, [radius, ambient, length, ...position]);
|
|
300
|
+
const api = useMemo<AccumulativeLightContext>(() => ({ update }), [update]);
|
|
301
|
+
useImperativeHandle((ref ?? null) as any, () => api, [api]);
|
|
302
|
+
useLayoutEffect(() => {
|
|
303
|
+
if (parent && group.current) parent.lights.set(group.current.uuid, api);
|
|
304
|
+
return () => { if (group.current) parent?.lights.delete(group.current.uuid); };
|
|
305
|
+
}, [parent, api]);
|
|
306
|
+
<group ref={group} {...props}>
|
|
307
|
+
@for (const index of Array.from({ length: amount }, (_, value) => value); key index) {
|
|
308
|
+
<directionalLight castShadow={castShadow} shadow-bias={bias} shadow-mapSize={[mapSize, mapSize]} intensity={intensity / amount}>
|
|
309
|
+
<orthographicCamera attach="shadow-camera" args={[-size, size, size, -size, near, far]} />
|
|
310
|
+
</directionalLight>
|
|
311
|
+
}
|
|
312
|
+
</group>
|
|
313
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useThree } from '@octanejs/three';
|
|
2
|
+
import { useEffect } from '@octanejs/three/renderer';
|
|
3
|
+
|
|
4
|
+
export function AdaptiveEvents() @{
|
|
5
|
+
const get = useThree((state) => state.get);
|
|
6
|
+
const setEvents = useThree((state) => state.setEvents);
|
|
7
|
+
const current = useThree((state) => state.performance.current);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const enabled = get().events.enabled;
|
|
10
|
+
return () => setEvents({ enabled });
|
|
11
|
+
}, []);
|
|
12
|
+
useEffect(() => setEvents({ enabled: current === 1 }), [current]);
|
|
13
|
+
null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function BakeShadows() @{
|
|
17
|
+
const gl = useThree((state) => state.gl);
|
|
18
|
+
const shadowMap = gl.shadowMap!;
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
shadowMap.autoUpdate = false;
|
|
21
|
+
shadowMap.needsUpdate = true;
|
|
22
|
+
return () => {
|
|
23
|
+
shadowMap.autoUpdate = shadowMap.needsUpdate = true;
|
|
24
|
+
};
|
|
25
|
+
}, [shadowMap]);
|
|
26
|
+
null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function AdaptiveDpr({ pixelated }: { pixelated?: boolean }) @{
|
|
30
|
+
const gl = useThree((state) => state.gl);
|
|
31
|
+
const active = useThree((state) => state.internal.active);
|
|
32
|
+
const current = useThree((state) => state.performance.current);
|
|
33
|
+
const initialDpr = useThree((state) => state.viewport.initialDpr);
|
|
34
|
+
const setDpr = useThree((state) => state.setDpr);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const domElement = gl.domElement as HTMLElement;
|
|
37
|
+
return () => {
|
|
38
|
+
if (active) setDpr(initialDpr);
|
|
39
|
+
if (pixelated && domElement) domElement.style.imageRendering = 'auto';
|
|
40
|
+
};
|
|
41
|
+
}, []);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
setDpr(current * initialDpr);
|
|
44
|
+
if (pixelated && gl.domElement) {
|
|
45
|
+
(gl.domElement as HTMLElement).style.imageRendering = current === 1 ? 'auto' : 'pixelated';
|
|
46
|
+
}
|
|
47
|
+
}, [current]);
|
|
48
|
+
null;
|
|
49
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** @jsxImportSource @octanejs/three/intrinsics */
|
|
2
|
+
import { useFrame, useThree } from '@octanejs/three';
|
|
3
|
+
import { useEffect, useLayoutEffect, useMemo } from '@octanejs/three/renderer';
|
|
4
|
+
import { AsciiEffect } from 'three-stdlib';
|
|
5
|
+
|
|
6
|
+
export type AsciiRendererProps = {
|
|
7
|
+
renderIndex?: number;
|
|
8
|
+
bgColor?: string;
|
|
9
|
+
fgColor?: string;
|
|
10
|
+
characters?: string;
|
|
11
|
+
invert?: boolean;
|
|
12
|
+
color?: boolean;
|
|
13
|
+
resolution?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function AsciiRenderer({
|
|
17
|
+
renderIndex = 1,
|
|
18
|
+
bgColor = 'black',
|
|
19
|
+
fgColor = 'white',
|
|
20
|
+
characters = ' .:-+*=%@#',
|
|
21
|
+
invert = true,
|
|
22
|
+
color = false,
|
|
23
|
+
resolution = 0.15,
|
|
24
|
+
}: AsciiRendererProps) @{
|
|
25
|
+
const { size, gl, scene, camera } = useThree();
|
|
26
|
+
const canvas = gl.domElement as HTMLCanvasElement;
|
|
27
|
+
const effect = useMemo(() => {
|
|
28
|
+
// three-stdlib follows the workspace's newer Three types while the renderer
|
|
29
|
+
// intentionally accepts the binding's structural renderer contract.
|
|
30
|
+
const value = new AsciiEffect(gl as any, characters, { invert, color, resolution });
|
|
31
|
+
value.domElement.style.position = 'absolute';
|
|
32
|
+
value.domElement.style.top = '0px';
|
|
33
|
+
value.domElement.style.left = '0px';
|
|
34
|
+
value.domElement.style.pointerEvents = 'none';
|
|
35
|
+
return value;
|
|
36
|
+
}, [characters, invert, color, resolution]);
|
|
37
|
+
|
|
38
|
+
useLayoutEffect(() => {
|
|
39
|
+
effect.domElement.style.color = fgColor;
|
|
40
|
+
effect.domElement.style.backgroundColor = bgColor;
|
|
41
|
+
}, [fgColor, bgColor]);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
canvas.style.opacity = '0';
|
|
45
|
+
canvas.parentNode!.appendChild(effect.domElement);
|
|
46
|
+
return () => {
|
|
47
|
+
canvas.style.opacity = '1';
|
|
48
|
+
canvas.parentNode!.removeChild(effect.domElement);
|
|
49
|
+
};
|
|
50
|
+
}, [effect]);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
effect.setSize(size.width, size.height);
|
|
54
|
+
}, [effect, size]);
|
|
55
|
+
|
|
56
|
+
useFrame(() => {
|
|
57
|
+
effect.render(scene as any, camera as any);
|
|
58
|
+
}, renderIndex);
|
|
59
|
+
|
|
60
|
+
null;
|
|
61
|
+
}
|