Calculate target quaternion without cheating with lookAt - three.js

How does one calculate the target quaternion (to be used later for slerp) for camera without stashing current quaternion and using lookAt?
This gives me the result (i.e. toQuaternion) I want:
// newTarget is Vector3
let fromQuaternion = (new THREE.Quaternion()).copy(this.camera.quaternion);
this.camera.lookAt(newTarget);
let toQuaternion = (new THREE.Quaternion()).copy(this.camera.quaternion);
this.camera.quaternion.set(fromQuaternion.x, fromQuaternion.y, fromQuaternion.z, fromQuaternion.w);
I'm trying to avoid the above with the following, but it's not working (getting much greater quaternion):
// currentTarget, newTarget are Vector3
var _c = (new THREE.Vector3()).copy(this.camera.position);
var _t0 = (new THREE.Vector3()).copy(currentTarget).sub(_c).normalize();
var _t1 = (new THREE.Vector3()).copy(newTarget).sub(_c).normalize();
var toQuaternion = new THREE.Quaternion();
toQuaternion.setFromUnitVectors(_t1, _t0);

Reversing the direction of the quaternion and initializing it to the current camera works:
var _c = (new THREE.Vector3()).copy(this.camera.position);
var _t0 = (new THREE.Vector3()).copy(currentTarget).sub(_c).normalize();
var _t1 = (new THREE.Vector3()).copy(this.orbitControls.target).sub(_c).normalize();
var toQuaternion = new THREE.Quaternion();
toQuaternion.setFromUnitVectors(_t0, _t1);
toQuaternion.multiply(this.camera.quaternion);

Related

THREE.js planeGeometry clipped inside the walls

I want to make a 3D building using Three.js. For example, I made 6 walls and a floor by checkerboard texture. I used clippingPlanes for wall1 and wall4:
floor1.material.clippingPlanes = [plane1,plane4];
I made my planes(plane1 and plane4) by my walls(wall1 and wall4). For example, my wall4 planeGeometry and plane4 code is here:
var wallGeometry4 = new THREE.PlaneGeometry(40, Floor_Height, 1, 1);
var wall4 = createMesh(wallGeometry4, "brick_diffuse.jpg", THREE.DoubleSide, 1024, 1024);
unit1.add(wall4);
wall4.position.x = -10;
wall4.position.y = 0;
wall4.position.z = -20;
wall4.rotation.y = 1.5 * Math.PI;
wall4.add(new THREE.EdgesHelper(wall4, 0x000000));
var plane4 = new THREE.Plane();
var normal4 = new THREE.Vector3();
var point4 = new THREE.Vector3();
normal4.set(0, 0, -1).applyQuaternion(wall4.quaternion);
point4.copy(wall4.position);
plane4.setFromNormalAndCoplanarPoint(normal4, point4);
But I see an empty area between wall5 and wall6, because plane4(that used for clipping the floor) isn't the same size of wall4. I think Plane4 is whole of the scene. How to change size of my plane to clip correctly? Or Is there any way to make floor bounded in walls?
One way to achieve this as suggested is to use ShapeGeometry.
When you are creating your walls you can save the x and z co-ordinate of their starting and ending points in an array to form a loop of points of Vector2. Then you can create a new custom shape from these points using shapeGeometry.
points = [{x:0,y:0},{x:0,y:10},{x:10,y:10},{x:10,y:0},{x:0,y:0}]
function getShapeFromPoints(points){
const shape = new THREE.Shape();
shape.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
}
return shape;
}
function createPlaneFromPoints(points) {
const planeMaterial = getPlaneMaterial();
const shape = getShapeFromPoints(points);
const geometry = new THREE.ShapeBufferGeometry(shape);
geometry.rotateX(degreeToRadians(-90));
const mesh = new THREE.Mesh(geometry, planeMaterial);
return mesh;
}
Hope that helps you!

Can't rotate an object on arbitrary axis in Threejs

