Why does this cube not rotate around its own axes? - matrix

I've got a cube which I would like to rotate around its own axes in 3D space by using keyboard input. The cube is still rotating around the world axes.
Here's my code:
var rotation_matrix_y, rotation_matrix_x, rotation_matrix_z;
var geometry = new THREE.CubeGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00, wireframe: true});
var cube = new THREE.Mesh(geometry, material);
var axisHelper = new THREE.AxisHelper( 5 );
cube.add( axisHelper );
scene.add(cube);
cube.rotation.set(0, 0, 0);
cube.matrix.makeRotationFromEuler(cube.rotation);
var render = function () {
document.addEventListener("keydown", onKeyDown, false);
function onKeyDown(e) {
// W - up
if (e.keyCode == 87) {
rotation_matrix_x = new THREE.Matrix4().makeRotationX(.0001);
cube.applyMatrix(rotation_matrix_x);
}
// S - down
if (e.keyCode == 83) {
rotation_matrix_x = new THREE.Matrix4().makeRotationX(-0.0001);
cube.applyMatrix(rotation_matrix_x);
}
// D - right
if (e.keyCode == 68) {
rotation_matrix_y = new THREE.Matrix4().makeRotationY(-0.0001);
cube.applyMatrix(rotation_matrix_y);
}
// A - left
if (e.keyCode == 65) {
rotation_matrix_y = new THREE.Matrix4().makeRotationY(0.0001);
cube.applyMatrix(rotation_matrix_y);
}
requestAnimationFrame(render);
stats.update();
renderer.render(scene, camera);
};
There are a couple of questions similar to this one, but they all seem to be outdated and some of the methods have been deprecated.

An easy way to rotate an object on its own x-axis is like so:
object.rotateX( radians );
There is also object.rotateY( radians ); and object.rotateZ( radians );
three.js r.66

Related

Avoid collision with walls. first-person

I've created navigation mesh on my Scene with walkable Property in Blender. The walkable area is floor without objects (You can see in the attached image).I could load it as glb data in my Three js project, connect it with addevent click action and calculate between mouse Position and my navmesh by Raycasting. The problem is when i click on the floor ,returned array via intersectObject, has length of one but as soon as i click on the walls it returns null array, even though I'm still on the the navMesh. btw I'm using first-person Camera. I've searched everywhere and the only solution that I've found was Three-pathfinding library-> function clampStep but there is no example for that. Any suggestion how can i restrict movement of camera between the walls?
Could Verge3d from Blender help or it has nothing to do with my Problem?
I'm totally beginner in both threejs and blender, would really appreciate any help or suggestion.
loading glb and mesh:
init: function(){
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xdddddd);
this.camera.rotation.y = 10 / 180 * Math.PI;
this.camera.position.x = 10;
this.camera.position.y = 3;
this.camera.position.z = 20;
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.renderer.physicallyCorrectLights = true;
document.body.appendChild(this.renderer.domElement);
this.controls = new FirstPersonControls(this.camera, this.renderer.domElement);
this.controls.movementSpeed = 5;
this.controls.lookSpeed = 0.01;
this.controls.noFly = false;
this.controls.lookVertical = false;
this.hlight = new THREE.AmbientLight(0xffffff, 3);;
this.scene.add(this.hlight);
let loader = new GLTFLoader();
loader.load("/ShowRoom121122.glb", (gltf) => {
const self = this;
gltf.scene.traverse(function (child) {
if (child.isMesh){
if (child.name=="navMesh_1"){
self.navmesh=child;
child.material.transparent = true;
child.material.opacity = 0.5;
const mesh = new THREE.Mesh(child.geometry, new THREE.MeshBasicMaterial({ wireframe: true, color: 0x111111}));
mesh.position.copy(child.position);
gltf.scene.add(mesh);
}else{
child.castShadow = false;
child.receiveShadow = true;
}
self.scene.add( gltf.scene );
}
})});
this.renderer.setAnimationLoop(this.animate)
},
raycasting:
raycast:function(e){
debugger
this.mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
this.mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
//2. set the picking ray from the camera position and mouse coordinates
this.raycaster.setFromCamera( this.mouse, this.camera );
//3. compute intersections
const intersects = this.raycaster.intersectObject( this.navmesh );
if (intersects.length>0){
const pt = intersects[0].point;
console.log(pt);
} },
animate:function(){
this.controls.update(0.01);
this.renderer.render(this.scene, this.camera);
this.renderer.domElement.addEventListener( 'click', this.raycast,false );
}
},

