Ok to store scene object instances as properties of scene? - javascript

This is a question about javascript and three.js coding style conventions. I prefer to use the latest ES-whatever conventions.
I'm wondering if instead of doing the usual:
var scene = new THREE.Scene();
var cube = new THREE.Mesh(new THREE.BoxGeometry(1,1,1), new THREE.MeshBasicMaterial(0xffffff))
scene.add(cube)
it would be ok to store the mesh object (and other objects, maybe lights and even camera) as properties of the scene object:
const scene = new THREE.Scene(); // or var, but that's not my question
scene.cube = THREE.Mesh(new THREE.BoxGeometry(1,1,1), new THREE.MeshBasicMaterial(0xffffff))
scene.add(scene.cube)
I like the idea of having references to all the three.js objects underneath the scene namespace -- makes it easier for me to access them later. I know I could use this with .name and .getObjectByName but that takes more code and seems messier to me.

There is a field on Object3D derived objects called .userData that you can store stuff that should get saved/serialized. But as far as storing props directly on objects.. It works.. but kinda has potential for problems if you end up overwriting something or later revs of three make use of your property name...
Edit: after reading the other posts here, they raise some good point, and I also wanted to throw out there that you can subclass the built-in three objects and make your own custom type that has your stuff. That might be tidier.

As far as separation of concern goes, you don't want to use Three's objects as data holders. It might seem like an easy way out, but will greatly reduce maintainability of your code. There is nothing preventing you from doing it today, though. Just consider, that you will have
scene.cube
scene.children[0] //same cube
scene.getObjectById(... cube id ...) //same cube
//... byName, ...byProperty etc. all pointing to the same cube
Remember, Scene extends Object3D with all its methods and properties, so, in the example scene below all objects are Object3D with each having children[] property.
[scene]
+----^-----------------------------+
[chairGroup] [light]
+--^--+-----+-----+-----+------+
[leg] [leg] [leg] [leg] [back] [seat]
Doesn't each node above look similar to DOM's Element?
I would encourage you to think about your scene as a tree. In fact, any UI is an n-tree of elements: web, mobile, X11 etc., and every UI framework is a tool to manipulate such tree. All approaches you use to manipulate DOM tree effectively work here.
Hence, below are various ways you can organize your code, from simple to more complex:
hello world rotating cube example is fine, 15-20 lines of code are ok as-is
rendering context: move scene, camera and renderer into some context object you can pass around your layers. Think of it as an equivalent of document in the browser.
high-level "Shadow DOM": organize a tree of your own components that each handle a group of 3d objects, make them react to events - external from UI clicks etc., or from Three, like visitor pattern during rendering. You can either keep references to 3d objects on these components, or recursively pass your structure to a function to adjust scene's hierarchy. Examples of such components in your tree could be Chair, Building, Planet, Starship etc.
data model: it might be tempting to store some data inside your components, but you should have a distinction between external data, usually a bit global, like numeberOfPlanets, timeOfDay etc., and internal data, like current rotation speed of a planet. Latter can be kept as part of your scene domain components.
full MVC: as with any UI, model-view-controller is applicable here. E.g. you can follow this intro into three.js MVC.
mediators, observers and usual workflows. See, e.g. my answer here.
... all the way to Redux-like immutable state management system
I hope, this answer will help people do some tactical architecture around Three.js that suits their project best.

Related

Animate CC - getChildByName canvas vs WebGL

