Use " vector.applyQuaternion " or similar in ammo.js - javascript

I want to make a browser VR shooting game, using Three.js and Ammo.js for the physics and rigidbodies. The vr head and controllers are set up the models are loaded, but the bullets won't shoot from the gun as I want to. When I tried to do the same using only Three.js with no rigidbodies in the scene, I used the "vector.applyQuaternion" from the Three.js documentation, and it worked, the bullets were fired from the top of the gun. The problem is I didn't find anything similar for using ammo.js
Code without using ammo.js
... function handleController( controller ) {
if ( controller1.userData.isSelecting ) {
bullet1.position.set( controller1.position.x , controller1.position.y + 0.018 , controller1.position.z -0.01);
bullet1.userData.velocity.x = 0;
bullet1.userData.velocity.y = 10;
bullet1.userData.velocity.z = 10;
bullet1.userData.velocity.applyQuaternion( controller1.quaternion );
scene.add(bullet1);
}
if ( controller2.userData.isSelecting ) {
bullet2.position.set( controller2.position.x , controller2.position.y + 0.018 , controller2.position.z -0.01 );
bullet2.userData.velocity.x = 0;
bullet2.userData.velocity.y = 10;
bullet2.userData.velocity.z = 10;
bullet2.userData.velocity.applyQuaternion( controller2.quaternion );
scene.add(bullet2);
}
} ...
function render() {
handleController( controller1 );
handleController( controller2 );
var delta = clock.getDelta()
bullet1.position.x -= bullet1.userData.velocity.x * delta;
bullet1.position.y -= bullet1.userData.velocity.y * delta;
bullet1.position.z -= bullet1.userData.velocity.z * delta;
bullet2.position.x -= bullet2.userData.velocity.x * delta;
bullet2.position.y -= bullet2.userData.velocity.y * delta;
bullet2.position.z -= bullet2.userData.velocity.z * delta;
renderer.render( scene, camera );
}
code with ammo.js
function createBullet1RigidBody( threeObject, physicsShape, mass ) {
//threeObject.position.copy( pos );
//threeObject.quaternion.copy( quat1 );
quat1.set( controller1.position.x, controller1.position.y , controller1.position.z , Math.PI/2 );
pos1 = new THREE.Vector3 ( controller1.position.x, controller1.position.y , controller1.position.z );
var transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin( new Ammo.btVector3( controller1.position.x, controller1.position.y, controller1.position.z -0.5) );
transform.setRotation( new Ammo.btQuaternion( quat1.x, quat1.y, quat1.z, quat1.w ) );
var motionState = new Ammo.btDefaultMotionState( transform );
var localInertia = new Ammo.btVector3( 0, 0, 0 );
physicsShape.calculateLocalInertia( mass, localInertia );
var rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, physicsShape, localInertia );
var body1 = new Ammo.btRigidBody( rbInfo );
threeObject.userData.physicsBody = body1;
scene.add( threeObject );
if ( mass > 0 ) {
bullet1Bodies.push( threeObject );
// Disable deactivation
body1.setActivationState( 4 );
}
physicsWorld.addRigidBody( body1 );
return body1;
}
function createBullet2RigidBody( threeObject, physicsShape, mass ) {
//threeObject.position.copy( pos );
//threeObject.quaternion.copy( quat2 );
var transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin( new Ammo.btVector3( controller2.position.x, controller2.position.y, controller2.position.z -0.5 ) );
transform.setRotation( new Ammo.btQuaternion( quat.x, quat.y, quat.z, quat.w ) );
var motionState = new Ammo.btDefaultMotionState( transform );
var localInertia = new Ammo.btVector3( 0, 0, 0 );
physicsShape.calculateLocalInertia( mass, localInertia );
var rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, physicsShape, localInertia );
var body2 = new Ammo.btRigidBody( rbInfo );
threeObject.userData.physicsBody = body2;
scene.add( threeObject );
if ( mass > 0 ) {
bullet2Bodies.push( threeObject );
// Disable deactivation
body2.setActivationState( 4 );
}
physicsWorld.addRigidBody( body2 );
return body2;
}
...
function handleController( controller ) {
if ( controller1.userData.isSelecting ) {
var bullet1Mass = 0.1;
var bullet1Radius = 0.6;
var bullet1 = new THREE.Mesh( geometry_bullet, new THREE.MeshLambertMaterial( { color: 0x2661b9 } ) );
var bullet1Shape = new Ammo.btSphereShape ( bullet1Radius );
bullet1Shape.setMargin ( margin );
var bullet1_body = createBullet1RigidBody( bullet1, bullet1Shape, bullet1Mass, bullet1Radius );
//bullet1.position.set( pos1.x, pos1.y, pos1.z );
bullet1.userData.physicsBody.setFriction( 0 );
bullet1_body.setLinearVelocity( new Ammo.btVector3( 100, 100, 100 , controller1.quaternion) );
}
if ( controller2.userData.isSelecting ) {
var bullet2Mass = 0.1;
var bullet2Radius = 0.6;
var bullet2 = new THREE.Mesh( geometry_bullet, new THREE.MeshLambertMaterial( { color: 0xee2f2f } ) );
var bullet2Shape = new Ammo.btSphereShape ( bullet2Radius );
var pos2 = new THREE.Vector3 ( controller2.position.x, controller2.position.y + 10, controller2.position.z +10 );
bullet2Shape.setMargin ( margin );
//quat2.set( 0,0,0,1 );
var bullet2_body = createBullet2RigidBody( bullet2, bullet2Shape, bullet2Mass, bullet2Radius, pos2, quat2 );
//var pos2 = bullet2.position.set( controller2.position.x, controller2.position.y, controller2.position.z -0.1 );
bullet2.userData.physicsBody.setFriction( 0 );
bullet2_body.setLinearVelocity( new Ammo.btVector3( 0, 0, controller2.position.z *100 ) );
//bullet2.userData.physicsBody.applyQuaternion(controller2.quaternion);
}
}
The result is that the bullets are created but they're moving only on the z axis. The velocity of the bullet2 changes if I move the controller on the x axis, and the bullets are moving only on z axis. Bullets of the bullet1 follows the movement on the x,y,z axis but not the rotation of the controller. The "bullet2.userData.physicsBody.applyQuaternion(controller2.quaternion);" line is a comment, if I remove the bars I get this error "TypeError: bullet2.userData.physicsBody.applyQuaternion is not a function". I was expecting to do the same as I did in the previous example without ammo (userData.velocity.applyQuaternion) but there's no LinearVelocity in the ammo.js, I can only use getLinearVelocity and setLinearVelocity.