I cannot type in dat.gui in JSFiddle

I have written code that works perfectly when i run it locally on my system.
This is the link:
https://jsfiddle.net/rand0mus3r/L3j7kz5a/
When you click the mesh, a dat.gui instance appears. However, when i use backspace or try to input something in the textbox, it doesn't work.
It works fine in my system though.
This is the code:
<!DOCTYPE html>
<html>
<head>
<title>Example 01.02 - First Scene</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.101.1/examples/js/controls/OrbitControls.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>
</body>
</html>
window.onload = init();
animate(); //calling function that does all the rendering
//GLOBAL VARS
var scene, camera, renderer;
var cube;
var raycaster, mouse;
var INTERSECTED;
//global flag
var isClicked = false;
//for the camera
var controls;
//creating and rendering the GUI
params = {
yAxis: "0.00001"
}
var gui = new dat.GUI();
gui.add(params, "yAxis").onFinishChange(val => {
cube.scale.y = parseFloat(val);
});
//we make sure to make it hidden
let vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
//SET CAMERA
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000)
camera.position.z = 5;
// create a render and set the size
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor("#e5e5e5"); //background color
renderer.setSize(window.innerWidth,window.innerHeight); //size of renderer
//bind rendered to the dom element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
//RAYCASTER
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2(1,1);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xffff00 }); //0xF7F7F7 = gray
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.scale.y = 0.00001;
cube.userData.originalColor = 0xffff00;
// position the cube
cube.position.x = 0;
cube.position.y = 3;
cube.position.z = 0;
/*
//USEFUL METHODS
cube.rotation.x +=0.5
cube.scale.x +=0.5
*/
// add the cube to the scene
scene.add(cube);
/* RENDERING A PLANE
var geometry = new THREE.PlaneGeometry( 20, 20);
var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.set(80,0,0);
scene.add( plane );
//plane.position.x = 2;
*/
//ADDING LIGHTS
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
//camera
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 1000;
// when the mouse moves, call the given function
document.addEventListener('mousemove', onDocumentMouseMove, false);
//when the mouse is clicked, call the given function
document.addEventListener('click', onDocumentMouseClick, false);
}
function onDocumentMouseMove(event)
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//TRY THIS
// intersects = raycaster.intersectObject(cube); // to get the cube only
//if the mouse hovers over the cube mesh, change its color to red
//when mouse leaves the mesh, change it back to its original color
//ONLY MAKE THESE MODIFICATION IF THE MESH IS NOT CLICKED
//BECAUSE IF IT IS CLICKED, YOU HAVE TO PAINT THE MESH ACCORDING TO THE onDocumentMouseClick()
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
cube.material.color.set( 0xF7F7F7 );
}
else if (isClicked === false)
{
cube.material.color.set( cube.userData.originalColor );
}
}
// 0xff0000 red
//0xF7F7F7 = gray
function onDocumentMouseClick(event) //if we detect a click event
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//if mouse is on top of the mesh when the click occurs, change color of mesh and render GUI
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
isClicked = true;
cube.material.color.set( 0xff0000);
/*
var params = {
textField: "Enter value:"
}
var item = gui.add(params, "textField").onFinishChange(function (value) {
//Do something with the new value
//console.log(value);
cube.scale.y +=value;
});
*/
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
}
//if mouse is on top of the mesh when the click occurs, but it already marked as 'clicked', now mark it as 'unclicked'
else if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === true)
{
isClicked = false;
cube.material.color.set( cube.userData.originalColor );
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// gui.__proto__.constructor.toggleHide()
//dat.GUI.toggleHide();
//gui.toggleHide()
}
}
function render()
{
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
renderer.render(scene, camera); //render the scene
}
function animate()
{
requestAnimationFrame( animate ); //pauses when user switches tab
controls.update();
render();
}
This problem is related to a bug in OrbitControls which was fixed with r110. Upgrading OrbitControls to r110 solves the issue.
window.onload = init();
animate(); //calling function that does all the rendering
//GLOBAL VARS
var scene, camera, renderer;
var cube;
var raycaster, mouse;
var INTERSECTED;
//global flag
var isClicked = false;
//for the camera
var controls;
//creating and rendering the GUI
params = {
yAxis: 0.00001
}
var gui = new dat.GUI();
gui.add(params, "yAxis").onFinishChange(val => {
cube.scale.y = val;
});
//we make sure to make it hidden
let vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
//SET CAMERA
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000)
camera.position.z = 5;
// create a render and set the size
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor("#e5e5e5"); //background color
renderer.setSize(window.innerWidth,window.innerHeight); //size of renderer
//bind rendered to the dom element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
//RAYCASTER
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2(1,1);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xffff00 }); //0xF7F7F7 = gray
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.scale.y = 0.00001;
cube.userData.originalColor = 0xffff00;
// position the cube
cube.position.x = 0;
cube.position.y = 3;
cube.position.z = 0;
/*
//USEFUL METHODS
cube.rotation.x +=0.5
cube.scale.x +=0.5
*/
// add the cube to the scene
scene.add(cube);
/* RENDERING A PLANE
var geometry = new THREE.PlaneGeometry( 20, 20);
var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.set(80,0,0);
scene.add( plane );
//plane.position.x = 2;
*/
//ADDING LIGHTS
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
//camera
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 1000;
// when the mouse moves, call the given function
document.addEventListener('mousemove', onDocumentMouseMove, false);
//when the mouse is clicked, call the given function
document.addEventListener('click', onDocumentMouseClick, false);
}
function onDocumentMouseMove(event)
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//TRY THIS
// intersects = raycaster.intersectObject(cube); // to get the cube only
//if the mouse hovers over the cube mesh, change its color to red
//when mouse leaves the mesh, change it back to its original color
//ONLY MAKE THESE MODIFICATION IF THE MESH IS NOT CLICKED
//BECAUSE IF IT IS CLICKED, YOU HAVE TO PAINT THE MESH ACCORDING TO THE onDocumentMouseClick()
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
cube.material.color.set( 0xF7F7F7 );
}
else if (isClicked === false)
{
cube.material.color.set( cube.userData.originalColor );
}
}
// 0xff0000 red
//0xF7F7F7 = gray
function onDocumentMouseClick(event) //if we detect a click event
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//if mouse is on top of the mesh when the click occurs, change color of mesh and render GUI
if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === false)
{
isClicked = true;
cube.material.color.set( 0xff0000);
/*
var params = {
textField: "Enter value:"
}
var item = gui.add(params, "textField").onFinishChange(function (value) {
//Do something with the new value
//console.log(value);
cube.scale.y +=value;
});
*/
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
}
//if mouse is on top of the mesh when the click occurs, but it already marked as 'clicked', now mark it as 'unclicked'
else if ( intersects.length > 0 && intersects[ 0 ].object === cube && isClicked === true)
{
isClicked = false;
cube.material.color.set( cube.userData.originalColor );
//when its clicked, change the visibily of the GUI
vis = gui.domElement.style.visibility;
gui.domElement.style.visibility = vis == "" ? "hidden" : "";
// gui.__proto__.constructor.toggleHide()
//dat.GUI.toggleHide();
//gui.toggleHide()
}
}
function render()
{
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
renderer.render(scene, camera); //render the scene
}
function animate()
{
requestAnimationFrame( animate ); //pauses when user switches tab
controls.update();
render();
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.110/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.110/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
<div id="WebGL-output">
</div>
BTW: Please always ensure to use three.js files from the same release. In your demo, three.js is from r110 whereas OrbitControls is from 101.1. Such configurations are not supported and can produce undefined behavior.

Threejs & PhysiJs physics engine not updated when Mesh rotate

I am a beginner of Threejs.
I created a Box Mesh and a Sphere Mesh and applied physics using physiJs.
What I want to do is to hit the ball when the Box Mesh rotates and passing through the ball.
However, when the box mesh rotates, it passes without hitting the ball.
I think the box mesh loses physicality when it starts spinning.
function createBall () {
var ball = null;
var ballGeo = new THREE.SphereGeometry(1.5, 30, 30);
var ballMat = Physijs.createMaterial(
new THREE.MeshBasicMaterial({specular: 0x111111})
, 0.3, 0.1
);
ball = new Physijs.SphereMesh(
ballGeo,
ballMat,
5
);
ball.position.set(30, 10, 0);
scene.add(ball);
}
function createBox () {
var material = Physijs.createMaterial(
new THREE.MeshLambertMaterial(
{
color: 0x8041D9,
}), 5, 0.3);
var boxMesh = new THREE.BoxGeometry(5, 5, 25);
box = new Physijs.BoxMesh(
boxMesh,
material,
5
);
box.position.z = 20;
scene.add(box);
}
function createHeightMap() {
var initColor = new THREE.Color( 0x00ff00 );
initColor.setHSL( 0.25, 0.85, 0.5 );
var ground_material = Physijs.createMaterial(
new THREE.MeshPhongMaterial(
{ color: 0x47C83E}
),
.5,
.5
);
var ground_geometry = new THREE.PlaneGeometry(800, 800, 100, 100);
ground = new Physijs.HeightfieldMesh(
ground_geometry,
ground_material,
0, // 질량
100, // PlaneGeometry 의 분할 세그먼트랑 똑같은 값으로 줘야 한다.
100 // PlaneGeometry 의 분할 세그먼트랑 똑같은 값으로 줘야 한다.
);
ground.position.y = -10;
ground.rotation.x = Math.PI / -2;
ground.receiveShadow = true;
var meshes = [];
var controls = new function () {
this.startRotate = false;
this.addBall = function () {
createBall();
};
this.addBox = function () {
createBox();
};
this.clearMeshes = function () {
meshes.forEach(function (e) {
scene.remove(e);
});
meshes = [];
}
};
var gui = new dat.GUI();
gui.add(controls, 'addBall');
gui.add(controls, 'addBox');
gui.add(controls, 'clearMeshes');
gui.add(controls, 'startRotate').onChange(function (e) {
isStartRoate = e;
});
return ground;
}
render = function () {
stats.update();
if (isStartRoate === true) {
var rotateMatrix = new THREE.Matrix4();
rotateMatrix.identity();
rotateMatrix.makeRotationY(0.05);
box.applyMatrix(rotateMatrix);
}
requestAnimationFrame(render);
renderer.render(scene, camera);
var axes = new THREE.AxesHelper(30);
scene.add(axes);
scene.simulate(undefined, 2);
};
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;
}
window.onload = initScene;
below is codepen link
codepen
It seems like physicality not updated.
please give me any idea
When using Physijs, you should use setLinearVelocity() or setAngularVelocity() in order to update the position and rotation of your objects in a physical correct way. The updated codepen shows this approach:
https://codepen.io/anon/pen/YJmajN
Besides, the way you create AxesHelper in the render loop is no good approach. Create the helper once during the setup up of your scene.

