diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c73798a..2a68ab1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,13 @@ jobs: # # Lint # - - run: pnpm lint:metadata - - run: pnpm lint:versions + # `check`, not `lint`: it adds the repo-wide `prettier --check` that the + # pre-commit hook cannot afford (every commit would invalidate it), and + # so covers a `git commit -n` or anything that wrote outside a commit. + - run: pnpm check + # Asserts the `inputs` above stay honest: which file makes which task + # re-run. Runs turbo with `--dry`, so it costs hashes, not builds. + - run: pnpm test:turbo # # Test diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..587f03b3 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,3 @@ +# `--concurrent false`: prettier rewrites the staged manifests that the +# metadata/syncpack checks then read, so the tasks must not overlap. +pnpm exec lint-staged --concurrent false diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 00000000..c22c321d --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,8 @@ +# What CI gates on, run once per push instead of once per commit: `check` +# reads the whole repo (`format:check` alone is ~10s), which is affordable +# here and is not in the pre-commit hook -- there, lint-staged fixes the +# staged files instead, and every commit would invalidate this. +# +# Nothing new since the last push is FULL TURBO, so the cost is only paid for +# what actually moved. +pnpm check diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..6a377690 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,20 @@ +# Generated, and `.patch` files prettier has no parser for -- `format:check` +# runs without `--ignore-unknown`, so it would error on them rather than skip +# them. Build outputs and `.turbo/` need no entry: they are gitignored, and +# prettier reads `.gitignore` too. +pnpm-lock.yaml +patches/ + +# Local agent checkouts, plus a vendored skill that has to stay byte-identical +# to what its registry emits. +.claude/ + +# Compiled payloads and 3D assets. Prettier claims a parser for every one of +# these -- `.gltf` is json to it, minified draco is babel -- so +# `--ignore-unknown` does not cover them: a single 200k-character line would +# be exploded into tens of thousands. Only `.glb` is safe by itself, being +# binary. Font atlases are deliberately not here: they are json we are happy +# to see formatted. +**/draco*/ +**/realism-effects/ +**/*.gltf diff --git a/.syncpackrc.json b/.syncpackrc.json index bc354e7f..c8d912f6 100644 --- a/.syncpackrc.json +++ b/.syncpackrc.json @@ -2,110 +2,67 @@ "versionGroups": [ { "label": "React is pinned for all examples (19.x is required by @react-three/fiber 9)", - "packages": [ - "@example/**" - ], - "dependencies": [ - "react", - "react-dom" - ], + "packages": ["@example/**"], + "dependencies": ["react", "react-dom"], "pinVersion": "19.2.8" }, { "label": "React types are pinned for all examples", - "packages": [ - "@example/**" - ], - "dependencies": [ - "@types/react" - ], + "packages": ["@example/**"], + "dependencies": ["@types/react"], "pinVersion": "19.2.17" }, { "label": "React DOM types are pinned for all examples", - "packages": [ - "@example/**" - ], - "dependencies": [ - "@types/react-dom" - ], + "packages": ["@example/**"], + "dependencies": ["@types/react-dom"], "pinVersion": "19.2.3" }, { "label": "three is pinned: the @splinetool/loader patch is keyed to r165, and postprocessing 6.36.6 needs < 0.173", - "packages": [ - "@example/**" - ], - "dependencies": [ - "three" - ], + "packages": ["@example/**"], + "dependencies": ["three"], "pinVersion": "0.165.0" }, { "label": "@react-three/fiber is pinned: 9.x is the React 19 line, and a caret could raise the three floor", - "packages": [ - "@example/**" - ], - "dependencies": [ - "@react-three/fiber" - ], + "packages": ["@example/**"], + "dependencies": ["@react-three/fiber"], "pinVersion": "9.6.1" }, { "label": "@react-three/drei is pinned: 10.x is the line matching @react-three/fiber 9", - "packages": [ - "@example/**" - ], - "dependencies": [ - "@react-three/drei" - ], + "packages": ["@example/**"], + "dependencies": ["@react-three/drei"], "pinVersion": "10.7.8" }, { "label": "Update lamina to 1.2.2 for all examples.", - "packages": [ - "@example/**" - ], - "dependencies": [ - "lamina" - ], + "packages": ["@example/**"], + "dependencies": ["lamina"], "pinVersion": "1.2.2" }, { "label": "postprocessing is pinned to the last release accepting three 0.165 (6.36.7+ needs >= 0.174); pnpm.overrides pins the transitive copy too", - "packages": [ - "@example/**" - ], - "dependencies": [ - "postprocessing" - ], + "packages": ["@example/**"], + "dependencies": ["postprocessing"], "pinVersion": "6.36.6" }, { "label": "suspend-react is pinned for all examples (the suspend(promise) pattern requires >= 0.1.3)", - "packages": [ - "@example/**" - ], - "dependencies": [ - "suspend-react" - ], + "packages": ["@example/**"], + "dependencies": ["suspend-react"], "pinVersion": "0.1.3" }, { "label": "@splinetool/loader is pinned to the last release that parses the repo's 2022-era .splinecode scenes (0.9.156+ silently fall back to a default gray material or crash; the three-r165 pnpm patch is keyed to this exact version)", - "packages": [ - "@example/**" - ], - "dependencies": [ - "@splinetool/loader" - ], + "packages": ["@example/**"], + "dependencies": ["@splinetool/loader"], "pinVersion": "0.9.153" }, { "label": "Examples must use identical versions of shared dependencies", - "packages": [ - "@example/**" - ], + "packages": ["@example/**"], "dependencies": [ "@react-three/postprocessing", "@react-three/rapier", @@ -144,12 +101,8 @@ }, { "label": "Workspace deps use workspace protocol", - "dependencies": [ - "@example/**" - ], - "dependencyTypes": [ - "prod" - ], + "dependencies": ["@example/**"], + "dependencyTypes": ["prod"], "pinVersion": "workspace:*" } ] diff --git a/apps/website/tsconfig.json b/apps/website/tsconfig.json index e7ff3a26..705f5ce5 100644 --- a/apps/website/tsconfig.json +++ b/apps/website/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "ES2017", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -23,9 +19,7 @@ } ], "paths": { - "@/*": [ - "./*" - ] + "@/*": ["./*"] } }, "include": [ @@ -35,7 +29,5 @@ ".next/types/**/*.ts", ".next/dev/types/**/*.ts" ], - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } diff --git a/examples/aquarium/.prettierrc b/examples/aquarium/.prettierrc deleted file mode 100755 index 6043ce2c..00000000 --- a/examples/aquarium/.prettierrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "printWidth": 160, - "tabWidth": 2, - "useTabs": false, - "semi": false, - "singleQuote": true, - "trailingComma": "none", - "bracketSpacing": true, - "jsxBracketSameLine": true -} \ No newline at end of file diff --git a/examples/aquarium/src/App.jsx b/examples/aquarium/src/App.jsx index e313656c..b8ad19f9 100755 --- a/examples/aquarium/src/App.jsx +++ b/examples/aquarium/src/App.jsx @@ -1,59 +1,137 @@ -import { useLayoutEffect, useEffect, useRef } from 'react' -import { Canvas, useFrame } from '@react-three/fiber' -import { useMask, useGLTF, useAnimations, Float, Instance, Instances, CameraControls } from '@react-three/drei' -import { Lightformer, Environment, RandomizedLight, AccumulativeShadows, MeshTransmissionMaterial } from '@react-three/drei' +import { useLayoutEffect, useEffect, useRef } from "react"; +import { Canvas, useFrame } from "@react-three/fiber"; +import { + useMask, + useGLTF, + useAnimations, + Float, + Instance, + Instances, + CameraControls, +} from "@react-three/drei"; +import { + Lightformer, + Environment, + RandomizedLight, + AccumulativeShadows, + MeshTransmissionMaterial, +} from "@react-three/drei"; -import shapesModel from './shapes-transformed.glb?url' -import turtleModel from './model_52a_-_kemps_ridley_sea_turtle_no_id-transformed.glb?url' +import shapesModel from "./shapes-transformed.glb?url"; +import turtleModel from "./model_52a_-_kemps_ridley_sea_turtle_no_id-transformed.glb?url"; export default function App({ spheres }) { return ( - - + + {/** Glass aquarium */} - + {spheres.map(([scale, color, speed, position], index) => ( - + ))} {/** Soft shadows */} - - + + {/** Custom environment map */} - + {[2, 0, 2, 0, 2, 0, 2, 0].map((x, i) => ( - + ))} - - + + - + - ) + ); } function Aquarium({ children, ...props }) { - const ref = useRef() - const { nodes } = useGLTF(shapesModel) - const stencil = useMask(1, false) + const ref = useRef(); + const { nodes } = useGLTF(shapesModel); + const stencil = useMask(1, false); useLayoutEffect(() => { // Apply stencil to all contents - ref.current.traverse((child) => child.material && Object.assign(child.material, { ...stencil })) - }, []) + ref.current.traverse( + (child) => + child.material && Object.assign(child.material, { ...stencil }), + ); + }, []); return ( - + {children} - ) + ); } -function Sphere({ position, scale = 1, speed = 0.1, color = 'white' }) { +function Sphere({ position, scale = 1, speed = 0.1, color = "white" }) { return ( - ) + ); } /* @@ -88,12 +166,14 @@ Source: https://sketchfab.com/3d-models/model-52a-kemps-ridley-sea-turtle-no-id- Title: Model 52A - Kemps Ridley Sea Turtle (no ID) */ function Turtle(props) { - const { scene, animations } = useGLTF(turtleModel) - const { actions, mixer } = useAnimations(animations, scene) + const { scene, animations } = useGLTF(turtleModel); + const { actions, mixer } = useAnimations(animations, scene); useEffect(() => { - mixer.timeScale = 0.5 - actions['Swim Cycle'].play() - }, []) - useFrame((state) => (scene.rotation.z = Math.sin(state.clock.elapsedTime / 4) / 2)) - return + mixer.timeScale = 0.5; + actions["Swim Cycle"].play(); + }, []); + useFrame( + (state) => (scene.rotation.z = Math.sin(state.clock.elapsedTime / 4) / 2), + ); + return ; } diff --git a/examples/aquarium/src/index.jsx b/examples/aquarium/src/index.jsx index 090b3094..4e768027 100755 --- a/examples/aquarium/src/index.jsx +++ b/examples/aquarium/src/index.jsx @@ -1,41 +1,62 @@ -import { createRoot } from 'react-dom/client' -import './styles.css' -import App from './App' -import { Logo } from '@pmndrs/branding' +import { createRoot } from "react-dom/client"; +import "./styles.css"; +import App from "./App"; +import { Logo } from "@pmndrs/branding"; function Overlay() { return ( -
- - +
+ + pmnd.rs
dev collective
-
ok —
-
29/01/2023
+
ok —
+
+ 29/01/2023 +
- ) + ); } -createRoot(document.getElementById('root')).render( +createRoot(document.getElementById("root")).render( <> - -) + , +); diff --git a/examples/aquarium/src/styles.css b/examples/aquarium/src/styles.css index b4e7a85e..0fa5c1c2 100755 --- a/examples/aquarium/src/styles.css +++ b/examples/aquarium/src/styles.css @@ -1,4 +1,4 @@ -@import url('https://rsms.me/inter/inter.css'); +@import url("https://rsms.me/inter/inter.css"); * { box-sizing: border-box; @@ -15,7 +15,7 @@ body, body { background: #c6e5db; - font-family: 'Inter'; + font-family: "Inter"; } a { diff --git a/examples/aquarium/vite.config.ts b/examples/aquarium/vite.config.ts index eca6e2f3..081c8d9f 100644 --- a/examples/aquarium/vite.config.ts +++ b/examples/aquarium/vite.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; export default defineConfig({ - plugins: [react()] -}) + plugins: [react()], +}); diff --git a/examples/arkanoid-under-60-loc/.prettierrc b/examples/arkanoid-under-60-loc/.prettierrc deleted file mode 100755 index 943d25e7..00000000 --- a/examples/arkanoid-under-60-loc/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "printWidth": 160, - "tabWidth": 2, - "useTabs": false, - "semi": false, - "singleQuote": false, - "trailingComma": "all", - "bracketSpacing": true, - "jsxBracketSameLine": true, - "fluid": false -} \ No newline at end of file diff --git a/examples/arkanoid-under-60-loc/src/App.jsx b/examples/arkanoid-under-60-loc/src/App.jsx index 0675737c..75af7a3c 100755 --- a/examples/arkanoid-under-60-loc/src/App.jsx +++ b/examples/arkanoid-under-60-loc/src/App.jsx @@ -1,7 +1,7 @@ -import * as THREE from "three" -import { Canvas, useFrame, useThree } from "@react-three/fiber" -import { CuboidCollider, Physics, RigidBody } from "@react-three/rapier" -import { useRef } from "react" +import * as THREE from "three"; +import { Canvas, useFrame, useThree } from "@react-three/fiber"; +import { CuboidCollider, Physics, RigidBody } from "@react-three/rapier"; +import { useRef } from "react"; export const App = () => ( @@ -14,12 +14,15 @@ export const App = () => ( -) +); function Ball() { - const ref = useRef() - const { viewport } = useThree() - const onCollisionEnter = () => (ref.current.setTranslation({ x: 0, y: 0, z: 0 }), ref.current.setLinvel({ x: 0, y: 10, z: 0 })) + const ref = useRef(); + const { viewport } = useThree(); + const onCollisionEnter = () => ( + ref.current.setTranslation({ x: 0, y: 0, z: 0 }), + ref.current.setLinvel({ x: 0, y: 10, z: 0 }) + ); return ( <> @@ -28,28 +31,48 @@ function Ball() { - + - ) + ); } const Enemy = ({ position, color }) => ( - + -) +); -function Paddle({ euler = new THREE.Euler(), quaternion = new THREE.Quaternion() }) { - const ref = useRef() +function Paddle({ + euler = new THREE.Euler(), + quaternion = new THREE.Quaternion(), +}) { + const ref = useRef(); useFrame(({ pointer, viewport }) => { - ref.current.setTranslation({ x: (pointer.x * viewport.width) / 2, y: -viewport.height / 3, z: 0 }) - ref.current.setRotation(quaternion.setFromEuler(euler.set(0, 0, (pointer.x * Math.PI) / 10))) - }) + ref.current.setTranslation({ + x: (pointer.x * viewport.width) / 2, + y: -viewport.height / 3, + z: 0, + }); + ref.current.setRotation( + quaternion.setFromEuler(euler.set(0, 0, (pointer.x * Math.PI) / 10)), + ); + }); return ( @@ -57,5 +80,5 @@ function Paddle({ euler = new THREE.Euler(), quaternion = new THREE.Quaternion() - ) + ); } diff --git a/examples/arkanoid-under-60-loc/src/index.jsx b/examples/arkanoid-under-60-loc/src/index.jsx index 5178fc32..b156ac5e 100755 --- a/examples/arkanoid-under-60-loc/src/index.jsx +++ b/examples/arkanoid-under-60-loc/src/index.jsx @@ -1,5 +1,5 @@ -import { createRoot } from "react-dom/client" -import { App } from "./App" -import "./styles.css" +import { createRoot } from "react-dom/client"; +import { App } from "./App"; +import "./styles.css"; -createRoot(document.getElementById("root")).render() +createRoot(document.getElementById("root")).render(); diff --git a/examples/arkanoid-under-60-loc/src/styles.css b/examples/arkanoid-under-60-loc/src/styles.css index a51f2eaa..7d9559be 100755 --- a/examples/arkanoid-under-60-loc/src/styles.css +++ b/examples/arkanoid-under-60-loc/src/styles.css @@ -26,10 +26,25 @@ body { position: fixed; overflow: hidden; overscroll-behavior-y: none; - font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif; + font-family: + -apple-system, + BlinkMacSystemFont, + avenir next, + avenir, + helvetica neue, + helvetica, + ubuntu, + roboto, + noto, + segoe ui, + arial, + sans-serif; color: black; background: #272730; - cursor: url("https://github.com/chenglou/react-motion/raw/master/demos/demo8-draggable-list/cursor.png") 39 39, auto; + cursor: + url("https://github.com/chenglou/react-motion/raw/master/demos/demo8-draggable-list/cursor.png") + 39 39, + auto; image-rendering: pixelated; } diff --git a/examples/arkanoid-under-60-loc/vite.config.ts b/examples/arkanoid-under-60-loc/vite.config.ts index ea1889ae..081c8d9f 100644 --- a/examples/arkanoid-under-60-loc/vite.config.ts +++ b/examples/arkanoid-under-60-loc/vite.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from "vite" -import react from "@vitejs/plugin-react" +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; export default defineConfig({ plugins: [react()], -}) +}); diff --git a/examples/arkanoid/.prettierrc b/examples/arkanoid/.prettierrc deleted file mode 100755 index 943d25e7..00000000 --- a/examples/arkanoid/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "printWidth": 160, - "tabWidth": 2, - "useTabs": false, - "semi": false, - "singleQuote": false, - "trailingComma": "all", - "bracketSpacing": true, - "jsxBracketSameLine": true, - "fluid": false -} \ No newline at end of file diff --git a/examples/arkanoid/pmndrs.json b/examples/arkanoid/pmndrs.json index 9080e492..aac52ee7 100644 --- a/examples/arkanoid/pmndrs.json +++ b/examples/arkanoid/pmndrs.json @@ -6,6 +6,12 @@ "authors": ["Paul Henschel"], "publishedAt": "2020-05-25", "source": "https://codesandbox.io/s/2yqpv", - "libraries": ["@react-spring/three", "@react-three/cannon", "@react-three/drei", "@react-three/fiber", "zustand"], + "libraries": [ + "@react-spring/three", + "@react-three/cannon", + "@react-three/drei", + "@react-three/fiber", + "zustand" + ], "assets": [] } diff --git a/examples/arkanoid/src/App.jsx b/examples/arkanoid/src/App.jsx index 70114f1c..990a0a8f 100755 --- a/examples/arkanoid/src/App.jsx +++ b/examples/arkanoid/src/App.jsx @@ -1,189 +1,313 @@ -import * as THREE from "three" -import React, { Suspense, useEffect, useLayoutEffect, useCallback, useRef, useMemo, useState } from "react" -import { Canvas, useFrame, useThree, useLoader, createPortal } from "@react-three/fiber" -import { Physics, useSphere, useBox, usePlane } from "@react-three/cannon" -import { useAspect, OrthographicCamera, Sphere, Box, Plane, Text, Extrude } from "@react-three/drei" -import { a, useSpring } from "@react-spring/three" -import Effects from "./Effects" -import bgImg from "./resources/bg.jpg" -import { useStore } from "./store" +import * as THREE from "three"; +import React, { + Suspense, + useEffect, + useLayoutEffect, + useCallback, + useRef, + useMemo, + useState, +} from "react"; +import { + Canvas, + useFrame, + useThree, + useLoader, + createPortal, +} from "@react-three/fiber"; +import { Physics, useSphere, useBox, usePlane } from "@react-three/cannon"; +import { + useAspect, + OrthographicCamera, + Sphere, + Box, + Plane, + Text, + Extrude, +} from "@react-three/drei"; +import { a, useSpring } from "@react-spring/three"; +import Effects from "./Effects"; +import bgImg from "./resources/bg.jpg"; +import { useStore } from "./store"; function useCollide(onColide) { - const contact = useStore((state) => state.contact) - const [{ impact }, api] = useSpring({ impact: 0 }, []) + const contact = useStore((state) => state.contact); + const [{ impact }, api] = useSpring({ impact: 0 }, []); const event = useCallback((e) => { - api.start({ impact: 10, config: { immediate: true } }) - requestAnimationFrame(() => api.start({ impact: 0 })) - if (onColide) onColide(e) - contact(e) - }, []) - return [impact, event] + api.start({ impact: 10, config: { immediate: true } }); + requestAnimationFrame(() => api.start({ impact: 0 })); + if (onColide) onColide(e); + contact(e); + }, []); + return [impact, event]; } function Paddle() { - const { viewport } = useThree() - const { width, height } = viewport - const [impact, onCollide] = useCollide() - const [ref, api] = useBox(() => ({ type: "Kinematic", args: [2.25, 0.75, 1], onCollide })) + const { viewport } = useThree(); + const { width, height } = viewport; + const [impact, onCollide] = useCollide(); + const [ref, api] = useBox(() => ({ + type: "Kinematic", + args: [2.25, 0.75, 1], + onCollide, + })); useFrame((state) => { - api.position.set(state.mouse.x * (width / 2 + 2), -height / 2.5, 0) - api.rotation.set(0, 0, (state.mouse.x * Math.PI) / 5) - }) + api.position.set(state.mouse.x * (width / 2 + 2), -height / 2.5, 0); + api.rotation.set(0, 0, (state.mouse.x * Math.PI) / 5); + }); return ( - + - ) + ); } function Ball() { - const [ref, api] = useSphere(() => ({ mass: 1, args: [0.4], position: [0, 3, 0], velocity: [0, 5, 0] })) - const restart = useStore((state) => state.restart) + const [ref, api] = useSphere(() => ({ + mass: 1, + args: [0.4], + position: [0, 3, 0], + velocity: [0, 5, 0], + })); + const restart = useStore((state) => state.restart); useEffect(() => { if (restart) { - api.position.set(0, 3, 0) - api.velocity.set(0, 5, 0) - api.angularVelocity.set(0, 0, 0) + api.position.set(0, 3, 0); + api.velocity.set(0, 5, 0); + api.angularVelocity.set(0, 0, 0); } - }, [restart]) + }, [restart]); return ( - ) + ); } -function Enemy({ long = false, right = false, y = 2, speed = 0.1, color = "hotpink" }) { - const { viewport } = useThree() - const { width } = viewport - const [impact, onCollide] = useCollide() - const [ref, api] = useBox(() => ({ type: "Static", args: [long ? 2.25 : 1.25, 0.75, 1], rotation: [0, 0, right ? 0.1 : -0.1], onCollide })) - let initial = right ? width : -width - let x = initial +function Enemy({ + long = false, + right = false, + y = 2, + speed = 0.1, + color = "hotpink", +}) { + const { viewport } = useThree(); + const { width } = viewport; + const [impact, onCollide] = useCollide(); + const [ref, api] = useBox(() => ({ + type: "Static", + args: [long ? 2.25 : 1.25, 0.75, 1], + rotation: [0, 0, right ? 0.1 : -0.1], + onCollide, + })); + let initial = right ? width : -width; + let x = initial; useFrame((state, delta) => { - api.position.set((x = right ? x - speed : x + speed), y, 0) - if (right ? x + 2 < -width / 2 : x - 2 > width / 2) x = initial - }) + api.position.set((x = right ? x - speed : x + speed), y, 0); + if (right ? x + 2 < -width / 2 : x - 2 > width / 2) x = initial; + }); return ( - + - ) + ); } function Enemies() { - const enemies = useStore((state) => state.enemies) - return enemies.map((props, i) => ) + const enemies = useStore((state) => state.enemies); + return enemies.map((props, i) => ); } function Walls() { - const reset = useStore((state) => state.reset) - const { viewport } = useThree() - const { width, height } = viewport - const [, onCollide] = useCollide() - const [, apiLeft] = usePlane(() => ({ type: "Static", rotation: [-Math.PI / 2, Math.PI / 2, 0], onCollide })) - const [, apiRight] = usePlane(() => ({ type: "Static", rotation: [Math.PI / 2, -Math.PI / 2, 0], onCollide })) - const [, apiBottom] = usePlane(() => ({ type: "Static", rotation: [-Math.PI / 2, 0, 0], onCollide: reset })) + const reset = useStore((state) => state.reset); + const { viewport } = useThree(); + const { width, height } = viewport; + const [, onCollide] = useCollide(); + const [, apiLeft] = usePlane(() => ({ + type: "Static", + rotation: [-Math.PI / 2, Math.PI / 2, 0], + onCollide, + })); + const [, apiRight] = usePlane(() => ({ + type: "Static", + rotation: [Math.PI / 2, -Math.PI / 2, 0], + onCollide, + })); + const [, apiBottom] = usePlane(() => ({ + type: "Static", + rotation: [-Math.PI / 2, 0, 0], + onCollide: reset, + })); useLayoutEffect(() => { - apiBottom.position.set(0, -height * 2, 0) - apiLeft.position.set(-width / 2 - 2, 0, 0) - apiRight.position.set(width / 2 + 2, 0, 0) - }, [width, height]) - return null + apiBottom.position.set(0, -height * 2, 0); + apiLeft.position.set(-width / 2 - 2, 0, 0); + apiRight.position.set(width / 2 + 2, 0, 0); + }, [width, height]); + return null; } function Bg() { - const texture = useLoader(THREE.TextureLoader, bgImg) - const scale = useAspect(1286, 574, 1.5) - return + const texture = useLoader(THREE.TextureLoader, bgImg); + const scale = useAspect(1286, 574, 1.5); + return ; } const StyledText = React.forwardRef( - ({ children, fontSize = 1, offset = 0.25, anchorX = "center", anchorY = "middle", textAlign = "justify", lineHeight = 0.75, ...props }, ref) => { - const { viewport } = useThree() - const textProps = { children, anchorX, anchorY, maxWidth: viewport.width, lineHeight, fontSize, "material-depthTest": false } + ( + { + children, + fontSize = 1, + offset = 0.25, + anchorX = "center", + anchorY = "middle", + textAlign = "justify", + lineHeight = 0.75, + ...props + }, + ref, + ) => { + const { viewport } = useThree(); + const textProps = { + children, + anchorX, + anchorY, + maxWidth: viewport.width, + lineHeight, + fontSize, + "material-depthTest": false, + }; return ( - ) + ); }, -) +); function Startup() { - const ref = useRef() + const ref = useRef(); useFrame((state) => { - const s = 1 + 0.01 * (1 + Math.sin(state.clock.getElapsedTime() * 2)) * 2 - if (ref.current) ref.current.scale.set(s, s, s) - }) - return + const s = 1 + 0.01 * (1 + Math.sin(state.clock.getElapsedTime() * 2)) * 2; + if (ref.current) ref.current.scale.set(s, s, s); + }); + return ( + + ); } function Heart(props) { const extrusionProps = useMemo(() => { - const heartShape = new THREE.Shape() - heartShape.moveTo(0.25, 0.25) - heartShape.bezierCurveTo(0.25, 0.25, 0.2, 0, 0, 0) - heartShape.bezierCurveTo(-0.3, 0, -0.3, 0.35, -0.3, 0.35) - heartShape.bezierCurveTo(-0.3, 0.55, -0.1, 0.77, 0.25, 0.95) - heartShape.bezierCurveTo(0.6, 0.77, 0.8, 0.55, 0.8, 0.35) - heartShape.bezierCurveTo(0.8, 0.35, 0.8, 0, 0.5, 0) - heartShape.bezierCurveTo(0.35, 0, 0.25, 0.25, 0.25, 0.25) - return [heartShape, { depth: 0, bevelEnabled: false }] - }, []) + const heartShape = new THREE.Shape(); + heartShape.moveTo(0.25, 0.25); + heartShape.bezierCurveTo(0.25, 0.25, 0.2, 0, 0, 0); + heartShape.bezierCurveTo(-0.3, 0, -0.3, 0.35, -0.3, 0.35); + heartShape.bezierCurveTo(-0.3, 0.55, -0.1, 0.77, 0.25, 0.95); + heartShape.bezierCurveTo(0.6, 0.77, 0.8, 0.55, 0.8, 0.35); + heartShape.bezierCurveTo(0.8, 0.35, 0.8, 0, 0.5, 0); + heartShape.bezierCurveTo(0.35, 0, 0.25, 0.25, 0.25, 0.25); + return [heartShape, { depth: 0, bevelEnabled: false }]; + }, []); return ( - + - ) + ); } function Status() { - const [virtualScene] = useState(() => new THREE.Scene()) - const virtualCamera = useRef() - const points = useStore((state) => state.points) - const { gl, scene, camera, viewport } = useThree() - const { width, height } = viewport.getCurrentViewport(virtualCamera.current) + const [virtualScene] = useState(() => new THREE.Scene()); + const virtualCamera = useRef(); + const points = useStore((state) => state.points); + const { gl, scene, camera, viewport } = useThree(); + const { width, height } = viewport.getCurrentViewport(virtualCamera.current); useFrame(() => { - gl.autoClear = true - gl.render(scene, camera) - gl.autoClear = false - gl.clearDepth() - gl.render(virtualScene, virtualCamera.current) - }, 2) + gl.autoClear = true; + gl.render(scene, camera); + gl.autoClear = false; + gl.clearDepth(); + gl.render(virtualScene, virtualCamera.current); + }, 2); return createPortal( <> - + - + , virtualScene, - ) + ); } function Perspective() { return useFrame((state) => { - state.camera.position.x = THREE.MathUtils.lerp(state.camera.position.x, state.mouse.x * 2, 0.1) - state.camera.updateProjectionMatrix() - }) + state.camera.position.x = THREE.MathUtils.lerp( + state.camera.position.x, + state.mouse.x * 2, + 0.1, + ); + state.camera.updateProjectionMatrix(); + }); } export default function App() { - const startup = useStore((state) => state.startup) - const start = useStore((state) => state.start) + const startup = useStore((state) => state.startup); + const start = useStore((state) => state.start); return (
- + - + {!startup && ( - + @@ -199,5 +323,5 @@ export default function App() {
- ) + ); } diff --git a/examples/arkanoid/src/Effects.jsx b/examples/arkanoid/src/Effects.jsx index 5dc92e92..76df03c2 100755 --- a/examples/arkanoid/src/Effects.jsx +++ b/examples/arkanoid/src/Effects.jsx @@ -1,24 +1,27 @@ -import React, { useRef, useEffect } from "react" -import { extend, useThree, useFrame } from "@react-three/fiber" -import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer" -import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass" -import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass" -import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass" -import { FilmPass } from "three/examples/jsm/postprocessing/FilmPass" -import { ColorCorrectionShader } from "three/examples/jsm/shaders/ColorCorrectionShader" +import React, { useRef, useEffect } from "react"; +import { extend, useThree, useFrame } from "@react-three/fiber"; +import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer"; +import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass"; +import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass"; +import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass"; +import { FilmPass } from "three/examples/jsm/postprocessing/FilmPass"; +import { ColorCorrectionShader } from "three/examples/jsm/shaders/ColorCorrectionShader"; -extend({ EffectComposer, ShaderPass, RenderPass, UnrealBloomPass, FilmPass }) +extend({ EffectComposer, ShaderPass, RenderPass, UnrealBloomPass, FilmPass }); export default function Effects() { - const composer = useRef() - const { scene, gl, size, camera } = useThree() - useEffect(() => void composer.current.setSize(size.width, size.height), [size]) + const composer = useRef(); + const { scene, gl, size, camera } = useThree(); + useEffect( + () => void composer.current.setSize(size.width, size.height), + [size], + ); useFrame(() => { - gl.autoClear = true - composer.current.render() - gl.autoClear = false - gl.clearDepth() - }, 1) + gl.autoClear = true; + composer.current.render(); + gl.autoClear = false; + gl.clearDepth(); + }, 1); return ( @@ -26,5 +29,5 @@ export default function Effects() { - ) + ); } diff --git a/examples/arkanoid/src/index.jsx b/examples/arkanoid/src/index.jsx index 8e843fb7..34a29b35 100755 --- a/examples/arkanoid/src/index.jsx +++ b/examples/arkanoid/src/index.jsx @@ -1,5 +1,5 @@ -import { createRoot } from "react-dom/client" -import App from "./App" -import "./styles.css" +import { createRoot } from "react-dom/client"; +import App from "./App"; +import "./styles.css"; -createRoot(document.getElementById("root")).render() +createRoot(document.getElementById("root")).render(); diff --git a/examples/arkanoid/src/resources/firasans_regular.json b/examples/arkanoid/src/resources/firasans_regular.json index f0af963e..b9acb47a 100755 --- a/examples/arkanoid/src/resources/firasans_regular.json +++ b/examples/arkanoid/src/resources/firasans_regular.json @@ -1 +1,90 @@ -{"glyphs":{"0":{"ha":775,"x_min":0,"x_max":0,"o":"m 388 943 b 699 464 596 943 699 771 b 388 -17 699 156 596 -17 b 76 464 179 -17 76 156 b 388 943 76 772 179 943 m 388 842 b 210 464 271 842 210 736 b 388 85 210 190 271 85 b 565 464 503 85 565 190 b 388 842 565 736 503 842 "},"1":{"ha":601,"x_min":0,"x_max":0,"o":"m 449 0 l 321 0 l 321 793 l 104 661 l 49 751 l 336 929 l 449 929 "},"2":{"ha":688,"x_min":0,"x_max":0,"o":"m 317 943 b 596 689 488 943 596 833 b 200 107 596 515 485 392 l 618 107 l 603 0 l 54 0 l 54 101 b 463 683 382 443 463 532 b 310 838 463 781 400 838 b 118 736 232 838 181 807 l 35 803 b 317 943 110 896 200 943 "},"3":{"ha":693,"x_min":0,"x_max":0,"o":"m 313 943 b 588 707 490 943 588 832 b 407 493 588 589 511 518 b 617 268 524 482 617 408 b 300 -17 617 106 493 -17 b 21 108 189 -17 92 24 l 97 179 b 296 88 158 115 221 88 b 483 269 413 88 483 160 b 299 436 483 397 411 436 l 229 436 l 244 535 l 292 535 b 460 699 383 535 460 590 b 306 840 460 786 399 840 b 114 757 232 840 176 815 l 47 833 b 313 943 126 907 213 943 "},"4":{"ha":739,"x_min":0,"x_max":0,"o":"m 697 229 l 576 229 l 576 0 l 453 0 l 453 229 l 56 229 l 56 321 l 335 943 l 442 899 l 190 331 l 454 331 l 465 581 l 576 581 l 576 331 l 697 331 "},"5":{"ha":696,"x_min":0,"x_max":0,"o":"m 583 829 l 221 829 l 221 556 b 369 592 269 581 319 592 b 633 297 526 592 633 482 b 311 -17 633 113 504 -17 b 36 101 193 -17 108 28 l 111 175 b 310 88 169 117 229 88 b 500 300 426 88 500 164 b 331 493 500 443 426 493 b 199 461 281 493 243 482 l 99 461 l 99 929 l 601 929 "},"6":{"ha":740,"x_min":0,"x_max":0,"o":"m 428 611 b 685 315 564 611 685 514 b 389 -17 685 113 553 -17 b 76 436 163 -17 76 169 b 428 943 76 740 208 943 b 619 886 503 943 565 922 l 569 801 b 426 842 526 828 478 842 b 204 489 292 842 211 701 b 428 611 265 575 340 611 m 389 85 b 556 311 499 85 556 175 b 406 510 556 458 493 510 b 206 382 322 510 253 458 b 389 85 211 179 265 85 "},"7":{"ha":617,"x_min":0,"x_max":0,"o":"m 575 833 l 228 -14 l 111 25 l 446 825 l 35 825 l 35 929 l 575 929 "},"8":{"ha":765,"x_min":0,"x_max":0,"o":"m 507 499 b 703 249 635 447 703 365 b 381 -17 703 93 569 -17 b 63 246 189 -17 63 93 b 246 488 63 365 132 438 b 101 704 146 538 101 608 b 383 943 101 861 242 943 b 665 708 522 943 665 867 b 507 499 665 614 613 551 m 383 847 b 226 703 288 847 226 796 b 399 538 226 607 292 572 l 422 529 b 540 704 506 576 540 625 b 383 847 540 790 485 847 m 382 85 b 569 247 499 85 569 150 b 367 443 569 347 517 392 l 332 456 b 196 246 240 411 196 347 b 382 85 196 144 267 85 "},"9":{"ha":729,"x_min":0,"x_max":0,"o":"m 360 943 b 660 582 560 943 660 800 b 165 -31 660 197 507 68 l 136 65 b 528 449 369 132 518 239 b 319 338 488 386 414 338 b 63 636 175 338 63 451 b 360 943 63 831 199 943 m 346 438 b 531 553 425 438 485 482 b 363 842 535 763 478 842 b 192 633 254 842 192 767 b 346 438 192 500 256 438 "}},"familyName":"Fira Sans","ascender":1299,"descender":-368,"underlinePosition":-104,"underlineThickness":69,"boundingBox":{"yMin":-490,"xMin":-1050,"yMax":1533,"xMax":1889},"resolution":1000,"original_font_information":{"format":0,"copyright":"Digitized data copyright 2012-2016, The Mozilla Foundation and Telefonica S.A.","fontFamily":"Fira Sans","fontSubfamily":"Regular","uniqueID":"4.203;CTDB;FiraSans-Regular","fullName":"Fira Sans Regular","version":"Version 4.203;PS 004.203;hotconv 1.0.88;makeotf.lib2.5.64775","postScriptName":"FiraSans-Regular","trademark":"Fira Sans is a trademark of The Mozilla Corporation.","manufacturer":"Carrois Corporate GbR & Edenspiekermann AG","designer":"Carrois Corporate & Edenspiekermann AG","manufacturerURL":"http://www.carrois.com","designerURL":"http://www.carrois.com","licence":"Licensed under the Open Font License, version 1.1 or later","licenceURL":"http://scripts.sil.org/OFL"},"cssFontWeight":"normal","cssFontStyle":"normal"} \ No newline at end of file +{ + "glyphs": { + "0": { + "ha": 775, + "x_min": 0, + "x_max": 0, + "o": "m 388 943 b 699 464 596 943 699 771 b 388 -17 699 156 596 -17 b 76 464 179 -17 76 156 b 388 943 76 772 179 943 m 388 842 b 210 464 271 842 210 736 b 388 85 210 190 271 85 b 565 464 503 85 565 190 b 388 842 565 736 503 842 " + }, + "1": { + "ha": 601, + "x_min": 0, + "x_max": 0, + "o": "m 449 0 l 321 0 l 321 793 l 104 661 l 49 751 l 336 929 l 449 929 " + }, + "2": { + "ha": 688, + "x_min": 0, + "x_max": 0, + "o": "m 317 943 b 596 689 488 943 596 833 b 200 107 596 515 485 392 l 618 107 l 603 0 l 54 0 l 54 101 b 463 683 382 443 463 532 b 310 838 463 781 400 838 b 118 736 232 838 181 807 l 35 803 b 317 943 110 896 200 943 " + }, + "3": { + "ha": 693, + "x_min": 0, + "x_max": 0, + "o": "m 313 943 b 588 707 490 943 588 832 b 407 493 588 589 511 518 b 617 268 524 482 617 408 b 300 -17 617 106 493 -17 b 21 108 189 -17 92 24 l 97 179 b 296 88 158 115 221 88 b 483 269 413 88 483 160 b 299 436 483 397 411 436 l 229 436 l 244 535 l 292 535 b 460 699 383 535 460 590 b 306 840 460 786 399 840 b 114 757 232 840 176 815 l 47 833 b 313 943 126 907 213 943 " + }, + "4": { + "ha": 739, + "x_min": 0, + "x_max": 0, + "o": "m 697 229 l 576 229 l 576 0 l 453 0 l 453 229 l 56 229 l 56 321 l 335 943 l 442 899 l 190 331 l 454 331 l 465 581 l 576 581 l 576 331 l 697 331 " + }, + "5": { + "ha": 696, + "x_min": 0, + "x_max": 0, + "o": "m 583 829 l 221 829 l 221 556 b 369 592 269 581 319 592 b 633 297 526 592 633 482 b 311 -17 633 113 504 -17 b 36 101 193 -17 108 28 l 111 175 b 310 88 169 117 229 88 b 500 300 426 88 500 164 b 331 493 500 443 426 493 b 199 461 281 493 243 482 l 99 461 l 99 929 l 601 929 " + }, + "6": { + "ha": 740, + "x_min": 0, + "x_max": 0, + "o": "m 428 611 b 685 315 564 611 685 514 b 389 -17 685 113 553 -17 b 76 436 163 -17 76 169 b 428 943 76 740 208 943 b 619 886 503 943 565 922 l 569 801 b 426 842 526 828 478 842 b 204 489 292 842 211 701 b 428 611 265 575 340 611 m 389 85 b 556 311 499 85 556 175 b 406 510 556 458 493 510 b 206 382 322 510 253 458 b 389 85 211 179 265 85 " + }, + "7": { + "ha": 617, + "x_min": 0, + "x_max": 0, + "o": "m 575 833 l 228 -14 l 111 25 l 446 825 l 35 825 l 35 929 l 575 929 " + }, + "8": { + "ha": 765, + "x_min": 0, + "x_max": 0, + "o": "m 507 499 b 703 249 635 447 703 365 b 381 -17 703 93 569 -17 b 63 246 189 -17 63 93 b 246 488 63 365 132 438 b 101 704 146 538 101 608 b 383 943 101 861 242 943 b 665 708 522 943 665 867 b 507 499 665 614 613 551 m 383 847 b 226 703 288 847 226 796 b 399 538 226 607 292 572 l 422 529 b 540 704 506 576 540 625 b 383 847 540 790 485 847 m 382 85 b 569 247 499 85 569 150 b 367 443 569 347 517 392 l 332 456 b 196 246 240 411 196 347 b 382 85 196 144 267 85 " + }, + "9": { + "ha": 729, + "x_min": 0, + "x_max": 0, + "o": "m 360 943 b 660 582 560 943 660 800 b 165 -31 660 197 507 68 l 136 65 b 528 449 369 132 518 239 b 319 338 488 386 414 338 b 63 636 175 338 63 451 b 360 943 63 831 199 943 m 346 438 b 531 553 425 438 485 482 b 363 842 535 763 478 842 b 192 633 254 842 192 767 b 346 438 192 500 256 438 " + } + }, + "familyName": "Fira Sans", + "ascender": 1299, + "descender": -368, + "underlinePosition": -104, + "underlineThickness": 69, + "boundingBox": { "yMin": -490, "xMin": -1050, "yMax": 1533, "xMax": 1889 }, + "resolution": 1000, + "original_font_information": { + "format": 0, + "copyright": "Digitized data copyright 2012-2016, The Mozilla Foundation and Telefonica S.A.", + "fontFamily": "Fira Sans", + "fontSubfamily": "Regular", + "uniqueID": "4.203;CTDB;FiraSans-Regular", + "fullName": "Fira Sans Regular", + "version": "Version 4.203;PS 004.203;hotconv 1.0.88;makeotf.lib2.5.64775", + "postScriptName": "FiraSans-Regular", + "trademark": "Fira Sans is a trademark of The Mozilla Corporation.", + "manufacturer": "Carrois Corporate GbR & Edenspiekermann AG", + "designer": "Carrois Corporate & Edenspiekermann AG", + "manufacturerURL": "http://www.carrois.com", + "designerURL": "http://www.carrois.com", + "licence": "Licensed under the Open Font License, version 1.1 or later", + "licenceURL": "http://scripts.sil.org/OFL" + }, + "cssFontWeight": "normal", + "cssFontStyle": "normal" +} diff --git a/examples/arkanoid/src/store.js b/examples/arkanoid/src/store.js index 7ab2e533..759281b2 100755 --- a/examples/arkanoid/src/store.js +++ b/examples/arkanoid/src/store.js @@ -1,12 +1,12 @@ -import { create } from "zustand" -import bgAudio from "./resources/sawsquarenoise_-_01_-_Towel_Defence_Splash_Screen.mp3" -import pingAudio from "./resources/select_006.ogg" -import spawnAudio from "./resources/select_007.ogg" +import { create } from "zustand"; +import bgAudio from "./resources/sawsquarenoise_-_01_-_Towel_Defence_Splash_Screen.mp3"; +import pingAudio from "./resources/select_006.ogg"; +import spawnAudio from "./resources/select_007.ogg"; -const ping = new Audio(pingAudio) -const spawn = new Audio(spawnAudio) -const bg = new Audio(bgAudio) -bg.loop = true +const ping = new Audio(pingAudio); +const spawn = new Audio(spawnAudio); +const bg = new Audio(bgAudio); +bg.loop = true; const useStore = create((set) => ({ points: 0, @@ -28,19 +28,20 @@ const useStore = create((set) => ({ { right: false, long: false, y: 5, speed: 0.165, color: "orange" }, ], start: () => { - set({ startup: false }) + set({ startup: false }); //bg.play() - document.body.style.cursor = "none" + document.body.style.cursor = "none"; }, reset: () => { - spawn.play((spawn.currentTime = 0)) - set({ points: 0, restart: true }) - setTimeout(() => set({ restart: false }), 10) + spawn.play((spawn.currentTime = 0)); + set({ points: 0, restart: true }); + setTimeout(() => set({ restart: false }), 10); }, contact: (e) => { - if (e.contact.impactVelocity > 4) set((state) => ({ points: state.points + 1 })) - ping.play((ping.currentTime = 0)) + if (e.contact.impactVelocity > 4) + set((state) => ({ points: state.points + 1 })); + ping.play((ping.currentTime = 0)); }, -})) +})); -export { useStore } +export { useStore }; diff --git a/examples/arkanoid/src/styles.css b/examples/arkanoid/src/styles.css index 25b3a4bc..7483e8d1 100755 --- a/examples/arkanoid/src/styles.css +++ b/examples/arkanoid/src/styles.css @@ -27,10 +27,25 @@ body { position: fixed; overflow: hidden; overscroll-behavior-y: none; - font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif; + font-family: + -apple-system, + BlinkMacSystemFont, + avenir next, + avenir, + helvetica neue, + helvetica, + ubuntu, + roboto, + noto, + segoe ui, + arial, + sans-serif; color: black; background: #272730; - cursor: url("https://github.com/chenglou/react-motion/raw/master/demos/demo8-draggable-list/cursor.png") 39 39, auto; + cursor: + url("https://github.com/chenglou/react-motion/raw/master/demos/demo8-draggable-list/cursor.png") + 39 39, + auto; image-rendering: pixelated; } diff --git a/examples/arkanoid/vite.config.ts b/examples/arkanoid/vite.config.ts index ea1889ae..081c8d9f 100644 --- a/examples/arkanoid/vite.config.ts +++ b/examples/arkanoid/vite.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from "vite" -import react from "@vitejs/plugin-react" +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; export default defineConfig({ plugins: [react()], -}) +}); diff --git a/examples/audio-analyser/.codesandbox/workspace.json b/examples/audio-analyser/.codesandbox/workspace.json index 7b46a7ed..0fb7abbb 100755 --- a/examples/audio-analyser/.codesandbox/workspace.json +++ b/examples/audio-analyser/.codesandbox/workspace.json @@ -1,20 +1,8 @@ { "responsive-preview": { - "Mobile": [ - 320, - 675 - ], - "Tablet": [ - 1024, - 765 - ], - "Desktop": [ - 1400, - 800 - ], - "Desktop HD": [ - 1920, - 1080 - ] + "Mobile": [320, 675], + "Tablet": [1024, 765], + "Desktop": [1400, 800], + "Desktop HD": [1920, 1080] } -} \ No newline at end of file +} diff --git a/examples/audio-analyser/.prettierrc b/examples/audio-analyser/.prettierrc deleted file mode 100755 index 6bf3659b..00000000 --- a/examples/audio-analyser/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "printWidth": 160, - "tabWidth": 2, - "useTabs": false, - "semi": false, - "singleQuote": true, - "trailingComma": "all", - "bracketSpacing": true, - "jsxBracketSameLine": false, - "fluid": false -} \ No newline at end of file diff --git a/examples/audio-analyser/src/App.jsx b/examples/audio-analyser/src/App.jsx index f193d5b6..f3c4d864 100755 --- a/examples/audio-analyser/src/App.jsx +++ b/examples/audio-analyser/src/App.jsx @@ -1,25 +1,30 @@ -import * as THREE from 'three' -import React, { Suspense, useEffect, useRef, useState, useMemo } from 'react' -import { Canvas, useFrame } from '@react-three/fiber' -import { useGLTF, useAnimations, MeshReflectorMaterial, useTexture } from '@react-three/drei' -import useStore from './store' +import * as THREE from "three"; +import React, { Suspense, useEffect, useRef, useState, useMemo } from "react"; +import { Canvas, useFrame } from "@react-three/fiber"; +import { + useGLTF, + useAnimations, + MeshReflectorMaterial, + useTexture, +} from "@react-three/drei"; +import useStore from "./store"; -import bustModel from './bust.glb?url' -import explosionModel from './explosion.glb?url' +import bustModel from "./bust.glb?url"; +import explosionModel from "./explosion.glb?url"; -import groundTex1 from './SurfaceImperfections003_1K_var1.jpg' -import groundTex2 from './SurfaceImperfections003_1K_Normal.jpg' +import groundTex1 from "./SurfaceImperfections003_1K_var1.jpg"; +import groundTex2 from "./SurfaceImperfections003_1K_Normal.jpg"; -const HPI = Math.PI / 2 -const vec = new THREE.Vector3() -const obj = new THREE.Object3D() -const red = new THREE.Color('#900909') +const HPI = Math.PI / 2; +const vec = new THREE.Vector3(); +const obj = new THREE.Object3D(); +const red = new THREE.Color("#900909"); export default function App(props) { return ( - - + + @@ -37,56 +42,87 @@ export default function App(props) { - ) + ); } function Bust() { - const ref = useRef() - const time = useRef(0) - const { scene, animations, materials } = useGLTF(bustModel) - const { actions, mixer } = useAnimations(animations, ref) - const { drums } = useStore((state) => state.audio) - const track = useStore((state) => state.track) + const ref = useRef(); + const time = useRef(0); + const { scene, animations, materials } = useGLTF(bustModel); + const { actions, mixer } = useAnimations(animations, ref); + const { drums } = useStore((state) => state.audio); + const track = useStore((state) => state.track); // Play all actions (the fragments flying off) - useEffect(() => Object.keys(actions).forEach((key) => actions[key].play()), []) + useEffect( + () => Object.keys(actions).forEach((key) => actions[key].play()), + [], + ); // Control the exploding statue and the inner materials color useFrame((_) => { - mixer.timeScale = track.synthonly ? 0.125 : 1 - if (!track.synthonly) mixer.setTime((time.current = THREE.MathUtils.lerp(time.current, track.kicks * 1.25, track.kicks === 0 ? 0.25 : 0.15))) - materials.inner.color.copy(red).multiplyScalar((drums.avg * drums.gain) / 30) - }) - return + mixer.timeScale = track.synthonly ? 0.125 : 1; + if (!track.synthonly) + mixer.setTime( + (time.current = THREE.MathUtils.lerp( + time.current, + track.kicks * 1.25, + track.kicks === 0 ? 0.25 : 0.15, + )), + ); + materials.inner.color + .copy(red) + .multiplyScalar((drums.avg * drums.gain) / 30); + }); + return ( + + ); } function Explosion({ beat, ...props }) { - const [state] = useState({ size: 0, signal: 0 }) - const sceneRef = useRef() - const instance = useRef() - const sphere = useRef() + const [state] = useState({ size: 0, signal: 0 }); + const sceneRef = useRef(); + const instance = useRef(); + const sphere = useRef(); // The GLTF only contains a point-cloud and baked keyframes for the explosion - const { scene: originalScene, animations } = useGLTF(explosionModel) - const scene = useMemo(() => originalScene.clone(true), [originalScene]) - const { actions, mixer } = useAnimations(animations, sceneRef) - const { drums, snare } = useStore((state) => state.audio) - const track = useStore((state) => state.track) - mixer.timeScale = 2 + const { scene: originalScene, animations } = useGLTF(explosionModel); + const scene = useMemo(() => originalScene.clone(true), [originalScene]); + const { actions, mixer } = useAnimations(animations, sceneRef); + const { drums, snare } = useStore((state) => state.audio); + const track = useStore((state) => state.track); + mixer.timeScale = 2; // Can reset and play all actions const play = () => Object.keys(actions).forEach((key) => { - actions[key].setLoop(THREE.LoopOnce).stop().reset() - actions[key].play() - }) + actions[key].setLoop(THREE.LoopOnce).stop().reset(); + actions[key].play(); + }); // Control the sphere and the sparks useFrame(() => { - if (drums.signal && track.kicks - 1 === beat && drums.gain) play((state.size = 1)) - if (snare.signal) state.size = 0 - sphere.current.scale.lerp(vec.set(state.size * drums.gain, state.size * drums.gain, state.size * drums.gain), 0.2) - sphere.current.children[0].intensity = drums.avg * drums.gain * 10 * Math.PI + if (drums.signal && track.kicks - 1 === beat && drums.gain) + play((state.size = 1)); + if (snare.signal) state.size = 0; + sphere.current.scale.lerp( + vec.set( + state.size * drums.gain, + state.size * drums.gain, + state.size * drums.gain, + ), + 0.2, + ); + sphere.current.children[0].intensity = + drums.avg * drums.gain * 10 * Math.PI; // This code transforms the empty GLTF nodes into a single drawcall via instancing - sceneRef.current.children.forEach((node, i) => instance.current.setMatrixAt(i, node.matrix)) - instance.current.visible = !!drums.gain - instance.current.instanceMatrix.needsUpdate = true - }) + sceneRef.current.children.forEach((node, i) => + instance.current.setMatrixAt(i, node.matrix), + ); + instance.current.visible = !!drums.gain; + instance.current.instanceMatrix.needsUpdate = true; + }); return ( @@ -96,40 +132,47 @@ function Explosion({ beat, ...props }) { - + - ) + ); } function Graph(props) { - const { synth } = useStore((state) => state.audio) - const ref = useRef() + const { synth } = useStore((state) => state.audio); + const ref = useRef(); useFrame(() => { for (let i = 0; i < 64; i++) { - obj.position.set(i * 0.04, synth.data[i] / 1000, 0) - obj.updateMatrix() - ref.current.setMatrixAt(i, obj.matrix) + obj.position.set(i * 0.04, synth.data[i] / 1000, 0); + obj.updateMatrix(); + ref.current.setMatrixAt(i, obj.matrix); } - ref.current.instanceMatrix.needsUpdate = true - }) + ref.current.instanceMatrix.needsUpdate = true; + }); return ( - ) + ); } function DancingDot() { - const { drums, snare } = useStore((state) => state.audio) - const dot = useRef() + const { drums, snare } = useStore((state) => state.audio); + const dot = useRef(); useFrame((_) => - dot.current.rotation.set(Math.sin(_.clock.elapsedTime * 2) / 10 + (drums.avg * drums.gain) / 100, _.clock.elapsedTime + (snare.avg * snare.gain) / 100, 0), - ) + dot.current.rotation.set( + Math.sin(_.clock.elapsedTime * 2) / 10 + (drums.avg * drums.gain) / 100, + _.clock.elapsedTime + (snare.avg * snare.gain) / 100, + 0, + ), + ); return ( @@ -137,28 +180,39 @@ function DancingDot() { - ) + ); } function Ground() { - const [floor, normal] = useTexture([groundTex1, groundTex2]) + const [floor, normal] = useTexture([groundTex1, groundTex2]); return ( - + - ) + ); } function Intro() { - const clicked = useStore((state) => state.clicked) - const api = useStore((state) => state.api) - useEffect(() => api.loaded(), []) + const clicked = useStore((state) => state.clicked); + const api = useStore((state) => state.api); + useEffect(() => api.loaded(), []); // Zoom in camera when user has pressed start return useFrame((state) => { if (clicked) { - state.camera.position.lerp(vec.set(-2 + state.mouse.x, 2, 4.5), 0.05) - state.camera.lookAt(0, 0, 0) + state.camera.position.lerp(vec.set(-2 + state.mouse.x, 2, 4.5), 0.05); + state.camera.lookAt(0, 0, 0); } - }) + }); } diff --git a/examples/audio-analyser/src/index.jsx b/examples/audio-analyser/src/index.jsx index 2f2e1b2f..202b13ac 100755 --- a/examples/audio-analyser/src/index.jsx +++ b/examples/audio-analyser/src/index.jsx @@ -1,28 +1,32 @@ -import { createRoot } from 'react-dom/client' -import React from 'react' -import App from './App' -import { Footer } from '@pmndrs/branding' -import useStore from './store' -import './styles.css' +import { createRoot } from "react-dom/client"; +import React from "react"; +import App from "./App"; +import { Footer } from "@pmndrs/branding"; +import useStore from "./store"; +import "./styles.css"; function Overlay() { - const loaded = useStore((state) => state.loaded) - const clicked = useStore((state) => state.clicked) - const api = useStore((state) => state.api) + const loaded = useStore((state) => state.loaded); + const clicked = useStore((state) => state.clicked); + const api = useStore((state) => state.api); return ( <> -
+
loaded && api.start()}> {!loaded ? ( - 'loading' + "loading" ) : ( <> - this sandbox needs + this sandbox needs
- user interaction for audio + + user interaction for audio +
- click to continue + click to continue )} @@ -31,16 +35,24 @@ function Overlay() {