how to THREE.Sprite change pivot point? - three.js

I want to set rotate transform-origin to the left of sprite, not center, when towards the camera and keep the angle to horizontal.
// 2d
var texture = new THREE.Texture(canvas)
texture.needsUpdate = true
var material = new THREE.SpriteMaterial({
map: texture,
transparent: true,
useScreenCoordinates: false,
})
var actualSize = 10 / size.h
var w = size.w / size.h * actualSize
var h = actualSize
var mesh = new THREE.Sprite(material)
mesh.scale.set(w, h, 1)

Related

In three.js how to position image texture similar to 'contain' in css?

My image texture is positioned relative to the center of 3d space instead of mesh and I don't quite understand what determines its size.
Here is example showing how the same image is positioned on different meshes:
https://imgur.com/glHE97L
I'd like the image be in the center of the mesh and it's size set similar as 'contain' in css.
The mesh is flat plane created using ShapeBufferGeometry:
const shape = new THREE.Shape( edgePoints );
const geometry = new THREE.ShapeBufferGeometry( shape );
To see any image I have to set:
texture.repeat.set(0.001, 0.001);
Not sure if that matters but after creating the mesh I than set its position and rotation:
mesh.position.copy( position[0] );
mesh.rotation.set( rotation[0], rotation[1], rotation[2] );
I've tried setting those:
mesh.updateMatrixWorld( true );
mesh.geometry.computeBoundingSphere();
mesh.geometry.verticesNeedUpdate = true;
mesh.geometry.elementsNeedUpdate = true;
mesh.geometry.morphTargetsNeedUpdate = true;
mesh.geometry.uvsNeedUpdate = true;
mesh.geometry.normalsNeedUpdate = true;
mesh.geometry.colorsNeedUpdate = true;
mesh.geometry.tangentsNeedUpdate = true;
texture.needsUpdate = true;
I've played with wrapS / wrapT and offset.
I've checked UV's - I don't yet fully understand this concept but it seems fine. Example of UV for one mesh (I understand those are XY coordinates and they seem to reflect the actual corners of my mesh):
uv: Float32BufferAttribute
array: Float32Array(8)
0: -208
1: 188
2: 338
3: 188
4: 338
5: 12
6: -208
7: 12
I've tried setting:
texture.repeat.set(imgHeight/geometryHeight/1000, imgWidth/geometryWidth/1000);
This is how THREE.ShapeGeometry() computes UV coordinate:
https://github.com/mrdoob/three.js/blob/e622cc7890e86663011d12ec405847baa4068515/src/geometries/ShapeGeometry.js#L157
But you can re-compute them, to put in range [0..1].
Here is an example, click the button to re-compute uvs of the shape geometry:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = Math.PI * 0.5;
scene.add(grid);
var points = [
new THREE.Vector2(0, 5),
new THREE.Vector2(-5, 4),
new THREE.Vector2(-3, -3),
new THREE.Vector2(2, -5),
new THREE.Vector2(5, 0)
];
var shape = new THREE.Shape(points);
var shapeGeom = new THREE.ShapeBufferGeometry(shape);
var shapeMat = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg")
});
var mesh = new THREE.Mesh(shapeGeom, shapeMat);
scene.add(mesh);
btnRecalc.addEventListener("click", onClick);
var box3 = new THREE.Box3();
var size = new THREE.Vector3();
var v3 = new THREE.Vector3(); // for re-use
function onClick(event) {
box3.setFromObject(mesh); // get AABB of the shape mesh
box3.getSize(size); // get size of that box
var pos = shapeGeom.attributes.position;
var uv = shapeGeom.attributes.uv;
for (let i = 0; i < pos.count; i++) {
v3.fromBufferAttribute(pos, i);
v3.subVectors(v3, box3.min).divide(size); // cast world uvs to range 0..1
uv.setXY(i, v3.x, v3.y);
}
uv.needsUpdate = true; // set it to true to make changes visible
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<button id="btnRecalc" style="position: absolute;">Re-calculate UVs</button>

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.

Smooth camera rotation on mouse down in THREE.js scene

I am trying to achieve smooth camera rotation effects similar to this http://showroom.littleworkshop.fr/
But the camera rotates in all three axis (Yaw, Pitch and Roll).
My code is:
var targetQuat = new THREE.Quaternion();
OnMouseDownHandler() {
var mouseVector = new THREE.Vector3(VS.Mouse.x, VS.Mouse.y);
mouseVector.unproject(VS.Camera);
var dir = mouseVector.sub(VS.Camera.position).normalize();
var oldPos = VS.Camera.position.clone();
var cameraPosN = oldPos.normalize();
var v1 = new THREE.Vector3(dir.x, dir.y, dir.z);
var quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 4 );
v1.applyQuaternion( quaternion );
targetQuat.setFromUnitVectors(cameraPosN, v1);
}
///////////
renderLoop() {
requestAnimationFrame(VS.RenderFrame);
var deltaTime = VS.Clock.getDelta();
VS.Camera.quaternion.slerp(targetQuat, deltaTime);
...
}

