├── .changeset ├── config.json ├── curvy-llamas-pump.md ├── honest-maps-dance.md ├── lucky-dragons-retire.md ├── olive-coats-perform.md ├── red-elephants-wave.md ├── sixty-llamas-boil.md ├── spotty-students-lay.md ├── stale-melons-double.md ├── stale-moles-float.md ├── thin-mails-press.md ├── two-jokes-wait.md └── yellow-pens-switch.md ├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── release.yml │ ├── sponsors.yml │ └── tests.yml ├── .gitignore ├── .npmrc ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── apps ├── material-composer-examples │ ├── CHANGELOG.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── models │ │ │ ├── ImphenziaPalette01-256-GradientX4-Emission.png │ │ │ ├── ImphenziaPalette01-256-GradientX4.png │ │ │ ├── spaceship26.bin │ │ │ ├── spaceship26.gltf │ │ │ ├── spaceship26_mod.bin │ │ │ └── spaceship26_mod.gltf │ ├── src │ │ ├── AdaptiveResolution.tsx │ │ ├── App.tsx │ │ ├── examples │ │ │ ├── CombinedModules.tsx │ │ │ ├── Fireball.tsx │ │ │ ├── HelloWorld.tsx │ │ │ ├── Memoization.tsx │ │ │ ├── PlasmaBall.tsx │ │ │ ├── Playground.tsx │ │ │ ├── Rotate.tsx │ │ │ ├── Textures.tsx │ │ │ ├── Translate.tsx │ │ │ ├── Vanilla.tsx │ │ │ ├── Velocity.tsx │ │ │ ├── lib │ │ │ │ └── loop.tsx │ │ │ └── textures │ │ │ │ ├── cloud.png │ │ │ │ ├── explosion.png │ │ │ │ ├── hexgrid.jpeg │ │ │ │ ├── index.ts │ │ │ │ ├── particle.png │ │ │ │ ├── rust-normal.jpeg │ │ │ │ └── smoke.png │ │ ├── favicon.svg │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vercel.json │ └── vite.config.ts ├── r3f-stage-examples │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── examples │ │ │ └── IcosahedronExample.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── render-composer-examples │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── public │ │ ├── textures │ │ │ └── lensdirt.jpg │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vercel.json │ ├── vite.config.ts │ └── vite.config.ts.timestamp-1665213014880.mjs ├── shader-composer-examples │ ├── .gitignore │ ├── CHANGELOG.md │ ├── index.html │ ├── package.json │ ├── public │ │ ├── textures │ │ │ ├── cloud.png │ │ │ ├── hexgrid.jpg │ │ │ ├── particle.png │ │ │ └── smoke.png │ │ └── vite.svg │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── examples │ │ │ ├── Billboarding.tsx │ │ │ ├── DiscoCube.tsx │ │ │ ├── Dissolve.tsx │ │ │ ├── Fireball.tsx │ │ │ ├── Flag.tsx │ │ │ ├── FloatingIsland.tsx │ │ │ ├── ForceField.tsx │ │ │ ├── HelloWorld.tsx │ │ │ ├── Planet.tsx │ │ │ ├── Rotation.tsx │ │ │ ├── StylizedWater.tsx │ │ │ ├── Textures.tsx │ │ │ ├── Water.tsx │ │ │ ├── helpers.ts │ │ │ ├── textures │ │ │ │ ├── explosion.png │ │ │ │ └── shader-composer-logo.jpg │ │ │ └── useRenderPass.ts │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vercel.json │ ├── vite.config.ts │ └── vite.config.ts.timestamp-1669730134853.mjs ├── spacerage │ ├── .editorconfig │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── public │ │ ├── models │ │ │ ├── ImphenziaPalette01-256-GradientX4-Emission.png │ │ │ ├── ImphenziaPalette01-256-GradientX4.png │ │ │ ├── asteroid03.bin │ │ │ ├── asteroid03.gltf │ │ │ ├── spaceship25.bin │ │ │ └── spaceship25.gltf │ │ ├── sounds │ │ │ ├── blurp.wav │ │ │ ├── blurp2.wav │ │ │ ├── explosion.wav │ │ │ ├── fire.wav │ │ │ ├── hit.wav │ │ │ ├── hitregister.wav │ │ │ ├── pew.mp3 │ │ │ └── taikobeat.mp3 │ │ ├── textures │ │ │ ├── lensdirt.jpg │ │ │ ├── particle.png │ │ │ ├── skybox │ │ │ │ ├── back.png │ │ │ │ ├── bottom.png │ │ │ │ ├── front.png │ │ │ │ ├── left.png │ │ │ │ ├── right.png │ │ │ │ └── top.png │ │ │ ├── smoke.png │ │ │ └── spark1.png │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── Game.tsx │ │ ├── assets.ts │ │ ├── common │ │ │ ├── GLTFAsset.tsx │ │ │ └── Skybox.tsx │ │ ├── configuration.ts │ │ ├── editor │ │ │ └── Sidebar.tsx │ │ ├── index.css │ │ ├── input.ts │ │ ├── lib │ │ │ ├── InstanceRNG.tsx │ │ │ ├── Memoize.tsx │ │ │ ├── ScenePass.tsx │ │ │ ├── StartScreen.tsx │ │ │ ├── animation-composer │ │ │ │ └── Animate.tsx │ │ │ ├── miniplex-systems-runner │ │ │ │ └── System.tsx │ │ │ ├── useAutoRefresh.tsx │ │ │ └── useCapture.tsx │ │ ├── main.tsx │ │ ├── scenes │ │ │ ├── gameplay │ │ │ │ ├── Asteroids.tsx │ │ │ │ ├── Bullets.tsx │ │ │ │ ├── FollowCamera.tsx │ │ │ │ ├── GameplayScene.tsx │ │ │ │ ├── HUD.tsx │ │ │ │ ├── Pickups.tsx │ │ │ │ ├── Player.tsx │ │ │ │ ├── PostProcessing.tsx │ │ │ │ ├── assets.tsx │ │ │ │ ├── sfx │ │ │ │ │ ├── EngineHumSFX.tsx │ │ │ │ │ └── PewPewSFX.tsx │ │ │ │ ├── state.tsx │ │ │ │ ├── systems │ │ │ │ │ ├── AgeSystem.tsx │ │ │ │ │ ├── AttractorSystem.tsx │ │ │ │ │ ├── BulletSystem.tsx │ │ │ │ │ ├── DestroyAfterSystem.tsx │ │ │ │ │ ├── ECSFlushSystem.tsx │ │ │ │ │ ├── PlayerSystem.tsx │ │ │ │ │ └── VelocitySystem.tsx │ │ │ │ └── vfx │ │ │ │ │ ├── AsteroidExplosions.tsx │ │ │ │ │ ├── BackgroundAsteroids.tsx │ │ │ │ │ ├── Debris.tsx │ │ │ │ │ ├── DustVFX.tsx │ │ │ │ │ ├── SmokeVFX.tsx │ │ │ │ │ └── Sparks.tsx │ │ │ └── menu │ │ │ │ ├── AsteroidField.tsx │ │ │ │ ├── MenuScene.tsx │ │ │ │ ├── PostProcessing.tsx │ │ │ │ ├── sfx │ │ │ │ └── MenuDroneSFX.tsx │ │ │ │ └── vfx │ │ │ │ ├── AsteroidBelt.tsx │ │ │ │ ├── Dust.tsx │ │ │ │ └── Nebula.tsx │ │ ├── state.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vercel.json │ └── vite.config.ts ├── timeline-composer-examples │ ├── .gitignore │ ├── CHANGELOG.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── ui-composer-examples │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── vfx-composer-examples │ ├── CHANGELOG.md │ ├── index.html │ ├── package.json │ ├── public │ └── models │ │ ├── ImphenziaPalette01-256-GradientX4-Emission.png │ │ ├── ImphenziaPalette01-256-GradientX4.png │ │ ├── spaceship26.bin │ │ ├── spaceship26.gltf │ │ ├── spaceship26_mod.bin │ │ └── spaceship26_mod.gltf │ ├── src │ ├── AdaptiveResolution.tsx │ ├── App.tsx │ ├── examples │ │ ├── Asteroid.tsx │ │ ├── ControlledParticles.tsx │ │ ├── FireflyExample.tsx │ │ ├── FogExample.tsx │ │ ├── MagicWellExample.tsx │ │ ├── Playground.tsx │ │ ├── SharedResourceExample.tsx │ │ ├── Simple.tsx │ │ ├── SoftParticlesExample.tsx │ │ ├── Stress.tsx │ │ ├── Vanilla.tsx │ │ ├── effects │ │ │ └── Aura.tsx │ │ ├── lib │ │ │ └── loop.tsx │ │ ├── modules │ │ │ └── Lava.ts │ │ ├── textures │ │ │ ├── cloud.png │ │ │ ├── explosion.png │ │ │ ├── hexgrid.jpeg │ │ │ ├── index.ts │ │ │ ├── particle.png │ │ │ ├── smoke.png │ │ │ └── stream.png │ │ └── units │ │ │ └── NoiseMask.tsx │ ├── favicon.svg │ ├── main.tsx │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vercel.json │ └── vite.config.ts ├── archive └── react-game-starter │ ├── .editorconfig │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.tsx │ ├── Background.tsx │ ├── Controller.tsx │ ├── PerformanceMonitor.tsx │ ├── assets │ │ └── react.svg │ ├── index.css │ ├── input │ │ └── controller.ts │ ├── lib │ │ ├── Animate.tsx │ │ ├── ComponentRenderLogger.tsx │ │ ├── Effect.tsx │ │ ├── Keypress.tsx │ │ ├── PostProcessing.tsx │ │ ├── README.md │ │ ├── aabb.ts │ │ ├── camera-composer │ │ │ ├── Camera.tsx │ │ │ └── index.ts │ │ ├── makeFSM.tsx │ │ ├── timeline-composer │ │ │ ├── Delay.tsx │ │ │ ├── Lifetime.tsx │ │ │ ├── Repeat.tsx │ │ │ └── index.ts │ │ └── useKeypress.ts │ ├── main.tsx │ ├── scenes │ │ ├── Gameplay │ │ │ ├── Court.tsx │ │ │ ├── GameplayScene.tsx │ │ │ ├── ScoreHUD.tsx │ │ │ ├── configuration.ts │ │ │ ├── entities │ │ │ │ ├── Ball.tsx │ │ │ │ ├── Enemy.tsx │ │ │ │ ├── Paddle.tsx │ │ │ │ ├── Player.tsx │ │ │ │ └── index.ts │ │ │ ├── state.ts │ │ │ ├── systems │ │ │ │ ├── BallSystem.tsx │ │ │ │ ├── CameraSystem.tsx │ │ │ │ ├── EnemySystem.tsx │ │ │ │ ├── PaddleSystem.tsx │ │ │ │ └── Systems.tsx │ │ │ └── vfx │ │ │ │ ├── BallImpact.tsx │ │ │ │ └── BallTrail.tsx │ │ └── Title │ │ │ └── TitleScene.tsx │ ├── state.ts │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vercel.json │ ├── vite.config.ts │ └── yarn.lock ├── babel.config.js ├── docs.css ├── jest.config.cjs ├── package.json ├── packages ├── audio-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── AudioContext.tsx │ │ ├── AudioListener.tsx │ │ ├── Compressor.tsx │ │ ├── Filter.tsx │ │ ├── Gain.tsx │ │ ├── LinearRamp.tsx │ │ ├── Oscillator.tsx │ │ ├── PositionalAudio.tsx │ │ ├── Reverb.tsx │ │ ├── hooks.ts │ │ ├── index.ts │ │ └── store.ts │ └── tsconfig.json ├── camera-composer │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── react │ │ └── package.json │ ├── src │ │ ├── index.ts │ │ └── react │ │ │ └── index.ts │ └── tsconfig.json ├── hmans-event │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── hmans-r3f-create-loader │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── hmans-use-mutable-list │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── hmans-use-nullable-state │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── input-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── react │ │ └── package.json │ ├── src │ │ ├── index.ts │ │ ├── lib │ │ │ └── event.ts │ │ └── react.tsx │ └── tsconfig.json ├── material-composer-patch-material │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── patchMaterial.ts │ │ └── root.ts │ └── tsconfig.json ├── material-composer-patched │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── lib │ │ │ └── useManagedInstance.tsx │ │ └── patched.tsx │ └── tsconfig.json ├── material-composer-r3f │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── Layer.tsx │ │ ├── composable.tsx │ │ ├── index.ts │ │ ├── lib │ │ │ └── use-detect-shallow-change.tsx │ │ ├── moduleRegistration.tsx │ │ └── reactor.tsx │ └── tsconfig.json ├── material-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── modules │ │ └── package.json │ ├── package.json │ ├── src │ │ ├── Layer.ts │ │ ├── compileModules.ts │ │ ├── index.ts │ │ ├── modules │ │ │ ├── Acceleration.ts │ │ │ ├── Alpha.ts │ │ │ ├── Billboard.ts │ │ │ ├── Color.ts │ │ │ ├── Fresnel.ts │ │ │ ├── Gradient.ts │ │ │ ├── Lifetime.ts │ │ │ ├── Rotate.ts │ │ │ ├── Scale.ts │ │ │ ├── Softness.ts │ │ │ ├── SurfaceWobble.ts │ │ │ ├── Texture.ts │ │ │ ├── Translate.ts │ │ │ ├── Velocity.ts │ │ │ └── index.ts │ │ └── units │ │ │ ├── experiments.ts │ │ │ └── index.ts │ ├── tsconfig.json │ └── units │ │ └── package.json ├── r3f-stage │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── Application.tsx │ │ ├── Example.tsx │ │ ├── Spinner.tsx │ │ ├── ThreeApplication.tsx │ │ ├── index.ts │ │ ├── lib │ │ │ └── tunnel-rat.tsx │ │ ├── stages │ │ │ ├── FlatStage.tsx │ │ │ └── index.ts │ │ └── ui │ │ │ ├── Description.tsx │ │ │ ├── Heading.tsx │ │ │ └── UI.tsx │ ├── styles.css │ └── tsconfig.json ├── render-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── Canvas.tsx │ │ ├── DefaultRenderPipeline.tsx │ │ ├── EffectComposer.tsx │ │ ├── effects │ │ │ ├── GodRaysEffect.tsx │ │ │ ├── LensDirtEffect.tsx │ │ │ ├── NoiseEffect.tsx │ │ │ ├── SMAAEffect.tsx │ │ │ ├── SelectiveBloomEffect.tsx │ │ │ ├── TextureEffect.tsx │ │ │ ├── TiltShiftEffect.tsx │ │ │ ├── VignetteEffect.tsx │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── lib │ │ │ ├── bitmask.ts │ │ │ └── usePostProcessingEffect.tsx │ │ └── passes │ │ │ ├── CopyPass.tsx │ │ │ ├── DepthCopyPass.tsx │ │ │ ├── EffectPass.tsx │ │ │ ├── LambdaPass.tsx │ │ │ ├── LayerRenderPass.tsx │ │ │ ├── RenderPass.tsx │ │ │ └── index.ts │ └── tsconfig.json ├── shader-composer-core │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── compiler.ts │ │ ├── debug.ts │ │ ├── expressions.ts │ │ ├── glslRepresentation.ts │ │ ├── glslType.ts │ │ ├── index.ts │ │ ├── snippets.ts │ │ ├── stdlib │ │ │ ├── artistic.ts │ │ │ ├── index.ts │ │ │ ├── logic.ts │ │ │ ├── math.ts │ │ │ ├── roots.ts │ │ │ ├── rotation.ts │ │ │ ├── scene.ts │ │ │ ├── spaces.ts │ │ │ ├── textures.ts │ │ │ ├── values.test.ts │ │ │ ├── values.ts │ │ │ ├── variables.test.ts │ │ │ ├── variables.ts │ │ │ └── vectors.ts │ │ ├── ticker.ts │ │ ├── tree.ts │ │ ├── units.ts │ │ ├── util │ │ │ ├── concatenator3000.ts │ │ │ └── idGenerator.ts │ │ └── vendor │ │ │ ├── glsl-rotate.ts │ │ │ └── index.ts │ ├── test │ │ ├── __snapshots__ │ │ │ ├── compiler.test.ts.snap │ │ │ └── units.test.ts.snap │ │ ├── compiler.test.ts │ │ ├── expressions.test.ts │ │ ├── glslRepresentation.test.ts │ │ ├── tree.test.ts │ │ └── units.test.ts │ └── tsconfig.json ├── shader-composer-noise │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── FBMNoise.ts │ │ ├── GerstnerWave.ts │ │ ├── PSRDNoise.ts │ │ ├── PerlinNoise.ts │ │ ├── Simplex3DNoise.ts │ │ ├── index.ts │ │ ├── mod289.ts │ │ ├── permute.ts │ │ ├── random.ts │ │ ├── taylorInvSqrt.ts │ │ └── turbulence.ts │ └── tsconfig.json ├── shader-composer-postprocessing │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── PostProcessingEffectRoot.ts │ │ ├── ShaderComposerEffect.ts │ │ ├── index.ts │ │ └── units.ts │ └── tsconfig.json ├── shader-composer-r3f │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── Shader.tsx │ │ ├── hooks.ts │ │ └── index.ts │ └── tsconfig.json ├── shader-composer-three │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── shader-composer-toybox │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── effects │ │ │ ├── Dissolve.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ └── tools │ │ │ ├── Billboard.ts │ │ │ ├── Displacement.ts │ │ │ ├── Grid2D.ts │ │ │ ├── Random.ts │ │ │ ├── Softness.ts │ │ │ └── index.ts │ └── tsconfig.json ├── shader-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── fun │ │ └── package.json │ ├── package.json │ ├── postprocessing │ │ └── package.json │ ├── r3f │ │ └── package.json │ ├── src │ │ ├── fun.ts │ │ ├── index.ts │ │ ├── postprocessing.ts │ │ ├── r3f.ts │ │ └── three.ts │ ├── three │ │ └── package.json │ └── tsconfig.json ├── state-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── createStateMachine.tsx │ │ └── index.ts │ └── tsconfig.json ├── timeline-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── Delay.tsx │ │ ├── Lifetime.tsx │ │ ├── Repeat.tsx │ │ └── index.ts │ └── tsconfig.json ├── ts-module-docs │ ├── package.json │ ├── src │ │ └── index.ts │ ├── test │ │ ├── mock │ │ │ ├── mock.ts │ │ │ └── tsconfig.json │ │ └── ts-module-docs.test.ts │ └── tsconfig.json ├── ui-composer │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── BooleanControl.tsx │ │ │ ├── Button.tsx │ │ │ ├── Heading.tsx │ │ │ ├── HorizontalGroup.tsx │ │ │ ├── HorizontalResizer.tsx │ │ │ ├── Input.tsx │ │ │ ├── Root.tsx │ │ │ ├── VerticalGroup.tsx │ │ │ └── index.ts │ │ ├── index.ts │ │ └── styles.tsx │ └── tsconfig.json ├── vfx-composer-r3f │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── Emitter.tsx │ │ ├── InstancedParticles.tsx │ │ ├── Particle.tsx │ │ ├── context.ts │ │ ├── hooks │ │ │ └── particles.ts │ │ ├── index.ts │ │ └── lib │ │ │ └── useFrameEffect.ts │ └── tsconfig.json └── vfx-composer │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ ├── src │ ├── InstancedParticles.ts │ ├── ParticleAttribute.ts │ ├── createParticleLifetime.ts │ ├── index.ts │ └── util │ │ └── makeAttribute.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tsconfig.json ├── turbo.json └── vercel.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [ 7 | ["vfx-composer", "vfx-composer-r3f"], 8 | ["shader-composer", "shader-composer-*", "@shader-composer/*"], 9 | ["material-composer", "material-composer-r3f"] 10 | ], 11 | "access": "public", 12 | "baseBranch": "main", 13 | "updateInternalDependencies": "patch", 14 | "ignore": ["*-examples", "spacerage"], 15 | "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { 16 | "onlyUpdatePeerDependentsWhenOutOfRange": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.changeset/curvy-llamas-pump.md: -------------------------------------------------------------------------------- 1 | --- 2 | "shader-composer": minor 3 | "@shader-composer/core": minor 4 | --- 5 | 6 | **Breaking:** The casting helpers (eg. `$vec3()`, `$mat3()` etc.) have been renamed to `vec3()`, `mat3()` etc., and have had their signature changed. Where the old implementations were able to take any number of arguments, the new helpers will only ever take _a single argument_. (If you want to cast multiple arguments, you can pass an array.) 7 | 8 | ```js 9 | /* Before: */ 10 | $vec3(1, 2, 3) 11 | 12 | /* After: */ 13 | vec3([1, 2, 3]) 14 | ``` 15 | -------------------------------------------------------------------------------- /.changeset/honest-maps-dance.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@material-composer/patch-material": minor 3 | --- 4 | 5 | **Breaking Change:** `PatchedMaterialMaster` has been renamed to `PatchedMaterialRoot`. 6 | -------------------------------------------------------------------------------- /.changeset/lucky-dragons-retire.md: -------------------------------------------------------------------------------- 1 | --- 2 | "render-composer": minor 3 | --- 4 | 5 | **Breaking Change:** `RenderPipeline` has been renamed to `DefaultRenderPipeline`. 6 | -------------------------------------------------------------------------------- /.changeset/red-elephants-wave.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@shader-composer/core": minor 3 | --- 4 | 5 | **Breaking Change:** `ShaderMaterialMaster` and `CustomShaderMaterialMaster` have been renamed to `ShaderMaterialRoot` and `CustomShaderMaterialRoot`. 6 | -------------------------------------------------------------------------------- /.changeset/sixty-llamas-boil.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@shader-composer/postprocessing": minor 3 | --- 4 | 5 | **Breaking Change:** `PostProcessingEffectMaster` has been renamed to `PostProcessingEffectRoot`. 6 | -------------------------------------------------------------------------------- /.changeset/spotty-students-lay.md: -------------------------------------------------------------------------------- 1 | --- 2 | "material-composer": patch 3 | "@material-composer/patch-material": patch 4 | "material-composer-r3f": patch 5 | "render-composer": patch 6 | "vfx-composer": patch 7 | "vfx-composer-r3f": patch 8 | --- 9 | 10 | Upgraded to the new version of Shader Composer. 11 | -------------------------------------------------------------------------------- /.changeset/stale-melons-double.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@shader-composer/core": minor 3 | --- 4 | 5 | Added two new units: `FrameTime` and `FrameCount`. 6 | 7 | `FrameTime` is a unit that represents the time in seconds since the application started. Most importantly, this value is guaranteed to be stable across the duration of a frame, so it's perfect for synchronizing multiple shaders. 8 | 9 | `FrameCount` provides an integer counter of the number of frames rendered since the start of the application. This, too, is great for synchronizing shaders, and might be better for when you need an auto-increasing integer value instead of a floating point time value. 10 | 11 | If you need these values in your JavaScript `update` callbacks, you can import the new `frame` object and access its `time` and `count` properties. 12 | -------------------------------------------------------------------------------- /.changeset/stale-moles-float.md: -------------------------------------------------------------------------------- 1 | --- 2 | "shader-composer": minor 3 | "@shader-composer/core": minor 4 | "@shader-composer/r3f": minor 5 | "@shader-composer/three": minor 6 | --- 7 | 8 | **Breaking:** The core Shader Composer package `@shader-composer/core` no longer requires Three.js as a peer dependency, and is now ready to be used together with other frameworks (or directly with WebGL.) 9 | 10 | Since some of the units provided within the standard library require framework-specific code to operate (like `CameraFar`, `CameraNear`, `Resolution` etc.), glue code needs to be created to use Shader Composer with other frameworks. 11 | 12 | The Three.js glue code lives in the `@shader-composer/three` package. 13 | 14 | TODO: example for usage 15 | -------------------------------------------------------------------------------- /.changeset/thin-mails-press.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@shader-composer/r3f": minor 3 | --- 4 | 5 | **Breaking Change:** `ShaderMaster` has been renamed to `ShaderRoot`. 6 | -------------------------------------------------------------------------------- /.changeset/two-jokes-wait.md: -------------------------------------------------------------------------------- 1 | --- 2 | "shader-composer": minor 3 | "@shader-composer/core": minor 4 | --- 5 | 6 | **New:** Arrays of specific lengths are now automatically casts to vectors and matrices; an array with 2 elements will be rendered as a `vec2`, an array with 3 elements will be rendered as a `vec3`, and so on. 7 | 8 | ```js 9 | /* Before, and this still works: */ 10 | ScaleAndOffset(value, Vec2([0.5, 0.5]), Vec2([0.5, 0.5])) 11 | 12 | /* But now this is also possible: */ 13 | ScaleAndOffset(value, [0.5, 0.5], [0.5, 0.5]) 14 | ``` 15 | 16 | If you want to cast an array to a specific type, you can use the `vec2()`, `vec3()` etc. helpers: 17 | 18 | ```js 19 | ScaleAndOffset(value, vec2([0.5, 0.5]), vec2([0.5, 0.5])) 20 | ``` 21 | 22 | Or wrap them in full units, like above. 23 | -------------------------------------------------------------------------------- /.changeset/yellow-pens-switch.md: -------------------------------------------------------------------------------- 1 | --- 2 | "shader-composer": minor 3 | --- 4 | 5 | Removed Three-specific bits from `UpdateCallback` 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | tab_width = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [hmans] 2 | -------------------------------------------------------------------------------- /.github/workflows/sponsors.yml: -------------------------------------------------------------------------------- 1 | name: Generate Sponsors README 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: 30 15 * * 0-6 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 🛎️ 11 | uses: actions/checkout@v2 12 | 13 | - name: Generate Sponsors 💖 14 | uses: JamesIves/github-sponsors-readme-action@v1 15 | with: 16 | token: ${{ secrets.PAT }} 17 | file: "README.md" 18 | 19 | - name: Deploy to GitHub Pages 🚀 20 | uses: JamesIves/github-pages-deploy-action@v4 21 | with: 22 | branch: main 23 | folder: "." 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Generated documentation 16 | /docs 17 | packages/**/docs 18 | apps/website/docs/reference/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | .DS_Store 25 | *.suo 26 | *.ntvs* 27 | *.njsproj 28 | *.sln 29 | *.sw? 30 | 31 | # Turborepo 32 | .turbo 33 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | -------------------------------------------------------------------------------- /apps/material-composer-examples/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # material-composer-examples 2 | 3 | ## 0.0.1 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies [03215af] 8 | - Updated dependencies [8ca879b] 9 | - Updated dependencies [8ca879b] 10 | - Updated dependencies [82ad766] 11 | - Updated dependencies [8ca879b] 12 | - material-composer@0.1.2 13 | - shader-composer@0.3.4 14 | - material-composer-r3f@0.1.2 15 | -------------------------------------------------------------------------------- /apps/material-composer-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Material Composer Examples 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/material-composer-examples/public/models/ImphenziaPalette01-256-GradientX4-Emission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/public/models/ImphenziaPalette01-256-GradientX4-Emission.png -------------------------------------------------------------------------------- /apps/material-composer-examples/public/models/ImphenziaPalette01-256-GradientX4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/public/models/ImphenziaPalette01-256-GradientX4.png -------------------------------------------------------------------------------- /apps/material-composer-examples/public/models/spaceship26.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/public/models/spaceship26.bin -------------------------------------------------------------------------------- /apps/material-composer-examples/public/models/spaceship26_mod.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/public/models/spaceship26_mod.bin -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/Playground.tsx: -------------------------------------------------------------------------------- 1 | import { Composable, Modules } from "material-composer-r3f" 2 | 3 | export default function Playground() { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/Rotate.tsx: -------------------------------------------------------------------------------- 1 | import { useControls } from "leva" 2 | import { composable, modules } from "material-composer-r3f" 3 | import { Description } from "r3f-stage" 4 | import { useMemo } from "react" 5 | import { GlobalTime, Rotation3D, Time, Vec3 } from "shader-composer" 6 | 7 | export default function Rotate() { 8 | const controls = useControls({ 9 | space: { value: "world", options: ["local", "world", "view"] } 10 | }) 11 | 12 | const time = useMemo(() => Time(), []) 13 | 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | The Rotate module applies a rotation to the object's 26 | vertices. 27 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/Velocity.tsx: -------------------------------------------------------------------------------- 1 | import { composable, modules } from "material-composer-r3f" 2 | import { Description } from "r3f-stage" 3 | import { useMemo } from "react" 4 | import { Time } from "shader-composer" 5 | import { Vector3 } from "three" 6 | 7 | export default function Velocity() { 8 | const time = useMemo(() => Time(), []) 9 | 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | The Velocity module implements stateless velocity 22 | animation, translating vertices by a given velocity vector multiplied by 23 | the time. Useful for particles! 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/lib/loop.tsx: -------------------------------------------------------------------------------- 1 | const clamp = (value: number, min: number, max: number) => 2 | Math.min(Math.max(value, min), max) 3 | 4 | const clamp01 = (value: number) => clamp(value, 0, 1) 5 | 6 | export const loop = (callback: (dt: number) => void) => { 7 | let running = true 8 | let lastTime = performance.now() 9 | 10 | const tick = () => { 11 | if (running) { 12 | requestAnimationFrame(tick) 13 | 14 | const now = performance.now() 15 | const dt = clamp01((now - lastTime) / 1000) 16 | lastTime = now 17 | 18 | callback(dt) 19 | } 20 | } 21 | 22 | tick() 23 | 24 | return () => { 25 | running = false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/textures/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/src/examples/textures/cloud.png -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/textures/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/src/examples/textures/explosion.png -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/textures/hexgrid.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/src/examples/textures/hexgrid.jpeg -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/textures/index.ts: -------------------------------------------------------------------------------- 1 | export { default as cloudUrl } from "./cloud.png" 2 | export { default as particleUrl } from "./particle.png" 3 | export { default as smokeUrl } from "./smoke.png" 4 | -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/textures/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/src/examples/textures/particle.png -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/textures/rust-normal.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/src/examples/textures/rust-normal.jpeg -------------------------------------------------------------------------------- /apps/material-composer-examples/src/examples/textures/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/material-composer-examples/src/examples/textures/smoke.png -------------------------------------------------------------------------------- /apps/material-composer-examples/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")!).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /apps/material-composer-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/material-composer-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/material-composer-examples/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/material-composer-examples/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }], 3 | "github": { 4 | "silent": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/material-composer-examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import react from "@vitejs/plugin-react" 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | optimizeDeps: { 8 | exclude: [ 9 | "material-composer", 10 | "material-composer-r3f", 11 | "@material-composer/patch-material" 12 | ] 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | r3f-stage example app 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "r3f-stage-examples", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@react-three/drei": "^9.34.3", 13 | "@react-three/fiber": "^8.8.9", 14 | "r3f-stage": "^0.3.5", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "three": "^0.145.0" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.0.21", 21 | "@types/react-dom": "^18.0.6", 22 | "@vitejs/plugin-react": "^2.1.0", 23 | "typescript": "^4.9.3", 24 | "vite": "^3.1.6" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/src/examples/IcosahedronExample.tsx: -------------------------------------------------------------------------------- 1 | import { Environment } from "@react-three/drei" 2 | 3 | export default function IcosahedronExample() { 4 | return ( 5 | <> 6 | 7 | 8 | 9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/r3f-stage-examples/src/index.css -------------------------------------------------------------------------------- /apps/r3f-stage-examples/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/r3f-stage-examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import react from "@vitejs/plugin-react" 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | optimizeDeps: { 8 | exclude: ["r3f-stage"] 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /apps/render-composer-examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/render-composer-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/render-composer-examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "render-composer-examples", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@hmans/r3f-animate": "^0.0.5", 13 | "@react-three/drei": "^9.34.3", 14 | "@react-three/fiber": "^8.8.9", 15 | "postprocessing": "^6.28.7", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "render-composer": "^0.2.8", 19 | "shader-composer": "workspace:^0.4.9", 20 | "three": "^0.145.0" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.0.21", 24 | "@types/react-dom": "^18.0.6", 25 | "@types/three": "^0.144.0", 26 | "@vitejs/plugin-react": "^2.1.0", 27 | "typescript": "^4.9.3", 28 | "vite": "^3.1.6" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/render-composer-examples/public/textures/lensdirt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/render-composer-examples/public/textures/lensdirt.jpg -------------------------------------------------------------------------------- /apps/render-composer-examples/src/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | width: 100vw; 4 | height: 100vh; 5 | margin: 0; 6 | padding: 0; 7 | overflow: hidden; 8 | user-select: none; 9 | background-color: black; 10 | color: #ccc; 11 | } 12 | 13 | body { 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | 17 | font: 18px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 18 | color: #f2f2f2; 19 | } 20 | 21 | a { 22 | font-weight: bold; 23 | display: block; 24 | color: #eee; 25 | text-decoration: none; 26 | } 27 | 28 | a:hover { 29 | color: hotpink; 30 | } 31 | 32 | div#root, 33 | div#app { 34 | height: 100vh; 35 | } 36 | 37 | canvas { 38 | position: fixed; 39 | width: 100vw; 40 | height: 100vh; 41 | } 42 | 43 | /* Panels */ 44 | 45 | div.panel { 46 | padding: 20px; 47 | position: fixed; 48 | z-index: 1; 49 | } 50 | 51 | div.panel > *:first-child { 52 | margin-top: 0; 53 | } 54 | 55 | div.panel > *:last-child { 56 | margin-bottom: 0; 57 | } 58 | -------------------------------------------------------------------------------- /apps/render-composer-examples/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /apps/render-composer-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/render-composer-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/render-composer-examples/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/render-composer-examples/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }], 3 | "github": { 4 | "silent": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/render-composer-examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | shader-composer Examples 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shader-composer-examples", 3 | "private": true, 4 | "version": "0.0.10", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@hmans/r3f-animate": "^0.0.5", 13 | "@react-three/drei": "^9.34.3", 14 | "@react-three/fiber": "^8.8.9", 15 | "fp-ts": "^2.12.3", 16 | "leva": "^0.9.31", 17 | "r3f-stage": "^0.3.5", 18 | "react": "^18.2.0", 19 | "react-dom": "^18.2.0", 20 | "shader-composer": "workspace:^0.4.9", 21 | "shader-composer-toybox": "^0.1.3", 22 | "three": "^0.145.0" 23 | }, 24 | "devDependencies": { 25 | "@types/react": "^18.0.21", 26 | "@types/react-dom": "^18.0.6", 27 | "@types/three": "^0.144.0", 28 | "@vitejs/plugin-react": "^2.1.0", 29 | "typescript": "^4.9.3", 30 | "vite": "^3.1.6" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/public/textures/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/shader-composer-examples/public/textures/cloud.png -------------------------------------------------------------------------------- /apps/shader-composer-examples/public/textures/hexgrid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/shader-composer-examples/public/textures/hexgrid.jpg -------------------------------------------------------------------------------- /apps/shader-composer-examples/public/textures/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/shader-composer-examples/public/textures/particle.png -------------------------------------------------------------------------------- /apps/shader-composer-examples/public/textures/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/shader-composer-examples/public/textures/smoke.png -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | } 13 | .logo:hover { 14 | filter: drop-shadow(0 0 2em #646cffaa); 15 | } 16 | .logo.react:hover { 17 | filter: drop-shadow(0 0 2em #61dafbaa); 18 | } 19 | 20 | @keyframes logo-spin { 21 | from { 22 | transform: rotate(0deg); 23 | } 24 | to { 25 | transform: rotate(360deg); 26 | } 27 | } 28 | 29 | @media (prefers-reduced-motion: no-preference) { 30 | a:nth-of-type(2) .logo { 31 | animation: logo-spin infinite 20s linear; 32 | } 33 | } 34 | 35 | .card { 36 | padding: 2em; 37 | } 38 | 39 | .read-the-docs { 40 | color: #888; 41 | } 42 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/examples/Flag.tsx: -------------------------------------------------------------------------------- 1 | import { useTexture } from "@react-three/drei" 2 | import { 3 | Add, 4 | GlobalTime, 5 | Mul, 6 | Sin, 7 | UV, 8 | Vec3, 9 | VertexPosition 10 | } from "shader-composer" 11 | import { Shader, ShaderRoot, useShader } from "shader-composer/r3f" 12 | import { DoubleSide } from "three" 13 | import textureUrl from "./textures/shader-composer-logo.jpg" 14 | 15 | export default function Flag() { 16 | const texture = useTexture(textureUrl) 17 | 18 | const shader = useShader(() => { 19 | const time = GlobalTime 20 | 21 | return ShaderRoot({ 22 | position: Vec3([ 23 | VertexPosition.x, 24 | VertexPosition.y, 25 | Mul(Sin(Add(Mul(time, 2), Add(Mul(UV.y, 8), Mul(UV.x, 14)))), 0.2) 26 | ]) 27 | }) 28 | }, []) 29 | 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/examples/helpers.ts: -------------------------------------------------------------------------------- 1 | import { useTexture } from "@react-three/drei" 2 | import { RepeatWrapping } from "three" 3 | 4 | export const useRepeatingTexture = (url: string) => { 5 | const texture = useTexture(url) 6 | texture.wrapS = RepeatWrapping 7 | texture.wrapT = RepeatWrapping 8 | return texture 9 | } 10 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/examples/textures/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/shader-composer-examples/src/examples/textures/explosion.png -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/examples/textures/shader-composer-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/shader-composer-examples/src/examples/textures/shader-composer-logo.jpg -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/shader-composer-examples/src/index.css -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }], 3 | "github": { 4 | "silent": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/shader-composer-examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import react from "@vitejs/plugin-react" 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | optimizeDeps: { 8 | exclude: [ 9 | "shader-composer", 10 | "@shader-composer/r3f", 11 | "shader-composer-toybox" 12 | ], 13 | include: ["react/jsx-runtime"] 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /apps/spacerage/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | tab_width = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /apps/spacerage/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/spacerage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPACE RAGE 2 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/spacerage/public/models/ImphenziaPalette01-256-GradientX4-Emission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/models/ImphenziaPalette01-256-GradientX4-Emission.png -------------------------------------------------------------------------------- /apps/spacerage/public/models/ImphenziaPalette01-256-GradientX4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/models/ImphenziaPalette01-256-GradientX4.png -------------------------------------------------------------------------------- /apps/spacerage/public/models/asteroid03.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/models/asteroid03.bin -------------------------------------------------------------------------------- /apps/spacerage/public/models/spaceship25.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/models/spaceship25.bin -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/blurp.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/blurp.wav -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/blurp2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/blurp2.wav -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/explosion.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/explosion.wav -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/fire.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/fire.wav -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/hit.wav -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/hitregister.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/hitregister.wav -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/pew.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/pew.mp3 -------------------------------------------------------------------------------- /apps/spacerage/public/sounds/taikobeat.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/sounds/taikobeat.mp3 -------------------------------------------------------------------------------- /apps/spacerage/public/textures/lensdirt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/lensdirt.jpg -------------------------------------------------------------------------------- /apps/spacerage/public/textures/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/particle.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/skybox/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/skybox/back.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/skybox/bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/skybox/bottom.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/skybox/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/skybox/front.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/skybox/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/skybox/left.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/skybox/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/skybox/right.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/skybox/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/skybox/top.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/smoke.png -------------------------------------------------------------------------------- /apps/spacerage/public/textures/spark1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/spacerage/public/textures/spark1.png -------------------------------------------------------------------------------- /apps/spacerage/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as UI from "ui-composer" 2 | import { StartScreen } from "./lib/StartScreen" 3 | import { Game } from "./Game" 4 | import { Sidebar } from "./editor/Sidebar" 5 | import { useState } from "react" 6 | 7 | export const App = () => { 8 | const [editorEnabled, setEditorEnabled] = useState(true) 9 | 10 | return ( 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 | {editorEnabled && ( 19 | <> 20 | 21 | 22 | 23 | 24 | 25 | )} 26 |
27 |
28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /apps/spacerage/src/assets.ts: -------------------------------------------------------------------------------- 1 | import { createLoader } from "@hmans/r3f-create-loader" 2 | import { TextureLoader } from "three" 3 | 4 | /* Use in project, in eg. `assets.ts` */ 5 | export const useAsset = { 6 | textures: { 7 | lensdirt: createLoader(TextureLoader, "/textures/lensdirt.jpg") 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/spacerage/src/common/GLTFAsset.tsx: -------------------------------------------------------------------------------- 1 | import { useGLTF } from "@react-three/drei" 2 | import { Mesh } from "three" 3 | 4 | export const GLTFAsset = ({ url }: { url: string }) => { 5 | const gltf = useGLTF(url) 6 | const mesh = (gltf.scene.children[0] as Mesh).clone() 7 | return 8 | } 9 | -------------------------------------------------------------------------------- /apps/spacerage/src/common/Skybox.tsx: -------------------------------------------------------------------------------- 1 | import { Environment } from "@react-three/drei" 2 | 3 | export const Skybox = () => { 4 | return ( 5 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /apps/spacerage/src/configuration.ts: -------------------------------------------------------------------------------- 1 | export const Stage = { 2 | Early: -200, 3 | Physics: -100, 4 | Normal: 0, 5 | Late: 100, 6 | Render: 200 7 | } 8 | -------------------------------------------------------------------------------- /apps/spacerage/src/editor/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import * as UI from "ui-composer" 2 | import { GameState, SidebarTunnel } from "../state" 3 | 4 | export const Sidebar = () => ( 5 | <> 6 | 7 | Scenes 8 | 9 | GameState.enter("menu")}> 10 | Menu Scene 11 | 12 | GameState.enter("gameplay")}> 13 | Gameplay Scene 14 | 15 | 16 | 17 | 18 | {/* */} 19 | 20 | 21 | 22 | ) 23 | -------------------------------------------------------------------------------- /apps/spacerage/src/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100vh; 4 | width: 100vw; 5 | margin: 0; 6 | padding: 0; 7 | overflow: hidden; 8 | } 9 | 10 | body { 11 | background-color: #000; 12 | } 13 | 14 | div#root { 15 | height: 100%; 16 | width: 100%; 17 | overflow: hidden; 18 | } 19 | -------------------------------------------------------------------------------- /apps/spacerage/src/lib/InstanceRNG.tsx: -------------------------------------------------------------------------------- 1 | import { $, Input, InstanceID } from "shader-composer" 2 | import { Random } from "shader-composer-toybox" 3 | 4 | export const InstanceRNG = 5 | ({ seed }: { seed?: Input<"float"> } = {}) => 6 | (offset: Input<"float"> = Math.random() * 10) => 7 | Random($`${offset} + float(${InstanceID}) * 1.1005`) 8 | -------------------------------------------------------------------------------- /apps/spacerage/src/lib/Memoize.tsx: -------------------------------------------------------------------------------- 1 | import { memo, ReactNode } from "react" 2 | 3 | export const Memoize = memo( 4 | (props: { children?: ReactNode }) => <>{props.children}, 5 | () => true 6 | ) 7 | -------------------------------------------------------------------------------- /apps/spacerage/src/lib/ScenePass.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, useState } from "react" 2 | import { Scene } from "three" 3 | import * as RC from "render-composer" 4 | import { createPortal } from "@react-three/fiber" 5 | 6 | export const ScenePass = ({ children }: { children?: ReactNode }) => { 7 | const [scene] = useState(() => new Scene()) 8 | 9 | return createPortal( 10 | <> 11 | {children} 12 | 13 | 14 | , 15 | scene, 16 | {} 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /apps/spacerage/src/lib/useAutoRefresh.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | 3 | export const useAutoRefresh = (interval = 1) => { 4 | const [_, setVersion] = useState(0) 5 | 6 | useEffect(() => { 7 | const id = setInterval(() => setVersion((v) => v + 1), interval * 1000) 8 | return () => clearInterval(id) 9 | }, []) 10 | } 11 | -------------------------------------------------------------------------------- /apps/spacerage/src/lib/useCapture.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react" 2 | import { IState, Store } from "statery" 3 | 4 | export const useCapture = ( 5 | store: Store, 6 | key: K 7 | ) => 8 | useCallback((value: S[K]) => store.set({ [key]: value } as S), [store, key]) 9 | -------------------------------------------------------------------------------- /apps/spacerage/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import * as SC from "shader-composer" 4 | import { App } from "./App" 5 | import "./index.css" 6 | 7 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 8 | 9 | 10 | 11 | ) 12 | 13 | SC.enableDebugging() 14 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/FollowCamera.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from "@react-three/fiber" 2 | import { useEntities } from "miniplex/react" 3 | import { useEffect } from "react" 4 | import { useStore } from "statery" 5 | import { Vector3 } from "three" 6 | import { Stage } from "../../configuration" 7 | import { store } from "../../state" 8 | import { ECS } from "./state" 9 | 10 | const offset = new Vector3(0, 0, 40) 11 | const playerPos = new Vector3() 12 | const tmpVec3 = new Vector3() 13 | 14 | export const FollowCamera = () => { 15 | const [player] = useEntities(ECS.world.with("player", "sceneObject")) 16 | 17 | const { camera } = useStore(store) 18 | 19 | useEffect(() => { 20 | if (camera) camera.quaternion.set(0, 0, 0, 1) 21 | }, [camera]) 22 | 23 | useFrame((_, dt) => { 24 | if (camera && player) { 25 | player.sceneObject.getWorldPosition(playerPos) 26 | const cameraTarget = tmpVec3.copy(playerPos).add(offset) 27 | camera.position.lerp(cameraTarget, 2 * dt) 28 | } 29 | }, Stage.Late) 30 | 31 | return null 32 | } 33 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/HUD.tsx: -------------------------------------------------------------------------------- 1 | import { PerspectiveCamera, Text } from "@react-three/drei" 2 | import { Color } from "three" 3 | import { ScenePass } from "../../lib/ScenePass" 4 | 5 | export const HUD = () => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 723.389.150 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/PostProcessing.tsx: -------------------------------------------------------------------------------- 1 | import * as RC from "render-composer" 2 | import { makeStore } from "statery" 3 | import { Mesh } from "three" 4 | import { useAsset } from "../../assets" 5 | 6 | export const store = makeStore({ 7 | sun: null as Mesh | null 8 | }) 9 | 10 | export const PostProcessing = () => { 11 | const texture = useAsset.textures.lensdirt() 12 | 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/assets.tsx: -------------------------------------------------------------------------------- 1 | import { AudioLoader, TextureLoader } from "three" 2 | import { GLTFLoader } from "three-stdlib" 3 | import { createLoader } from "@hmans/r3f-create-loader" 4 | 5 | /* Use in project, in eg. `assets.ts` */ 6 | export const useAsset = { 7 | asteroid: createLoader(GLTFLoader, "/models/asteroid03.gltf"), 8 | playerShip: createLoader(GLTFLoader, "/models/spaceship25.gltf"), 9 | music: createLoader(AudioLoader, "/sounds/taikobeat.mp3"), 10 | textures: { 11 | smoke: createLoader(TextureLoader, "/textures/smoke.png"), 12 | particle: createLoader(TextureLoader, "/textures/particle.png") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/sfx/PewPewSFX.tsx: -------------------------------------------------------------------------------- 1 | import { Gain, LinearRamp, Oscillator } from "audio-composer" 2 | import { between } from "randomish" 3 | 4 | export const PewPewSFX = () => ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/systems/AgeSystem.tsx: -------------------------------------------------------------------------------- 1 | import { Stage } from "../../../configuration" 2 | import { System } from "../../../lib/miniplex-systems-runner/System" 3 | import { ECS } from "../state" 4 | 5 | const withAge = ECS.world.with("age") 6 | 7 | export const AgeSystem = () => ( 8 | { 13 | for (const entity of withAge) { 14 | entity.age += dt 15 | } 16 | 17 | /* Squeeze Bucketeer in */ 18 | for (let i = withAge.entities.length - 1; i >= 0; i--) { 19 | const entity = withAge.entities[i] 20 | entity.age += dt 21 | } 22 | }} 23 | /> 24 | ) 25 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/systems/DestroyAfterSystem.tsx: -------------------------------------------------------------------------------- 1 | import { Stage } from "../../../configuration" 2 | import { System } from "../../../lib/miniplex-systems-runner/System" 3 | import { ECS, queue } from "../state" 4 | 5 | const entitiesToEliminate = ECS.world 6 | .with("destroyAfter", "age") 7 | .where((e) => e.age > e.destroyAfter) 8 | 9 | export const DestroyAfterSystem = () => ( 10 | { 15 | for (const entity of entitiesToEliminate) { 16 | queue(() => ECS.world.remove(entity)) 17 | } 18 | }} 19 | /> 20 | ) 21 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/systems/ECSFlushSystem.tsx: -------------------------------------------------------------------------------- 1 | import { Stage } from "../../../configuration" 2 | import { System } from "../../../lib/miniplex-systems-runner/System" 3 | import { ECS, queue } from "../state" 4 | 5 | export const ECSFlushSystem = () => ( 6 | queue.flush()} 11 | /> 12 | ) 13 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/systems/VelocitySystem.tsx: -------------------------------------------------------------------------------- 1 | import { Stage } from "../../../configuration" 2 | import { System } from "../../../lib/miniplex-systems-runner/System" 3 | import { ECS } from "../state" 4 | 5 | const entities = ECS.world.with("sceneObject", "velocity") 6 | 7 | export const VelocitySystem = () => ( 8 | { 13 | for (const { sceneObject, velocity } of entities) { 14 | sceneObject.position.addScaledVector(velocity, dt) 15 | } 16 | }} 17 | /> 18 | ) 19 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/gameplay/vfx/BackgroundAsteroids.tsx: -------------------------------------------------------------------------------- 1 | import { between, plusMinus } from "randomish" 2 | import { Material, Mesh } from "three" 3 | import { InstanceSetupCallback } from "vfx-composer" 4 | import { Emitter, InstancedParticles } from "vfx-composer-r3f" 5 | import { useAsset } from "../assets" 6 | 7 | export const BackgroundAsteroids = ({ 8 | amount = 10_000 9 | }: { 10 | amount?: number 11 | }) => { 12 | const gltf = useAsset.asteroid() 13 | const mesh = gltf.scene.children[0] as Mesh 14 | 15 | const setup: InstanceSetupCallback = ({ position, rotation, scale }) => { 16 | position.set(plusMinus(1000), plusMinus(1000), between(-30, -500)) 17 | rotation.random() 18 | scale.setScalar(between(0.5, 4)) 19 | } 20 | 21 | return ( 22 | 27 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/menu/AsteroidField.tsx: -------------------------------------------------------------------------------- 1 | import { useGLTF } from "@react-three/drei" 2 | import { insideSphere, power } from "randomish" 3 | import { Material, Mesh, Vector3 } from "three" 4 | import { Emitter, InstancedParticles } from "vfx-composer-r3f" 5 | 6 | export const AsteroidField = () => { 7 | const gltf = useGLTF("/models/asteroid03.gltf") 8 | const mesh = gltf.scene.children[0] as Mesh 9 | 10 | return ( 11 | 12 | 17 | { 21 | const pos = insideSphere(1000) 22 | position.copy(pos as Vector3) 23 | scale.setScalar(0.5 + power(5) * 10) 24 | rotation.random() 25 | }} 26 | /> 27 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/menu/PostProcessing.tsx: -------------------------------------------------------------------------------- 1 | import * as RC from "render-composer" 2 | import { makeStore, useStore } from "statery" 3 | import { Mesh } from "three" 4 | import { useAsset } from "../../assets" 5 | 6 | export const store = makeStore({ 7 | sun: null as Mesh | null 8 | }) 9 | 10 | export const PostProcessing = () => { 11 | const { sun } = useStore(store) 12 | const texture = useAsset.textures.lensdirt() 13 | 14 | return ( 15 | 16 | 17 | 18 | {sun && } 19 | 20 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /apps/spacerage/src/scenes/menu/sfx/MenuDroneSFX.tsx: -------------------------------------------------------------------------------- 1 | import { Filter, Gain, Oscillator, Reverb } from "audio-composer" 2 | 3 | export const MenuDroneSFX = () => ( 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /apps/spacerage/src/state.ts: -------------------------------------------------------------------------------- 1 | import { createStateMachine } from "state-composer" 2 | import { makeStore } from "statery" 3 | import { PerspectiveCamera } from "three" 4 | import tunnel from "tunnel-rat" 5 | 6 | export type GameState = "nothing" | "menu" | "gameplay" 7 | 8 | export const GameState = createStateMachine("gameplay") 9 | 10 | export const startGame = () => GameState.enter("gameplay") 11 | 12 | /* A global store for global things. This will eventually be replaced 13 | with more Miniplex entities. */ 14 | export const store = makeStore({ 15 | camera: null as PerspectiveCamera | null 16 | }) 17 | 18 | export const SidebarTunnel = tunnel() 19 | -------------------------------------------------------------------------------- /apps/spacerage/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/spacerage/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/spacerage/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/spacerage/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }], 3 | "github": { 4 | "silent": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/spacerage/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import react from "@vitejs/plugin-react" 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | build: { 8 | sourcemap: true 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # examples 2 | 3 | ## 0.1.0 4 | 5 | ### Minor Changes 6 | 7 | - 06b5fad: **Added:** Implemented an actual examples app. 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies [06b5fad] 12 | - Updated dependencies [479bc8a] 13 | - timeline-composer@0.1.0 14 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Timeline Composer for React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timeline-composer-examples", 3 | "private": true, 4 | "version": "0.1.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "timeline-composer": "^0.1.7" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.21", 18 | "@types/react-dom": "^18.0.6", 19 | "@vitejs/plugin-react": "^2.1.0", 20 | "typescript": "^4.9.3", 21 | "vite": "^3.1.6" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | background-color: #fff; 7 | color: #444; 8 | font: 20px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 9 | 10 | padding: 2rem; 11 | } 12 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/timeline-composer-examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | UI Composer Examples 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui-composer-examples", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@hmans/things": "^0.0.6", 13 | "@react-three/drei": "^9.34.3", 14 | "@react-three/fiber": "^8.8.9", 15 | "@stitches/react": "^1.2.8", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "render-composer": "workspace:^0.2.8", 19 | "three": "^0.145.0", 20 | "ui-composer": "workspace:^0.0.1" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.0.21", 24 | "@types/react-dom": "^18.0.6", 25 | "@types/three": "^0.144.0", 26 | "@vitejs/plugin-react": "^2.1.0", 27 | "typescript": "^4.9.3", 28 | "vite": "^3.1.6" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/ui-composer-examples/src/index.css -------------------------------------------------------------------------------- /apps/ui-composer-examples/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/ui-composer-examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # vfx-composer-examples 2 | 3 | ## 0.0.1 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies [03215af] 8 | - Updated dependencies [8ca879b] 9 | - Updated dependencies [8ca879b] 10 | - Updated dependencies [8ca879b] 11 | - Updated dependencies [a9a91ed] 12 | - Updated dependencies [82ad766] 13 | - Updated dependencies [8ca879b] 14 | - material-composer@0.1.2 15 | - shader-composer@0.3.4 16 | - vfx-composer-r3f@0.2.1 17 | - material-composer-r3f@0.1.2 18 | - vfx-composer@0.2.1 19 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | VFX Composer Examples 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/public/models/ImphenziaPalette01-256-GradientX4-Emission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/public/models/ImphenziaPalette01-256-GradientX4-Emission.png -------------------------------------------------------------------------------- /apps/vfx-composer-examples/public/models/ImphenziaPalette01-256-GradientX4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/public/models/ImphenziaPalette01-256-GradientX4.png -------------------------------------------------------------------------------- /apps/vfx-composer-examples/public/models/spaceship26.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/public/models/spaceship26.bin -------------------------------------------------------------------------------- /apps/vfx-composer-examples/public/models/spaceship26_mod.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/public/models/spaceship26_mod.bin -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/ControlledParticles.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from "@react-three/fiber" 2 | import { useRef } from "react" 3 | import { Group } from "three" 4 | import { Particle, InstancedParticles } from "vfx-composer-r3f" 5 | 6 | export default function ControlledParticlesExample() { 7 | const group = useRef(null!) 8 | 9 | useFrame(({ clock }) => { 10 | group.current.position.set( 11 | Math.cos(clock.elapsedTime * 1.3), 12 | Math.sin(clock.elapsedTime * 1.7), 13 | 0 14 | ) 15 | }, -100) 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/Playground.tsx: -------------------------------------------------------------------------------- 1 | import { composable, modules } from "material-composer-r3f" 2 | import { FrameTime, Mul } from "shader-composer" 3 | import { Color } from "three" 4 | 5 | export default function Playground() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/SoftParticlesExample.tsx: -------------------------------------------------------------------------------- 1 | import { composable, modules } from "material-composer-r3f" 2 | import { FlatStage, Layers, useRenderPipeline } from "r3f-stage" 3 | import { useUniformUnit } from "shader-composer/r3f" 4 | import { Emitter, InstancedParticles } from "vfx-composer-r3f" 5 | 6 | export const SoftParticlesExample = () => { 7 | const depthTexture = useUniformUnit("sampler2D", useRenderPipeline().depth) 8 | 9 | return ( 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/lib/loop.tsx: -------------------------------------------------------------------------------- 1 | const clamp = (value: number, min: number, max: number) => 2 | Math.min(Math.max(value, min), max) 3 | 4 | const clamp01 = (value: number) => clamp(value, 0, 1) 5 | 6 | export const loop = (callback: (dt: number) => void) => { 7 | let running = true 8 | let lastTime = performance.now() 9 | 10 | const tick = () => { 11 | if (running) { 12 | requestAnimationFrame(tick) 13 | 14 | const now = performance.now() 15 | const dt = clamp01((now - lastTime) / 1000) 16 | lastTime = now 17 | 18 | callback(dt) 19 | } 20 | } 21 | 22 | tick() 23 | 24 | return () => { 25 | running = false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/modules/Lava.ts: -------------------------------------------------------------------------------- 1 | import { ModuleFactory } from "material-composer" 2 | import { moduleComponent } from "material-composer-r3f" 3 | import { Heat, HeatOptions } from "material-composer/units" 4 | import { Gradient } from "shader-composer" 5 | import { Color } from "three" 6 | 7 | export type LavaProps = HeatOptions 8 | 9 | export const LavaModule: ModuleFactory = (props) => (state) => ({ 10 | ...state, 11 | color: Gradient( 12 | Heat(state.position, props), 13 | [new Color("#03071E"), 0], 14 | [new Color("#03071E"), 0.1], 15 | [new Color("#DC2F02").multiplyScalar(1), 0.5], 16 | [new Color("#E85D04").multiplyScalar(10), 0.6], 17 | [new Color("#FFBA08").multiplyScalar(10), 0.65], 18 | [new Color("white").multiplyScalar(1), 0.99], 19 | [new Color("white").multiplyScalar(1), 1] 20 | ) 21 | }) 22 | 23 | export const Lava = moduleComponent(LavaModule) 24 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/textures/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/src/examples/textures/cloud.png -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/textures/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/src/examples/textures/explosion.png -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/textures/hexgrid.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/src/examples/textures/hexgrid.jpeg -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/textures/index.ts: -------------------------------------------------------------------------------- 1 | export { default as cloudUrl } from "./cloud.png" 2 | export { default as particleUrl } from "./particle.png" 3 | export { default as smokeUrl } from "./smoke.png" 4 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/textures/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/src/examples/textures/particle.png -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/textures/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/src/examples/textures/smoke.png -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/textures/stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmans/composer-suite/5dda73920d4fb7752cecbc37f6896428ff98340f/apps/vfx-composer-examples/src/examples/textures/stream.png -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/examples/units/NoiseMask.tsx: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/function" 2 | import { 3 | Add, 4 | Div, 5 | GlobalTime, 6 | Input, 7 | Mul, 8 | Negate, 9 | NormalizePlusMinusOne, 10 | OneMinus, 11 | Saturate, 12 | ScaleAndOffset, 13 | Smoothstep, 14 | Sub, 15 | UV 16 | } from "shader-composer" 17 | import { PSRDNoise2D } from "shader-composer" 18 | 19 | /* TODO: extract this into SC or SC-toybox or similar? */ 20 | 21 | export const NoiseMask = ( 22 | threshold: Input<"float"> = 0.5, 23 | fringe: Input<"float"> = 0.5, 24 | time: Input<"float"> = GlobalTime 25 | ) => { 26 | const noise = NormalizePlusMinusOne( 27 | PSRDNoise2D(ScaleAndOffset(UV, [8, 8], [0, Negate(time)])) 28 | ) 29 | 30 | return pipe( 31 | Smoothstep( 32 | Sub(threshold, Div(fringe, 2)), 33 | Add(threshold, Div(fringe, 2)), 34 | OneMinus(UV.y) 35 | ), 36 | (v) => Sub(v, Mul(noise, threshold)), 37 | Saturate 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom/client" 3 | import App from "./App" 4 | 5 | ReactDOM.createRoot(document.getElementById("root")!).render( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }], 3 | "github": { 4 | "silent": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/vfx-composer-examples/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite" 2 | import react from "@vitejs/plugin-react" 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | optimizeDeps: { 8 | exclude: ["vfx-composer", "vfx-composer-r3f", "render-composer"] 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /archive/react-game-starter/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | tab_width = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /archive/react-game-starter/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /archive/react-game-starter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # react-game-starter 2 | 3 | ## 0.0.2 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies [d3e2b88] 8 | - Updated dependencies [a962a31] 9 | - Updated dependencies [8519e17] 10 | - Updated dependencies [e699129] 11 | - shader-composer-r3f@0.4.5 12 | - shader-composer@0.4.5 13 | - vfx-composer@0.3.0 14 | 15 | ## 0.0.1 16 | 17 | ### Patch Changes 18 | 19 | - Updated dependencies [a92d0d3] 20 | - Updated dependencies [433f93b] 21 | - Updated dependencies [765b29d] 22 | - Updated dependencies [c3dcb12] 23 | - Updated dependencies [411f406] 24 | - Updated dependencies [9406986] 25 | - Updated dependencies [6d99c19] 26 | - Updated dependencies [765b29d] 27 | - shader-composer@0.4.0 28 | - shader-composer-r3f@0.4.0 29 | - timeline-composer@0.1.6 30 | - shader-composer-toybox@0.1.3 31 | - vfx-composer@0.2.3 32 | -------------------------------------------------------------------------------- /archive/react-game-starter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Game Starter 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | } 13 | .logo:hover { 14 | filter: drop-shadow(0 0 2em #646cffaa); 15 | } 16 | .logo.react:hover { 17 | filter: drop-shadow(0 0 2em #61dafbaa); 18 | } 19 | 20 | @keyframes logo-spin { 21 | from { 22 | transform: rotate(0deg); 23 | } 24 | to { 25 | transform: rotate(360deg); 26 | } 27 | } 28 | 29 | @media (prefers-reduced-motion: no-preference) { 30 | a:nth-of-type(2) .logo { 31 | animation: logo-spin infinite 20s linear; 32 | } 33 | } 34 | 35 | .card { 36 | padding: 2em; 37 | } 38 | 39 | .read-the-docs { 40 | color: #888; 41 | } 42 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/Background.tsx: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/function" 2 | import { 3 | Add, 4 | Mul, 5 | Round, 6 | ShaderMaterialRoot, 7 | Time, 8 | VertexPosition 9 | } from "shader-composer" 10 | import { useShader } from "@shader-composer/r3f" 11 | import { Simplex3DNoise } from "shader-composer-toybox" 12 | import { Color } from "three" 13 | 14 | export const Background = () => { 15 | const shader = useShader(() => { 16 | return ShaderMaterialRoot({ 17 | color: pipe( 18 | VertexPosition, 19 | (v) => Mul(v, 0.5), 20 | (v) => Round(v), 21 | (v) => Add(v, Time()), 22 | (v) => Simplex3DNoise(v), 23 | (v) => Mul(v, 0.1), 24 | (v) => Add(v, 0.1), 25 | (v) => Mul(new Color("#333"), v) 26 | ) 27 | }) 28 | }, []) 29 | 30 | return ( 31 | 32 | 33 | 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/Controller.tsx: -------------------------------------------------------------------------------- 1 | import { controller } from "./input/controller" 2 | import { useController } from "./lib/useController" 3 | 4 | export const Controller = () => { 5 | /* TODO: Combine this with the controler definition in controller.ts */ 6 | /* Initialize and update game input */ 7 | useController(controller) 8 | 9 | return null 10 | } 11 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/PerformanceMonitor.tsx: -------------------------------------------------------------------------------- 1 | import { Perf } from "r3f-perf" 2 | 3 | export const PerformanceMonitor = () => ( 4 | 5 | ) 6 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | width: 100vw; 4 | height: 100vh; 5 | margin: 0; 6 | padding: 0; 7 | overflow: hidden; 8 | user-select: none; 9 | background-color: black; 10 | color: #ccc; 11 | } 12 | 13 | body { 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | 17 | font: 18px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 18 | color: #f2f2f2; 19 | } 20 | 21 | div#root { 22 | height: 100vh; 23 | } 24 | 25 | canvas { 26 | position: fixed; 27 | width: 100vw; 28 | height: 100vh; 29 | } 30 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/ComponentRenderLogger.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Profiler, 3 | ProfilerOnRenderCallback, 4 | ReactNode, 5 | useCallback 6 | } from "react" 7 | 8 | export function ComponentRenderLogger({ children }: { children?: ReactNode }) { 9 | const onRender = useCallback((id, phase) => { 10 | console.log("React component rendered:", phase) 11 | }, []) 12 | 13 | return ( 14 | 15 | {children} 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/Effect.tsx: -------------------------------------------------------------------------------- 1 | import { EffectCallback, useEffect } from "react" 2 | 3 | export const Effect = ({ callback }: { callback?: EffectCallback }) => { 4 | useEffect(callback) 5 | return null 6 | } 7 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/Keypress.tsx: -------------------------------------------------------------------------------- 1 | import { useKeypress } from "./useKeypress" 2 | 3 | export const Keypress = ({ 4 | code, 5 | onPress 6 | }: { 7 | code: string 8 | onPress: () => void 9 | }) => { 10 | useKeypress(code, onPress) 11 | return null 12 | } 13 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/README.md: -------------------------------------------------------------------------------- 1 | This folder contains a set of helper functions, modules and components that, 2 | in many cases, _should_ be extracted or moved to some other library. 3 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/aabb.ts: -------------------------------------------------------------------------------- 1 | export type AABB = { 2 | x: number 3 | y: number 4 | width: number 5 | height: number 6 | } 7 | 8 | export const AABB = ( 9 | x: number, 10 | y: number, 11 | width: number, 12 | height: number 13 | ): AABB => ({ 14 | x, 15 | y, 16 | width, 17 | height 18 | }) 19 | 20 | export const isColliding = (r1: AABB, r2: AABB) => 21 | !( 22 | r1.y + r1.height < r2.y || 23 | r1.x > r2.x + r2.width || 24 | r1.y > r2.y + r2.height || 25 | r1.x + r1.width < r2.x 26 | ) 27 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/camera-composer/Camera.tsx: -------------------------------------------------------------------------------- 1 | import { useThree } from "@react-three/fiber" 2 | import { useLayoutEffect, useRef } from "react" 3 | import { PerspectiveCamera } from "three" 4 | 5 | export function Camera() { 6 | const camera = useRef(null!) 7 | const set = useThree(({ set }) => set) 8 | const size = useThree(({ size }) => size) 9 | 10 | /* Adjust the camera's aspect ratio to match the canvas. */ 11 | useLayoutEffect(() => { 12 | if (!camera.current) return 13 | camera.current.aspect = size.width / size.height 14 | }, [size]) 15 | 16 | /* Set the camera as the active camera */ 17 | useLayoutEffect(() => { 18 | if (!camera.current) return 19 | set({ camera: camera.current }) 20 | }, [set, camera]) 21 | 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/camera-composer/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Camera" 2 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/timeline-composer/Lifetime.tsx: -------------------------------------------------------------------------------- 1 | import { FC, ReactNode } from "react" 2 | import { useDelay } from "./Delay" 3 | 4 | type LifetimeProps = { 5 | children?: ReactNode 6 | seconds: number 7 | } 8 | 9 | export const Lifetime: FC = ({ children, seconds }) => { 10 | const ready = useDelay(seconds) 11 | 12 | return ready ? null : <>{children} 13 | } 14 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/timeline-composer/Repeat.tsx: -------------------------------------------------------------------------------- 1 | import { FC, Fragment, ReactNode, useState } from "react" 2 | import { Delay } from "./Delay" 3 | 4 | type RepeatProps = { children: ReactNode; times?: number; interval?: number } 5 | 6 | export const Repeat: FC = ({ 7 | children, 8 | times = Infinity, 9 | interval = 1 10 | }) => { 11 | const [iteration, setIteration] = useState(1) 12 | 13 | return ( 14 | 15 | {iteration < times && ( 16 | setIteration(iteration + 1)} 19 | /> 20 | )} 21 | {children} 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/timeline-composer/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Delay" 2 | export * from "./Lifetime" 3 | export * from "./Repeat" 4 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/lib/useKeypress.ts: -------------------------------------------------------------------------------- 1 | import { KeyCode } from "@hmans/controlfreak" 2 | import { useEffect } from "react" 3 | 4 | export const useKeypress = (code: KeyCode, callback: () => void) => { 5 | useEffect(() => { 6 | const handleKeydown = (event: KeyboardEvent) => { 7 | if (event.code === code) { 8 | callback() 9 | } 10 | } 11 | 12 | window.addEventListener("keydown", handleKeydown) 13 | 14 | return () => { 15 | window.removeEventListener("keydown", handleKeydown) 16 | } 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/ScoreHUD.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from "@react-three/drei" 2 | import { GroupProps } from "@react-three/fiber" 3 | import { useStore } from "statery" 4 | import { enemyColor, playerColor } from "./configuration" 5 | import { store } from "./state" 6 | 7 | export function ScoreHUD(props: GroupProps) { 8 | const { playerScore, enemyScore } = useStore(store) 9 | 10 | return ( 11 | 12 | 13 | {playerScore} 14 | 15 | 16 | {enemyScore} 17 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/configuration.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Just some tiny bits of global configuration to keep things 3 | easy to tweak. 4 | */ 5 | 6 | import { Color } from "three" 7 | 8 | export const courtWidth = 18 9 | export const courtHeight = 10 10 | 11 | export const playerColor = "hsl(130, 100%, 60%)" 12 | export const enemyColor = "hsl(200, 100%, 60%)" 13 | 14 | export const paddleWidth = 0.5 15 | export const paddleHeight = 2.25 16 | export const paddleSpeed = 12 17 | 18 | export const ballRadius = 0.35 19 | 20 | export const wallColor = new Color("hotpink") 21 | 22 | export const wallShake = 0.5 23 | export const paddleShake = 0.5 24 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/entities/Ball.tsx: -------------------------------------------------------------------------------- 1 | import { Vector3 } from "three" 2 | import { Animate, AnimationFunction } from "../../../lib/Animate" 3 | import { ballRadius } from "../configuration" 4 | import { useGameplayStore } from "../state" 5 | import { BallTrailEmitter } from "../vfx/BallTrail" 6 | 7 | const rotate = 8 | (speed: Vector3): AnimationFunction => 9 | (dt, { rotation }) => { 10 | rotation.x += speed.x * dt 11 | rotation.y += speed.y * dt 12 | rotation.z += speed.z * dt 13 | } 14 | 15 | export const Ball = () => ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ) 25 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/entities/Enemy.tsx: -------------------------------------------------------------------------------- 1 | import { courtWidth, enemyColor, paddleWidth } from "../configuration" 2 | import { setGameObject } from "../state" 3 | import { Paddle } from "./Paddle" 4 | 5 | export const Enemy = () => ( 6 | 10 | 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/entities/Paddle.tsx: -------------------------------------------------------------------------------- 1 | import { ColorRepresentation } from "three" 2 | import { paddleHeight, paddleWidth } from "../configuration" 3 | 4 | export type PaddleProps = { 5 | color: ColorRepresentation 6 | } 7 | 8 | export const Paddle = ({ color }: PaddleProps) => ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/entities/Player.tsx: -------------------------------------------------------------------------------- 1 | import { courtWidth, paddleWidth, playerColor } from "../configuration" 2 | import { setGameObject } from "../state" 3 | import { Paddle } from "./Paddle" 4 | 5 | export const Player = () => ( 6 | 10 | 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/entities/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Ball" 2 | export * from "./Player" 3 | export * from "./Enemy" 4 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/systems/CameraSystem.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from "@react-three/fiber" 2 | import { Quaternion, Vector3 } from "three" 3 | import { useGameplayStore } from "../state" 4 | 5 | const center = new Vector3() 6 | const currentQuat = new Quaternion() 7 | const targetQuat = new Quaternion() 8 | 9 | export const CameraSystem = () => { 10 | const { cameraTarget } = useGameplayStore() 11 | 12 | useFrame(({ camera }, dt) => { 13 | if (!cameraTarget) return 14 | 15 | /* Move camera target back to screen center */ 16 | cameraTarget.position.lerp(center, 0.1) 17 | 18 | /* Smoothly follow camera target */ 19 | currentQuat.copy(camera.quaternion) 20 | camera.lookAt(cameraTarget.position) 21 | targetQuat.copy(camera.quaternion) 22 | camera.quaternion.slerpQuaternions(currentQuat, targetQuat, 0.1) 23 | }) 24 | 25 | return null 26 | } 27 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/systems/EnemySystem.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from "@react-three/fiber" 2 | import { between, chance } from "randomish" 3 | import { paddleSpeed } from "../configuration" 4 | import { store, useGameplayStore } from "../state" 5 | 6 | export const EnemySystem = () => { 7 | const { enemy, enemySlack, ball } = useGameplayStore() 8 | 9 | useFrame((_, dt) => { 10 | if (!enemy || !ball) return 11 | 12 | /* Determine a target height */ 13 | const targetHeight = ball.position.y 14 | 15 | if (enemy.position.y > targetHeight + enemySlack) { 16 | enemy.position.y -= dt * paddleSpeed 17 | } else if (enemy.position.y < targetHeight - enemySlack) { 18 | enemy.position.y += dt * paddleSpeed 19 | } 20 | 21 | /* Randomly change slack */ 22 | if (chance(0.01)) { 23 | store.set({ enemySlack: between(1, 5) }) 24 | } 25 | }) 26 | 27 | return null 28 | } 29 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Gameplay/systems/Systems.tsx: -------------------------------------------------------------------------------- 1 | import { BallSystem } from "./BallSystem" 2 | import { CameraSystem } from "./CameraSystem" 3 | import { EnemySystem } from "./EnemySystem" 4 | import { PaddleSystem } from "./PaddleSystem" 5 | 6 | export const Systems = () => ( 7 | <> 8 | 9 | 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/scenes/Title/TitleScene.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from "@react-three/drei" 2 | import { Keypress } from "../../lib/Keypress" 3 | import { enterGameplay } from "../../state" 4 | 5 | export const TitleScene = () => ( 6 | <> 7 | 8 | 9 | {"PRESS SPACE\nTO PONG"} 10 | 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/state.ts: -------------------------------------------------------------------------------- 1 | import { makeFSM } from "./lib/makeFSM" 2 | import { initializeGameplay } from "./scenes/Gameplay/state" 3 | 4 | type State = "title" | "gameplay" 5 | 6 | const { MatchState, enterState, isCurrentState } = makeFSM("title") 7 | 8 | export { MatchState } 9 | 10 | export const enterGameplay = () => { 11 | if (!isCurrentState("title")) return 12 | initializeGameplay() 13 | enterState("gameplay") 14 | } 15 | 16 | export const returnToTitle = () => 17 | isCurrentState("gameplay") && enterState("title") 18 | -------------------------------------------------------------------------------- /archive/react-game-starter/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /archive/react-game-starter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": false, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /archive/react-game-starter/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /archive/react-game-starter/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "silent": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /archive/react-game-starter/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | "@babel/preset-env", 4 | "@babel/preset-react", 5 | [ 6 | "@babel/preset-typescript", 7 | { 8 | isTSX: true, 9 | allExtensions: true 10 | } 11 | ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /docs.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap"); 2 | 3 | body { 4 | font: 18px/1.5 "Roboto"; 5 | } 6 | 7 | .tsd-filter-visibility { 8 | display: none; 9 | } 10 | -------------------------------------------------------------------------------- /jest.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | preset: "ts-jest", 4 | testMatch: ["**/?(*.)+(spec|test).+(ts|tsx)"], 5 | testPathIgnorePatterns: ["node_modules"], 6 | testEnvironment: "jsdom", 7 | moduleFileExtensions: ["js", "ts", "tsx"], 8 | globals: { 9 | "ts-jest": { 10 | isolatedModules: true 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/audio-composer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Audio Composer 2 | 3 | ## 0.0.2 4 | 5 | ### Patch Changes 6 | 7 | - e075cfb: First release! 8 | -------------------------------------------------------------------------------- /packages/audio-composer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "audio-composer", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "Audio!", 9 | "version": "0.0.2", 10 | "main": "dist/audio-composer.cjs.js", 11 | "module": "dist/audio-composer.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | [ 27 | "@babel/preset-typescript", 28 | { 29 | "isTSX": true, 30 | "allExtensions": true 31 | } 32 | ] 33 | ] 34 | }, 35 | "peerDependencies": { 36 | "@react-three/fiber": "^8.7.0", 37 | "react": "^18.2.0", 38 | "three": "^0.144.0" 39 | }, 40 | "dependencies": { 41 | "@hmans/use-const": "^0.0.1", 42 | "statery": "^0.6.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/audio-composer/src/AudioContext.tsx: -------------------------------------------------------------------------------- 1 | import { useConst } from "@hmans/use-const" 2 | import React, { createContext, ReactNode } from "react" 3 | import * as THREE from "three" 4 | 5 | export const AudioNodeContext = createContext(null!) 6 | 7 | export type AudioContextProps = { 8 | children?: ReactNode 9 | } 10 | 11 | export const AudioContext = ({ children }: AudioContextProps) => { 12 | const context = useConst(() => THREE.AudioContext.getContext()) 13 | 14 | return ( 15 | 16 | {children} 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /packages/audio-composer/src/AudioListener.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useLayoutEffect, useRef } from "react" 2 | import { AudioNodeContext } from "./AudioContext" 3 | import { store } from "./store" 4 | 5 | export const AudioListener = () => { 6 | const listener = useRef(null!) 7 | 8 | const context = useContext(AudioNodeContext) 9 | 10 | useLayoutEffect(() => { 11 | store.set({ listener: listener.current }) 12 | 13 | return () => { 14 | store.set({ listener: null }) 15 | } 16 | }, [listener]) 17 | 18 | useLayoutEffect(() => { 19 | // listener.current.setFilter(context) 20 | listener.current.gain.connect(context) 21 | 22 | return () => { 23 | listener.current.gain.disconnect() 24 | } 25 | }, [listener, context]) 26 | 27 | return 28 | } 29 | -------------------------------------------------------------------------------- /packages/audio-composer/src/Compressor.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { AudioNodeContext } from "./AudioContext" 3 | import { useAudioNode } from "./hooks" 4 | 5 | export type CompressorProps = { 6 | children?: React.ReactNode 7 | } & DynamicsCompressorOptions 8 | 9 | export const Compressor = React.forwardRef< 10 | DynamicsCompressorNode, 11 | CompressorProps 12 | >(({ children, ...props }, ref) => { 13 | const node = useAudioNode((ctx) => ctx.createDynamicsCompressor(), ref) 14 | 15 | const t = node.context.currentTime 16 | node.threshold.setValueAtTime(-50, t) 17 | node.knee.setValueAtTime(40, t) 18 | node.ratio.setValueAtTime(12, t) 19 | node.attack.setValueAtTime(0, t) 20 | node.release.setValueAtTime(0.25, t) 21 | 22 | return ( 23 | 24 | {children} 25 | 26 | ) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/audio-composer/src/Filter.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef, ReactNode, useLayoutEffect } from "react" 2 | import { AudioNodeContext } from "./AudioContext" 3 | import { useAudioNode } from "./hooks" 4 | 5 | export type FilterProps = { 6 | children?: ReactNode 7 | type?: BiquadFilterType 8 | frequency?: number 9 | Q?: number 10 | } 11 | 12 | export const Filter = forwardRef( 13 | ({ children, type = "allpass", frequency = 2000, Q }, ref) => { 14 | const node = useAudioNode((ctx) => ctx.createBiquadFilter(), ref) 15 | 16 | useLayoutEffect(() => { 17 | node.type = type 18 | }, [node, type]) 19 | 20 | useLayoutEffect(() => { 21 | node.frequency.setTargetAtTime(frequency, node.context.currentTime, 0.1) 22 | }, [node, frequency]) 23 | 24 | useLayoutEffect(() => { 25 | if (Q !== undefined) node.Q.value = Q 26 | }, [node, Q]) 27 | 28 | return ( 29 | 30 | {children} 31 | 32 | ) 33 | } 34 | ) 35 | -------------------------------------------------------------------------------- /packages/audio-composer/src/Gain.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef, ReactNode, useLayoutEffect } from "react" 2 | import { AudioNodeContext } from "./AudioContext" 3 | import { useAudioNode } from "./hooks" 4 | 5 | export type GainProps = { 6 | children?: ReactNode 7 | volume?: number 8 | target?: string 9 | } 10 | 11 | export const Gain = forwardRef( 12 | ({ volume = 0.5, children, target }, ref) => { 13 | const node = useAudioNode((ctx) => ctx.createGain(), ref, target) 14 | 15 | useLayoutEffect(() => { 16 | node.gain.value = volume 17 | }, [volume]) 18 | 19 | return ( 20 | 21 | {children} 22 | 23 | ) 24 | } 25 | ) 26 | -------------------------------------------------------------------------------- /packages/audio-composer/src/LinearRamp.tsx: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect as useEffect } from "react" 2 | import { useAudioContext } from "./hooks" 3 | 4 | export type LinearRampProps = { 5 | property: string 6 | to: number 7 | duration: number 8 | } 9 | 10 | export const LinearRamp = ({ to, duration, property }: LinearRampProps) => { 11 | const parent = useAudioContext() as any 12 | 13 | useEffect(() => { 14 | if (!parent) return 15 | 16 | const time = parent.context.currentTime 17 | const prop = parent[property] as AudioParam 18 | 19 | if (!prop) { 20 | console.error("Invalid property", property) 21 | return 22 | } 23 | 24 | prop.setValueAtTime(prop.value, time) 25 | prop.linearRampToValueAtTime(to, time + duration) 26 | }, [parent]) 27 | 28 | return null 29 | } 30 | -------------------------------------------------------------------------------- /packages/audio-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./AudioContext" 2 | export * from "./AudioListener" 3 | export * from "./Compressor" 4 | export * from "./Filter" 5 | export * from "./Gain" 6 | export * from "./hooks" 7 | export * from "./LinearRamp" 8 | export * from "./Oscillator" 9 | export * from "./PositionalAudio" 10 | export * from "./Reverb" 11 | -------------------------------------------------------------------------------- /packages/audio-composer/src/store.ts: -------------------------------------------------------------------------------- 1 | import { makeStore } from "statery" 2 | import * as THREE from "three" 3 | 4 | export const store = makeStore({ 5 | listener: null as THREE.AudioListener | null 6 | }) 7 | -------------------------------------------------------------------------------- /packages/audio-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/camera-composer/README.md: -------------------------------------------------------------------------------- 1 | # Camera Composer 2 | -------------------------------------------------------------------------------- /packages/camera-composer/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/camera-composer-react.cjs.js", 3 | "module": "dist/camera-composer-react.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/camera-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from "three" 2 | 3 | export const moveForward = (camera: Camera) => { 4 | camera.translateZ(-1) 5 | } 6 | -------------------------------------------------------------------------------- /packages/camera-composer/src/react/index.ts: -------------------------------------------------------------------------------- 1 | export const react = "cool" 2 | -------------------------------------------------------------------------------- /packages/camera-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/hmans-event/README.md: -------------------------------------------------------------------------------- 1 | # @hmans/event 2 | -------------------------------------------------------------------------------- /packages/hmans-event/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hmans/event", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "", 9 | "version": "0.0.1", 10 | "main": "dist/hmans-event.cjs.js", 11 | "module": "dist/hmans-event.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | [ 26 | "@babel/preset-typescript", 27 | { 28 | "isTSX": true, 29 | "allExtensions": true 30 | } 31 | ] 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/hmans-event/src/index.ts: -------------------------------------------------------------------------------- 1 | export type Listener

= (payload: P) => void 2 | 3 | export class Event

{ 4 | listeners = new Set>() 5 | 6 | constructor() { 7 | this.emit = this.emit.bind(this) 8 | } 9 | 10 | clear() { 11 | this.listeners.clear() 12 | } 13 | 14 | addListener(listener: Listener

) { 15 | this.listeners.add(listener) 16 | return () => this.removeListener(listener) 17 | } 18 | 19 | removeListener(listener: Listener

) { 20 | this.listeners.delete(listener) 21 | } 22 | 23 | emit(payload: P) { 24 | for (const listener of this.listeners) { 25 | listener(payload) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/hmans-event/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/hmans-r3f-create-loader/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @hmans/r3f-create-loader 2 | 3 | ## 0.0.3 4 | 5 | ### Patch Changes 6 | 7 | - cb0001d: **Fixed:** Improved typings. 8 | 9 | ## 0.0.2 10 | 11 | ### Patch Changes 12 | 13 | - 5218618: **Fixed:** Added a simple README :-) 14 | -------------------------------------------------------------------------------- /packages/hmans-r3f-create-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hmans/r3f-create-loader", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "", 9 | "version": "0.0.3", 10 | "main": "dist/hmans-r3f-create-loader.cjs.js", 11 | "module": "dist/hmans-r3f-create-loader.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | [ 27 | "@babel/preset-typescript", 28 | { 29 | "isTSX": true, 30 | "allExtensions": true 31 | } 32 | ] 33 | ] 34 | }, 35 | "peerDependencies": { 36 | "@react-three/fiber": "^8.8.7" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/hmans-r3f-create-loader/src/index.ts: -------------------------------------------------------------------------------- 1 | import { LoaderResult, useLoader } from "@react-three/fiber" 2 | 3 | /** 4 | * Creates and returns a loader hook for the given asset, and immediately 5 | * queues the asset for preloading. 6 | * 7 | * @param loader The loader class to use for loading the asset. 8 | * @param url The URL of the asset. 9 | * @returns The loader hook you can use in your component to access the asset. 10 | */ 11 | export const createLoader = ( 12 | loader: new () => LoaderResult, 13 | url: string 14 | ) => { 15 | useLoader.preload(loader, url) 16 | return () => useLoader(loader, url) 17 | } 18 | -------------------------------------------------------------------------------- /packages/hmans-r3f-create-loader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/hmans-use-mutable-list/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @hmans/use-mutable-list 2 | 3 | ## 0.0.2 4 | 5 | ### Patch Changes 6 | 7 | - 2a56a89: The mutable list API now also contains a `useItem` hook that makes adding something to the list much more convenient. 8 | -------------------------------------------------------------------------------- /packages/hmans-use-mutable-list/README.md: -------------------------------------------------------------------------------- 1 | # useMutableList 2 | -------------------------------------------------------------------------------- /packages/hmans-use-mutable-list/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hmans/use-mutable-list", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "", 9 | "version": "0.0.2", 10 | "main": "dist/hmans-use-mutable-list.cjs.js", 11 | "module": "dist/hmans-use-mutable-list.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | [ 27 | "@babel/preset-typescript", 28 | { 29 | "isTSX": true, 30 | "allExtensions": true 31 | } 32 | ] 33 | ] 34 | }, 35 | "peerDependencies": { 36 | "react": "^18.2.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/hmans-use-mutable-list/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/hmans-use-nullable-state/README.md: -------------------------------------------------------------------------------- 1 | # useNullableState 2 | -------------------------------------------------------------------------------- /packages/hmans-use-nullable-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hmans/use-nullable-state", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "", 9 | "version": "0.0.1", 10 | "main": "dist/hmans-use-nullable-state.cjs.js", 11 | "module": "dist/hmans-use-nullable-state.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | [ 27 | "@babel/preset-typescript", 28 | { 29 | "isTSX": true, 30 | "allExtensions": true 31 | } 32 | ] 33 | ] 34 | }, 35 | "peerDependencies": { 36 | "react": "^18.2.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/hmans-use-nullable-state/src/index.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | 3 | export const useNullableState = (initialValue: T | (() => T) = null!) => 4 | useState(initialValue) 5 | -------------------------------------------------------------------------------- /packages/hmans-use-nullable-state/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/input-composer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # input-composer 2 | 3 | ## 0.0.2 4 | 5 | ### Patch Changes 6 | 7 | - 362dac2: First release with some actual input composition bits. :-) 8 | -------------------------------------------------------------------------------- /packages/input-composer/README.md: -------------------------------------------------------------------------------- 1 | # Input Composer 2 | -------------------------------------------------------------------------------- /packages/input-composer/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/input-composer-react.cjs.js", 3 | "module": "dist/input-composer-react.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/input-composer/src/lib/event.ts: -------------------------------------------------------------------------------- 1 | export type Listener

= (payload: P) => void 2 | 3 | export class Event

{ 4 | listeners = new Set>() 5 | 6 | constructor() { 7 | this.emit = this.emit.bind(this) 8 | } 9 | 10 | clear() { 11 | this.listeners.clear() 12 | } 13 | 14 | addListener(listener: Listener

) { 15 | this.listeners.add(listener) 16 | return () => this.removeListener(listener) 17 | } 18 | 19 | removeListener(listener: Listener

) { 20 | this.listeners.delete(listener) 21 | } 22 | 23 | emit(payload: P) { 24 | for (const listener of this.listeners) { 25 | listener(payload) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/input-composer/src/react.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from "@react-three/fiber" 2 | import { useEffect } from "react" 3 | import { Controller as ControllerImpl } from "./index" 4 | 5 | export const useController = ( 6 | controller: ControllerImpl, 7 | renderPriority?: number 8 | ) => { 9 | useEffect(() => { 10 | controller.start() 11 | return () => controller.stop() 12 | }) 13 | 14 | useFrame(() => { 15 | controller.update() 16 | }, renderPriority) 17 | } 18 | 19 | export const Controller = ({ 20 | controller, 21 | updatePriority 22 | }: { 23 | controller: ControllerImpl 24 | updatePriority?: number 25 | }) => { 26 | useController(controller, updatePriority) 27 | return null 28 | } 29 | -------------------------------------------------------------------------------- /packages/input-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/material-composer-patch-material/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@material-composer/patch-material", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hendrik.mans.de" 7 | }, 8 | "description": "", 9 | "version": "0.1.3", 10 | "main": "dist/material-composer-patch-material.cjs.js", 11 | "module": "dist/material-composer-patch-material.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | "@babel/preset-typescript" 27 | ] 28 | }, 29 | "devDependencies": { 30 | "@shader-composer/core": "workspace:^0.4.9" 31 | }, 32 | "peerDependencies": { 33 | "@shader-composer/core": ">=0.4.9", 34 | "three": ">=0.141.0" 35 | }, 36 | "dependencies": { 37 | "@hmans/types": "^0.0.3", 38 | "fp-ts": "^2.12.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/material-composer-patch-material/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./root" 2 | export * from "./patchMaterial" 3 | -------------------------------------------------------------------------------- /packages/material-composer-patch-material/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/material-composer-patched/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./patched" 2 | 3 | /* TODO: remove legacy API in a future version */ 4 | export { Patched as patched } from "./patched" 5 | -------------------------------------------------------------------------------- /packages/material-composer-patched/src/lib/useManagedInstance.tsx: -------------------------------------------------------------------------------- 1 | import { DependencyList, useLayoutEffect, useMemo } from "react" 2 | 3 | export const useManagedPrimitive = ( 4 | factory: () => T, 5 | deps: DependencyList = [] 6 | ) => { 7 | const instance = useMemo(() => factory(), deps) 8 | useLayoutEffect(() => () => (instance as any).dispose?.(), [instance]) 9 | return instance 10 | } 11 | -------------------------------------------------------------------------------- /packages/material-composer-patched/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/material-composer-r3f/README.md: -------------------------------------------------------------------------------- 1 | # material-composer-r3f 2 | 3 | ...needs a README :-) 4 | -------------------------------------------------------------------------------- /packages/material-composer-r3f/src/Layer.tsx: -------------------------------------------------------------------------------- 1 | import { Layer as LayerImpl, LayerArgs } from "material-composer" 2 | import React, { ReactNode, useMemo } from "react" 3 | import { useDetectShallowChange } from "./lib/use-detect-shallow-change" 4 | import { 5 | ModuleRegistrationContext, 6 | provideModuleRegistration, 7 | useModuleRegistration 8 | } from "./moduleRegistration" 9 | 10 | export type LayerProps = LayerArgs & { children?: ReactNode } 11 | 12 | export const Layer = ({ children, ...props }: LayerProps) => { 13 | const modules = provideModuleRegistration() 14 | 15 | /* Recreate the layer every time the props or modules change */ 16 | const layer = useMemo(() => { 17 | return LayerImpl({ ...props, modules: modules.list }) 18 | }, [modules.version, useDetectShallowChange(props)]) 19 | 20 | /* Register it with the parent */ 21 | useModuleRegistration(layer) 22 | 23 | return ( 24 | 25 | {children} 26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /packages/material-composer-r3f/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@material-composer/patch-material" 2 | export * from "@material-composer/patched" 3 | export * from "./composable" 4 | export * from "./Layer" 5 | export { moduleComponent, ModuleReactor as Modules } from "./reactor" 6 | 7 | /* Legacy */ 8 | /* TODO: remove in future version */ 9 | export { Composable as composable } from "./composable" 10 | export { ModuleReactor as modules } from "./reactor" 11 | -------------------------------------------------------------------------------- /packages/material-composer-r3f/src/lib/use-detect-shallow-change.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react" 2 | 3 | export const useDetectShallowChange = (props: { [key: string]: any }) => { 4 | const version = useRef(0) 5 | const previousProps = useRef() 6 | 7 | const changed = 8 | /* If we don't have a previous state, we have a change! */ 9 | !previousProps.current || 10 | /* If the number of props has changed, we have a change! */ 11 | Object.keys(props).length !== Object.keys(previousProps.current).length || 12 | /* If any of the props have changed, we have a change! */ 13 | Object.keys(props).some((key) => { 14 | return !Object.is(previousProps.current[key], props[key]) 15 | }) 16 | 17 | /* Remember the props for the next render. */ 18 | previousProps.current = props 19 | 20 | return changed ? ++version.current : version 21 | } 22 | -------------------------------------------------------------------------------- /packages/material-composer-r3f/src/moduleRegistration.tsx: -------------------------------------------------------------------------------- 1 | import { MutableListAPI, useMutableList } from "@hmans/use-mutable-list" 2 | import { Module } from "material-composer" 3 | import { createContext, useContext } from "react" 4 | 5 | export const ModuleRegistrationContext = createContext>( 6 | null! 7 | ) 8 | 9 | export const provideModuleRegistration = () => useMutableList() 10 | 11 | export const useModuleRegistration = (module: Module) => { 12 | const api = useContext(ModuleRegistrationContext) 13 | api.useItem(module) 14 | } 15 | -------------------------------------------------------------------------------- /packages/material-composer-r3f/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/material-composer/README.md: -------------------------------------------------------------------------------- 1 | ![material-composer](https://user-images.githubusercontent.com/1061/187884712-9f6e67e4-fb26-4791-94bd-9abbb4898e7c.jpg) 2 | -------------------------------------------------------------------------------- /packages/material-composer/modules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/material-composer-modules.cjs.js", 3 | "module": "dist/material-composer-modules.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/material-composer/src/compileModules.ts: -------------------------------------------------------------------------------- 1 | import { PatchedMaterialRoot } from "@material-composer/patch-material" 2 | import { initialModuleState, Module, pipeModules } from "." 3 | 4 | /** 5 | * Compiles a list of Material Composer modules into a shader graph that 6 | * can be consumed by Shader Composer's `composeShader` function. 7 | * 8 | * @param modules A list of Material Composer modules (see `Module`) 9 | * @returns A shader root node that can be passed to `compileShader` 10 | */ 11 | export const compileModules = (modules: Module[]) => { 12 | /* Transform state with given modules. */ 13 | const state = pipeModules(initialModuleState(), ...modules) 14 | 15 | /* Construct a shader root unit */ 16 | return PatchedMaterialRoot(state) 17 | } 18 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Acceleration.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/function" 2 | import { Input, Mul, Pow } from "shader-composer" 3 | import { ModuleFactory } from ".." 4 | import { Space, Translate } from "./Translate" 5 | 6 | export type AccelerationProps = { 7 | direction: Input<"vec3"> 8 | time: Input<"float"> 9 | space?: Space 10 | } 11 | 12 | export const Acceleration: ModuleFactory = ({ 13 | direction, 14 | time, 15 | space = "world" 16 | }) => 17 | Translate({ 18 | space, 19 | offset: pipe( 20 | direction, 21 | (v) => Mul(v, Pow(time, 2)), 22 | (v) => Mul(v, 0.5) 23 | ) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Billboard.ts: -------------------------------------------------------------------------------- 1 | import { ModuleFactory } from ".." 2 | import { Billboard as BillboardUnit } from "shader-composer-toybox" 3 | 4 | export const Billboard: ModuleFactory = () => (state) => ({ 5 | ...state, 6 | position: BillboardUnit(state.position) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Fresnel.ts: -------------------------------------------------------------------------------- 1 | import { Fresnel as FresnelUnit, FresnelProps } from "shader-composer" 2 | import { ModuleFactory } from ".." 3 | import { Layer } from "../Layer" 4 | import { Color } from "./Color" 5 | 6 | export type FresnelArgs = FresnelProps 7 | 8 | export const Fresnel: ModuleFactory = (props) => 9 | Layer({ 10 | opacity: FresnelUnit(props), 11 | modules: [Color({ color: "white" })] 12 | }) 13 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Lifetime.ts: -------------------------------------------------------------------------------- 1 | import { $, Input, Vec3 } from "shader-composer" 2 | import { ModuleFactory } from ".." 3 | 4 | export const Lifetime: ModuleFactory<{ progress: Input<"float"> }> = ({ 5 | progress 6 | }) => (state) => ({ 7 | ...state, 8 | color: Vec3(state.color, { 9 | fragment: { 10 | body: $`if (${progress} < 0.0 || ${progress} > 1.0) discard;` 11 | } 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Rotate.ts: -------------------------------------------------------------------------------- 1 | import { ModuleFactory } from ".." 2 | import { Input, Mat3, Mul } from "shader-composer" 3 | 4 | type RotateProps = { 5 | rotation: Input<"mat3" | "mat4"> 6 | normal?: boolean 7 | } 8 | 9 | export const Rotate: ModuleFactory = ({ rotation, normal = true }) => ( 10 | state 11 | ) => ({ 12 | ...state, 13 | normal: normal ? Mul(state.normal, Mat3(rotation)) : state.normal, 14 | position: Mul(state.position, Mat3(rotation)) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Scale.ts: -------------------------------------------------------------------------------- 1 | import { ModuleFactory } from ".." 2 | import { Input, Mul } from "shader-composer" 3 | 4 | type ScaleProps = { 5 | scale: Input<"float"> 6 | } 7 | 8 | export const Scale: ModuleFactory = ({ scale = 1 }) => (state) => ({ 9 | ...state, 10 | position: Mul(state.position, scale) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Softness.ts: -------------------------------------------------------------------------------- 1 | import { Input, Mul, Unit } from "shader-composer" 2 | import { Softness as SoftnessUnit } from "shader-composer-toybox" 3 | import { ModuleFactory } from ".." 4 | 5 | export const Softness: ModuleFactory<{ 6 | softness: Input<"float"> 7 | depthTexture: Unit<"sampler2D"> 8 | }> = ({ softness, depthTexture }) => (state) => ({ 9 | ...state, 10 | alpha: Mul(state.alpha, SoftnessUnit(softness, state.position, depthTexture)) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/SurfaceWobble.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/function" 2 | import { Add, Input, Mul, PSRDNoise3D } from "shader-composer" 3 | import { ModuleFactory } from ".." 4 | 5 | export type SurfaceWobbleProps = { 6 | offset?: Input<"vec3" | "float"> 7 | amplitude?: Input<"float"> 8 | frequency?: Input<"float"> 9 | } 10 | 11 | export const SurfaceWobble: ModuleFactory = 12 | ({ offset = 1, amplitude = 1, frequency = 1 }) => 13 | (state) => { 14 | const displacement = pipe( 15 | state.position, 16 | (v) => Mul(v, frequency), 17 | (v) => Add(v, offset), 18 | (v) => PSRDNoise3D(v), 19 | (v) => Mul(v, amplitude) 20 | ) 21 | 22 | const position = pipe( 23 | displacement, 24 | (v) => Mul(state.normal, v), 25 | (v) => Add(state.position, v) 26 | ) 27 | 28 | return { ...state, position } 29 | } 30 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/Velocity.ts: -------------------------------------------------------------------------------- 1 | import { Input, Mul } from "shader-composer" 2 | import { Space, Translate } from "./Translate" 3 | 4 | type VelocityProps = { 5 | direction: Input<"vec3"> 6 | time: Input<"float"> 7 | space?: Space 8 | } 9 | 10 | export const Velocity = ({ direction, time, space = "world" }: VelocityProps) => 11 | Translate({ offset: Mul(direction, time), space }) 12 | -------------------------------------------------------------------------------- /packages/material-composer/src/modules/index.ts: -------------------------------------------------------------------------------- 1 | import { Module } from ".." 2 | 3 | export * from "./Acceleration" 4 | export * from "./Alpha" 5 | export * from "./Billboard" 6 | export * from "./Color" 7 | export * from "./Fresnel" 8 | export * from "./Gradient" 9 | export * from "./Lifetime" 10 | export * from "./Rotate" 11 | export * from "./Scale" 12 | export * from "./Softness" 13 | export * from "./SurfaceWobble" 14 | export * from "./Texture" 15 | export * from "./Translate" 16 | export * from "./Velocity" 17 | 18 | export const CustomModule = ({ module }: { module: Module }): Module => module 19 | -------------------------------------------------------------------------------- /packages/material-composer/src/units/experiments.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/function" 2 | import { Add, Clamp01, Input, Mul, Pow, Turbulence3D } from "shader-composer" 3 | 4 | export type HeatOptions = { 5 | offset?: Input<"vec3" | "float"> 6 | scale?: Input<"float"> 7 | octaves?: number 8 | power?: Input<"float"> 9 | } 10 | 11 | export const Heat = ( 12 | v: Input<"vec3">, 13 | { offset = [0, 0, 0], scale = 1, octaves = 5, power = 1 }: HeatOptions 14 | ) => 15 | pipe( 16 | v, 17 | (v) => Add(v, offset), 18 | (v) => Mul(v, scale), 19 | (v) => Turbulence3D(v, octaves), 20 | (v) => Add(v, 0.5), 21 | (v) => Clamp01(v), 22 | (v) => Pow(v, power) 23 | ) 24 | -------------------------------------------------------------------------------- /packages/material-composer/src/units/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./experiments" 2 | -------------------------------------------------------------------------------- /packages/material-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/material-composer/units/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/material-composer-units.cjs.js", 3 | "module": "dist/material-composer-units.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/Application.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react" 2 | import { ThreeApplication, ThreeApplicationProps } from "./ThreeApplication" 3 | import { UI } from "./ui/UI" 4 | 5 | export type ApplicationProps = {} & ThreeApplicationProps 6 | 7 | export const Application: FC = ({ ...props }) => { 8 | return ( 9 | <> 10 | 11 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/Example.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, Suspense } from "react" 2 | import { Link, Redirect, Route } from "wouter" 3 | import { Spinner } from "./Spinner" 4 | import { navigationTunnel } from "./ui/UI" 5 | 6 | export type ExampleProps = { 7 | children?: ReactNode 8 | path: string 9 | title?: string 10 | makeDefault?: boolean 11 | } 12 | 13 | export const Example = ({ children, path, title, makeDefault = false }: ExampleProps) => { 14 | const url = `/examples/${path}` 15 | 16 | return ( 17 | <> 18 | 19 | {title || path} 20 | 21 | 22 | 23 | }>{children} 24 | 25 | 26 | {makeDefault && ( 27 | 28 | 29 | 30 | )} 31 | 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/Spinner.tsx: -------------------------------------------------------------------------------- 1 | import { useFrame } from "@react-three/fiber" 2 | import React, { useRef } from "react" 3 | import { Mesh } from "three" 4 | 5 | export const Spinner = () => { 6 | const mesh = useRef(null!) 7 | 8 | useFrame(({ clock }, dt) => { 9 | const a = Math.pow((Math.sin(clock.elapsedTime * 7) + 2) * 0.5, 3) 10 | mesh.current.rotation.y += (1 + a) * dt 11 | }) 12 | 13 | return ( 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Layers, useRenderPipeline } from "render-composer" 2 | export { Application } from "./Application" 3 | export * from "./Example" 4 | export * from "./stages" 5 | export * from "./ui/Description" 6 | export * from "./ui/Heading" 7 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/stages/FlatStage.tsx: -------------------------------------------------------------------------------- 1 | import { GroupProps } from "@react-three/fiber" 2 | import { ColorRepresentation } from "three" 3 | import React from "react" 4 | 5 | export type FlatStageProps = GroupProps & { color?: ColorRepresentation } 6 | 7 | export const FlatStage = ({ children, color = "#555", ...props }: FlatStageProps) => ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | {children} 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/stages/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./FlatStage" 2 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/ui/Description.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { descriptionTunnel } from "./UI" 3 | 4 | export type DescriptionProps = { children: React.ReactNode } 5 | 6 | export const Description = ({ children }: DescriptionProps) => { 7 | return {children} 8 | } 9 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/ui/Heading.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react" 2 | import { navigationTunnel } from "./UI" 3 | 4 | export type HeadingProps = { 5 | children: ReactNode 6 | } 7 | 8 | export const Heading = ({ children }: HeadingProps) => ( 9 | 10 |

{children}

11 | 12 | ) 13 | -------------------------------------------------------------------------------- /packages/r3f-stage/src/ui/UI.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import tunnel from "../lib/tunnel-rat" 3 | 4 | export const descriptionTunnel = tunnel() 5 | export const navigationTunnel = tunnel() 6 | 7 | export const UI = () => { 8 | return ( 9 | <> 10 |
11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /packages/r3f-stage/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/render-composer/src/Canvas.tsx: -------------------------------------------------------------------------------- 1 | import * as R3F from "@react-three/fiber" 2 | import React, { StrictMode } from "react" 3 | 4 | export type CanvasProps = R3F.Props & { 5 | /** When true, enables React's StrictMode. */ 6 | strict?: boolean 7 | } 8 | 9 | export const Canvas = ({ strict = false, children, ...props }: CanvasProps) => ( 10 | {children} : children} 21 | {...props} 22 | /> 23 | ) 24 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/GodRaysEffect.tsx: -------------------------------------------------------------------------------- 1 | import { useThree } from "@react-three/fiber" 2 | import * as PP from "postprocessing" 3 | import { usePostProcessingEffect } from "../lib/usePostProcessingEffect" 4 | 5 | export type GodRaysEffectProps = { 6 | lightSource: ConstructorParameters[1] 7 | } & ConstructorParameters[2] 8 | 9 | export const GodRaysEffect = ({ 10 | lightSource, 11 | ...props 12 | }: GodRaysEffectProps) => { 13 | const camera = useThree((s) => s.camera) 14 | usePostProcessingEffect( 15 | () => new PP.GodRaysEffect(camera, lightSource, props), 16 | { lightSource, ...props } 17 | ) 18 | return null 19 | } 20 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/NoiseEffect.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { usePostProcessingEffect } from "../lib/usePostProcessingEffect" 3 | 4 | export type NoiseEffectProps = ConstructorParameters< 5 | typeof PP.NoiseEffect 6 | >[0] & { opacity?: number } 7 | 8 | export const NoiseEffect = ({ 9 | blendFunction = PP.BlendFunction.NORMAL, 10 | premultiply, 11 | opacity = 1 12 | }: NoiseEffectProps) => { 13 | usePostProcessingEffect( 14 | () => new PP.NoiseEffect({ blendFunction, premultiply }), 15 | { blendMode: new PP.BlendMode(blendFunction, opacity) } 16 | ) 17 | return null 18 | } 19 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/SMAAEffect.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { usePostProcessingEffect } from "../lib/usePostProcessingEffect" 3 | 4 | export const SMAAEffect = ( 5 | props: ConstructorParameters[0] 6 | ) => { 7 | usePostProcessingEffect(() => new PP.SMAAEffect(props), props) 8 | return null 9 | } 10 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/SelectiveBloomEffect.tsx: -------------------------------------------------------------------------------- 1 | import { useThree } from "@react-three/fiber" 2 | import * as PP from "postprocessing" 3 | import { useLayoutEffect } from "react" 4 | import { usePostProcessingEffect } from "../lib/usePostProcessingEffect" 5 | 6 | export const SelectiveBloomEffect = ( 7 | props: ConstructorParameters[2] 8 | ) => { 9 | const scene = useThree((s) => s.scene) 10 | const camera = useThree((s) => s.camera) 11 | 12 | const effect = usePostProcessingEffect( 13 | () => 14 | new PP.SelectiveBloomEffect(scene, camera, { 15 | blendFunction: PP.BlendFunction.ADD, 16 | mipmapBlur: true, 17 | luminanceThreshold: 1, 18 | luminanceSmoothing: 0.2, 19 | intensity: 1, 20 | ...props 21 | }), 22 | props 23 | ) 24 | 25 | useLayoutEffect(() => { 26 | effect.inverted = true 27 | }, [effect]) 28 | 29 | return null 30 | } 31 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/TextureEffect.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { usePostProcessingEffect } from "../lib/usePostProcessingEffect" 3 | 4 | export const TextureEffect = ( 5 | props: ConstructorParameters[0] 6 | ) => { 7 | usePostProcessingEffect(() => new PP.TextureEffect(props), props) 8 | return null 9 | } 10 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/TiltShiftEffect.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { usePostProcessingEffect } from "../lib/usePostProcessingEffect" 3 | 4 | export const TiltShiftEffect = ( 5 | props: ConstructorParameters[0] 6 | ) => { 7 | usePostProcessingEffect(() => new PP.TiltShiftEffect(props), props) 8 | return null 9 | } 10 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/VignetteEffect.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { usePostProcessingEffect } from "../lib/usePostProcessingEffect" 3 | 4 | export const VignetteEffect = ( 5 | props: ConstructorParameters[0] 6 | ) => { 7 | usePostProcessingEffect(() => new PP.VignetteEffect(props), props) 8 | return null 9 | } 10 | -------------------------------------------------------------------------------- /packages/render-composer/src/effects/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./GodRaysEffect" 2 | export * from "./LensDirtEffect" 3 | export * from "./TiltShiftEffect" 4 | export * from "./NoiseEffect" 5 | export * from "./SelectiveBloomEffect" 6 | export * from "./SMAAEffect" 7 | export * from "./TextureEffect" 8 | export * from "./VignetteEffect" 9 | -------------------------------------------------------------------------------- /packages/render-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Canvas" 2 | export * from "./EffectComposer" 3 | export * from "./effects" 4 | export * from "./lib/bitmask" 5 | export * from "./passes" 6 | export * from "./DefaultRenderPipeline" 7 | 8 | /* Temporary, until we have a better API */ 9 | export { usePostProcessingEffect } from "./lib/usePostProcessingEffect" 10 | -------------------------------------------------------------------------------- /packages/render-composer/src/lib/bitmask.ts: -------------------------------------------------------------------------------- 1 | export const bitmask = (...groups: number[]) => 2 | groups.reduce((acc, layer) => acc | (1 << layer), 0) 3 | 4 | const not = (...groups: number[]) => ~bitmask(...groups) 5 | 6 | bitmask.not = not 7 | -------------------------------------------------------------------------------- /packages/render-composer/src/lib/usePostProcessingEffect.tsx: -------------------------------------------------------------------------------- 1 | import { useThree } from "@react-three/fiber" 2 | import * as PP from "postprocessing" 3 | import { useContext, useLayoutEffect, useMemo } from "react" 4 | import { EffectPassContext } from "../passes/EffectPass" 5 | 6 | export const usePostProcessingEffect = ( 7 | ctor: () => T, 8 | props: P 9 | ) => { 10 | /* Create effect */ 11 | const effect = useMemo(ctor, []) 12 | 13 | /* Update props on rerender */ 14 | useLayoutEffect(() => { 15 | Object.assign(effect, props) 16 | }, [effect, props]) 17 | 18 | /* Handle resolution changes */ 19 | const size = useThree((s) => s.size) 20 | useLayoutEffect(() => { 21 | effect.setSize(size.width, size.height) 22 | }, [effect, size.width, size.height]) 23 | 24 | /* Register with the effect pass */ 25 | useContext(EffectPassContext).useItem(effect) 26 | 27 | return effect 28 | } 29 | -------------------------------------------------------------------------------- /packages/render-composer/src/passes/CopyPass.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { forwardRef, useContext, useImperativeHandle, useMemo } from "react" 3 | import { EffectComposerContext } from "../EffectComposer" 4 | 5 | export const CopyPass = forwardRef((_, ref) => { 6 | const pass = useMemo(() => new PP.CopyPass(), []) 7 | useContext(EffectComposerContext).useItem(pass) 8 | useImperativeHandle(ref, () => pass) 9 | return null 10 | }) 11 | -------------------------------------------------------------------------------- /packages/render-composer/src/passes/DepthCopyPass.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { forwardRef, useContext, useImperativeHandle, useMemo } from "react" 3 | import { BasicDepthPacking } from "three" 4 | import { EffectComposerContext } from "../EffectComposer" 5 | 6 | export const DepthCopyPass = forwardRef((_, ref) => { 7 | const pass = useMemo( 8 | () => new PP.DepthCopyPass({ depthPacking: BasicDepthPacking }), 9 | [] 10 | ) 11 | 12 | useImperativeHandle(ref, () => pass) 13 | 14 | useContext(EffectComposerContext).useItem(pass) 15 | 16 | return null 17 | }) 18 | -------------------------------------------------------------------------------- /packages/render-composer/src/passes/LambdaPass.tsx: -------------------------------------------------------------------------------- 1 | import * as PP from "postprocessing" 2 | import { forwardRef, useContext, useImperativeHandle, useMemo } from "react" 3 | import { EffectComposerContext } from "../EffectComposer" 4 | 5 | export type LambdaPassProps = { 6 | fun: Function 7 | } 8 | 9 | export const LambdaPass = forwardRef( 10 | ({ fun }, ref) => { 11 | const pass = useMemo(() => new PP.LambdaPass(fun), []) 12 | useContext(EffectComposerContext).useItem(pass) 13 | useImperativeHandle(ref, () => pass) 14 | return null 15 | } 16 | ) 17 | -------------------------------------------------------------------------------- /packages/render-composer/src/passes/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./CopyPass" 2 | export * from "./DepthCopyPass" 3 | export * from "./EffectPass" 4 | export * from "./LambdaPass" 5 | export * from "./LayerRenderPass" 6 | export * from "./RenderPass" 7 | -------------------------------------------------------------------------------- /packages/render-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@shader-composer/core", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "", 9 | "version": "0.4.9", 10 | "main": "dist/shader-composer-core.cjs.js", 11 | "module": "dist/shader-composer-core.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": true, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-typescript" 26 | ] 27 | }, 28 | "dependencies": { 29 | "fp-ts": "^2.12.3" 30 | }, 31 | "devDependencies": { 32 | "gl-matrix": "^3.4.3", 33 | "three": ">=0.141.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/debug.ts: -------------------------------------------------------------------------------- 1 | export let DEBUG = false 2 | 3 | export const enableDebugging = () => { 4 | DEBUG = true 5 | } 6 | 7 | export const disableDebugging = () => { 8 | DEBUG = false 9 | } 10 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/expressions.ts: -------------------------------------------------------------------------------- 1 | import { glslRepresentation } from "./glslRepresentation" 2 | 3 | const zip = (a: TemplateStringsArray, b: any[]) => a.map((k, i) => [k, b[i]]) 4 | 5 | export type Expression = { 6 | _: "Expression" 7 | values: any[] 8 | render: () => string 9 | toString: () => string 10 | } 11 | 12 | export const glsl = (strings: TemplateStringsArray, ...values: any[]): Expression => { 13 | const render = () => 14 | zip( 15 | strings, 16 | values.map((v) => glslRepresentation(v)) 17 | ) 18 | .flat() 19 | .join("") 20 | 21 | return { 22 | _: "Expression", 23 | values, 24 | render, 25 | toString: render 26 | } 27 | } 28 | 29 | /** A shortcut for the `glsl` tagged template literal helper. */ 30 | export const $ = glsl 31 | 32 | export function isExpression(v: any): v is Expression { 33 | return v && v._ === "Expression" 34 | } 35 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { compileShader } from "./compiler" 2 | export { disableDebugging, enableDebugging } from "./debug" 3 | export * from "./expressions" 4 | export * from "./glslType" 5 | export * from "./snippets" 6 | export * from "./stdlib" 7 | export * from "./ticker" 8 | export * from "./tree" 9 | export * from "./units" 10 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/snippets.ts: -------------------------------------------------------------------------------- 1 | import { Expression } from "./expressions" 2 | 3 | export type Snippet = { 4 | _: "Snippet" 5 | name: string 6 | render: (name: string) => Expression 7 | expression: Expression 8 | } 9 | 10 | export const Snippet = (render: (name: string) => Expression): Snippet => { 11 | /* Start with a randomized name. The compiler will assign a deterministic name in its prepare step. */ 12 | const name = `uninitialized_snippet_${Math.floor(Math.random() * 1000000)}` 13 | 14 | return { 15 | _: "Snippet", 16 | name, 17 | render, 18 | expression: render(name) 19 | } 20 | } 21 | 22 | export const renameSnippet = (snippet: Snippet, name: string) => { 23 | snippet.name = name 24 | snippet.expression = snippet.render(name) 25 | } 26 | 27 | export function isSnippet(x: any): x is Snippet { 28 | return x && x._ === "Snippet" 29 | } 30 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/stdlib/index.ts: -------------------------------------------------------------------------------- 1 | /** @packageDocumentation 2 | * The `shader-composer/stdlib` module contains a core collection of Shader unit implementations. 3 | */ 4 | 5 | export * from "./artistic" 6 | export * from "./logic" 7 | export * from "./math" 8 | export * from "./roots" 9 | export * from "./rotation" 10 | export * from "./scene" 11 | export * from "./spaces" 12 | export * from "./textures" 13 | export * from "./values" 14 | export * from "./variables" 15 | export * from "./vectors" 16 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/stdlib/logic.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "../expressions" 2 | import { type } from "../glslType" 3 | import { GLSLType, Unit, Input } from "../units" 4 | import { Bool } from "./values" 5 | 6 | export const If = ( 7 | expression: Input<"bool">, 8 | then: Input, 9 | else_: Input 10 | ) => Unit(type(then) as T, $`(${expression} ? ${then} : ${else_})`) 11 | 12 | export const GreaterOrEqual = (a: Input, b: Input) => 13 | Bool($`(${a} >= ${b})`) 14 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/stdlib/textures.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "../expressions" 2 | import { Input, Unit } from "../units" 3 | import { Float, Vec3, Vec4 } from "./values" 4 | 5 | export const Texture2D = (sampler2D: Unit<"sampler2D">, xy: Input<"vec2">) => { 6 | const texture2D = Vec4($`texture2D(${sampler2D}, ${xy})`, { 7 | name: "Texture2D" 8 | }) 9 | 10 | return { 11 | ...texture2D, 12 | 13 | /** The color value sampled from the texture. */ 14 | color: Vec3($`${texture2D}.rgb`, { name: "Texture2D Color" }), 15 | 16 | /** The alpha value sampled from the texture. */ 17 | alpha: Float($`${texture2D}.a`, { name: "Texture2D Alpha" }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/stdlib/variables.test.ts: -------------------------------------------------------------------------------- 1 | import { glslRepresentation } from "../glslRepresentation" 2 | import { UniformUnit } from "./variables" 3 | 4 | describe("Uniform", () => { 5 | it("uses its uniform name as its string representation", () => { 6 | const uniform = UniformUnit("float", 0, { variableName: "foo" }) 7 | expect(glslRepresentation(uniform)).toBe("u_foo") 8 | }) 9 | 10 | it("uses u_variableName as the default name if none other is given", () => { 11 | const uniform = UniformUnit("float", 0, { variableName: "foo123" }) 12 | expect(glslRepresentation(uniform)).toBe("u_foo123") 13 | }) 14 | 15 | it("allows the user to set a uniform name explicitly", () => { 16 | const uniform = UniformUnit("float", 0, { 17 | variableName: "foo", 18 | uniformName: "u_bar" 19 | }) 20 | expect(glslRepresentation(uniform)).toBe("u_bar") 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/ticker.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An object containing information about the current frame. 3 | */ 4 | export const frame = { 5 | /** 6 | * The current frame time, in seconds. This value does not change 7 | * over the course of a single frame. 8 | */ 9 | time: performance.now() / 1000, 10 | 11 | /** 12 | * The current frame count. 13 | */ 14 | count: 0 15 | } 16 | 17 | function tick() { 18 | requestAnimationFrame(tick) 19 | 20 | frame.time = performance.now() / 1000 21 | frame.count++ 22 | } 23 | 24 | requestAnimationFrame(tick) 25 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/util/concatenator3000.ts: -------------------------------------------------------------------------------- 1 | export type Part = any 2 | 3 | export const isPresent = (p: Part) => !!p 4 | 5 | export const concatenate = (...parts: Part[]): string => 6 | parts 7 | .flat(Infinity) 8 | .filter(isPresent) 9 | .join("\n") 10 | 11 | export const identifier = (...parts: Part[]): string => 12 | parts 13 | .filter(isPresent) 14 | .join("_") 15 | .replace(/_{2,}/g, "_") 16 | 17 | export const statement = (...parts: Part[]): string => 18 | parts.filter(isPresent).join(" ") + ";" 19 | 20 | export const assignment = (...parts: Part[]): string => statement(parts.join(" = ")) 21 | 22 | export const block = (...parts: Part[]): Part[] => [ 23 | "{", 24 | concatenate(parts) 25 | .split("\n") 26 | .map((line) => " " + line) 27 | .join("\n"), 28 | "}" 29 | ] 30 | 31 | export const sluggify = (s: string) => s.replace(/[^a-zA-Z0-9]/g, "_") 32 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/util/idGenerator.ts: -------------------------------------------------------------------------------- 1 | export default (initial = 0) => () => ++initial 2 | -------------------------------------------------------------------------------- /packages/shader-composer-core/src/vendor/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./glsl-rotate" 2 | -------------------------------------------------------------------------------- /packages/shader-composer-core/test/expressions.test.ts: -------------------------------------------------------------------------------- 1 | import { $, glsl, isExpression } from "../src/expressions" 2 | 3 | describe("glsl", () => { 4 | it("creates an Expression instance", () => { 5 | const expr = glsl`foo = bar;` 6 | expect(isExpression(expr)).toBe(true) 7 | }) 8 | 9 | it("renders to a string through the render() function", () => { 10 | const expr = glsl`foo = bar;` 11 | expect(expr.render()).toBe("foo = bar;") 12 | }) 13 | 14 | it("handles nested values", () => { 15 | const expr = $`foo = vec3(${[1, 2, 3]})` 16 | expect(expr.render()).toBe("foo = vec3(vec3(1.0, 2.0, 3.0))") 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/shader-composer-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/README.md: -------------------------------------------------------------------------------- 1 | # shader-composer-r3f 2 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@shader-composer/noise", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "A collection of noise-generating Shader Composer units.", 9 | "version": "0.4.9", 10 | "main": "dist/shader-composer-noise.cjs.js", 11 | "module": "dist/shader-composer-noise.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-typescript" 26 | ] 27 | }, 28 | "peerDependencies": { 29 | "@shader-composer/core": ">=0.4.9" 30 | }, 31 | "devDependencies": { 32 | "@shader-composer/core": "workspace:^0.4.9" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./FBMNoise" 2 | export * from "./GerstnerWave" 3 | export * from "./PerlinNoise" 4 | export * from "./PSRDNoise" 5 | export * from "./Simplex3DNoise" 6 | export * from "./turbulence" 7 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/src/mod289.ts: -------------------------------------------------------------------------------- 1 | import { glsl, Snippet } from "@shader-composer/core" 2 | 3 | export const mod289 = Snippet( 4 | (name) => glsl` 5 | vec3 ${name}(vec3 x) 6 | { 7 | return x - floor(x * (1.0 / 289.0)) * 289.0; 8 | } 9 | 10 | vec4 ${name}(vec4 x) 11 | { 12 | return x - floor(x * (1.0 / 289.0)) * 289.0; 13 | } 14 | ` 15 | ) 16 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/src/permute.ts: -------------------------------------------------------------------------------- 1 | import { glsl, Snippet } from "@shader-composer/core" 2 | import { mod289 } from "./mod289" 3 | 4 | export const permute = Snippet( 5 | (name) => 6 | glsl` 7 | vec4 ${name}(vec4 x) 8 | { 9 | return ${mod289}(((x*34.0)+10.0)*x); 10 | } 11 | ` 12 | ) 13 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/src/random.ts: -------------------------------------------------------------------------------- 1 | import { glsl, Snippet } from "@shader-composer/core" 2 | 3 | export const rand = Snippet( 4 | (rand) => glsl` 5 | float ${rand}(float n) { return fract(sin(n) * 1e4); } 6 | 7 | float ${rand}(vec2 p) { 8 | return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * 9 | (0.1 + abs(sin(p.y * 13.0 + p.x)))); 10 | } 11 | ` 12 | ) 13 | 14 | export const rand2 = Snippet( 15 | (rand2) => glsl` 16 | vec2 ${rand2}(vec2 p) { 17 | return fract( 18 | sin(vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)))) * 43758.5453 19 | ); 20 | } 21 | ` 22 | ) 23 | 24 | export const rand3 = Snippet( 25 | (rand3) => glsl` 26 | vec2 ${rand3}(vec3 p) { 27 | return mod(((p * 34.0) + 1.0) * p, 289.0); 28 | } 29 | ` 30 | ) 31 | 32 | export const rand4 = Snippet( 33 | (rand4) => glsl` 34 | vec4 ${rand4}(vec4 p) { return mod(((p * 34.0) + 1.0) * p, 289.0); } 35 | ` 36 | ) 37 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/src/taylorInvSqrt.ts: -------------------------------------------------------------------------------- 1 | import { glsl, Snippet } from "@shader-composer/core" 2 | 3 | export const taylorInvSqrt = Snippet( 4 | (name) => glsl` 5 | vec4 ${name}(vec4 r) 6 | { 7 | return 1.79284291400159 - 0.85373472095314 * r; 8 | } 9 | ` 10 | ) 11 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/src/turbulence.ts: -------------------------------------------------------------------------------- 1 | import { $, Float, Input, Snippet } from "@shader-composer/core" 2 | import { psrdnoise3 } from "./PSRDNoise" 3 | 4 | export const turbulence3D = Snippet( 5 | (turbulence3D) => $` 6 | float ${turbulence3D}(vec3 p, float octaves) { 7 | float t = -0.5; 8 | 9 | float alpha = 0.0; 10 | vec3 period; 11 | vec3 gradient; 12 | 13 | for (float f = 1.0 ; f <= octaves; f++) { 14 | float power = pow(2.0, f); 15 | float noise = ${psrdnoise3}(vec3(power * p), period, alpha, gradient); 16 | t += abs(noise / power); 17 | } 18 | 19 | return t; 20 | } 21 | ` 22 | ) 23 | 24 | export const Turbulence3D = (p: Input<"vec3">, octaves: Input<"float"> = 10) => 25 | Float($`${turbulence3D}(${p}, ${octaves})`, { name: `Turbulence3D` }) 26 | -------------------------------------------------------------------------------- /packages/shader-composer-noise/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer-postprocessing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@shader-composer/postprocessing", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "Build postprocessing effects using Shader Composer.", 9 | "version": "0.0.1", 10 | "main": "dist/shader-composer-postprocessing.cjs.js", 11 | "module": "dist/shader-composer-postprocessing.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-typescript" 26 | ] 27 | }, 28 | "devDependencies": { 29 | "@shader-composer/core": "workspace:^0.4.9" 30 | }, 31 | "peerDependencies": { 32 | "postprocessing": "^6.28.7", 33 | "@shader-composer/core": ">=0.4.9", 34 | "three": ">=0.141.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/shader-composer-postprocessing/src/PostProcessingEffectRoot.ts: -------------------------------------------------------------------------------- 1 | import { $, Input, Root } from "@shader-composer/core" 2 | import { InputAlpha, InputColor } from "./units" 3 | 4 | export type PostProcessingEffectRootProps = { 5 | color?: Input<"vec3"> 6 | alpha?: Input<"float"> 7 | } 8 | 9 | export const PostProcessingEffectRoot = ({ 10 | color = InputColor, 11 | alpha = InputAlpha 12 | }: PostProcessingEffectRootProps) => 13 | Root({ 14 | fragment: { 15 | body: $` 16 | outputColor = vec4(${color}.rgb, ${alpha}); 17 | ` 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /packages/shader-composer-postprocessing/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./PostProcessingEffectRoot" 2 | export * from "./ShaderComposerEffect" 3 | export * from "./units" 4 | -------------------------------------------------------------------------------- /packages/shader-composer-postprocessing/src/units.ts: -------------------------------------------------------------------------------- 1 | import { $, Float, Vec2, Vec3 } from "@shader-composer/core" 2 | 3 | export const InputColor = Vec3($`inputColor.rgb`) 4 | export const InputAlpha = Float($`inputColor.a`) 5 | export const UV = Vec2($`uv`) 6 | -------------------------------------------------------------------------------- /packages/shader-composer-postprocessing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer-r3f/README.md: -------------------------------------------------------------------------------- 1 | # shader-composer-r3f 2 | -------------------------------------------------------------------------------- /packages/shader-composer-r3f/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./hooks" 2 | export * from "./Shader" 3 | 4 | import setupThree from "@shader-composer/three" 5 | setupThree() 6 | -------------------------------------------------------------------------------- /packages/shader-composer-r3f/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer-three/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@shader-composer/three", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "Build Three.js shaders using JavaScript.", 9 | "version": "0.4.9", 10 | "main": "dist/shader-composer-three.cjs.js", 11 | "module": "dist/shader-composer-three.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | "@babel/preset-typescript" 27 | ] 28 | }, 29 | "peerDependencies": { 30 | "@shader-composer/core": ">=0.4.9", 31 | "three": ">=0.141.0" 32 | }, 33 | "devDependencies": { 34 | "@shader-composer/core": "workspace:^0.4.9" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/shader-composer-three/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/README.md: -------------------------------------------------------------------------------- 1 | # Shader Composer Toybox 2 | 3 | > **Warning** This package is deprecated. Its contents will be integrated into different `@shader-composer/*` packages over time. 4 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shader-composer-toybox", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "Assorted effects and units for shader-composer.", 9 | "version": "0.1.3", 10 | "main": "dist/shader-composer-toybox.cjs.js", 11 | "module": "dist/shader-composer-toybox.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | "@babel/preset-typescript" 27 | ] 28 | }, 29 | "dependencies": { 30 | "fp-ts": "^2.12.3", 31 | "shader-composer": "^0.4.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/src/effects/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Dissolve" 2 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./effects" 2 | export * from "./tools" 3 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/src/tools/Billboard.ts: -------------------------------------------------------------------------------- 1 | import { $, Input, ModelMatrix, Snippet, Vec3, ViewMatrix } from "shader-composer" 2 | 3 | const billboard = Snippet( 4 | (name) => $` 5 | vec3 ${name}(vec2 v, mat4 view){ 6 | vec3 up = vec3(view[0][1], view[1][1], view[2][1]); 7 | vec3 right = vec3(view[0][0], view[1][0], view[2][0]); 8 | vec3 p = right * v.x + up * v.y; 9 | return p; 10 | } 11 | ` 12 | ) 13 | 14 | export const Billboard = (position: Input<"vec3">) => 15 | Vec3($`${billboard}(${position}.xy, 16 | ${ViewMatrix} 17 | * ${ModelMatrix} 18 | #ifdef USE_INSTANCING 19 | * instanceMatrix 20 | #endif 21 | )`) 22 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/src/tools/Grid2D.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/function" 2 | import { Float, Fract, Input, Mul, OneMinus, Step } from "shader-composer" 3 | 4 | export const Grid2D = ( 5 | v: Input<"vec2">, 6 | scale: Input<"float"> = 1, 7 | thickness: Input<"float"> = 0.1 8 | ) => { 9 | const { x, y } = Mul(v, scale) 10 | 11 | const fx = Fract(x) 12 | const fy = Fract(y) 13 | 14 | const sx = Step(thickness, fx) 15 | const sy = Step(thickness, fy) 16 | 17 | return pipe( 18 | Mul(sx, sy), 19 | (v) => OneMinus(v), 20 | (v) => Float(v, { name: "Grid2D" }) 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/src/tools/Random.ts: -------------------------------------------------------------------------------- 1 | import { Input, Float, $ } from "shader-composer" 2 | 3 | export const Random = (n: Input<"float">) => 4 | Float($`fract(sin(${n}) * 1e4)`, { name: "Random (Float)" }) 5 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/src/tools/Softness.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from "fp-ts/function" 2 | import { 3 | Div, 4 | Float, 5 | Input, 6 | LocalToViewSpace, 7 | PerspectiveDepth, 8 | Saturate, 9 | ScreenUV, 10 | Sub, 11 | Unit, 12 | varying 13 | } from "shader-composer" 14 | 15 | export const Softness = ( 16 | softness: Input<"float">, 17 | position: Input<"vec3">, 18 | depthTexture: Unit<"sampler2D"> 19 | ) => 20 | Float( 21 | pipe( 22 | position, 23 | /* Convert position to view space and grab depth */ 24 | (v) => varying(LocalToViewSpace(v).z), 25 | /* Subtract from the existing scene depth at the fragment coordinate */ 26 | (v) => Sub(v, PerspectiveDepth(ScreenUV, depthTexture)), 27 | /* Divide by softness factor */ 28 | (v) => Div(v, softness), 29 | /* Clamp between 0 and 1 */ 30 | (v) => Saturate(v) 31 | ), 32 | 33 | { name: "Soft Particle" } 34 | ) 35 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/src/tools/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Billboard" 2 | export * from "./Displacement" 3 | export * from "./Grid2D" 4 | export * from "./Random" 5 | export * from "./Softness" 6 | -------------------------------------------------------------------------------- /packages/shader-composer-toybox/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer/fun/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/shader-composer-fun.cjs.js", 3 | "module": "dist/shader-composer-fun.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer/postprocessing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/shader-composer-postprocessing.cjs.js", 3 | "module": "dist/shader-composer-postprocessing.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer/r3f/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/shader-composer-r3f.cjs.js", 3 | "module": "dist/shader-composer-r3f.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer/src/fun.ts: -------------------------------------------------------------------------------- 1 | import { Add, Div, Mix, Mul, Sub } from "@shader-composer/core" 2 | import { GLSLType, Input } from "@shader-composer/core" 3 | 4 | export const mix = 5 | (b: Input, f: Input<"float">) => 6 | (a: Input) => 7 | Mix(a, b, f) 8 | 9 | export const add = 10 | (b: Input) => 11 | (a: Input) => 12 | Add(a, b) 13 | 14 | export const sub = 15 | (b: Input) => 16 | (a: Input) => 17 | Sub(a, b) 18 | 19 | export const mul = 20 | (b: Input) => 21 | (a: Input) => 22 | Mul(a, b) 23 | 24 | export const div = 25 | (b: Input) => 26 | (a: Input) => 27 | Div(a, b) 28 | -------------------------------------------------------------------------------- /packages/shader-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@shader-composer/core" 2 | export * from "@shader-composer/noise" 3 | -------------------------------------------------------------------------------- /packages/shader-composer/src/postprocessing.ts: -------------------------------------------------------------------------------- 1 | export * from "@shader-composer/postprocessing" 2 | -------------------------------------------------------------------------------- /packages/shader-composer/src/r3f.ts: -------------------------------------------------------------------------------- 1 | export * from "@shader-composer/r3f" 2 | 3 | /* Make sure the Three.js initialization code is imported */ 4 | import "@shader-composer/three" 5 | -------------------------------------------------------------------------------- /packages/shader-composer/src/three.ts: -------------------------------------------------------------------------------- 1 | export * from "@shader-composer/three" 2 | -------------------------------------------------------------------------------- /packages/shader-composer/three/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "dist/shader-composer-three.cjs.js", 3 | "module": "dist/shader-composer-three.esm.js" 4 | } 5 | -------------------------------------------------------------------------------- /packages/shader-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/state-composer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # state-composer 2 | 3 | ## 0.1.0 4 | 5 | ### Minor Changes 6 | 7 | - 567d19a: First release! There's even a README! Incredible! 8 | -------------------------------------------------------------------------------- /packages/state-composer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "state-composer", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "", 9 | "version": "0.1.0", 10 | "main": "dist/state-composer.cjs.js", 11 | "module": "dist/state-composer.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | [ 27 | "@babel/preset-typescript", 28 | { 29 | "isTSX": true, 30 | "allExtensions": true 31 | } 32 | ] 33 | ] 34 | }, 35 | "peerDependencies": { 36 | "react": "^18.2.0", 37 | "react-dom": "^18.2.0" 38 | }, 39 | "dependencies": { 40 | "statery": "0.6.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/state-composer/src/createStateMachine.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react" 2 | import { makeStore, useStore } from "statery" 3 | 4 | export const createStateMachine = (initialState: S) => { 5 | const store = makeStore({ 6 | state: initialState as S 7 | }) 8 | 9 | const matchesState = (state: S, other: S | S[]) => 10 | Array.isArray(other) ? other.includes(state) : state === other 11 | 12 | const Match = ({ 13 | state, 14 | children 15 | }: { 16 | state: S | S[] 17 | children?: ReactNode 18 | }) => { 19 | const { state: currentState } = useStore(store) 20 | 21 | return matchesState(currentState, state) ? <>{children} : null 22 | } 23 | 24 | const enter = (state: S) => { 25 | const { state: currentState } = store.state 26 | 27 | if (state && state !== currentState) { 28 | store.set({ state }) 29 | } 30 | } 31 | 32 | const is = (state: S | S[]) => matchesState(store.state.state, state) 33 | 34 | return { 35 | Match, 36 | enter, 37 | is 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/state-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./createStateMachine" 2 | -------------------------------------------------------------------------------- /packages/state-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/timeline-composer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timeline-composer", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "Composable timelines for React.", 9 | "version": "0.1.7", 10 | "main": "dist/timeline-composer.cjs.js", 11 | "module": "dist/timeline-composer.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | [ 27 | "@babel/preset-typescript", 28 | { 29 | "isTSX": true, 30 | "allExtensions": true 31 | } 32 | ] 33 | ] 34 | }, 35 | "peerDependencies": { 36 | "react": "^18.2.0", 37 | "react-dom": "^18.2.0" 38 | }, 39 | "dependencies": { 40 | "@hmans/use-animation-frame": "^0.0.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/timeline-composer/src/Delay.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode, useRef, useState } from "react" 2 | import { useAnimationFrame } from "@hmans/use-animation-frame" 3 | 4 | type DelayProps = { 5 | children?: ReactNode 6 | seconds: number 7 | onComplete?: () => void 8 | } 9 | 10 | export const Delay: FC = ({ seconds, onComplete, children }) => { 11 | const ready = useDelay(seconds, onComplete) 12 | return ready ? <>{children} : null 13 | } 14 | 15 | export const useDelay = (seconds: number, onComplete?: () => void) => { 16 | const [ready, setReady] = useState(false) 17 | const timeRemaining = useRef(seconds) 18 | 19 | useAnimationFrame((dt) => { 20 | if (ready) return 21 | 22 | timeRemaining.current -= dt 23 | 24 | if (timeRemaining.current <= 0) { 25 | onComplete?.() 26 | setReady(true) 27 | } 28 | }) 29 | 30 | return ready 31 | } 32 | -------------------------------------------------------------------------------- /packages/timeline-composer/src/Lifetime.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, ReactNode } from "react" 2 | import { useDelay } from "./Delay" 3 | 4 | type LifetimeProps = { 5 | children?: ReactNode 6 | seconds: number 7 | } 8 | 9 | export const Lifetime: FC = ({ children, seconds }) => { 10 | const ready = useDelay(seconds) 11 | 12 | return ready ? null : <>{children} 13 | } 14 | -------------------------------------------------------------------------------- /packages/timeline-composer/src/Repeat.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, Fragment, ReactNode, useState } from "react" 2 | import { Delay } from "./Delay" 3 | 4 | type RepeatProps = { children: ReactNode; times?: number; seconds?: number } 5 | 6 | const Wrap = ({ children }: { children: ReactNode }) => <>{children} 7 | 8 | export const Repeat: FC = ({ 9 | children, 10 | times = Infinity, 11 | seconds = 1 12 | }) => { 13 | const [iteration, setIteration] = useState(1) 14 | 15 | return ( 16 | 17 | {iteration < times && ( 18 | { 22 | setIteration((i) => i + 1) 23 | }} 24 | /> 25 | )} 26 | 27 | {/* To my surprise, React.Fragment with a key did not work here. */} 28 | {children} 29 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /packages/timeline-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Delay" 2 | export * from "./Lifetime" 3 | export * from "./Repeat" 4 | -------------------------------------------------------------------------------- /packages/timeline-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/ts-module-docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-module-docs", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "", 9 | "version": "0.0.1", 10 | "main": "dist/ts-module-docs.cjs.js", 11 | "module": "dist/ts-module-docs.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build", 21 | "test": "jest" 22 | }, 23 | "babel": { 24 | "presets": [ 25 | "@babel/preset-env", 26 | "@babel/preset-react", 27 | [ 28 | "@babel/preset-typescript", 29 | { 30 | "isTSX": true, 31 | "allExtensions": true 32 | } 33 | ] 34 | ] 35 | }, 36 | "dependencies": { 37 | "@microsoft/tsdoc": "^0.14.2", 38 | "ts-morph": "^16.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/ts-module-docs/test/mock/mock.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds two numbers and returns the result. 3 | * 4 | * It's pretty basic, but it's nice! 5 | * 6 | * @param a The first number to add. 7 | * @param b The second number to add. This comment is 8 | * intentially spanning multiple lines. 9 | * 10 | * @returns The sum of `a` and `b`. Exciting! 11 | */ 12 | export const add = (a: number, b: number) => a + b 13 | -------------------------------------------------------------------------------- /packages/ts-module-docs/test/mock/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "strict": true, 8 | "jsx": "react", 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "isolatedModules": true, 12 | "skipLibCheck": true 13 | }, 14 | "include": ["*.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/ts-module-docs/test/ts-module-docs.test.ts: -------------------------------------------------------------------------------- 1 | import { extractModuleDocumentation } from "../src" 2 | 3 | describe("extractModuleDocumentation", () => { 4 | const extractDocs = () => 5 | extractModuleDocumentation( 6 | __dirname + "/mock/tsconfig.json", 7 | __dirname + "/mock/mock.ts" 8 | ) 9 | 10 | it("extracts name and description", () => { 11 | const docs = extractDocs() 12 | 13 | const first = docs.symbols[0] 14 | 15 | expect(first.name).toMatchInlineSnapshot(`"add"`) 16 | 17 | expect(first.description).toMatchInlineSnapshot(` 18 | "Adds two numbers and returns the result. 19 | 20 | It's pretty basic, but it's nice! 21 | 22 | " 23 | `) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/ts-module-docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/ui-composer/README.md: -------------------------------------------------------------------------------- 1 | # ui-composer 2 | -------------------------------------------------------------------------------- /packages/ui-composer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui-composer", 3 | "author": { 4 | "name": "Hendrik Mans", 5 | "email": "hendrik@mans.de", 6 | "url": "https://hmans.co" 7 | }, 8 | "description": "Composable UI for games built with React.", 9 | "version": "0.0.1", 10 | "main": "dist/ui-composer.cjs.js", 11 | "module": "dist/ui-composer.esm.js", 12 | "files": [ 13 | "dist/**", 14 | "LICENSE", 15 | "README.md" 16 | ], 17 | "license": "MIT", 18 | "sideEffects": false, 19 | "scripts": { 20 | "build": "preconstruct build" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env", 25 | "@babel/preset-react", 26 | [ 27 | "@babel/preset-typescript", 28 | { 29 | "isTSX": true, 30 | "allExtensions": true 31 | } 32 | ] 33 | ] 34 | }, 35 | "peerDependencies": { 36 | "react": "^18.2.0", 37 | "react-dom": "^18.2.0" 38 | }, 39 | "dependencies": { 40 | "@stitches/react": "^1.2.8" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/BooleanControl.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react" 2 | import { styled } from "../styles" 3 | 4 | export type BooleanControlProps = { 5 | description?: string 6 | checked?: boolean 7 | onChange?: (checked: boolean) => void 8 | } 9 | 10 | export const BooleanControl = ({ 11 | description, 12 | onChange, 13 | checked: initialChecked = false 14 | }: BooleanControlProps) => { 15 | const [checked, setChecked] = useState(initialChecked) 16 | 17 | return ( 18 | <> 19 | { 23 | onChange?.(e.target.checked) 24 | setChecked(e.target.checked) 25 | }} 26 | /> 27 | {description && {description}} 28 | 29 | ) 30 | } 31 | 32 | const Description = styled("span", { 33 | marginLeft: "0.25rem" 34 | }) 35 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "../styles" 2 | 3 | export const Button = styled("button", { 4 | width: "100%", 5 | padding: "6px 8px 5px 8px", 6 | borderRadius: "5px", 7 | boxShadow: 8 | "1px 1px 1px 0 rgba(0, 0, 0, 0.1), inset 1px 1px 0 0 rgba(255, 255, 255, 0.1)", 9 | border: "1px solid rgba(0, 0, 0, 0.8)", 10 | borderWidth: "1px 1px 3px 1px", 11 | 12 | outline: "none", 13 | 14 | font: "inherit", 15 | backgroundColor: "#555", 16 | color: "rgba(255, 255, 255, 0.7)", 17 | cursor: "pointer", 18 | textShadow: "rgba(0, 0, 0, 0.2) 1px 2px 1px", 19 | 20 | "&:hover": { 21 | backgroundColor: "#666" 22 | }, 23 | 24 | "&:active": { 25 | borderWidth: "2px 0 2px 2px" 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/Heading.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "../styles" 2 | 3 | export const Heading = styled("h3", { 4 | font: "inherit", 5 | fontWeight: "bold", 6 | textShadow: "rgba(0, 0, 0, 0.5) 2px 2px 1px", 7 | color: "$headings", 8 | margin: "1.5rem 0 0.5rem 0" 9 | }) 10 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/HorizontalGroup.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "../styles" 2 | 3 | export const HorizontalGroup = styled("div", { 4 | display: "flex", 5 | flexDirection: "row", 6 | justifyContent: "space-between", 7 | width: "100%", 8 | 9 | variants: { 10 | align: { 11 | start: { alignItems: "flex-start" }, 12 | center: { alignItems: "center" }, 13 | end: { alignItems: "flex-end" } 14 | }, 15 | 16 | gap: { 17 | true: { 18 | gap: "0.25rem" 19 | } 20 | } 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/HorizontalResizer.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "../styles" 2 | import React from "react" 3 | 4 | export const HorizontalResizer = () => { 5 | const [dragging, setDragging] = React.useState(false) 6 | 7 | const handleMouseDown = React.useCallback(() => { 8 | setDragging(true) 9 | }, []) 10 | 11 | const handleMouseUp = React.useCallback(() => { 12 | setDragging(false) 13 | }, []) 14 | 15 | return ( 16 | 20 | ) 21 | } 22 | 23 | const HorizontalResizerDiv = styled("div", { 24 | backgroundColor: "#000", 25 | transition: "background-color 0.15s", 26 | width: 5, 27 | cursor: "col-resize", 28 | "&:hover": { 29 | backgroundColor: "hotpink" 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/Input.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "../styles" 2 | 3 | export const Input = styled("input", { 4 | font: "inherit", 5 | color: "inherit", 6 | backgroundColor: "inherit", 7 | border: "0", 8 | background: "#222", 9 | padding: "5px 8px", 10 | outline: "none", 11 | caretColor: "hotpink", 12 | borderRadius: "5px", 13 | boxShadow: 14 | "inset 1px 1px 2px 0 rgba(0, 0, 0, 0.5), inset -1px -1px 2px 0 rgba(255, 255, 255, 0.2)", 15 | "&::selection": { 16 | backgroundColor: "hotpink", 17 | color: "white" 18 | }, 19 | width: "100%" 20 | }) 21 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/Root.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "../styles" 2 | 3 | export const Root = styled("div", { 4 | font: "14px/1.5 Inter, sans-serif", 5 | backgroundColor: "#111", 6 | width: "100%", 7 | height: "100%", 8 | display: "flex", 9 | userSelect: "none" 10 | }) 11 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/VerticalGroup.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "../styles" 2 | 3 | export const VerticalGroup = styled("div", { 4 | display: "flex", 5 | flexDirection: "column", 6 | gap: 4, 7 | padding: 4 8 | }) 9 | -------------------------------------------------------------------------------- /packages/ui-composer/src/components/index.ts: -------------------------------------------------------------------------------- 1 | import { collapseChildren, styled } from "../styles" 2 | 3 | export * from "./BooleanControl" 4 | export * from "./Button" 5 | export * from "./Heading" 6 | export * from "./HorizontalGroup" 7 | export * from "./HorizontalResizer" 8 | export * from "./Input" 9 | export * from "./Root" 10 | export * from "./VerticalGroup" 11 | 12 | export const Panel = styled("div", collapseChildren, { 13 | backgroundColor: "$panelBackground", 14 | borderRadius: 5, 15 | 16 | color: "$panelText", 17 | textShadow: "rgba(0, 0, 0, 0.2) 1px 2px 1px", 18 | padding: "1rem" 19 | }) 20 | 21 | export const Control = styled("div", { 22 | display: "flex", 23 | alignItems: "center", 24 | minHeight: "2rem", 25 | margin: "0.25rem 0" 26 | }) 27 | 28 | export const Label = styled("div", { 29 | flex: "0 0 30%" 30 | }) 31 | -------------------------------------------------------------------------------- /packages/ui-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./components" 2 | -------------------------------------------------------------------------------- /packages/ui-composer/src/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStitches } from "@stitches/react" 2 | 3 | /* Palette: https://coolors.co/palette/22223b-4a4e69-9a8c98-c9ada7-f2e9e4 */ 4 | export const { styled, css, globalCss } = createStitches({ 5 | theme: { 6 | colors: { 7 | panelBackground: "#333", 8 | panelText: "#dcc", 9 | headings: "#F2E9E4" 10 | } 11 | } 12 | }) 13 | 14 | export const collapseChildren = css({ 15 | "*:first-child": { marginTop: 0 }, 16 | "*:last-child": { marginBottom: 0 } 17 | }) 18 | 19 | const globalStyles = globalCss({ 20 | "@import": ["https://rsms.me/inter/inter.css"], 21 | "*": { 22 | boxSizing: "border-box" 23 | }, 24 | body: { 25 | margin: 0, 26 | padding: 0, 27 | height: "100%", 28 | width: "100%", 29 | overflow: "hidden", 30 | font: "14px/1.5 Inter, sans-serif" 31 | }, 32 | "div#root": { 33 | width: "100vw", 34 | height: "100vh" 35 | } 36 | }) 37 | 38 | globalStyles() 39 | -------------------------------------------------------------------------------- /packages/ui-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/vfx-composer-r3f/src/context.ts: -------------------------------------------------------------------------------- 1 | import { World } from "miniplex" 2 | import { createContext, useContext } from "react" 3 | import { InstancedParticles } from "vfx-composer" 4 | import { Entity } from "./InstancedParticles" 5 | 6 | export const createParticlesContext = () => 7 | createContext<{ 8 | particles: InstancedParticles 9 | ecs: World 10 | }>(null!) 11 | 12 | export type ParticlesContext = ReturnType 13 | 14 | export const DefaultContext = createParticlesContext() 15 | -------------------------------------------------------------------------------- /packages/vfx-composer-r3f/src/hooks/particles.ts: -------------------------------------------------------------------------------- 1 | import { useConst } from "@hmans/use-const" 2 | import { createParticleLifetime, ParticleAttribute } from "vfx-composer" 3 | 4 | export const useParticleLifetime = () => 5 | useConst(() => createParticleLifetime()) 6 | 7 | export const useParticleAttribute = < 8 | T extends Parameters[0] 9 | >( 10 | ctor: () => T 11 | ) => useConst(() => ParticleAttribute(ctor())) 12 | -------------------------------------------------------------------------------- /packages/vfx-composer-r3f/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./context" 2 | export * from "./Emitter" 3 | export * from "./hooks/particles" 4 | export * from "./InstancedParticles" 5 | export * from "./Particle" 6 | -------------------------------------------------------------------------------- /packages/vfx-composer-r3f/src/lib/useFrameEffect.ts: -------------------------------------------------------------------------------- 1 | import { useFrame } from "@react-three/fiber" 2 | import { useRef } from "react" 3 | 4 | /* TODO: Extract this into hmans/things or similar */ 5 | 6 | export function useFrameEffect( 7 | dependencyCallback: () => T, 8 | callback: (args: T) => void, 9 | renderPriority = 0 10 | ) { 11 | const value = useRef(null!) 12 | 13 | useFrame(function useFrameEffectUpdate() { 14 | const newValue = dependencyCallback() 15 | 16 | if (value.current !== newValue) { 17 | value.current = newValue 18 | callback(newValue) 19 | } 20 | }, renderPriority) 21 | } 22 | -------------------------------------------------------------------------------- /packages/vfx-composer-r3f/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/vfx-composer/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./createParticleLifetime" 2 | export * from "./ParticleAttribute" 3 | export * from "./InstancedParticles" 4 | 5 | import setupThree from "@shader-composer/three" 6 | setupThree() 7 | -------------------------------------------------------------------------------- /packages/vfx-composer/src/util/makeAttribute.ts: -------------------------------------------------------------------------------- 1 | import { InstancedBufferAttribute } from "three" 2 | 3 | export const makeAttribute = (count: number, itemSize: number) => 4 | new InstancedBufferAttribute(new Float32Array(count * itemSize), itemSize) 5 | -------------------------------------------------------------------------------- /packages/vfx-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "packages/*" 3 | - "apps/*" 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "strict": true, 8 | "jsx": "react", 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "isolatedModules": true, 12 | "skipLibCheck": true 13 | }, 14 | "include": ["packages/*/src/**/*"], 15 | "exclude": ["**/node_modules/**/*", "**/*.spec.ts", "**/*.test.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "pipeline": { 4 | "//#dev:link": { 5 | "cache": false 6 | }, 7 | 8 | "dev": { 9 | "dependsOn": ["//#dev:link"], 10 | "cache": false 11 | }, 12 | 13 | "build": { 14 | "dependsOn": ["^build"], 15 | "outputs": ["dist/**", "*/dist/**"], 16 | "outputMode": "new-only" 17 | }, 18 | 19 | "test": { 20 | "dependsOn": ["^test"], 21 | "outputs": [], 22 | "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }], 3 | "github": { 4 | "silent": true 5 | } 6 | } 7 | --------------------------------------------------------------------------------