Three.js - Create new mesh from certain faces/vertices of another mesh

I´ve been several days struggling with a particular Three.js issue, and I cannot find any way to do it. This is my case:
1) I have a floating mesh, formed by several triangled faces. This mesh is created from the geometry returned by a loader, after obtaining its vertices and faces using getAttribute('position'): How to smooth mesh triangles in STL loaded BufferGeometry
2) What I want to do now is to "project" the bottom face agains the floor.
3) Later, with this new face added, create the resulting mesh of filling the space between the 3 vertices of both faces.
I already have troubles in step 2... To create a new face I´m supossed to have its 3 vertices already added to geometry.vertices. I did it, cloning the original face vertices. I use geometry.vertices.push() results to know their new indexes, and later I use that indexes (-1) to finally create the new face. But its shape is weird, also the positions and the size. I think I´m not getting the world/scene/vector position equivalence theory right :P
I tried applying this, with no luck:
How to get the absolute position of a vertex in three.js?
Converting World coordinates to Screen coordinates in Three.js using Projection
http://barkofthebyte.azurewebsites.net/post/2014/05/05/three-js-projecting-mouse-clicks-to-a-3d-scene-how-to-do-it-and-how-it-works
I discovered that if I directly clone the full original face and simply add it to the mesh, the face is added but in the same position, so I cannot then change its vertices to place it on the floor (or at least without modifying the original face vertices!). I mean, I can change their x, y, z properties, but they are in a very small measure that doesn´t match the original mesh dimensions.
Could someone help me get this concept right?
EDIT: source code
// Create geometry
var geo = new THREE.Geometry();
var geofaces = [];
var geovertices = [];
original_geometry.updateMatrixWorld();
for(var index in original_geometry.faces){
// Get original face vertexNormals to know its 3 vertices
var face = original_geometry[index];
var vertexNormals = face.vertexNormals;
// Create 3 new vertices, add it to the array and then create a new face using the vertices indexes
var vertexIndexes = [null, null, null];
for (var i = 0, l = vertexNormals.length; i < l; i++) {
var vectorClone = vertexNormals[i].clone();
vectorClone.applyMatrix4( original_geometry.matrixWorld );
//vectorClone.unproject(camera); // JUST TESTING
//vectorClone.normalize(); // JUST TESTING
var vector = new THREE.Vector3(vectorClone.x, vectorClone.z, vectorClone.y)
//vector.normalize(); // JUST TESTING
//vector.project(camera); // JUST TESTING
//vector.unproject(camera); // JUST TESTING
vertexIndexes[i] = geovertices.push( vector ) - 1;
}
var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
geofaces.push(newFace);
}
// Assign filled arrays to the geometry
geo.faces = geofaces;
geo.vertices = geovertices;
geo.mergeVertices();
geo.computeVertexNormals();
geo.computeFaceNormals();
// Create a new mesh with resulting geometry and add it to scene (in this case, to the original mesh to keep the positions)
new_mesh = new THREE.Mesh( geo, new THREE.MeshFaceMaterial(material) ); // material is defined elsewhere
new_mesh.position.set(0, -100, 0);
original_mesh.add( new_mesh );
I created a fully operational JSFiddle with the case to try things and see the problem more clear. With this STL (smaller than my local example) I cannot even see the badly cloned faces added to the scene.. Maybe they are too small or out of focus.
Take a look to the calculateProjectedMesh() function, here is where I tried to clone and place the bottom faces (already detected because they have a different materialIndex):
JSFiddle: https://jsfiddle.net/tc39sgo1/
var container;
var stlPath = 'https://dl.dropboxusercontent.com/s/p1xp4lhy4wxmf19/Handle_Tab_floating.STL';
var camera, controls, scene, renderer, model;
var mouseX = 0,
mouseY = 0;
var test = true;
var meshPlane = null, meshStl = null, meshCube = null, meshHang = null;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
/*THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;*/
var materials = [];
materials.push( new THREE.MeshPhongMaterial({color : 0x00FF00, side:0, shading: THREE.FlatShading, transparent: true, opacity: 0.9, overdraw : true, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0xFF0000, transparent: true, opacity: 0.8, side:0, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
materials.push( new THREE.MeshPhongMaterial({color : 0x0000FF, side:2, shading: THREE.FlatShading, overdraw : true, metal: false, wireframe: false}) );
var lineMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.05 });
init();
animate();
function webglAvailable() {
try {
var canvas = document.createElement('canvas');
return !!(window.WebGLRenderingContext && (
canvas.getContext('webgl') || canvas.getContext('experimental-webgl')));
} catch (e) {
return false;
}
}
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 0.1, 100000000);
camera.position.x = 1500;
camera.position.z = -2000;
camera.position.y = 1000;
controls = new THREE.OrbitControls(camera);
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0x101030); //0x101030
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(0, 3, 0).normalize();
scene.add(directionalLight);
var directionalLight = new THREE.DirectionalLight(0xffffff, 2);
directionalLight.position.set(0, 1, -2).normalize();
scene.add(directionalLight);
if (webglAvailable()) {
renderer = new THREE.WebGLRenderer();
} else {
renderer = new THREE.CanvasRenderer();
}
renderer.setClearColor( 0xCDCDCD, 1 );
// renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousemove', onDocumentMouseMove, false);
window.addEventListener('resize', onWindowResize, false);
createPlane(500, 500);
createCube(500);
loadStl();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 2;
mouseY = (event.clientY - windowHalfY) / 2;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
function createPlane(width, height) {
var planegeometry = new THREE.PlaneBufferGeometry(width, height, 0, 0);
var material = new THREE.MeshLambertMaterial({
color: 0xFFFFFF,
side: THREE.DoubleSide
});
planegeometry.computeBoundingBox();
planegeometry.center();
meshPlane = new THREE.Mesh(planegeometry, material);
meshPlane.rotation.x = 90 * (Math.PI/180);
//meshPlane.position.y = -height/2;
scene.add(meshPlane);
}
function createCube(size) {
var geometry = new THREE.BoxGeometry( size, size, size );
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
geometry.center();
var material = new THREE.MeshPhongMaterial({
color: 0xFF0000,
opacity: 0.04,
transparent: true,
wireframe: true,
side: THREE.DoubleSide
});
meshCube = new THREE.Mesh(geometry, material);
meshCube.position.y = size/2;
scene.add(meshCube);
}
function loadStl() {
var loader = new THREE.STLLoader();
loader.load( stlPath, function ( geometry ) {
// Convert BufferGeometry to Geometry
var geometry = new THREE.Geometry().fromBufferGeometry( geometry );
geometry.computeBoundingBox();
geometry.computeVertexNormals();
geometry.center();
var faces = geometry.faces;
for(var index in faces){
var face = faces[index];
var faceNormal = face.normal;
var axis = new THREE.Vector3(0,-1,0);
var angle = Math.acos(axis.dot(faceNormal));
var angleReal = (angle / (Math.PI/180));
if(angleReal <= 70){
face.materialIndex = 1;
}
else{
face.materialIndex = 0;
}
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
meshStl = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
meshStl.position.x = 0;
meshStl.position.y = 400;
scene.add( meshStl );
// Once loaded, calculate projections mesh
calculateProjectedMesh();
});
}
function calculateProjectedMesh(){
var geometry = meshStl.geometry;
var faces = geometry.faces;
var vertices = geometry.vertices;
var geometry_projected = new THREE.Geometry();
var faces_projected = [];
var vertices_projected = [];
meshStl.updateMatrixWorld();
for(var index in faces){
var face = faces[index];
// This are the faces
if(face.materialIndex == 1){
var vertexIndexes = [face.a, face.b, face.c];
for (var i = 0, l = vertexIndexes.length; i < l; i++) {
var relatedVertice = vertices[ vertexIndexes[i] ];
var vectorClone = relatedVertice.clone();
console.warn(vectorClone);
vectorClone.applyMatrix4( meshStl.matrixWorld );
////////////////////////////////////////////////////////////////
// TEST: draw line
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
//geometry.vertices.push(new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z));
geometry.vertices.push(new THREE.Vector3(vectorClone.x, meshPlane.position.y, vectorClone.z));
var line = new THREE.Line(geometry, lineMaterial);
scene.add(line);
console.log("line added");
////////////////////////////////////////////////////////////////
vectorClone.y = 0;
var vector = new THREE.Vector3(vectorClone.x, vectorClone.y, vectorClone.z);
vertexIndexes[i] = vertices_projected.push( vector ) - 1;
}
var newFace = new THREE.Face3( vertexIndexes[0], vertexIndexes[1], vertexIndexes[2] );
newFace.materialIndex = 2;
faces_projected.push(newFace);
}
}
geometry_projected.faces = faces_projected;
geometry_projected.vertices = vertices_projected;
geometry_projected.mergeVertices();
console.info(geometry_projected);
meshHang = new THREE.Mesh(geometry_projected, new THREE.MeshFaceMaterial(materials));
var newY = -(2 * meshStl.position.y) + 0;
var newY = -meshStl.position.y;
meshHang.position.set(0, newY, 0);
meshStl.add( meshHang );
}
EDIT: Finally!! I got it! To clone the original faces I must access their 3 original vertices using "a", "b" and "c" properties, which are indexes referencing Vector3 instances in the "vertices" array of the original geometry.
I cloned the 3 vertices flatting the Z position to zero, use their new indexes to create the new face and add it to the projection mesh (in blue).
I´m also adding lines as a visual union between both faces. Now I´m ready for step 3, but I think this is complex enough to close this question.
Thanks for the updateMatrixWorld clue! It was vital to achieve my goal ;)
try this
original_geometry.updateMatrixWorld();
var vertexIndexes = [null, null, null];
for (var i = 0, l = vertexNormals.length; i < l; i++) {
var position = original_geometry.geometry.vertices[i].clone();
position.applyMatrix4( original_geometry.matrixWorld );
var vector = new THREE.Vector3(position.x, position.y, position.z)
vertexIndexes[i] = geovertices.push( vector ) - 1;
}

