Three.js adding a rigged, animated Asset to an existing Scene - javascript

I'm working on a Tamagotchi like browser app in three.js at the moment. But currently I'm stuck with implementing a hand, that pets the avatar when clicked.
The Hand is a rigged Blender model with 2 animations, idle and the poking animation. In the gltf Viewer the model works fine with both animations.
But when added in js, the hand is either completely distorted, or rendered correctly but, positions aren't recognized(for movement with the cursor).
Most of the examples I looked at only added a general scene, but not just one animated model. In both versions of those animations, I get an animation error.
Code for the distorted version:
loader.load('resources/models/gltf/Hand.gltf', function(gltf) {
gltf.scene.traverse(function(node) {
if (node.isMesh) hand = node;
});
//hand.material.morphTargets = true;
scene.add(hand);
mixer = new THREE.AnimationMixer(hand);
clips = hand.animations;
hand = gltf;
scene.add(hand.scene);
});
The second version, where the Hand is rendered correctly, but positions for event handling aren't recognized.
loader.load('resources/models/gltf/Hand.gltf', function(gltf) {
var hand = gltf.scene;
var animations = gltf.animations;
mixer = new THREE.AnimationMixer(hand);
for (var i = 0; i < animations.length; i++) {
mixer.clipAction(animations[i]).play();
}
scene.add(hand);
});
function for idle animation:
function idleAnim() {
var idleClip = THREE.AnimationClip.findByName(clips, "Idle");
var action = mixer.clipAction(idleClip);
action.play();
console.log("idling");
}
Link: https://github.com/JoeJoe49/AnimTest
Thanks in advance and greetings.

In your first example, you're pulling the "hand" object out of your import scene, adding it to your render scene, Then adding the rest of the import scene to your render scene.
My guess is that you need to pull out "hand" from higher in the hierarchy. It probably has a few parent objects that need to come along with to preserve the correct hierarchy for the animation.
It's worth doing a scene.traverse((o)=>{console.log(o)} to get a clear picture of how your scene is being exported. I've found with the blender gltf exporter for instance, there are usually 2 separate parent nodes, one for positioning and one for scaling+rotation, so.. it's worth looking at because it might not be exactly what you expect.
fwiw I grabbed your repo and opened the gltfs in my model previewer, but I didn't seem to see any animations on them. My previewer is set to play all animations it finds, in sequence.. so not sure what's going on there. I'm guessing these are skinnedmeshes and not morphtargets?

Related

three.js: How can I target object's position to another (grouped) object, while allowing rotation to follow AR camera?

I'm using an augmented reality library that does some fancy image tracking stuff. After learning a whole lot about this project, I'm now beyond my current ability and could use some help. For our purposes, the library creates an (empty) anchor point at the center of an IRL image target in-camera. Then moves the virtual world around the IRL camera.
My goal is to drive plane.rotation to always face the camera, while keeping plane.position locked to the anchor point. Additionally, plane.rotation values will be referenced later in development.
const THREE = window.MINDAR.IMAGE.THREE;
document.addEventListener('DOMContentLoaded', () => {
const start = async() => {
// initialize MindAR
const mindarThree = new window.MINDAR.IMAGE.MindARThree({
container: document.body,
imageTargetSrc: '../../assets/targets/testQR.mind',
});
const {renderer, scene, camera} = mindarThree;
// create AR object
const geometry = new THREE.PlaneGeometry(1, 1.25);
const material = new THREE.MeshBasicMaterial({color: 0x00ffff, transparent: true, opacity: 0.5});
const plane = new THREE.Mesh(geometry, material);
// create anchor
const anchor = mindarThree.addAnchor(0);
anchor.group.add(plane);
// start AR
await mindarThree.start();
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
}
start();
});
Everything I've tried so far went into the solutions already massaged into the (functioning draft) code. I have, however, done some research and found a couple avenues that might or might not work. Just tossing them out to see what might stick or inspire another solution. Skill-wise, I'm still in the beginner category, so any help figuring this out is much appreciated.
identify plane object by its group index number;
drive (override lib?) object rotation (x, y, z) to face camera;
possible solutions from dev:
"You can get those values through the anchor object, e.g. anchor.group.position. Meaning that you can use the current three.js API and get those values but without using it for rendering i.e. don't append the renderer.domElement to document."
"You can hack into the source code of mindar (it's open source)."
"Another way might be easier for you to try is to just create another camera yourself. I believe you can have multiple cameras, and just render another layer on top using your new camera."
I think it may be as simple as calling lookAt in the animation loop function:
// start AR
await mindarThree.start();
renderer.setAnimationLoop(() => {
plane.lookAt(new THREE.Vector3());
renderer.render(scene, camera);
});
This assumes the camera is always located at (0,0,0) (i.e., new THREE.Vector3()). This seems to be true from my limited testing. I found it helpful to debug by copy-pasting the MindAR three.js example into this codepen and printing some relevant values to the console.
Also note that, internally, MindAR's three.js module seems to directly modify the world matrix of the anchor.group object without modifying the position/rotation/scale parameters.

