Skip to content

Commit

Permalink
feat!: React 19 support (#302)
Browse files Browse the repository at this point in the history
  • Loading branch information
aryankarim authored Jan 16, 2025
1 parent 369c0e5 commit 093c7d6
Show file tree
Hide file tree
Showing 19 changed files with 147 additions and 145 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ From
> the screen diagonal. This is especially beneficial for GPGPU passes and
> effects that use complex fragment shaders.
Postprocessing also supports srgb-encoding out of the box, as well as WebGL2
Postprocessing also supports gamma correction out of the box, as well as WebGL2
MSAA (multi sample anti aliasing), which is react-postprocessing's default, you
get high performance crisp results w/o jagged edges.

Expand Down
2 changes: 1 addition & 1 deletion docs/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ From
> the screen diagonal. This is especially beneficial for GPGPU passes and
> effects that use complex fragment shaders.
Postprocessing also supports srgb-encoding out of the box, as well as WebGL2
Postprocessing also supports gamma correction out of the box, as well as WebGL2
MSAA (multi sample anti aliasing), which is react-postprocessing's default, you
get high performance crisp results w/o jagged edges.

Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,21 @@
"maath": "^0.6.0",
"n8ao": "^1.6.6",
"postprocessing": "^6.32.1",
"three-stdlib": "^2.23.4"
"three-stdlib": "^2.35.7"
},
"devDependencies": {
"@react-three/drei": "^9.68.2",
"@react-three/fiber": "^8.13.0",
"@react-three/fiber": "9.0.0-rc.4",
"@storybook/addon-essentials": "^7.0.10",
"@storybook/addon-interactions": "^7.0.10",
"@storybook/addon-links": "^7.0.10",
"@storybook/blocks": "^7.0.10",
"@storybook/react": "^7.0.10",
"@storybook/react-vite": "^7.0.11",
"@storybook/testing-library": "^0.0.14-next.2",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
"@types/three": "^0.150.2",
"@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2",
"@types/three": "^0.156.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"eslint": "^8.39.0",
Expand All @@ -78,19 +78,19 @@
"husky": "^8.0.3",
"lint-staged": "^13.2.2",
"prettier": "^2.8.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rimraf": "^5.0.0",
"semantic-release": "^21.0.2",
"storybook": "^7.0.10",
"three": "^0.151.3",
"three": "^0.156.0",
"typescript": "^5.0.4",
"vite": "^4.3.5",
"vitest": "^2.1.8"
},
"peerDependencies": {
"@react-three/fiber": "^8.0",
"react": "^18.0",
"three": ">= 0.138.0"
"@react-three/fiber": "9.0.0-rc.4",
"react": ">=19.0",
"three": ">= 0.156.0"
}
}
2 changes: 1 addition & 1 deletion src/EffectComposer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ global.IS_REACT_ACT_ENVIRONMENT = true
vi.mock('scheduler', () => require('scheduler/unstable_mock'))

