I'm trying to animate a three.js block in such a way that it returns to its original position when the animation ends, using tween.js.
Is there a way to achieve this with tween.js only using one tween?
I have got this working as shown below:
var position = {x: -200, y: 150, width: 1, height: 1, depth: 1, rotx: -0.5, roty: 0.7, rotz: 0.9};
var target = {x: 200, y: -100, width: 0.4, height: 3, depth: 8, rotx: 0.3, roty: -0.4, rotz: -0.6};
var position2 = {x: -200, y: 150, width: 1, height: 1, depth: 1, rotx: -0.5, roty: 0.7, rotz: 0.9};
var mesh = new THREE.Mesh(
new THREE.CubeGeometry(190, 45, 30),
new THREE.MeshBasicMaterial({color: 0x444444}),
0
);
mesh.position.set(position.x, position.y, 0);
mesh.rotation.set(position.rotx, position.roty, position.rotz);
scene.add(mesh);
var t1 = new TWEEN.Tween(position).to(target, 2000);
t1.onUpdate(function() {
mesh.position.set(position.x, position.y, 0);
mesh.scale.set(position.width, position.height, position.depth);
mesh.rotation.set(position.rotx, position.roty, position.rotz);
});
t1.easing(TWEEN.Easing.Quadratic.Out);
t1.onComplete(function() {t2.start();});
var t2 = new TWEEN.Tween(target).to(position2, 2000);
t2.onUpdate(function() {
mesh.position.set(target.x, target.y, 0);
mesh.scale.set(target.width, target.height, target.depth);
mesh.rotation.set(target.rotx, target.roty, target.rotz);
});
t2.easing(TWEEN.Easing.Quadratic.In);
t1.start();
And I have the tweens updating in my animation function:
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
mesh.__dirtyPosition = true;
mesh.__dirtyRotation = true;
TWEEN.update();
}
animate();
This is working as I expect it to, but it is clearly very inefficient, and difficult to work around.
Any and all help will be appreciated.
You're overcomplicating things a bit by re-naming the x, y, z properties to width, height, depth or rotx, roty, rotz. This only means you have to manually translate these properties onUpdate when you do scale.x = position.width and rotation.x = position.rotx. I recommend you keep x, y, z, to avoid these repetitive assignments.
// We set our start and target pos using the THREE.js "x, y, z" nomenclature
var startPos = {x: -200, y: 150, z: 0};
var targetPos = {x: 200, y: -100, z: 0};
// Scale also is defined in "x, y, z"
var startScale = {x: 1, y: 1, z: 1};
var targetScale = {x: 0.4, y: 3, z: 8};
// Rotation also has "x, y, z" degrees in Euler angles
var startRot = {x: -0.5, y: 0.7, z: 0.9};
var targetRot = {x: 0.3, y: -0.4, z: -0.6};
// Standard mesh setup
var mesh = new THREE.Mesh(
new THREE.CubeGeometry(190, 45, 30),
new THREE.MeshBasicMaterial({color: 0x444444})
);
mesh.position.copy(startPos);
mesh.rotation.copy(startRot);
scene.add(mesh);
// Create shortcuts for shorter easing names
var QuadOut = TWEEN.Easing.Quadratic.Out;
var QuadIn = TWEEN.Easing.Quadratic.In;
// Create one tween for position
// Notice that you can chain the animation
// back to startPos by doing double ".to().to()""
var t1 = new TWEEN.Tween(mesh.position)
.to(targetPos, 2000, QuadOut)
.to(startPos, 2000, QuadIn);
// Second, we tween the mesh's rotation
var t2 = new TWEEN.Tween(mesh.rotation)
.to(targetRot, 2000, QuadOut)
.to(startRot, 2000, QuadIn);
// Third, we tween the mesh's scale
var t3 = new TWEEN.Tween(mesh.scale)
.to(targetScale, 2000, QuadOut)
.to(startScale, 2000, QuadIn);
t1.start();
t2.start();
t3.start();
And finally, during animate(), you no longer have to change __dirtyPosition or anything, because the tween is updating the mesh's properties directly.
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
renderer.render(scene, camera);
}
animate();
Related
I have a problem with bounding box points calculation. I'm using three.js to render polygons, it's basically 2D with orthographic camera. Unfortunately, simple bounding box calculation - iterate over points and get extreme values doesn't work correctly after camera is rotated. It stays aligned to axes. I'd like bounding box to be aligned to a viewport (just like in the picture below). It can be rotated by any angle, has to be always aligned to a viewport.
I added an example below - how to calculate points of the bounding box on the right?
Image description: left - trivial bounding box without rotation, middle - axis aligned bounding box, right - desired result - viewport aligned bounding box
Fiddle producing middle case: https://jsfiddle.net/tqrc2ue6/5/
var camera, scene, renderer, geometry, material, line, axesHelper, boundingBoxGeometry, boundingBoxLine;
const polygonPoints = [{
x: 10,
y: 10,
z: 0
},
{
x: 15,
y: 15,
z: 0
},
{
x: 20,
y: 10,
z: 0
},
{
x: 25,
y: 20,
z: 0
},
{
x: 15,
y: 20,
z: 0
},
{
x: 10,
y: 10,
z: 0
},
]
function getBoundingBoxGeometry(geometry) {
geometry.computeBoundingBox();
const boundingBox = geometry.boundingBox;
const boundingBoxPoints = [{
x: boundingBox.min.x,
y: boundingBox.min.y,
z: 0
},
{
x: boundingBox.max.x,
y: boundingBox.min.y,
z: 0
},
{
x: boundingBox.max.x,
y: boundingBox.max.y,
z: 0
},
{
x: boundingBox.min.x,
y: boundingBox.max.y,
z: 0
},
{
x: boundingBox.min.x,
y: boundingBox.min.y,
z: 0
},
];
return new THREE.BufferGeometry().setFromPoints(boundingBoxPoints);
}
init();
animate();
function init() {
scene = new THREE.Scene();
axesHelper = new THREE.AxesHelper(10);
scene.add(axesHelper);
//camera = new THREE.OrthographicCamera(-25, 25, -25, 25, -1, 1);
//camera.position.set(15, 15)
//camera.rotation.z = -Math.PI / 4
var frustumSize = 50
var aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera(frustumSize * aspect / -2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, -1, 1);
//camera.rotation.z = 2 * Math.PI /3
camera.rotation.z = 3 * Math.PI / 4
camera.position.set(15, 15)
scene.add(camera);
geometry = new THREE.BufferGeometry().setFromPoints(polygonPoints);
boundingBoxGeometry = getBoundingBoxGeometry(geometry);
material = new THREE.LineBasicMaterial({
color: 0xffffff
});
line = new THREE.Line(geometry, material);
scene.add(line);
boundingBoxLine = new THREE.Line(boundingBoxGeometry, material)
scene.add(boundingBoxLine);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
The left figure is obtained by computing the bounding box of the original coordinates.
The right figure is obtained by computing the bounding box of the rotated coordinates.
The central figure is obtained by computing the bounding box in the original coordinates and applying the rotation to the corners. It is no more axis aligned wrt the original coordinates.
Yves explained the concept. You need to convert points from one coordinate system to another to solve this. But since it's an orthographic view, you can also use camera projection for conversions.
In this way, we project all the points to the screen coordinate system, we calculate the position of the box in this coordinate, and then we unproject the points of the box to the world coordinate system.
I updated your sample to demonstrate this. Just keep in mind that width and height of this rectangle are valid only if view direction be parallel to one of the X, Y or Z axes.
I'am trying to clip the FabricJS rect shape to the polygon shape. The clipping works okay until the polygon shape which need to be clipped is now scaled. After this there is some weird offset that is caused by the polygon clipping.
Can anyone help me how can i fix the function to prevent the polygon offset issue when clip object is scaled.
This is how it looks before scalling. The clipping works fine
Image => https://i.imgur.com/Eop2YJh.png
And then there is the problem when the polygon is scaled.
2: Image => https://i.imgur.com/ICkP8SG.png
Here is the code on fiddle with the clipping function
https://jsfiddle.net/0xpvc9uq/
So if there is anyone who knows whats the point and how can I fix it I would appriciate it.
Thx
var canvas = new fabric.Canvas('c');
var rect1 = new fabric.Rect({
left: 0, top: 0,
width: 900, height: 900,
fill: 'blue',
selectable: false,
clipTo: clipRegion,
scaleX: 1.5,
scaleY: 1.5
});
var clipPoly = new fabric.Polygon([
{ x: 180, y: 10 },
{ x: 300, y: 50 },
{ x: 300, y: 180 },
{ x: 180, y: 220 }
], {
originX: 'left',
originY: 'top',
left: 180,
top: 10,
fill: 'transparent', /* use transparent for no fill */
strokeWidth: 0,
selectable: false,
strokeWidth: 1,
stroke: "red",
scaleX: 1.3,
scaleY: 1.3
});
canvas.add(rect1, clipPoly);
function clipRegion (ctx) {
rect1.setCoords();
const clipObj = clipPoly;
const scaleXTo1 = (1 / rect1.scaleX);
const scaleYTo1 = (1 / rect1.scaleY);
ctx.save();
const ctxLeft = -( rect1.width / 2 ) - clipObj.strokeWidth - rect1.strokeWidth;
const ctxTop = -( rect1.height / 2 ) - clipObj.strokeWidth - rect1.strokeWidth;
ctx.translate( ctxLeft, ctxTop );
ctx.scale(scaleXTo1, scaleYTo1);
ctx.rotate((rect1.angle * -1) * (Math.PI / 180));
ctx.beginPath();
const matrix = clipPoly.calcTransformMatrix();
let points = [];
clipObj.points.forEach( (point) => {
points.push({
x: ((point.x * matrix[0]) + (clipObj.strokeWidth * clipObj.scaleX)) - rect1.oCoords.tl.x,
y: ((point.y * matrix[3]) + (clipObj.strokeWidth * clipObj.scaleY)) - rect1.oCoords.tl.y
});
});
ctx.moveTo(points[0].x, points[0].y);
points.forEach((point) => {
ctx.lineTo(point.x, point.y);
});
ctx.lineTo(points[0].x, points[0].y);
ctx.closePath();
ctx.restore();
}
I discovered that there is also another approach that can be used and that solves all the problem.
The clipRegion function now looks like:
function clipRegion (ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clipPoly.render(ctx);
ctx.restore();
}
Which makes the rendering okay. If still anyone have other way to fix the above problem, I would like to see the answer
three.js r89
var A = new THREE.Object3D();
var B = new THREE.Object3D();
scene.add(A);
A.add(B);
A.position.set(0, 0, -2);
var bPos = B.getWorldPosition();
bPos is still Vector3 {x: 0, y: 0, z: 0}. Why? I thought it should be Vector3 {x: 0, y: 0, z: -2} (B moved with A).
Proof:
I have a ball and two straight vertical surfaces in my world.
When I apply a force to the ball I expect it to stay in a straight line, however it appears to bounce off at an angle.
fiddle: https://jsfiddle.net/zvjvvzeL/11/
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies,
Body = Matter.Body,
Vector = Matter.Vector,
Events = Matter.Events;
// create an engine
var engine = Engine.create();
var canvas = document.getElementById('canvas');
engine.world.gravity.y = 0; // gravity not needed in this app
// create a renderer
var render = Render.create({
element: document.body,
canvas: canvas,
engine: engine,
options: {wireframes: true}
});
var ball_0 = Bodies.circle(100, 150, 11, {
density: 0.04,
frictionAir: 0.06,
restitution: 0.8,
friction: 0.3
});
var cushion_left = Bodies.rectangle(34, 160, 100, 208, { isStatic: true });
var cushion_right = Bodies.rectangle(492, 160, 100, 208, { isStatic: true });
// add all of the bodies to the world
World.add(engine.world, [cushion_left, cushion_right, ball_0]);
render.options.height = 300;
canvas.height = 300;
Engine.run(engine);
Render.run(render);
Body.applyForce(ball_0, { x: 0, y: 0 }, { x: 0.5, y: 0 });
Not too familiar with MatterJS, but it seems like the ball has angular rotation applied by default. I think this only affects a closed system like the one you've built.
Maybe you'll want it on in the long run, but for now you can set intertia : Infinity
var ball_0 = Bodies.circle(100, 150, 11, {
density: 0.04,
frictionAir: 0.06,
restitution: 0.8,
friction: 0.3,
inertia : Infinity
});
But now you also have to apply slightly more force to get the ball to touch the wall. I just turned it up to .6
Body.applyForce(ball_0, { x: 0, y: 0 }, { x: .6, y: 0 });
My scene has a cube and a directional light with your Helper.
The cube is placed on X: 300 Y: 100 z: 0 and the light is positioned at X: 300 Y: 600 z: 0.
The Target object of light has the same position of the cube.
But the target of the helper is being drawn at (0, 0, 0).
How do I make drawing Helper point to the target light?
This is my code:
var geometry = new THREE.CubeGeometry(200, 200, 200);
var material = new THREE.MeshPhongMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material);
cube.position.set(300, 100, 0);
scene.add(cube);
var color = 0xffffff;
var intensity = 1;
var light = new THREE.DirectionalLight( color, intensity );
light.position.set(300, 600, 0);
light.target.position.set(300, 100, 0);
scene.add( light );
var helper = new THREE.DirectionalLightHelper( light, 20 );
scene.add( helper );
helper.update();