Skip to content

Commit

Permalink
Emitters can now retrieve the particles mesh from context
Browse files Browse the repository at this point in the history
  • Loading branch information
hmans committed Aug 2, 2022
1 parent 81451b8 commit 2d867ec
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 41 deletions.
5 changes: 5 additions & 0 deletions .changeset/hot-laws-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vfx-composer": patch
---

**Added:** `<Emitter>` will now retrieve the parent `<Particles>` via context if none is specified explicitly.
70 changes: 40 additions & 30 deletions apps/examples/src/examples/Simple.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import { useTexture } from "@react-three/drei"
import { between, plusMinus, random, upTo } from "randomish"
import { useState } from "react"
import { OneMinus, Time } from "shader-composer"
import { Color, MeshStandardMaterial, Vector2, Vector3 } from "three"
import { MeshStandardMaterial, Vector2, Vector3 } from "three"
import { Repeat } from "three-vfx"
import { makeParticles, VFX, VFXMaterial } from "vfx-composer/fiber"
import {
Emitter,
makeParticles,
Particles,
VFX,
VFXMaterial
} from "vfx-composer/fiber"
import { Lifetime } from "vfx-composer/modules"
import { ParticleAttribute } from "vfx-composer/units"

const Effect = makeParticles()

const FREQ = 8
import { particleUrl } from "./textures"

export const Simple = () => {
const texture = useTexture(particleUrl)

const [variables] = useState(() => ({
time: Time(),
lifetime: ParticleAttribute(new Vector2()),
velocity: ParticleAttribute(new Vector3()),
color: ParticleAttribute(new Color())
velocity: ParticleAttribute(new Vector3())
}))

const { ParticleProgress, ParticleAge, module: lifetimeModule } = Lifetime(
Expand All @@ -26,37 +31,42 @@ export const Simple = () => {

return (
<group>
<Effect.Root maxParticles={1_000_000} safetyBuffer={1_000}>
<Particles maxParticles={1000} safetyBuffer={1_000}>
<planeGeometry />

<VFXMaterial baseMaterial={MeshStandardMaterial} color="hotpink">
<VFXMaterial
baseMaterial={MeshStandardMaterial}
map={texture}
transparent
depthWrite={false}
>
<VFX.Billboard />
<VFX.Scale scale={OneMinus(ParticleProgress)} />
<VFX.Velocity velocity={variables.velocity} time={ParticleAge} />
<VFX.Acceleration force={new Vector3(0, -10, 0)} time={ParticleAge} />
<VFX.SetColor color={variables.color} />
<VFX.Module module={lifetimeModule} />
</VFXMaterial>
</Effect.Root>

<Repeat interval={1 / FREQ}>
<Effect.Emitter
count={100_000 / FREQ}
setup={({ position, rotation }) => {
const t = variables.time.uniform.value
const { lifetime, velocity, color } = variables

/* Randomize the instance transform */
position.randomDirection().multiplyScalar(upTo(6))
rotation.random()
<Repeat interval={1}>
<Emitter
count={100}
setup={({ position }) => {
/* Randomize the instance transform */
position.randomDirection().multiplyScalar(upTo(1))

/* Write values into the instanced attributes */
const start = t + random() / FREQ
lifetime.value.set(start, start + between(1, 3))
velocity.value.set(plusMinus(5), between(5, 18), plusMinus(5))
color.value.setRGB(Math.random(), Math.random(), Math.random())
}}
/>
</Repeat>
/* Write values into the instanced attributes */
const t = variables.time.uniform.value
const start = t //+ random()
variables.lifetime.value.set(start, start + between(1, 3))
variables.velocity.value.set(
plusMinus(5),
between(5, 18),
plusMinus(5)
)
}}
/>
</Repeat>
</Particles>
</group>
)
}
21 changes: 17 additions & 4 deletions packages/vfx-composer/src/fiber/Emitter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,39 @@ import React, {
} from "react"
import { Object3D } from "three"
import { InstanceSetupCallback, Particles } from "../Particles"
import { useParticlesContext } from "./Particles"

export type EmitterProps = Object3DProps & {
particles: MutableRefObject<Particles> | RefObject<Particles>
particles?: MutableRefObject<Particles> | RefObject<Particles>
count?: number
continuous?: boolean
setup?: InstanceSetupCallback
}

export const Emitter = forwardRef<Object3D, EmitterProps>(
({ particles, count = 1, continuous = false, setup, ...props }, ref) => {
(
{
particles: particlesProp,
count = 1,
continuous = false,
setup,
...props
},
ref
) => {
const object = useRef<Object3D>(null!)
const particles = particlesProp?.current || useParticlesContext()

useEffect(() => {
if (!particles) return
if (continuous) return
particles.current?.emit(count, setup)
particles.emit(count, setup)
}, [particles])

useFrame(() => {
if (!particles) return
if (!continuous) return
particles.current?.emit(count, setup)
particles.emit(count, setup)
})

useImperativeHandle(ref, () => object.current)
Expand Down
34 changes: 29 additions & 5 deletions packages/vfx-composer/src/fiber/Particles.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { extend, InstancedMeshProps, Node } from "@react-three/fiber"
import { extend, InstancedMeshProps } from "@react-three/fiber"
import React, {
createContext,
forwardRef,
useContext,
useEffect,
useImperativeHandle,
useRef
useRef,
useState
} from "react"
import { Particles as ParticlesImpl } from "../Particles"
import { VFXMaterial as VFXMaterialImpl } from "../VFXMaterial"
Expand All @@ -25,16 +28,33 @@ declare global {
}
}

const Context = createContext<ParticlesImpl | null>(null)

export const useParticlesContext = () => useContext(Context)

export const Particles = forwardRef<ParticlesImpl, ParticlesProps>(
(
{ maxParticles = 1000, safetyBuffer = 100, geometry, material, ...props },
{
children,
maxParticles = 1000,
safetyBuffer = 100,
geometry,
material,
...props
},
ref
) => {
const [_, setReady] = useState(false)
const particles = useRef<ParticlesImpl>(null!)

/*
We need to initialize the particles mesh in an effect, because in most cases,
the material driving it will only be created in its children.
*/
useEffect(() => {
particles.current.setupParticles()
}, [])
setReady(true)
}, [particles])

useImperativeHandle(ref, () => particles.current)

Expand All @@ -43,7 +63,11 @@ export const Particles = forwardRef<ParticlesImpl, ParticlesProps>(
args={[geometry, material, maxParticles, safetyBuffer]}
ref={particles}
{...props}
/>
>
<Context.Provider value={particles.current}>
{children}
</Context.Provider>
</vfxComposerParticles>
)
}
)
4 changes: 2 additions & 2 deletions packages/vfx-composer/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type ModuleState = {

export type Module = (state: ModuleState) => ModuleState
export type ModuleProps = Record<string, any>
export type ModuleFactory<P extends ModuleProps> = (props: P) => Module
export type ModuleFactory<P extends ModuleProps = {}> = (props: P) => Module

export type ModulePipe = Module[]

Expand Down Expand Up @@ -97,7 +97,7 @@ export const Acceleration = ({ force, time }: AccelerationProps) =>
)
})

export const Billboard = (): Module => (state) => ({
export const Billboard: ModuleFactory = () => (state) => ({
...state,
position: BillboardUnit(state.position)
})
Expand Down

0 comments on commit 2d867ec

Please sign in to comment.