Specialized components
Physics and spatial systems
Three.js monkey patching & proxies
16 min
overview the ir engine uses specialized techniques to integrate the three js rendering library with its entity component system (ecs) architecture this integration ensures that the ecs remains the single source of truth for entity data while leveraging three js's powerful rendering capabilities two key techniques enable this seamless integration monkey patching and javascript proxies these mechanisms ensure that changes to entity transforms are consistently reflected in both the ecs and three js, preventing synchronization issues and maintaining system integrity the integration challenge three js and the ecs architecture both maintain their own representations of spatial data ecs transformcomponent stores position, rotation, and scale data for entities within the ecs framework three js object3d contains its own position, rotation, and scale properties for rendering without proper integration, these two systems could become desynchronized, leading to inconsistencies where the ecs might think an entity is at position x=5 the three js object might be rendered at position x=10 the ir engine solves this challenge by ensuring the ecs transformcomponent is always the authoritative source of spatial information, while keeping three js objects automatically synchronized monkey patching monkey patching involves modifying the behavior of existing three js methods to make them compatible with the ecs architecture this technique is implemented in the threejspatches ts file world position calculation one key area of patching is how three js calculates an object's world position // simplified from src/threejspatches ts import { object3d, vector3 } from 'three'; // override three js's built in method object3d prototype getworldposition = function(targetvector vector3) vector3 { // use the matrixworld that's already linked to the ecs transformcomponent targetvector setfrommatrixposition(this matrixworld); return targetvector; }; this patch ensures that when three js needs to determine an object's world position (for rendering, raycasting, etc ), it uses the matrix data that has already been calculated by the ecs transform system additional patches the engine also patches other three js methods to maintain ecs compatibility // simplified examples of other patches object3d prototype getworldquaternion = function(targetquaternion) { // use ecs calculated world matrix targetquaternion setfromrotationmatrix(this matrixworld); return targetquaternion; }; object3d prototype getworldscale = function(targetscale) { // use ecs calculated world matrix this matrixworld decompose( position, quaternion, targetscale); return targetscale; }; these patches ensure that all spatial queries in three js respect the ecs as the authoritative source of transform data javascript proxies while monkey patching modifies three js methods, javascript proxies intercept operations on three js objects to ensure changes are properly reflected in the ecs this is particularly important for properties like position , quaternion , and scale vector3 proxies the proxifyvector3withdirty function creates a proxy for three js vector3 properties // simplified from src/common/proxies/createthreejsproxy ts export const proxifyvector3withdirty = ( ecspositionstorage, // transformcomponent position entity, // entity id ecsdirtyflags, // transformcomponent dirty threejsvector // three js vector3 (e g , mesh position) ) => { // intercept property access for 'x' object defineproperty(threejsvector, 'x', { get() { return ecspositionstorage x\[entity]; // read from ecs }, set(newvalue) { ecsdirtyflags\[entity] = 1; // mark ecs component as dirty ecspositionstorage x\[entity] = newvalue; // update ecs data } }); // similar definitions for 'y' and 'z' // return threejsvector; }; this proxy ensures that when code modifies a three js object's position (e g , mesh position x = 10 ), the change is automatically applied to the ecs transformcomponent and the component is marked for update objectcomponent integration the objectcomponent is the bridge between an ecs entity and its three js representation when an objectcomponent is added to an entity, it sets up the necessary proxies and direct matrix links // simplified from src/renderer/components/objectcomponent ts export const objectcomponent = definecomponent({ name 'objectcomponent', schema s type\<object3d>({ required true }), onset(entity, component, obj object3d) { // ensure entity has a transformcomponent setcomponent(entity, transformcomponent); const transform = getcomponent(entity, transformcomponent); // set up proxies for position, rotation, and scale proxifyvector3withdirty( transformcomponent position, entity, transformcomponent dirty, obj position ); // similar for quaternion and scale // direct matrix linking critical for performance obj matrix = transform matrix; obj matrixworld = transform matrixworld; obj matrixautoupdate = false; // ecs handles updates component set(obj); } }); the direct linking of matrices ( obj matrix = transform matrix ) is particularly important it means there's no copying of matrix data between the ecs and three js; they literally share the same underlying data structures data flow the integration of these techniques creates two valid paths for updating an entity's transform path 1 updating the ecs directly (recommended) // get the transform component const transform = getcomponent(myentity, transformcomponent); // update position transform position x = 20; // mark as dirty (may be automatic depending on implementation) transformcomponent dirty\[myentity] = 1; the transformcomponent data is updated the transform system detects the dirty flag and recalculates matrices since the three js object's matrices are directly linked to the ecs matrices, the visual representation is automatically updated path 2 updating through the three js object // get the three js object via objectcomponent const threeobject = getcomponent(myentity, objectcomponent); // update position threeobject position x = 20; the proxy intercepts the property assignment the proxy updates the ecs transformcomponent data the proxy marks the component as dirty the transform system processes the update as in path 1 sequencediagram participant usercode participant proxy participant ecs as transformcomponent participant transformsys as transform system participant threeobj as three js object participant renderer alt updating ecs directly usercode >>ecs transform position x = 20 note right of ecs component marked dirty transformsys >>ecs update matrices note right of threeobj threeobj matrixworld is ecs matrixworld renderer >>threeobj render using matrices else updating via three js object usercode >>proxy threeobj position x = 20 proxy >>ecs update position and mark dirty transformsys >>ecs update matrices note right of threeobj threeobj matrixworld is ecs matrixworld renderer >>threeobj render using matrices end benefits this integration approach provides several key benefits single source of truth the ecs transformcomponent is always the authoritative source of spatial data consistency changes are automatically synchronized between systems performance direct matrix linking avoids redundant calculations and data copying flexibility developers can work with either ecs components or three js objects as needed maintainability clear separation between data (components) and logic (systems) best practices while both update paths are valid, the recommended approach is to modify the ecs transformcomponent directly whenever possible this ensures the most predictable behavior and maintains the architectural integrity of the system conclusion the three js monkey patching and proxy techniques enable the ir engine to leverage the powerful rendering capabilities of three js while maintaining the architectural benefits of an entity component system this integration ensures consistent behavior, optimal performance, and a clean development experience throughout this documentation series, we've explored the core systems of the ir engine the foundational entity component system (ecs) core docid\ hxkz89g8mzhpu25yy0aal the spatial transform system docid 4qeojagryifci ukqrpaz the visual rendering system docid 9tweoypzmllk2luw1ohxc the interactive input system docid\ gaammfydn5tltewfrx3jn the realistic physics system docid\ ddtqld02fhez3s5xhqvot the coordinate management in reference space management docid\ jv 2db5evnyb4piiqn3yi the immersive xr (extended reality) integration docid\ r8 zl2geuvevqapp8x0x these systems, combined with the integration techniques described in this chapter, provide a robust foundation for building complex, interactive 3d and xr applications