// Create virtual R3F root for testing
extend(THREE)
extend(THREE as any)
const root = createRoot({
style: {} as CSSStyleDeclaration,
addEventListener: (() => {}) as any,
Expand Down
21 changes: 11 additions & 10 deletions src/EffectComposer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { TextureDataType } from 'three'
import type { TextureDataType, Group } from 'three'
import { HalfFloatType, NoToneMapping } from 'three'
import React, {
import {
type JSX,
memo,
forwardRef,
useMemo,
useEffect,
Expand All @@ -9,13 +11,12 @@ import React, {
useRef,
useImperativeHandle,
} from 'react'
import { useThree, useFrame } from '@react-three/fiber'
import { useThree, useFrame, type Instance } from '@react-three/fiber'
import {
EffectComposer as EffectComposerImpl,
RenderPass,
EffectPass,
NormalPass,
// @ts-ignore
DepthDownsamplingPass,
Effect,
Pass,
Expand Down Expand Up @@ -51,7 +52,7 @@ export type EffectComposerProps = {
const isConvolution = (effect: Effect): boolean =>
(effect.getAttributes() & EffectAttribute.CONVOLUTION) === EffectAttribute.CONVOLUTION

export const EffectComposer = React.memo(
export const EffectComposer = memo(
forwardRef<EffectComposerImpl, EffectComposerProps>(
(
{
Expand Down Expand Up @@ -128,25 +129,25 @@ export const EffectComposer = React.memo(
enabled ? renderPriority : 0
)

const group = useRef(null)
const group = useRef<Group>(null!)
useLayoutEffect(() => {
const passes: Pass[] = []

// TODO: rewrite all of this with R3F v9
const groupInstance = (group.current as any)?.__r3f as { objects: unknown[] }
const groupInstance = (group.current as Group & { __r3f: Instance<Group> }).__r3f

if (groupInstance && composer) {
const children = groupInstance.objects
const children = groupInstance.children

for (let i = 0; i < children.length; i++) {
const child = children[i]
const child = children[i].object

if (child instanceof Effect) {
const effects: Effect[] = [child]

if (!isConvolution(child)) {
let next: unknown = null
while ((next = children[i + 1]) instanceof Effect) {
while ((next = children[i + 1]?.object) instanceof Effect) {
if (isConvolution(next)) break
effects.push(next)
i++
Expand Down
5 changes: 3 additions & 2 deletions src/Selection.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as THREE from 'three'
import React, { createContext, useState, useContext, useEffect, useRef, useMemo } from 'react'
import { type ThreeElements } from '@react-three/fiber'

export type Api = {
selected: THREE.Object3D[]
select: React.Dispatch<React.SetStateAction<THREE.Object3D[]>>
enabled: boolean
}
export type SelectApi = JSX.IntrinsicElements['group'] & {
export type SelectApi = Omit<ThreeElements['group'], 'ref'> & {
enabled?: boolean
}

Expand All @@ -24,7 +25,7 @@ export function Select({ enabled = false, children, ...props }: SelectApi) {
useEffect(() => {
if (api && enabled) {
let changed = false
const current: THREE.Object3D<THREE.Event>[] = []
const current: THREE.Object3D[] = []
group.current.traverse((o) => {
o.type === 'Mesh' && current.push(o)
if (api.selected.indexOf(o) === -1) changed = true
Expand Down
2 changes: 1 addition & 1 deletion src/effects/Autofocus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type AutofocusProps = React.ComponentProps<typeof DepthOfField> & {
}

export type AutofocusApi = {
dofRef: RefObject<DepthOfFieldEffect>
dofRef: RefObject<DepthOfFieldEffect | null>
hitpoint: THREE.Vector3
update: (delta: number, updateTarget: boolean) => void
}
Expand Down
1 change: 1 addition & 0 deletions src/effects/Bloom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import { wrapEffect } from '../util'

export const Bloom = wrapEffect(BloomEffect, {
blendFunction: BlendFunction.ADD,
args: [],
})
3 changes: 1 addition & 2 deletions src/effects/GodRays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import { EffectComposerContext } from '../EffectComposer'
import { resolveRef } from '../util'

type GodRaysProps = ConstructorParameters<typeof GodRaysEffect>[2] & {
sun: Mesh | Points | React.MutableRefObject<Mesh | Points>
sun: Mesh | Points | React.RefObject<Mesh | Points>
}

export const GodRays = forwardRef(function GodRays(props: GodRaysProps, ref: Ref<GodRaysEffect>) {
const { camera } = useContext(EffectComposerContext)
const effect = useMemo(() => new GodRaysEffect(camera, resolveRef(props.sun), props), [camera, props])
// @ts-ignore v6.30.2 https://github.com/pmndrs/postprocessing/pull/470/commits/091ef6f9516ca02efa7576305afbecf1ce8323ae
useLayoutEffect(() => void (effect.lightSource = resolveRef(props.sun)), [effect, props.sun])
return <primitive ref={ref} object={effect} dispose={null} />
})
2 changes: 1 addition & 1 deletion src/effects/Noise.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NoiseEffect, BlendFunction } from 'postprocessing'
import { wrapEffect } from '../util'

export const Noise = wrapEffect(NoiseEffect, { blendFunction: BlendFunction.COLOR_DODGE })
export const Noise = wrapEffect(NoiseEffect, { blendFunction: BlendFunction.COLOR_DODGE, args: [] })
6 changes: 3 additions & 3 deletions src/effects/Outline.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { OutlineEffect } from 'postprocessing'
import { Ref, MutableRefObject, forwardRef, useMemo, useEffect, useContext, useRef } from 'react'
import { Ref, RefObject, forwardRef, useMemo, useEffect, useContext, useRef } from 'react'
import { Object3D } from 'three'
import { useThree } from '@react-three/fiber'
import { EffectComposerContext } from '../EffectComposer'
import { selectionContext } from '../Selection'
import { resolveRef } from '../util'

type ObjectRef = MutableRefObject<Object3D>
type ObjectRef = RefObject<Object3D>

export type OutlineProps = ConstructorParameters<typeof OutlineEffect>[2] &
Partial<{
Expand Down Expand Up @@ -93,7 +93,7 @@ export const Outline = forwardRef(function Outline(
invalidate()
}, [effect, invalidate, selectionLayer])

const ref = useRef<OutlineEffect>()
const ref = useRef<OutlineEffect>(undefined)
useEffect(() => {
if (api && api.enabled) {
if (api.selected?.length) {
Expand Down
2 changes: 1 addition & 1 deletion src/effects/ScanlineEffect.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ScanlineEffect, BlendFunction } from 'postprocessing'
import { wrapEffect } from '../util'

export const Scanline = wrapEffect(ScanlineEffect, { blendFunction: BlendFunction.OVERLAY, density: 1.25 })
export const Scanline = wrapEffect(ScanlineEffect, { blendFunction: BlendFunction.OVERLAY, density: 1.25, args: [] })
4 changes: 2 additions & 2 deletions src/effects/SelectiveBloom.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { SelectiveBloomEffect, BlendFunction } from 'postprocessing'
import type { BloomEffectOptions } from 'postprocessing'
import React, { Ref, MutableRefObject, forwardRef, useMemo, useEffect, useContext, useRef } from 'react'
import React, { Ref, RefObject, forwardRef, useMemo, useEffect, useContext, useRef } from 'react'
import { Object3D } from 'three'
import { useThree } from '@react-three/fiber'
import { EffectComposerContext } from '../EffectComposer'
import { selectionContext } from '../Selection'
import { resolveRef } from '../util'

type ObjectRef = MutableRefObject<Object3D>
type ObjectRef = RefObject<Object3D>

export type SelectiveBloomProps = BloomEffectOptions &
Partial<{
Expand Down
7 changes: 2 additions & 5 deletions src/effects/Texture.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TextureEffect } from 'postprocessing'
import { Ref, forwardRef, useMemo, useLayoutEffect } from 'react'
import { useLoader } from '@react-three/fiber'
import { TextureLoader, RepeatWrapping } from 'three'
import { TextureLoader, SRGBColorSpace, RepeatWrapping } from 'three'

type TextureProps = ConstructorParameters<typeof TextureEffect>[0] & {
textureSrc: string
Expand All @@ -13,10 +13,7 @@ export const Texture = forwardRef<TextureEffect, TextureProps>(function Texture(
) {
const t = useLoader(TextureLoader, textureSrc)
useLayoutEffect(() => {
// @ts-ignore
if ('encoding' in t) t.encoding = 3001 // sRGBEncoding
// @ts-ignore
else t.colorSpace = 'srgb'
t.colorSpace = SRGBColorSpace
t.wrapS = t.wrapT = RepeatWrapping
}, [t])
const effect = useMemo(() => new TextureEffect({ ...props, texture: t || texture }), [props, t, texture])
Expand Down
2 changes: 1 addition & 1 deletion src/effects/TiltShift.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TiltShiftEffect, BlendFunction } from 'postprocessing'
import { wrapEffect } from '../util'

export const TiltShift = wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.ADD })
export const TiltShift = wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.ADD, args: [] })
2 changes: 1 addition & 1 deletion src/effects/TiltShift2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ export class TiltShiftEffect extends Effect {
}
}

export const TiltShift2 = wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.NORMAL })
export const TiltShift2 = wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.NORMAL, args: [] })
2 changes: 1 addition & 1 deletion src/effects/Water.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ export class WaterEffectImpl extends Effect {
}
}

export const WaterEffect = wrapEffect(WaterEffectImpl, { blendFunction: BlendFunction.NORMAL })
export const WaterEffect = wrapEffect(WaterEffectImpl, { blendFunction: BlendFunction.NORMAL, args: [] })
11 changes: 5 additions & 6 deletions src/util.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import React, { MutableRefObject } from 'react'
import React, { RefObject } from 'react'
import { Vector2 } from 'three'
import * as THREE from 'three'
import { type ReactThreeFiber, extend, useThree } from '@react-three/fiber'
import { type ReactThreeFiber, type ThreeElement, extend, useThree } from '@react-three/fiber'
import type { Effect, BlendFunction } from 'postprocessing'

export const resolveRef = <T,>(ref: T | React.MutableRefObject<T>) =>
export const resolveRef = <T,>(ref: T | React.RefObject<T>) =>
typeof ref === 'object' && ref != null && 'current' in ref ? ref.current : ref

export type EffectConstructor = new (...args: any[]) => Effect

export type EffectProps<T extends EffectConstructor> = ReactThreeFiber.Node<
T extends Function ? T['prototype'] : InstanceType<T>,
T
export type EffectProps<T extends EffectConstructor> = ThreeElement<
T extends Function ? T['prototype'] : InstanceType<T>
> &
ConstructorParameters<T>[0] & {
blendFunction?: BlendFunction
Expand Down
Loading

0 comments on commit 093c7d6

Please sign in to comment.