coronaindia
Version:
COVID19 India Tracker, predictive analytics and research. Built by engineers, data scientists, AI and medical professionals.
386 lines (329 loc) • 12.2 kB
JavaScript
var bgWidth = 80;
var bgHeight = 160;
class Robot {
constructor() {
//this.windowWidth = window.innerWidth;
//this.windowHeight = window.innerHeight;
this.windowWidth = 80;
this.windowHeight = 160;
this.animation = {
flyingHeight: 0.4,
flyingFreq: 0.015,
eyeAmplitude: 2,
eyelidAmplitude: 1,
modelsAmplitude: .3,
reactionTime: .10,
speed: .1,
eyelidsOpening: 7,
flying: true };
// BINDINGS
//////////////////////////////////////////
this.onWindowResize = this.onWindowResize.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.render = this.render.bind(this);
// EVENTS
//////////////////////////////////////////
window.addEventListener('resize', this.onWindowResize);
window.addEventListener('mousemove', this.onMouseMove);
this.init();
}
/**
* Init functions.
*/
init() {
this.scene = new THREE.Scene();
this.mouse = new THREE.Vector2(window.innerWidth, window.innerWidth);
this.newRenderer();
this.newCamera();
this.newLight();
this.newGround();
this.newRobot();
this.newGui();
this.render();
}
/**
* New renderer.
*/
newRenderer() {
this.renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
this.renderer.setSize(this.windowWidth, this.windowHeight);
this.renderer.setClearColor(0x9756b2, 0);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
$( ".botAnime" ).append(this.renderer.domElement);
}
/**
* New camera.
*/
newCamera() {
const aspect = this.windowWidth / this.windowHeight;
const fieldOfView = 50;
const near = .1;
const far = 1000;
this.camera = new THREE.PerspectiveCamera(fieldOfView, aspect, near, far);
this.camera.position.set(0, 1, 10);
}
/**
* New lights.
*/
newLight() {
const ambientLight = new THREE.AmbientLight(0xe9e9e9, 1);
ambientLight.position.set(0, 100, 0);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, .1);
directionalLight.position.set(0, 100, 20);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
this.scene.add(directionalLight);
}
/**
* New ground.
*/
newGround() {
// var planeGeometry = new THREE.PlaneBufferGeometry( 45, 45 );
// planeGeometry.rotateX( - Math.PI / 2 );
// var planeMaterial = new THREE.ShadowMaterial();
// planeMaterial.opacity = 0.2;
// var plane = new THREE.Mesh( planeGeometry, planeMaterial );
// plane.position.y = -200;
// plane.receiveShadow = true;
// scene.add( plane );
const geometry = new THREE.PlaneBufferGeometry(45, 45);
//geometry.rotateX( - Math.PI / 2 );
const material = new THREE.ShadowMaterial();
material.opacity = 0.2;
this.ground = new THREE.Mesh(geometry, material);
this.ground.rotation.x = THREE.Math.degToRad(-90);
this.ground.position.y = -7;
this.ground.receiveShadow = true;
this.scene.add(this.ground);
// const geometry = new THREE.PlaneGeometry(45, 45);
// const material = new THREE.MeshLambertMaterial({ color: 0x9555b0, alpha:0}); //color: 0x9555b0
// // .lerpHSL ( color : Color, alpha : Float ) : Color
// // material.transparent = true;
// this.ground = new THREE.Mesh(geometry, material);
// this.ground.rotation.x = THREE.Math.degToRad(-90);
// this.ground.position.y = -7;
// this.ground.receiveShadow = true;
// this.scene.add(this.ground);
}
/**
* Texture Loader.
* @param {int} number of textures
*/
textureLoader(numberOfTextures) {
this.textureLoader = {
loader: new THREE.TextureLoader(),
total: numberOfTextures,
loaded: 0,
loadedComplete: false,
textureLoaded() {
this.loaded++;
if (this.loaded == this.total) {
this.loadedComplete = true;
}
console.log(`${this.loaded}/${this.total} texture(s) loaded`);
} };
}
/**
* New robot.
*/
newRobot() {
const url = {
texture1: 'https://elcomrades.github.io/coronaindia/views/robot/face.jpg',
texture2: 'https://elcomrades.github.io/coronaindia/views/robot/texture.jpg',
object: 'https://elcomrades.github.io/coronaindia/views/robot/boule.DAE'
// texture1: '/views/robot/face.jpg',
// texture2: '/views/robot/texture.jpg',
// object: '/views/robot/boule.DAE'
};
const loader = new THREE.ColladaLoader();
loader.load(url.object, collada => {
// Init texture rendering
this.dataTexture = [url.texture1, url.texture2];
this.textureLoader(this.dataTexture.length);
// Robot model
this.models = collada.scene;
this.models.rotation.y = THREE.Math.degToRad(-90); // Rotate robot in front direction
// Set a pivot point
this.mesh = new THREE.Object3D();
this.box = new THREE.Box3().setFromObject(this.models);
this.box.center(this.models.position);
this.models.position.multiplyScalar(-1);
this.mesh.add(this.models);
this.scene.add(this.mesh);
// Eye
this.eye = this.models.getObjectByName("Eye", true);
// Eyelid
this.eyelidTop = this.models.getObjectByName("Eyelid-top", true);
this.eyelidBottom = this.models.getObjectByName("Eyelid-bottom", true);
this.eyelidTop.rotation.x = THREE.Math.degToRad(-88); // -65 = Open; -88 = closed;
this.eyelidBottom.rotation.x = THREE.Math.degToRad(-85); // -115= Open; -85 = closed;
// Lens
const lens = this.models.getObjectByName("Lens", true);
lens.material.map = this.textureLoader.loader.load(this.dataTexture[0], this.textureLoader.textureLoaded());
lens.material.map.minFilter = THREE.LinearFilter;
const pointLight = new THREE.PointLight(0xffffff, 0.5, 1);
pointLight.position.z = 30;
lens.add(pointLight);
const tl = new TimelineMax({ repeat: -1, yoyo: true });
tl.from(pointLight, 1, { intensity: 5 });
// Body
this.body = this.models.getObjectByName("Body", true);
this.body.children[0].material.map = this.textureLoader.loader.load(this.dataTexture[1], this.textureLoader.textureLoaded());
this.body.children[0].material.map.minFilter = THREE.LinearFilter;
this.body.children[3].castShadow = true;
this.camera.lookAt(this.models.position);
this.parameters = {
lunchIntro: true,
introComplete: false,
coef: 0,
height: this.models.position.y };
// Remove loading message
TweenMax.to(document.getElementById('loading'), .5, { opacity: 0 });
});
}
/**
* animation: Introduction.
*/
introAnimation() {
const delayTurnOff = 1000;
const delayTurnOn = 3000;
TweenMax.set(this.mesh.position, { y: 15 });
TweenMax.set(this.mesh.rotation, { y: THREE.Math.degToRad(720), z: THREE.Math.degToRad(720) });
setTimeout(() => {
this.turnOffAnimation();
}, delayTurnOff);
setTimeout(() => {
this.turnOnAnimation();
}, delayTurnOn);
}
/**
* animation: Turn off.
*/
turnOffAnimation() {
TweenMax.to(this.mesh.position, 1.5, { y: this.ground.position.y + this.box.max.y / 2, ease: Bounce.easeOut });
TweenMax.to(this.mesh.rotation, 2, {
x: THREE.Math.degToRad(getRandom(-20, 0)),
y: THREE.Math.degToRad(getRandom(-30, 30)),
z: THREE.Math.degToRad(getRandom(-20, 20)),
ease: Power2.easeOut });
}
/**
* animation: Turn on.
*/
turnOnAnimation() {
TweenMax.to(this.mesh.position, 1.5, { y: this.animation.flyingHeight, ease: Power2.easeOut });
TweenMax.to(this.mesh.rotation, 1, { x: 0, y: 0, z: 0, ease: Power2.easeOut });
TweenMax.to(this.eyelidTop.rotation, .5, { x: THREE.Math.degToRad(-75), ease: Power2.easeOut }, 1.5);
TweenMax.to(this.eyelidBottom.rotation, .5, { x: THREE.Math.degToRad(-100), ease: Power2.easeOut, onStart: () => {
this.parameters.introComplete = true;
} }, 1.5);
}
/**
* animation: Flying effect.
*/
flyingAnimation() {
this.parameters.coef += this.animation.flyingFreq;
const c = Math.sin(Math.PI * this.parameters.coef);
this.eye.position.y = this.animation.eyeAmplitude * c;
this.models.position.y = c * this.animation.modelsAmplitude + this.parameters.height;
this.eyelidTop.position.y = this.eyelidBottom.position.y = this.animation.eyelidAmplitude * c;
}
/**
* animation: Standby.
*/
standbyAnimation(state) {
if (!state) {
this.turnOffAnimation();
TweenMax.to(this.models.position, .5, { y: this.parameters.height }); // prevent ground collision
} else if (state) {
this.turnOnAnimation();
}
}
/**
* animation: Mouse interaction.
*/
mouseAnimation() {
TweenMax.to(this.eye.rotation, this.animation.speed, { x: this.mouse.y / 5, y: this.mouse.x / 3, delay: this.animation.reactionTime });
TweenMax.to(this.mesh.rotation, this.animation.speed, { y: this.mouse.x / 2, x: -(this.mouse.y / 5), delay: this.animation.reactionTime });
TweenMax.to(this.eyelidTop.rotation, this.animation.speed, { y: this.mouse.x / 4, x: THREE.Math.degToRad(-75 + this.animation.eyelidsOpening) + (this.mouse.y - Math.abs(this.mouse.x)) / 10, delay: this.animation.reactionTime });
TweenMax.to(this.eyelidBottom.rotation, this.animation.speed, { y: this.mouse.x / 4, x: THREE.Math.degToRad(-100 - this.animation.eyelidsOpening) + (this.mouse.y + Math.abs(this.mouse.x)) / 10, delay: this.animation.reactionTime });
}
/**
* Update robot, only when textures and 3D models are loaded.
*/
updateRobot() {
if (this.models !== undefined && this.textureLoader.loadedComplete === true) {
// Introduction
if (this.parameters.lunchIntro) {
this.introAnimation();
this.parameters.lunchIntro = false;
}
// Flying
if (this.parameters.introComplete && this.animation.flying) {
this.flyingAnimation();
}
}
}
/**
* Mouse update.
*/
mouseUpdate() {
if (this.parameters.introComplete) {
this.mouseAnimation();
}
}
/**
* EVENT: On mouse move, set the mouse position and update mouseUpdate.
*/
onMouseMove(event) {
this.mouse.x = event.clientX / window.innerWidth * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
this.mouseUpdate();
}
/**
* EVENT: On window resize, update parameters.
*/
onWindowResize() {
this.windowWidth = bgWidth; //window.innerWidth;
this.windowHeight = bgHeight; //window.innerHeight;
this.renderer.setSize(this.windowWidth, this.windowHeight);
this.camera.aspect = this.windowWidth / this.windowHeight;
this.camera.updateProjectionMatrix();
}
/**
* dat.GUI, display animation parameters.
*/
newGui() {
const gui = new dat.GUI({ width: 270 });
const f1 = gui.addFolder('Flying animation');
f1.add(this.animation, 'flyingFreq', .01, .05).name('frequency');
f1.add(this.animation, 'modelsAmplitude', 0, 1).name('robot amplitude');
f1.add(this.animation, 'eyelidAmplitude', 0, 5).name('eyelid amplitude');
f1.add(this.animation, 'eyeAmplitude', 0, 5).name('eye amplitude');
f1.add(this.animation, 'flying').onChange(e => {
this.standbyAnimation(e);
});
const f2 = gui.addFolder('Mouse interaction');
f2.add(this.animation, 'reactionTime', 0, .5).name('reaction time');
f2.add(this.animation, 'speed', .1, 1).name('slowness');
f2.add(this.animation, 'eyelidsOpening', 0, 10).name('eyelids opening');
}
/**
* Render.
*/
render() {
this.updateRobot();
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.render);
}}
/**
* Get random.
* @param {int} min
* @param {int} max
* @return {int} random number
*/
const getRandom = (min, max) => Math.random() * (max - min + 1) + min;
window.onload = new Robot();