There is an online 3d editor where you can edit individual meshes (move, scale, rotate). Ability to edit meshes implemented using custom transform controls which based on threejs's TransformControls code. This is fragment from mousemove event:
var intersect = intersectObjects(pointer, [xzPlane]); // intersect mouse's pointer with horizontal plane
var point = new THREE.Vector3();
point.copy(intersect.point);
point.sub(offset); // coords from mousedown event (from start stretching)
// some code for 'scale' value calculating base on 'point' variable
// var scale = ...;
//
mesh.scale.x = scale;
This code works well if the mesh does not rotate.
Requires scaling always happened to the world coordinate system. This is programming question
For example, from this:
To this:
P.S. I think that custom mesh matrix must be created, but I have very little experience with matrices
Thanks!
Instead of setting the rotation, like so:
mesh.rotation.set( Math.PI/4, 0, 0 );
apply the identical rotation to the geometry, instead:
var euler = new THREE.Euler( Math.PI / 4, 0, 0 );
mesh.geometry.applyMatrix( new THREE.Matrix4().makeRotationFromEuler( euler ) );
Now, you can set the scale and get the result you want.
mesh.scale.z = 2;
fiddle: http://jsfiddle.net/Tm7Ab/5/
three.js r.67
Related
Quick abstract: I don't know how to give to an object, attached to the camera, a local rotation so that it is rotated as if it is attached to the scene.
Background: I am the creator of this website which offers to players of the related game the ability to build levels on a 3D Builder, in a web browser. Everything work fine with it but some parts are not well optimized, specially the toolbar (or dashboard), where objects can be selected to be added to the main scene.
Those toolbar objects need to be sticked at the bottom of the screen and always above all layers but their rotation has to be relative to the main axis of the scene. That's where I face some weird behaviors on browsers other than Firefox and Chrome.
If first thought this other thread was saving my life on this matter by implementing the Object3D.onBeforeRender function, but I faced other issues.
Currently those objects are attached to the scene and their position is updated relatively to the camera with the onBeforeRender function of each object. On such way I noticed some flickering results with Edge and iPhone browser, despite everything ends up fine in the rendering. Not nice on the scene rotation...
So I tried something else by attaching each object to the camera and giving them their supposed world rotation. Now when testing on Edge there is no flickering and the results is much better but I can't get how to give them the same rotation than when they are attached to the scene.
Here is a visual:
and an animated gif (you can see a little how the red background and cube at the right part are moving/flickering, but it is awfull if done quicklier):
For the #1 (at the right) there's a good rendering but a bad experience on some browsers and for the #2 (at the left) the rotation, based on the camera, is not good but the experience is fine.
Here are some code abstracts I've implemented:
For #1:
rightCube.relPos = new THREE.Vector3(500, 0, 0); // a new property to be used
rightCube.onBeforeRender = function( renderer, scene, camera, geometry, material, group ) {
camReference.position.copy(this.relPos);
var pos = camReference.getWorldPosition();
this.position.set( pos.x, pos.y, pos.z );
};
scene.add(rightCube);
And camReference is a simple Object3D representing the camera origin
For the #2:
leftCube.onBeforeRender = function( renderer, scene, camera, geometry, material, group ) {
this.rotation.copy(camera.getWorldRotation());
};
camera.add(leftCube);
I admit on this last part, that getting the camera world rotation is not sufficient but that's where I'm stuck and need some help: I don't know how to give to an object, attached to the camera, a local rotation so that it is rotated as if it is attached to the scene.
This post is pretty long but I hope my problem can be well understood... and that someone will give me the key to make it work.
Three.js r82
I've found out how to deal with that problem. I had to go into some calculus and I am surprised that it is not yet implemented in Three.js. If I have missed something #WestLangley please let me know!
Basically my request is to have a rotateToWorld functionnality from the camera coordinate. Getting the rotation of the camera is not enought because of the orbit control position offset which could be different than the Scene Axis. From the camera coordinate, rotating to the World can be seen as a rotation around x then y (Euler rotations), z being directed to the viewer. Here is a figure I tried to draw as a better explanation:
Here is the function I have coded to do that:
var rotateToWorld = function (camera, controls) {
var p = camera.position.clone();
var o = controls.target.clone();
// position relative to the control
p.sub(o);
var Dxyz = Math.sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
var Dxz = Math.sqrt(p.x*p.x + p.z*p.z);
return {
x: Math.acos(Dxz/Dxyz) * (p.y>0?1:-1), // Angle Rotation on x axis camera
y: Math.acos(p.z/Dxz) * (p.x>0?-1:1), // Angle Rotation on y axis camera
z: 0 // No rotation around z axis
};
};
Then from a cube object attached to the camera (not to the scene) it can be rendered like that:
cube.onBeforeRender = function( renderer, scene, camera, geometry, material, group ) {
var R = rotateToWorld(camera, controls); // controls defined elsewhere in the code
this.rotation.set(R.x, R.y, 0);
};
As a result this is what I got with a perspective camera:
And now it works with Microsoft Edge, Safari and other browsers despite when attaching to the scene and updating the position of the object (to keep it in front of the camera) it was only workable with Firefox and Chrome (as for today).
I wonder now if this is a functionnality that could be added to Three.js...
EDIT:
#WestLangley pointed out in comments that the clone() function of Vector3 instantiate a new object each time it is called, so better is to use a closure function to initialize once p and o as Vector3 objects then update them with the copy() function.
So here is a better version:
var rotateToWorld = function() {
var p = new THREE.Vector3();
var o = new THREE.Vector3();
return function rotateToWorld(camera, controls) {
p.copy(camera.position);
o.copy(controls.target);
// position relative to the control
p.sub(o);
var Dxyz = Math.sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
var Dxz = Math.sqrt(p.x*p.x + p.z*p.z);
return {
x: Math.acos(Dxz/Dxyz) * (p.y>0?1:-1), // Angle Rotation on X axis camera
y: Math.acos(p.z/Dxz) * (p.x>0?-1:1), // Angle Rotation on Y axis camera
z: 0 // No rotation around z axis
};
}
}();
Again, thanks #WestLangley! :)
In ThreeJS, if the camera is rotated at any given angle, how can I determine the coordinate of the point exactly 1 unit in the direction that the camera is facing.
If you are unfamiliar with ThreeJS camera rotation angles, the rotations are very very weird.
For example, if all of my rotation variable are 0, then the function would return (0, 0, -1).
If the rotation x is Pi / 2 (90 degrees), then the function would return (1, 0, 0)
How could I create a function to do this? I don't need everything, primarily just the math.
You want to get the world position of a point that is one unit in front of the camera.
One way to do that is to add an object in front of, and as a child of, the camera. Then you just need to query the world position of the object.
By default, when a camera's rotation is ( 0, 0, 0 ), the camera is looking down its negative z-axis. So here is the pattern to follow:
var object = new THREE.Object3D();
object.position.set( 0, 0, - 1 );
scene.add( camera ); // this is required when the camera has children
camera.add( object );
Now, you can get the point you need like so:
var worldPos = new THREE.Vector3(); // create once and reuse it
...
camera.updateMatrixWorld(); // this is called in the render loop, so you may not have to call it again. Experiment.
worldPos.setFromMatrixPosition( object.matrixWorld );
three.js r.71
Just did an update from r67 - r69 in ThreeJS and ends up having problems referring their positions to one (same) vector.
Before I did this which worked:
var vector = new THREE.Vector3(50, 50, 50);
_Mesh1.position = vector;
_Mesh2.position = vector;
which made it possible that when I moved one of the meshes it moved the other one as well.
In r69 the position vector remains the same (aka 0, 0, 0) which means that I have to manually set the X, Y and Z coords for each mesh whenever I mode another one.
Am I missing some change here? Or what should I do to fix this?
Object3D's position, rotation, quaternion and scale properties are now immutable.
See the source code file Object3D.js.
You can no longer use the following pattern:
object.position = vector;
Instead, you must use either
object.position.set( x, y, z );
or
object.position.copy( vector );
three.js r.69
I did vector stuff for already 13 years now, but am still struggling with the way three.js handles things. So what I like to do is best described with a fan that always faces the camera and, of course, rotates.
This is how I achieved this in another language:
// Create a vector facing in camera direction.
temp.x = 0;
temp.y = 1;
temp.z = 0;
vec_rotate(temp, camera.pan);
// Apply vector direction to scene object.
vec_to_angle(entity.pan, temp);
// Rotate scene object's angle around another angle / imaginary line from camera to object.
ang_rotate(
entity.pan,
vector(random(360), 0, 0)
);
Thus, after applying entity.lookAt(camera.position) I am missing an angle rotation based on the current angle (last function call of the example).
One way to model a fan is like so:
var fan = new THREE.Mesh( ... );
var pivot = new THREE.Object3D();
scene.add( pivot );
pivot.lookAt( camera.position );
pivot.add( fan );
Then in the animation loop (assuming the fan mesh by default faces the positive-z axis),
fan.rotation.z += 0.01;
three.js r.68
I have a camera that moves in a few different ways in the scene. The camera should rotate around a target position. In my case, this is a point on a mesh that the user has targeted. Because the camera usually doesn't require moving relative to this point, I was not able to use the pivot idea here: https://github.com/mrdoob/three.js/issues/1830. My current solution uses the following code:
var rotationY = new THREE.Matrix4();
var rotationX = new THREE.Matrix4();
var translation = new THREE.Matrix4();
var translationInverse = new THREE.Matrix4();
var matrix = new THREE.Matrix4();
function rotateCameraAroundObject(dx, dy, target) {
// collect up and right vectors from camera perspective
camComponents.up = rotateVectorForObject(new THREE.Vector3(0,1,0), camera.matrixWorld);
camComponents.right = rotateVectorForObject(new THREE.Vector3(1,0,0), camera.matrixWorld);
matrix.identity();
rotationX.makeRotationAxis(camComponents.right, -dx);
rotationY.makeRotationAxis(camComponents.up, -dy);
translation.makeTranslation(
target.position.x - camera.position.x,
target.position.y - camera.position.y,
target.position.z - camera.position.z);
translationInverse.getInverse(translation);
matrix.multiply(translation).multiply(rotationY).multiply(rotationX).multiply(translationInverse);
camera.applyMatrix(matrix);
camera.lookAt(target.position);
}
The issue is that we do not want to use lookAt, because of the reorientation. We want to be able to remove that line.
If we use the code above without lookAt, we rotate around the point but we do not look at the point. My understanding is that my method should rotate the camera's view as much as the camera itself is rotate, but instead the camera is rotated only a small amount. Could anyone help me understand what's wrong?
EDIT: Cleaned up the original post and code to hopefully clarify my question.
My thinking is that I can translate to the origin (my target position), rotate my desired amount, and then translate back to the beginning position. Because of the rotation, I expect to be in a new position looking at the origin.
In fact, I'm testing it now without the translation matrices being used, so the matrix multiplication line is:
matrix.multiply(rotationY).multiply(rotationX);
and it seems to be behaving the same. Thanks for all the help so far!
ONE MORE THING! A part of the problem is that when the camera behaves badly close to the north or south poles. I am looking for a 'free roaming' sort of feel.
Put the following in your render loop:
camera.position.x = target.position.x + radius * Math.cos( constant * elapsedTime );
camera.position.z = target.position.z + radius * Math.sin( constant * elapsedTime );
camera.lookAt( target.position );
renderer.render( scene, camera );
Alternatively, you can use THREE.OrbitControls or THREE.TrackballControls. See the three.js examples.
The Gimbal lock that you are referring to (reorientation) is because of the use of Euler angles in the default implementation of the camera lookat. If you set
camera.useQuaternion = true;
before your call to lookat, then euler angles will not be used. Would this solve your problem ?