Disable collision of body in Cannon.js

I have a bunch of planes that fit together to form terrain. Each individual plane has it's own cannon.js body (I use three.js for rendering visuals) for collision. Due to memory constraints I de-render each object when the player moves to far away from the object. I can de-render objects easily in three.js just by turning them invisible, but there's no clear way to do this in cannon.js. Basically I want to disable a cannon.js object without deleting it outright.
I've already looked through the docs and there's basically nothing on how to do this. I've also seen no questions on any form on this topic.
Example code below to show you how I want to implement this.
//terrain generation
for (z=0; z<6; z++) {
for (x=0; x<6; x++) {
//cannon.js hitbox creation
var groundShape = new CANNON.Box(new CANNON.Vec3(2,0.125,2));
var groundBody = new CANNON.Body({ mass: 0, material: zeromaterial});
groundBody.addShape(groundShape);
groundBody.position.set(x*4,0,z*4);
world.addBody(groundBody);
maparray.push(groundBody);
//three.js plane creation
grassmesh = new THREE.Mesh(grassgeometry, grassmaterial);
grassmesh.castShadow = true;
grassmesh.receiveShadow = true;
grassmesh.position.set(x*4,0,z*4);
scene.add(grassmesh);
maparray.push(grassmesh);
}
}
...
function animate() {
//detect if player is outside of loadDistance of object
for(i=0; i<maparray; i++){
if(Math.abs(maparray[i].position.x - player.position.x) <
loadDistance && Math.abs(maparray[i].position.z -
player.position.z) < loadDistance) {
//code here magically turns off collisions for object.
}
}
}
animate();
To exclude a CANNON.Body from the simulation, run the following:
world.removeBody(groundBody);
To add it back again, run:
world.addBody(groundBody);
It’s perfectly fine to remove and add it back like this. It will help you get better performance when running word.step().

javascript games ThreeJS and Box2D conflicts?

I've been trying to experiment with box2d and threejs.
So box2d has a series of js iterations, I've been successful at using them so far in projects as well as threejs in others, but I'm finding when including the latest instance of threejs and box2dweb, threejs seems to be mis-performing when just close to box2dweb but maybe I'm missing something really simple, like a better way to load them in together, or section them off from one another?
I've tried a few iterations of the box2d js code now and I always seemed to run into the same problem with later versions of threejs and box2d together! - currently version 91 threejs.
The problem I'm seeing is very weird.
I'm really hoping someone from either the box2d camp or threejs camp can help me out with this one, please?
Below is a very simple example where I don't initialize anything to do with box2d, but just by having the library included theres problems and you can test by removing that resource, then it behaves like it should.
The below demo uses threejs 91 and box2dweb. It is supposed to every couple of seconds create a box or a simple sphere each with a random colour. Very simple demo, you will see the mesh type never changes and the colour seems to propagate across all mesh instances. However if you remove the box2dweb resource from the left tab then it functions absolutely fine, very odd :/
jsfiddle link here
class Main {
constructor(){
this._container = null;
this._scene = null;
this._camera = null;
this._renderer = null;
console.log('| Main |');
this.init();
}
init(){
this.initScene();
this.addBox(0, 0, 0);
this.animate();
}
initScene() {
this._container = document.getElementById('viewport');
this._scene = new THREE.Scene();
this._camera = new THREE.PerspectiveCamera(75, 600 / 400, 0.1, 1000);
this._camera.position.z = 15;
this._camera.position.y = -100;
this._camera.lookAt(new THREE.Vector3());
this._renderer = new THREE.WebGLRenderer({antialias:true});
this._renderer.setPixelRatio( 1 );
this._renderer.setSize( 600, 400 );
this._renderer.setClearColor( 0x000000, 1 );
this._container.appendChild( this._renderer.domElement );
}
addBox(x,y,z) {
var boxGeom = new THREE.BoxGeometry(5,5,5);
var sphereGeom = new THREE.SphereGeometry(2, 5, 5);
var colour = parseInt(Math.random()*999999);
var boxMat = new THREE.MeshBasicMaterial({color:colour});
var rand = parseInt(Math.random()*2);
var mesh = null;
if(rand == 1) {
mesh = new THREE.Mesh(boxGeom, boxMat);
}
else {
mesh = new THREE.Mesh(sphereGeom, boxMat);
}
this._scene.add(mesh);
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
}
animate() {
requestAnimationFrame( this.animate.bind(this) );
this._renderer.render( this._scene, this._camera );
}
}
var main = new Main();
window.onload = main.init;
//add either a box or a sphere with a random colour every now and again
setInterval(function() {
main.addBox(((Math.random()*100)-50), ((Math.random()*100)-50), ((Math.random()*100)-50));
}.bind(this), 4000);
so the way im including the library locally is just a simple...
<script src="js/vendor/box2dweb.js"></script>
So just by including the box2d library threejs starts to act weird, I have tested this across multiple computers too and multiple version of both box2d (mainly box2dweb) and threejs.
So with later versions of threejs it seems to have some comflicts with box2d.
I found from research that most of the box2d conversions to js are sort of marked as not safe for thread conflicts.
Im not sure if this could be the cause.
I also found examples where people have successfully used box2d with threejs but the threejs is always quite an old version, however you can see exactly the same problems occurring in my example, when I update them.
So below is a demo I found and I wish I could credit the author, but here is a copy of the fiddle using threejs 49
jsfiddle here
.....and then below just swapping the resource of threejs from 49 to 91
jsfiddle here
its quite an odd one and maybe the two libraries just don't play together anymore but would be great if someone can help or has a working example of them working together on latest threejs version.
I have tried a lot of different box2d versions but always found the same problem, could this be a problem with conflicting libraries or unsafe threads?
but also tried linking to the resource include in the fiddles provided.
Any help really appreciated!!

