import * as React from 'react' ; import * as OGL from 'ogl' ; import { createRoot } from '../renderer' ; import { applyProps } from '../utils' ; import { Instance , InstanceProps , RootState , EventHandlers , RenderProps , Subscription } from '../types' ; export type ObjectMap = { nodes : { [ name ] : OGL . Mesh ; } ; programs : { [ name ] : OGL . Program ; } ; } ; /**
 * Collects nodes and programs from a Mesh.
 */ export const buildGraph = ( object ) => { const data = { nodes : { } , programs : { } } ; if ( object ) { object . traverse ( ( obj ) => { if ( obj . name ) { data . nodes [ obj . name ] = obj ; } if ( obj . program ?. gltfMaterial && ! data . programs [ obj . program . gltfMaterial . name ] ) { data . programs [ obj . program . gltfMaterial . name ] = obj . program ; } } ) ; } return data ; } ; /**
 * Creates event handlers, returning an event handler method.
 */ export const createEvents = ( state ) => { const handleEvent = ( event , type ) => { state . mouse . x = ( event . offsetX / state . renderer . width ) * 2 - 1 ; state . mouse . y = - ( event . offsetY / state . renderer . height ) * 2 + 1 ; state . raycaster . castMouse ( state . camera , state . mouse ) ; const intersects = state . raycaster . intersectBounds ( state . scene . children . filter ( ( child ) => child ?. geometry ?. attributes ?. position ) as OGL . Mesh [ ] ) ; const isHoverEvent = type === 'onPointerMove' ; intersects . forEach ( ( object ) => { const handlers = object . __handlers ; if ( ! handlers ) return ; if ( isHoverEvent && ! state . hovered . get ( object . id ) ) { state . hovered . set ( object . id , object ) ; handlers . onPointerMove ?. ( event ) ; handlers . onPointerOver ?. ( event ) ; } else { handlers [ type ] ?. ( event ) ; } } ) ; if ( isHoverEvent || type === 'onPointerDown' ) { state . hovered . forEach ( ( object ) => { const handlers = object . __handlers ; if ( ! intersects . length || ! intersects . find ( ( i ) => i === object ) ) { state . hovered . delete ( object . id ) ; if ( handlers ?. onPointerOut ) handlers . onPointerOut ( event ) ; } } ) ; } return intersects ; } ; return { handleEvent } ; } ; /**
 * Configures rendering internals akin to R3F.
 */ export const createInternals = ( canvas , props ) : RootState => { const renderer = props . renderer instanceof OGL . Renderer ? props . renderer : new OGL . Renderer ( { antialias : true , powerPreference : 'high-performance' , ... ( props . renderer as any ) , canvas : canvas , } ) ; if ( props . renderer ) applyProps ( renderer , props . renderer as InstanceProps ) ; const gl = renderer . gl ; gl . clearColor ( 1 , 1 , 1 , 0 ) ; if ( renderer . gl . endFrameEXP ) { const renderFrame = renderer . render . bind ( renderer ) ; renderer . render = ( { scene , camera } ) => { renderFrame ( { scene , camera } ) ; renderer . gl . endFrameEXP ( ) ; } ; } const camera = props . camera instanceof OGL . Camera ? props . camera : new OGL . Camera ( gl , { fov : 75 , near : 1 , far : 1000 , ... ( props . camera as any ) } ) ; camera . position . z = 5 ; if ( props . camera ) applyProps ( camera , props . camera as InstanceProps ) ; const scene = new OGL . Transform ( gl ) ; let priority = 0 ; let subscribed = [ ] ; const subscribe = ( refCallback , renderPriority ? ) => { subscribed . push ( refCallback ) ; if ( renderPriority ) priority += 1 ; } ; const unsubscribe = ( refCallback , renderPriority ? ) => { subscribed = subscribed . filter ( ( entry ) => entry !== refCallback ) ; if ( renderPriority ) priority -= 1 ; } ; const mouse = new OGL . Vec2 ( ) ; const raycaster = new OGL . Raycast ( gl ) ; const hovered = new Map ( ) ; const state = { ... props , renderer , gl , camera , scene , priority , subscribed , subscribe , unsubscribe , mouse , raycaster , hovered , } ; const root = createRoot ( canvas , state ) ; if ( props . onCreated ) props . onCreated ( state ) ; const animate = ( time ? ) => { if ( props . frameloop === 'never' ) return cancelAnimationFrame ( state . animation ) ; state . animation = requestAnimationFrame ( animate ) ; subscribed . forEach ( ( ref ) => ref . current ?. ( state , time ) ) ; if ( priority ) return ; renderer . render ( { scene , camera } ) ; } ; if ( props . frameloop !== 'never' ) animate ( ) ; return { ... state , root } ; } ;