I'm trying to obtain animation curves from fbx file with FBX SDK. I'm trying like it said in FBX docs: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref__import_scene_2_display_animation_8cxx_example_html . Here's the code:
int layersCount = currAnimStack->GetMemberCount();
/// layersCount = 1
FbxAnimLayer* layer = currAnimStack->GetMember<FbxAnimLayer>(0);
//// layer is ok, it's not null
FbxAnimCurve* curve = inNode->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
curve = inNode->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y, NULL);
curve = inNode->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z, NULL);
curve = inNode->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X, NULL);
curve = inNode->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y, NULL);
curve = inNode->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z, NULL);
curve = inNode->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X, NULL, false);
curve = inNode->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y, NULL, false);
curve = inNode->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z, NULL, false);
curve = inNode->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X, NULL, false);
curve = inNode->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y, NULL, false);
curve = inNode->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z, NULL, false);
But curve is always null, no matter how I try. At the same time, in 3ds Max curves are being displayed. So what may be wrong?
That operation during exporting from 3ds Max gave me the curves I need:
Go to Motion->Trajectories and press Collapse.
Related
Similar to the question in this stack overflow question Three.js: Get updated vertices with morph targets I am interested in how to get the "actual" position of the vertices of a mesh with a skeletal animation.
I have tried printing out the position values, but they are never actually updated (as I am to understand, this is because they are calculated on the GPU, not CPU). The answer to the question above mentioned doing the same computations on the CPU as on the GPU to get the up to date vertex positions for morph target animations, but is there a way to do this same approach for skeletal animations? If so, how??
Also, for the morph targets, someone pointed out that this code is already present in the Mesh.raycast function (https://github.com/mrdoob/three.js/blob/master/src/objects/Mesh.js#L115 ). However, I don't see HOW the raycast works with skeletal animation meshes-- how does it know the updated position of the faces?
Thank you!
A similar topic was discussed in the three.js forum some time ago. I've presented there a fiddle which computes the AABB for a skinned mesh per frame. The code actually performs the same vertex displacement via JavaScript like in the vertex shader. The routine looks like so:
function updateAABB( skinnedMesh, aabb ) {
var skeleton = skinnedMesh.skeleton;
var boneMatrices = skeleton.boneMatrices;
var geometry = skinnedMesh.geometry;
var index = geometry.index;
var position = geometry.attributes.position;
var skinIndex = geometry.attributes.skinIndex;
var skinWeigth = geometry.attributes.skinWeight;
var bindMatrix = skinnedMesh.bindMatrix;
var bindMatrixInverse = skinnedMesh.bindMatrixInverse;
var i, j, si, sw;
aabb.makeEmpty();
//
if ( index !== null ) {
// indexed geometry
for ( i = 0; i < index.count; i ++ ) {
vertex.fromBufferAttribute( position, index[ i ] );
skinIndices.fromBufferAttribute( skinIndex, index[ i ] );
skinWeights.fromBufferAttribute( skinWeigth, index[ i ] );
// the following code section is normally implemented in the vertex shader
vertex.applyMatrix4( bindMatrix ); // transform to bind space
skinned.set( 0, 0, 0 );
for ( j = 0; j < 4; j ++ ) {
si = skinIndices.getComponent( j );
sw = skinWeights.getComponent( j );
boneMatrix.fromArray( boneMatrices, si * 16 );
// weighted vertex transformation
temp.copy( vertex ).applyMatrix4( boneMatrix ).multiplyScalar( sw );
skinned.add( temp );
}
skinned.applyMatrix4( bindMatrixInverse ); // back to local space
// expand aabb
aabb.expandByPoint( skinned );
}
} else {
// non-indexed geometry
for ( i = 0; i < position.count; i ++ ) {
vertex.fromBufferAttribute( position, i );
skinIndices.fromBufferAttribute( skinIndex, i );
skinWeights.fromBufferAttribute( skinWeigth, i );
// the following code section is normally implemented in the vertex shader
vertex.applyMatrix4( bindMatrix ); // transform to bind space
skinned.set( 0, 0, 0 );
for ( j = 0; j < 4; j ++ ) {
si = skinIndices.getComponent( j );
sw = skinWeights.getComponent( j );
boneMatrix.fromArray( boneMatrices, si * 16 );
// weighted vertex transformation
temp.copy( vertex ).applyMatrix4( boneMatrix ).multiplyScalar( sw );
skinned.add( temp );
}
skinned.applyMatrix4( bindMatrixInverse ); // back to local space
// expand aabb
aabb.expandByPoint( skinned );
}
}
aabb.applyMatrix4( skinnedMesh.matrixWorld );
}
Also, for the morph targets, someone pointed out that this code is already present in the Mesh.raycast function
Yes, you can raycast against morphed meshes. Raycasting against skinned meshes is not supported yet. The code in Mesh.raycast() is already very complex. I think it needs some serious refactoring before it is further enhanced. In the meantime, you can use the presented code snippet to build a solution by yourself. The vertex displacement logic is actually the most complicated part.
Live demo: https://jsfiddle.net/fnjkeg9x/1/
three.js R107
I am working on an arcade style Everest Flight Simulator.
In my debugger where I am building this, I have a terrain and helicopter class which generate the BufferGeometry terrain mesh, the Groups for the helipad Geometries, and the group for the helicopter Camera and Geometry.
My issue is that currently I can't seem to get any collision to detect. I imagine it may not support BufferGeometries so that is an issue for me because I need the terrain to be a Buffer since it's far too expansive... as a standard geometry it causes a memory crash in the browser.
However, testing the helipad geometries alone it still does not trigger. They are in a group so I add the groups to a global window array and set the collision check to be recursive but to no avail.
Ultimately, I am open to other forms of collision detection and may need two types as I have to use buffer geometries. Any ideas on how to fix this or a better solution?
The Helicopter Object Itself
// Rect to Simulate Helicopter
const geometry = new THREE.BoxGeometry( 2, 1, 4 ),
material = new THREE.MeshBasicMaterial(),
rect = new THREE.Mesh( geometry, material );
rect.position.x = 0;
rect.position.y = terrain.returnCameraStartPosY();
rect.position.z = 0;
rect.rotation.order = "YXZ";
rect.name = "heli";
// Link Camera and Helicopter
const heliCam = new THREE.Group(),
player = new Helicopter(heliCam, "OH-58 Kiowa", 14000);
heliCam.add(camera);
heliCam.add(rect);
heliCam.position.set( 0, 2040, -2000 );
heliCam.name = "heliCam";
scene.add(heliCam);
Adding Objects to Global Collision Array
// Add Terrain
const terrain = new Terrain.ProceduralTerrain(),
terrainObj = terrain.returnTerrainObj(),
helipadEnd = new Terrain.Helipad( 0, 1200, -3600, "Finish", true ),
helipadStart = new Terrain.Helipad( 0, 2000, -2000, "Start", false ),
helipadObjStart = helipadStart.returnHelipadObj(),
helipadObjEnd = helipadEnd.returnHelipadObj();
window.collidableMeshList.push(terrainObj);
window.collidableMeshList.push(helipadObjStart);
window.collidableMeshList.push(helipadObjEnd);
Collision Detection Function Run Every Frame
collisionDetection(){
const playerOrigin = this.heli.children[1].clone(); // Get Box Mesh from Player Group
for (let i = playerOrigin.geometry.vertices.length - 1; i >= 0; i--) {
const localVertex = playerOrigin.geometry.vertices[i].clone(),
globalVertex = localVertex.applyMatrix4( playerOrigin.matrix ),
directionVector = globalVertex.sub( playerOrigin.position ),
ray = new THREE.Raycaster( playerOrigin, directionVector.clone().normalize() ),
collisionResults = ray.intersectObjects( window.collidableMeshList, true ); // Recursive Boolean for children
if ( collisionResults.length > 0 ){
this.landed = true;
console.log("Collision");
}
// if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ){
// this.landed = true;
// console.log("Collision with vectorLength")
// }
}
}
It's hard to tell what's going on inside your custom classes, but it looks like you're using an Object3D as the first argument of the raycaster, instead of a Vector3 when you use this.heli.children[1].clone(). Why don't you try something like:
var raycaster = new THREE.Raycaster();
var origin = this.heli.children[1].position;
raycaster.set(origin, direction);
Also, are you sure you're using a BufferGeometry? Because when you access a vertex value like this: playerOrigin.geometry.vertices[i], it should give you an error. There is no vertices attribute in a BufferGeometry so I don't know how you're determining the direction vector.
I'm somewhat new to Three js, and my linear algebra days were back in the 90s so I don't recall much about quarternions. My issue is I have 8 vertices for a cube that I can use to create a custom geometry mesh from, but it doesn't set the position / rotation / scale info for its world matrix. Therefor it can not be used cleanly by other three js modules like controls. I can look up the math and calculate what position / scale / rotation (rotation gets a bit hairy with some fun acos stuff) should be and create a standard boxgeometry from that. But it seems like there should be some way to do it via three js objects if I can generate the proper matrix to apply to it. The quarternion setFromUnitVectors looked interesting, but I'd still have to do some work to generate the vectors. Any ideas would be appreciated thanks
Edit: :) So let me try and simplify. I have 8 vertices, I want to create a box geometry. But box geometry doesn't take vertices. It takes width, height, depth (relatively easy to calculate) and then you set the position/scale/rotation. So here's my code thus far:
5____4
1/___0/|
| 6__|_7
2/___3/
const box = new Box3();
box.setFromPoints(points);
const width = points[1].distanceTo(points[0]);
const height = points[3].distanceTo(points[0]);
const depth = points[4].distanceTo(points[0]);
const geometry = new BoxGeometry(width, height, depth);
mesh = new Mesh(geometry, material);
const center = box.getCenter(new Vector3());
const normalizedCorner = points[0].clone().sub(center);
const quarterian = new Quaternion();
quarterian.setFromUnitVectors(geometry.vertices[0], normalizedCorner);
mesh.setRotationFromQuaternion(quarterian);
mesh.position.copy(center);
The problem being my rotation element is wrong (besides my vectors not being unit vectors). I'm apparently not getting the correct quarternion to rotate my mesh correctly.
Edit: From WestLangley's suggestion, I'm creating a rotation matrix. However, while it rotates in the correct plane, the angle is off. Here's what I have added:
const matrix = new Matrix4();
const widthVector = new Vector3().subVectors(points[6], points[7]).normalize();
const heightVector = new Vector3().subVectors(points[6], points[5]).normalize();
const depthVector = new Vector3().subVectors(points[6], points[2]).normalize();
matrix.set(
widthVector.x, heightVector.x, depthVector.x, 0,
widthVector.y, heightVector.y, depthVector.y, 0,
widthVector.z, heightVector.z, depthVector.z, 0,
0, 0, 0, 1,
);
mesh.quaternion.setFromRotationMatrix(matrix);
Per WestLangley's comments I wasn't creating my matrix correctly. The correct matrix looks like:
const matrix = new Matrix4();
const widthVector = new Vector3().subVectors(points[7], points[6]).normalize();
const heightVector = new Vector3().subVectors(points[5], points[6]).normalize();
const depthVector = new Vector3().subVectors(points[2], points[6]).normalize();
matrix.set(
widthVector.x, heightVector.x, depthVector.x, 0,
widthVector.y, heightVector.y, depthVector.y, 0,
widthVector.z, heightVector.z, depthVector.z, 0,
0, 0, 0, 1,
);
mesh.quaternion.setFromRotationMatrix(matrix);
I want to create a CurvePath for example
var spline = new THREE.SplineCurve3([
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(1, 1, 0),
]);
and I will send a particle along with path using (javascript psuedocode)
var t = 0;
function update(){
t = t + 0.05;
particle.position = spline.getPointAt(t)
}
However I want the splinecurve not to create soft bends at the edges of the shape, so for the shape above the particle will turn at a right angle at the point (1, 0, 0).
I know this should be implemented in LineCurve3 or something, but for all the other curves except for SplineCurve3, getPoint() is not implemented.
Im using THREE r59.
EDIT: THREE.Curve.create() has been deprecated. See this answer for the new pattern to follow.
To create your own curve class, a sub-class of THREE.Curve, here is the pattern to follow:
MyCurve = THREE.Curve.create(
// define the constructor (args optional)
function( points, s ) {
this.points = points;
this.myProperty = s; // add a property if you want
},
// define the getPoint() function
function( t ) {
return new THREE.Vector3( x, y, z ); // flesh this out
}
);
In your case, you can duplicate SplineCurve3 -- you just need to change the getPoint() function. To do so, you can replace this:
v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight);
v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight);
v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight);
with simple linear interpolation:
v.copy( pt1 ).lerp( pt2, weight );
three.js r.60
Slightly complex, so bear with me:
Ray intersect works perfectly when an object has no morphTargets.
When an object has morphTargets only the original position can be intersected, that is to say, if I morph a model from 0,0,0 to 50,50,50 the ray will not intersect with the object at 50,50,50, instead, when I mouse over 0,0,0 I get an intersection (even though the object is no longer there!?).
Is there some sort of flag I need to turn on to make three.js aware that the verts have moved?
Edit, code added.
This makes my mesh and adds it to the objects array (which ray intersect uses):
function createDeer( deerGeometry, materials ) {
mesh = new THREE.MorphAnimMesh( deerGeometry, new THREE.MeshLambertMaterial( { color: 0xE8E8E8, ambient: 0xE8E8E8, morphTargets: true, vertexColors: THREE.FaceColors } ) );
mesh.scale.set( 3, 3, 3 );
mesh.position.set( 0, -3, 0 );
mesh.rotation.set( 0, 0, 0 );
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.geometry.dynamic = true;
scene.add( mesh );
objects.push( mesh );
}
Ray intersection happens on mouseDown (there's a mouseOver as well, same thing), like I said, the code works fine, it's just intersecting with the original unmorphed mesh:
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( objects );
if ( intersects.length > 0 ) {
SELECTED = intersects[ 0 ].object;
for(var i=0; i<objects.length; i++)
{
if(SELECTED.position.x == objects[0].position.x) {
thisObject = i;
}
}
}
var intersects = ray.intersectObject( plane );
container.style.cursor = 'pointer';
}
}
I've decided the problem must be related to the fact that the position of the deer (as in the mesh transform) never changes, however the vertices do move away, and as the ray intersect is comparing object positions perhaps the problem is here?
I've made a pull request that has been merged and fixes this.
Note that for it to work, the boundingSphere of the object needs to contain the full extent of the morphing
The MorphTarget animation takes place entirely on GPU (in the shaders code) while the ray intersection is always computed on CPU. So in fact, there's no easy way to achieve what you're describing here.