@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,40 @@
1
+ import { useThree } from '@octanejs/three';
2
+ import { useLayoutEffect } from '@octanejs/three/renderer';
3
+ import {
4
+ Camera,
5
+ CubeCamera,
6
+ Object3D,
7
+ Scene,
8
+ type WebGLRenderer,
9
+ WebGLCubeRenderTarget,
10
+ } from 'three';
11
+
12
+ export type PreloadProps = {
13
+ all?: boolean;
14
+ scene?: Object3D;
15
+ camera?: Camera;
16
+ };
17
+
18
+ export function Preload({ all, scene, camera }: PreloadProps) @{
19
+ const renderer = useThree(({ gl }) => gl);
20
+ const defaultCamera = useThree(({ camera: value }) => value);
21
+ const defaultScene = useThree(({ scene: value }) => value);
22
+ useLayoutEffect(() => {
23
+ const invisible: Object3D[] = [];
24
+ if (all) {
25
+ (scene || defaultScene).traverse((object) => {
26
+ if (object.visible === false) {
27
+ invisible.push(object);
28
+ object.visible = true;
29
+ }
30
+ });
31
+ }
32
+ renderer.compile(scene || defaultScene, camera || defaultCamera);
33
+ const target = new WebGLCubeRenderTarget(128);
34
+ const cubeCamera = new CubeCamera(0.01, 100000, target);
35
+ cubeCamera.update(renderer as unknown as WebGLRenderer, (scene || defaultScene) as Scene);
36
+ target.dispose();
37
+ invisible.forEach((object) => (object.visible = false));
38
+ }, []);
39
+ null;
40
+ }
@@ -0,0 +1,47 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { create } from '@octanejs/zustand/universal';
3
+ import { DefaultLoadingManager } from 'three';
4
+ import { resolveRenderChildren, type RenderChildren } from './octane-children.js';
5
+
6
+ interface Data {
7
+ errors: string[];
8
+ active: boolean;
9
+ progress: number;
10
+ item: string;
11
+ loaded: number;
12
+ total: number;
13
+ }
14
+
15
+ let saveLastTotalLoaded = 0;
16
+
17
+ export const useProgress = /* @__PURE__ */ create<Data>((set) => {
18
+ DefaultLoadingManager.onStart = (item, loaded, total) => {
19
+ set({
20
+ active: true,
21
+ item,
22
+ loaded,
23
+ total,
24
+ progress: ((loaded - saveLastTotalLoaded) / (total - saveLastTotalLoaded)) * 100,
25
+ });
26
+ };
27
+ DefaultLoadingManager.onLoad = () => set({ active: false });
28
+ DefaultLoadingManager.onError = (item) => set((state) => ({ errors: [...state.errors, item] }));
29
+ DefaultLoadingManager.onProgress = (item, loaded, total) => {
30
+ if (loaded === total) saveLastTotalLoaded = total;
31
+ set({
32
+ active: true,
33
+ item,
34
+ loaded,
35
+ total,
36
+ progress: ((loaded - saveLastTotalLoaded) / (total - saveLastTotalLoaded)) * 100 || 100,
37
+ });
38
+ };
39
+ return { errors: [], active: false, progress: 0, item: '', loaded: 0, total: 0 };
40
+ });
41
+
42
+ export function Progress({ children }: { children?: RenderChildren<Data> }) @{
43
+ const result = useProgress();
44
+ <>
45
+ {resolveRenderChildren(children, result)}
46
+ </>
47
+ }
@@ -0,0 +1,111 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { createPortal, type ComputeFunction, type ThreeElements, type ThreeRef, useFrame, useThree } from '@octanejs/three';
3
+ import { useEffect, useImperativeHandle, useMemo, useRef, useState, type RefObject } from '@octanejs/three/renderer';
4
+ import type { OctaneNode } from 'octane';
5
+ import * as THREE from 'three';
6
+
7
+ export type RenderCubeTextureProps = Omit<ThreeElements['texture'], 'ref' | 'args' | 'rotation'> & {
8
+ stencilBuffer?: boolean;
9
+ depthBuffer?: boolean;
10
+ generateMipmaps?: boolean;
11
+ renderPriority?: number;
12
+ eventPriority?: number;
13
+ frames?: number;
14
+ compute?: ComputeFunction;
15
+ flip?: boolean;
16
+ resolution?: number;
17
+ children: OctaneNode;
18
+ near?: number;
19
+ far?: number;
20
+ position?: ThreeElements['cubeCamera']['position'];
21
+ rotation?: ThreeElements['cubeCamera']['rotation'];
22
+ scale?: ThreeElements['cubeCamera']['scale'];
23
+ quaternion?: ThreeElements['cubeCamera']['quaternion'];
24
+ matrix?: ThreeElements['cubeCamera']['matrix'];
25
+ matrixAutoUpdate?: boolean;
26
+ };
27
+
28
+ type RenderCubeTextureComponentProps = Omit<RenderCubeTextureProps, 'children'> & { children?: OctaneNode };
29
+
30
+ export type RenderCubeTextureApi = {
31
+ scene: THREE.Scene;
32
+ fbo: THREE.WebGLCubeRenderTarget;
33
+ camera: THREE.CubeCamera;
34
+ };
35
+
36
+ function CubeContainer({ frames, renderPriority, children, camera }: {
37
+ frames: number;
38
+ renderPriority: number;
39
+ children?: OctaneNode;
40
+ camera: RefObject<THREE.CubeCamera>;
41
+ }) @{
42
+ let count = 0;
43
+ useFrame((state) => {
44
+ if (frames === Infinity || count < frames) {
45
+ camera.current?.update(state.gl as any, state.scene);
46
+ count++;
47
+ }
48
+ }, renderPriority);
49
+ <>{children}</>
50
+ }
51
+
52
+ export function RenderCubeTexture({
53
+ children,
54
+ compute,
55
+ renderPriority = -1,
56
+ eventPriority = 0,
57
+ frames = Infinity,
58
+ stencilBuffer = false,
59
+ depthBuffer = true,
60
+ generateMipmaps = false,
61
+ resolution = 896,
62
+ near = 0.1,
63
+ far = 1000,
64
+ flip = false,
65
+ position,
66
+ rotation,
67
+ scale,
68
+ quaternion,
69
+ matrix,
70
+ matrixAutoUpdate,
71
+ ref,
72
+ ...props
73
+ }: RenderCubeTextureComponentProps & { ref?: ThreeRef<RenderCubeTextureApi> }) @{
74
+ const { size, viewport } = useThree();
75
+ const camera = useRef<THREE.CubeCamera | null>(null);
76
+ const fbo = useMemo(() => {
77
+ const target = new THREE.WebGLCubeRenderTarget(
78
+ Math.max((resolution || size.width) * viewport.dpr, (resolution || size.height) * viewport.dpr),
79
+ { stencilBuffer, depthBuffer, generateMipmaps },
80
+ );
81
+ target.texture.isRenderTargetTexture = !flip;
82
+ target.texture.flipY = true;
83
+ target.texture.type = THREE.HalfFloatType;
84
+ return target;
85
+ }, [resolution, flip]);
86
+ useEffect(() => () => fbo.dispose(), [fbo]);
87
+ const [virtualScene] = useState(() => new THREE.Scene());
88
+ const primitiveProps = props as any;
89
+ useImperativeHandle((ref ?? null) as any, () => ({ scene: virtualScene, fbo, camera: camera.current! }), [fbo]);
90
+ <>
91
+ {createPortal(
92
+ <CubeContainer renderPriority={renderPriority} frames={frames} camera={camera as RefObject<THREE.CubeCamera>}>
93
+ {children}
94
+ <group onPointerOver={() => null} />
95
+ </CubeContainer>,
96
+ virtualScene,
97
+ { events: { compute, priority: eventPriority } },
98
+ )}
99
+ <primitive object={fbo.texture} {...primitiveProps} />
100
+ <cubeCamera
101
+ ref={camera}
102
+ args={[near, far, fbo]}
103
+ position={position}
104
+ rotation={rotation}
105
+ scale={scale}
106
+ quaternion={quaternion}
107
+ matrix={matrix}
108
+ matrixAutoUpdate={matrixAutoUpdate}
109
+ />
110
+ </>
111
+ }
@@ -0,0 +1,102 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { createPortal, type ComputeFunction, type ThreeElements, type ThreeRef, useFrame, useThree } from '@octanejs/three';
3
+ import { useCallback, useImperativeHandle, useState } from '@octanejs/three/renderer';
4
+ import type { OctaneNode } from 'octane';
5
+ import * as THREE from 'three';
6
+ import { useFBO } from './Fbo.three.tsrx';
7
+
8
+ export type RenderTextureProps = Omit<ThreeElements['texture'], 'ref' | 'args'> & {
9
+ width?: number;
10
+ height?: number;
11
+ samples?: number;
12
+ stencilBuffer?: boolean;
13
+ depthBuffer?: boolean;
14
+ generateMipmaps?: boolean;
15
+ renderPriority?: number;
16
+ eventPriority?: number;
17
+ frames?: number;
18
+ compute?: ComputeFunction;
19
+ children: OctaneNode;
20
+ };
21
+
22
+ type RenderTextureComponentProps = Omit<RenderTextureProps, 'children'> & { children?: OctaneNode };
23
+
24
+ function Container({ frames, renderPriority, children, fbo }: {
25
+ frames: number;
26
+ renderPriority: number;
27
+ children?: OctaneNode;
28
+ fbo: THREE.WebGLRenderTarget;
29
+ }) @{
30
+ let count = 0;
31
+ useFrame((state) => {
32
+ if (frames !== Infinity && count >= frames) return;
33
+ const gl = state.gl as any;
34
+ const oldAutoClear = gl.autoClear;
35
+ const oldXrEnabled = gl.xr.enabled;
36
+ const oldRenderTarget = gl.getRenderTarget();
37
+ const oldIsPresenting = gl.xr.isPresenting;
38
+ gl.autoClear = true;
39
+ gl.xr.enabled = false;
40
+ gl.xr.isPresenting = false;
41
+ gl.setRenderTarget(fbo);
42
+ gl.render(state.scene, state.camera);
43
+ gl.setRenderTarget(oldRenderTarget);
44
+ gl.autoClear = oldAutoClear;
45
+ gl.xr.enabled = oldXrEnabled;
46
+ gl.xr.isPresenting = oldIsPresenting;
47
+ count++;
48
+ }, renderPriority);
49
+ <>{children}</>
50
+ }
51
+
52
+ export function RenderTexture({
53
+ children,
54
+ compute,
55
+ width,
56
+ height,
57
+ samples = 8,
58
+ renderPriority = 0,
59
+ eventPriority = 0,
60
+ frames = Infinity,
61
+ stencilBuffer = false,
62
+ depthBuffer = true,
63
+ generateMipmaps = false,
64
+ ref,
65
+ ...props
66
+ }: RenderTextureComponentProps & { ref?: ThreeRef<THREE.Texture> }) @{
67
+ const { size, viewport } = useThree();
68
+ const fbo = useFBO((width || size.width) * viewport.dpr, (height || size.height) * viewport.dpr, {
69
+ samples,
70
+ stencilBuffer,
71
+ depthBuffer,
72
+ generateMipmaps,
73
+ });
74
+ const [virtualScene] = useState(() => new THREE.Scene());
75
+ const primitiveProps = props as any;
76
+ const uvCompute = useCallback<ComputeFunction>((event, state, previous) => {
77
+ let parent = (fbo.texture as any)?.__r3f?.parent?.object ?? (fbo.texture as any)?.__octane?.parent?.object;
78
+ while (parent && !(parent instanceof THREE.Object3D)) {
79
+ parent = parent.__r3f?.parent?.object ?? parent.__octane?.parent?.object;
80
+ }
81
+ if (!parent || !previous) return false as any;
82
+ if (!previous.raycaster.camera) previous.events.compute?.(event, previous, previous.previousRoot?.getState());
83
+ const [intersection] = previous.raycaster.intersectObject(parent);
84
+ if (!intersection?.uv) return false as any;
85
+ state.raycaster.setFromCamera(
86
+ state.pointer.set(intersection.uv.x * 2 - 1, intersection.uv.y * 2 - 1),
87
+ state.camera,
88
+ );
89
+ }, []);
90
+ useImperativeHandle((ref ?? null) as any, () => fbo.texture, [fbo]);
91
+ <>
92
+ {createPortal(
93
+ <Container renderPriority={renderPriority} frames={frames} fbo={fbo}>
94
+ {children}
95
+ <group onPointerOver={() => null} />
96
+ </Container>,
97
+ virtualScene,
98
+ { events: { compute: compute ?? uvCompute, priority: eventPriority } },
99
+ )}
100
+ <primitive object={fbo.texture} {...primitiveProps} />
101
+ </>
102
+ }
@@ -0,0 +1,59 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, type ThreeRef } from '@octanejs/three';
3
+ import { useLayoutEffect, useMemo, useRef } from '@octanejs/three/renderer';
4
+ import * as THREE from 'three';
5
+
6
+ function assignRef<T>(ref: ThreeRef<T> | undefined, value: T | null): void {
7
+ if (Array.isArray(ref)) for (const item of ref) assignRef(item, value);
8
+ else if (typeof ref === 'function') ref(value);
9
+ else if (ref && 'current' in ref) ref.current = value;
10
+ }
11
+
12
+ export type ResizeProps =
13
+ Omit<ThreeElements['group'], 'ref'> & {
14
+ width?: boolean;
15
+ height?: boolean;
16
+ depth?: boolean;
17
+ box3?: THREE.Box3;
18
+ precise?: boolean;
19
+ };
20
+
21
+ export function Resize({
22
+ children,
23
+ width,
24
+ height,
25
+ depth,
26
+ box3,
27
+ precise = true,
28
+ ref,
29
+ ...props
30
+ }: ResizeProps & { ref?: ThreeRef<THREE.Group> }) @{
31
+ const container = useRef<THREE.Group | null>(null);
32
+ const outer = useRef<THREE.Group | null>(null);
33
+ const inner = useRef<THREE.Group | null>(null);
34
+ const mergedRef = useMemo(
35
+ () => (value: THREE.Group | null) => {
36
+ container.current = value;
37
+ assignRef(ref, value);
38
+ },
39
+ [ref],
40
+ );
41
+ useLayoutEffect(() => {
42
+ if (!outer.current || !inner.current) return;
43
+ outer.current.matrixWorld.identity();
44
+ const box = box3 || new THREE.Box3().setFromObject(inner.current, precise);
45
+ const w = box.max.x - box.min.x;
46
+ const h = box.max.y - box.min.y;
47
+ const d = box.max.z - box.min.z;
48
+ let dimension = Math.max(w, h, d);
49
+ if (width) dimension = w;
50
+ if (height) dimension = h;
51
+ if (depth) dimension = d;
52
+ outer.current.scale.setScalar(1 / dimension);
53
+ }, [width, height, depth, box3, precise]);
54
+ <group ref={mergedRef} {...props}>
55
+ <group ref={outer}>
56
+ <group ref={inner}>{children}</group>
57
+ </group>
58
+ </group>
59
+ }
@@ -0,0 +1,106 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import type { ThreeElements, ThreeRef } from '@octanejs/three';
3
+ import { useLayoutEffect, useMemo, useRef } from '@octanejs/three/renderer';
4
+ import { type ExtrudeGeometry, type Mesh, Shape } from 'three';
5
+ import { toCreasedNormals } from 'three-stdlib';
6
+
7
+ const eps = 0.00001;
8
+
9
+ function createShape(width: number, height: number, radius0: number): Shape {
10
+ const shape = new Shape();
11
+ const radius = radius0 - eps;
12
+ shape.absarc(eps, eps, eps, -Math.PI / 2, -Math.PI, true);
13
+ shape.absarc(eps, height - radius * 2, eps, Math.PI, Math.PI / 2, true);
14
+ shape.absarc(width - radius * 2, height - radius * 2, eps, Math.PI / 2, 0, true);
15
+ shape.absarc(width - radius * 2, eps, eps, 0, -Math.PI / 2, true);
16
+ return shape;
17
+ }
18
+
19
+ export type RoundedBoxProps =
20
+ {
21
+ args?: [width?: number, height?: number, depth?: number];
22
+ radius?: number;
23
+ smoothness?: number;
24
+ bevelSegments?: number;
25
+ steps?: number;
26
+ creaseAngle?: number;
27
+ } & Omit<ThreeElements['mesh'], 'ref' | 'args'>;
28
+
29
+ export type RoundedBoxGeometryProps =
30
+ Omit<RoundedBoxProps, 'children'> & Omit<ThreeElements['extrudeGeometry'], 'args' | 'ref'>;
31
+
32
+ export function RoundedBox({
33
+ args: [width = 1, height = 1, depth = 1] = [],
34
+ radius = 0.05,
35
+ steps = 1,
36
+ smoothness = 4,
37
+ bevelSegments = 4,
38
+ creaseAngle = 0.4,
39
+ children,
40
+ ref,
41
+ ...rest
42
+ }: RoundedBoxProps & { ref?: ThreeRef<Mesh> }) @{
43
+ const meshRef = useMemo(() => (value: Mesh | null) => assignThreeRef(ref, value), [ref]);
44
+
45
+ <mesh ref={meshRef} {...rest}>
46
+ <RoundedBoxGeometry
47
+ args={[width, height, depth]}
48
+ radius={radius}
49
+ steps={steps}
50
+ smoothness={smoothness}
51
+ bevelSegments={bevelSegments}
52
+ creaseAngle={creaseAngle}
53
+ />
54
+ {children}
55
+ </mesh>
56
+ }
57
+
58
+ function assignThreeRef<T>(ref: ThreeRef<T> | undefined, value: T | null): void {
59
+ if (Array.isArray(ref)) {
60
+ for (const item of ref) assignThreeRef(item, value);
61
+ } else if (typeof ref === 'function') {
62
+ ref(value);
63
+ } else if (ref !== undefined && 'current' in ref) {
64
+ ref.current = value;
65
+ }
66
+ }
67
+
68
+ export function RoundedBoxGeometry({
69
+ args: [width = 1, height = 1, depth = 1] = [],
70
+ radius = 0.05,
71
+ steps = 1,
72
+ smoothness = 4,
73
+ bevelSegments = 4,
74
+ creaseAngle = 0.4,
75
+ ref,
76
+ ...rest
77
+ }: RoundedBoxGeometryProps & { ref?: ThreeRef<ExtrudeGeometry> }) @{
78
+ const shape = useMemo(() => createShape(width, height, radius), [width, height, radius]);
79
+ const params = useMemo(
80
+ () => ({
81
+ depth: depth - radius * 2,
82
+ bevelEnabled: true,
83
+ bevelSegments: bevelSegments * 2,
84
+ steps,
85
+ bevelSize: radius - eps,
86
+ bevelThickness: radius,
87
+ curveSegments: smoothness,
88
+ }),
89
+ [depth, radius, smoothness, bevelSegments, steps],
90
+ );
91
+ const geometryRef = useRef<ExtrudeGeometry | null>(null);
92
+
93
+ useLayoutEffect(() => {
94
+ if (geometryRef.current !== null) {
95
+ geometryRef.current.center();
96
+ // three-mesh-bvh augments the workspace BufferGeometry type from its
97
+ // peer instance; both libraries operate on the same Three.js object.
98
+ toCreasedNormals(
99
+ geometryRef.current as unknown as Parameters<typeof toCreasedNormals>[0],
100
+ creaseAngle,
101
+ );
102
+ }
103
+ }, [shape, params, creaseAngle]);
104
+
105
+ <extrudeGeometry ref={[geometryRef, ref ?? null]} args={[shape, params]} {...rest} />
106
+ }
@@ -0,0 +1,116 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements } from '@octanejs/three';
3
+ import { useLayoutEffect, useRef, useState, type RefObject } from '@octanejs/three/renderer';
4
+ import type { OctaneNode } from 'octane';
5
+ import {
6
+ Color,
7
+ Group,
8
+ InstancedBufferAttribute,
9
+ InstancedMesh,
10
+ Mesh,
11
+ Object3D,
12
+ Vector3,
13
+ } from 'three';
14
+ import { MeshSurfaceSampler } from 'three-stdlib';
15
+ import { slotDefault } from './compiler-slot.js';
16
+
17
+ type SamplePayload = {
18
+ position: Vector3;
19
+ normal: Vector3;
20
+ color: Color;
21
+ };
22
+
23
+ type TransformPayload = SamplePayload & {
24
+ dummy: Object3D;
25
+ sampledMesh: Mesh;
26
+ };
27
+
28
+ export type TransformFn = (payload: TransformPayload, index: number) => void;
29
+
30
+ export type SamplerProps = Omit<ThreeElements['group'], 'ref'> & {
31
+ mesh?: RefObject<Mesh>;
32
+ instances?: RefObject<InstancedMesh>;
33
+ weight?: string;
34
+ transform?: TransformFn;
35
+ count?: number;
36
+ children?: OctaneNode;
37
+ };
38
+
39
+ export interface useSurfaceSamplerProps {
40
+ transform?: TransformFn;
41
+ weight?: string;
42
+ count?: number;
43
+ }
44
+
45
+ export function useSurfaceSampler(
46
+ mesh: RefObject<Mesh>,
47
+ count = 16,
48
+ transform?: TransformFn,
49
+ weight?: string,
50
+ instanceMesh?: RefObject<InstancedMesh> | null,
51
+ ): InstancedBufferAttribute {
52
+ count = slotDefault(count, 16);
53
+ transform = slotDefault(transform, undefined);
54
+ weight = slotDefault(weight, undefined);
55
+ instanceMesh = slotDefault(instanceMesh, undefined);
56
+ const [buffer, setBuffer] = useState(() => {
57
+ const values = Array.from(
58
+ { length: count },
59
+ () => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
60
+ ).flat();
61
+ return new InstancedBufferAttribute(Float32Array.from(values), 16);
62
+ });
63
+
64
+ useLayoutEffect(() => {
65
+ if (!mesh.current) return;
66
+ // three-stdlib currently resolves a newer @types/three copy; runtime instances are compatible.
67
+ const sampler = new MeshSurfaceSampler(mesh.current as any);
68
+ if (weight) sampler.setWeightAttribute(weight);
69
+ sampler.build();
70
+ const position = new Vector3();
71
+ const normal = new Vector3();
72
+ const color = new Color();
73
+ const dummy = new Object3D();
74
+ mesh.current.updateMatrixWorld(true);
75
+ for (let index = 0; index < count; index++) {
76
+ sampler.sample(position as any, normal as any, color as any);
77
+ if (transform) {
78
+ transform({ dummy, sampledMesh: mesh.current, position, normal, color }, index);
79
+ } else {
80
+ dummy.position.copy(position);
81
+ }
82
+ dummy.updateMatrix();
83
+ instanceMesh?.current?.setMatrixAt(index, dummy.matrix);
84
+ dummy.matrix.toArray(buffer.array, index * 16);
85
+ }
86
+ if (instanceMesh?.current) instanceMesh.current.instanceMatrix.needsUpdate = true;
87
+ buffer.needsUpdate = true;
88
+ setBuffer(new InstancedBufferAttribute(buffer.array as Float32Array, buffer.itemSize).copy(buffer));
89
+ }, [mesh, instanceMesh, weight, count, transform]);
90
+
91
+ return buffer;
92
+ }
93
+
94
+ export function Sampler({
95
+ children,
96
+ weight,
97
+ transform,
98
+ instances,
99
+ mesh,
100
+ count = 16,
101
+ ...props
102
+ }: SamplerProps) @{
103
+ const group = useRef<Group | null>(null);
104
+ const instancedRef = useRef<InstancedMesh | null>(null);
105
+ const meshToSampleRef = useRef<Mesh | null>(null);
106
+ useLayoutEffect(() => {
107
+ instancedRef.current = instances?.current ?? group.current?.children.find(
108
+ (child) => Object.hasOwn(child, 'instanceMatrix'),
109
+ ) as InstancedMesh | null;
110
+ meshToSampleRef.current = mesh?.current ?? group.current?.children.find(
111
+ (child) => child.type === 'Mesh',
112
+ ) as Mesh | null;
113
+ }, [children, mesh?.current, instances?.current]);
114
+ useSurfaceSampler(meshToSampleRef, count, transform, weight, instancedRef);
115
+ <group ref={group} {...props}>{children}</group>
116
+ }
@@ -0,0 +1,43 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, type ThreeRef, useFrame } from '@octanejs/three';
3
+ import { useMemo, useRef } from '@octanejs/three/renderer';
4
+ import { Object3D, Vector3 } from 'three';
5
+ import { calculateScaleFactor } from './calculateScaleFactor.js';
6
+
7
+ const worldPosition = new Vector3();
8
+
9
+ export type ScreenSizerProps =
10
+ Omit<ThreeElements['object3D'], 'ref'> & {
11
+ scale?: number;
12
+ };
13
+
14
+ function assignRef<T>(ref: ThreeRef<T> | undefined, value: T | null): void {
15
+ if (Array.isArray(ref)) for (const item of ref) assignRef(item, value);
16
+ else if (typeof ref === 'function') ref(value);
17
+ else if (ref && 'current' in ref) ref.current = value;
18
+ }
19
+
20
+ export function ScreenSizer({ scale = 1, ref, ...props }: ScreenSizerProps & {
21
+ ref?: ThreeRef<Object3D>;
22
+ }) @{
23
+ const container = useRef<Object3D | null>(null);
24
+ const mergedRef = useMemo(
25
+ () => (value: Object3D | null) => {
26
+ container.current = value;
27
+ assignRef(ref, value);
28
+ },
29
+ [ref],
30
+ );
31
+ useFrame((state) => {
32
+ const object = container.current;
33
+ if (!object) return;
34
+ const factor = calculateScaleFactor(
35
+ object.getWorldPosition(worldPosition),
36
+ scale,
37
+ state.camera,
38
+ state.size,
39
+ );
40
+ object.scale.setScalar(factor * scale);
41
+ });
42
+ <object3D ref={mergedRef} {...props} />
43
+ }
@@ -0,0 +1,21 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, type ThreeRef, useFrame } from '@octanejs/three';
3
+ import { useRef } from '@octanejs/three/renderer';
4
+ import type { Group } from 'three';
5
+
6
+ export type ScreenSpaceProps = Omit<ThreeElements['group'], 'ref'> & { depth?: number };
7
+
8
+ export function ScreenSpace({ children, depth = -1, ref, ...rest }: ScreenSpaceProps & {
9
+ ref?: ThreeRef<Group>;
10
+ }) @{
11
+ const localRef = useRef<Group | null>(null);
12
+ useFrame(({ camera }) => {
13
+ if (localRef.current) {
14
+ localRef.current.quaternion.copy(camera.quaternion);
15
+ localRef.current.position.copy(camera.position);
16
+ }
17
+ });
18
+ <group ref={[localRef, ref ?? null]} {...rest}>
19
+ <group position={[0, 0, -depth]}>{children}</group>
20
+ </group>
21
+ }