Tracking the change in objects' positions and quaternions

This has been tested in v146.

Vanilla JS in the simplest way I could imagine, allows to enable tracking of existing objects’ properties (potentially in somebody else’s code you have no full control of).

This solution solves two problems:

  1. A situation where different functions (that can be called in unknown order before or after the render call) need to know if the property in question has changed since the last function call.

  2. Immediate notification (event like) of the change that will execute some code.

First, I create new properties on the object and replace the old ones with getters and setters to make it “reactive”:

  // arr = [cam.quaternion, Obj3D.position, ...]
  // mailbox = {}

const enableTracking = (arr, mailbox) => { 

	arr.forEach(obj => { 

		const q = obj.isQuaternion;
		const prop = q ? ['_x', '_y', '_z'] : ['x', 'y', 'z'];
		if (q) prop.push('_w');

		for(const p of prop) {
			const np = '_' + p;
			Object.defineProperty(obj, np, {
				configurable: true,
				enumerable: true,
				value: obj[p],
				writable: true,
			});
			
			Object.defineProperty(obj, p, {
				configurable: true,
				enumerable: true,
				get: () => obj[np],
				set: v => { 
					obj[np] = v;
					for (const pr in mailbox) mailbox[pr] = true;
				},
			});
		}
	});
};

mailbox is an object inside which all interested parties can create their properties (subscribe). All those properties will be triggered by the setter.

Use case to solve problem 1:

const mailbox = { cam_check: false };
enableTracking([camera.position, camera.quaternion], mailbox);

const someFunction = () => {
    if(mailbox.cam_check) {
        mailbox.cam_check = false;
        // do something
    }
};

Use case to solve problem 2 (event-like notification):

const mailbox = {
    set cam_pos_quat_changed(v) { rebuildCamCoordSys() },
};

...

enableTracking([camera.position, camera.quaternion], mailbox);
2 Likes

That’s interesting, I ended up building a whole separate quat/vec3 abstraction for myself for this very reason. At least from my perspective - this is a super useful feature in general.

You can write your engine to be fully tick-driven, or fully event-driven. Or… why not a mix of both? Three’s is mostly tick-driven, with a couple of exceptions here and there such as onBeforeCompile etc.

I find that there are plenty of features that are easier to code in an event-driven way, and even when there is no tangible benefit to how easy something is to implement - you have cases where very little changes in the scene, and event-driven approach ends up being leagues more efficient.

Imagine you have 100,000 objects of some kind in your scene, and only 3 of them change state this frame, in event-driven approach you can know that and only process those 3. Sure, this is pretty unusual case, but even if you have just thousand or two objects here and there - it tends to add up.

1 Like