requestAnimationFrame in polymer - javascript

I'm trying to implement an thrre.js animation on polymer with the code below. I can render the scene the first time, but when I call requestAnimationFrame(animate) - i.e. I call the function animate itself - then the code brake because it can't find any variable defined before. I know that requestAnimationFrame() accepts 2 parameters: the function to call and the element on which to call the function. I tried many different element without success.
Does anyone have any idea? thanks a lot.
(function(root) {
'use strict';
Polymer('x-trial-objects', {
app : document.querySelector('x-app'),
container : null,
scene : null,
camera : null,
renderer : null,
controls : null,
stats : null,
keyboard : new THREEx.KeyboardState(),
clock : new THREE.Clock(),
cube : null,
parameters : null,
gui : null,
// Trial params
dimensionChoice : 1,
speed : 0.007,
spikeNum : 3,
radiusChange : 0.02,
minLength : 1,
maxLength : 3,
rangeLength : 2,
eventOccur : 1,
ang : [-40, -95, 170, 40],
getRandomInt : function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
timeOcc : function timeOcc(speed){
// Create array of possible times at which the event could occur, based on the rate of change (speed variable)
var _this = this,
timeArray = [],
i;
for (i=10;i<1/speed;i+=1) {
// this adds all possible steps of the dimension and rounds to 3 decimals
// Update - this now only allows the event to occur after the first 10 time steps to give subjects time to actually see the stimulus before the change
timeArray.push( Math.round((i*speed)*1000) / 1000 );
}
// Select a random timepoint in that array as the onset time of the event, and a timepoint N points beyond for the offset.
var t1 = _this.getRandomInt(0,timeArray.length-1),
t2 = t1+7;
return [timeArray[t1],timeArray[t2]];
},
sigmoid : function(x,t) {
// scale the x parameter by the temperature parameter
var x=(x*t*2)-t;
// calculate sigmoid
return 1 / ( 1 + Math.exp(-x) );
},
quadratic : function(x) {
// rescale the x parameter between -1 and 1
x=(x*2)-1;
// Calculate quadratic
return x*x;
},
init : function (){
var _this = this
console.log(this)
console.log(document)
// Note that I could have a purely random number between 0 and 1 on each trial, but here I am specifically sampling from just 10 points along the dimension. This (a) ensures a good sampling across the whole dimension over trials and (b) allows us to constrain the sampling to certain parts of the dimension so that we can later test stimuli that were not presented in this phase. I may want to change this so that the overall experimental script deals with the randomisation. That way I can have better control over the sampling so that e.g. I have 10 points along the dimension, each sampled the same number of times over trials. At present i do not have this level of control, as every time the script is called it will pick one of these at random.
var dimensionPoints = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1];
if (_this.dimensionChoice===1){
_this.k1 = 0;
_this.k2 = dimensionPoints[_this.getRandomInt(0, dimensionPoints.length-1)];
}
if (_this.dimensionChoice===2){
_this.k1 = dimensionPoints[_this.getRandomInt(0, dimensionPoints.length-1)];
_this.k2 = 0;
}
// SCENE
_this.scene = new THREE.Scene();
// CAMERA
var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
_this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
_this.scene.add(_this.camera);
_this.camera.position.set(0,0,10);
_this.camera.lookAt(_this.scene.position);
// RENDERER
if ( Detector.webgl )
_this.renderer = new THREE.WebGLRenderer( {antialias:true} );
else
_this.renderer = new THREE.CanvasRenderer();
_this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
_this.container = this.shadowRoot.querySelector('#ThreeJS')
_this.container.appendChild( _this.renderer.domElement );
// EVENTS
THREEx.WindowResize(_this.renderer, _this.camera);
THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
// CONTROLS
_this.controls = new THREE.OrbitControls( _this.camera, _this.renderer.domElement );
// STATS
_this.stats = new Stats();
_this.stats.domElement.style.position = 'absolute';
_this.stats.domElement.style.bottom = '0px';
_this.stats.domElement.style.zIndex = 100;
_this.container.appendChild( _this.stats.domElement );
// LIGHT
var ambientLight = new THREE.AmbientLight( 0x222222 );
var light = new THREE.DirectionalLight( 0xFFFFFF, 1.0 );
light.position.set( 200, 400, 500 );
var light2 = new THREE.DirectionalLight( 0xFFFFFF, 1.0 );
light2.position.set( -500, 250, -200 );
_this.scene.add(ambientLight);
_this.scene.add(light);
_this.scene.add(light2);
//Create central sphere
var radius = 0.6;
var geometry = new THREE.SphereGeometry( radius, 32, 16 );
var material = new THREE.MeshLambertMaterial( { color: "grey" } );
var sphere = new THREE.Mesh( geometry, material );
//Add the sphere to object spikedBall
_this.spikedBall = new THREE.Object3D();
_this.spikedBall.add( sphere );
// Note that I can no longer loop through these four spikes, as I want to be able to scale each one independently after adding them to the scene
var cylGeom = new THREE.CylinderGeometry( 0, 0.3, _this.minLength, 32 );
var cylinder = new THREE.Mesh( cylGeom, material );
cylinder.position.y=_this.minLength/2;
_this.spike1 = new THREE.Object3D();
_this.spike1.add(cylinder);
_this.spike1.rotation.z = _this.ang[0] * Math.PI/180;
_this.spike1.scale.y = (_this.k1 * _this.rangeLength) + _this.minLength;
_this.spikedBall.add( _this.spike1 );
var cylGeom = new THREE.CylinderGeometry( 0, 0.3, _this.minLength, 32 );
var cylinder = new THREE.Mesh( cylGeom, material );
cylinder.position.y=_this.minLength/2;
_this.spike2 = new THREE.Object3D();
_this.spike2.add(cylinder);
_this.spike2.rotation.z = _this.ang[1] * Math.PI/180;
_this.spike2.scale.y = (_this.sigmoid(_this.k1,7) * _this.rangeLength) + _this.minLength;
_this.spikedBall.add( _this.spike2 );
var cylGeom = new THREE.CylinderGeometry( 0, 0.3, _this.minLength, 32 );
var cylinder = new THREE.Mesh( cylGeom, material );
cylinder.position.y = _this.minLength/2;
_this.spike3 = new THREE.Object3D();
_this.spike3.add(cylinder);
_this.spike3.rotation.z = _this.ang[2] * Math.PI/180;
_this.spike3.scale.y = (_this.k2 * _this.rangeLength) + _this.minLength;
_this.spikedBall.add( _this.spike3 );
var cylGeom = new THREE.CylinderGeometry( 0, 0.3, _this.minLength, 32 );
var cylinder = new THREE.Mesh( cylGeom, material );
cylinder.position.y=_this.minLength/2;
_this.spike4 = new THREE.Object3D();
_this.spike4.add(cylinder);
_this.spike4.rotation.z = _this.ang[3] * Math.PI/180;
_this.spike4.scale.y = (_this.quadratic(_this.k2) * _this.rangeLength) + _this.minLength;
_this.spikedBall.add( _this.spike4 );
//Add spiked Ball to the scene
_this.scene.add( _this.spikedBall )
console.log(_this.scene)
},
render : function() {
var _this = this;
_this.renderer.render( _this.scene, _this.camera );
},
animate : function animate() {
console.log(this.container)
var _this = this,
timeOccur = _this.timeOcc(_this.speed);
if (_this.dimensionChoice===1)
{
_this.k1 += _this.speed;
_this.k1 = Math.round(_this.k1*1000) / 1000;
_this.spike1.scale.y = (_this.k1 * _this.rangeLength) + _this.minLength;
_this.spike2.scale.y = (_this.sigmoid(_this.k1,7) * _this.rangeLength) + _this.minLength;
if ( _this.k1>=timeOccur[0] && _this.k1<=timeOccur[1])
{
_this.spikedBall.children[_this.spikeNum].children[0].geometry = new THREE.CylinderGeometry( _this.radiusChange, 0.3, 1, 32 );
}
else {
_this.spikedBall.children[_this.spikeNum].children[0].geometry = new THREE.CylinderGeometry( 0, 0.3, 1, 32 );
}
if (_this.k1 < 1)
{
requestAnimationFrame( animate);
}
}
this.render();
},
domReady : function domReady () {
this.init();
this.animate();
}
});}(window));

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 animated bezier curves

