Problem in Mesh Rotation around another Mesh of imported FBX model in Three.js - three.js

For the learning purposes, I downloaded a layalty-free FBX model from a website, which happens to be a helicopter. I want to emulate the rotation of the helicopter blades programmatically in Three.js. I imported the moded successfully by means of FBXLoader, without any problem. I checked its meshes in Blender, and it has more than fifty meshes. I pinpointed the blades' meshes and wrote this in the load() function:
pivotPoint = new THREE.Object3D();
const loader = new THREE.FBXLoader();
group = new THREE.Object3D();
loader.load(
'Apache.fbx',
object => {
scene.add(object);
const twentyFive = scene.getObjectByName('Mesh25'); //This is the shaft which the blades should rotate around
console.log(twentyFive); //x: 685.594482421875, y: 136.4067840576172, z: -501.9534606933594
twentyFive.add(pivotPoint);
const twentyEight = scene.getObjectByName('Mesh28');//These four are the blades
const twentyNine = scene.getObjectByName('Mesh29');
const twentySeven = scene.getObjectByName('Mesh27');
const twentySix = scene.getObjectByName('Mesh26');
group.add(twentyEight);
group.add(twentyNine);
group.add(twentySeven);
group.add(twentySix);
pivotPoint.add(group);
scene.add(pivotPoint);
scene.add(twentyFive);
},
progress => ...,
error => ...
);
and the following in the loop render function:
pivotPoint.rotation.y += 0.01;
However, either the four blades disappear once I add the nesting Object3Ds or upon changing the code into the above version with numerous mutations, the four blades would strangely rotate around some other point in sky, apart from the fuselage, while the awe-stricken pilot watches the catastrophe and amazed by the aforementioned code, as if the helicopter is about to crash any second!
I tried many changes to the code. Basically I had once used the Object3D parenting for some light sources on another scene, but have no idea what's the issue now. Besides, the rotation of the blades around Mesh25 (my wished pivot) is around a big circle with no contacts with the fuselage, although all four are beautifully revolve around their center of mass.
I really appreciate any help, as I really need to learn to wrestle with similar imported models.

Use attach instead of add in the appropriate places.
const twentyFive = scene.getObjectByName('Mesh25');
// add the pivot and group first so they are in the scene
pivotPoint.add(group);
twentyFive.add(pivotPoint);
const twentyEight = scene.getObjectByName('Mesh28');
const twentyNine = scene.getObjectByName('Mesh29');
const twentySeven = scene.getObjectByName('Mesh27');
const twentySix = scene.getObjectByName('Mesh26');
// use attach to move something in the scene hierarchy without
// changing its position
group.attach(twentyEight);
group.attach(twentyNine);
group.attach(twentySeven);
group.attach(twentySix);
This assumes the model is created correctly in the first place and that the the shaft's position Mesh25 is in the center of the shaft.
Note: If the shaft's origin is in the correct position and the blades are already children of the shaft you can just rotate the shaft.

Related

Make children of imported Three.js group follow pointer when their canvas gets hovered

