Can't rotate an object on arbitrary axis in Threejs - three.js

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.

Related

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);
...
}

Merge geometries, but use a single material

Somewhat new to Three.js and 3d libraries in general.
I merged two geometries (a quarter cylinder and a plane) using this code:
var planeGeo = new THREE.PlaneGeometry(planeW, planeD / 2, 199, 399);
var planeMesh = new THREE.Mesh(planeGeo);
planeMesh.updateMatrix();
var cylinderGeo = new THREE.CylinderGeometry(100, 100, planeW, 199, 399, true, 0, Math.PI / 2);
cylinderGeo.rotateZ(Math.PI / 2).translate(0, 200, -100);
var cylinderMesh = new THREE.Mesh(cylinderGeo);
cylinderMesh.updateMatrix();
var singleGeometry = new THREE.Geometry();
singleGeometry.merge(planeMesh.geometry, planeMesh.matrix);
singleGeometry.merge(cylinderMesh.geometry, cylinderMesh.matrix);
var testmaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });
mesh = new THREE.Mesh(singleGeometry, testmaterial);
scene.add(mesh);
I then would like to use a single material (png) over the entire thing. This code doesn't work:
textureLoader.load('data/test.png', function (texture) {
material = new THREE.MeshLambertMaterial({
map: texture
});
});
Later in the block with the merging...
mesh = new THREE.Mesh(singleGeometry, material);
scene.add(mesh);
This results in:
I would like the end result to be a single draped png over the entire merged geometry, but I can't find anything that suggests this is a normal thing to do. Is there a better way to achieve that result than merging geometries? Or am I just looking in the wrong places?
A poor-mans solution to achieve this, using the shape supplied in your post, is the following:
https://jsfiddle.net/87wg5z27/44/
Using code from this answer: https://stackoverflow.com/a/20774922/4977165
It sets the UVs based on the bounding box of the geometry, leaving out the z-coordinate (=0). Thats why the texture is a little bit stretched at the top, you can correct that manually or maybe its sufficent for you.
geometry.computeBoundingBox();
var max = geometry.boundingBox.max,
min = geometry.boundingBox.min;
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
var faces = geometry.faces;
geometry.faceVertexUvs[0] = [];
for (var i = 0; i < faces.length ; i++) {
var v1 = geometry.vertices[faces[i].a],
v2 = geometry.vertices[faces[i].b],
v3 = geometry.vertices[faces[i].c];
geometry.faceVertexUvs[0].push([
new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y),
new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y),
new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y)
]);
}
geometry.uvsNeedUpdate = true;

Three.js incorrect texture mapping orientation

I'm trying apply a simple texture mapping to a cube, but when the texture is applied it's rotated 90° CCW.
Just for my tests I'm using the same uv mapping for all faces:
window.head = function(){
var txSplitX = 1/64;
var txSplitY = 1/32;
var mesh;
var cube = new THREE.CubeGeometry( 100, 100, 100);
var material = new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture('assets/charSkin/Superman.png') } );
var faceTx= [
new THREE.Vector2(0.125,0.75),
new THREE.Vector2(0.25,0.75),
new THREE.Vector2(0.25,0.5),
new THREE.Vector2(0.125,0.5)
];
var j=0, k=1;
for(var i=0; i<6;i++){
cube.faceVertexUvs[0][j] = [faceTx[0],faceTx[1],faceTx[3]];
cube.faceVertexUvs[0][k] = [faceTx[1],faceTx[2],faceTx[3]];
j+=2;
k+=2;
}
this.mesh = new THREE.Mesh(cube, material);
}
The result I have :
http://securilabs.free.fr/images/result.png
My texture :
http://securilabs.free.fr/images/Superman.png
How can I get the correct orientation of the texture on each face ?

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!

How do I make a material show up on a sphere