Based on ammo versions I've seen, your call to setLinearVelocity() is not correct. The btVector3 constructor must take x,y,z only. Calculate it using ThreeJS math similarly to where you've done elsewhere (choose magnitude and rotate the vector3 by the controller's quaternion). That probably solves your issue.
Note: I question why your bullets need to be physically simulated. Most shooting games do not do this. Easier and faster is to visually update its progress while checking for when it intercepts a player/wall. If you desire a gravity effect then apply small vertical drop every frame. If you want ricochet then use the normal of the intercepted surface to alter the velocity. Ammo engine is good for things like a grenade bouncing (complex, unpredictable interactions).

Related

THREE.js Thick Arrow with lookAt() capability

I wanted to make a "Thick Arrow" mesh i.e. an arrow like the standard Arrow Helper but with the shaft made out of a cylinder instead of a line.
tldr; do not copy the Arrow Helper design; see the Epilogue section at end of the question.
So I copied and modified the code for my needs (dispensed with constructor and methods) and made the changes and now it works OK:-
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//... START of ARROWMAKER SET of FUNCTIONS
// adapted from https://github.com/mrdoob/three.js/blob/master/src/helpers/ArrowHelper.js
//====================================
function F_Arrow_Fat_noDoesLookAt_Make ( dir, origin, length, shaftBaseWidth, shaftTopWidth, color, headLength, headBaseWidth, headTopWidth )
{
//... dir is assumed to be normalized
var thisArrow = new THREE.Object3D();////SW
if ( dir === undefined ) dir = new THREE.Vector3( 0, 0, 1 );
if ( origin === undefined ) origin = new THREE.Vector3( 0, 0, 0 );
if ( length === undefined ) length = 1;
if ( shaftBaseWidth === undefined ) shaftBaseWidth = 0.02 * length;
if ( shaftTopWidth === undefined ) shaftTopWidth = 0.02 * length;
if ( color === undefined ) color = 0xffff00;
if ( headLength === undefined ) headLength = 0.2 * length;
if ( headBaseWidth === undefined ) headBaseWidth = 0.4 * headLength;
if ( headTopWidth === undefined ) headTopWidth = 0.2 * headLength;//... 0.0 for a point.
/* CylinderBufferGeometry parameters from:-
// https://threejs.org/docs/index.html#api/en/geometries/CylinderBufferGeometry
* radiusTop — Radius of the cylinder at the top. Default is 1.
* radiusBottom — Radius of the cylinder at the bottom. Default is 1.
* height — Height of the cylinder. Default is 1.
* radialSegments — Number of segmented faces around the circumference of the cylinder. Default is 8
* heightSegments — Number of rows of faces along the height of the cylinder. Default is 1.
* openEnded — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.
* thetaStart — Start angle for first segment, default = 0 (three o'clock position).
* thetaLength — The central angle, often called theta, of the circular sector. The default is 2*Pi, which makes for a complete cylinder.
*/
//var shaftGeometry = new THREE.CylinderBufferGeometry( 0.0, 0.5, 1, 8, 1 );//for strongly tapering, pointed shaft
var shaftGeometry = new THREE.CylinderBufferGeometry( 0.1, 0.1, 1, 8, 1 );//shaft is cylindrical
//shaftGeometry.translate( 0, - 0.5, 0 );
shaftGeometry.translate( 0, + 0.5, 0 );
//... for partial doesLookAt capability
//shaftGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
var headGeometry = new THREE.CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); //for strongly tapering, pointed head
headGeometry.translate( 0, - 0.5, 0 );
//... for partial doesLookAt capability
//headGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
thisArrow.position.copy( origin );
/*thisArrow.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
thisArrow.line.matrixAutoUpdate = false;
thisArrow.add( thisArrow.line ); */
thisArrow.shaft = new THREE.Mesh( shaftGeometry, new THREE.MeshLambertMaterial( { color: color } ) );
thisArrow.shaft.matrixAutoUpdate = false;
thisArrow.add( thisArrow.shaft );
thisArrow.head = new THREE.Mesh( headGeometry, new THREE.MeshLambertMaterial( { color: color } ) );
thisArrow.head.matrixAutoUpdate = false;
thisArrow.add( thisArrow.head );
//thisArrow.setDirection( dir );
//thisArrow.setLength( length, headLength, headTopWidth );
var arkle = new THREE.AxesHelper (2 * length);
thisArrow.add (arkle);
F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir ) ;////SW
F_Arrow_Fat_noDoesLookAt_setLength ( thisArrow, length, headLength, headBaseWidth ) ;////SW
F_Arrow_Fat_noDoesLookAt_setColor ( thisArrow, color ) ;////SW
scene.add ( thisArrow );
//... this screws up for the F_Arrow_Fat_noDoesLookAt kind of Arrow
//thisArrow.lookAt(0,0,0);//...makes the arrow's blue Z axis lookAt Point(x,y,z).
}
//... EOFn F_Arrow_Fat_noDoesLookAt_Make().
//=============================================
function F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir )
{
// dir is assumed to be normalized
if ( dir.y > 0.99999 )
{
thisArrow.quaternion.set( 0, 0, 0, 1 );
} else if ( dir.y < - 0.99999 )
{
thisArrow.quaternion.set( 1, 0, 0, 0 );
} else
{
const _axis = /*#__PURE__*/ new THREE.Vector3();
_axis.set( dir.z, 0, - dir.x ).normalize();
const radians = Math.acos( dir.y );
thisArrow.quaternion.setFromAxisAngle( _axis, radians );
}
}
//... EOFn F_Arrow_Fat_noDoesLookAt_setDirection().
//=========================================
function F_Arrow_Fat_noDoesLookAt_setLength( thisArrow, length, headLength, headBaseWidth )
{
if ( headLength === undefined ) headLength = 0.2 * length;
if ( headBaseWidth === undefined ) headBaseWidth = 0.2 * headLength;
thisArrow.shaft.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
//x&z the same, y as per length-headLength
//thisArrow.shaft.position.y = length;//SW ???????
thisArrow.shaft.updateMatrix();
thisArrow.head.scale.set( headBaseWidth, headLength, headBaseWidth ); //x&z the same, y as per length
thisArrow.head.position.y = length;
thisArrow.head.updateMatrix();
}
//...EOFn F_Arrow_Fat_noDoesLookAt_setLength().
//========================================
function F_Arrow_Fat_noDoesLookAt_setColor( thisArrow, color )
{
thisArrow.shaft.material.color.set( color );
thisArrow.head.material.color.set( color );
}
//...EOFn F_Arrow_Fat_noDoesLookAt_setColor().
//... END of ARROWMAKER SET of FUNCTIONS
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
This works OK for a fixed-direction arrow where the arrow direction can be supplied at time of construction.
But now I need to change the arrow orientation over time (for tracking a moving target). Currently the Object3D.lookAt() function is not sufficient because the arrow points along its Object3D y-axis, whereas lookAt() orients the Object3D z-axis to look at the given target position.
With experimentation I have gotten part-way there by using:-
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
on the shaft and head geometries (the 2 lines are commented out in the above code extract). This seems to get the cylinder meshes pointing in the correct direction. But the problem is that the meshes are mis-shaped and the head mesh is displaced away from the shaft mesh.
With trial and error I might be able to adjust the code to get the arrow to work for my present example. But (given my weak understanding of quaternions) I am not confident that it would (a) be general enough to apply in all situations or (b) be sufficiently future-proof against evolution of THREE.js.
So I would be grateful for any solutions/recommendations on how to achieve the lookAt() capability for this "Thick Arrow".
Epilogue
My main takeaway is NOT to follow the design of the Helper Arrow.
As TheJim01's and somethinghere's answers indicate, there is an easier approach using the Object3D.add() "nesting" function.
For example:-
(1) create two cylinder meshes (for arrowshaft and arrowhead) which by default will point in the Y-direction; make geometry length =1.0 to assist future re-scaling.
(2) Add the meshes to a parent Object3D object.
(3) Rotate the parent +90 degrees around the X-axis using parent.rotateX(Math.PI/2).
(4) Add the parent to a grandparent object.
(5) Subsequently use grandparent.lookAt(target_point_as_World_position_Vec3_or_x_y_z).
N.B. lookAt() will not work properly if parent or grandparent have scaling other than (n,n,n).
The parent and grandparent object types may be plain THREE.Object3D, or THREE.Group, or THREE.Mesh (made invisible if required e.g. by setting small dimensions or .visibility=false)
Arrow Helper can be used dynamically but only if the internal direction is set to (0,0,1) before using lookAt().
You can apply lookAt to any Object3D. Object3D.lookAt( ... )
You have already discovered that lookAt causes the shapes to point in the +Z direction, and are compensating for that. But it can be taken a step further with the introduction of a Group. Groups are also derived from Object3D, so they also support the lookAt method.
let W = window.innerWidth;
let H = window.innerHeight;
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(28, 1, 1, 1000);
camera.position.set(10, 10, 50);
camera.lookAt(scene.position);
scene.add(camera);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 0, -1);
camera.add(light);
const group = new THREE.Group();
scene.add(group);
const arrowMat = new THREE.MeshLambertMaterial({color:"green"});
const arrowGeo = new THREE.ConeBufferGeometry(2, 5, 32);
const arrowMesh = new THREE.Mesh(arrowGeo, arrowMat);
arrowMesh.rotation.x = Math.PI / 2;
arrowMesh.position.z = 2.5;
group.add(arrowMesh);
const cylinderGeo = new THREE.CylinderBufferGeometry(1, 1, 5, 32);
const cylinderMesh = new THREE.Mesh(cylinderGeo, arrowMat);
cylinderMesh.rotation.x = Math.PI / 2;
cylinderMesh.position.z = -2.5;
group.add(cylinderMesh);
function render() {
renderer.render(scene, camera);
}
function resize() {
W = window.innerWidth;
H = window.innerHeight;
renderer.setSize(W, H);
camera.aspect = W / H;
camera.updateProjectionMatrix();
render();
}
window.addEventListener("resize", resize);
resize();
let rad = 0;
function animate() {
rad += 0.05;
group.lookAt(Math.sin(rad) * 100, Math.cos(rad) * 100, 100);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
background: skyblue;
}
<script src="https://threejs.org/build/three.min.js"></script>
The key here is that the cone/shaft are made to point in the +Z direction, and then added to the Group. This means their orientations are now local to the group. When the group's lookAt changes, the shapes follow suit. And because the "arrow" shapes point in the group's local +Z direction, that means they also point at whatever position was given to group.lookAt(...);.
Further work
This is just a starting point. You'll need to adapt this to how you want it to work with constructing the arrow at the correct position, with the correct length, etc. Still, the grouping pattern should make lookAt easier to work with.
All you require is some more understanding of nesting, which allows you to place objects relative to their parents. As mentioned in the answer above, you could use Group or Object3D, but you don't have to. You can just nest your arrowhead on your cylinder and point your cylinder into the z-direction, then use the built-in, dont-overcomplicate-things methods lookAt.
Try not to use matrices or quaternions for simple things like this, as it makes for a way harder time figuring things out. Since THREE.js allows for nested frames, make use of that!
const renderer = new THREE.WebGLRenderer;
const camera = new THREE.PerspectiveCamera;
const scene = new THREE.Scene;
const mouse = new THREE.Vector2;
const raycaster = new THREE.Raycaster;
const quaternion = new THREE.Quaternion;
const sphere = new THREE.Mesh(
new THREE.SphereGeometry( 10, 10, 10 ),
new THREE.MeshBasicMaterial({ transparent: true, opacity: .1 })
);
const arrow = new THREE.Group;
const arrowShaft = new THREE.Mesh(
// We want to ensure our arrow is completely offset into one direction
// So the translation ensure every bit of it is in Y+
new THREE.CylinderGeometry( .1, .3, 3 ).translate( 0, 1.5, 0 ),
new THREE.MeshBasicMaterial({ color: 'blue' })
);
const arrowPoint = new THREE.Mesh(
// Same thing, translate to all vertices or +Y
new THREE.ConeGeometry( 1, 2, 10 ).translate( 0, 1, 0 ),
new THREE.MeshBasicMaterial({ color: 'red' })
);
const trackerPoint = new THREE.Mesh(
new THREE.SphereGeometry( .2 ),
new THREE.MeshBasicMaterial({ color: 'green' })
);
const clickerPoint = new THREE.Mesh(
trackerPoint.geometry,
new THREE.MeshBasicMaterial({ color: 'yellow' })
);
camera.position.set( 10, 10, 10 );
camera.lookAt( scene.position );
// Place the point at the top of the shaft
arrowPoint.position.y = 3;
// Point the shaft into the z-direction
arrowShaft.rotation.x = Math.PI / 2;
// Attach the point to the shaft
arrowShaft.add( arrowPoint );
// Add the shaft to the global arrow group
arrow.add( arrowShaft );
// Add the arrow to the scene
scene.add( arrow );
scene.add( sphere );
scene.add( trackerPoint );
scene.add( clickerPoint );
renderer.domElement.addEventListener( 'mousemove', mouseMove );
renderer.domElement.addEventListener( 'click', mouseClick );
renderer.domElement.addEventListener( 'wheel', mouseWheel );
render();
document.body.appendChild( renderer.domElement );
function render(){
renderer.setSize( innerWidth, innerHeight );
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.render( scene, camera );
}
function mouseMove( event ){
mouse.set(
event.clientX / event.target.clientWidth * 2 - 1,
-event.clientY / event.target.clientHeight * 2 + 1
);
raycaster.setFromCamera( mouse, camera );
const hit = raycaster.intersectObject( sphere ).shift();
if( hit ){
trackerPoint.position.copy( hit.point );
render();
}
document.body.classList.toggle( 'tracking', !!hit );
}
function mouseClick( event ){
clickerPoint.position.copy( trackerPoint.position );
arrow.lookAt( trackerPoint.position );
render();
}
function mouseWheel( event ){
const angle = Math.PI * event.wheelDeltaX / innerWidth;
camera.position.applyQuaternion(
quaternion.setFromAxisAngle( scene.up, angle )
);
camera.lookAt( scene.position );
render();
}
body { padding: 0; margin: 0; }
body.tracking { cursor: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r123/three.min.js"></script>
You can wheel around using your mouse (if it has horizontal scroll, should be on trackpads) and click to point the arrow. I also added some tracking points so you can see that `lookAt' does work without overcomplicating it, and that is is pointing at the point you clicked on the wrapping sphere.
And with that, I definitely typed the word shaft too often. It's starting to sound weird.

enableAngularMotor in ammo js doesn't seem to function when changing object from BoxBufferGeometry to BufferGeometry

I'm creating a pinball game using three.js and ammo.js and am having issues rotating the flippers using enableAngularMotor when using bufferGeometry cloned from a loaded GLTF file, when the same code using a BoxBufferGeometry works ok.
The original test build I had a threejs BoxBufferGeometry block wired up to the hinge, and got that working. I have shifted the code to the loaded callback on the gltf loader in three.js and the geometry from the new nicer model clones ok, and can be read, but the same hinge wiring code doesn't seem to rotate the flipper.
var loader = new THREE.GLTFLoader();
let mass = ms;
loader.load(whichFile,function ( gltf ) {
gltf.animations; // Array<THREE.AnimationClip>
gltf.scene; // THREE.Scene
gltf.scenes; // Array<THREE.Scene>
gltf.cameras; // Array<THREE.Camera>
gltf.asset; // Object
gltf.scene.scale.set(Size[0],Size[1],Size[2]);
gltf.scene.position.set(Pos[0],Pos[1],Pos[2]);
gltf.asset.castShadow = true;
gltf.asset.receiveShadow = true;
gltf.scene.traverse(function (child) {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
let geometry = new THREE.Geometry().fromBufferGeometry( child.geometry );
let qCircX = new THREE.Mesh(geometry, child.material);
//scene.add(qCircX);
qCircX.scale.set(Size[0],Size[1],Size[2]);
qCircX.position.set(Pos[0],Pos[1],Pos[2]);
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
child.geometry = new THREE.BufferGeometry().fromGeometry( geometry );
let qCirc = new THREE.Mesh(child.geometry, child.material);
qCirc.scale.set(Size[0],Size[1],Size[2]);
qCirc.position.set(Pos[0],Pos[1],Pos[2]);
qCirc.castShadow = true;
qCirc.receiveShadow = true;
scene.add(qCirc);
let transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin( new Ammo.btVector3( Pos[0],Pos[1],Pos[2] ) );
let motionState = new Ammo.btDefaultMotionState( transform );
let vertices, face, triangles = [];
vertices = geometry.vertices;
vertices = generateVertexMesh(geometry, vertices, triangles);
let i, triangle, triangle_mesh = new Ammo.btTriangleMesh;
let _vec3_1 = new Ammo.btVector3(0,0,0);
let _vec3_2 = new Ammo.btVector3(0,0,0);
let _vec3_3 = new Ammo.btVector3(0,0,0);
for ( i = 0; i < triangles.length; i++ ) {
triangle = triangles[i];
_vec3_1.setX(triangle[0].x);
_vec3_1.setY(triangle[0].y);
_vec3_1.setZ(triangle[0].z);
_vec3_2.setX(triangle[1].x);
_vec3_2.setY(triangle[1].y);
_vec3_2.setZ(triangle[1].z);
_vec3_3.setX(triangle[2].x);
_vec3_3.setY(triangle[2].y);
_vec3_3.setZ(triangle[2].z);
triangle_mesh.addTriangle(
_vec3_1,
_vec3_2,
_vec3_3,
true
);
}
let colSurround = new Ammo.btBvhTriangleMeshShape( triangle_mesh, true, true );
let localInertia = new Ammo.btVector3( 0, 0, 0 );
colSurround.calculateLocalInertia( mass, localInertia );
let rbInfo = new Ammo.btRigidBodyConstructionInfo( mass, motionState, colSurround, localInertia );
let body = new Ammo.btRigidBody( rbInfo );
body.setRestitution(rest);
qCirc.userData.physicsBody = body;
qCirc.name = objName;
collidableMeshList.push(qCirc);
if(addToScoreColliders){
scoreList.push(objName);
}
//console.log(ballArray);
if ( mass > 0 ) {
rigidBodies.push( qCirc );
//console.log(rigidBodies);
body.setActivationState( 4 );
}
physicsWorld.addRigidBody( body );
if(objName == "flipperL"){
let pos = {x:-16,y:0,z:27}, posBumper = {x:0,y:0,z:0}, scaleHinge = {x:1,y:1,z:1}, scaleBumper = {x:1,y:1,z:1}, quat = {x:0,y:0,z:0,w:1}, quatBumper = {x:0,y:0,z:0,w:1}, mass = 0, massBumper = 9;
// create the hinge and bumper objects
leftHinge = createPhysicsGeometry(0xff0000,pos,scaleHinge,quat,mass,"leftHinge",0,[0,0], true);
// Hinge constraint to move the bumper
let pivotA = new Ammo.btVector3( 0, 0.5, 0 ),pivotB = new Ammo.btVector3( -1.5, -3, 0 ),axis = new Ammo.btVector3( 0, 1, 0 );
leftHingeConstraint = new Ammo.btHingeConstraint( leftHinge.userData.physicsBody, qCirc.userData.physicsBody, pivotA, pivotB, axis, axis, true );
leftHingeConstraint.setLimit(0, Math.PI/2 * 0.5, 0.9, 0.3, 1);
physicsWorld.addConstraint( leftHingeConstraint, true );
leftHingeActive = true;
bumperGeoms[0] = qCirc;
}
if(objName == "flipperR"){
let pos = {x:16,y:0,z:27}, posBumper = {x:0,y:0,z:0}, scaleHinge = {x:1,y:1,z:1}, scaleBumper = {x:1,y:1,z:1}, quat = {x:0, y:0, z:0, w: 1},quatBumper = {x:0,y:0,z:0,w:1}, mass = 0, massBumper = 9;
// create the hinge and bumper objects
rightHinge = createPhysicsGeometry(0xff0000,pos,scaleHinge,quat,mass,"rightHinge",0,[0,0], true);
// Hinge constraint to move the bumper
let pivotA = new Ammo.btVector3( 0, 0.5, 0 ),pivotB = new Ammo.btVector3( 1.5, -3, 0 ),axis = new Ammo.btVector3( 0, 1, 0 );
//console.log(bumperArray);
rightHingeConstraint = new Ammo.btHingeConstraint( rightHinge.userData.physicsBody, qCirc.userData.physicsBody, pivotA, pivotB, axis, axis, true );
rightHingeConstraint.setLimit(-Math.PI/2 * 0.5, 0, 0.9, 0.3, 1);
physicsWorld.addConstraint( rightHingeConstraint, true );
rightHingeActive = true;
bumperGeoms[1] = qCirc;
}
}
});
},
// called while loading is progressing
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened : ' + error );
});
}```
When an area of the screen is pressed, the angular motor is enabled with a multiplier to turn the flipper, whereas now the flipper doesn't rotate. I don't get any errors logging to the console, and logging geometry to check it's loaded ok is fine.
Ok, so I took an alternate approach and make the gltf scene a child of the existing simple cuboid flipper.

Three.js Blender Animation Mic Frequency Trigger Issue

I am attempting to import a multiple layer animation from Blender exported using the three.js plugin into JavaScript and have the different animations triggered by microphone frequency input. I have been able to combine the playing of the animations via buttons and a .obj being rotated depending on the frequency from mic input. But I am struggling to assign the mic to the animation.
<div class="info">
<button onclick="fadeAction( 'idle' );">idle</button>
<button onclick="fadeAction( 'run' );">run</button>
<button onclick="fadeAction( 'wrap' );">wrap</button>
</div>
loader.load( 'tentacleanimation6.json', function( geometry, materials ) {
materials.forEach( function ( material ) {
material.skinning = true;
} );
mesh = new THREE.SkinnedMesh(
geometry,
new THREE.MeshFaceMaterial( materials )
);
mixer = new THREE.AnimationMixer( mesh );
action.idle = mixer.clipAction( geometry.animations[ 1 ] );
action.run = mixer.clipAction( geometry.animations[ 0 ] );
action.wrap = mixer.clipAction( geometry.animations[ 2 ] );
action.idle.setEffectiveWeight( 1 );
action.run.setEffectiveWeight( 1 );
action.wrap.setEffectiveWeight( 1 );
action.idle.play();
mesh.position.y = 0;
mesh.position.x = -3;
scene.add( mesh );
} );
fadeAction = function () {
var activeActionName = 'idle';
return function ( name ) {
var from = action[ activeActionName ].play();
var to = action[ name ].play();
from.enabled = true;
to.enabled = true;
from.crossFadeTo( to, .3 );
activeActionName = name;
}
}();
Then the mic input from getUserMedia is processed as followed triggering a .obj file to rotate:
script_processor_analysis_node.onaudioprocess = function() {
analyser_node.getByteFrequencyData(array_freq_domain);
if (microphone_stream.playbackState == microphone_stream.PLAYING_STATE) {
var render = function () {
for ( var i = 0; i < (array_freq_domain.length); i++ ){
var valueone = array_freq_domain[array_freq_domain.length / 2];
mouthone.rotation.x = deg2rad( valueone ) / 7;
var valuetwo = array_freq_domain[6];
mouthtwo.rotation.x = deg2rad( valuetwo ) / 7;
}
renderer.render(scene, camera);
}
requestAnimationFrame( render );
}
}
The issue becomes when the two are combined and I am not sure what needs to be called in the onaudioprocess section to trigger an animation to play? Also there seems to be an issue with rendering, as the function for onaudioprocess needs to be rendered as above and the animation needs to be rendered with the following and when these are combined they cancel each other out:
;( function update () {
requestAnimationFrame( update );
var delta = clock.getDelta();
var theta = clock.getElapsedTime();
if ( mixer ) { mixer.update( delta ); }
renderer.render( scene, camera );
} )();

Can I use THREE js ExtrudeGeometry to extrude a shape over a sharp path?

I have used CatmullRomCurve3 to extrude over a curved line. Is it possible to extrude over a sharp line like below:
What I want to have is:
With possibly as few polygons as possible.
There are a number of ways you could create a geometry of steps, but to do so using ExtrudeGeometry, you could do something like:
var shape = new THREE.Shape();
shape.moveTo( 0, 0 );
var numSteps = 10, stepSize = 10;
for ( var i = 0; i < numSteps; i ++ ) {
shape.lineTo( i * stepSize, ( i + 1 ) * stepSize );
shape.lineTo( ( i + 1 ) * stepSize, ( i + 1 ) * stepSize );
}
var extrudeSettings = { amount: 100, bevelEnabled: false };
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var material = new THREE.MeshBasicMaterial( {color: 0xffffff } );
var steps = new THREE.Mesh( geometry, material );
For a full example based on webgl_geometry_extrude_shapes2, see below:
http://jsfiddle.net/cc146hcx/
I ended up creating vertices, faces and calculating UVs myself. For anyone wanting to do something similar this helped me a lot. I still do not know if ExtrudeGeometry could have been used here.

three.js - get object name with mouse click

I had loaded 3 external model with the name into my scene using json loader and now i want to get the name of the model/object by clicking it.
Below is the that i had used to load the model
var object_material = new THREE.MeshBasicMaterial({
color: 0xd6d6d6,
side: THREE.DoubleSide
});
var loader = new THREE.JSONLoader();
loader.load("models/"+file,
function(geometry, object_material)
{
var object = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(object_material));
model = new THREE.Object3D();
model.id=file;
model.name='sample'+file;
model.userData.id='sampledata'+file;
model.add(object);
model.position.set(obj_x,obj_y,obj_z);
model.mirroredLoop = true;
model.castShadow = true;
model.receiveShadow = true;
scene.add(model);
}
);
Below is my mouse down function
function onMouseDown(event) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var pLocal = new THREE.Vector3(0, 0, -1);
var pWorld = pLocal.applyMatrix4(camera.matrixWorld);
var ray = new THREE.Raycaster(pWorld, vector.sub(pWorld).normalize());
// Get meshes from all objects
var getMeshes = function(children) {
var meshes = [];
for (var i = 0; i < children.length; i++)
{
if (children[i].children.length > 0) {
meshes = meshes.concat(getMeshes(children[i].children));
} else if (children[i] instanceof THREE.Mesh) {
meshes.push(children[i]);
}
}
return meshes;
};
function attributeValues(o)
{
var out = [];
for (var key in o) {
if (!o.hasOwnProperty(key))
continue;
out.push(o[key]);
}
return out;
}
var objects = attributeValues(this.o3dByEntityId);
var meshes = getMeshes(objects);
var intersects = ray.intersectObjects(meshes);
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( scene.children );
console.log(scene);
// this console displays all the objects under children - THREE.Object3D - name as name: "sample513.js"
if ( intersects.length > 0 )
{
// but for the clickedObject - the length is > 0 and name is empty
var clickedObject = intersects[0].object;
console.log(clickedObject.parent.userData.id); // return as undefined
if ( INTERSECTED != intersects[ 0 ].object )
{
INTERSECTED= intersects[ 0 ].object;
name = INTERSECTED.name;
}
} else {
console.log('intersects.length is 0');
}
}
Even-though i had provided the model name in the userData , i am not able to retrieve it . can any one guide me how to retrieve the name of the object when it is clicked
Try to make through this example. Look at messages in the console.
<script src="js/controls/EventsControls.js"></script>
EventsControls = new EventsControls( camera, renderer.domElement );
EventsControls.attachEvent( 'onclick', function() {
console.log( 'this.focused.name: ' + this.focused.name );
});
// if use drag and drop
EventsControls.attachEvent( 'dragAndDrop', function () {
this.container.style.cursor = 'move';
this.focused.position.y = this.previous.y;
});
EventsControls.attachEvent( 'mouseOut', function () {
this.container.style.cursor = 'auto';
});
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load( "models/Tux.js", addModelToScene );
function addModelToScene( geometry, materials ) {
var material = new THREE.MeshFaceMaterial( materials );
model = new THREE.Mesh( geometry, material );
model.scale.set( 10, 10, 10 ); model.name = 'Tux';
model.rotation.x = -Math.PI/2;
model.position.set( 175, 45, 125 );
scene.add( model );
EventsControls.attach( model );
}
The parent of the clickedObject is probably undefined. Perhaps you can console log the clickedObject and see which path you need to access the id.

Categories