In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when they get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes in Three.js).
I think, firstly, I have to detect, where currently pointer is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far:
Hi everyone.
In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when their canvas get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes of Three.js).
I think, firstly, I have to detect, where currently pointer on canvas is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far. Function that translates meshes isn't written yet. Mousemove callback returns pretty big digits, though:
// Load object and play a third-party animation
loader.load("Object_001.fbx", (object) => {
mixer = new THREE.AnimationMixer(object);
const action = mixer.clipAction(object.animations[0]);
action.play();
object.traverse((child) => {
if (child.isMesh) {
child.material.map = texture;
child.material.needsUpdate = true;
}
});
scene.add(object);
});
// log coordinates of the pointer
const mouse = new THREE.Vector3();
const position = new THREE.Vector3();
function onMouseMove(event) {
mouse.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
mouse.unproject(camera);
mouse.sub(camera.position).normalize();
const distance = -camera.position.z / mouse.z;
position.copy(camera.position).add(mouse.multiplyScalar(distance));
console.log(position);
}
wrapperElement.addEventListener("mousemove", onMouseMove);
Thanks in advance.
Made a codepen here:
https://codepen.io/cdeep/pen/YzxPPZQ
The cursor only exists in the canvas dom element which is a rendering of the camera view frustum.
The easiest way to make the object follow a mouse is to get the point of intersection of the mouse with another object in the 3d scene and set the object position to the intersection point. The above codepen showcases that.
raycaster.setFromCamera( mouse, camera );
const intersects = raycaster.intersectObjects([ground]);
if(intersects.length) {
const { point } = intersects[0];
cube.position.copy(point.setY(0.5));
}
You could also position it at a fixed distance from the mouse but it looks odd in my opinion:
const distance = 10;
raycaster.setFromCamera( mouse, camera );
const { origin, direction } = raycaster.ray;
cube.position.copy(origin.clone().add(direction.multiplyScalar(distance)));
Documentation for raycaster:
https://threejs.org/docs/index.html?q=ray#api/en/core/Raycaster
Raycasting is used for mouse picking (working out what objects in the
3d space the mouse is over) amongst other things.

threejs - raycasting in AR with controller after repositioning

I'm rather new to threejs, so what I'm doing might not be the most efficient way.
I have an object in AR on a mobile device and I want to know if I intersect with it when touching on the screen.
I use the following code to generate the raycast, and it works initally.
const tempMatrix = new THREE.Matrix4();
tempMatrix.identity().extractRotation(this.controller.matrixWorld);
this.raycaster.ray.origin.setFromMatrixPosition(this.controller.matrixWorld);
this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
However, I have the ability to reposition the object (i.e. reset the position so the object is in front, relative to the current camera direction and position) by moving and rotating the whole scene.
After the repositioning, the raycasting is completely offset and is not casting rays anywhere near where I touch the screen.
Repositioning is done like this (while it works, if there's a better way, let me know!) :
public handleReposition(): void {
const xRotation = Math.abs(this.camera.rotation.x) > Math.PI / 2 ? -Math.PI : 0;
const yRotation = this.camera.rotation.y;
this.scene.rotation.set(xRotation, yRotation, xRotation);
this.scene.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z);
}
How can I achieve to raycast to the correct new location?
Thanks!
Assuming this.scene is actually the main threejs Scene, it's usually a bad idea to change its rotation or position, since it will affect everything inside the scene, including the controller. I'd suggest moving your object instead, or add your object(s) to a Group and move that.

Three.js parenting mesh to bone