Rotation around an axis three.js

I'm using three.js Revision 69 and i've got a problem rotating objects around global axis.
I've found on many website the function rotateAroundWorldAxis defined as follow:
function rotateAroundWorldAxis( object, axis, radians ) {
var rotationMatrix = new THREE.Matrix4();
rotationMatrix.makeRotationAxis( axis.normalize(), radians );
rotationMatrix.multiply( object.matrix ); // pre-multiply
object.matrix = rotationMatrix;
object.rotation.setFromRotationMatrix(object.matrix);
}
I've called rotateAroundWorldAxis in my render function like that:
function render() {
var yAxis = new THREE.Vector3(0,1,0);
rotateAroundWorldAxis(albero,yAxis,Math.PI / 180);
requestAnimationFrame( render );
renderer.render( scene, camera );
}
But the result is always the same, the object is rotating around his own axis, I've obtained the same result using another function found on the web: rotateAroundObjectAxis
var rotObjectMatrix;
function rotateAroundObjectAxis(object, axis, radians) {
rotObjectMatrix = new THREE.Matrix4();
rotObjectMatrix.makeRotationAxis(axis.normalize(), radians);
object.matrix.multiply(rotObjectMatrix);
object.rotation.setFromRotationMatrix(object.matrix);
}
Could someone please help me finding out what is wrong with my code? Why those two function are achieving the same result even if they were made for different purposes?
The full javascript is:
function drawStuff() {
var albero = new THREE.Object3D();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var cone = createCone(0, 0, 0); //create cone of the tree
var cylinder = createCylinder(0, -1.1, 0); //create cylinder of the tree
albero = createAlbero(-4, 2, 3);
scene.add(albero);
var axisHelper = new THREE.AxisHelper( 5 );
scene.add( axisHelper );
camera.position.z = 20;
function createCone(x, y, z){
var coneGeometry = new THREE.CylinderGeometry(0.0, 0.7, 2, 32, 32);
var coneMaterial = new THREE.MeshBasicMaterial( {color: 0xFF0000} );
var cone = new THREE.Mesh( coneGeometry, coneMaterial );
cone.position.set(x, y, z);
return cone;
}
function createCylinder(x, y, z){
var cylGeometry = new THREE.CylinderGeometry(0.1, 0.1, 0.4, 32, 32);
var cylinderMaterial = new THREE.MeshBasicMaterial( {color: 0xffff00} );
var cylinder = new THREE.Mesh( cylGeometry, cylinderMaterial );
cylinder.position.set(x, y, z);
return cylinder;
}
function createAlbero(x ,y, z){
albero.add(cone);
albero.add(cylinder);
albero.position.set(x ,y, z);
return albero;
}
var rotObjectMatrix;
function rotateAroundObjectAxis(object, axis, radians) {
rotObjectMatrix = new THREE.Matrix4();
rotObjectMatrix.makeRotationAxis(axis.normalize(), radians);
//moltiplico la matrice dell'oggetto per la matrice di rotazione
object.matrix.multiply(rotObjectMatrix);
object.rotation.setFromRotationMatrix(object.matrix);
}
function rotateAroundWorldAxis( object, axis, radians ) {
var rotationMatrix = new THREE.Matrix4();
rotationMatrix.makeRotationAxis( axis.normalize(), radians );
rotationMatrix.multiply( object.matrix ); // pre-multiply
object.matrix = rotationMatrix;
object.rotation.setFromRotationMatrix(object.matrix);
}
function render() {
var yAxis = new THREE.Vector3(30,50,1);
rotateAroundWorldAxis(cone2,yAxis,Math.PI / 180);
requestAnimationFrame( render );
renderer.render( scene, camera );
}
render();
}
If I am correct in understanding your intent, you are interested in rotating your objects about a specified axis, which essentially would set them moving around in a circle.
This is actually more about the positioning of the objects as opposed to the orientation (read: rotation) of the object. Therefore, it might be better to write a function that would manually set the position of the object based on the rotation that you are interested in.
function rotateAboutWorldAxis(object, axis, angle) {
var rotationMatrix = new THREE.Matrix4();
rotationMatrix.makeRotationAxis( axis.normalize(), angle );
var currentPos = new THREE.Vector4(object.position.x, object.position.y, object.position.z, 1);
var newPos = currentPos.applyMatrix4(rotationMatrix);
object.position.x = newPos.x;
object.position.y = newPos.y;
object.position.z = newPos.z;
}
Try that, and let me know if that works for you.
I've found another way to rotate around world axis, using a function called "rotateEuler"
function rotateEuler(object, eul) {
object.position.applyEuler(eul);
}
Here is the call to the function:
var alberoRot= new THREE.Euler(0,0.02,0, "XYZ");
rotateEuler(albero,earthRot);

Resources