@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.
Files changed (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/UPSTREAM.md +92 -0
  4. package/package.json +96 -0
  5. package/src/config.ts +14 -0
  6. package/src/core/AccumulativeShadows.three.tsrx +313 -0
  7. package/src/core/AdaptivePerformance.three.tsrx +49 -0
  8. package/src/core/AsciiRenderer.three.tsrx +61 -0
  9. package/src/core/Bounds.three.tsrx +323 -0
  10. package/src/core/Bvh.three.tsrx +116 -0
  11. package/src/core/CameraControls.three.tsrx +181 -0
  12. package/src/core/CameraShake.three.tsrx +104 -0
  13. package/src/core/Caustics.three.tsrx +107 -0
  14. package/src/core/Center.three.tsrx +145 -0
  15. package/src/core/Clone.three.tsrx +167 -0
  16. package/src/core/Cloud.three.tsrx +314 -0
  17. package/src/core/ComputedAttribute.three.tsrx +27 -0
  18. package/src/core/ContactShadows.three.tsrx +106 -0
  19. package/src/core/CubeTexture.three.tsrx +32 -0
  20. package/src/core/CurveModifier.three.tsrx +37 -0
  21. package/src/core/Decal.three.tsrx +132 -0
  22. package/src/core/Detailed.three.tsrx +28 -0
  23. package/src/core/DetectGPU.three.tsrx +22 -0
  24. package/src/core/Effects.three.tsrx +123 -0
  25. package/src/core/Environment.three.tsrx +220 -0
  26. package/src/core/Example.three.tsrx +56 -0
  27. package/src/core/Fbo.three.tsrx +70 -0
  28. package/src/core/Fbx.three.tsrx +25 -0
  29. package/src/core/Fisheye.three.tsrx +82 -0
  30. package/src/core/GizmoHelper.three.tsrx +115 -0
  31. package/src/core/GizmoViewcube.three.tsrx +82 -0
  32. package/src/core/GizmoViewport.three.tsrx +122 -0
  33. package/src/core/Gltf.three.tsrx +62 -0
  34. package/src/core/GradientTexture.three.tsrx +66 -0
  35. package/src/core/Grid.three.tsrx +170 -0
  36. package/src/core/Helper.three.tsrx +56 -0
  37. package/src/core/Hud.three.tsrx +52 -0
  38. package/src/core/Image.three.tsrx +146 -0
  39. package/src/core/Instances.three.tsrx +332 -0
  40. package/src/core/Ktx2.three.tsrx +59 -0
  41. package/src/core/Lightformer.three.tsrx +75 -0
  42. package/src/core/MarchingCubes.three.tsrx +125 -0
  43. package/src/core/MeshPortalMaterial.three.tsrx +103 -0
  44. package/src/core/MeshReflectorMaterial.three.tsrx +67 -0
  45. package/src/core/MeshRefractionMaterial.three.tsrx +70 -0
  46. package/src/core/MeshTransmissionMaterial.three.tsrx +71 -0
  47. package/src/core/MotionPathControls.three.tsrx +139 -0
  48. package/src/core/MultiMaterial.three.tsrx +32 -0
  49. package/src/core/Outlines.three.tsrx +162 -0
  50. package/src/core/PerformanceMonitor.three.tsrx +144 -0
  51. package/src/core/PointerLockControls.three.tsrx +84 -0
  52. package/src/core/Points.three.tsrx +242 -0
  53. package/src/core/PositionalAudio.three.tsrx +76 -0
  54. package/src/core/Preload.three.tsrx +40 -0
  55. package/src/core/Progress.three.tsrx +47 -0
  56. package/src/core/RenderCubeTexture.three.tsrx +111 -0
  57. package/src/core/RenderTexture.three.tsrx +102 -0
  58. package/src/core/Resize.three.tsrx +59 -0
  59. package/src/core/RoundedBox.three.tsrx +106 -0
  60. package/src/core/Sampler.three.tsrx +116 -0
  61. package/src/core/ScreenSizer.three.tsrx +43 -0
  62. package/src/core/ScreenSpace.three.tsrx +21 -0
  63. package/src/core/Segments.three.tsrx +119 -0
  64. package/src/core/Sky.three.tsrx +63 -0
  65. package/src/core/Sparkles.three.tsrx +145 -0
  66. package/src/core/Splat.three.tsrx +52 -0
  67. package/src/core/SpotLight.three.tsrx +350 -0
  68. package/src/core/SpriteAnimator.three.tsrx +338 -0
  69. package/src/core/Stage.three.tsrx +112 -0
  70. package/src/core/Stars.three.tsrx +103 -0
  71. package/src/core/Stats.three.tsrx +35 -0
  72. package/src/core/StatsGl.three.tsrx +60 -0
  73. package/src/core/Svg.three.tsrx +82 -0
  74. package/src/core/Text.three.tsrx +96 -0
  75. package/src/core/Text3D.three.tsrx +90 -0
  76. package/src/core/Texture.three.tsrx +72 -0
  77. package/src/core/Trail.three.tsrx +167 -0
  78. package/src/core/TrailTexture.three.tsrx +171 -0
  79. package/src/core/TransformControls.three.tsrx +155 -0
  80. package/src/core/VideoTexture.three.tsrx +116 -0
  81. package/src/core/Wireframe.three.tsrx +92 -0
  82. package/src/core/basic-controls.three.tsrx +353 -0
  83. package/src/core/calculateScaleFactor.ts +42 -0
  84. package/src/core/cameras.three.tsrx +230 -0
  85. package/src/core/catalog-resource.ts +12 -0
  86. package/src/core/catalog-textures.three.tsrx +105 -0
  87. package/src/core/compiler-slot.ts +9 -0
  88. package/src/core/effects-utils.ts +8 -0
  89. package/src/core/environment-loader.ts +87 -0
  90. package/src/core/font-resource.ts +31 -0
  91. package/src/core/gltf-loader.ts +33 -0
  92. package/src/core/gradient-types.ts +4 -0
  93. package/src/core/index.ts +295 -0
  94. package/src/core/lines.three.tsrx +329 -0
  95. package/src/core/meshBounds.ts +26 -0
  96. package/src/core/motion-materials.three.tsrx +93 -0
  97. package/src/core/octane-children.ts +18 -0
  98. package/src/core/octane-element.ts +16 -0
  99. package/src/core/resource.ts +29 -0
  100. package/src/core/scene-motion.three.tsrx +183 -0
  101. package/src/core/shaderMaterial.ts +50 -0
  102. package/src/core/shadows.three.tsrx +164 -0
  103. package/src/core/shapes.three.tsrx +170 -0
  104. package/src/core/simple-materials.three.tsrx +124 -0
  105. package/src/core/softShadows.three.tsrx +151 -0
  106. package/src/core/splat-resource.ts +598 -0
  107. package/src/core/useAnimations.three.tsrx +57 -0
  108. package/src/core/useAspect.three.tsrx +10 -0
  109. package/src/core/useBoxProjectedEnv.three.tsrx +113 -0
  110. package/src/core/useCamera.three.tsrx +26 -0
  111. package/src/core/useContextBridge.three.tsrx +25 -0
  112. package/src/core/useDepthBuffer.three.tsrx +35 -0
  113. package/src/core/useEnvironment.three.tsrx +81 -0
  114. package/src/core/useFont.three.tsrx +12 -0
  115. package/src/core/useIntersect.three.tsrx +38 -0
  116. package/src/core/useSpriteLoader.three.tsrx +249 -0
  117. package/src/core/video-texture-resource.ts +14 -0
  118. package/src/index.ts +2 -0
  119. package/src/materials/BlurPass.ts +94 -0
  120. package/src/materials/CausticsMaterials.ts +197 -0
  121. package/src/materials/ConvolutionMaterial.ts +65 -0
  122. package/src/materials/MeshReflectorMaterial.ts +180 -0
  123. package/src/materials/MeshRefractionMaterial.ts +131 -0
  124. package/src/materials/MeshTransmissionMaterial.ts +182 -0
  125. package/src/materials/PortalMaterial.ts +38 -0
  126. package/src/materials/WireframeMaterial.ts +241 -0
  127. package/src/materials/makeSDFGenerator.ts +155 -0
  128. package/src/troika-three-text.d.ts +19 -0
  129. package/src/web/CycleRaycast.three.tsrx +81 -0
  130. package/src/web/DragControls.three.tsrx +82 -0
  131. package/src/web/FaceControls.three.tsrx +178 -0
  132. package/src/web/FaceLandmarker.three.tsrx +48 -0
  133. package/src/web/Facemesh.three.tsrx +464 -0
  134. package/src/web/Html.three.tsrx +390 -0
  135. package/src/web/KeyboardControls.three.tsrx +98 -0
  136. package/src/web/Loader.tsrx +61 -0
  137. package/src/web/PresentationControls.three.tsrx +63 -0
  138. package/src/web/ScrollControls.three.tsrx +120 -0
  139. package/src/web/Select.three.tsrx +155 -0
  140. package/src/web/View.three.tsrx +46 -0
  141. package/src/web/face-landmarker-resource.ts +18 -0
  142. package/src/web/facemesh-data.ts +54 -0
  143. package/src/web/index.ts +41 -0
  144. package/src/web/keyboard-controls-types.ts +33 -0
  145. package/src/web/media-textures.three.tsrx +50 -0
  146. package/src/web/pivotControls/annotation.tsrx +17 -0
  147. package/src/web/pivotControls/context.ts +38 -0
  148. package/src/web/pivotControls/index.three.tsrx +877 -0
  149. package/src/web/scroll-html-root.tsrx +27 -0
  150. package/src/web/useCursor.three.tsrx +19 -0
  151. package/src/web/useGesture.ts +27 -0
  152. package/status.json +14 -0
@@ -0,0 +1,22 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { use } from '@octanejs/three/renderer';
3
+ import { getGPUTier, type GetGPUTier } from 'detect-gpu';
4
+ import { slotDefault } from './compiler-slot.js';
5
+ import { resolveRenderChildren, type RenderChildren } from './octane-children.js';
6
+ import { resourcePromise } from './resource.js';
7
+
8
+ export const useDetectGPU = (props?: GetGPUTier) => {
9
+ props = slotDefault(props, undefined);
10
+ return use(resourcePromise(() => getGPUTier(props), ['useDetectGPU']));
11
+ };
12
+
13
+ export type DetectGPUProps = {
14
+ children?: RenderChildren<ReturnType<typeof useDetectGPU>>;
15
+ } & Parameters<typeof useDetectGPU>[0];
16
+
17
+ export function DetectGPU({ children, ...options }: DetectGPUProps) @{
18
+ const result = useDetectGPU(options);
19
+ <>
20
+ {resolveRenderChildren(children, result)}
21
+ </>
22
+ }
@@ -0,0 +1,123 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import {
3
+ extend,
4
+ type ThreeElement,
5
+ type ThreeRef,
6
+ useFrame,
7
+ useThree,
8
+ } from '@octanejs/three';
9
+ import { useEffect, useMemo, useRef, useState } from '@octanejs/three/renderer';
10
+ import * as THREE from 'three';
11
+ import {
12
+ EffectComposer,
13
+ GammaCorrectionShader,
14
+ RenderPass,
15
+ ShaderPass,
16
+ } from 'three-stdlib';
17
+ import { isOctaneChildrenBlock } from './octane-element.js';
18
+
19
+ const EffectComposerElement = extend(EffectComposer);
20
+ const RenderPassElement = extend(RenderPass);
21
+ const ShaderPassElement = extend(ShaderPass);
22
+
23
+ export type EffectsProps = Omit<ThreeElement<typeof EffectComposer>, 'ref' | 'args'> & {
24
+ multisamping?: number;
25
+ colorSpace?: THREE.ColorSpace;
26
+ type?: THREE.TextureDataType;
27
+ renderIndex?: number;
28
+ disableGamma?: boolean;
29
+ disableRenderPass?: boolean;
30
+ disableRender?: boolean;
31
+ depthBuffer?: boolean;
32
+ stencilBuffer?: boolean;
33
+ anisotropy?: number;
34
+ };
35
+
36
+ function assignRef<T>(ref: ThreeRef<T> | undefined, value: T | null): void {
37
+ if (Array.isArray(ref)) for (const item of ref) assignRef(item, value);
38
+ else if (typeof ref === 'function') ref(value);
39
+ else if (ref && 'current' in ref) ref.current = value;
40
+ }
41
+
42
+ function appendPasses(passes: any[], children: unknown): void {
43
+ if (Array.isArray(children)) {
44
+ for (const child of children) appendPasses(passes, child);
45
+ return;
46
+ }
47
+ // Universal plan/element descriptors are objects. Compiled children blocks and
48
+ // other functions are not cloneable — those render through auto-attach instead.
49
+ if (children == null || typeof children === 'boolean' || typeof children === 'function') return;
50
+ if (typeof children !== 'object') return;
51
+ const element = children as { props?: Record<string, unknown> };
52
+ const key = passes.length;
53
+ passes.push({
54
+ ...element,
55
+ key: String(key),
56
+ props: { ...element.props, key, attach: `passes-${key}` },
57
+ });
58
+ }
59
+
60
+ export function Effects({
61
+ children,
62
+ multisamping = 8,
63
+ renderIndex = 1,
64
+ disableRender,
65
+ disableGamma,
66
+ disableRenderPass,
67
+ depthBuffer = true,
68
+ stencilBuffer = false,
69
+ anisotropy = 1,
70
+ colorSpace,
71
+ type,
72
+ ref,
73
+ ...props
74
+ }: EffectsProps & { ref?: ThreeRef<EffectComposer> }) @{
75
+ const composer = useRef<EffectComposer | null>(null);
76
+ const mergedRef = useMemo(
77
+ () => (value: EffectComposer | null) => {
78
+ composer.current = value;
79
+ assignRef(ref, value);
80
+ },
81
+ [ref],
82
+ );
83
+ const { scene, camera, gl, size, viewport } = useThree();
84
+ const [target] = useState(() => {
85
+ const renderTarget = new THREE.WebGLRenderTarget(size.width, size.height, {
86
+ type: type || THREE.HalfFloatType,
87
+ format: THREE.RGBAFormat,
88
+ depthBuffer,
89
+ stencilBuffer,
90
+ anisotropy,
91
+ });
92
+ if (type === THREE.UnsignedByteType && colorSpace != null) {
93
+ renderTarget.texture.colorSpace = colorSpace;
94
+ }
95
+ renderTarget.samples = multisamping;
96
+ return renderTarget;
97
+ });
98
+ useEffect(() => {
99
+ composer.current?.setSize(size.width, size.height);
100
+ composer.current?.setPixelRatio(viewport.dpr);
101
+ }, [gl, size, viewport.dpr]);
102
+ useFrame(() => {
103
+ if (!disableRender) composer.current?.render();
104
+ }, renderIndex);
105
+ const passes: any[] = [];
106
+ if (!disableRenderPass) {
107
+ passes.push(<RenderPassElement key="renderpass" attach={`passes-${passes.length}`} args={[scene, camera] as any} />);
108
+ }
109
+ if (!disableGamma) {
110
+ passes.push(<ShaderPassElement key="gammapass" attach={`passes-${passes.length}`} args={[GammaCorrectionShader]} />);
111
+ }
112
+ const blockChildren = isOctaneChildrenBlock(children) ? children : null;
113
+ if (!blockChildren) appendPasses(passes, children);
114
+ <EffectComposerElement
115
+ ref={mergedRef}
116
+ args={[gl as any, target]}
117
+ __octaneAutoAttachArray="passes"
118
+ {...props as any}
119
+ >
120
+ {passes}
121
+ {blockChildren}
122
+ </EffectComposerElement>
123
+ }
@@ -0,0 +1,220 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import {
3
+ applyProps,
4
+ createPortal,
5
+ extend,
6
+ type ThreeElement,
7
+ useFrame,
8
+ useThree,
9
+ } from '@octanejs/three';
10
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState, type RefObject } from '@octanejs/three/renderer';
11
+ import type { OctaneNode } from 'octane';
12
+ import { universalComponent } from 'octane/universal';
13
+ import {
14
+ CubeCamera,
15
+ type CubeTexture,
16
+ type Euler,
17
+ HalfFloatType,
18
+ Scene,
19
+ type Texture,
20
+ WebGLCubeRenderTarget,
21
+ } from 'three';
22
+ import { GroundProjectedEnv as GroundProjectedEnvImpl } from 'three-stdlib';
23
+ import type { EnvironmentLoaderProps } from './environment-loader.js';
24
+ import { useEnvironment } from './useEnvironment.three.tsrx';
25
+
26
+ export type EnvironmentProps = {
27
+ children?: OctaneNode;
28
+ frames?: number;
29
+ near?: number;
30
+ far?: number;
31
+ resolution?: number;
32
+ background?: boolean | 'only';
33
+ blur?: number;
34
+ backgroundBlurriness?: number;
35
+ backgroundIntensity?: number;
36
+ backgroundRotation?: Euler;
37
+ environmentIntensity?: number;
38
+ environmentRotation?: Euler;
39
+ map?: Texture;
40
+ scene?: Scene | RefObject<Scene>;
41
+ ground?: boolean | { radius?: number; height?: number; scale?: number };
42
+ } & EnvironmentLoaderProps;
43
+
44
+ function isSceneRef(scene: Scene | RefObject<Scene>): scene is RefObject<Scene> {
45
+ return Boolean(scene && 'current' in scene && scene.current?.isScene);
46
+ }
47
+
48
+ function resolveScene(scene: Scene | RefObject<Scene>): Scene {
49
+ return isSceneRef(scene) ? scene.current! : scene;
50
+ }
51
+
52
+ function setEnvironmentProperties(
53
+ background: boolean | 'only',
54
+ scene: Scene | RefObject<Scene> | undefined,
55
+ defaultScene: Scene,
56
+ texture: Texture,
57
+ sceneProps: Partial<EnvironmentProps> = {},
58
+ ): () => void {
59
+ const nextProps = {
60
+ backgroundBlurriness: 0,
61
+ backgroundIntensity: 1,
62
+ backgroundRotation: [0, 0, 0],
63
+ environmentIntensity: 1,
64
+ environmentRotation: [0, 0, 0],
65
+ ...sceneProps,
66
+ };
67
+ const target = resolveScene(scene ?? defaultScene);
68
+ const oldBackground = target.background;
69
+ const oldEnvironment = target.environment;
70
+ const oldSceneProps = {
71
+ backgroundBlurriness: target.backgroundBlurriness,
72
+ backgroundIntensity: target.backgroundIntensity,
73
+ backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
74
+ environmentIntensity: target.environmentIntensity,
75
+ environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0],
76
+ };
77
+ if (background !== 'only') target.environment = texture;
78
+ if (background) target.background = texture;
79
+ applyProps(target as any, nextProps as any);
80
+ return () => {
81
+ if (background !== 'only') target.environment = oldEnvironment;
82
+ if (background) target.background = oldBackground;
83
+ applyProps(target as any, oldSceneProps as any);
84
+ };
85
+ }
86
+
87
+ export function EnvironmentMap({
88
+ scene,
89
+ background = false,
90
+ map,
91
+ ...config
92
+ }: EnvironmentProps) @{
93
+ const defaultScene = useThree((state) => state.scene);
94
+ useLayoutEffect(() => {
95
+ if (map) return setEnvironmentProperties(background, scene, defaultScene, map, config);
96
+ });
97
+ null
98
+ }
99
+
100
+ export function EnvironmentCube({
101
+ background = false,
102
+ scene,
103
+ blur,
104
+ backgroundBlurriness,
105
+ backgroundIntensity,
106
+ backgroundRotation,
107
+ environmentIntensity,
108
+ environmentRotation,
109
+ ...loaderOptions
110
+ }: EnvironmentProps) @{
111
+ const texture = useEnvironment(loaderOptions);
112
+ const defaultScene = useThree((state) => state.scene);
113
+ useLayoutEffect(() => setEnvironmentProperties(background, scene, defaultScene, texture, {
114
+ backgroundBlurriness: blur ?? backgroundBlurriness,
115
+ backgroundIntensity,
116
+ backgroundRotation,
117
+ environmentIntensity,
118
+ environmentRotation,
119
+ }));
120
+ useEffect(() => () => texture.dispose(), [texture]);
121
+ null
122
+ }
123
+
124
+ export function EnvironmentPortal({
125
+ children,
126
+ near = 0.1,
127
+ far = 1000,
128
+ resolution = 256,
129
+ frames = 1,
130
+ map,
131
+ background = false,
132
+ blur,
133
+ backgroundBlurriness,
134
+ backgroundIntensity,
135
+ backgroundRotation,
136
+ environmentIntensity,
137
+ environmentRotation,
138
+ scene,
139
+ files,
140
+ path,
141
+ preset,
142
+ extensions,
143
+ }: EnvironmentProps) @{
144
+ const gl = useThree((state) => state.gl);
145
+ const defaultScene = useThree((state) => state.scene);
146
+ const camera = useRef<CubeCamera | null>(null);
147
+ const [virtualScene] = useState(() => new Scene());
148
+ const target = useMemo(() => {
149
+ const value = new WebGLCubeRenderTarget(resolution);
150
+ value.texture.type = HalfFloatType;
151
+ return value;
152
+ }, [resolution]);
153
+ useEffect(() => () => target.dispose(), [target]);
154
+ useLayoutEffect(() => {
155
+ if (frames === 1 && camera.current) {
156
+ const autoClear = gl.autoClear;
157
+ gl.autoClear = true;
158
+ camera.current.update(gl as any, virtualScene);
159
+ gl.autoClear = autoClear;
160
+ }
161
+ return setEnvironmentProperties(background, scene, defaultScene, target.texture, {
162
+ backgroundBlurriness: blur ?? backgroundBlurriness,
163
+ backgroundIntensity,
164
+ backgroundRotation,
165
+ environmentIntensity,
166
+ environmentRotation,
167
+ });
168
+ }, [children, virtualScene, target.texture, scene, defaultScene, background, frames, gl]);
169
+ let count = 1;
170
+ useFrame(() => {
171
+ if ((frames === Infinity || count < frames) && camera.current) {
172
+ const autoClear = gl.autoClear;
173
+ gl.autoClear = true;
174
+ camera.current.update(gl as any, virtualScene);
175
+ gl.autoClear = autoClear;
176
+ count++;
177
+ }
178
+ });
179
+ <>
180
+ {createPortal(
181
+ <>
182
+ {children}
183
+ <cubeCamera ref={camera} args={[near, far, target]} />
184
+ @if (files || preset) {
185
+ <EnvironmentCube background={true} files={files} preset={preset} path={path} extensions={extensions} />
186
+ } @else if (map) {
187
+ <EnvironmentMap background={true} map={map} extensions={extensions} />
188
+ }
189
+ </>,
190
+ virtualScene,
191
+ )}
192
+ </>
193
+ }
194
+
195
+ const GroundProjectedEnvElement = extend(GroundProjectedEnvImpl);
196
+
197
+ function EnvironmentGround(props: EnvironmentProps) @{
198
+ const textureDefault = useEnvironment(props);
199
+ const texture = props.map ?? textureDefault;
200
+ useEffect(() => () => textureDefault.dispose(), [textureDefault]);
201
+ const args = useMemo<[CubeTexture | Texture]>(() => [texture], [texture]);
202
+ const height = typeof props.ground === 'object' ? props.ground.height : undefined;
203
+ const radius = typeof props.ground === 'object' ? props.ground.radius : undefined;
204
+ const scale = typeof props.ground === 'object' ? props.ground.scale ?? 1000 : 1000;
205
+ <>
206
+ <EnvironmentMap {...props} map={texture} />
207
+ <GroundProjectedEnvElement args={args as any} scale={scale} height={height} radius={radius} />
208
+ </>
209
+ }
210
+
211
+ export function Environment(props: EnvironmentProps) @{
212
+ const component = props.ground
213
+ ? EnvironmentGround
214
+ : props.map
215
+ ? EnvironmentMap
216
+ : props.children
217
+ ? EnvironmentPortal
218
+ : EnvironmentCube;
219
+ <>{universalComponent('three', component as any, props as unknown as Record<string, unknown>)}</>
220
+ }
@@ -0,0 +1,56 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, type ThreeRef } from '@octanejs/three';
3
+ import { useCallback, useImperativeHandle, useMemo, useState } from '@octanejs/three/renderer';
4
+ import type { OctaneNode } from 'octane';
5
+ import { MeshNormalMaterial, MeshStandardMaterial, type ColorRepresentation, type Group } from 'three';
6
+ import { Center } from './Center.three.tsrx';
7
+ import { Text3D } from './Text3D.three.tsrx';
8
+
9
+ export type ExampleProps = {
10
+ font: string;
11
+ color?: ColorRepresentation;
12
+ debug?: boolean;
13
+ bevelSize?: number;
14
+ children?: OctaneNode;
15
+ } & Omit<ThreeElements['group'], 'ref'>;
16
+
17
+ export type ExampleApi = {
18
+ incr: (x?: number) => void;
19
+ decr: (x?: number) => void;
20
+ };
21
+
22
+ export function Example({
23
+ font,
24
+ color = '#cbcbcb',
25
+ bevelSize = 0.04,
26
+ debug = false,
27
+ children,
28
+ ref,
29
+ ...props
30
+ }: ExampleProps & { ref?: ThreeRef<ExampleApi> }) @{
31
+ const [counter, setCounter] = useState(0);
32
+ const incr = useCallback((x = 1) => setCounter(counter + x), [counter]);
33
+ const decr = useCallback((x = 1) => setCounter(counter - x), [counter]);
34
+ const api = useMemo<ExampleApi>(() => ({ incr, decr }), [incr, decr]);
35
+ useImperativeHandle((ref ?? null) as any, () => api, [api]);
36
+ const material = useMemo(
37
+ () => debug
38
+ ? new MeshNormalMaterial({ wireframe: true })
39
+ : new MeshStandardMaterial({ color }),
40
+ [debug, color],
41
+ );
42
+ <group {...props}>
43
+ @try {
44
+ <Center top cacheKey={JSON.stringify({ counter, font })}>
45
+ <Text3D
46
+ bevelEnabled
47
+ bevelSize={bevelSize}
48
+ font={font}
49
+ material={material}
50
+ children={counter}
51
+ />
52
+ </Center>
53
+ } @pending {}
54
+ {children}
55
+ </group>
56
+ }
@@ -0,0 +1,70 @@
1
+ import { useThree } from '@octanejs/three';
2
+ import { useEffect, useLayoutEffect, useMemo } from '@octanejs/three/renderer';
3
+ import * as THREE from 'three';
4
+ import { slotDefault } from './compiler-slot.js';
5
+ import { resolveRenderChildren, type RenderChildren } from './octane-children.js';
6
+
7
+ type FBOSettings =
8
+ {
9
+ /** @deprecated use `depthBuffer` instead. */
10
+ depth?: boolean;
11
+ } & THREE.RenderTargetOptions;
12
+
13
+ export function useFBO(width?: number | FBOSettings, height?: number, settings?: FBOSettings) {
14
+ width = slotDefault(width, undefined);
15
+ height = slotDefault(height, undefined);
16
+ settings = slotDefault(settings, undefined);
17
+ const size = useThree((state) => state.size);
18
+ const viewport = useThree((state) => state.viewport);
19
+ const targetWidth =
20
+ typeof width === 'number' ? width : size.width * viewport.dpr;
21
+ const targetHeight =
22
+ typeof height === 'number' ? height : size.height * viewport.dpr;
23
+ const resolvedSettings = (typeof width === 'number' ? settings : width) || {};
24
+ const { samples = 0, depth, ...targetSettings } = resolvedSettings;
25
+ const depthBuffer = depth ?? resolvedSettings.depthBuffer;
26
+ const target = useMemo(() => {
27
+ const value = new THREE.WebGLRenderTarget(targetWidth, targetHeight, {
28
+ minFilter: THREE.LinearFilter,
29
+ magFilter: THREE.LinearFilter,
30
+ type: THREE.HalfFloatType,
31
+ ...targetSettings,
32
+ });
33
+ if (depthBuffer) {
34
+ value.depthTexture = new THREE.DepthTexture(targetWidth, targetHeight, THREE.FloatType);
35
+ }
36
+ value.samples = samples;
37
+ return value;
38
+ }, []);
39
+ useLayoutEffect(() => {
40
+ target.setSize(targetWidth, targetHeight);
41
+ if (samples) target.samples = samples;
42
+ }, [samples, target, targetWidth, targetHeight]);
43
+ useEffect(() => () => target.dispose(), []);
44
+ return target;
45
+ }
46
+
47
+ type UseFBOParams = Parameters<typeof useFBO>;
48
+ type FBO = ReturnType<typeof useFBO>;
49
+
50
+ export type FboProps =
51
+ {
52
+ children?: RenderChildren<FBO>;
53
+ width?: UseFBOParams[0];
54
+ height?: UseFBOParams[1];
55
+ } & FBOSettings;
56
+
57
+ export function Fbo({ children, width, height, ref, ...settings }: FboProps & {
58
+ ref?: { current: FBO | null } | ((value: FBO | null) => void);
59
+ }) @{
60
+ const target = useFBO(width, height, settings);
61
+ useLayoutEffect(() => {
62
+ if (typeof ref === 'function') ref(target);
63
+ else if (ref) ref.current = target;
64
+ return () => {
65
+ if (typeof ref === 'function') ref(null);
66
+ else if (ref) ref.current = null;
67
+ };
68
+ }, [ref, target]);
69
+ <>{resolveRenderChildren(children, target)}</>
70
+ }
@@ -0,0 +1,25 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { useLoader, type ThreeRef } from '@octanejs/three';
3
+ import type { Group } from 'three';
4
+ import { FBXLoader } from 'three-stdlib';
5
+ import { Clone, type CloneProps } from './Clone.three.tsrx';
6
+
7
+ export const useFBX = Object.assign(
8
+ (path: string): Group => useLoader(FBXLoader, path) as unknown as Group,
9
+ {
10
+ preload(path: string) {
11
+ useLoader.preload(FBXLoader, path);
12
+ },
13
+ clear(input: string | string[]) {
14
+ useLoader.clear(FBXLoader, input);
15
+ },
16
+ },
17
+ );
18
+
19
+ export function Fbx({ path, ref, ...props }: { path: string; ref?: ThreeRef<Group> } & Omit<
20
+ CloneProps,
21
+ 'object'
22
+ >) @{
23
+ const fbx = useFBX(path);
24
+ <Clone ref={ref} {...props} object={fbx.children[0]} />
25
+ }
@@ -0,0 +1,82 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ComputeFunction, type ThreeElements, useFrame, useThree } from '@octanejs/three';
3
+ import { useCallback, useLayoutEffect, useRef, useState, type RefObject } from '@octanejs/three/renderer';
4
+ import type { OctaneNode } from 'octane';
5
+ import * as THREE from 'three';
6
+ import { RenderCubeTexture, type RenderCubeTextureApi } from './RenderCubeTexture.three.tsrx';
7
+
8
+ export type FisheyeProps = ThreeElements['mesh'] & {
9
+ zoom?: number;
10
+ segments?: number;
11
+ resolution?: number;
12
+ children: OctaneNode;
13
+ renderPriority?: number;
14
+ };
15
+
16
+ function UpdateCubeCamera({ api }: { api: RefObject<RenderCubeTextureApi> }) @{
17
+ const position = new THREE.Vector3();
18
+ const rotation = new THREE.Quaternion();
19
+ const scale = new THREE.Vector3();
20
+ const flip = new THREE.Euler(0, Math.PI, 0);
21
+ useFrame((state) => {
22
+ state.camera.matrixWorld.decompose(position, rotation, scale);
23
+ api.current?.camera.position.copy(position);
24
+ api.current?.camera.quaternion.setFromEuler(flip).premultiply(rotation);
25
+ });
26
+ null
27
+ }
28
+
29
+ export function Fisheye({
30
+ renderPriority = 1,
31
+ zoom = 0,
32
+ segments = 64,
33
+ children,
34
+ resolution = 896,
35
+ ...props
36
+ }: FisheyeProps) @{
37
+ const sphere = useRef<THREE.Mesh | null>(null);
38
+ const cubeApi = useRef<RenderCubeTextureApi | null>(null);
39
+ const { width, height } = useThree((state) => state.size);
40
+ const [camera] = useState(() => new THREE.OrthographicCamera());
41
+ useLayoutEffect(() => {
42
+ camera.position.set(0, 0, 100);
43
+ camera.zoom = 100;
44
+ camera.left = width / -2;
45
+ camera.right = width / 2;
46
+ camera.top = height / 2;
47
+ camera.bottom = height / -2;
48
+ camera.updateProjectionMatrix();
49
+ }, [width, height]);
50
+ const radius = (Math.sqrt(width * width + height * height) / 100) * (0.5 + zoom / 2);
51
+ const normal = new THREE.Vector3();
52
+ const sphereBounds = new THREE.Sphere(new THREE.Vector3(), radius);
53
+ const normalMatrix = new THREE.Matrix3();
54
+ const compute = useCallback<ComputeFunction>((event, state) => {
55
+ state.pointer.set(
56
+ ((event as any).offsetX / state.size.width) * 2 - 1,
57
+ -((event as any).offsetY / state.size.height) * 2 + 1,
58
+ );
59
+ state.raycaster.setFromCamera(state.pointer, camera);
60
+ if (!state.raycaster.ray.intersectSphere(sphereBounds, normal)) return;
61
+ normal.normalize();
62
+ const cubeCamera = cubeApi.current?.camera;
63
+ if (!cubeCamera) return;
64
+ normalMatrix.getNormalMatrix(cubeCamera.matrixWorld);
65
+ cubeCamera.getWorldPosition(state.raycaster.ray.origin);
66
+ state.raycaster.ray.direction.set(0, 0, 1).reflect(normal);
67
+ state.raycaster.ray.direction.x *= -1;
68
+ state.raycaster.ray.direction.applyNormalMatrix(normalMatrix).multiplyScalar(-1);
69
+ }, []);
70
+ useFrame((state) => {
71
+ if (renderPriority && sphere.current) state.gl.render(sphere.current as any, camera);
72
+ }, renderPriority);
73
+ <mesh ref={sphere} {...props} scale={radius}>
74
+ <sphereGeometry args={[1, segments, segments]} />
75
+ <meshBasicMaterial>
76
+ <RenderCubeTexture compute={compute} attach="envMap" flip resolution={resolution} ref={cubeApi}>
77
+ {children}
78
+ <UpdateCubeCamera api={cubeApi as RefObject<RenderCubeTextureApi>} />
79
+ </RenderCubeTexture>
80
+ </meshBasicMaterial>
81
+ </mesh>
82
+ }