Afternoon. I have an FLA with a single MovieClip on the stage - the clip is named myThing in the Library and also has an instance name of myThing. On another layer I have the following script:
this.myThing=this.getChildByName("myThing");
console.log(this.myThing);
When I run this in a WebGL project it works as I'd expect and returns a JS object but when I run the same thing in a canvas project (which is what I need to use) it comes back null.
Initially, can anyone explain what the difference is between a WebGL and a canvas project in Adobe Animate CC and how I can get a reference to child clips to control their timelines?
Along with that, can anyone point me to any decent resources on scripting these projects? It seems like no matter what I search for I always end up back at that *!#%£¡ rabbit tutorial that manages to cram very little info into an awful lot of words.
Thanks all, your help is always appreciated :)
So I was being a numpty.
The name property of any asset defaults to null. This is not a problem because the getChildByName() method is not really necessary (for me at least) once I realise that you can just call this.someChild.someMethod().
I got hooked up on the wrong approach because it was the only one I could find examples of. I'm still finding documentation very sketchy and not very helpful when compared to AS3 or even competing JS libraries like Greensock
Also not sure why my first approach worked in WebGL but not canvas. Ah well, onwards and upwards...
WebGL and HTML5 Canvas documents work somewhat differently in Animate CC.
In WebGL, symbols having instance names are accessible as follows:
var mySymbol = this.getChildByName("instance-name");
In Canvas, the same can be done as follows:
var mySymbol = this.instance-name;
Unnamed instances can be referenced using this.getChildAt(index) in both canvas and WebGL.
Once a reference to the required instance is obtained, you can easily control it as desired. (gotoAndPlay()/Stop() etc.)
PS: In canvas, Symbol-instance names are not explicitly set as name properties of corresponding symbols in the output - hence the name property is returned as null.

Moving object performance - ThreeJs

Context :
I'm working on a pretty simple THREE.JS project, and it is, I believe, optimized in a pretty good way.
I'm using a WebGLRenderer to display lot's of Bode plot extracted from an audio signal every 50ms. This is pretty cool, but obviously, the more Bode I display, the more laggy it is. In addition, Bodes are moving at constant speed, letting new ones some space to be displayed.
I'm now at a point where I implemented every "basic" optimization I found on Internet, and I managed to get a 30 fps constantly at about 10.000.000 lines displayed, with such a bad computer (nVidia GT 210 and Core i3 2100...).
Note also i'm not using any lights,reflections... Only basic lines =)
As it is a working project, i'm not allowed to show some screenshots/code, sorry ...
Current implementation :
I'm using an array to store all my Bodes, which are each displayed via a THREE.Line.
FYI, actually 2000 THREE.Line are used.
When a Bode has been displayed and moved for 40s, it is then deleted and the THREE.Line is re-used with another one. Note that to move these, I'm modifying THREE.Line.position property.
Note also that I already disabled my scene and object matrix autoUpdate, as I'm doing it manually. (Thx for pointing that Volune).
My Question :
Do the THREE.Line.position modification induces some heavy
calculations the renderer has already done ? Or is three.js aware that my object did not change and
avoid these ?
In other words, I'd like to know if rendering/updating the same object which was just translated is heavier in the rendering process than just leaving it alone, without updating his matrix etc...
Is there any sort of low-level optimization, either in ThreeJS about rendering the same objects many times ? Is this optimization cancelled when I move my object ?
If so, I've in mind an other way to do this : using only two big Mesh, which are folowing each other, but this induces merging/deleting parts of their geometries each frames... Might it be better ?
Thanks in advance.
I found in the sources (here and here) that the meshes matrices are updated each frame no matter the position changed or not.
This means that the position modification does not induce heavy calculation itself. This also means that a lot of matrices are updated and a lot of uniforms are sent to the GC each frame.
I would suggest trying your idea with one or two big meshes. This should reduce javascript computations internal to THREE.js, and the only big communications with the GC will be relative to the big buffers.
Also note that there exists a WebGL function bufferSubData (MSDN documentation) to update parts of a buffer, but it seems not yet usable in THREE.js

Is there a way to extend a ThreeJS object?

