Skip to content

Commit

Permalink
New Firefly example (#143)
Browse files Browse the repository at this point in the history
* Reimplement Firefly example

* Alpha

* Copy emitter position

* Cleanup

* Yo

* Also inherit scale and rotation

* changeset
  • Loading branch information
hmans committed Aug 2, 2022
1 parent 6f4770b commit cd19781
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/plenty-countries-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vfx-composer": minor
---

**Changed:** `<Emitter>` now applies its world transform to spawned particles, meaning you can parent it to other scene objects for easy-peasy particle trails.
75 changes: 52 additions & 23 deletions apps/examples/src/examples/FireflyExample.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
import { useFrame } from "@react-three/fiber"
import { upTo } from "randomish"
import { useRef } from "react"
import { Color, Mesh, MeshStandardMaterial, NormalBlending } from "three"
import { Emitter, MeshParticles, MeshParticlesMaterial } from "three-vfx"
import { useRef, useState } from "react"
import { OneMinus, Time } from "shader-composer"
import {
Color,
Mesh,
MeshStandardMaterial,
NormalBlending,
Vector2,
Vector3
} from "three"
import { Emitter, Particles, VFX, VFXMaterial } from "vfx-composer/fiber"
import { Lifetime } from "vfx-composer/modules"
import { ParticleAttribute } from "vfx-composer/units"

const tmpVec3 = new Vector3()

export const FireflyExample = () => {
const mesh = useRef<Mesh>(null!)

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

const { ParticleProgress, ParticleAge, module: lifetimeModule } = Lifetime(
variables.lifetime,
variables.time
)

useFrame(({ clock }) => {
const t = clock.elapsedTime
mesh.current.position.set(
Expand All @@ -17,36 +40,42 @@ export const FireflyExample = () => {
})

return (
<MeshParticles>
<Particles>
<planeGeometry args={[0.2, 0.2]} />

<MeshParticlesMaterial
<VFXMaterial
baseMaterial={MeshStandardMaterial}
color={new Color(2, 1, 2)}
blending={NormalBlending}
billboard
depthTest={true}
depthWrite={false}
transparent
/>
>
<VFX.Billboard />
<VFX.Velocity velocity={variables.velocity} time={ParticleAge} />
<VFX.Acceleration force={new Vector3(0, -10, 0)} time={ParticleAge} />
<VFX.SetAlpha alpha={OneMinus(ParticleProgress)} />
<VFX.Module module={lifetimeModule} />
</VFXMaterial>

<mesh ref={mesh}>
<dodecahedronGeometry args={[0.5]} />
<meshStandardMaterial color="hotpink" />
</mesh>

<Emitter
continuous
count={10}
setup={(c) => {
c.position.randomDirection().add(mesh.current.position)
c.lifetime.delay = upTo(0.1)
c.lifetime.duration = upTo(1)
c.velocity.randomDirection().multiplyScalar(upTo(5))
c.acceleration.set(0, -12, 0)
c.alpha.max = 0
}}
/>
</MeshParticles>
<Emitter
continuous
count={10}
setup={({ position }) => {
/*
The position automatically inherits the emitter's position, but let's
add a little random offset to spice things up!
*/
position.add(tmpVec3.randomDirection().multiplyScalar(upTo(0.8)))

const t = variables.time.uniform.value
variables.lifetime.value.set(t, t + 1)
variables.velocity.value.randomDirection().multiplyScalar(upTo(5))
}}
/>
</mesh>
</Particles>
)
}
2 changes: 1 addition & 1 deletion apps/examples/src/examples/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export default [
{ path: "simple", name: "Simple", component: <Simple /> },
{ path: "vanilla", name: "Vanilla", component: <Vanilla /> },
{ path: "stress", name: "Stress", component: <Stress /> },
{ path: "firefly", name: "Firefly", component: <FireflyExample /> },
{ path: "explosion", name: "Explosion (Legacy)", component: <Explosion /> },
{ path: "firefly", name: "Firefly (Legacy)", component: <FireflyExample /> },
{ path: "fog", name: "Fog (Legacy)", component: <Fog /> },
{ path: "snow", name: "Snow (Legacy)", component: <Snow intensity={500} /> },
{ path: "dust", name: "Dust (Legacy)", component: <DustExample /> },
Expand Down
25 changes: 22 additions & 3 deletions packages/vfx-composer/src/fiber/Emitter.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Object3DProps, useFrame } from "@react-three/fiber"
import { Instance } from "@react-three/fiber/dist/declarations/src/core/renderer"
import React, {
forwardRef,
MutableRefObject,
RefObject,
useCallback,
useEffect,
useImperativeHandle,
useRef
} from "react"
import { Object3D } from "three"
import { Matrix4, Object3D, Quaternion, Vector3 } from "three"
import { InstanceSetupCallback, Particles } from "../Particles"
import { useParticlesContext } from "./Particles"

Expand All @@ -18,6 +20,9 @@ export type EmitterProps = Object3DProps & {
setup?: InstanceSetupCallback
}

const tmpMatrix = new Matrix4()
const particlesMatrix = new Matrix4()

export const Emitter = forwardRef<Object3D, EmitterProps>(
(
{
Expand All @@ -32,16 +37,30 @@ export const Emitter = forwardRef<Object3D, EmitterProps>(
const object = useRef<Object3D>(null!)
const particles = particlesProp?.current || useParticlesContext()

const emitterSetup = useCallback<InstanceSetupCallback>(
(props) => {
tmpMatrix
.copy(object.current.matrixWorld)
.premultiply(particlesMatrix)
.decompose(props.position, props.rotation, props.scale)

setup?.(props)
},
[particles, setup]
)

useEffect(() => {
if (!particles) return
if (continuous) return
particles.emit(count, setup)
particlesMatrix.copy(particles!.matrixWorld).invert()
particles.emit(count, emitterSetup)
}, [particles])

useFrame(() => {
if (!particles) return
if (!continuous) return
particles.emit(count, setup)
particlesMatrix.copy(particles!.matrixWorld).invert()
particles.emit(count, emitterSetup)
})

useImperativeHandle(ref, () => object.current)
Expand Down
7 changes: 7 additions & 0 deletions packages/vfx-composer/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,11 @@ export const SetColor = ({ color }: { color: Input<"vec3"> }): Module => (
color
})

export const SetAlpha = ({ alpha }: { alpha: Input<"float"> }): Module => (
state
) => ({
...state,
alpha
})

export const Module = ({ module }: { module: Module }): Module => module

0 comments on commit cd19781

Please sign in to comment.