My goal is to attach separate objects (eg: wearable items) to an animated model, so that the bound objects are controlled by the models animation.
I have found these, but all seems outdated.
Three.js attaching object to bone
https://github.com/mrdoob/three.js/issues/3187
I experimenting with a character imported from blender that is skinned & rigged & animated.
My problem is: As I add a new mesh to a specific bone of the model (the commented out part in the code), the current animation clip is switched to the first one (t-pose), and the skinning get broken (model turns white).
However the object is connects to the bone, and moves with it.
const {scene, animations} = await Utils.loadModel('model/someName.gltf');
const model = scene.children[0];
const material = new THREE.MeshBasicMaterial();
material.alphaTest = 0.5;
material.skinning = true;
model.traverse((child) => {
if (child.material) {
material.map = child.material.map;
child.material = material;
child.material.needsUpdate = true;
}
if (child.isBone && child.name === 'RightHand') {
// child.add(createMesh());
}
});
gScene.add(model);
It dosen't work correctly, even if a simple cube is added, but it would be nice if I could add boots to a character, that is moves as its foot.
Looks like I have found a solution.
I updated the demo (It's only a PoC) https://github.com/tomo0613/3d-animated-char-test
first (in blender):
add bones to the shoe that should move with the character. (i guess these should heave the same bones [positions, rotations, sizes]; there are differences in the demo)
then is JS code:
add the shoe (entire mesh) as a child mesh to the leg bone.
before every render, copy the characters bone properties to the shoe bone properties [position & quaternion]
I'm still curious, is there any intended /or better way doing this.?

Raycasting in threeJs, when target is over object

I'm fairly new to three.js and trying to get a better understanding of ray casting. I have used it so far in a game to show when an object collides with another on the page which works perfectly. In the game I'm building this is to take health from the hero as it crashes into walls.
I am now trying to implement a target which when hovered over some objects it will auto shoot. However the target only registers a target hit once the object passes through the target mesh rather than when its ray is cast through it.
to further detail this, the ray is cast from the camera through the target, if the target is on a mesh (stored as an object array) then I want it to trigger a function.
within my update function I have this:
var ray = new THREE.Raycaster();
var crossHairClone = crossHair.position.clone();
var coards = {};
coards.x = crossHairClone.x
coards.y = crossHairClone.y
ray.setFromCamera(coards, camera);
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 ) {
console.log('Target Hit!', collisionResults)
}
The console log is only triggered when the collidableMeshList actually touches the target mesh rather than when it is aiming at it.
How do I extend the ray to pass through the target (which I thought it was already doing) so that if anything hits the ray then it triggers my console log.
Edit
I've added a URL to the game in progress. There are other wider issues with the game, my current focus is just the target ray casting.
Game Link
Many thanks to everyone who helped me along the way on this one but I finally solved it, only not through the means that I had expected to. Upon reading into the setFromCamera() function it looks like it is mainly used around mouse coards which in my instance was not what I wanted. I wanted a target on the page and the ray to pass through this. For some reason my Ray was shooting vertically up from the centre of the whole scene.
In the end I tried a different approach and set the Rays position and direction directly rather rely on setting it from the cameras position.
var vector = new THREE.Vector3(crossHair.position.x, crossHair.position.y, crossHair.position.z);
var targetRay = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var enemyHit = targetRay.intersectObject( enemy );
if ( enemyHit.length > 0 ) {
console.log('Target Hit!', targetRay)
}
I'll leave this up for a while before accepting this answer in case anyone has a better approach than this or has any corrections over what I have said in regards to setFromCamera().

Dynamic change a lot of textures is very low performance

I am a newbie in Three js. In my project, I need draw a ground with a lot of texture. The ground has many layers, every layer has 4 textures and textures in different layers are different size. Below picture describe the ground:
Ground is one mesh has multiple material:
this.mesh = new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
Suppose I have a car it always at center of ground, in other word, center of all layers. When it move, the ground will translate to make sure the car always at center. So everytime ground translate, I need to update texture in new position.
The picture draw 3 layers for illustration, but in my project is 6 layers. So everytime all texture change, that means need to change 6 * 4 = 24 textures, and that cause low fps in my program.
This is my function to load texture from indexed DB every time a texture change:
Ground.prototype.loadTextureFromIndexedDB = function (url, materialIndex) {
var loader = new THREE.TextureLoader();
loader.crossOrigin = '';
loader.load(url,
function (texture) {
var groundMaterial = ground.mesh.material.materials[materialIndex];
groundMaterial.map.dispose();
groundMaterial.map = texture;
groundMaterial.map.anisotropy = ground.maxAnisotropy;
groundMaterial.map.minFilter = THREE.LinearFilter;
groundMaterial.map.needsUpdate = true;
img = null;
window.URL.revokeObjectURL(url);
});
}
I have tried many solutions. One of them is make a mesh with a BufferGeometry and MultiMaterial with array of ShaderMaterial. As what I known, it is the best for performance in THREE JS, isn't it? If it is then maybe THREE JS is not powerful as I thinked. Should I change to another API for my project?
Anyone suggest me any solution to make higher performance in my program? Thanks a lot!

Resources