Models of a JSON in the wrong orientation in THREE.js

I am building a website with 3D graphics using THREE JavaScript, I am trying to import the following scene to site but in the site all the objects are facing to the inside of the planet: The scene problem
Here is the code I am using to import the scene:
//ADDING GEOMETRY
var objects = [];
var loader = new THREE.ObjectLoader();
loader.load('models/planeta-NoMerge.json', function( obj ){
scene.add( obj );
scene.traverse(function( children ){
objects.push(children);
});
});
I´m doing some raycasting because I need to show a different scene if the user clicks on a model.
And if you need something else, please let me know: D
All in all the probem is not in code you have posted. I have had similar issues here and then, but I could fix it by tweaking the export settings. There you can flip normals, include/exclude several things like Material, Bones, Animations, etc...
So it's hard to tell which Settings you need, since that is up to your needs, but playing arround with the settings will bring you further.

Three.js skinned animation mesh disappears when material skinning is true

I've exported an animated model from Blender which doesn't seem to have any issue instantiating. I'm able to create the THREE.Animation and model, but I was finding there was no animation. I realized I needed to set skinning true on each material, but when I do that the entire mesh goes missing.
Below is my (quick and messy) code trying to get everything to work.
function loadModel() {
var loader = new THREE.JSONLoader();
loader.load('assets/models/Robot.js', function(geom, mat) {
_mesh = new THREE.Object3D();
_scene.add(_mesh);
geom.computeBoundingBox();
ensureLoop(geom.animation);
THREE.AnimationHandler.add(geom.animation);
for (var i = 0; i < mat.length; i++) {
var m = mat[i];
//m.skinning = true; <-- Uncommenting this makes the model disappear
//m.morphTargets = true; <-- This causes all sorts of WebGL warnings
m.wrapAround = true;
}
var mesh = new THREE.SkinnedMesh(geom, new THREE.MeshFaceMaterial(mat));
mesh.scale.set(400, 400, 400);
mesh.position.set(0, -200, 0);
mesh.rotation.set(Utils.toRadians(-90), 0, 0);
_mesh.add(mesh);
_robot = mesh;
Render.startRender(loop);
var animation = new THREE.Animation(mesh, geom.animation.name);
animation.JITCompile = false;
animation.interpolationType = THREE.AnimationHandler.LINEAR;
animation.play();
});
}
I believe I'm updating the AnimationHandler correctly in my loop
function loop() {
_mesh.rotation.y += 0.01;
var delta = 0.75 * _clock.getDelta();
THREE.AnimationHandler.update(delta);
}
In the section metadata of the exported JSON file the number of morphTargets and bones are both greater than 0?
I think that you followed the example here:
http://threejs.org/examples/#webgl_animation_skinning_morph
in which the animated model uses Morph Target and Skeletal Animation (see Wikipedia for the theoretical concepts).
If the animated model uses only Skeletal Animation as in this example http://alteredqualia.com/three/examples/webgl_animation_skinning_tf2.html
you have to instantiate a THREE.SkinnedMesh Object and then set only the m.skinning property to true.
I was having the same problem just now. What worked for me was to remake the model with applied scale and have keyframes for LocRotScale, not just location.
lately, I've encoutered a similar issue of mesh disapearing while exporting blender skinning animation to json. It turned out, the mesh I was using had double vertex (one vertice hidding another). All looks good While creating the vertex groups and the animations in blender, but when I imported the mesh via three.js, it kept disapearing as soon as the animation started. In other words, If 1 vertice from your mesh is omitted from the vertex groups, you will experience this disapearing behavior. To prevent this issue, I now use the "remove doubles" function from blender to validate the mesh integrity before exporting it to json. You might have encountered the same issue and redoing your mesh work fix it... Anyways, the question is pretty old, but the topic is still valid as of today, so I hope this fresh info will help someone out there...
Peace INF1

Categories

Resources