Object3d lookAt and Vector3 apply Euler methods threejs example

For todays post on a threejs example I wanted to make a quick project that has to do with how the lookAt method of the object3d class is not always a kind of golden hammer kind of solution when it comes to setting the orientation of an object, or in this case a group of objects. For the most part that method works well, and is very easy to use, I just call the method off of the object that is based off of object3d, typically a camera, but it can be any other object3d based object, and pass the position that I want the object to look at. However in some cases the end result may not work the way that I want it to, so I can not just turn my brain off and not think about this kind of stuff at all some of the time.

This example then should help to show what I mean by this as it is a whole bunch of objects positioned around the origin and the are all made to look at the origin with the lookAt method. However two objects of interest might be at the top of the over all area where one plain is positioned one way and the other is flipped around completely. this is not always such a bad thing, but it is if what I want to do is make an animation in which this kind of object is of that of a plane that might make a motion like that of a loop.

This threejs example of the lookAt, and applyEuler methods in threejs and what to know first

This is one of my many posts on a threejs project example, this one has to do with using the object3d lookAt method and the Vector3 apply Eueler method. This is then not any kind of getting started with threejs kind of post, and I assume that you have at least a little experience working with threejs and client side javaScript in general. I will then not be getting into every little detail that you should know before hand in this section, but I will mention a few things you might want to read up more on before and if you have not done so thus far at this point.

There is reading more on the Object3d lookAt method and object3d class in general

I have wrote posts on the Object3d lookAt method as well as the object3d class in general. I use the look at method all the time in just about all of my source code examples when it comes to setting the rotation of a camera, however it can also be used with objects in general beyond just that of cameras. In this example I am using the lookAt method to set to orientation of mesh objects which like cameras are also based on the object3d class.

Read up more on the apply Euler method of Vector3 as well as Vector3 in general.

In this example I am not just using the look at method, but also the apply Euler method of the Vector3 class along with the Euler class to set the position of mesh objects. With that said I have wrote a post on the apply Euler method itself, as well as another on the Vector3 class in general. When it comes to using the apply Euler method I need to have a Vector3 instance with a value other than 0,0,0 and when calling the method I need to pass an instance of the Euler class which is yet even another class that is worth looking into further.

Version Numbers matter

When I first made this example I was using r135 of threejs.

Source code is on github

The source code for this example can be found in my test threejs repository, along with many others.

1 - The first version of this threejs example

For this example I have everything that I want packed into a single javaScript file, at least when it comes to this first revision to which I might expand on at a later point in the future when and if I get to it. Anyway I start out the file with the usual set of objects that I will want for any threejs project my creating the scene object, camera and renderer. After that I am adding a light source in the form of a directional light, after that I get into some helper functions that I am going to be using to create an update the collection of mesh objects for this. Finally I have an app loop but for now it is just a way to make to so that the orbit controls will work.

When it comes to the helpers section I have an array of materials that I will be using for each mesh object of each group that I will be using for the sake of having a kind of object to use for this. I have one material for the tail of each plane group, and another for all the other mesh objects that I am using. I then have a function that I can call that will create a single mesh object for an over all group of mesh objects. After that I have a make model helper that will crate a new instance of THREE.Group and add all the parts for the over all model.

There is then a create wrap method that will just create a collection fo these models objects, and then I have a helper that I am using to position a whole bunch of these mesh objects, for this example I have it set at about 50 of them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//******** **********
// SCENE, CAMERA, RENDERER
//******** **********
var scene = new THREE.Scene();
scene.background = new THREE.Color('#8a8a8a');
scene.add( new THREE.GridHelper(10, 10, 0x00ff00, 0xffffff) )
var camera = new THREE.PerspectiveCamera(60, 320 / 240, 0.1, 1000);
camera.position.set(10, 10, 10);
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(640, 480);
document.getElementById('demo').appendChild(renderer.domElement);
//******** **********
// LIGHT
//******** **********
var dl = new THREE.DirectionalLight(0xffffff, 1);
dl.position.set(1,3,2);
scene.add(dl);
//******** **********
// HELPER FUNCTIONS
//******** **********
// materuials to use for mesh objects
var materials = [
new THREE.MeshStandardMaterial({color: new THREE.Color('cyan')}),
new THREE.MeshStandardMaterial({color: new THREE.Color('red')})
];
// make a part of the object
var mkPart = function(g, partName, w, h, d, x, y, z, mi){
// the mesh object
var m = new THREE.Mesh(
new THREE.BoxGeometry(w, h, d),
materials[mi === undefined ? 0 : mi]);
// name of part
m.name = g.name + '_' + partName;
// position it
m.position.set(x, y, z);
return m;
};
// make the whole group with all parts
var mkModel = function(gName){
var g = new THREE.Group();
g.name = gName || 'g-' + g.uuid;
// add parts
g.add( mkPart(g, 'body', 1, 1, 4, 0, 0, 0) );
g.add( mkPart(g, 'tail', 0.5, 1, 1, 0, 1, -1.5, 1) );
g.add( mkPart(g, 'rwing', 2, 0.5, 1, -1.5, 0, 0) );
g.add( mkPart(g, 'lwing', 2, 0.5, 1, 1.5, 0, 0) );
return g;
};
// make a collection of them
var createWrap = function(){
var wrap = new THREE.Group();
var i = 0, count = 50;
while(i < count){
var g = mkModel('g' + i);
wrap.add( g );
i += 1;
}
wrap.scale.set(0.5, 0.5, 0.5);
return wrap;
};
// position a wrap object
var positionWrap = function(wrap, bias, ringCount){
bias = bias === undefined ? 1 : bias;
ringCount = ringCount === undefined ? 5 : ringCount;
var count = wrap.children.length,
i = 0;
perRing = count / ringCount,
radius = 15;
var yaStep = 90 / ringCount;
while(i < count){
var per = i / count;
var g = wrap.children[i];
var ring = Math.floor( i / perRing );
var rPer = ( i - perRing * ring) / perRing;
var x = Math.PI * 2 * rPer,
s = ring < ringCount / 2 ? 0 : 1;
y = Math.PI / 180 * yaStep * ring * bias,
z = 0;
var e = new THREE.Euler(x, y, z);
g.position.set(0, 0, radius).applyEuler( e );
g.lookAt(0, 0, 0);
i += 1;
}
};
//
var wrapA = createWrap();
positionWrap(wrapA, 1);
scene.add(wrapA);
var wrapB = createWrap();
positionWrap(wrapB, -1);
scene.add(wrapB);
//******** **********
// LOOP and ORBIT CONTROLS
//******** **********
new THREE.OrbitControls(camera, renderer.domElement);
var fps = 30,
lt = new Date(),
frame = 0,
maxFrame = 300;
var loop = function () {
var now = new Date(),
per = frame / maxFrame,
bias = 1 - Math.abs(0.5 - per) / 0.5,
secs = (now - lt) / 1000;
requestAnimationFrame(loop);
if(secs > 1 / fps){
positionWrap(wrapA, 1 - 1 * bias, 5);
positionWrap(wrapB, -1 + 1 * bias, 5 );
renderer.render(scene, camera);
frame += fps * secs;
frame %= maxFrame;
lt = now;
}
};
loop();

Conclusion

This example helped me get a good visual idea of what the deal is with the use of the look at method and how it sets rotation for objects. The result is that the lookAt method will work just fine for most situations in which I would want to use it, but there are a few use cases in which it will now work the way that I would want it to. For example using it to set the rotation of a biplane model that might do a loop or two in a scene, when it comes to that I will need to do a bit more than just use the lookAt method, for find some other solution completely actually.