I have a plan geometry with these coordinates:
var planVertices = new Float32Array(18);
//Vertice 1
planVertices[0] = -47.63179171506315;
planVertices[1] = 33.77709642112255;
planVertices[2] = -39.992833485603335;
//Vertice4
planVertices[3] = -47.63719374716282;
planVertices[4] =33.67262125968933;
planVertices[5] = -39.885335769653324;
//Vertice2
planVertices[6] = -46.49726260129362;
planVertices[7] = 33.71843400657177;
planVertices[8] = -39.992833485603335;
//Vertice4
planVertices[9] = -47.63719374716282;
planVertices[10] = 33.67262125968933;
planVertices[11] = -39.885335769653324;
//Vertice3
planVertices[12] = -46.50266463339329;
planVertices[13] = 33.61395884513855;
planVertices[14] = -39.885335769653324;
//Vertice2
planVertices[15] = -46.49726260129362;
planVertices[16] = 33.71843400657177;
planVertices[17] = -39.992833485603335;
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(planVertices,3));
var material = new THREE.MeshBasicMaterial({
color: 0x000000,
side: THREE.DoubleSide,
});
var segments = new THREE.Mesh(geometry, material);
scene.add(segments);
var p1 = new THREE.Vector3(planVertices[0],planVertices[1],planVertices[2]);
var p2 = new THREE.Vector3(planVertices[6],planVertices[7],planVertices[8]);
var axisLocal = new THREE.Vector3().subVectors(p2,p1).normalize;
segments.rotateOnAxis(axisLocal,0.707);
How can I rotate it from my axisLocal vector? This is just a part of my geometry, I have at least 80 independants plane that I want to rotate each one from its axisLocal.
[SOLVED]
I have centered my object into the origin, then perfom my rotation and re apply inverse translation.
//Center object to the origin
var matTrans = new THREE.Matrix4();
var matTransInv = new THREE.Matrix4();
segments.geometry.computeBoundingBox();
var center = new THREE.Vector3();
segments.geometry.boundingBox.getCenter(center);
center.negate();
matTrans.makeTranslation(center.x, center.y, center.z);
matTransInv.getInverse(matTrans);
segments.applyMatrix(matTrans);
//Rotate object at the origin from its axis
var matRot = new THREE.Matrix4();
matRot.makeRotationAxis(axis, -0.707);
segments.applyMatrix(matRot);
//Inverse translation
segments.applyMatrix(matTransInv);
meshes inherit from Object3D which has a .rotateOnAxis(axis,angle) method.

threejs moving one object moves the second too

I'm new at three.js and i need to load a particle object
it seems to work if add particles on vertices
if i load my Json once and inside the load() i build 2 different THREE.Points when i mov the first the second also move if i use 2 different load calls and inside each i build a THREE.Points object each object can be move separately
this i the function that i call inside animate
function animateParticles( particleSystem, particleSystemOriginal, deltaTime ) {
var vertsOriginal = particleSystemOriginal.geometry.vertices;
var verts = particleSystem.geometry.vertices;
for(var i = 0; i < verts.length; i++) {
var vertOriginal = vertsOriginal[i]; // original position
var vert = verts[i]; // cloud position
var vertOriginalY = vertOriginal.y;
var vertY = vert.y;
if (i==1) console.log("vertOriginalY " + vertOriginalY + " vertY " + vertY);
vert.y = vertY - (10 * deltaTime); // move
}
particleSystemOriginal.geometry.verticesNeedUpdate = true;
particleSystem.geometry.verticesNeedUpdate = true;
}
it seems strange that i need to load twice the same object to move one of them
in my function i'just testing and moving dove the cloud but what i would like is to shake particles, so i need to know particles original position and set particles new position
EDIT 1
i load my model like this
var loader = new THREE.JSONLoader();
loader.load('../3d-models/creati/mymodel-001.json', function(geometry, materials) {
var material = new THREE.MeshNormalMaterial();
var particleModelOriginal = new THREE.Mesh( geometry, material );
var particleModel = particleModelOriginal.clone();
/* build particle THREE.Points */
particleSystemOriginal = new THREE.Points(particlesOriginal, particleMaterial);
particleSystem = new THREE.Points(particles, particleMaterial);
//particleSystem = particleSystemOriginal.clone();
});
If I understand the question correctly, you probably need to use the .clone() object on mesh/geometry that you are loading if you want to create multiple objects. Otherwise they are referencing the same object, and will both be modified if one of them are modified

