What's the best simple way to handle the update pattern?

That’s the clean way, userData shouldn’t contain methods or references, but it can be added to the base classes Mesh and SkinnedMesh as well (prototype), for a more generic approach as loaded assets already created objects with the base classes.

I use tags which can be added to Object3D based classes, these tags can be singletons or instances. This way controllers can be added as well as temporary effects and such, it’s kinda similar to tags in Cinema 4D.

Events implemented in the tag such as step (frame) and input determine what they will listen to, so this logic is detached from the scene hierarchie to avoid unnecessary traversal in rich scenes.

A controller tag is an object/class (singleton or instance) itself to reuse on assets, for example to control an AI a singleton receieves the objects it has to process while an instance is individual to every object the tag is added to.

I shaped this concept over time with focus on performance and memory, it probably isn’t exactly a common existing concept, maybe a mix or extension, but it fits all needs i have .

I think this is actually a strong argument in favor of my suggestion.

We should define the property as an empty function on the base class so the superclasses can safely overwrite it. By “reserve the method”, I meant that either we put an empty placeholder Object3D.update method, or we just make a statement that we’ll never do anything to mess up user code that add this method. I don’t have a preference which, and I also don’t care what name is chosen - tick, onUpdate, etc. All good.

There are already several methods like this in three.js. Usually, their name starts with “on” - onBeforeRender, onAfterRender, onUploadCallback, and so on.

Not saying this is exactly the same - here the user has to manually create the main update function that calls all the small ones, whereas the “on” methods are called automatically. But it’s not that different, and I don’t think it’s unusual to define a placeholder method that users can overwrite.

Yeah, agreed. I have been considering which way do it, but for now settled on just adding the property since it’s simple and I’ve seen other people do it that way. In this case, the end result is the same.

But the main reason (I’m aware of) that adding a property is bad practice is that a future version of the library may add an Object3D.update method. Extending the class to add the method has the same problem.

Can you explain what you mean by this?

I’m not familiar with Cinema4D but this sounds like an interesting approach. However, I doubt it’s going to useful for me. My goal here is to create something simple enough that I can write about it in introductory tutorials.

The reason I’m using this approach is because, when creating small and simple examples, this is the simplest way I have found so far of encapsulating animation/mixer/mesh/geometry/materials and any extra code to update them.

1 Like

Adding properties to instances outside the prototype and constructor will internally create a new “class” in js engines, so you can monkey patch it and add the property to the prototype before your application starts.

Yes your suggested updated callback with traversal definitely will work, especially to be done without an extra plugin or library. For full controllers you might want to have something more reusable, controllers that might temporary create new objects you have to store somewhere, and other events such as create/destroy etc.

1 Like

Hmm, good to know. Do you know of anywhere that documents this? I vaguely remember reading that on the V8 blog recently, not sure where though. Don’t worry if you can’t remember, I’ll take your word for it :slightly_smiling_face:

I had expected this anyway because I’m binding the mixer to the mesh using a closure, which as far as I know creates a new class shape as well.

Seems like I should rename this method to tick or onUpdate to avoid collision with existing update methods inside three.js, and then do this:

Object3D.prototype.tick = () => {}

GLTFLoader.load(url, (gltf) => {
   const object = gltf.scene.children[0];
   const mixer = new AnimationMixer(object);

   const action = mixer.clipAction(gltf.animations[0]);
   action.play();

   object.tick = (delta) => {
      mixer.update(delta);
   }
});

@fyrestar is that similar to what you are suggesting?

@donmccurdy I remember now why I’m not using extend - I want this to work with loaded models, and short of patching the loader I can’t control what they return.

2 Likes

I can’t recall the exact source right now, it probably was the V8 blog actually ^^

I’d go with onUpdate to stick with the “on” pattern like onBeforeRender

Yes, just making sure the property is reserved.

Yes this is why i use a generic class for models too, besides the tags for controllers i use a model for the pure data (just like userData) wich represents the abstracted layer basically, like character data, health, name, items etc to have every layer separated instead trying to store all on one.

Just as a side note as this addresses this architecture, it would be great if THREE internally also would replace the internal condition forest to a faster and more clean and flexible reference based system, so instead checking through many “hardcoded” conditions like isMehsStandardMaterial, isShaderMaterial etc just moving the relevant code to the materials class with abstract methods would be great ^^ could be also applied to many other cases. That would also make extending THREE easier as anything like this currently requires core changes.

1 Like

That would be my preference too, but I’m afraid at some point three.js will add an onUpdate method that breaks my code.

I think tick is usually used to represent a fixed timestep but that’s probably less of an issue.

1 Like

Maybe onTick then? :smiley_cat:

I personally use “step”. And “update” rather for event based methods which might be not called per frame or even multiple times.

1 Like

The reason I want to call this update is that it’s an extremely common name for this method. Along with the animation loop, this seems to be one of the most common patterns in graphics programming.

For example:

1. Game programming patterns chapter that I linked above:

Simulate a collection of independent objects by telling each to process one frame of behavior at a time.

If the Game Loop pattern is the best thing since sliced bread, then the Update Method pattern is its butter. A wide swath of games featuring live entities that the player interacts with use this pattern in some form or other. If the game has space marines, dragons, Martians, ghosts, or athletes, there’s a good chance it uses this pattern.

2. Unity MonoBehaviour.Update

Update is the most commonly used function to implement any kind of game script.

Bonus cool description of the Unity loop.

3. Unreal calls it ticking

Actors and components are ticked once per frame, unless a minimum ticking interval is specified.

