@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,329 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import {
3
+ type ThreeElement,
4
+ type ThreeElements,
5
+ type ThreeRef,
6
+ type Vector2 as FiberVector2,
7
+ type Vector3 as FiberVector3,
8
+ useThree,
9
+ } from '@octanejs/three';
10
+ import {
11
+ useCallback,
12
+ useEffect,
13
+ useLayoutEffect,
14
+ useMemo,
15
+ useRef,
16
+ useState,
17
+ } from '@octanejs/three/renderer';
18
+ import {
19
+ CatmullRomCurve3,
20
+ Color,
21
+ type ColorRepresentation,
22
+ CubicBezierCurve3,
23
+ EdgesGeometry,
24
+ type BufferAttribute,
25
+ type BufferGeometry,
26
+ type Mesh,
27
+ QuadraticBezierCurve3,
28
+ Vector2,
29
+ Vector3,
30
+ Vector4,
31
+ } from 'three';
32
+ import {
33
+ Line2,
34
+ LineGeometry,
35
+ LineMaterial,
36
+ type LineMaterialParameters,
37
+ LineSegments2,
38
+ LineSegmentsGeometry,
39
+ } from 'three-stdlib';
40
+
41
+ function assignRef<T>(ref: ThreeRef<T> | undefined, value: T | null): void {
42
+ if (Array.isArray(ref)) for (const item of ref) assignRef(item, value);
43
+ else if (typeof ref === 'function') ref(value);
44
+ else if (ref && 'current' in ref) ref.current = value;
45
+ }
46
+
47
+ export type LineProps =
48
+ Omit<
49
+ {
50
+ points: ReadonlyArray<FiberVector2 | FiberVector3>;
51
+ vertexColors?: ReadonlyArray<
52
+ Color | [number, number, number] | [number, number, number, number]
53
+ >;
54
+ lineWidth?: number;
55
+ segments?: boolean;
56
+ } & Omit<LineMaterialParameters, 'vertexColors' | 'color'> & Omit<
57
+ ThreeElement<typeof Line2>,
58
+ 'ref' | 'args'
59
+ > & Omit<ThreeElement<typeof LineMaterial>, 'ref' | 'color' | 'vertexColors' | 'args'> & {
60
+ color?: ColorRepresentation;
61
+ },
62
+ 'ref'
63
+ >;
64
+
65
+ export function Line({
66
+ points,
67
+ color = 0xffffff,
68
+ vertexColors,
69
+ linewidth,
70
+ lineWidth,
71
+ segments,
72
+ dashed,
73
+ ref,
74
+ ...rest
75
+ }: LineProps & { ref?: ThreeRef<Line2 | LineSegments2> }) @{
76
+ const size = useThree((state) => state.size);
77
+ const line = useMemo(() => (segments ? new LineSegments2() : new Line2()), [segments]);
78
+ const [material] = useState(() => new LineMaterial());
79
+ const itemSize =
80
+ (vertexColors?.[0] as number[] | undefined)?.length === 4 ? 4 : 3;
81
+ const geometry = useMemo(() => {
82
+ const value = segments ? new LineSegmentsGeometry() : new LineGeometry();
83
+ const positions = points.map((point) => {
84
+ const array = Array.isArray(point);
85
+ return point instanceof Vector3 || point instanceof Vector4
86
+ ? [point.x, point.y, point.z]
87
+ : point instanceof Vector2
88
+ ? [point.x, point.y, 0]
89
+ : array && point.length === 3
90
+ ? [point[0], point[1], point[2]]
91
+ : array && point.length === 2
92
+ ? [point[0], point[1], 0]
93
+ : point;
94
+ });
95
+ value.setPositions(positions.flat() as number[]);
96
+ if (vertexColors) {
97
+ color = 0xffffff;
98
+ const values = vertexColors.map((value) => value instanceof Color ? value.toArray() : value);
99
+ value.setColors(values.flat(), itemSize);
100
+ }
101
+ return value;
102
+ }, [points, segments, vertexColors, itemSize]);
103
+ const mergedRef = useMemo(() => (value: Line2 | LineSegments2 | null) => assignRef(ref, value), [
104
+ ref,
105
+ ]);
106
+ useLayoutEffect(() => {
107
+ line.computeLineDistances();
108
+ }, [points, line]);
109
+ useLayoutEffect(() => {
110
+ if (dashed) material.defines.USE_DASH = '';
111
+ else delete material.defines.USE_DASH;
112
+ material.needsUpdate = true;
113
+ }, [dashed, material]);
114
+ useEffect(
115
+ () => () => {
116
+ geometry.dispose();
117
+ material.dispose();
118
+ },
119
+ [geometry],
120
+ );
121
+ <primitive object={line} ref={mergedRef as any} {...rest as any}>
122
+ <primitive object={geometry} attach="geometry" />
123
+ <primitive
124
+ object={material}
125
+ attach="material"
126
+ color={color}
127
+ vertexColors={Boolean(vertexColors)}
128
+ resolution={[size.width, size.height]}
129
+ linewidth={linewidth ?? lineWidth ?? 1}
130
+ dashed={dashed}
131
+ transparent={itemSize === 4}
132
+ {...rest as any}
133
+ />
134
+ </primitive>
135
+ }
136
+
137
+ export type QuadraticBezierLineRef =
138
+ Line2 & {
139
+ setPoints(start: Vector3 | [number, number, number], end: | Vector3
140
+ | [number, number, number], mid: Vector3 | [number, number, number]): void;
141
+ };
142
+
143
+ export type QuadraticBezierLineProps =
144
+ Omit<LineProps, 'points' | 'ref' | 'segments'> & {
145
+ start: Vector3 | [number, number, number];
146
+ end: Vector3 | [number, number, number];
147
+ mid?: Vector3 | [number, number, number];
148
+ segments?: number;
149
+ };
150
+
151
+ const quadraticOffset = new Vector3();
152
+
153
+ export function QuadraticBezierLine({
154
+ start = [0, 0, 0],
155
+ end = [0, 0, 0],
156
+ mid,
157
+ segments = 20,
158
+ ref,
159
+ ...rest
160
+ }: QuadraticBezierLineProps & { ref?: ThreeRef<QuadraticBezierLineRef> }) @{
161
+ const localRef = useRef<QuadraticBezierLineRef | null>(null);
162
+ const [curve] = useState(
163
+ () => new QuadraticBezierCurve3(undefined as any, undefined as any, undefined as any),
164
+ );
165
+ const getPoints = useCallback((
166
+ from: Vector3 | [number, number, number],
167
+ to: Vector3 | [number, number, number],
168
+ middle?: Vector3 | [number, number, number],
169
+ count = 20,
170
+ ) => {
171
+ if (from instanceof Vector3) curve.v0.copy(from);
172
+ else curve.v0.set(...from);
173
+ if (to instanceof Vector3) curve.v2.copy(to);
174
+ else curve.v2.set(...to);
175
+ if (middle instanceof Vector3) curve.v1.copy(middle);
176
+ else if (Array.isArray(middle)) curve.v1.set(...middle);
177
+ else {
178
+ curve.v1.copy(
179
+ curve.v0.clone().add(curve.v2.clone().sub(curve.v0)).add(
180
+ quadraticOffset.set(0, curve.v0.y - curve.v2.y, 0),
181
+ ),
182
+ );
183
+ }
184
+ return curve.getPoints(count);
185
+ }, []);
186
+ useLayoutEffect(() => {
187
+ if (!localRef.current) return;
188
+ localRef.current.setPoints = (from, to, middle) => {
189
+ const values = getPoints(from, to, middle);
190
+ localRef.current?.geometry?.setPositions(values.flatMap((point) => point.toArray()));
191
+ };
192
+ });
193
+ const points = useMemo(() => getPoints(start, end, mid, segments), [start, end, mid, segments]);
194
+ const mergedRef = useMemo(
195
+ () => (value: QuadraticBezierLineRef | null) => {
196
+ localRef.current = value;
197
+ assignRef(ref, value);
198
+ },
199
+ [ref],
200
+ );
201
+ <Line ref={mergedRef as any} points={points} {...rest as any} />
202
+ }
203
+
204
+ export type CubicBezierLineProps =
205
+ Omit<LineProps, 'points' | 'ref' | 'segments'> & {
206
+ start: Vector3 | [number, number, number];
207
+ end: Vector3 | [number, number, number];
208
+ midA: Vector3 | [number, number, number];
209
+ midB: Vector3 | [number, number, number];
210
+ segments?: number;
211
+ };
212
+
213
+ export function CubicBezierLine({
214
+ start,
215
+ end,
216
+ midA,
217
+ midB,
218
+ segments = 20,
219
+ ref,
220
+ ...rest
221
+ }: CubicBezierLineProps & { ref?: ThreeRef<Line2> }) @{
222
+ const points = useMemo(() => {
223
+ const startVector =
224
+ start instanceof Vector3 ? start : new Vector3(...start);
225
+ const endVector =
226
+ end instanceof Vector3 ? end : new Vector3(...end);
227
+ const midAVector =
228
+ midA instanceof Vector3 ? midA : new Vector3(...midA);
229
+ const midBVector =
230
+ midB instanceof Vector3 ? midB : new Vector3(...midB);
231
+ return new CubicBezierCurve3(startVector, midAVector, midBVector, endVector).getPoints(
232
+ segments,
233
+ );
234
+ }, [start, end, midA, midB, segments]);
235
+ <Line ref={ref as any} points={points} {...rest as any} />
236
+ }
237
+
238
+ export type CatmullRomLineProps =
239
+ Omit<LineProps, 'ref' | 'segments'> & {
240
+ closed?: boolean;
241
+ curveType?: 'centripetal' | 'chordal' | 'catmullrom';
242
+ tension?: number;
243
+ segments?: number;
244
+ };
245
+
246
+ export function CatmullRomLine({
247
+ points,
248
+ closed = false,
249
+ curveType = 'centripetal',
250
+ tension = 0.5,
251
+ segments = 20,
252
+ vertexColors,
253
+ ref,
254
+ ...rest
255
+ }: CatmullRomLineProps & { ref?: ThreeRef<Line2> }) @{
256
+ const curve = useMemo(() => {
257
+ const mapped = points.map(
258
+ (point) => point instanceof Vector3
259
+ ? point
260
+ : new Vector3(...point as [number, number, number]),
261
+ );
262
+ return new CatmullRomCurve3(mapped, closed, curveType, tension);
263
+ }, [points, closed, curveType, tension]);
264
+ const segmentedPoints = useMemo(() => curve.getPoints(segments), [curve, segments]);
265
+ const interpolatedVertexColors = useMemo(() => {
266
+ if (!vertexColors || vertexColors.length < 2) return undefined;
267
+ if (vertexColors.length === segments + 1) return vertexColors;
268
+ const mapped = vertexColors.map(
269
+ (color) => color instanceof Color ? color : new Color(...color as [number, number, number]),
270
+ );
271
+ if (closed) mapped.push(mapped[0].clone());
272
+ const values: Color[] = [mapped[0]];
273
+ const divisions = segments / (mapped.length - 1);
274
+ for (let index = 1; index < segments; index++) {
275
+ const alpha = (index % divisions) / divisions;
276
+ const colorIndex = Math.floor(index / divisions);
277
+ values.push(mapped[colorIndex].clone().lerp(mapped[colorIndex + 1], alpha));
278
+ }
279
+ values.push(mapped[mapped.length - 1]);
280
+ return values;
281
+ }, [vertexColors, segments]);
282
+ <Line
283
+ ref={ref as any}
284
+ points={segmentedPoints}
285
+ vertexColors={interpolatedVertexColors}
286
+ {...rest as any}
287
+ />
288
+ }
289
+
290
+ export type EdgesRef = Mesh<any, any>;
291
+ export type EdgesProps =
292
+ Partial<ThreeElements['mesh']> & {
293
+ threshold?: number;
294
+ lineWidth?: number;
295
+ geometry?: BufferGeometry;
296
+ color?: ColorRepresentation;
297
+ } & Omit<LineMaterialParameters, 'vertexColors' | 'color'>;
298
+
299
+ export function Edges({ threshold = 15, geometry: explicitGeometry, ref, ...props }: EdgesProps & {
300
+ ref?: ThreeRef<EdgesRef>;
301
+ }) @{
302
+ const localRef = useRef<LineSegments2 | null>(null);
303
+ const points = useMemo(() => [0, 0, 0, 1, 0, 0], []);
304
+ const memoizedGeometry = useRef<BufferGeometry | null>(null);
305
+ const memoizedThreshold = useRef<number | null>(null);
306
+ const mergedRef = useMemo(
307
+ () => (value: LineSegments2 | null) => {
308
+ localRef.current = value;
309
+ assignRef(ref as ThreeRef<LineSegments2> | undefined, value);
310
+ },
311
+ [ref],
312
+ );
313
+ useLayoutEffect(() => {
314
+ if (!localRef.current) return;
315
+ const parent = localRef.current.parent as Mesh | null;
316
+ const geometry = explicitGeometry ?? parent?.geometry;
317
+ if (!geometry) return;
318
+ if (memoizedGeometry.current === geometry && memoizedThreshold.current === threshold) return;
319
+ memoizedGeometry.current = geometry;
320
+ memoizedThreshold.current = threshold;
321
+ const edgeGeometry = new EdgesGeometry(geometry, threshold);
322
+ const values = (edgeGeometry.attributes.position as BufferAttribute).array as Float32Array;
323
+ localRef.current.geometry.setPositions(values);
324
+ localRef.current.geometry.attributes.instanceStart.needsUpdate = true;
325
+ localRef.current.geometry.attributes.instanceEnd.needsUpdate = true;
326
+ localRef.current.computeLineDistances();
327
+ });
328
+ <Line segments points={points} ref={mergedRef as any} raycast={() => null} {...props as any} />
329
+ }
@@ -0,0 +1,26 @@
1
+ import { type Intersection, Matrix4, type Mesh, Ray, type Raycaster, Sphere, Vector3 } from 'three';
2
+
3
+ const _inverseMatrix = /* @__PURE__ */ new Matrix4();
4
+ const _ray = /* @__PURE__ */ new Ray();
5
+ const _sphere = /* @__PURE__ */ new Sphere();
6
+ const _vA = /* @__PURE__ */ new Vector3();
7
+
8
+ export function meshBounds(this: Mesh, raycaster: Raycaster, intersects: Intersection[]): void {
9
+ const geometry = this.geometry;
10
+ const material = this.material;
11
+ const matrixWorld = this.matrixWorld;
12
+ if (material === undefined) return;
13
+ if (geometry.boundingSphere === null) geometry.computeBoundingSphere();
14
+ _sphere.copy(geometry.boundingSphere!);
15
+ _sphere.applyMatrix4(matrixWorld);
16
+ if (raycaster.ray.intersectsSphere(_sphere) === false) return;
17
+ _inverseMatrix.copy(matrixWorld).invert();
18
+ _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix);
19
+ if (geometry.boundingBox !== null && _ray.intersectBox(geometry.boundingBox, _vA) === null)
20
+ return;
21
+ intersects.push({
22
+ distance: _vA.distanceTo(raycaster.ray.origin),
23
+ point: _vA.clone(),
24
+ object: this,
25
+ });
26
+ }
@@ -0,0 +1,93 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, type ThreeRef, useFrame } from '@octanejs/three';
3
+ import { useMemo, useState } from '@octanejs/three/renderer';
4
+ import * as THREE from 'three';
5
+
6
+ const distortShader = '#define GLSLIFY 1\nvec3 mod289(vec3 x){return x-floor(x*(1.0/289.0))*289.0;}vec4 mod289(vec4 x){return x-floor(x*(1.0/289.0))*289.0;}vec4 permute(vec4 x){return mod289(((x*34.0)+1.0)*x);}vec4 taylorInvSqrt(vec4 r){return 1.79284291400159-0.85373472095314*r;}float snoise(vec3 v){const vec2 C=vec2(1.0/6.0,1.0/3.0);const vec4 D=vec4(0.0,0.5,1.0,2.0);vec3 i=floor(v+dot(v,C.yyy));vec3 x0=v-i+dot(i,C.xxx);vec3 g=step(x0.yzx,x0.xyz);vec3 l=1.0-g;vec3 i1=min(g.xyz,l.zxy);vec3 i2=max(g.xyz,l.zxy);vec3 x1=x0-i1+C.xxx;vec3 x2=x0-i2+C.yyy;vec3 x3=x0-D.yyy;i=mod289(i);vec4 p=permute(permute(permute(i.z+vec4(0.0,i1.z,i2.z,1.0))+i.y+vec4(0.0,i1.y,i2.y,1.0))+i.x+vec4(0.0,i1.x,i2.x,1.0));float n_=0.142857142857;vec3 ns=n_*D.wyz-D.xzx;vec4 j=p-49.0*floor(p*ns.z*ns.z);vec4 x_=floor(j*ns.z);vec4 y_=floor(j-7.0*x_);vec4 x=x_*ns.x+ns.yyyy;vec4 y=y_*ns.x+ns.yyyy;vec4 h=1.0-abs(x)-abs(y);vec4 b0=vec4(x.xy,y.xy);vec4 b1=vec4(x.zw,y.zw);vec4 s0=floor(b0)*2.0+1.0;vec4 s1=floor(b1)*2.0+1.0;vec4 sh=-step(h,vec4(0.0));vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy;vec4 a1=b1.xzyw+s1.xzyw*sh.zzww;vec3 p0=vec3(a0.xy,h.x);vec3 p1=vec3(a0.zw,h.y);vec3 p2=vec3(a1.xy,h.z);vec3 p3=vec3(a1.zw,h.w);vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3)));p0*=norm.x;p1*=norm.y;p2*=norm.z;p3*=norm.w;vec4 m=max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0);m=m*m;return 42.0*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));}';
7
+
8
+ type Uniform<T> = { value: T };
9
+
10
+ export class DistortMaterialImpl extends THREE.MeshPhysicalMaterial {
11
+ _time: Uniform<number> = { value: 0 };
12
+ _distort: Uniform<number> = { value: 0.4 };
13
+ _radius: Uniform<number> = { value: 1 };
14
+
15
+ constructor(parameters: THREE.MeshPhysicalMaterialParameters = {}) {
16
+ super(parameters);
17
+ this.setValues(parameters);
18
+ }
19
+
20
+ override onBeforeCompile(shader: THREE.WebGLProgramParametersWithUniforms): void {
21
+ shader.uniforms.time = this._time;
22
+ shader.uniforms.radius = this._radius;
23
+ shader.uniforms.distort = this._distort;
24
+ shader.vertexShader = `uniform float time;\nuniform float radius;\nuniform float distort;\n${distortShader}\n${shader.vertexShader}`;
25
+ shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', 'float updateTime = time / 50.0;\nfloat noise = snoise(vec3(position / 2.0 + updateTime * 5.0));\nvec3 transformed = vec3(position * (noise * pow(distort, 2.0) + radius));');
26
+ }
27
+
28
+ get time(): number { return this._time.value; }
29
+ set time(value: number) { this._time.value = value; }
30
+ get distort(): number { return this._distort.value; }
31
+ set distort(value: number) { this._distort.value = value; }
32
+ get radius(): number { return this._radius.value; }
33
+ set radius(value: number) { this._radius.value = value; }
34
+ }
35
+
36
+ export class WobbleMaterialImpl extends THREE.MeshStandardMaterial {
37
+ _time: Uniform<number> = { value: 0 };
38
+ _factor: Uniform<number> = { value: 1 };
39
+
40
+ constructor(parameters: THREE.MeshStandardMaterialParameters = {}) {
41
+ super(parameters);
42
+ this.setValues(parameters);
43
+ }
44
+
45
+ override onBeforeCompile(shader: THREE.WebGLProgramParametersWithUniforms): void {
46
+ shader.uniforms.time = this._time;
47
+ shader.uniforms.factor = this._factor;
48
+ shader.vertexShader = `uniform float time;\nuniform float factor;\n${shader.vertexShader}`;
49
+ shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', 'float theta = sin( time + position.y ) / 2.0 * factor;\nfloat c = cos( theta );\nfloat s = sin( theta );\nmat3 m = mat3( c, 0, s, 0, 1, 0, -s, 0, c );\nvec3 transformed = vec3( position ) * m;\nvNormal = vNormal * m;');
50
+ }
51
+
52
+ get time(): number { return this._time.value; }
53
+ set time(value: number) { this._time.value = value; }
54
+ get factor(): number { return this._factor.value; }
55
+ set factor(value: number) { this._factor.value = value; }
56
+ }
57
+
58
+ export type MeshDistortMaterialProps = Omit<ThreeElements['meshPhysicalMaterial'], 'ref'> & {
59
+ time?: number;
60
+ distort?: number;
61
+ radius?: number;
62
+ speed?: number;
63
+ factor?: number;
64
+ };
65
+
66
+ export function MeshDistortMaterial({ speed = 1, ref, ...props }: MeshDistortMaterialProps & {
67
+ ref?: ThreeRef<DistortMaterialImpl>;
68
+ }) @{
69
+ const [material] = useState(() => new DistortMaterialImpl());
70
+ const mergedRef = useMemo(() => (value: DistortMaterialImpl | null) => assignRef(ref, value), [ref]);
71
+ useFrame((state) => (material.time = state.clock.elapsedTime * speed));
72
+ <primitive object={material} ref={mergedRef} attach="material" {...props as any} />
73
+ }
74
+
75
+ export type WobbleMaterialProps = Omit<ThreeElements['meshStandardMaterial'], 'ref'> & {
76
+ speed?: number;
77
+ factor?: number;
78
+ };
79
+
80
+ export function MeshWobbleMaterial({ speed = 1, ref, ...props }: WobbleMaterialProps & {
81
+ ref?: ThreeRef<WobbleMaterialImpl>;
82
+ }) @{
83
+ const [material] = useState(() => new WobbleMaterialImpl());
84
+ const mergedRef = useMemo(() => (value: WobbleMaterialImpl | null) => assignRef(ref, value), [ref]);
85
+ useFrame((state) => (material.time = state.clock.elapsedTime * speed));
86
+ <primitive object={material} ref={mergedRef} attach="material" {...props as any} />
87
+ }
88
+
89
+ function assignRef<T>(ref: ThreeRef<T> | undefined, value: T | null): void {
90
+ if (Array.isArray(ref)) for (const item of ref) assignRef(item, value);
91
+ else if (typeof ref === 'function') ref(value);
92
+ else if (ref && 'current' in ref) ref.current = value;
93
+ }
@@ -0,0 +1,18 @@
1
+ import { isChildrenBlock } from 'octane';
2
+ import type { OctaneNode } from 'octane';
3
+
4
+ // Compiler-generated TSRX children blocks are zero-argument functions and are
5
+ // structurally assignable to this callback shape. Keep the public type focused
6
+ // on the upstream render-prop contract so callback parameters remain contextual.
7
+ export type RenderChildren<T> = (value: T) => OctaneNode;
8
+
9
+ export function isRenderFunction(value: unknown): value is (...args: any[]) => unknown {
10
+ return typeof value === 'function' && !isChildrenBlock(value);
11
+ }
12
+
13
+ export function resolveRenderChildren<T>(
14
+ children: RenderChildren<T> | undefined,
15
+ value: T,
16
+ ): unknown {
17
+ return isRenderFunction(children) ? children(value) : children;
18
+ }
@@ -0,0 +1,16 @@
1
+ import { isChildrenBlock, isValidElement } from 'octane';
2
+
3
+ /** Keeps renderer-neutral element detection outside universal compiler imports. */
4
+ export const isOctaneElement: typeof isValidElement = isValidElement;
5
+
6
+ /** Distinguishes compiled child blocks from user-authored render-prop functions. */
7
+ export const isOctaneChildrenBlock: typeof isChildrenBlock = isChildrenBlock;
8
+
9
+ /** Iterates universal child values, whose descriptors intentionally differ from DOM elements. */
10
+ export function forEachOctaneChild(children: unknown, visit: (child: unknown) => void): void {
11
+ if (Array.isArray(children)) {
12
+ for (const child of children) forEachOctaneChild(child, visit);
13
+ } else if (children !== null && children !== undefined && typeof children !== 'boolean') {
14
+ visit(children);
15
+ }
16
+ }
@@ -0,0 +1,29 @@
1
+ type Key = readonly unknown[];
2
+
3
+ interface ResourceEntry<T = unknown> {
4
+ readonly keys: Key;
5
+ readonly promise: Promise<T>;
6
+ }
7
+
8
+ const entries: ResourceEntry[] = [];
9
+
10
+ function equalKeys(previous: Key, next: Key): boolean {
11
+ return previous.length === next.length && previous.every((value, index) => value === next[index]);
12
+ }
13
+
14
+ export function resourcePromise<T>(loader: (...keys: any[]) => Promise<T>, keys: Key): Promise<T> {
15
+ const cached = entries.find((entry) => equalKeys(entry.keys, keys));
16
+ if (cached !== undefined) return cached.promise as Promise<T>;
17
+ const entry: ResourceEntry<T> = { keys: [...keys], promise: loader(...keys) };
18
+ entries.push(entry);
19
+ return entry.promise;
20
+ }
21
+
22
+ export function preloadResource<T>(loader: (...keys: any[]) => Promise<T>, keys: Key): void {
23
+ void resourcePromise(loader, keys);
24
+ }
25
+
26
+ export function clearResource(keys: Key): void {
27
+ const index = entries.findIndex((entry) => equalKeys(entry.keys, keys));
28
+ if (index !== -1) entries.splice(index, 1);
29
+ }
@@ -0,0 +1,183 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, type ThreeRef, useFrame } from '@octanejs/three';
3
+ import { useEffect, 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)) {
8
+ for (const item of ref) assignRef(item, value);
9
+ } else if (typeof ref === 'function') ref(value);
10
+ else if (ref && 'current' in ref) ref.current = value;
11
+ }
12
+
13
+ export type BillboardProps =
14
+ Omit<ThreeElements['group'], 'ref'> & {
15
+ follow?: boolean;
16
+ lockX?: boolean;
17
+ lockY?: boolean;
18
+ lockZ?: boolean;
19
+ };
20
+
21
+ export function Billboard({
22
+ children,
23
+ follow = true,
24
+ lockX = false,
25
+ lockY = false,
26
+ lockZ = false,
27
+ ref,
28
+ ...props
29
+ }: BillboardProps & { ref?: ThreeRef<THREE.Group> }) @{
30
+ const inner = useRef<THREE.Group | null>(null);
31
+ const localRef = useRef<THREE.Group | null>(null);
32
+ const quaternion = useMemo(() => new THREE.Quaternion(), []);
33
+ const mergedRef = useMemo(
34
+ () => (value: THREE.Group | null) => {
35
+ localRef.current = value;
36
+ assignRef(ref, value);
37
+ },
38
+ [ref],
39
+ );
40
+ useFrame(({ camera }) => {
41
+ if (!follow || !localRef.current || !inner.current) return;
42
+ const previousRotation = inner.current.rotation.clone();
43
+ localRef.current.updateMatrix();
44
+ localRef.current.updateWorldMatrix(false, false);
45
+ localRef.current.getWorldQuaternion(quaternion);
46
+ camera.getWorldQuaternion(inner.current.quaternion).premultiply(quaternion.invert());
47
+ if (lockX) inner.current.rotation.x = previousRotation.x;
48
+ if (lockY) inner.current.rotation.y = previousRotation.y;
49
+ if (lockZ) inner.current.rotation.z = previousRotation.z;
50
+ });
51
+ <group ref={mergedRef} {...props}>
52
+ <group ref={inner}>{children}</group>
53
+ </group>
54
+ }
55
+
56
+ export type FloatProps =
57
+ Omit<ThreeElements['group'], 'ref'> & {
58
+ enabled?: boolean;
59
+ speed?: number;
60
+ rotationIntensity?: number;
61
+ floatIntensity?: number;
62
+ floatingRange?: [number?, number?];
63
+ autoInvalidate?: boolean;
64
+ };
65
+
66
+ export function Float({
67
+ children,
68
+ enabled = true,
69
+ speed = 1,
70
+ rotationIntensity = 1,
71
+ floatIntensity = 1,
72
+ floatingRange = [-0.1, 0.1],
73
+ autoInvalidate = false,
74
+ ref,
75
+ ...props
76
+ }: FloatProps & { ref?: ThreeRef<THREE.Group> }) @{
77
+ const inner = useRef<THREE.Group | null>(null);
78
+ const offset = useRef(Math.random() * 10000);
79
+ const mergedRef = useMemo(
80
+ () => (value: THREE.Group | null) => {
81
+ inner.current = value;
82
+ assignRef(ref, value);
83
+ },
84
+ [ref],
85
+ );
86
+ useFrame((state) => {
87
+ if (!enabled || speed === 0 || !inner.current) return;
88
+ if (autoInvalidate) state.invalidate();
89
+ const time = offset.current + state.clock.elapsedTime;
90
+ inner.current.rotation.x = (Math.cos((time / 4) * speed) / 8) * rotationIntensity;
91
+ inner.current.rotation.y = (Math.sin((time / 4) * speed) / 8) * rotationIntensity;
92
+ inner.current.rotation.z = (Math.sin((time / 4) * speed) / 20) * rotationIntensity;
93
+ const base = Math.sin((time / 4) * speed) / 10;
94
+ inner.current.position.y = THREE.MathUtils.mapLinear(
95
+ base,
96
+ -0.1,
97
+ 0.1,
98
+ floatingRange[0] ?? -0.1,
99
+ floatingRange[1] ?? 0.1,
100
+ ) *
101
+ floatIntensity;
102
+ inner.current.updateMatrix();
103
+ });
104
+ <group {...props}>
105
+ <group ref={mergedRef} matrixAutoUpdate={false}>{children}</group>
106
+ </group>
107
+ }
108
+
109
+ const easeInExpo = (value: number) => (value === 0 ? 0 : Math.pow(2, 10 * value - 10));
110
+
111
+ export type BackdropProps =
112
+ ThreeElements['group'] & {
113
+ floor?: number;
114
+ segments?: number;
115
+ receiveShadow?: boolean;
116
+ };
117
+
118
+ export function Backdrop({
119
+ children,
120
+ floor = 0.25,
121
+ segments = 20,
122
+ receiveShadow,
123
+ ...props
124
+ }: BackdropProps) @{
125
+ const geometry = useRef<THREE.PlaneGeometry | null>(null);
126
+ useLayoutEffect(() => {
127
+ if (!geometry.current) return;
128
+ let index = 0;
129
+ const offset = segments / segments / 2;
130
+ const position = geometry.current.attributes.position as THREE.BufferAttribute;
131
+ for (let x = 0; x < segments + 1; x++) {
132
+ for (let y = 0; y < segments + 1; y++) {
133
+ position.setXYZ(
134
+ index++,
135
+ x / segments - offset + (x === 0 ? -floor : 0),
136
+ y / segments - offset,
137
+ easeInExpo(x / segments),
138
+ );
139
+ }
140
+ }
141
+ position.needsUpdate = true;
142
+ geometry.current.computeVertexNormals();
143
+ }, [segments, floor]);
144
+ <group {...props}>
145
+ <mesh receiveShadow={receiveShadow} rotation={[-Math.PI / 2, 0, Math.PI / 2]}>
146
+ <planeGeometry ref={geometry} args={[1, 1, segments, segments]} />
147
+ {children}
148
+ </mesh>
149
+ </group>
150
+ }
151
+
152
+ const boundingBox = new THREE.Box3();
153
+ const boundingBoxSize = new THREE.Vector3();
154
+
155
+ export type BBAnchorProps =
156
+ ThreeElements['group'] & {
157
+ anchor: THREE.Vector3 | [number, number, number];
158
+ };
159
+
160
+ export function BBAnchor({ anchor, ...props }: BBAnchorProps) @{
161
+ const ref = useRef<THREE.Group | null>(null);
162
+ const parentRef = useRef<THREE.Object3D | null>(null);
163
+ useEffect(() => {
164
+ if (ref.current?.parent?.parent) {
165
+ parentRef.current = ref.current.parent;
166
+ ref.current.parent.parent.add(ref.current);
167
+ }
168
+ }, []);
169
+ useFrame(() => {
170
+ if (!parentRef.current || !ref.current) return;
171
+ boundingBox.setFromObject(parentRef.current);
172
+ boundingBox.getSize(boundingBoxSize);
173
+ ref.current.position.set(
174
+ parentRef.current.position.x +
175
+ (boundingBoxSize.x * (Array.isArray(anchor) ? anchor[0] : anchor.x)) / 2,
176
+ parentRef.current.position.y +
177
+ (boundingBoxSize.y * (Array.isArray(anchor) ? anchor[1] : anchor.y)) / 2,
178
+ parentRef.current.position.z +
179
+ (boundingBoxSize.z * (Array.isArray(anchor) ? anchor[2] : anchor.z)) / 2,
180
+ );
181
+ });
182
+ <group ref={ref} {...props} />
183
+ }