Three.VideoTexture stretches portrait video

I am using Three.VideoTexture(video) to load video on canvas. It's work perfectly fine with landscape videos. But when i tried to load a portrait video, on canvas video gets stretched.
var wrap3D = $("#" + threeJsPreviewHTMLElement);//.find(".wrap3d");
videoTexture = new THREE.VideoTexture(video);
videoTexture.minFilter = THREE.LinearFilter;
videoTexture.magFilter = THREE.LinearFilter;
videoTexture.format = THREE.RGBFormat;
scene = new THREE.Scene();
var WIDTH = 520, HEIGHT = 520;
var domID = "videoContext";
if (!isDesktop) {
HEIGHT = $("#modalTumbler3d").height();
WIDTH = $("#modalTumbler3d").width();
domID = "videoContextDevice";
}
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(WIDTH, HEIGHT);
if (!wrap3D.find('#' + domID).length) {
wrap3D.append(renderer.domElement);
}
else {
wrap3D.find('#' + domID).replaceWith(renderer.domElement);
}
renderer.domElement.id = domID;
//Two CAMERA options - perspective is preferred
camera = new THREE.PerspectiveCamera(50, WIDTH / HEIGHT, .1, 20000);
camera.position.set(0, 0, 150);
scene.add(camera);
//Lights
var hemLight = new THREE.HemisphereLight(0x999999, 0xffffff, 1);
scene.add(hemLight);
var spotLight = new THREE.PointLight(0x555555);
spotLight.position.set(0, 15, -20);
scene.add(spotLight);
var spotLight = new THREE.PointLight(0x555555);
spotLight.position.set(0, -15, 20);
scene.add(spotLight);
//size and position of tumbler from camera
var Y = 0;
var A = 0;
var B = 0;
var scale = 1;
//cylinder for the movie preview
//var movieFrame = new THREE.CylinderGeometry(2, 1.8, 2.5, 50, 1, true, -1.59, Math.PI)
var movieFrame = new THREE.CylinderGeometry(1, 1, 2.5, 100, 3, true, -1.59, Math.PI)
//var movieFrame = new THREE.CylinderGeometry(2, 1.9, 2.5, 50, 1, true, -1.59, Math.PI)
var movieMaterial = new THREE.MeshBasicMaterial({ map: videoTexture, overdraw: true, side: THREE.DoubleSide });
var moviePlayer = new THREE.Mesh(movieFrame, movieMaterial);
moviePlayer.position.y = Y;
moviePlayer.rotation.y = A;
moviePlayer.renderOrder = 14;
moviePlayer.scale.set(scale, scale, scale);
scene.add(moviePlayer);
//control with the mouse or two finger pinch
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minPolarAngle = 0; // radians
controls.maxPolarAngle = 2.5; // radians
controls.minDistance = 5;
controls.maxDistance = 10;
controls.enabled = false;
This is what i have tried to play video. I have used CylinderGeometry to load video in Cylinder form.
In this case, the texture will simply cover the whole CylinderGeometry. So any distortion you see is result of the aspect-ratio of the video not matching the aspect-ratio of the cylinder-fragment..
Based on your code you have there a height of 2.5 and a width of Math.PI, so your aspect-ratio is ~1.25. Now for a portrait-video (aspect-ratio <1) you need to adjust the geometry. (so for a 3:4 potrait-video you'd need a thetaLength of 1.875/radius instead of Math.PI).

ExtrudeGeometry with diagonal line

I have created an ExtrudeGeometry Object used the three.js lib,code bellow:
var extrudeSettings = {};
extrudeSettings.bevelEnabled = false;
extrudeSettings.bevelSegments = 0;
extrudeSettings.steps =2;
//create a shape
var rectShape;
......
//add the shape to scene
var geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
var mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: color,wireframe:true }));
mesh.position.set(100, 100, 0 );
mesh.rotation.x = -Math.PI / 2;
mesh.scale.set(s, s, s);
scene.add(mesh);
If I set the wireframe property true,the diagonal line of the object will show,how can I avoid it.Are there some property I should set?thanks!

Resources