I could go on, but I think I’ll probably find that every game engine implements this pattern in some manner.

I know three.js is not a game engine, but any loop based graphics app will inevitably share many concepts with game engines, which is why I’m suggesting reserving one method name on Object3D to make using this pattern easier.

I’ll do some more work on this and open a GH issue. In the meantime I’ll follow Unreal and call the method tick.

1 Like

Yeah that’s totally valid and more just a matter of taste :smiley_cat: i want to distinguish between the update constantly called in the render loop by the engine and the explicit use of update, for example to update a geometry with a buffer received from a worker. I’ve seen step in other engines too , but the reason i use it is like i said to separate update and the constant game logic per frame update, “update” is used in many ways.

1 Like

From How To Write Fast, Memory-Efficient JavaScript

If you care about speed, try very hard to keep your functions monomorphic, i.e. make sure that variables (including properties, arrays and function parameters) only ever contain objects with the same hidden class.

A single operation like delete o.x can change an object’s hidden class; adding a property would do the same. From What’s up with monomorphism?

Fluffy dictionary nature of JavaScript objects also makes it easy to create accidental polymorphism:

function A() {
  this.x = 1;
}

var a = new A(), b = new A(); // same shape

if (something) {
  a.y = 2;  // shape of a no longer matches b.
}

Thanks for the link. So, as @Fyrestar suggests, doing this:

Object3D.prototype.tick = () => {}

before creating any instances of Object3D or derived classes will prevent deoptimization from occurring.

This is off topic i guess, but maybe it helps you to avoid the same kind of issues that we faced.

At work we’re making a CAD system based on threejs, also a head-less framework for modelling operations similar to three-csg, but at full CAD capacity. Updates were exactly like the method described above. When consumed it was put into a collection to avoid full traverse.

Eventually it became clear that the pattern caused problems. Objects updating themselves turned out to be a recipe for race conditions. The object should be a reflection of state, it shouldn’t bear implicit knowledge of state. Whenever render order changed for whatever reason (server push, etc), it became mind boggling, behaviour was basically switching around in no manageable order.

After years of fighting against it we figured that updates belong to the component level. By that i mean the instance that has access to state or logic as well as the view. That layer receives state and acts upon view objects, also frame based if needed. This is a very prominent pattern in front-end these days, and i think even gaming would benefit from that.

1 Like

I’d say that’s completely on topic, thanks :slight_smile:

In ECS terms, what you are suggesting is per-component update rather than per-entity update, is that correct?

I don’t think it’s a problem for my use case though. As I mentioned above, my goal is to use this in small and light demos, and introductory tutorials, not large-scale apps. It should be possible to avoid race conditions without much trouble.

Is this also a problem for userData? Will this also change an object’s hidden class?

const o = new Object3D()

o.userData.x = 2;

Using primitives including objects constructed with {} should use a hashmap, but it depends on the assigned values, it shouldn’t be treated the same as an instance of a “class” as they aren’t always final as a class usually is. However i would generally always construct objects with their required fields whenever possible at least with default values.

Yeah, but in this case {} is a property of the class. So the question is, does the hidden class shape include all properties that are themselves compound data types, and will de-optimization of the entire class occur when adding properties to sub-objects?

If the answer is no, then I’m back to the situation where it might be better for me to create object.userData.update and accept that clone will ignore this.

Also, since the GLTFLoader adds properties to userData and users are encouraged to add new properties here at runtime, it would be useful for us to know whether this is ever a performance issue.

I have no idea how to go about testing this though… I think testing JS engine optimizations is usually done on the command line using special tools.

Objects assigned are references to these objects, so what happens inside userData isn’t relevant for the Object3D instance. This is why i don’t create a empty userData object per instance and rather set this property to null on the prototype by default.

There is no issue with adding and removing properties to hashmaps, we use them for lut and other high frequent changes too.

If you still might prefer going with classes extending Mesh etc, for example class Enemy extends Mesh, you could also just copy the mesh from gltf on your new instance of Enemy, you would have to clone the loaded asset anyway.

Adding properties at runtime like this below wouldn’t be good anyway, you would mix instances with properties occasionally undefined. (progressively adding new properties)

function damage( enemyMesh ) {

	if ( enemyMesh.userData.health === undefined )
		enemyMesh.userData.health = 1.0;
	
	enemyMesh.userData.health -= 0.1;

}

This can be easily avoided by either extending the class or at the point you make a clone of your asset:

const enemy = enemyAsset.clone();

enemy.userData = {
	health: 1
};

That’s probably not the kind of data you want to store but i hope it illustrates what i mean.

I wouldn’t worry about clone, rather about serialization since it’s supposed to store static data :cat:

1 Like

Ah, of course. Makes sense.

Does this cause problems with the GLTFLoader?

Yeah, I’m doing something similar in a couple of larger apps that I’m working on. However, I feel it’s too complicated for an introductory tutorial, hence the monkey patching.

I’m not a fan of monkey patching but I also think in certain situations, especially in simple apps, it’s so much simpler than the alternatives that it’s worth doing. As long as you do due diligence and don’t adopt it as a common pattern.

I’ll go with Object3D.prototype.tick in the first section of the book, with a caveat that it’s not considered best practices, and then add a small section later in the book to explore alternatives.

1 Like

I like this, since the Enemy extends Mesh it’s possible to just do:

myEnemy.geometry = myMesh.geometry
myEnemy.material = myMesh.material

I don’t think it’s that simple, because GLTFLoader returns a Scene which may contain many kinds of objects, not just meshes. It’s not too bad if you know in advance how you glTF files are structured though.