Code viewer for World: Bouncy Balls

// Physijs based World
// Bouncy balls 

 

AB.screenshotStep  = 200;   
  
	// Take screenshot on this step. (All resources should have finished loading.) Default 50.

	

const BALLSIZE = 5 ;

const BALLPOS       = BALLSIZE * 2 ; 		 // x,z start position of balls is random in this interval
const BALLHEIGHT    = BALLSIZE * 10 ; 		 // y start position of balls 

const HEAVYIMPACT   = BALLHEIGHT * 1.2 ;     // heavy impacts have y velocity greater than this 


const GROUNDSIZE    = BALLSIZE * 50;

const startRadius   = BALLSIZE * 30 ;
const maxRadius     = BALLSIZE * 250 ;



 const FILE_ARRAY = [
                "/uploads/starter/earth.1.jpg",
                "/uploads/starter/earth.2.jpg",
                "/uploads/starter/earth.3.jpg",
                "/uploads/starter/earth.4.jpg",
                "/uploads/starter/earth.5.jpg"
                ];

	
	
	const TEXTURE_GROUND 	= '/uploads/starter/rocks.jpg' ;
//	const TEXTURE_GROUND 	= '/uploads/starter/latin.jpg' ;

	const SOUND_COLLISION 	= '/uploads/starter/bounce.mp3' ;
 
 // credit:
 // http://soundbible.com/1343-Jump.html
 
 
const SKYCOLOR  = 0xffffcc;    

const LOW_FRICTION 	    = 0.1; 		
const MEDIUM_FRICTION 	= 0.5; 		
const HIGH_FRICTION 	= 0.95; 		
	 
const LOW_RESTITUTION   	= 0.1; 		 
const MEDIUM_RESTITUTION 	= 0.5; 		 
const HIGH_RESTITUTION 	    = 0.95; 		 





function randomfloatAtoB ( A, B )			 
{
 return ( A + ( Math.random() * (B-A) ) );
}

function randomintAtoB ( A, B )			 
{
 return  ( Math.round ( randomfloatAtoB ( A, B ) ) );
}





 	
function World() { 

	var textureArray;
	

function initScene()
{

	// --- Light ------------------------------------------------------------------

	var light = new THREE.DirectionalLight( 0xFFFFFF, 1.3 );
	
	// close to origin, high up, works best for shadows
	light.position.set ( BALLHEIGHT, (BALLHEIGHT * 2), 0 );
	light.target.position.copy( threeworld.scene.position );

	light.castShadow = true;
		
	// how far away to draw shadows:
	light.shadow.camera.left    = -GROUNDSIZE;
	light.shadow.camera.right   =  GROUNDSIZE;
	light.shadow.camera.bottom  = -GROUNDSIZE;
	light.shadow.camera.top     =  GROUNDSIZE;

    // higher quality shadows at expense of computation time:
	light.shadow.mapSize.width  = 4096;
	light.shadow.mapSize.height = 4096;
	light.shadow.bias = -0.0001;

	threeworld.scene.add( light );

	


	// --- Ground ------------------------------------------------------------------

	var  ground_material, ground;

	var loader = new THREE.TextureLoader();

	ground_material = Physijs.createMaterial(
	    // new THREE.MeshBasicMaterial( { color: 0xdddddd  } ),
		new THREE.MeshLambertMaterial({  map: loader.load( TEXTURE_GROUND ) }),
		LOW_FRICTION,
		HIGH_RESTITUTION );
	
	ground_material.map.wrapS = THREE.RepeatWrapping;
	ground_material.map.wrapT = THREE.RepeatWrapping;
	ground_material.map.repeat.set( 3, 3 );
	
	
// ground as plane allows bounces but seems to be infinite plane

    ground = new Physijs.PlaneMesh (
        new THREE.PlaneGeometry ( GROUNDSIZE, GROUNDSIZE ),
        ground_material );
        
    ground.rotation.x = (Math.PI / 2) * 3;

/*
// ground as box does not seem to allow bounces

	ground = new Physijs.BoxMesh (
		new THREE.BoxGeometry ( GROUNDSIZE, 1, GROUNDSIZE ),
		ground_material, 
		0 );				 
*/

	ground.receiveShadow = true;
	threeworld.scene.add( ground );



	// --- first ball ------------------------------------------------------------------

    textureArray = [
        ( new THREE.ImageUtils.loadTexture( FILE_ARRAY[0] ) ),
        ( new THREE.ImageUtils.loadTexture( FILE_ARRAY[1] ) ),
        ( new THREE.ImageUtils.loadTexture( FILE_ARRAY[2] ) ),
        ( new THREE.ImageUtils.loadTexture( FILE_ARRAY[3] ) ),
        ( new THREE.ImageUtils.loadTexture( FILE_ARRAY[4] ) )
        ];

	createBall();
}

 
function randomElementOfArray ( a )
{
    var i = randomintAtoB ( 0, a.length - 1 );     
    return ( a[i] );
}
 
 
function createBall() 	 
{
  // (subtle bug) apparently each ball needs its own material if it is to bounce separately
 
    var ball_material =    Physijs.createMaterial (
            new THREE.MeshLambertMaterial({ map:  randomElementOfArray(textureArray)  }),  
            LOW_FRICTION,
		    HIGH_RESTITUTION );
	
    var ball = new Physijs.SphereMesh (
        new THREE.SphereGeometry( BALLSIZE, 20, 20 ),
        ball_material,
        1 );                  // mass
        
    
	ball.collisions = 0;
	ball.castShadow = true;
				
	ball.position.set(  randomintAtoB(-BALLPOS,BALLPOS),		BALLHEIGHT,		randomintAtoB(-BALLPOS,BALLPOS)	);
				
	ball.addEventListener( 'collision', function( other_object, relative_velocity, relative_rotation, contact_normal ) 
	{
		var mainImpact = relative_velocity.y;		// impact in direction of gravity 
	//	console.log ( Math.abs(mainImpact) );
 		if (  Math.abs(mainImpact) > HEAVYIMPACT )			// main impact, not lesser ones as it settles  
		{
			soundCollision();
		}
	});
	
	
	threeworld.scene.add( ball );
	
	setTimeout( createBall, randomintAtoB ( 200, 1000 ) );		 

}



	
function soundCollision()
{
		var x = "<audio    src=" + SOUND_COLLISION + "   autoplay  > </audio>";
		$("#user_span2").html( x );
}





// --- public interface ----------------------------------------------------------------------


	this.endCondition;	
	

	
this.newRun = function() 
{
  this.endCondition = false;

  threeworld.init3d ( startRadius, maxRadius, SKYCOLOR  ); 

			// sets up renderer, scene, camera
		
 
 // can adjust renderer:
 
	threeworld.renderer.shadowMap.enabled = true;
 

 // scene is Physijs.Scene, not THREE.Scene
 // can adjust scene - change gravity from default:
 
		threeworld.scene.setGravity ( new THREE.Vector3( 0, -50, 0 ));

	  
	initScene();

	threeworld.lookat.copy ( threeworld.scene.position );  
	 
	threeworld.scene.simulate();		// Physics simulate - runs on independent timer to AB nextStep

};



this.nextStep = function()
{
};


this.endRun = function()
{
};

 
}