I'm using ThreeJS to create an interaction where people can click cubes. However these cubes behave differently when clicked (different color animations, to keep the idea simple).
My idea was to create extension classes of the THREE.Mesh object and add my custom functions and attributes. This would help isolate the different behaviors of the cubes and provide a cleaner code.
I tried using John Resigs' function to extend classes, but it seems to work just for classes that ultimately extend his "Class" class.
Is there a way to do this?
There are several ways to make class-based systems in Javascript. John Resig's "Class" is a great one, but it is not the type of inheritance that Three.js uses.
Notice in the Cube class file the line:
THREE.Geometry.call( this );
Javascript does not provide a built-in model for class inheritance, so unless you are using a library (like John Resig's) that bakes inheritance into class construction, you have to call the super method explicitly.
Your class would inherit from CubeGeometry if, inside your class, you call:
THREE.CubeGeometry.call( this );
You will also likely want to set CubeGeometry to be the prototype:
THREE.MyCubeGeometry.prototype = new THREE.CubeGeometry();
THREE.MyCubeGeometry.prototype.constructor = THREE.MyCubeGeometry;

Performance drops when trying to reset whole scene with Three.js

In my current project Im using Threejs for buildin a level with meshes. All the graphical stuff with camera, scene, projector, renderer and so on is done in one object. For test purposes I want to reset the whole scene with different parameters, for example different level sizes.
Because I want measure time of an algorithm I want a "full" reset. So my current approach is deleting the div-box containing the scene/canvas and deleting the whole object which has the threejs code. After this I instantiate a new object for the graphical level. Unfortunately doing this like 10 times in a row results in drastical performance loss.
I also tried deleting all meshes in the scene with scene.delete() and deleting things like scene, renderer and so on before deleting the whole object. But still performance issues.
So how can I achieve a whole reset of all graphical webgl components without performance loss?
Thanks in advance.
Deleting everything to do with three won't solve the problem, because even as your WebGLRenderer is deleted, it never releases it's WebGL context, so you end up with multiple WebGL contexts running simultaneously. Performance will degrade for each additional live context. Eventually, a limit will be hit.
Refer to this question for a hacked way to release the context.
Releasing the context is not supported by three.js since you really shouldn't need to recreate the context. In my case, using Angular with multiple application phases where some use WebGL and some don't, I simply persist an instance of the renderer in the page-level controller, such that I can access it in subcontrollers and so never need to recreate the WebGLRenderer nor, thus, the context.
Two functions that may increase your performance resetting: for each object obj in your scene, try both:
scene.remove( obj );
renderer.deallocateObject( obj );

How to properly use HTML5's canvas within JavaScript classes?

First off, I use the term "classes" to mean functions with prototypes that might be in a separate file from the main initializing file for what I'm working on.
Now for my question/issue:
I'm working on building something in JavaScript/HTML5, and trying to program it "properly" (ie. using prototypes with formats that are, I hope, standard). In my main JavaScript file, I have methods that have create use an instance (basically the root instance of my OOP/prototype based script) that sets up the canvas.
I also have another file that is loaded which contains a 'class' for creating clickable buttons. As of right now, I'm just trying to get the buttons to draw on the canvas, however, I can't access the instance of the canvas because I believe the instance is out of scope (which is fine, I don't want everything I do to contain a long dot-notation for referencing instances). However, this is giving me trouble when trying to draw a rectangle on the canvas, considering the 'class' the button is in, isn't able to reference the context outside of it.
I found a way around this by using something along the lines of:
function CreateButton(btn_x, btn_y, btn_width, btn_height, btn_txt) {
// ... (check and define parameters)
CreateButton.prototype.custom_canvas = document.getElementById('root_canvas');
CreateButton.prototype.ctxt = this.custom_canvas.getContext('2d');
CreateButton.prototype.ctxt.fillStyle = '#666666';
CreateButton.prototype.ctxt.fillRect(this.x, this.y, this.width, this.height);
}
Basically, it's writing on top of the canvas with a canvas of the same name? I'd assume that I can still manipulate the regular canvas afterwards and it would just act as if it was a single canvas element. I worried that redrawing on the canvas might use up a lot of memory when many things are added, however, no matter the method, writing on top of the canvas can't be avoided (even when in the same scope).
Any suggestions, or help? Or is my method of using the same canvas within a different class acceptable?
Thanks for any feedback.
[UPDATE]
Hmm, maybe I should try passing the context as a parameter and just using that.
...Or should I just make the canvas a global object? Any suggestions?
I guess you could try to implement some sort of "WidgetManager" that retains reference to canvas and your widgets. It will use a callback mechanism to render widgets. Each widget (ie. in this case Button) will have certain kind of rendering states (pressed, released) and some kind of internal state. Other widgets might have more complicated states.
Note that "WidgetManager" should probably keep track of widget "z" and user presses (which widget was hit). Based on this it should be able to trigger handlers bound to widgets. In a way you have to reinvent what basic UI libs do already. :)
I think you are better off by working out your design this way before moving into the implementation phase. A lot depends on what you really want to with it. :)
Note that you can simplify rendering and checks a lot by using multiple canvasii instead of just one. In this case you'll have to deal with z-index and absolute positioning but at least you get to piggyback on some of the existing stuff without having to implement it yourself.

Categories

Resources