Rotation after a Rotation not acting how I Expect - three.js
I have a rubiks-cube-like puzzle I am trying to model in webgl using Three.js:
Each of the two-color centers can rotate. When they rotate, they bring all of the pieces around them along on the rotation. For example, if we rotate the orange-blue center, this is what happens:
When you complete a rotation, everything lines up:
However, now when I try to rotate the Orange-White center, I get strange behavior for the two pieces it inherited from the Orange-Blue center after the first rotation:
I expect them to rotate like the other pieces, but instead they rotate differently.
I am using Three.js's Object3D.rotateOnAxis() function to do my rotations:
function rotate ( center, distance ) {
var axis = center.axis;
center.model.rotateOnAxis ( axis, distance );
for ( var i in center.stickers ) {
center.stickers[i].rotateOnAxis ( axis, distance );
}
for ( var i in center.children ) {
center.children[i].model.rotateOnAxis ( axis, distance );
//Note: the stickers are just the colored faces
for ( var s in center.children[i].stickers ) {
center.children[i].stickers[s].rotateOnAxis ( axis, distance );
}
}
}
Here is what my key-press code looks like:
function showCube() {
//set stuff up, then...
count = 0;
parent.onkeypress = function(event) {
if ( count < 6 ) {
rotate( blocks.centers [ "BO" ], Math.PI / 6 );
count++;
if ( count == 6 ) {
moveFace ( blocks, "O1", "OY", "BW" );
moveFace ( blocks, "O2", "OW", "BY" );
moveFace ( blocks, "B1", "BW", "OY" );
moveFace ( blocks, "B2", "BY", "OW" );
moveCorner ( blocks, "BOW", "OW", "OY" );
moveCorner ( blocks, "BOW", "BW", "BY" );
moveCorner ( blocks, "BOY", "OY", "OW" );
moveCorner ( blocks, "BOY", "BY", "BW" );
}
} else {
rotate( blocks.centers [ "OW" ], Math.PI / 6 );
}
}
}
function moveFace ( blocks, child, oldParent, newParent ) {
var index = blocks.centers [ oldParent ].children.indexOf ( blocks.faces [ child ] );
blocks.centers [ oldParent ].children.splice ( index, 1 );
blocks.centers [ newParent ].children.push ( blocks.faces [ child ] );
}
function moveCorner ( blocks, child, oldParent, newParent ) {
var index = blocks.centers [ oldParent ].children.indexOf ( blocks.corners [ child ] );
blocks.centers [ oldParent ].children.splice ( index, 1 );
blocks.centers [ newParent ].children.push ( blocks.corners [ child ] );
}
Interestingly, to get the Blue-Orange-Yellow corner to rotate correctly, I need to do the rotation around the difference between the BO vector and the OW vector.
That is to say:
Blue-Orange axis is the normalized vector of: (0, 1, 1)
Orange-White axis is the normalized vector of: (1, 0, 1)
After the first rotation is complete, if I try to rotate the BOY
corner around the normal of (1, 0, 1), I get the behavior shown in
the pictures.
However, if I try to rotate it around (0, 1, 1) - (1,
0, 1), i.e. (-1, 1, 0), I get the desired behavior.
I don't understand what's happening. Can you help me update my rotate function so I get the behavior I want, without having to keep a long list of past rotations that a piece has experienced?
I suspect that after I do my first rotation, I need to tell the piece to "zero" its rotation state without causing any motion, but I'm not quite sure how to do that, or if it's the right approach.
You can play with the thing here: http://joshuad.net/clover-cube/so-question/
Thanks!
I fixed it by changing my rotate method to this:
function rotate ( center, distance ) {
var axis = center.axis;
center.model.rotateOnAxis ( axis, distance );
applyStatesToMatrixDirectly ( center.model );
for ( var stickerIndex in center.stickers ) {
center.stickers[stickerIndex].rotateOnAxis ( axis, distance );
applyStatesToMatrixDirectly ( center.stickers[stickerIndex] );
}
for ( var childIndex in center.children ) {
center.children[childIndex].model.rotateOnAxis ( axis, distance );
applyStatesToMatrixDirectly ( center.children[childIndex].model );
for ( var childStickerIndex in center.children[childIndex].stickers ) {
center.children[childIndex].stickers[childStickerIndex].rotateOnAxis ( axis, distance );
applyStatesToMatrixDirectly ( center.children[childIndex].stickers[childStickerIndex] );
}
}
}
function applyStatesToMatrixDirectly ( model ) {
model.updateMatrix();
model.geometry.applyMatrix( model.matrix );
model.position.set( 0, 0, 0 );
model.rotation.set( 0, 0, 0 );
model.scale.set( 1, 1, 1 )
model.updateMatrix();
}
The idea is that the function applyStatesToMatrixDirectly() applies the rotation directly to the model matrix and then reset all of the rotation data (as well as everything else). This allows the model to "forget" that it has been rotated, allowing the new rotateOnAxis to work.
Related
How to calculate the needed velocity vector to fire an arrow to hit a certain point/
Im using Oimo.js Physics library with 3 js. I fire my arrow at a target but my math doesn't seem to be right and I'm having trouble remembering exactly how all the kinematic formulas works. I have an attack function which creates a projectile and fires it with a 3d vector. But its not behaving how I thought it would and ended up needing to hard code a y value which doesn't really work either. Can someone point me in the correct direction? I also want the arrow to have a slight arc in its trajectory. public attack( target: Unit, isPlayer: boolean ): void { let collisionGroup = isPlayer ? CollisionGroup.PLAYER_PROJECTILES : CollisionGroup.ENEMY_PROJECTILES; let collidesWithGroup = isPlayer ? CollidesWith.PLAYER_PROJECTILES : CollidesWith.ENEMY_PROJECTILES; this.model.lookAt( target.position ); let direction: Vector3 = new Vector3( 0, 0, 0 ); direction = this.model.getWorldDirection( direction ); let value = this.calculateVelocity(); let velocity = new Vector3( direction.x * value, Math.sin( _Math.degToRad( 30 ) ) * value, direction.z * value ); let arrow = this.gameWorld.addProjectile( 'arrow3d', 'box', false, new Vector3( this.model.position.x, 5, this.model.position.z ), new Vector3( 0, 0, 0 ), false, collisionGroup, collidesWithGroup ); arrow.scale = new Vector3( 10, 10, 5 ); arrow.setVelocity( velocity ); this.playAnimation( 'attack', false ); } protected calculateVelocity(): number { return Math.sqrt( -2 * ( -9.8 / 60 ) * this.distanceToTarget ); } Im dividing by 60 because of the oimo.js timestep.
three.js: Get updated vertices with skeletal animations?
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
Custom UVGenerator - howto?
I have a use case in which users can specify texture that then is to be mapped onto an object across multiple faces. So unfortunately I cannot use standard UV mapping like with Blender and thus want to write my own custom UVGenerator to create my own projection. So I am looking for detailed info how to do it. I know that THREE.ExtrudeGeometry.WorldUVGenerator can serve as an example that is located in src/extras/geometries/ExtrudeGeometry.js In there I found two methods where I am not sure how the work together. The first is generateTopUV which basically takes 3 vertices as parameters and expects that I return three pairs of u/v values. So that's pretty straight forward what the method does. generateTopUV: function ( geometry, indexA, indexB, indexC ) The second is a odd to me since it takes four vertices and I wonder why and also the name doesn't really help me. Hope sb can shed some light on this. It's code for WorldUVGenerator is this: generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { var vertices = geometry.vertices; var a = vertices[ indexA ]; var b = vertices[ indexB ]; var c = vertices[ indexC ]; var d = vertices[ indexD ]; if ( Math.abs( a.y - b.y ) < 0.01 ) { return [ new THREE.Vector2( a.x, 1 - a.z ), new THREE.Vector2( b.x, 1 - b.z ), new THREE.Vector2( c.x, 1 - c.z ), new THREE.Vector2( d.x, 1 - d.z ) ]; } else { return [ new THREE.Vector2( a.y, 1 - a.z ), new THREE.Vector2( b.y, 1 - b.z ), new THREE.Vector2( c.y, 1 - c.z ), new THREE.Vector2( d.y, 1 - d.z ) ]; } } Cheers Tom
In the end I solved this by just setting geometry.faceVertexUvs with an array of arrays where the inner array contained 3 vector2's that give the uv values. Works nicely :-) and no need to handle 4 vertices
The three vertices in the Top and Bottom UV generation process each define one triangle. The side wall is (separately) created as a string of rectangles, each of which is subsequently split into two coplanar triangles. Those 4 vertices define one rectangle. If a bevel is requested, it is part of this rectangle process.
What is the meaning of skin indices and skin weights?
This is undocumented, so I'm asking here. I'm trying to animate a mesh in JavaScript. I'm using Blender->Three.js exporter because it is convenient. I can't use Three.js itself because I was not able to figure out how to solve certain problems on it (rendering the normals and depth informations of a scene with animated meshes to a buffer). So, how do you read the "skinIndices" and "skinWeights" properties that get exported from Blender to Three.js? What do they mean, and what are they roles when calculating the position of the vertices on the animations? "bones" : [ {"parent":-1,"name":"pelvis","pos":[-3.52132e-08,0.0410043,0.880063],"rotq":[0,0,0,1]}, {"parent":0,"name":"thigh.L","pos":[0.0878887,0.00522349,0.102822],"rotq":[0,0,0,1]}, {"parent":1,"name":"shin.L","pos":[0.103679,0.00638392,-0.445744],"rotq":[0,0,0,1]}, {"parent":2,"name":"foot.L","pos":[0.0655578,0.0194668,-0.418675],"rotq":[0,0,0,1]}, {"parent":3,"name":"toe.L","pos":[0.0280578,-0.107185,-0.0704246],"rotq":[0,0,0,1]}, {"parent":3,"name":"heel.L","pos":[3.58224e-05,0.036576,-0.0885088],"rotq":[0,0,0,1]}, {"parent":0,"name":"thigh.R","pos":[-0.0878888,0.00522352,0.102822],"rotq":[0,0,0,1]}, {"parent":6,"name":"shin.R","pos":[-0.103679,0.00638412,-0.445745],"rotq":[0,0,0,1]}, {"parent":7,"name":"foot.R","pos":[-0.0655576,0.0194677,-0.418675],"rotq":[0,0,0,1]}, {"parent":8,"name":"toe.R","pos":[-0.0280577,-0.107185,-0.0704248],"rotq":[0,0,0,1]}, {"parent":8,"name":"heel.R","pos":[-3.57926e-05,0.036576,-0.0885083],"rotq":[0,0,0,1]}, {"parent":0,"name":"stomach","pos":[5.37268e-09,-0.008465,0.121596],"rotq":[0,0,0,1]}, {"parent":11,"name":"chest","pos":[1.94616e-08,0.0538289,0.269019],"rotq":[0,0,0,1]}, {"parent":12,"name":"upper_arm.L","pos":[0.160045,-0.010388,0.159844],"rotq":[0,0,0,1]}, {"parent":13,"name":"forearm.L","pos":[0.165089,0.0102809,-0.232678],"rotq":[0,0,0,1]}, {"parent":14,"name":"hand.L","pos":[0.0980782,-0.0148839,-0.245313],"rotq":[0,0,0,1]}, {"parent":15,"name":"index.L.001","pos":[0.019191,-0.040475,-0.0743723],"rotq":[0,0,0,1]}, {"parent":16,"name":"index.L.002","pos":[-0.00562334,-0.00824448,-0.0310695],"rotq":[0,0,0,1]}, {"parent":17,"name":"index.L.003","pos":[-0.00953785,-0.00126594,-0.0192741],"rotq":[0,0,0,1]}, {"parent":15,"name":"middle.L.001","pos":[0.0191911,-0.0188201,-0.0769786],"rotq":[0,0,0,1]}, {"parent":19,"name":"middle.L.002","pos":[0.00288424,-0.00695575,-0.0326532],"rotq":[0,0,0,1]}, {"parent":20,"name":"middle.L.003","pos":[-0.0111618,-0.00550338,-0.0242877],"rotq":[0,0,0,1]}, {"parent":15,"name":"ring.L.001","pos":[0.0186397,0.00194495,-0.0777299],"rotq":[0,0,0,1]}, {"parent":22,"name":"ring.L.002","pos":[0.00393239,-0.00062982,-0.0309386],"rotq":[0,0,0,1]}, {"parent":23,"name":"ring.L.003","pos":[-0.00873661,-0.00165674,-0.024165],"rotq":[0,0,0,1]}, {"parent":15,"name":"pinky.L.001","pos":[0.0191911,0.02271,-0.0758559],"rotq":[0,0,0,1]}, {"parent":25,"name":"pinky.L.002","pos":[-0.0057596,0.0014303,-0.0236881],"rotq":[0,0,0,1]}, {"parent":26,"name":"pinky.L.003","pos":[-0.00877053,-0.0020119,-0.0195478],"rotq":[0,0,0,1]}, {"parent":15,"name":"thumb.L.001","pos":[-0.0073517,-0.0318671,-0.0156776],"rotq":[0,0,0,1]}, {"parent":28,"name":"thumb.L.002","pos":[-0.00941652,-0.0166059,-0.0179188],"rotq":[0,0,0,1]}, {"parent":29,"name":"thumb.L.003","pos":[-0.0081799,-0.0129757,-0.0276645],"rotq":[0,0,0,1]}, {"parent":12,"name":"upper_arm.R","pos":[-0.160044,-0.010388,0.159844],"rotq":[0,0,0,1]}, {"parent":31,"name":"forearm.R","pos":[-0.165089,0.0102809,-0.232679],"rotq":[0,0,0,1]}, {"parent":32,"name":"hand.R","pos":[-0.0980774,-0.0148839,-0.245313],"rotq":[0,0,0,1]}, {"parent":33,"name":"index.R.001","pos":[-0.0185038,-0.0404748,-0.0743726],"rotq":[0,0,0,1]}, {"parent":34,"name":"index.R.002","pos":[0.00562337,-0.00824449,-0.0310695],"rotq":[0,0,0,1]}, {"parent":35,"name":"index.R.003","pos":[0.00953785,-0.00126596,-0.0192741],"rotq":[0,0,0,1]}, {"parent":33,"name":"middle.R.001","pos":[-0.0185038,-0.0188199,-0.0769789],"rotq":[0,0,0,1]}, {"parent":37,"name":"middle.R.002","pos":[-0.00288421,-0.00695577,-0.0326532],"rotq":[0,0,0,1]}, {"parent":38,"name":"middle.R.003","pos":[0.0111619,-0.00550339,-0.0242877],"rotq":[0,0,0,1]}, {"parent":33,"name":"ring.R.001","pos":[-0.0179525,0.00194514,-0.0777302],"rotq":[0,0,0,1]}, {"parent":40,"name":"ring.R.002","pos":[-0.00393245,-0.000629827,-0.0309386],"rotq":[0,0,0,1]}, {"parent":41,"name":"ring.R.003","pos":[0.00873658,-0.00165676,-0.024165],"rotq":[0,0,0,1]}, {"parent":33,"name":"pinky.R.001","pos":[-0.0185039,0.0227101,-0.0758562],"rotq":[0,0,0,1]}, {"parent":43,"name":"pinky.R.002","pos":[0.0057596,0.00143027,-0.0236881],"rotq":[0,0,0,1]}, {"parent":44,"name":"pinky.R.003","pos":[0.00877053,-0.00201192,-0.0195478],"rotq":[0,0,0,1]}, {"parent":33,"name":"thumb.R.001","pos":[0.00803882,-0.0318669,-0.0156779],"rotq":[0,0,0,1]}, {"parent":46,"name":"thumb.R.002","pos":[0.00941664,-0.0166059,-0.0179188],"rotq":[0,0,0,1]}, {"parent":47,"name":"thumb.R.003","pos":[0.00817987,-0.0129757,-0.0276645],"rotq":[0,0,0,1]}, {"parent":12,"name":"neck","pos":[1.6885e-08,-0.0164749,0.225555],"rotq":[0,0,0,1]}, {"parent":49,"name":"head","pos":[0.000806741,-0.0273245,0.0637051],"rotq":[0,0,0,1]}], "skinIndices" : [ 11,0,11,0,1,11,11,0,0,11,0,11,1,11,1,11,0,11,11,0,11,0,11,0,1,11,11,0,11,0,0,11,1,11,1,11,0,11,0,11,0,11,12,0,0,11,0,11, 0,11,12,0,12,0,11,0,11,0,11,0,12,0,11,0,11,0,11,0,12,0,12,11,11,0,11,0,12,13,11,0,0,11,11,0,12,13,12,13,0,11,12,13,12,0, 12,0,13,12,13,12,12,13,12,0,12,13,13,12,12,13,12,13,13,12,13,0,12,13,12,0,12,13,13,0,12,0,13,0,0,13,13,0,13,0,0,13,0,13, 13,0,13,0,0,13,13,12,13,12,13,12,13,0,13,0,13,0,13,12,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13, (... too big)] "skinWeights" : [ 0.454566,0.443267,0.456435,0.4405,0.568642,0.331477,0.452697,0.446034,0.600277,0.577654,0.603738,0.578153,0.557686,0.334716, 0.579597,0.328238,0.596817,0.577156,0.481496,0.447683,0.604872,0.59171,0.466162,0.448242,0.567426,0.35812,0.49683,0.447124, 0.618979,0.590887,0.592533,0.590764,0.578989,0.341559,0.555862,0.37468,0.477411,0.438341,0.617349,0.569542,0.454728,0.432345, 0.401061,0.337472,0.500093,0.444338,0.633534,0.572105,0.601164,0.56698,0.388198,0.308292,0.413925,0.366652,0.449179,0.424051, 0.618298,0.58735,0.458406,0.430254,0.473939,0,0.439952,0.417849,0.605333,0.579977,0.631263,0.594722,0.517687,0,0.430191,0.274572, (... too big)] "animations" : [ {"name":"ArmatureAction", "fps":24, "length":0.625, "hierarchy": [{"parent":-1,"keys":[ {"time":0,"pos":[-3.52132e-08,0.0410043,0.880063],"rot":[0,0,0,1],"scl":[1,1,1]}, {"time":0.291667,"pos":[-3.52132e-08,0.0410043,0.880063]}, {"time":0.625,"pos":[-3.52132e-08,0.0410043,0.880063],"rot":[0,0,0,1],"scl":[1,1,1]}] }, {"parent":0,"keys":[ {"time":0,"pos":[0.0878887,0.00522349,0.102822],"rot":[0,0,0,1],"scl":[1,1,1]}, {"time":0.291667,"pos":[0.0878887,0.00522349,0.102822],"rot":[-0.36166,-1.53668e-08,-7.05768e-10,0.93231]}, {"time":0.625,"pos":[0.0878887,0.00522349,0.102822],"rot":[0,0,0,1],"scl":[1,1,1]} ]}, {"parent":1,"keys":[ {"time":0,"pos":[0.103679,0.00638392,-0.445744],"rot":[0,0,0,1],"scl":[1,1,1]}, {"time":0.291667,"pos":[0.103679,0.00638392,-0.445744]}, {"time":0.625,"pos":[0.103679,0.00638392,-0.445744],"rot":[0,0,0,1],"scl":[1,1,1]} ]}, {"parent":2,"keys":[ {"time":0,"pos":[0.0655578,0.0194668,-0.418675],"rot":[0,0,0,1],"scl":[1,1,1]}, {"time":0.291667,"pos":[0.0655578,0.0194668,-0.418675]}, {"time":0.625,"pos":[0.0655578,0.0194668,-0.418675],"rot":[0,0,0,1],"scl":[1,1,1]} ]}, {"parent":3,"keys":[ {"time":0,"pos":[0.0280578,-0.107185,-0.0704246],"rot":[0,0,0,1],"scl":[1,1,1]}, {"time":0.291667,"pos":[0.0280578,-0.107185,-0.0704246]}, {"time":0.625,"pos":[0.0280578,-0.107185,-0.0704246],"rot":[0,0,0,1],"scl":[1,1,1]} ]}, {"parent":4,"keys":[ {"time":0,"pos":[3.58149e-05,0.036576,-0.0885088],"rot":[0,0,0,1],"scl":[1,1,1]}, {"time":0.291667,"pos":[3.58149e-05,0.036576,-0.0885088]}, {"time":0.625,"pos":[3.58149e-05,0.036576,-0.0885088],"rot":[0,0,0,1],"scl":[1,1,1]} ]},
Each vertex corresponds to one skin index which corresponds to one skin weight. The skin index is the index of the bone that the particular vertex is influenced by (each vertex can only belong to one bone). The skin weight is the amount of influence that bone has over that vertex.
The skinIndices and skinWeights properties are arrays of arrays (technically the inner arrays are three.js Vector4 objects). Each item in the outer array of either corresponds, one-to-one, based on the indexed position, with each vertex in the mesh. Here's the relevant JSONLoader code that creates the values for these properties from a three.js JSON model file: if ( json.skinWeights ) { for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { var x = json.skinWeights[ i ]; var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; geometry.skinWeights.push( new Vector4( x, y, z, w ) ); } } if ( json.skinIndices ) { for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { var a = json.skinIndices[ i ]; var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; geometry.skinIndices.push( new Vector4( a, b, c, d ) ); } } The docs for the three.js Geometry object were updated to include info about this [some formatting and two inconsistency corrections mine]: Just like the skinWeights property, the skinIndices' values correspond to the geometry's vertices. Each vertex can have up to 4 bones associated with it. So if you look at the first vertex, and the first skin index, this will tell you the bones associated with that vertex. For example the first vertex could have a value of ( 10.05, 30.10, 12.12 ). Then the first skin index could have the value of ( 10, 2, 0, 0 ). The first skin weight could have the value of ( 0.8, 0.2, 0, 0 ). In affect this would take the first vertex, and then the bone mesh.bones[10] and apply it 80% of the way. Then it would take the bone mesh.bones[2] and apply it 20% of the way. The next two values have a weight of 0, so they would have no affect. In code another example could look like this: // e.g. geometry.skinIndices[15] = new THREE.Vector4( 0, 5, 9, 0 ); geometry.skinWeights[15] = new THREE.Vector4( 0.2, 0.5, 0.3, 0 ); // corresponds with the following vertex geometry.vertices[15]; // these bones will be used like so: skeleton.bones[0]; // weight of 0.2 skeleton.bones[5]; // weight of 0.5 skeleton.bones[9]; // weight of 0.3 skeleton.bones[10]; // weight of 0
generate bounding spheres for child objects
we are trying to generate bounding Spheres for child objects. Sadly we do not know how to get the proper positions for them. Even if the child objects are clearly NOT placed in the center, the position vector is still 0,0,0 We are using the UTF8Loader for importing objects and do not have access to the "standard" vertices array. Furthermore it looks like bounding radius is calculated by distance from center of the parent. Any ideas? Live demo: Login with this demo account: http://threever.org/login user: Demo passw: demoacc and go to: threever.org/editor A picture of the problem: picture And the code snippet when traversing the object: object.traverse( function( node ) { if( node.geometry ) { if(node.geometry.boundingSphere){ [...] var sphere = new THREE.Mesh( spheregeometry, material); sphere.position.copy(node.position); [...] _this.scene.add(sphere); } } }); EDIT: We tried the described workflow (thanks #WestLangley) for updating the matrix position but still had no luck. Since it looks like that the bounding spheres are not generated correctly either (in general to large) we decided to try out another approach: We were generating bounding geometry for the sole purpose of having "selection meshes" for ray casting (which can be somewhat difficult if the mesh is THREE.BufferGeometry [not?]). So we looked in the THREE.UTF8Loader file and tried to reconstruct the way geometry is created when useBuffers is false . Then we set the dynamic flag for THREE.BufferGeometry to true which gives access to the attribute Arrays. When the mesh is generated we use our createSelectionDummy function to generate a simplified THREE.Geometry version of the mesh (no uvs, merged Vertices, normal vector(0,0,0)) using the attribute Arrays. What do you think of this technique? Does this have a performance gain vs useBuffers:false ? this.createSelectionDummy = function( indices, positions ) { var geom = new THREE.Geometry(); for( var i = 0; i < indices.length; i += 3 ) { geom.vertices.push( new THREE.Vector3( positions[ i*3 ], positions[ i*3+1 ], positions[ i*3+2 ] ) ); geom.vertices.push( new THREE.Vector3( positions[ i*3+3 ], positions[ i*3+4 ], positions[ i*3+5 ] ) ); geom.vertices.push( new THREE.Vector3( positions[ i*3+6 ], positions[ i*3+7 ], positions[ i*3+8 ] ) ); geom.faces.push( new THREE.Face3( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], new THREE.Vector3(0,0,0) , null, 0 ) ); } //reduce mesh size by 3 geom.mergeVertices(); geom.computeCentroids(); geom.computeFaceNormals(); return (new THREE.Mesh( geom, new THREE.MeshBasicMaterial({wireframe:true}) )); };
You can get an object's world position like so: var position = object.matrixWorld.getPosition(); It is important to make sure the object's world matrix is updated first. This normally happens in each render call. But if for some reason it is not, you can call the following first: object.updateMatrixWorld( true ); You can compute the bounding sphere radius object.geometry.boundingSphere.radius this way: object.geometry.computeBoundingSphere();