I'm currently trying to tween-rotate a cube in 3D and thanks to this post (How to rotate a object on axis world three.js?) the rotation without tweening works without any problems. So currently I'm trying to transfer the rotation done by setFromRotationMatrix to something I can use as end rotation for my tween.
EDIT:
Here is what I have at the moment:
// function for rotation dice
function moveCube() {
// reset parent object rotation
pivot.rotation.set( 0, 0, 0 );
pivot.updateMatrixWorld();
// attach dice to pivot object
THREE.SceneUtils.attach( dice, scene, pivot );
// set variables for rotation direction
var rotateZ = -1;
var rotateX = -1;
if (targetRotationX < 0) {
rotateZ = 1;
} else if (targetRotationY < 0) {
rotateX = 1;
}
// check what drag direction was higher
if (Math.abs(targetRotationX) > Math.abs(targetRotationY)) {
// rotation
var newPosRotate = {z: rotateZ * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
//rotateAroundWorldAxis(dice, new THREE.Vector3(0, 0, rotateZ), Math.PI / 2);
} else {
// rotation
var newPosRotate = {x: -rotateX * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
//rotateAroundWorldAxis(dice, new THREE.Vector3(-rotateX, 0, 0), Math.PI / 2);
}
// detach dice from parent object
THREE.SceneUtils.detach( dice, pivot, scene );
}
Thanks to WestLangley I think I'm finally close to a solution that is easy to do and will serve my purpose. When initializing the pivot object I set it to the exact same position as the dice, so the rotation will still be around the center of the dice.
var loader = new THREE.JSONLoader();
loader.load(
'models/dice.json',
function ( geometry, materials ) {
material = new THREE.MeshFaceMaterial( materials );
dice = new THREE.Mesh( geometry, material );
dice.scale.set(1.95, 1.95, 1.95);
dice.position.set(2.88, 0.98, 0.96);
scene.add( dice );
pivot = new THREE.Object3D();
pivot.rotation.set( 0, 0, 0 );
pivot.position.set(dice.position.x, dice.position.y, dice.position.z);
scene.add( pivot );
}
);
The solution I have atm (upper snippet) does not attach the dice to the pivot object as parent. I'm probably overlooking something very basic ...
EDIT END
As I thought it was a really simple thing I had to do, to get it working:
I only needed to move the detachment of the child object (the dice) to the beginning of the function, instead of having it at the end of it and it works the charm.
Here's the working code:
// function for rotating dice
function moveCube() {
// detach dice from parent object first or attaching child object won't work as expected
THREE.SceneUtils.detach( dice, pivot, scene );
// reset parent object rotation
pivot.rotation.set( 0, 0, 0 );
pivot.updateMatrixWorld();
// attach dice to pivot object
THREE.SceneUtils.attach( dice, scene, pivot );
// set variables for rotation direction
var rotateZ = -1;
var rotateX = -1;
if (targetRotationX < 0) {
rotateZ = 1;
} else if (targetRotationY < 0) {
rotateX = 1;
}
// check what drag direction was higher
if (Math.abs(targetRotationX) > Math.abs(targetRotationY)) {
// rotation
var newPosRotate = {z: rotateZ * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
} else {
// rotation
var newPosRotate = {x: -rotateX * (Math.PI / 2)};
new TWEEN.Tween(pivot.rotation)
.to(newPosRotate, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
}
}
Thanks a lot for helping!
Related
I'm worked with Three.JS before, but not on meshes. I think I am approaching my problem the right way, but I'm not sure.
The Goal
I'm trying to make a 3D blobby object that has specific verticies. The direction of the verticies are fixed, but their radius from center varies. You can imagine it sort of like an audio equalizer, except radial and in 3D.
I'm open to scrapping this approach and taking a totally different one if there's some easier way to do this.
Current Progress
I took this example and cleaned/modified it to my needs. Here's the HTML and JavaScript:
HTML (disco-ball.html)
<!DOCTYPE html>
<html>
<head>
<title>Disco Ball</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/ConvexGeometry.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style type='text/css'>
/* set margin to 0 and overflow to hidden, to go fullscreen */
body { margin: 0; overflow: hidden; }
</style>
</head>
<body>
<div id="Stats-output"></div>
<div id="WebGL-output"></div>
<script type="text/javascript" src="01-app.js"></script>
</body>
</html>
And the JavaScript (01-app.js):
window.onload = init;
const PARAMS = {
SHOW_SURFACE : true,
SHOW_POINTS : true,
SHOW_WIREFRAME : true,
SHOW_STATS : true
};
// once everything is loaded, we run our Three.js stuff.
function init() {
var renderParams = {
webGLRenderer : createWebGLRenderer(),
step : 0,
rotationSpeed : 0.007,
scene : new THREE.Scene(),
camera : createCamera(),
};
// Create the actual points.
var points = getPoints(
100, // Number of points (approximate)
10, // Unweighted radius
// Radius weights for a few points. This is a multiplier.
[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2]
);
if (PARAMS.SHOW_STATS) {
renderParams.stats = initStats();
}
if (PARAMS.SHOW_SURFACE) {
renderParams.surface = getHullMesh(points);
renderParams.scene.add(renderParams.surface);
}
if (PARAMS.SHOW_POINTS) {
renderParams.sphereGroup = getSphereGroup(points);
renderParams.scene.add(sphereGroup);
}
render(renderParams);
}
function render(params) {
if (params.stats) {
params.stats.update();
}
if (params.sphereGroup) {
params.sphereGroup.rotation.y = params.step;
}
params.step += params.rotationSpeed;
if (params.surface) {
params.surface.rotation.y = params.step;
}
// render using requestAnimationFrame
requestAnimationFrame(function () {render(params)});
params.webGLRenderer.render(params.scene, params.camera);
}
// ******************************************************************
// Helper functions
// ******************************************************************
function getPoints (count, baseRadius, weightMap) {
// Because this is deterministic, we can pass in a weight map to adjust
// the radii.
var points = distributePoints(count,baseRadius,weightMap);
points.forEach((d,i) => {
points[i] = new THREE.Vector3(d[0],d[1],d[2]);
});
return points;
}
// A deterministic function for (approximately) evenly distributing n points
// over a sphere.
function distributePoints (count, radius, weightMap) {
// I'm not sure why I need this...
count *= 100;
var points = [];
var area = 4 * Math.PI * Math.pow(radius,2) / count;
var dist = Math.sqrt(area);
var Mtheta = Math.round(Math.PI / dist);
var distTheta = Math.PI / Mtheta
var distPhi = area / distTheta;
for (var m = 0; m < Mtheta; m++) {
let theta = (Math.PI * (m + 0.5)) / Mtheta;
let Mphi = Math.round((2 * Math.PI * Math.sin(theta)) / distPhi);
for (var n = 0; n < Mphi; n++) {
let phi = ((2 * Math.PI * n) / Mphi);
// Use the default radius, times any multiplier passed in through the
// weightMap. If no multiplier is present, use 1 to leave it
// unchanged.
points.push(createPoint(radius * (weightMap[points.length] || 1),theta,phi));
}
}
return points;
}
function createPoint (radius, theta, phi) {
var x = radius * Math.sin(theta) * Math.cos(phi);
var y = radius * Math.sin(theta) * Math.sin(phi);
var z = radius * Math.cos(theta);
return [Math.round(x), Math.round(y), Math.round(z)];
}
function createWebGLRenderer () {
// create a render and set the size
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
return webGLRenderer;
}
function createCamera () {
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 50;
camera.lookAt(new THREE.Vector3(0, 0, 0));
return camera;
}
function getSphereGroup (points) {
sphereGroup = new THREE.Object3D();
var material = new THREE.MeshBasicMaterial({color: 0xFF0000, transparent: false});
points.forEach(function (point) {
var spGeom = new THREE.SphereGeometry(0.2);
var spMesh = new THREE.Mesh(spGeom, material);
spMesh.position.copy(point);
sphereGroup.add(spMesh);
});
return sphereGroup;
}
function getHullMesh (points) {
// use the same points to create a convexgeometry
var surfaceGeometry = new THREE.ConvexGeometry(points);
var surface = createMesh(surfaceGeometry);
return surface;
}
function createMesh(geom) {
// assign two materials
var meshMaterial = new THREE.MeshBasicMaterial({color: 0x666666, transparent: true, opacity: 0.25});
meshMaterial.side = THREE.DoubleSide;
var wireFrameMat = new THREE.MeshBasicMaterial({color: 0x0000ff});
wireFrameMat.wireframe = PARAMS.SHOW_WIREFRAME;
// create a multimaterial
var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial, wireFrameMat]);
return mesh;
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
What I'm Missing
You can see that there are two points on the "ball" for which I've doubled the radius (big spikes). Of course, since I'm using a ConvexGeometry, the shape is convex... so a number of the points are hidden. What kind of ... non-convex geometry can I use to make those points no longer be hidden?
I would like to subdivide the mesh a bit so it's not simply vertex-to-vertex, but a bit smoother. How can I do that (the spikes less spikey and more blobby)?
I'd like to modify the mesh so different points spike different amounts every few seconds (I have some data arrays that describe how much). How do I modify the geometry after its been made? Ideally with some kind of tweening, but I can do without of that's extremely hard =)
Thanks!
Smooth and animate a mesh.
Three provides a huge range of options. These are just suggestions, your best bet is to read the Three documentation start point and find what suits you.
A mesh is just a set of 3D points and an array of indexes describing each triangle. Once you have built the mesh you only need to update the verts and let Three update the shader attributes, and the mesh normals
Your questions
Q1. Use Three.Geometry for the mesh.
Q2. As you are building the mesh you can use the curve helpers eg Three.CubicBezierCurve3 or Three.QuadraticBezierCurve3 or maybe your best option Three.SplineCurve
Another option is to use a modifier and create the simple mesh and then let Three subdivide the mesh for you. eg three example webgl modifier subdivision
Though not the fastest solution, if the vert count is low it will do this each frame without any loss of frame rate.
Q3. Using Three.Geometry you can can set the mesh morph targets, an array of vertices.
Another option is to use a modifier, eg three example webgl modifier subdivision
Or you can modify the vertices directly each frame.
for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
geometry.vertices[ i ].x = ?;
geometry.vertices[ i ].y = ?;
geometry.vertices[ i ].z = ?;
}
mesh.geometry.verticesNeedUpdate = true;
How you do it?
There are a zillion other ways to do this. Which is the best will depend on the load and amount of complexity you want to create. Spend some time and read the doc's, and experiment.
What I would do! maybe?
I am not too sure what you are trying to achieve but the following is a way of getting some life into the animation rather than the overdone curves that seem so ubiquitous these days.
So if the vert count is not too high I would use a Three.BufferGeometry and modify the verts each frame. Rather than use curves I would weight subdivision verts to follow a polynomial curve f(x) = x^2/(x^2 + (1-x)^2) where x is the normalized distance between two control verts (note don't use x=0.5 rather subdivide the mesh in > 2 times)
EG the two control points and two smoothing verts
// two control points
const p1 = {x,y,z};
const p2 = {x,y,z};
// two weighted points
// dx,dy,dz are deltas
// w is the weighted position s-curve
// wa, and wd are acceleration and drag coefficients. Try to keep their sum < 1
const pw1 = {x, y, z, dx, dy, dz, w : 1/3, wa : 0.1,wd : 0.7};
const pw2 = {x, y, z, dx, dy, dz, w : 2/3, wa : 0.1,wd : 0.7};
// Compute w
pw1.w = Math.pow(pw1.w,2) / ( Math.pow(pw1.w,2) + Math.pow(1 - pw1.w,2));
pw2.w = Math.pow(pw2.w,2) / ( Math.pow(pw2.w,2) + Math.pow(1 - pw2.w,2));
Then for each weighted point you can find the new delta and update the position
// do for x,y,z
x = (p2.x - p1.x); // these points are updated every frame
// get the new pw1 vert target position
x = p1.x + x * w;
// get new delta
pw1.dx += (x - pw1.x) * pw1.wa; // set delta
pw1.dx *= pw1.wd;
// set new position
pw1.x += pw1.dx;
Do for all weighted points then set geometry.vertices
The wa,wd coefficients will change the behaviour of the smoothing, you will have to play with these values to suit your own taste. Must be 0 <= (wa,wd) < 1 and the sum should be wa + wd < 1. High sumed values will result in oscillations, too high and the oscillations will be uncontrolled.
I'm using version 68 of three.js.
I would like to click somewhere and get the X, Y, and Z coordinates. I followed the steps here, but they give me a Z value of 0: Mouse / Canvas X, Y to Three.js World X, Y, Z
Basically, if I have a mesh in the scene, and I click in the middle of it, I'm hoping to be able to calculate the same values as the position of that mesh. This is just an example. I know I could use raycasting and see if I collided with a mesh and then just check its position. However, I want this to work even if I didn't click a mesh.
Is this possible? Here is a jsfiddle: http://jsfiddle.net/j9ydgyL3/
In that jsfiddle, if I could manage to click in the center of that square, I'm hoping to calculate 10, 10, 10 for the X, Y, and Z values respectively because those are the coordinates of the square's position. Here are the two functions of concern:
function getMousePosition(clientX, clientY) {
var mouse2D = new THREE.Vector3();
var mouse3D = new THREE.Vector3();
mouse2D.x = (clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(clientY / window.innerHeight) * 2 + 1;
mouse2D.z = 0.5;
mouse3D = projector.unprojectVector(mouse2D.clone(), camera);
return mouse3D;
//var vector = new THREE.Vector3(
//( clientX / window.innerWidth ) * 2 - 1,
//- ( clientY / window.innerHeight ) * 2 + 1,
//0.5 );
//projector.unprojectVector( vector, camera );
//var dir = vector.sub( camera.position ).normalize();
//var distance = - camera.position.z / dir.z;
//var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );
//return pos;
}
function onDocumentMouseUp(event) {
event.preventDefault();
var mouse3D = getMousePosition(event.clientX, event.clientY);
console.log(mouse3D.x + ' ' + mouse3D.y + ' ' + mouse3D.z);
}
I left some of the other code I tried commented out. Please note that this commented-out code didn't work in the jsfiddle website, maybe because they're still using version 54 of three.js. It works fine on my machine with version 68.
Edit: To clarify, I would like to be able to get the coordinates no matter where the mouse is. I just used a mesh in this example because it's easy to verify if it works by seeing if the calculated coordinates are the same as the mesh's. What I would really like is for it to work without using raycasting on a mesh. For example, we could have it always printing the calculated coordinates to the console every time the mouse moves, no matter what is in the scene.
You should use a THREE.Raycaster for this. When you set a list of intersectObjects you will be able to get an array of objects that intersected with the ray. So you can get the position from the 'clicked' object from returned list. Check the updated fiddle here.
I also changed your Three.js to version R68
For more advanced use of THREE.RayCaster check the examples at Threejs.org/examples like this example with interactive cubes.
Relevant code from the updated fiddle:
function getMousePosition(clientX, clientY) {
var mouse2D = new THREE.Vector3();
var mouse3D = new THREE.Vector3();
mouse2D.x = (clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(clientY / window.innerHeight) * 2 + 1;
mouse2D.z = 0.5;
mouse3D = projector.unprojectVector(mouse2D.clone(), camera);
return mouse3D;
var vector = new THREE.Vector3(
(clientX / window.innerWidth) * 2 - 1, -(clientY / window.innerHeight) * 2 + 1,
0.5);
projector.unprojectVector(vector, camera);
var dir = vector.sub(camera.position).normalize();
var distance = -camera.position.z / dir.z;
var pos = camera.position.clone().add(dir.multiplyScalar(distance));
return pos;
}
function onDocumentMouseUp(event) {
event.preventDefault();
var mouse3D = getMousePosition(event.clientX, event.clientY);
console.log(mouse3D.x + ' ' + mouse3D.y + ' ' + mouse3D.z);
var vector = new THREE.Vector3( mouse3D.x, mouse3D.y, 1 );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects(scene.children );
if(intersects.length > 0){
console.log(intersects[0].object.position);
}
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
I've got an exported animation from blender loaded into my scene. What I’m trying to achieve is to click the object, the object animated 50% the way through it's keyframes and stops, then if I click the object again the remaining 50% of keyframes are iterated over.
So far I have the object animating to 50%. The issue is that no intersection is found in the click eventhandler.
function mousedown( event )
{
event.preventDefault();
// update mouse object
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// find intersections
// create a reay with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
var ray = projector.pickingRay( vector.clone(), camera );
//Check for intersections
var intersects = ray.intersectObjects( targetList , true);
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
console.log(intersects);
//Start animating - animation is a global var in this scene triggering
//the animation.
animation = true;
}
else
{
console.log("No intersections");
}
}
This works when the object is first instantiated. It doesn't work after the object has 'been animated' though. After some debugging I can see that even after the object has moved (the first 50% of the animation has played through) the object's geometry's position = (0, 0, 0).
Which seems to make sense as to why the mousedown event isn't picking up an intersection, as in the 'world' the object isn't actually positioned where it seems to be on the screen.
Here's the code for loading the object:
jsonLoader.load("assets/models/cheese01.js", function(geometry){
geometry.computeMorphNormals();
geometry.computeVertexNormals();
// geometry.computeBoundingSphere();
// geometry.normalsNeedUpdate = true;
// verticesNeedUpdate = true;
var material = new THREE.MeshLambertMaterial({
color: 0xaa1100,
morphTargets: true,
morphNormals: true,
wrapAround: true
});
var mesh = new THREE.MorphAnimMesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.duration = 3000;
mesh.time = 0;
mesh.scale.set( 1, 1, 1 );
mesh.rotation.x = degToRad(175);
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
geometry.normalsNeedUpdate = true;
mesh.parseAnimations();
scene.add(mesh);
//Objects subject to intersection checks (from mouse click)
targetList.push(mesh);
//Objects to animate
morphs.push(mesh);
});
Cheers in advance for any help!
I have a problem to detect if my mouse is over a particle to push it away from the mouse
with a particlesystem in threejs
I use a raycaster but nothing hits.
I also try to add a hitbox to the particle but the hitbox doesn't follow the particle.
for (var p = 0; p < particleCount; p++) {
// create a particle with random
// position values, -250 -> 250
var pX = Math.random() * 200 - 200 / 2,
pY = Math.random() * 150-150/2,
pZ = -5,
particle = new THREE.Vector3(pX, pY, pZ);
// create a velocity vector
particle.velocity = new THREE.Vector3(
0, // x
Math.random()*maxVelocity, // y: random vel
0); // z
// add it to the geometry
// add hitbox on particle
var material = new THREE.MeshBasicMaterial({color : 0x45a314});
var circleGeometry = new THREE.CircleGeometry(maxDistance, 8);
particle.hitbox = new THREE.Mesh(circleGeometry,material);
particle.hitbox.position.set(pX,pY,pZ);
particles.vertices.push(particle);
}
the onmousemove function
function handleMouseMove(event) {
event.preventDefault();
mousePos.x = (event.clientX / window.innerWidth) * 2 - 1;
mousePos.y = -(event.clientY / window.innerHeight) * 2 + 1;
//controle
mouse_vector.set(mousePos.x,mousePos.y,mousePos.z);
projector.unprojectVector(mouse_vector,camera);
var direction = mouse_vector.sub(camera.position).normalize();
ray.set(camera.position, direction);
}
the update function
function update() {
var pCount = particleCount;
while(pCount --){
// get the particle
var particle = particles.vertices[pCount];
// check if we need to reset
if (particle.y > 80 ) {
particle.z = -4;
particle.x = Math.random() * 200 - 200 / 2;
particle.y = -75;
particle.velocity.y = Math.random()*maxVelocity;
// particle.velocity.x = Math.random()*0.4-0.2;
// particle.velocity.y = Math.random()*0.4-0.2;
}
intersects = ray.intersectObject(particle.hitbox);
if(intersects.length){
console.log("hit");
}
// and the position
particle.add(
particle.velocity);
}
// flag to the particle system
// that we've changed its vertices.
particleSystem.geometry.__dirtyVertices = true;
// draw
renderer.render(scene, camera);
// set up the next call
requestAnimationFrame(update);
}
a fiddle of my code
For your hitbox to function, make sure that you add it to the particle with particle.add(particle.hitbox). In that case, the hitbox position is fine as (0, 0, 0) because it will be added to the exact location of its parent object. When the hitbox is a child of the parent particle, it can also be referenced as such with particle.children.
Otherwise, as opposed to using a raycaster, you might try checking to see whether any particles fall within a certain range of the cursor's 2D position, and iterate through those within the bounds to push away. When needing to interact with multiple particles at once, that might be the easier option. Hope that helps!
Is it possible to do rotations taking axis of the world and not of the object?
I need to do some rotations of an object, but after the first rotation, I can't do other rotations like i want.
If it's not possible to do rotation on the axis of the world, my second option is to reset the axis after the first rotation. Is there some function for this?
I can't use object.eulerOrder because it changes the orientation of my object when I set object.eulerOrder="YZX" after some rotations.
UPDATED: THREE - 0.125.2
DEMO: codesandbox.io
const THREE = require("three");
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry(1, 1, 1, 4, 4, 4);
const material = new THREE.MeshBasicMaterial({
color: 0x628297,
wireframe: true
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// select the Z world axis
const myAxis = new THREE.Vector3(0, 0, 1);
// rotate the mesh 45 on this axis
cube.rotateOnWorldAxis(myAxis, THREE.Math.degToRad(45));
function animate() {
// rotate our object on its Y axis,
// but notice the cube has been transformed on world axis, so it will be tilted 45deg.
cube.rotation.y += 0.008;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
Here's a small variation. Tested with r56.
THREE.Object3D._matrixAux = new THREE.Matrix4(); // global auxiliar variable
// Warnings: 1) axis is assumed to be normalized.
// 2) matrix must be updated. If not, call object.updateMatrix() first
// 3) this assumes we are not using quaternions
THREE.Object3D.prototype.rotateAroundWorldAxis = function(axis, radians) {
THREE.Object3D._matrixAux.makeRotationAxis(axis, radians);
this.matrix.multiplyMatrices(THREE.Object3D._matrixAux,this.matrix); // r56
THREE.Object3D._matrixAux.extractRotation(this.matrix);
this.rotation.setEulerFromRotationMatrix(THREE.Object3D._matrixAux, this.eulerOrder );
this.position.getPositionFromMatrix( this.matrix );
}
THREE.Object3D.prototype.rotateAroundWorldAxisX = function(radians) {
this._vector.set(1,0,0);
this.rotateAroundWorldAxis(this._vector,radians);
}
THREE.Object3D.prototype.rotateAroundWorldAxisY = function(radians) {
this._vector.set(0,1,0);
this.rotateAroundWorldAxis(this._vector,radians);
}
THREE.Object3D.prototype. rotateAroundWorldAxisZ = function(degrees){
this._vector.set(0,0,1);
this.rotateAroundWorldAxis(this._vector,degrees);
}
The three last lines are just to resync the params (position,rotation) from the matrix... I wonder if there is a more efficient way to do that...
Somewhere around r59 this gets a lot easier (rotate around x):
bb.GraphicsEngine.prototype.calcRotation = function ( obj, rotationX)
{
var euler = new THREE.Euler( rotationX, 0, 0, 'XYZ' );
obj.position.applyEuler(euler);
}
Updated answer from #Neil (tested on r98)
function rotateAroundWorldAxis(obj, axis, radians) {
let rotWorldMatrix = new THREE.Matrix4();
rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rotWorldMatrix.multiply(obj.matrix);
obj.matrix = rotWorldMatrix;
obj.setRotationFromMatrix(obj.matrix);
}
#acarlon Your answer might just have ended a week of frustration. I've refined your function a bit. Here are my variations. I hope this saves someone else the 20+ hours I spent trying to figure this out.
function calcRotationAroundAxis( obj3D, axis, angle ){
var euler;
if ( axis === "x" ){
euler = new THREE.Euler( angle, 0, 0, 'XYZ' );
}
if ( axis === "y" ){
euler = new THREE.Euler( 0, angle, 0, 'XYZ' );
}
if ( axis === "z" ){
euler = new THREE.Euler( 0, 0, angle, 'XYZ' );
}
obj3D.position.applyEuler( euler );
}
function calcRotationIn3D( obj3D, angles, order = 'XYZ' ){
var euler;
euler = new THREE.Euler( angles.x, angles.y, angles.z, order );
obj3D.position.applyEuler( euler );
}
This works beautifully in r91.
Hope it helps.