├── .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 | 
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 |
--------------------------------------------------------------------------------