@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,115 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, useFrame, useThree } from '@octanejs/three';
3
+ import { useCallback, useEffect, useMemo, useRef } from '@octanejs/three/renderer';
4
+ import { createContext, type OctaneNode, useContext } from 'octane';
5
+ import { Group, Matrix4, Object3D, OrthographicCamera as OrthographicCameraImpl, Quaternion, Vector3 } from 'three';
6
+ import type { OrbitControls as OrbitControlsType } from 'three-stdlib';
7
+ import { Hud } from './Hud.three.tsrx';
8
+ import { OrthographicCamera } from './cameras.three.tsrx';
9
+
10
+ type GizmoHelperContext = { tweenCamera: (direction: Vector3) => void };
11
+ const GizmoContext = createContext<GizmoHelperContext>({} as GizmoHelperContext);
12
+
13
+ export const useGizmoContext = () => useContext(GizmoContext);
14
+
15
+ const turnRate = 2 * Math.PI;
16
+ const dummy = new Object3D();
17
+ const matrix = new Matrix4();
18
+ const q1 = new Quaternion();
19
+ const q2 = new Quaternion();
20
+ const target = new Vector3();
21
+ const targetPosition = new Vector3();
22
+
23
+ type ControlsProto = {
24
+ update(delta?: number): void;
25
+ target: Vector3;
26
+ getTarget?(target: Vector3): Vector3;
27
+ setPosition?(x: number, y: number, z: number): void;
28
+ };
29
+
30
+ export type GizmoHelperProps = ThreeElements['group'] & {
31
+ alignment?: 'top-left' | 'top-right' | 'bottom-right' | 'bottom-left' | 'bottom-center' | 'center-right' | 'center-left' | 'center-center' | 'top-center';
32
+ margin?: [number, number];
33
+ renderPriority?: number;
34
+ autoClear?: boolean;
35
+ onUpdate?: () => void;
36
+ onTarget?: () => Vector3;
37
+ children?: OctaneNode;
38
+ };
39
+
40
+ function isOrbitControls(controls: ControlsProto | undefined): controls is ControlsProto & OrbitControlsType {
41
+ return Boolean(controls && 'minPolarAngle' in controls);
42
+ }
43
+
44
+ function isCameraControls(controls: ControlsProto | undefined): controls is ControlsProto & Required<Pick<ControlsProto, 'getTarget' | 'setPosition'>> {
45
+ return Boolean(controls && 'getTarget' in controls);
46
+ }
47
+
48
+ function getControlsTarget(controls: ControlsProto | undefined, destination: Vector3): Vector3 | undefined {
49
+ return controls?.getTarget ? controls.getTarget(destination) : controls?.target;
50
+ }
51
+
52
+ export function GizmoHelper({
53
+ alignment = 'bottom-right',
54
+ margin = [80, 80],
55
+ renderPriority = 1,
56
+ onUpdate,
57
+ onTarget,
58
+ children,
59
+ }: GizmoHelperProps) @{
60
+ const size = useThree((state) => state.size);
61
+ const mainCamera = useThree((state) => state.camera);
62
+ const defaultControls = useThree((state) => state.controls as unknown as ControlsProto | undefined);
63
+ const invalidate = useThree((state) => state.invalidate);
64
+ const gizmo = useRef<Group | null>(null);
65
+ const virtualCamera = useRef<OrthographicCameraImpl | null>(null);
66
+ const animating = useRef(false);
67
+ const radius = useRef(0);
68
+ const focusPoint = useRef(new Vector3());
69
+ const defaultUp = useRef(new Vector3());
70
+ useEffect(() => {
71
+ defaultUp.current.copy(mainCamera.up);
72
+ dummy.up.copy(mainCamera.up);
73
+ }, [mainCamera]);
74
+ const tweenCamera = useCallback((direction: Vector3) => {
75
+ animating.current = true;
76
+ if (defaultControls || onTarget) {
77
+ focusPoint.current = onTarget?.() ?? getControlsTarget(defaultControls, focusPoint.current) ?? focusPoint.current;
78
+ }
79
+ radius.current = mainCamera.position.distanceTo(target);
80
+ q1.copy(mainCamera.quaternion);
81
+ targetPosition.copy(direction).multiplyScalar(radius.current).add(target);
82
+ dummy.lookAt(targetPosition);
83
+ q2.copy(dummy.quaternion);
84
+ invalidate();
85
+ }, [defaultControls, mainCamera, onTarget, invalidate]);
86
+ useFrame((_state, delta) => {
87
+ if (!virtualCamera.current || !gizmo.current) return;
88
+ if (animating.current) {
89
+ if (q1.angleTo(q2) < 0.01) {
90
+ animating.current = false;
91
+ if (isOrbitControls(defaultControls)) mainCamera.up.copy(defaultUp.current);
92
+ } else {
93
+ q1.rotateTowards(q2, delta * turnRate);
94
+ mainCamera.position.set(0, 0, 1).applyQuaternion(q1).multiplyScalar(radius.current).add(focusPoint.current);
95
+ mainCamera.up.set(0, 1, 0).applyQuaternion(q1).normalize();
96
+ mainCamera.quaternion.copy(q1);
97
+ if (isCameraControls(defaultControls)) defaultControls.setPosition(mainCamera.position.x, mainCamera.position.y, mainCamera.position.z);
98
+ if (onUpdate) onUpdate(); else defaultControls?.update(delta);
99
+ invalidate();
100
+ }
101
+ }
102
+ matrix.copy(mainCamera.matrix).invert();
103
+ gizmo.current.quaternion.setFromRotationMatrix(matrix);
104
+ });
105
+ const context = useMemo(() => ({ tweenCamera }), [tweenCamera]);
106
+ const [marginX, marginY] = margin;
107
+ const x = alignment.endsWith('-center') ? 0 : alignment.endsWith('-left') ? -size.width / 2 + marginX : size.width / 2 - marginX;
108
+ const y = alignment.startsWith('center-') ? 0 : alignment.startsWith('top-') ? size.height / 2 - marginY : -size.height / 2 + marginY;
109
+ <Hud renderPriority={renderPriority}>
110
+ <GizmoContext.Provider value={context}>
111
+ <OrthographicCamera makeDefault ref={virtualCamera} position={[0, 0, 200]} />
112
+ <group ref={gizmo} position={[x, y, 0]}>{children}</group>
113
+ </GizmoContext.Provider>
114
+ </Hud>
115
+ }
@@ -0,0 +1,82 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeEvent, useThree } from '@octanejs/three';
3
+ import { useMemo, useState } from '@octanejs/three/renderer';
4
+ import { CanvasTexture, Vector3 } from 'three';
5
+ import { useGizmoContext } from './GizmoHelper.three.tsrx';
6
+
7
+ type XYZ = [number, number, number];
8
+ type GizmoViewcubeProps = {
9
+ font?: string;
10
+ opacity?: number;
11
+ color?: string;
12
+ hoverColor?: string;
13
+ textColor?: string;
14
+ strokeColor?: string;
15
+ onClick?: (event: ThreeEvent<MouseEvent>) => null;
16
+ faces?: string[];
17
+ };
18
+ type FaceMaterialProps = GizmoViewcubeProps & { hover: boolean; index: number };
19
+ type EdgeCubeProps = GizmoViewcubeProps & { dimensions: XYZ; position: Vector3 };
20
+ const colors = { bg: '#f0f0f0', hover: '#999', text: 'black', stroke: 'black' };
21
+ const defaultFaces = ['Right', 'Left', 'Top', 'Bottom', 'Front', 'Back'];
22
+ const makePositionVector = (xyz: number[]) => new Vector3(...(xyz as XYZ)).multiplyScalar(0.38);
23
+ const corners = [[1,1,1],[1,1,-1],[1,-1,1],[1,-1,-1],[-1,1,1],[-1,1,-1],[-1,-1,1],[-1,-1,-1]].map(makePositionVector);
24
+ const edges = [[1,1,0],[1,0,1],[1,0,-1],[1,-1,0],[0,1,1],[0,1,-1],[0,-1,1],[0,-1,-1],[-1,1,0],[-1,0,1],[-1,0,-1],[-1,-1,0]].map(makePositionVector);
25
+ const cornerDimensions: XYZ = [0.25, 0.25, 0.25];
26
+ const edgeDimensions = edges.map((edge) => edge.toArray().map((axis) => axis === 0 ? 0.5 : 0.25) as XYZ);
27
+
28
+ function FaceMaterial({ hover, index, font = '20px Inter var, Arial, sans-serif', faces = defaultFaces, color = colors.bg, hoverColor = colors.hover, textColor = colors.text, strokeColor = colors.stroke, opacity = 1 }: FaceMaterialProps) @{
29
+ const gl = useThree((state) => state.gl);
30
+ const texture = useMemo(() => {
31
+ const canvas = document.createElement('canvas'); canvas.width = 128; canvas.height = 128;
32
+ const context = canvas.getContext('2d')!;
33
+ context.fillStyle = color; context.fillRect(0, 0, 128, 128); context.strokeStyle = strokeColor; context.strokeRect(0, 0, 128, 128);
34
+ context.font = font; context.textAlign = 'center'; context.fillStyle = textColor; context.fillText(faces[index]!.toUpperCase(), 64, 76);
35
+ return new CanvasTexture(canvas);
36
+ }, [index, faces, font, color, textColor, strokeColor]);
37
+ <meshBasicMaterial map={texture} map-anisotropy={gl.capabilities.getMaxAnisotropy() || 1} attach={`material-${index}`} color={hover ? hoverColor : 'white'} transparent opacity={opacity} />
38
+ }
39
+
40
+ function FaceCube(props: GizmoViewcubeProps) @{
41
+ const { tweenCamera } = useGizmoContext();
42
+ const [hover, setHover] = useState<number | null>(null);
43
+ <mesh
44
+ onPointerOut={(event) => { event.stopPropagation(); setHover(null); }}
45
+ onPointerMove={(event) => { event.stopPropagation(); setHover(Math.floor(event.faceIndex! / 2)); }}
46
+ onClick={props.onClick || ((event) => { event.stopPropagation(); tweenCamera(event.face!.normal); })}
47
+ >
48
+ <FaceMaterial index={0} hover={hover === 0} {...props} /><FaceMaterial index={1} hover={hover === 1} {...props} />
49
+ <FaceMaterial index={2} hover={hover === 2} {...props} /><FaceMaterial index={3} hover={hover === 3} {...props} />
50
+ <FaceMaterial index={4} hover={hover === 4} {...props} /><FaceMaterial index={5} hover={hover === 5} {...props} />
51
+ <boxGeometry />
52
+ </mesh>
53
+ }
54
+
55
+ function EdgeCube({ onClick, dimensions, position, hoverColor = colors.hover }: EdgeCubeProps) @{
56
+ const { tweenCamera } = useGizmoContext();
57
+ const [hover, setHover] = useState(false);
58
+ <mesh scale={1.01} position={position}
59
+ onPointerOver={(event) => { event.stopPropagation(); setHover(true); }}
60
+ onPointerOut={(event) => { event.stopPropagation(); setHover(false); }}
61
+ onClick={onClick || ((event) => { event.stopPropagation(); tweenCamera(position); })}
62
+ >
63
+ <meshBasicMaterial color={hover ? hoverColor : 'white'} transparent opacity={0.6} visible={hover} />
64
+ <boxGeometry args={dimensions} />
65
+ </mesh>
66
+ }
67
+
68
+ export function GizmoViewcube(props: GizmoViewcubeProps) @{
69
+ <group scale={[60, 60, 60]}>
70
+ <FaceCube {...props} />
71
+ <EdgeCube position={edges[0]!} dimensions={edgeDimensions[0]!} {...props} /><EdgeCube position={edges[1]!} dimensions={edgeDimensions[1]!} {...props} />
72
+ <EdgeCube position={edges[2]!} dimensions={edgeDimensions[2]!} {...props} /><EdgeCube position={edges[3]!} dimensions={edgeDimensions[3]!} {...props} />
73
+ <EdgeCube position={edges[4]!} dimensions={edgeDimensions[4]!} {...props} /><EdgeCube position={edges[5]!} dimensions={edgeDimensions[5]!} {...props} />
74
+ <EdgeCube position={edges[6]!} dimensions={edgeDimensions[6]!} {...props} /><EdgeCube position={edges[7]!} dimensions={edgeDimensions[7]!} {...props} />
75
+ <EdgeCube position={edges[8]!} dimensions={edgeDimensions[8]!} {...props} /><EdgeCube position={edges[9]!} dimensions={edgeDimensions[9]!} {...props} />
76
+ <EdgeCube position={edges[10]!} dimensions={edgeDimensions[10]!} {...props} /><EdgeCube position={edges[11]!} dimensions={edgeDimensions[11]!} {...props} />
77
+ <EdgeCube position={corners[0]!} dimensions={cornerDimensions} {...props} /><EdgeCube position={corners[1]!} dimensions={cornerDimensions} {...props} />
78
+ <EdgeCube position={corners[2]!} dimensions={cornerDimensions} {...props} /><EdgeCube position={corners[3]!} dimensions={cornerDimensions} {...props} />
79
+ <EdgeCube position={corners[4]!} dimensions={cornerDimensions} {...props} /><EdgeCube position={corners[5]!} dimensions={cornerDimensions} {...props} />
80
+ <EdgeCube position={corners[6]!} dimensions={cornerDimensions} {...props} /><EdgeCube position={corners[7]!} dimensions={cornerDimensions} {...props} />
81
+ </group>
82
+ }
@@ -0,0 +1,122 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, type ThreeEvent, useThree } from '@octanejs/three';
3
+ import { useMemo, useState } from '@octanejs/three/renderer';
4
+ import { CanvasTexture } from 'three';
5
+ import { universalComponent } from 'octane/universal';
6
+ import { useGizmoContext } from './GizmoHelper.three.tsrx';
7
+
8
+ type AxisProps = { color: string; rotation: [number, number, number]; scale?: [number, number, number] };
9
+ type AxisHeadProps = Omit<ThreeElements['sprite'], 'ref'> & {
10
+ arcStyle: string;
11
+ label?: string;
12
+ labelColor: string;
13
+ axisHeadScale?: number;
14
+ disabled?: boolean;
15
+ font: string;
16
+ onClick?: (event: ThreeEvent<MouseEvent>) => null;
17
+ };
18
+
19
+ type GizmoViewportProps = ThreeElements['group'] & {
20
+ axisColors?: [string, string, string];
21
+ axisScale?: [number, number, number];
22
+ labels?: [string, string, string];
23
+ axisHeadScale?: number;
24
+ labelColor?: string;
25
+ hideNegativeAxes?: boolean;
26
+ hideAxisHeads?: boolean;
27
+ disabled?: boolean;
28
+ font?: string;
29
+ onClick?: (event: ThreeEvent<MouseEvent>) => null;
30
+ };
31
+
32
+ function Axis({ scale = [0.8, 0.05, 0.05], color, rotation }: AxisProps) @{
33
+ <group rotation={rotation}>
34
+ <mesh position={[0.4, 0, 0]}>
35
+ <boxGeometry args={scale} />
36
+ <meshBasicMaterial color={color} toneMapped={false} />
37
+ </mesh>
38
+ </group>
39
+ }
40
+
41
+ function AxisHead({ onClick, font, disabled, arcStyle, label, labelColor, axisHeadScale = 1, ...props }: AxisHeadProps) @{
42
+ const gl = useThree((state) => state.gl);
43
+ const texture = useMemo(() => {
44
+ const canvas = document.createElement('canvas');
45
+ canvas.width = 64;
46
+ canvas.height = 64;
47
+ const context = canvas.getContext('2d')!;
48
+ context.beginPath();
49
+ context.arc(32, 32, 16, 0, 2 * Math.PI);
50
+ context.closePath();
51
+ context.fillStyle = arcStyle;
52
+ context.fill();
53
+ if (label) {
54
+ context.font = font;
55
+ context.textAlign = 'center';
56
+ context.fillStyle = labelColor;
57
+ context.fillText(label, 32, 41);
58
+ }
59
+ return new CanvasTexture(canvas);
60
+ }, [arcStyle, label, labelColor, font]);
61
+ const [active, setActive] = useState(false);
62
+ const scale = (label ? 1 : 0.75) * (active ? 1.2 : 1) * axisHeadScale;
63
+ <sprite
64
+ scale={scale}
65
+ onPointerOver={!disabled ? (event) => { event.stopPropagation(); setActive(true); } : undefined}
66
+ onPointerOut={!disabled ? onClick || ((event) => { event.stopPropagation(); setActive(false); }) : undefined}
67
+ {...props}
68
+ >
69
+ <spriteMaterial map={texture} map-anisotropy={gl.capabilities.getMaxAnisotropy() || 1} alphaTest={0.3} opacity={label ? 1 : 0.75} toneMapped={false} />
70
+ </sprite>
71
+ }
72
+
73
+ type HeadsProps = {
74
+ font: string; disabled?: boolean; labelColor: string; onClick?: (event: ThreeEvent<MouseEvent>) => null;
75
+ axisHeadScale: number; pointerDown?: (event: ThreeEvent<PointerEvent>) => void;
76
+ colors: [string, string, string]; labels: [string, string, string];
77
+ };
78
+
79
+ function PositiveHeads({ font, disabled, labelColor, onClick, axisHeadScale, pointerDown, colors, labels }: HeadsProps) @{
80
+ <>
81
+ <AxisHead font={font} disabled={disabled} labelColor={labelColor} onClick={onClick} axisHeadScale={axisHeadScale} onPointerDown={pointerDown} arcStyle={colors[0]} position={[1, 0, 0]} label={labels[0]} />
82
+ <AxisHead font={font} disabled={disabled} labelColor={labelColor} onClick={onClick} axisHeadScale={axisHeadScale} onPointerDown={pointerDown} arcStyle={colors[1]} position={[0, 1, 0]} label={labels[1]} />
83
+ <AxisHead font={font} disabled={disabled} labelColor={labelColor} onClick={onClick} axisHeadScale={axisHeadScale} onPointerDown={pointerDown} arcStyle={colors[2]} position={[0, 0, 1]} label={labels[2]} />
84
+ </>
85
+ }
86
+
87
+ function AllHeads(props: HeadsProps) @{
88
+ <>
89
+ <PositiveHeads {...props} />
90
+ <AxisHead font={props.font} disabled={props.disabled} labelColor={props.labelColor} onClick={props.onClick} axisHeadScale={props.axisHeadScale} onPointerDown={props.pointerDown} arcStyle={props.colors[0]} position={[-1, 0, 0]} />
91
+ <AxisHead font={props.font} disabled={props.disabled} labelColor={props.labelColor} onClick={props.onClick} axisHeadScale={props.axisHeadScale} onPointerDown={props.pointerDown} arcStyle={props.colors[1]} position={[0, -1, 0]} />
92
+ <AxisHead font={props.font} disabled={props.disabled} labelColor={props.labelColor} onClick={props.onClick} axisHeadScale={props.axisHeadScale} onPointerDown={props.pointerDown} arcStyle={props.colors[2]} position={[0, 0, -1]} />
93
+ </>
94
+ }
95
+
96
+ function NoHeads() @{}
97
+
98
+ export function GizmoViewport({
99
+ hideNegativeAxes,
100
+ hideAxisHeads,
101
+ disabled,
102
+ font = '18px Inter var, Arial, sans-serif',
103
+ axisColors = ['#ff2060', '#20df80', '#2080ff'],
104
+ axisHeadScale = 1,
105
+ axisScale,
106
+ labels = ['X', 'Y', 'Z'],
107
+ labelColor = '#000',
108
+ onClick,
109
+ ...props
110
+ }: GizmoViewportProps) @{
111
+ const [colorX, colorY, colorZ] = axisColors;
112
+ const { tweenCamera } = useGizmoContext();
113
+ const pointerDown = !disabled ? (event: ThreeEvent<PointerEvent>) => { tweenCamera(event.object.position); event.stopPropagation(); } : undefined;
114
+ const headsComponent = hideAxisHeads ? NoHeads : hideNegativeAxes ? PositiveHeads : AllHeads;
115
+ const headsProps: HeadsProps = { font, disabled, labelColor, onClick, axisHeadScale, pointerDown, colors: axisColors, labels };
116
+ <group scale={40} {...props}>
117
+ <Axis color={colorX} rotation={[0, 0, 0]} scale={axisScale} />
118
+ <Axis color={colorY} rotation={[0, 0, Math.PI / 2]} scale={axisScale} />
119
+ <Axis color={colorZ} rotation={[0, -Math.PI / 2, 0]} scale={axisScale} />
120
+ {universalComponent('three', headsComponent as any, headsProps as unknown as Record<string, unknown>)}
121
+ </group>
122
+ }
@@ -0,0 +1,62 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ObjectMap, useLoader, type ThreeRef } from '@octanejs/three';
3
+ import type { Group } from 'three';
4
+ import { GLTFLoader, type GLTF } from 'three-stdlib';
5
+ import { Clone, type CloneProps } from './Clone.three.tsrx';
6
+ import { slotDefault } from './compiler-slot.js';
7
+ import {
8
+ type ExtendLoader,
9
+ gltfExtensions,
10
+ type Path,
11
+ setGltfDecoderPath,
12
+ type UseDraco,
13
+ type UseMeshopt,
14
+ } from './gltf-loader.js';
15
+
16
+ type GltfResult = GLTF & ObjectMap;
17
+
18
+ export const useGLTF = Object.assign(
19
+ <T extends Path,>(
20
+ path: T,
21
+ useDraco?: UseDraco,
22
+ useMeshopt?: UseMeshopt,
23
+ extendLoader?: ExtendLoader,
24
+ ): T extends any[] ? GltfResult[] : GltfResult => {
25
+ useDraco = slotDefault(useDraco, undefined);
26
+ useMeshopt = slotDefault(useMeshopt, undefined);
27
+ extendLoader = slotDefault(extendLoader, undefined);
28
+ return useLoader(
29
+ GLTFLoader,
30
+ path,
31
+ gltfExtensions(useDraco, useMeshopt, extendLoader) as any,
32
+ ) as unknown as T extends any[] ? GltfResult[] : GltfResult;
33
+ },
34
+ {
35
+ preload(path: Path, useDraco?: UseDraco, useMeshopt?: UseMeshopt, extendLoader?: ExtendLoader) {
36
+ useLoader.preload(
37
+ GLTFLoader,
38
+ path,
39
+ gltfExtensions(useDraco, useMeshopt, extendLoader) as any,
40
+ );
41
+ },
42
+ clear(path: Path) {
43
+ useLoader.clear(GLTFLoader, path);
44
+ },
45
+ setDecoderPath: setGltfDecoderPath,
46
+ },
47
+ );
48
+
49
+ export type GltfProps =
50
+ Omit<CloneProps, 'object'> & {
51
+ src: string;
52
+ useDraco?: UseDraco;
53
+ useMeshOpt?: UseMeshopt;
54
+ extendLoader?: ExtendLoader;
55
+ };
56
+
57
+ export function Gltf({ src, useDraco, useMeshOpt, extendLoader, ref, ...props }: GltfProps & {
58
+ ref?: ThreeRef<Group>;
59
+ }) @{
60
+ const { scene } = useGLTF(src, useDraco, useMeshOpt, extendLoader);
61
+ <Clone ref={ref} {...props} object={scene as unknown as Group} />
62
+ }
@@ -0,0 +1,66 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type ThreeElements, useThree } from '@octanejs/three';
3
+ import { useMemo } from '@octanejs/three/renderer';
4
+ import * as THREE from 'three';
5
+ import { GradientType } from './gradient-types.js';
6
+
7
+ export { GradientType } from './gradient-types.js';
8
+
9
+ export type GradientTextureProps =
10
+ {
11
+ stops: number[];
12
+ colors: THREE.ColorRepresentation[];
13
+ attach?: string;
14
+ size?: number;
15
+ width?: number;
16
+ type?: GradientType;
17
+ innerCircleRadius?: number;
18
+ outerCircleRadius?: string | number;
19
+ } & Omit<ThreeElements['texture'], 'args' | 'type'>;
20
+
21
+ export function GradientTexture({
22
+ stops,
23
+ colors,
24
+ size = 1024,
25
+ width = 16,
26
+ type = GradientType.Linear,
27
+ innerCircleRadius = 0,
28
+ outerCircleRadius = 'auto',
29
+ ...props
30
+ }: GradientTextureProps) @{
31
+ const gl = useThree((state) => state.gl);
32
+ const canvas = useMemo(() => {
33
+ const value = document.createElement('canvas');
34
+ const context = value.getContext('2d')!;
35
+ value.width = width;
36
+ value.height = size;
37
+ let gradient: CanvasGradient;
38
+ if (type === GradientType.Linear) {
39
+ gradient = context.createLinearGradient(0, 0, 0, size);
40
+ } else {
41
+ const centerX = value.width / 2;
42
+ const centerY = value.height / 2;
43
+ const radius =
44
+ outerCircleRadius !== 'auto'
45
+ ? Math.abs(Number(outerCircleRadius))
46
+ : Math.sqrt(centerX ** 2 + centerY ** 2);
47
+ gradient = context.createRadialGradient(
48
+ centerX,
49
+ centerY,
50
+ Math.abs(innerCircleRadius),
51
+ centerX,
52
+ centerY,
53
+ radius,
54
+ );
55
+ }
56
+ const color = new THREE.Color();
57
+ let index = stops.length;
58
+ while (index--) gradient.addColorStop(stops[index], color.set(colors[index]).getStyle());
59
+ context.save();
60
+ context.fillStyle = gradient;
61
+ context.fillRect(0, 0, width, size);
62
+ context.restore();
63
+ return value;
64
+ }, [stops]);
65
+ <canvasTexture colorSpace={gl.outputColorSpace} args={[canvas]} attach="map" {...props as any} />
66
+ }
@@ -0,0 +1,170 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { applyProps, type ThreeElements, type ThreeRef, useFrame } from '@octanejs/three';
3
+ import { useMemo, useRef, useState } from '@octanejs/three/renderer';
4
+ import * as THREE from 'three';
5
+ import { shaderMaterial } from './shaderMaterial.js';
6
+
7
+ export type GridMaterialType = {
8
+ cellSize?: number;
9
+ cellThickness?: number;
10
+ cellColor?: THREE.ColorRepresentation;
11
+ sectionSize?: number;
12
+ sectionThickness?: number;
13
+ sectionColor?: THREE.ColorRepresentation;
14
+ followCamera?: boolean;
15
+ infiniteGrid?: boolean;
16
+ fadeDistance?: number;
17
+ fadeStrength?: number;
18
+ fadeFrom?: number;
19
+ side?: THREE.Side;
20
+ };
21
+
22
+ export type GridProps =
23
+ Omit<ThreeElements['mesh'], 'ref' | 'args'> & GridMaterialType & {
24
+ args?: ConstructorParameters<typeof THREE.PlaneGeometry>;
25
+ };
26
+
27
+ const GridMaterial = shaderMaterial(
28
+ {
29
+ cellSize: 0.5,
30
+ sectionSize: 1,
31
+ fadeDistance: 100,
32
+ fadeStrength: 1,
33
+ fadeFrom: 1,
34
+ cellThickness: 0.5,
35
+ sectionThickness: 1,
36
+ cellColor: new THREE.Color(),
37
+ sectionColor: new THREE.Color(),
38
+ infiniteGrid: false,
39
+ followCamera: false,
40
+ worldCamProjPosition: new THREE.Vector3(),
41
+ worldPlanePosition: new THREE.Vector3(),
42
+ },
43
+ `varying vec3 localPosition;
44
+ varying vec4 worldPosition;
45
+
46
+ uniform vec3 worldCamProjPosition;
47
+ uniform vec3 worldPlanePosition;
48
+ uniform float fadeDistance;
49
+ uniform bool infiniteGrid;
50
+ uniform bool followCamera;
51
+
52
+ void main() {
53
+ localPosition = position.xzy;
54
+ if (infiniteGrid) localPosition *= 1.0 + fadeDistance;
55
+
56
+ worldPosition = modelMatrix * vec4(localPosition, 1.0);
57
+ if (followCamera) {
58
+ worldPosition.xyz += (worldCamProjPosition - worldPlanePosition);
59
+ localPosition = (inverse(modelMatrix) * worldPosition).xyz;
60
+ }
61
+
62
+ gl_Position = projectionMatrix * viewMatrix * worldPosition;
63
+ }`,
64
+ `varying vec3 localPosition;
65
+ varying vec4 worldPosition;
66
+
67
+ uniform vec3 worldCamProjPosition;
68
+ uniform float cellSize;
69
+ uniform float sectionSize;
70
+ uniform vec3 cellColor;
71
+ uniform vec3 sectionColor;
72
+ uniform float fadeDistance;
73
+ uniform float fadeStrength;
74
+ uniform float fadeFrom;
75
+ uniform float cellThickness;
76
+ uniform float sectionThickness;
77
+
78
+ float getGrid(float size, float thickness) {
79
+ vec2 r = localPosition.xz / size;
80
+ vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r);
81
+ float line = min(grid.x, grid.y) + 1.0 - thickness;
82
+ return 1.0 - min(line, 1.0);
83
+ }
84
+
85
+ void main() {
86
+ float g1 = getGrid(cellSize, cellThickness);
87
+ float g2 = getGrid(sectionSize, sectionThickness);
88
+
89
+ vec3 from = worldCamProjPosition*vec3(fadeFrom);
90
+ float dist = distance(from, worldPosition.xyz);
91
+ float d = 1.0 - min(dist / fadeDistance, 1.0);
92
+ vec3 color = mix(cellColor, sectionColor, min(1.0, sectionThickness * g2));
93
+
94
+ gl_FragColor = vec4(color, (g1 + g2) * pow(d, fadeStrength));
95
+ gl_FragColor.a = mix(0.75 * gl_FragColor.a, gl_FragColor.a, g2);
96
+ if (gl_FragColor.a <= 0.0) discard;
97
+
98
+ #include <tonemapping_fragment>
99
+ #include <colorspace_fragment>
100
+ }`,
101
+ );
102
+
103
+ function assignRef<T>(ref: ThreeRef<T> | undefined, value: T | null): void {
104
+ if (Array.isArray(ref)) for (const item of ref) assignRef(item, value);
105
+ else if (typeof ref === 'function') ref(value);
106
+ else if (ref && 'current' in ref) ref.current = value;
107
+ }
108
+
109
+ export function Grid({
110
+ args,
111
+ cellColor = '#000000',
112
+ sectionColor = '#2080ff',
113
+ cellSize = 0.5,
114
+ sectionSize = 1,
115
+ followCamera = false,
116
+ infiniteGrid = false,
117
+ fadeDistance = 100,
118
+ fadeStrength = 1,
119
+ fadeFrom = 1,
120
+ cellThickness = 0.5,
121
+ sectionThickness = 1,
122
+ side = THREE.BackSide,
123
+ ref,
124
+ ...props
125
+ }: GridProps & { ref?: ThreeRef<THREE.Mesh> }) @{
126
+ const localRef = useRef<THREE.Mesh | null>(null);
127
+ const mergedRef = useMemo(
128
+ () => (value: THREE.Mesh | null) => {
129
+ localRef.current = value;
130
+ assignRef(ref, value);
131
+ },
132
+ [ref],
133
+ );
134
+ const [material] = useState(() => new GridMaterial());
135
+ applyProps(material, {
136
+ cellSize,
137
+ sectionSize,
138
+ cellColor,
139
+ sectionColor,
140
+ cellThickness,
141
+ sectionThickness,
142
+ fadeDistance,
143
+ fadeStrength,
144
+ fadeFrom,
145
+ infiniteGrid,
146
+ followCamera,
147
+ side,
148
+ transparent: true,
149
+ extensions: { derivatives: true },
150
+ });
151
+ const plane = new THREE.Plane();
152
+ const upVector = new THREE.Vector3(0, 1, 0);
153
+ const zeroVector = new THREE.Vector3();
154
+ useFrame((state) => {
155
+ if (!localRef.current) return;
156
+ plane.setFromNormalAndCoplanarPoint(upVector, zeroVector).applyMatrix4(
157
+ localRef.current.matrixWorld,
158
+ );
159
+ const worldCamProjPosition = material.uniforms.worldCamProjPosition as THREE.Uniform<
160
+ THREE.Vector3
161
+ >;
162
+ const worldPlanePosition = material.uniforms.worldPlanePosition as THREE.Uniform<THREE.Vector3>;
163
+ plane.projectPoint(state.camera.position, worldCamProjPosition.value);
164
+ worldPlanePosition.value.set(0, 0, 0).applyMatrix4(localRef.current.matrixWorld);
165
+ });
166
+ <mesh ref={mergedRef} frustumCulled={false} {...props}>
167
+ <primitive object={material} attach="material" />
168
+ <planeGeometry args={args} />
169
+ </mesh>
170
+ }
@@ -0,0 +1,56 @@
1
+ /** @jsxImportSource @octanejs/three/intrinsics */
2
+ import { type RefObject, useFrame, useThree } from '@octanejs/three';
3
+ import { useLayoutEffect, useRef } from '@octanejs/three/renderer';
4
+ import { Object3D } from 'three';
5
+ import { stripTrailingSlot } from './compiler-slot.js';
6
+
7
+ type Falsey = false | null | undefined;
8
+ type HelperType = Object3D & { update?: () => void; dispose?: () => void };
9
+ type HelperConstructor = new (...args: any[]) => any;
10
+ type HelperArgs<T> = T extends [infer _, ...infer Rest] ? Rest : never;
11
+
12
+ export function useHelper<T extends HelperConstructor>(
13
+ object3D: RefObject<Object3D> | Falsey,
14
+ helperConstructor: T,
15
+ ...args: HelperArgs<ConstructorParameters<T>>
16
+ ) {
17
+ args = stripTrailingSlot(args) as HelperArgs<ConstructorParameters<T>>;
18
+ const helper = useRef<HelperType | null | undefined>(null);
19
+ const scene = useThree((state) => state.scene);
20
+ useLayoutEffect(() => {
21
+ let currentHelper: HelperType | undefined;
22
+ if (object3D && object3D.current && helperConstructor) {
23
+ helper.current = currentHelper =
24
+ new helperConstructor(object3D.current, ...args) as HelperType;
25
+ }
26
+ if (currentHelper) {
27
+ currentHelper.traverse((child) => (child.raycast = () => null));
28
+ scene.add(currentHelper);
29
+ return () => {
30
+ helper.current = undefined;
31
+ scene.remove(currentHelper);
32
+ currentHelper.dispose?.();
33
+ };
34
+ }
35
+ }, [scene, helperConstructor, object3D, ...args]);
36
+ useFrame(() => void helper.current?.update?.());
37
+ return helper;
38
+ }
39
+
40
+ export type HelperProps<T extends HelperConstructor> = {
41
+ type: T;
42
+ args?: HelperArgs<ConstructorParameters<T>>;
43
+ };
44
+
45
+ export function Helper<T extends HelperConstructor>({
46
+ type: helperConstructor,
47
+ args = [] as never,
48
+ }: HelperProps<T>) @{
49
+ const thisRef = useRef<Object3D | null>(null);
50
+ const parentRef = useRef<Object3D | null>(null);
51
+ useLayoutEffect(() => {
52
+ parentRef.current = thisRef.current?.parent ?? null;
53
+ });
54
+ useHelper(parentRef, helperConstructor, ...args);
55
+ <object3D ref={thisRef} />
56
+ }