/**
* @module
*/
define([
'three',
'lodash',
'input/Key',
], function(THREE, _, KeyInput) {
'use strict';
/**
* @constructor
* @param {module:model/Game} model The game model that the view should show
* on the screen.
* @this {module:view/Game}
*/
var exports = function(model) {
this.model = model;
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.1, 1000
);
this.camera.position.set(0, 0, 20);
var canvas = document.getElementById('canvas');
this.renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMapEnabled = true;
window.addEventListener('resize', this.__onWindowResize.bind(this), false);
var floorTexturePath = 'img/wood1.png';
var wallTexturePath = 'img/wood2.png';
var obstacleTexturePath = 'img/galvanizedBlue.jpg';
this.labyrinthGroup = new THREE.Object3D();
this.__initLabyrinthContainer(
this.labyrinthGroup, floorTexturePath, wallTexturePath
);
this.__initBall(this.labyrinthGroup);
this.__initWalls(this.labyrinthGroup, wallTexturePath);
this.__initExitPath(this.labyrinthGroup);
this.__initObstacles(this.labyrinthGroup, obstacleTexturePath);
this.__initLights(this.scene);
this.scene.add(this.labyrinthGroup);
this.keyInput = new KeyInput();
};
/**
* Update the camera and renderer after the browser's window size has
* changed.
*
* @private
* @this {module:view/Game}
*/
exports.prototype.__onWindowResize = function() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
};
/**
* Add the labyrinth container's 4 walls and floor.
*
* @private
* @this {module:view/Game}
*/
exports.prototype.__initLabyrinthContainer = function(
group, floorTexturePath, wallTexturePath
) {
var walls = [
{ // Left wall
x: -5.5 * this.model.MULTIPLIER,
y: 0,
z: 0,
width: 1 * this.model.MULTIPLIER,
height: 10 * this.model.MULTIPLIER,
texture: new THREE.ImageUtils.loadTexture(wallTexturePath),
castShadow: true,
},
{ // Right wall
x: 5.5 * this.model.MULTIPLIER,
y: 0,
z: 0,
width: 1 * this.model.MULTIPLIER,
height: 10 * this.model.MULTIPLIER,
texture: new THREE.ImageUtils.loadTexture(wallTexturePath),
castShadow: true,
},
{ // Upper wall
x: 0,
y: 5.5 * this.model.MULTIPLIER,
z: 0,
width: 12 * this.model.MULTIPLIER,
height: 1 * this.model.MULTIPLIER,
texture: new THREE.ImageUtils.loadTexture(wallTexturePath),
castShadow: true,
},
{ // Bottom wall
x: 0,
y: -5.5 * this.model.MULTIPLIER,
z: 0,
width: 12 * this.model.MULTIPLIER,
height: 1 * this.model.MULTIPLIER,
texture: new THREE.ImageUtils.loadTexture(wallTexturePath),
castShadow: true,
},
{ // Floor
x: 0,
y: 0,
z: -1 * this.model.MULTIPLIER,
width: 10 * this.model.MULTIPLIER,
height: 10 * this.model.MULTIPLIER,
texture: new THREE.ImageUtils.loadTexture(floorTexturePath),
castShadow: false,
}
];
_.forEach(walls, function(wall) {
// Create the box gemoetry
var gemoetry = new THREE.BoxGeometry(
wall.width,
wall.height,
this.model.MULTIPLIER
);
// Create the material
var material = new THREE.MeshPhongMaterial({map: wall.texture});
wall.texture.wrapS = wall.texture.wrapT = THREE.RepeatWrapping;
wall.texture.repeat.set(wall.width / 2, wall.height / 2);
// Create the box
var box = new THREE.Mesh(gemoetry, material);
box.position.set(
wall.x,
wall.y,
wall.z
);
box.receiveShadow = true;
box.castShadow = wall.castShadow;
group.add(box);
}.bind(this));
};
/**
* Create the ball from the model and add it to `group`.
*
* @private
* @this {module:view/Game}
* @param {Object3D} group The group that the ball will be added to.
*/
exports.prototype.__initBall = function(group) {
var gemoetry = new THREE.SphereGeometry(
this.model.ball.radius,
32,
32
);
var material = new THREE.MeshPhongMaterial({color: 0xff0000});
this.ball = new THREE.Mesh(gemoetry, material);
this.ball.position.z = -this.model.MULTIPLIER / 2 + this.model.ball.radius;
this.ball.castShadow = true;
this.ball.receiveShadow = true;
group.add(this.ball);
};
/**
* Create the walls from the model and add them to `group`.
*
* @private
* @this {module:view/Game}
* @param {Object3D} group The group that the walls will be added to.
*/
exports.prototype.__initWalls = function(group, wallTexturePath) {
_.forEach(this.model.walls, function(wall) {
var x = wall.body.GetPosition().get_x();
var y = wall.body.GetPosition().get_y();
// Create the box gemoetry
var gemoetry = new THREE.BoxGeometry(
wall.width,
wall.height,
this.model.MULTIPLIER
);
var texture = new THREE.ImageUtils.loadTexture(wallTexturePath);
var material = new THREE.MeshPhongMaterial({map: texture});
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(wall.width / 2, wall.height / 2);
var box = new THREE.Mesh(gemoetry, material);
box.position.set(
x - this.model.MULTIPLIER * 5,
y - this.model.MULTIPLIER * 5,
0
);
box.castShadow = true;
box.receiveShadow = true;
group.add(box);
}.bind(this));
};
/**
* Create the exit path from the model and add it to `group`.
*
* @private
* @this {module:view/Game}
* @param {Object3D} group The group that the exit path will be added to.
*/
exports.prototype.__initExitPath = function(group) {
_.forEach(this.model.exitPath, function(wall) {
var gemoetry = new THREE.BoxGeometry(
this.model.MULTIPLIER,
this.model.MULTIPLIER,
this.model.MULTIPLIER
);
var material = new THREE.MeshPhongMaterial({
color: 0x00ff00,
transparent: true,
opacity: 0.25
});
var cube = new THREE.Mesh(gemoetry, material);
cube.position.set(
wall.x - 5 * this.model.MULTIPLIER,
wall.y - 5 * this.model.MULTIPLIER,
-this.model.MULTIPLIER + 0.01
);
group.add(cube);
}.bind(this));
};
/**
* Create the obstacles from the model and add them to `group`.
*
* @private
* @this {module:view/Game}
* @param {Object3D} group The group that the obstacles will be added to.
* @param {String} obstacleTexturePath Path to the texture that will be used
* for the mesh.
*/
exports.prototype.__initObstacles = function(group, obstacleTexturePath) {
_.forEach(this.model.obstacles, function(obstacle) {
var x = obstacle.body.GetPosition().get_x();
var y = obstacle.body.GetPosition().get_y();
var gemoetry = new THREE.CylinderGeometry(
obstacle.radius,
obstacle.radius,
this.model.MULTIPLIER / 2
);
var texture = new THREE.ImageUtils.loadTexture(obstacleTexturePath);
var material = new THREE.MeshPhongMaterial({map: texture});
var cylinder = new THREE.Mesh(gemoetry, material);
cylinder.position.set(
x - 5 * this.model.MULTIPLIER,
y - 5 * this.model.MULTIPLIER,
-this.model.MULTIPLIER / 4
);
cylinder.rotation.x = Math.PI / 2;
cylinder.castShadow = true;
cylinder.receiveShadow = true;
group.add(cylinder);
}.bind(this));
};
/**
* Create lights and add them to `group`.
*
* @private
* @this {module:view/Game}
* @param {Object3D} group The group that the lights will be added to.
*/
exports.prototype.__initLights = function(group) {
var light = new THREE.DirectionalLight(0xffffff, 1.0);
light.position.set(10, 10, 60);
light.castShadow = true;
//light.shadowCameraVisible = true;
light.shadowCameraLeft = -12;
light.shadowCameraRight = 12;
light.shadowCameraTop = 12;
light.shadowCameraBottom = -12;
light.shadowCameraFar = 100;
light.shadowMapWidth = 1024;
light.shadowMapHeight = 1024;
group.add(light);
light = new THREE.AmbientLight(0x202020);
group.add(light);
};
/**
* Start the render loop function.
*
* @this {module:view/Game}
*/
exports.prototype.render = function() {
requestAnimationFrame(this.render.bind(this));
var bpos = this.model.ball.body.GetPosition();
this.ball.position.x = bpos.get_x() - this.model.MULTIPLIER * 5;
this.ball.position.y = bpos.get_y() - this.model.MULTIPLIER * 5;
if (this.keyInput.isDown(this.keyInput.LEFT)) {
this.model.tiltLeft();
this.labyrinthGroup.rotation.y = -Math.PI / 20;
} else if (this.keyInput.isDown(this.keyInput.RIGHT)) {
this.model.tiltRight();
this.labyrinthGroup.rotation.y = Math.PI / 20;
} else {
this.model.releaseHorizontalTilt();
this.labyrinthGroup.rotation.y = 0;
}
if (this.keyInput.isDown(this.keyInput.UP)) {
this.model.tiltUp();
this.labyrinthGroup.rotation.x = -Math.PI / 20;
} else if (this.keyInput.isDown(this.keyInput.DOWN)) {
this.model.tiltDown();
this.labyrinthGroup.rotation.x = Math.PI / 20;
} else {
this.model.releaseVerticalTilt();
this.labyrinthGroup.rotation.x = 0;
}
this.model.step(1/60);
this.renderer.render(this.scene, this.camera);
};
return exports;
});