// set the scene size
var WIDTH = 1650,
HEIGHT = 700;
// set some camera attributes
var VIEW_ANGLE = 100,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
// get the DOM element to attach to
// - assume we've got jQuery to hand
var $container = $('#container');
// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.PerspectiveCamera( VIEW_ANGLE,
ASPECT,
NEAR,
FAR );
//camera.lookAt(new THREE.Vector3( 0, 0, 0 ));
var scene = new THREE.Scene();
// the camera starts at 0,0,0 so pull it back
camera.position.x = 200;
camera.position.y = 200;
camera.position.z = 300;
// start the renderer
renderer.setSize(WIDTH, HEIGHT);
// attach the render-supplied DOM element
$container.append(renderer.domElement);
// create the sphere's material
var sphereMaterial = new THREE.MeshLambertMaterial(
{
color: 0xCC0000
});
// set up the sphere vars
var radius = 60, segments = 20, rings = 20;
// create a new mesh with sphere geometry -
// we will cover the sphereMaterial next!
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius, segments, rings),
img);
// add the sphere to the scene
scene.add(sphere);
// and the camera
scene.add(camera);
// create a point light
var pointLight = new THREE.PointLight( 0xFFFFFF );
// set its position
pointLight.position.x = 50;
pointLight.position.y = 100;
pointLight.position.z = 180;
// add to the scene
scene.add(pointLight);
// add a base plane
var planeGeo = new THREE.PlaneGeometry(500, 500,8, 8);
var planeMat = new THREE.MeshLambertMaterial({color: 0x666699});
var plane = new THREE.Mesh(planeGeo, planeMat);
plane.position.x = 160;
plane.position.y = 0;
plane.position.z = 20;
//rotate it to correct position
plane.rotation.x = -Math.PI/2;
scene.add(plane);
// add 3D img
var img = new THREE.MeshBasicMaterial({
map:THREE.ImageUtils.loadTexture('cube.png')
});
img.map.needsUpdate = true;
// draw!
renderer.render(scene, camera);
I've put the var img as a material to the sphere but everytime I render it aoutomaticly changes color...
How do I do so it just will have the image as I want... not all the colors?
I whould possibly put the img on a plane.
You need to use the callback of the loadTexture function. If you refer to the source of THREE.ImageUtils you will see that the loadTexture function is defined as
loadTexture: function ( url, mapping, onLoad, onError ) {
var image = new Image();
var texture = new THREE.Texture( image, mapping );
var loader = new THREE.ImageLoader();
loader.addEventListener( 'load', function ( event ) {
texture.image = event.content;
texture.needsUpdate = true;
if ( onLoad ) onLoad( texture );
} );
loader.addEventListener( 'error', function ( event ) {
if ( onError ) onError( event.message );
} );
loader.crossOrigin = this.crossOrigin;
loader.load( url, image );
return texture;
},
Here, only the url parameter is required. The texture mapping can be passed in as null. The last two parameters are the callbacks. You can pass in a function for each one which will be called at the respective load and error events. The problem you are experiencing is most likely caused by the fact that three.js is attempting to apply your material before your texture is properly loaded. In order to remedy this, you should only do this inside a function passed as the onLoad callback (which will only be called after the image has been loaded up). Also, you should always create your material before you apply it to an object.
So you should change your code from:
// set up the sphere vars
var radius = 60, segments = 20, rings = 20;
// create a new mesh with sphere geometry -
// we will cover the sphereMaterial next!
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius, segments, rings),
img);
// add the sphere to the scene
scene.add(sphere);
to something like:
var sphereGeo, sphereTex, sphereMat, sphereMesh;
var radius = 60, segments = 20, rings = 20;
sphereGeo = new THREE.SphereGeometry(radius, segments, rings);
sphereTex = THREE.ImageUtils.loadTexture('cube.png', null, function () {
sphereMat = new THREE.MeshBasicMaterial({map: sphereTex});
sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
scene.add(sphereMesh);
});

Resources