Attach text above 3D Object

I'm wondering if it is possible to attach a text above a 3D Object?
If so, how would I do it?
So far I'm doing the following below to load a mesh with its material and lastly adding it to a THREE.Object3D(); and adding it to the scene. Works great without any problems.
Next step is I want to show a nice text above its this object that is always fixed and can be seen from every angle.
loader.load('assets/' + enemyUrl, function (geometry, materials) {
material = new THREE.MeshFaceMaterial( materials );
model = new THREE.SkinnedMesh( geometry, material );
var mats = model.material.materials;
for (var i = 0,length = mats.length; i < length; i++) {
var m = mats[i];
m.skinning = true;
}
ensureLoop(geometry.animations[0]);
function ensureLoop( tmp ) {
for ( var i = 0; i < tmp.hierarchy.length; i ++ ) {
var bone = tmp.hierarchy[ i ];
var first = bone.keys[ 0 ];
var last = bone.keys[ bone.keys.length - 1 ];
last.pos = first.pos;
last.rot = first.rot;
last.scl = first.scl;
}
}
model.scale.set(2.5,2.5,2.5);
// TODO: Randomize where to put it in the world
yawObject.position.y = spawnPosition.y;
yawObject.position.x = spawnPosition.x;
yawObject.position.z = spawnPosition.z;
yawObject.add(model);
scene.add(yawObject);
});
Something like this:
This is what my game looks like now:
sure its possible. you can create a canvas with text on it, which you use as a texture on a plane that always looks at the camera, you could also do it with a single particle, but im not quite sure how it would work since particle materials have a size parameter. something like:
var name = 'Rovdjuret';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
ctx.font="20px Georgia";
ctx.fillText(name,10,50);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true; //just to make sure it's all up to date.
var label = new THREE.Mesh(new THREE.PlaneGeometry, new THREE.MeshBasicMaterial({map:texture}));
and inside the render/animation loop you make all the labels look at the camera:
label.lookAt(camera.position);

Tween camera position while rotation with slerp -- THREE.js

I want to tween camera position while rotation.
Here is my function:
function moveAndLookAt(camera, dstpos, dstlookat, options) {
options || (options = {duration: 300});
var origpos = new THREE.Vector3().copy(camera.position); // original position
var origrot = new THREE.Euler().copy(camera.rotation); // original rotation
camera.position.set(dstpos.x, dstpos.y, dstpos.z);
camera.lookAt(dstlookat);
var dstrot = new THREE.Euler().copy(camera.rotation)
// reset original position and rotation
camera.position.set(origpos.x, origpos.y, origpos.z);
camera.rotation.set(origrot.x, origrot.y, origrot.z);
//
// Tweening
//
// position
new TWEEN.Tween(camera.position).to({
x: dstpos.x,
y: dstpos.y,
z: dstpos.z
}, options.duration).start();;
// rotation (using slerp)
(function () {
var qa = camera.quaternion; // src quaternion
var qb = new THREE.Quaternion().setFromEuler(dstrot); // dst quaternion
var qm = new THREE.Quaternion();
var o = {t: 0};
new TWEEN.Tween(o).to({t: 1}, options.duration).onUpdate(function () {
THREE.Quaternion.slerp(qa, qb, qm, o.t);
camera.quaternion.set(qm.x, qm.y, qm.z, qm.w);
}).start();
}).call(this);
}
It works great: http://codepen.io/abernier/pen/zxzObm
The only problem is the tween for rotation does NOT seem to be linear... causing decay with position's tween (which is linear).
How can I turn slerp into a linear tween ?
Thank you
In your code
// rotation (using slerp)
(function () {
var qa = camera.quaternion; // src quaternion
Change it to
qa = new THREE.Quaternion().copy(camera.quaternion); // src quaternion
The way you do it, qa is the same as the camera quaternion, and it feeds back in the slerp calculus. It must be a constant variable.

Resources