@codyjasonbennett/react-ogl
Version:
A barebones react renderer for OGL.
243 lines (214 loc) • 8.13 kB
text/typescript
/// <reference types="webxr" />
import type { Fiber as ReconcilerFiber } from 'react-reconciler'
import type * as OGL from 'ogl'
import type * as React from 'react'
import type { SetState, GetState, UseBoundStore, StoreApi } from 'zustand'
export interface OGLEvent<TEvent extends Event> extends Partial<OGL.RaycastHit> {
nativeEvent: TEvent
}
export interface EventHandlers {
/** Fired when the mesh is clicked or tapped. */
onClick?: (event: OGLEvent<MouseEvent>) => void
/** Fired when a pointer becomes inactive over the mesh. */
onPointerUp?: (event: OGLEvent<PointerEvent>) => void
/** Fired when a pointer becomes active over the mesh. */
onPointerDown?: (event: OGLEvent<PointerEvent>) => void
/** Fired when a pointer moves over the mesh. */
onPointerMove?: (event: OGLEvent<PointerEvent>) => void
/** Fired when a pointer enters the mesh's bounds. */
onPointerOver?: (event: OGLEvent<PointerEvent>) => void
/** Fired when a pointer leaves the mesh's bounds. */
onPointerOut?: (event: OGLEvent<PointerEvent>) => void
}
export type Attach = string | ((parent: any, self: any) => () => void)
export type Fiber = RootStore & ReconcilerFiber
export type Catalogue = Record<Capitalize<keyof OGLElements>, { new (...args: any): any }>
export type InstanceProps = {
[key: string]: unknown
} & {
/** Used to instantiate the underlying OGL object. Changing `args` will reconstruct the object and update any associated refs */
args?: any[]
/** An external OGL object to attach this element to */
object?: any
/** Describes how to attach this element via a property or set of add & remove callbacks */
attach?: Attach
/** Optionally opt-out of disposal with `dispose={null}` */
dispose?: null
}
/**
* Internal react-ogl instance.
*/
export interface Instance {
root: Fiber
parent: Instance | null
children: Instance[]
type: keyof OGLElements
props: InstanceProps
object: any | null
}
export interface XRManager {
session: XRSession | null
setSession(session: XRSession | null): void
connect(session: XRSession): void
disconnect(): void
}
export interface EventManager {
connected: boolean
connect: (target: HTMLCanvasElement, state: RootState) => void
disconnect: (target: HTMLCanvasElement, state: RootState) => void
[name: string]: any
}
export interface Size {
width: number
height: number
}
export type Frameloop = 'always' | 'never'
export type Subscription = (state: RootState, time: number, frame?: XRFrame) => any
export type Interactive = OGL.Mesh & { __handlers?: Partial<EventHandlers> }
export interface RootState {
set: SetState<RootState>
get: GetState<RootState>
size: Size
xr: XRManager
orthographic: boolean
frameloop: Frameloop
renderer: OGL.Renderer
gl: OGL.OGLRenderingContext
scene: OGL.Transform
camera: OGL.Camera
priority: number
subscribed: React.MutableRefObject<Subscription>[]
subscribe: (refCallback: React.MutableRefObject<Subscription>, renderPriority?: number) => void
unsubscribe: (refCallback: React.MutableRefObject<Subscription>, renderPriority?: number) => void
events?: EventManager
mouse?: OGL.Vec2
raycaster?: OGL.Raycast
hovered?: Map<number, Interactive>
[key: string]: any
}
export type Act = (cb: () => Promise<any>) => Promise<any>
export type RootStore = UseBoundStore<RootState, StoreApi<RootState>>
export interface Root {
render: (element: React.ReactNode) => RootStore
unmount: () => void
}
export type DPR = [number, number] | number
export type RenderProps = {
size?: Size
orthographic?: boolean
frameloop?: Frameloop
renderer?:
| ((canvas: HTMLCanvasElement) => OGL.Renderer)
| OGL.Renderer
| WithOGLProps<OGL.Renderer>
| Partial<OGL.RendererOptions>
gl?: OGL.OGLRenderingContext
dpr?: DPR
camera?: OGL.Camera | WithOGLProps<OGL.Camera> | Partial<OGL.CameraOptions>
scene?: OGL.Transform
events?: EventManager
onCreated?: (state: RootState) => any
}
type NonFunctionKeys<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T]
type Filter<T, O> = T extends []
? []
: T extends [infer H, ...infer R]
? H extends O
? Filter<R, O>
: [H, ...Filter<R, O>]
: T
type Args<T> = T extends new (...args: any) => any ? ConstructorParameters<T> : never
type ColorNames = 'black' | 'white' | 'red' | 'green' | 'blue' | 'fuchsia' | 'cyan' | 'yellow' | 'orange'
type UniformValue = ColorNames | number | number[] | OGL.Texture | OGL.Texture[]
type UniformRepresentation = UniformValue | { [structName: string]: UniformValue }
type UniformList = {
[uniform: string]: UniformRepresentation | { value: UniformRepresentation }
}
interface MathRepresentation extends Array<number> {
set(...args: any): any
}
type MathProps<T extends MathRepresentation> = T | Parameters<T['set']> | number
type WithMathProps<T> = { [K in keyof T]: T[K] extends MathRepresentation ? MathProps<T[K]> : T[K] }
type WithProgramProps<T> = T extends OGL.Program
? Omit<T, 'vertex' | 'fragment' | 'uniforms'> & {
vertex?: string
fragment?: string
uniforms?: UniformList
}
: T
type WithGeometryProps<T> = T extends OGL.Geometry
? T & {
[name: string]: Partial<Omit<OGL.Attribute, 'data'>> & Required<Pick<OGL.Attribute, 'data'>>
}
: T
export type WithOGLProps<T, O = {}> = Partial<
WithGeometryProps<WithProgramProps<WithMathProps<Omit<T, NonFunctionKeys<O>>>>>
> &
O
export type Node<T, P> = WithOGLProps<
T,
{
args?: Filter<Args<P>, OGL.OGLRenderingContext>
dispose?: null
attach?: Attach
children?: React.ReactNode
ref?: React.Ref<T>
key?: React.Key
} & (T extends OGL.Mesh ? Partial<EventHandlers> : {})
>
export interface OGLElements {
// primitive
primitive: Omit<Node<{}, {}>, 'args'> & { object: any }
// Core
geometry: Node<OGL.Geometry, typeof OGL.Geometry>
program: Node<OGL.Program, typeof OGL.Program>
renderer: Node<OGL.Renderer, typeof OGL.Renderer>
camera: Node<OGL.Camera, typeof OGL.Camera>
transform: Node<OGL.Transform, typeof OGL.Transform>
mesh: Node<OGL.Mesh, typeof OGL.Mesh>
texture: Node<OGL.Texture, typeof OGL.Texture>
renderTarget: Node<OGL.RenderTarget, typeof OGL.RenderTarget>
// Math
color: Node<OGL.Color, typeof OGL.Color>
euler: Node<OGL.Euler, typeof OGL.Euler>
mat3: Node<OGL.Mat3, typeof OGL.Mat3>
mat4: Node<OGL.Mat4, typeof OGL.Mat4>
quat: Node<OGL.Quat, typeof OGL.Quat>
vec2: Node<OGL.Vec2, typeof OGL.Vec2>
vec3: Node<OGL.Vec3, typeof OGL.Vec3>
vec4: Node<OGL.Vec4, typeof OGL.Vec4>
// Extra
plane: Node<OGL.Plane, typeof OGL.Plane>
box: Node<OGL.Box, typeof OGL.Box>
sphere: Node<OGL.Sphere, typeof OGL.Sphere>
cylinder: Node<OGL.Cylinder, typeof OGL.Cylinder>
triangle: Node<OGL.Triangle, typeof OGL.Triangle>
torus: Node<OGL.Torus, typeof OGL.Torus>
orbit: Node<OGL.Orbit, typeof OGL.Orbit>
raycast: Node<OGL.Raycast, typeof OGL.Raycast>
curve: Node<OGL.Curve, typeof OGL.Curve>
post: Node<OGL.Post, typeof OGL.Post>
skin: Node<OGL.Skin, typeof OGL.Skin>
animation: Node<OGL.Animation, typeof OGL.Animation>
text: Node<OGL.Text, typeof OGL.Text>
normalProgram: Node<OGL.Program, typeof OGL.Program>
flowmap: Node<OGL.Flowmap, typeof OGL.Flowmap>
gPGPU: Node<OGL.GPGPU, typeof OGL.GPGPU>
polyline: Node<OGL.Polyline, typeof OGL.Polyline>
shadow: Node<OGL.Shadow, typeof OGL.Shadow>
kTXTexture: Node<OGL.KTXTexture, typeof OGL.KTXTexture>
textureLoader: Node<OGL.TextureLoader, typeof OGL.TextureLoader>
gLTFLoader: Node<OGL.GLTFLoader, typeof OGL.GLTFLoader>
gLTFSkin: Node<OGL.GLTFSkin, typeof OGL.GLTFSkin>
basisManager: Node<OGL.BasisManager, typeof OGL.BasisManager>
axesHelper: Node<OGL.AxesHelper, typeof OGL.AxesHelper>
faceNormalsHelper: Node<OGL.FaceNormalsHelper, typeof OGL.FaceNormalsHelper>
gridHelper: Node<OGL.GridHelper, typeof OGL.GridHelper>
vertexNormalsHelper: Node<OGL.VertexNormalsHelper, typeof OGL.VertexNormalsHelper>
wireMesh: Node<OGL.WireMesh, typeof OGL.WireMesh>
}
declare global {
namespace JSX {
interface IntrinsicElements extends OGLElements {}
}
}