EDITED. SOLUTION FOUND
I need to know how to implement animation of the points in a curve to simulate string movement in 3D with performance in mind.
Multiple strings between two points for example.
Fiddle provided. (code updated)
So I have curveObject and I'm trying to change position of a point1. (code updated)
var camera, scene, renderer;
var angle1 = angle2 = 0;
var curve1, point1, curveObject, geometryCurve, materialCurve;
var params1 = {P0x: 0, P0y: 0,
P1x: 2, P1y: 2,
P2x: -2, P2y: 1,
P3x: 0, P3y: 3,
steps: 30};
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 10;
scene.add(camera);
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setClearColor( 0x16112b, 1 );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
createBezierCurveNEW = function (cpList, steps) {
var N = Math.round(steps)+1 || 20;
var geometry = new THREE.Geometry();
var curve = new THREE.CubicBezierCurve3();
var cp = cpList[0];
curve.v0 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[1];
curve.v1 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[2];
curve.v2 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[3];
curve.v3 = new THREE.Vector3(cp[0], cp[1], cp[2]);
var j, stepSize = 1/(N-1);
for (j = 0; j < N; j++) {
geometry.vertices.push( curve.getPoint(j * stepSize) );
}
return geometry;
};
function CreateCurve(){
scene.remove(curve1);
var controlPoints1 = [
[params1.P0x, params1.P0y, 0],
[params1.P1x, params1.P1y, 0],
[params1.P2x, params1.P2y, 0],
[params1.P3x, params1.P3y, 0] ];
var curveGeom1 = createBezierCurveNEW(controlPoints1, params1.steps);
var mat = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 5 } );
curve1 = new THREE.Line( curveGeom1, mat );
scene.add(curve1);
};
CreateCurve();
function animate() {
CreateCurve();
render();
angle1 -= .007;
angle2 += .003;
params1.P1x = Math.cos(angle1)+2;
params1.P1y = Math.sin(angle1)+3;
params1.P2x = -Math.cos(angle2)-2;
params1.P2y = Math.cos(angle2)+1;
requestAnimationFrame(animate);
};
animate();
function render() {
renderer.render(scene, camera);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>
I see value increment in console,
but no actual visual feedback. My guess - I need to update curve somehow.
Final goal is to smoothly animate slow sine-like movement of the curve.
like control points of bezier curve are being moved in Photoshop.
(The goal was reached. Sadly not by my own. I've stumbled upon some helper code lib at http://cs.wellesley.edu/~cs307/lectures/15.shtml so BIG thanks to these guys.)
There is little info regarding curve animation in threejs.
Maybe someone already got going something similar.
(The goal was reached. Sadly not by my own. I've stumbled upon some helper code lib at http://cs.wellesley.edu/~cs307/lectures/15.shtml so BIG thanks to these guys.)

Meshes are in Scene however nothing but the renderer's clear color appears

So I'm working with Three.js and jQuery to create a small visual application. At the moment all I want is for all the meshes I have, to appear on screen.
The Problem: None of the meshes appear on screen whatsoever.
Exceptions: The renderer's clear color appears (0x00bfff) and console.log(scene) confirms that all the meshes are in the scene.
Attempts to Fix: Use THREE.Projector, THREE.Raycaster, change camera positioning, and many more attempts.
I'm still very new to Three.js and programming in general so please be very critical of my work. Anything helps! Thanks!
WORLD.JS
$(document).ready(function() {
initialize();
animate();
});
var initialize = function() {
clock = new THREE.Clock(); // timer used to calculate time between rendering frames
scene = new THREE.Scene(); // list of objects that are to be "read" (rendered)
camera = new THREE.PerspectiveCamera(35, // FOV
window.innerWidth / window.innerHeight, // Aspect Ratio
.1, // Near
10000); // Far
camera.position.set( 25, 25, 125 );
camera.lookAt( scene.position );
setupEnvironment();
setupAI();
renderer = new THREE.WebGLRenderer(); // renderer will draw as WebGL rather than HTML5 Canvas
renderer.setSize( window.innerWidth, window.innerHeight ); // size of the canvas that renderer will draw on
renderer.setClearColor( 0x00bfff, 1 );
document.body.appendChild( renderer.domElement ); // adds the canvas to the document
};
var animate = function() { // animates the scene with frames
requestAnimationFrame(animate); // works recursively
render(); // update and display
}
var render = function() {
var delta = clock.getDelta() // gets the seconds passed since the last call to this method
// AI collision needed
// AI update needed
renderer.render( scene, camera ) // repaint
}
var setupEnvironment = function() {
ground = new BoxMesh( 10, 0.1, 10, 0x6C4319, 1 );
positionThenAdd( ground, [[ 0, 0 ]] );
light1 = new THREE.PointLight( 0xFFFFFF, .5 );
light1.position.set( 10, 10, 10 );
scene.add( light1 );
light2 = new THREE.PointLight( 0xFFFFFF, 1 );
light2.position.set( -10, -10, 10 );
scene.add( light2 );
};
var setupAI = function() {
sheep = new BoxMesh( 1, 1, 1, 0xFFFFFF, 3 );
positionThenAdd( sheep, [[ 0, 0 ],
[ 4.5, 0 ],
[ 9.5, 0 ]]);
sheepHerder = new BoxMesh( 1, 1, 1, 0x996633, 1 );
positionThenAdd( sheepHerder, [[ 4.5, 7.5 ]] );
};
function BoxMesh( width, height, depth, hexColor, amount ) { // creates one or more box meshes
this.width = width;
this.height = height;
this.depth = depth;
this.hexColor = hexColor;
this.amount = amount; // amount of box meshes to be made
boxSize = new THREE.BoxGeometry( width, height, depth );
boxMaterial = new THREE.MeshLambertMaterial( { color: hexColor } );
var all = []; // will contain all of the box meshes
for(var n = 1; n <= amount; n++) { // adds a new box mesh to the end of the all array
all.push(new THREE.Mesh( boxSize, boxMaterial )); // uses the attributes given by the BoxMesh constructor's parameters
}
return all; // returns all of the created box meshes as an array;
}
var positionThenAdd = function( varMesh, posArrXByZ ) { // positions an object and then adds it to the scene
this.varMesh = varMesh; // variable name of the mesh(es) array
this.posArrXByZ = posArrXByZ; // posArrXByZ stands for "array of positions in the format of X-by-Z"
// posArrXByZ is a 2 dimensional array where the first dimension is for the specific mesh to be positioned...
// and the second dimension is the positional coordinates.
// posArrXByZ = [ [x0,z0], [x1,z1], ...[xn,zn] ]
for(var mesh = 0; mesh < varMesh.length; mesh++) { // mesh accesses the varMesh array
varMesh[mesh].position.set( varMesh[mesh].geometry.parameters.width/2 + posArrXByZ[mesh][0], // the x coordinate, varMesh[mesh].width/2 makes the x coordinate act upon the closest side
varMesh[mesh].geometry.parameters.height/2 + ground.height, // the y coordinate, which is pre-set to rest on top of the ground
varMesh[mesh].geometry.parameters.depth/2 + posArrXByZ[mesh][1] ); // the z coordinate, varMesh[mesh].height/2 makes the y coordinate act upon the closest side
scene.add( varMesh[mesh] ); // adds the specific mesh that was just positioned
}
};
HTML FILE
<!DOCTYPE html>
<html>
<head>
<title>Taro's World</title>
<style>
body {
margin: 0;
padding: 0;
border: 0;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="mrdoob-three.js-d6384d2/build/Three.js"></script>
<script src="mrdoob-three.js-d6384d2/examples/js/renderers/Projector.js"></script>
<script src="world.js"></script>
</head>
<body></body>
</html>
Two things are broken in your script :
in your positionThenAdd function, at position.set(...), you wrote somewhere ground.height. ground is an array, you probably meant varMesh[mesh].geometry.parameters.height.
your console should print that positionThenAdd is not a function. While you declared previous functions writing function myFunction(){....} you declared this one that way : var positionThenAdd = function () { ... };. The difference in javascript is that, as any variable, positionThenAdd will then be reachable in the script order. Since you write it at the end, nothing can reach it. You just have to modify its declaration to function positionThenAdd(){...}. See var functionName = function() {} vs function functionName() {}
Your scene : http://jsfiddle.net/ba8vvkyg/1/

How can I change the position of my CSG object before subtraction when usingThreejs and ThreeCSG

I'm trying to subtract "holes" in platforms using ThreeCSG. I want the holes to be subtracted in specific locations on the larger platform.
var geometry = new THREE.CubeGeometry( 500, 10, 500 );
var hole_geometry = new THREE.CubeGeometry( 50, 11, 50 );
var material = Physijs.createMaterial( new THREE.MeshLambertMaterial( { color: 0xEEEEEE } ), 0.2, 0.8 );
var hole_material = Physijs.createMaterial( new THREE.MeshLambertMaterial( { color: 0x000000, side: THREE.DoubleSide } ), 0.2, 0.8 );
var platform = { platform: null, hole: null };
// platform
platform.platform = new Physijs.BoxMesh(geometry, material, 0);
platform.platform.position.y = -i*300;
var platformBSP = new ThreeBSP( platform.platform );
// hole
platform.hole = new Physijs.BoxMesh(hole_geometry, hole_material, 0);
platform.hole.position.y = -i*300;
platform.hole.position.x = Math.floor(Math.random()*(251))*(Math.random() < 0.5 ? -1 : 1);
platform.hole.position.z = Math.floor(Math.random()*(251))*(Math.random() < 0.5 ? -1 : 1);
var holeBSP = new ThreeBSP( platform.hole );
platformBSP = platformBSP.subtract(holeBSP);
platform.platform = platformBSP.toMesh(material);
platform.platform.position.y = -i*300;
scene.add( platform_array[i].platform );
scene.add( platform_array[i].hole );
My problem is whenever the hole is converted from Threejs to ThreeCSG is doesn't take the position into account so every hole created in a platform is dead center instead of a random place.
I can't seem to find any documentation on how to reposition the "hole" after it is converted to a ThreeCSG object.
The technique used in the ThreeCSG source is to transform the geometry into a mesh, then to translate the mesh, and then to make a BSP from the translated mesh. See the 2nd line here:
var cube_geometry = new THREE.CubeGeometry( 3, 3, 3 );
var cube_mesh = new THREE.Mesh( cube_geometry );
cube_mesh.position.x = -7;
var cube_bsp = new ThreeBSP( cube_mesh );
var sphere_geometry = new THREE.SphereGeometry( 1.8, 32, 32 );
var sphere_mesh = new THREE.Mesh( sphere_geometry );
sphere_mesh.position.x = -7;
var sphere_bsp = new ThreeBSP( sphere_mesh );
var subtract_bsp = cube_bsp.subtract( sphere_bsp );
var result = subtract_bsp.toMesh( new THREE.MeshLambertMaterial({ shading: THREE.SmoothShading, map: THREE.ImageUtils.loadTexture('texture.png') }) );
result.geometry.computeVertexNormals();
scene.add( result );
In order to set your mesh in the correct place before cutting, you can perform a matrix transform on the geometry itself.
platform.platform = new Physijs.BoxMesh(geometry, material, 0);
platform.platform.geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, -i * 300, 0 ) );
It's possible a bug is preventing THREEBSP to use the position attribute (does Physijs mess with this? I have never used it).

Categories