LocalState for custom renderer node instances instead of userData
alvarosabu opened this issue · comments
Description
As the author and maintainer of TresJS, I would like to simplify and clean the custom renderer nodeOps
to make the code more readable and easier to maintain.
At, we are using overpopulation the property userData for the scene and objects on the scene graph as a workaround for accessing state or adding extra properties and methods that we need for TresJS nodeOps like this:
scene.userData.tres__registerAtPointerEventHandler = registerObject
userData
is a property that the end-user can also use, we need to add the .tres__
prefix to avoid naming collisions, adding an extra cognitive load to the code.
Code ends up being kind of difficult to read:
remove(node) {
if (!node) return
// remove is only called on the node being removed and not on child nodes.
if (node.isObject3D) {
const object3D = node as unknown as Object3D
const disposeMaterialsAndGeometries = (object3D: Object3D) => {
const tresObject3D = object3D as TresObject3D
if (!object3D.userData.tres__materialViaProp) {
tresObject3D.material?.dispose()
tresObject3D.material = undefined
}
if (!object3D.userData.tres__geometryViaProp) {
tresObject3D.geometry?.dispose()
tresObject3D.geometry = undefined
}
}
const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
const deregisterBlockingObjectAtPointerEventHandler
= scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler
const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => {
if (!deregisterBlockingObjectAtPointerEventHandler)
throw 'could not find tres__deregisterBlockingObjectAtPointerEventHandler on scene\'s userData'
scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler?.(object as Object3D)
if (!deregisterAtPointerEventHandler)
throw 'could not find tres__deregisterAtPointerEventHandler on scene\'s userData'
if (
object && supportedPointerEvents.some(eventName => object[eventName])
)
deregisterAtPointerEventHandler?.(object as Object3D)
}
const deregisterCameraIfRequired = (object: Object3D) => {
const deregisterCamera = scene?.userData.tres__deregisterCamera
if (!deregisterCamera)
throw 'could not find tres__deregisterCamera on scene\'s userData'
if ((object as Camera).isCamera)
deregisterCamera?.(object as Camera)
}
node.removeFromParent?.()
object3D.traverse((child: Object3D) => {
disposeMaterialsAndGeometries(child)
deregisterCameraIfRequired(child)
deregisterAtPointerEventHandlerIfRequired?.(child as TresObject)
})
disposeMaterialsAndGeometries(object3D)
deregisterCameraIfRequired(object3D)
deregisterAtPointerEventHandlerIfRequired?.(object3D as TresObject)
}
node.dispose?.()
},
Suggested solution
Add a local state for each instance called __tres
which contains:
export interface LocalState = {
type: string
// objects and parent are used when children are added with `attach` instead of being added to the Object3D scene graph
objects: Instance[]
parent: Instance | null
primitive?: boolean
eventCount: number
handlers: Partial<EventHandlers>
memoizedProps: { [key: string]: any }
}
export interface TresObject3D extends THREE.Object3D<THREE.Object3DEventMap> {
geometry?: THREE.BufferGeometry & TresBaseObject
material?: THREE.Material & TresBaseObject
__tres: LocalState
}
We could include the context of TresContextProvider on the scene object to handle the register of cameras and event handlers or even inside each LocalState on a potential property root
export type LocalState = {
type: string
// objects and parent are used when children are added with `attach` instead of being added to the Object3D scene graph
root: TresContext,
objects: Instance[]
parent: Instance | null
primitive?: boolean
eventCount: number
handlers: Partial<EventHandlers>
memoizedProps: { [key: string]: any }
}
Alternative
No response
Additional context
No response
Validations
- I agree to follow this project's Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.