<!DOCTYPE html>
<html ng-app="HW4">
<head>
<link rel="stylesheet" type="text/css" href="style.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.7/angular.js" data-semver="1.3.7" data-require="angular.js@*"></script>
<script src="Common/MV.js" type="text/javascript"></script>
<script src="scripts/main.js" type="text/javascript"></script>
<script src="scripts/TextureService.js" type="text/javascript"></script>
<script src="scripts/ShaderService.js" type="text/javascript"></script>
<script src="scripts/CameraService.js" type="text/javascript"></script>
<script src="scripts/LightingService.js" type="text/javascript"></script>
<script src="scripts/AnimalService.js" type="text/javascript"></script>
<script src="scripts/WebGLDirective.js" type="text/javascript"></script>
<script src="scripts/ShapeCreator.js" type="text/javascript"></script>
<script src="scripts/MaterialService.js" type="text/javascript"></script>
</head>
<body ng-keypress="handleKeypress($event)">
<div>
<script id="vertShader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vNormal;
attribute vec2 vTexCoord;
varying vec2 fTexCoord;
varying vec3 fNormal, fPosition;
uniform mat4 viewMatrix, perspectiveMatrix, modelViewMatrix;
void
prepFragShader()
{
fTexCoord = vTexCoord;
fNormal = (modelViewMatrix*vNormal).xyz;
fPosition = (modelViewMatrix*vPosition).xyz;
}
void
main()
{
prepFragShader();
gl_Position = perspectiveMatrix * viewMatrix * modelViewMatrix * vPosition;
}
</script>
<script id="fragShader" type="x-shader/x-fragment">
precision mediump float;
const int MAX_POINT_LIGHTS = 10;
const int MAX_SPOT_LIGHTS = 10;
const int RENDER_TYPE_BOTH = 2;
const int RENDER_TYPE_LIGHTING = 0;
const int RENDER_TYPE_TEXTURE = 1;
varying vec2 fTexCoord;
varying vec3 fNormal;
varying vec3 fPosition;
struct BaseLight
{
vec3 Color;
float AmbientIntensity;
float DiffuseIntensity;
bool Power;
};
struct DirectionalLight
{
BaseLight Base;
vec3 Direction;
};
struct Attenuation
{
float Constant;
float Linear;
float Exponential;
};
struct PointLight
{
BaseLight Base;
vec3 Position;
Attenuation Attenuation;
};
struct SpotLight
{
PointLight Base;
vec3 Direction;
float Cutoff;
};
struct Material
{
vec3 Color;
vec3 EmitColor;
float Shininess;
float ShineIntensity;
float EmitIntensity;
};
uniform int pointLightCount;
uniform int spotLightCount;
uniform DirectionalLight directionalLight;
uniform PointLight pointLight[MAX_POINT_LIGHTS];
uniform SpotLight spotLight[MAX_SPOT_LIGHTS];
uniform Material material;
uniform sampler2D textureBank;
uniform vec3 eyePosition;
uniform int renderType;
uniform float textureRepeat;
void CalcLightInternal(in BaseLight light, in vec3 lightDirection, in vec3 normal, out vec4 lightColor)
{
if(!light.Power) { lightColor = vec4(0, 0, 0, 1); return; }
vec4 ambient = vec4(light.Color, 1.0) * light.AmbientIntensity;
float diffuseFactor = dot(normal, -lightDirection) * light.DiffuseIntensity;
vec4 diffuse = vec4(0.0, 0.0, 0.0, 0.0);
vec4 specular = vec4(0.0, 0.0, 0.0, 0.0);
if (diffuseFactor > 0.0)
{
diffuse = vec4(light.Color, 1.0) * diffuseFactor ;
vec3 eyeDirection = normalize(eyePosition - fPosition);
vec3 lightReflect = normalize(reflect(lightDirection, normal));
float specularFactor = pow(dot(eyeDirection, lightReflect), material.Shininess) * material.ShineIntensity;
if (specularFactor > 0.0)
{
specular = vec4(light.Color, 1.0) * specularFactor;
}
}
lightColor = ambient + diffuse + specular;
}
void CalcDirectionalLight(in vec3 normal, out vec4 lightColor)
{
CalcLightInternal(directionalLight.Base, directionalLight.Direction, normal, lightColor);
}
void CalcPointLight(in PointLight light, in vec3 normal, out vec4 lightColor)
{
vec3 lightDirection = fPosition - light.Position;
float distance = length(lightDirection);
lightDirection = normalize(lightDirection);
CalcLightInternal(light.Base, lightDirection, normal, lightColor);
float attenuation = light.Attenuation.Constant +
light.Attenuation.Linear * distance +
light.Attenuation.Exponential * distance * distance;
lightColor = lightColor / attenuation;
}
void CalcSpotLight(in SpotLight light, in vec3 normal, out vec4 lightColor)
{
vec3 lightDirectionFromPixel = normalize(light.Base.Position - fPosition );
float spotAngle = dot(lightDirectionFromPixel, normalize(light.Direction));
if(spotAngle > light.Cutoff)
{
CalcPointLight(light.Base, normal, lightColor);
lightColor = lightColor * ( 1.0 - (1.0 - spotAngle) * 1.0/(1.0 - light.Cutoff));
}
else
{
lightColor = vec4(0, 0, 0, 1);
}
}
void main()
{
vec3 normal = normalize(fNormal);
vec4 totalLight = vec4(0, 0, 0, 0);
CalcDirectionalLight(normal, totalLight);
for(int i = 0; i < MAX_POINT_LIGHTS; i++)
{
if(i + 1 > pointLightCount) break;
vec4 totalPointLight = vec4(0, 0, 0, 0);
CalcPointLight(pointLight[i], normal, totalPointLight);
totalLight += totalPointLight;
}
for(int i = 0; i < MAX_SPOT_LIGHTS; i++)
{
if(i+1 > spotLightCount) break;
vec4 totalSpotLight = vec4(0, 0, 0, 0);
CalcSpotLight(spotLight[i], normal, totalSpotLight);
totalLight += totalSpotLight;
}
gl_FragColor = vec4(0,0,0,1);//
if(renderType == RENDER_TYPE_LIGHTING || renderType == RENDER_TYPE_BOTH)
gl_FragColor = vec4(material.Color + (material.EmitColor*material.EmitIntensity), 1.0);
if(renderType == RENDER_TYPE_TEXTURE || renderType == RENDER_TYPE_BOTH)
gl_FragColor += texture2D(textureBank, fract(fTexCoord.xy * textureRepeat));
if(renderType == RENDER_TYPE_LIGHTING || renderType == RENDER_TYPE_BOTH)
gl_FragColor *= totalLight;
}
</script>
</div>
<span ng-include="'partials/applicationControls.html'"></span>
</body>
</html>
(function(){
var module = angular.module("HW4", []);
module.run(function ($rootScope)
{
var self = $rootScope;
/***
* Method to run once the page is loaded
*/
window.onload = function(){
self.MAX_SPEED = 100 ;
self.MIN_SPEED = 0;
self.MIN_LOC = -40;
self.MAX_LOC = 40;
self.position = 0;
};
/***
* Toggles the application variable for speed
*/
self.ChangeSpeed = function(newSpeed)
{
if(newSpeed > self.MAX_SPEED) newSpeed = self.MAX_SPEED;
else if(newSpeed < self.MIN_SPEED) newSpeed = self.MIN_SPEED;
self.speed = newSpeed;
};
/***
* Change the rotation of the dog in the scene. Unfortunately I couldn't get it to change around
* his center, so it rotates around the starting position
*/
self.ChangeRotation = function(rotation)
{
self.rotation = rotation%360;
self.position = 0;
};
/***
* Resets the position when the Reset button is clicked
*/
self.ResetAnimation = function()
{
self.position = 0;
};
/***
* Handles application keypresses in the application. The Application controls watch for changes in these
* variables and update the UI when user clicks the key Automagically
*/
self.handleKeypress = function($event)
{
var keyCode = $event.which;
switch(keyCode)
{
case 114:
self.ResetAnimation();
break;
}
};
});
}());
/* Styles go here */
table, td {
border: 1px solid black;
border-collapse: collapse;
padding-left: 5px;
padding-right: 5px;
white-space: nowrap;
}
3D Scene
==========
This is a culmination of everything I learned about WebGL in Computer Graphics(CS4600) at the U of U. It was developed using the AngularJS framework;
Learning AngularJS was an effort I took on my own, not required by the class. There are examples of using Directives and Services in this application.
What is WebGL
--------------------
WebGL is an implementation of OpenGL that interfaces with a systems 3D hardware, such as a graphics card. WebGL uses a combination of
GLSL(OpenGL shader code) and JavaScript to create 3D scenes that can be viewed in real time. Shader code(GLSL) is code that is prewritten and
compiled/linked at runtime. It is usually(and especially in this case) found in <script> tags in the index.html file
What is included here
---------------------
The application is built using the AngularJS Framework. The main application is found in index.html, and all the controls are found in partials/applicationControls.html
The image is the first control on the page; Followed by camera controls:
Look At - The point the camera is looking at
Eye Point - Where the camera is located
There is technically 3 lights in the scene:
Directional Light - Light similar to sun light. It's everywhere in the scene with a direction but typically very low power
Point Light - A light with a location that sends light in all direction from that point. This light has the ability of fading as the distance from the light gets bigger
In the case of this scene, the point light is simply the light that creates the sun effect
Spot Light - A point light that creates a cone of light rather then sends light in every direction.
But I've also included 5 additional lights that appear depending on the scenes settings... You'll just have to play around to see them :)
Controls have been given for each light; Position and Direction is obvious:
Intensity - Strength of the light
ALL SHADER CODE is found in index.html. There is quite a bit there so be sure you understand it if you are going to use try something else with it
Orginization
------------
You'll find most of the functionality of the code in the WebGLDirective.js file. This is a angular directive that defines a canvas using WebGL and the shader code.
Objects included are:
Dog - AnimalService.js
Camera - CameraService.js
Lighting: Directional, Point, and Spot lights - LightingService.js
Materials: There's a bunch, and you can make your own using the CreateMaterial call - MaterialService.js
Compiled Shaders - ShaderService.js
Shapes: Spheres, Tetrahydran, Cubes, Planes - ShapeCreator.js
Textures: All the textures that are in the scene - TextureService.js
<!DOCTYPE html>
<div>
<div gl-canvas id="hw3-canvas"
speed="speed" height="512" width="512"
eye="eye" lookat="lookat" render-type="renderType" ambient-intensity="ambientIntensity" sun-intensity="sunIntensity"
light-falloff="lightFalloff" spot-angle="spotAngle" light-pos="lightPos" light-dir="lightDir"
ambient-power="ambientPower=='true'" party-dog="partyDog=='true'" lighthouse="lighthouse=='true'"></div>
<div>
<button ng-mouseup="ResetAnimation()">Reset Animation</button>
<select ng-model="renderType" ng-init="renderType=2">
<option value="0">Just Lighting</option>
<option value="1">Just Textures</option>
<option value="2">Both Lighting and Textures</option>
</select>
</div>
<div>
<p>
<label>Animation Speed:</label>
<input type="range" min={{MIN_SPEED}} max={{MAX_SPEED}} ng-change="ChangeSpeed(speed)" ng-init="ChangeSpeed(30)" ng-model="speed" />
<label ng-bind="speed"></label>
<br/>
<label>Animal Rotation:</label>
<input type="range" min=0 max=360 ng-change="ChangeRotation(rotation)" ng-init="ChangeRotation(90)" ng-model="rotation" />
<label ng-bind="rotation"></label>
<br/>
<table>
<tr>
<td>
<lable>Directional Light Intensity</lable>
<br/>
<input type="range" min=10 max=100 ng-init='ambientIntensity = 50' ng-model="ambientIntensity" />
<label ng-bind="ambientIntensity"></label>
</td>
<td>
<lable>Sun Light Intensity</lable>
<br/>
<input type="range" min=1 max=100 ng-init='sunIntensity = 10' ng-model="sunIntensity" />
<label ng-bind="sunIntensity"></label>
</td>
<td>
<label>Angle of Spotlight</label>
<br/>
<input type="range" min=0 max=180 ng-init='spotAngle = 90' ng-model="spotAngle" />
<label ng-bind="spotAngle"></label>
</td>
<td>
<label>Spotlight Falloff</label>
<br/>
<input type="range" min=0 max=100 ng-init='lightFalloff = 0' ng-model="lightFalloff" />
<label ng-bind="lightFalloff"></label>
</td>
</tr>
<tr>
<td>
<div>
<label>Lookat Point:</label>
<br/>
<label>X</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-init="lookat = [0, 0, 0]" ng-model="lookat[0]" />
<label ng-bind="lookat[0]>0 && lookat[0]<1 ? 0 : lookat[0]"></label>
<br/>
<label>Y</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="lookat[1]" />
<label ng-bind="lookat[1]>0 && lookat[1]<1 ? 0 : lookat[1]"></label>
<br/>
<label>Z</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="lookat[2]" />
<label ng-bind="lookat[2]>0 && lookat[2]<1 ? 0 : lookat[2]"></label>
</div>
</td>
<td>
<div>
<label>Eye Point:</label>
<br/>
<label>X</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-init='eye = [5, 3, 15]' ng-model="eye[0]" />
<label ng-bind="eye[0]>0 && eye[0]<1 ? 0 : eye[0]"></label>
<br/>
<label>Y</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="eye[1]" />
<label ng-bind="eye[1]>0 && eye[1]<1 ? 0 : eye[1]"></label>
<br/>
<label>Z</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="eye[2]" />
<label ng-bind="eye[2]>0 && eye[2]<1 ? 0 : eye[2]"></label>
</div>
</td>
<td>
<label>Light Position</label>
<br/>
<label>X</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-init='lightPos = [0, 5, 0]' ng-model="lightPos[0]" />
<label ng-bind="lightPos[0]>0 && lightPos[0]<1 ? 0 : lightPos[0]"></label>
<br/>
<label>Y</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="lightPos[1]" />
<label ng-bind="lightPos[1]>0 && lightPos[1]<1 ? 0 : lightPos[1]"></label>
<br/>
<label>Z</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="lightPos[2]" />
<label ng-bind="lightPos[2]>0 && lightPos[2]<1 ? 0 : lightPos[2]"></label>
</td>
<td>
<label>Light Direction</label>
<br/>
<label>X</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-init='lightDir = [0, 1, 0]' ng-model="lightDir[0]" />
<label ng-bind="lightDir[0]>0 && lightDir[0]<1 ? 0 : lightDir[0]"></label>
<br/>
<label>Y</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="lightDir[1]" />
<label ng-bind="lightDir[1]>0 && lightDir[1]<1 ? 0 : lightDir[1]"></label>
<br/>
<label>Z</label>
<input type="range" min={{MIN_LOC}} max={{MAX_LOC}} ng-model="lightDir[2]" />
<label ng-bind="lightDir[2]>0 && lightDir[2]<1 ? 0 : lightDir[2]"></label>
</td>
</tr>
<tr>
<td>
<form>
<label>Directional Light</label><br/>
<input type="radio" ng-model="ambientPower" ng-init="ambientPower='true'" value="true" >On<br/>
<input type="radio" ng-model="ambientPower" value="false">Off<br/>
</form>
</td>
<td>
<form>
<label>Party Dog</label><br/>
<input type="radio" ng-model="partyDog" ng-init="partyDog='false'" value="true" >On<br/>
<input type="radio" ng-model="partyDog" value="false">Off<br/>
</form>
</td>
<td>
<form>
<label>Lighthouse</label><br/>
<input type="radio" ng-model="lighthouse" ng-init="lighthouse='false'" value="true" >On<br/>
<input type="radio" ng-model="lighthouse" value="false">Off<br/>
</form>
</td>
</tr>
</table>
</div>
<audio id="partyDogMusic" loop="true">
<source src="https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/party.mp3" type="audio/ogg">
Your browser does not support the audio element.
</audio>
</div>
angular.module('HW4').directive('glCanvas',
['ShaderService','LightingService', 'MaterialService', 'ShapeCreator', 'CameraService','AnimalService', 'TextureService','$interval',
function glCanvas(ShaderService, LightingService, MaterialService, ShapeCreator, CameraService, AnimalService, TextureService, $interval)
{
//Scope contains the keywords that can be used in the html tag gl-canvas
return {
restrict: 'A',
scope: {
'width': '=', 'height': '=',
'speed': '=', 'eye': '=', 'lookat': '=',
'renderType': '=renderType',
'lightFalloff': '=lightFalloff',
'lightPos': '=lightPos',
'lightDir': '=lightDir',
'spotAngle': '=spotAngle',
'ambientPower': '=ambientPower',
'partyDog': '=partyDog',
'lighthouse': '=lighthouse',
'flashlight': '=',
'ambientIntensity': '=ambientIntensity',
'sunIntensity': '=sunIntensity'
},
link: function postLink(scope, element, attrs)
{
var self = scope;
var gl, shaders, animationTimer, dog, camera,
ground, sky, sun, tetrahedron, cube, directionalLight;
var spotLights = [], pointLights = [];
var lighthouseTheta = 0, partyDogPhi= 0, partyDogTheta= 0;
var partyDogMusic = document.getElementById("partyDogMusic");
/***
* Constructor - Constructs this directive that manages the WebGL canvas
*/
self.init = function()
{
//Create the canvas element and apply HTML details
var canvas = document.createElement( 'canvas' );
canvas.width = self.width;
canvas.height = self.height;
canvas.style.border = "5px solid black";
//Initialize canvas with WebGL. Set Viewport and default bg color; enable Depth test
gl = self.initWebGL( canvas );
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
gl.enable(gl.DEPTH_TEST);
// Load shaders
shaders = ShaderService.initShaders( gl, "vertShader", "fragShader");
gl.useProgram( shaders );
//Create the camera
camera = CameraService.CreateCamera();
//Ambient light
directionalLight = LightingService.CreateDirectionalLight({
Color: vec3(0.1,0.1,0.1),
AmbientIntensity: 1,
DiffuseIntensity: 1});
//Sun light
pointLights.push(LightingService.CreatePointLight({
Color: vec3(1,1,1),
Position: vec3(0, 75, 150),
Attenuation: vec3(1,0,0.001)}));
//Party Dog flashing light
pointLights.push(LightingService.CreatePointLight({
Color: vec3(1,1,1),
Position: vec3(0, 0, 0),
Attenuation: vec3(1,0,0.1)
}));
//Main light
spotLights.push(LightingService.CreateSpotLight({
Color: vec3(1,1,1),
AmbientIntensity:1,
DiffuseIntensity: 0.1}));
//Lighthouse light
spotLights.push(LightingService.CreateSpotLight({
Position: vec3(5, 8, -10),
Cutoff: 65,
Color: vec3(1,1,1),
AmbientIntensity:1,
DiffuseIntensity: 0.1,
Attenuation: vec3(1,0,0.01)}));
//Three party dog lights
spotLights.push(LightingService.CreateSpotLight({
Cutoff: 40,
Color: vec3(1,0,0),
Direction: vec3(0,1,0),
AmbientIntensity:1,
DiffuseIntensity: 0.1,
Attenuation: vec3(1,0.5,0.1)}));
spotLights.push(LightingService.CreateSpotLight({
Cutoff: 40,
Color: vec3(0,1,0),
Direction: vec3(0,1,0),
AmbientIntensity:1,
DiffuseIntensity: 0.1,
Attenuation: vec3(1,0,0.1)}));
spotLights.push(LightingService.CreateSpotLight({
Cutoff: 40,
Color: vec3(0,0,1),
Direction: vec3(0,1,0),
AmbientIntensity:1,
DiffuseIntensity: 0.1,
Attenuation: vec3(1,0,0.1)}));
//Create the Grass
ground = ShapeCreator.CreatePlane({
texture: TextureService.CreateTexture(
{filename: [TextureService.Tile()], repeat: 100}),
shift: vec3(0,-1.25,0),
scale: vec3(300,1,300),
material: MaterialService.CreateObsidian()});
ground.initBuffers(gl, shaders);
//Create the sky
sky = ShapeCreator.CreateSphere({
texture: TextureService.CreateTexture(
{filename: [TextureService.Sky()]}),
scale: vec3(300,300,300),
material: MaterialService.CreateSky()});
sky.initBuffers(gl, shaders);
//Create the sun
sun = ShapeCreator.CreateSphere({
texture: TextureService.CreateTexture(
{filename: [TextureService.Sun()]}),
shift: vec3(0,75,150),
scale: vec3(50,50,50),
material: MaterialService.CreateSun()});
sun.initBuffers(gl, shaders);
//Creates a tetrahedron with 3 textures
tetrahedron = ShapeCreator.CreateTetrahedron({
texture: TextureService.CreateTexture(
{filename: [TextureService.Broccoli(), TextureService.Gravity(), TextureService.Face()]}),
shift: vec3(5, 3.75, -10),
scale: vec3(10, 10, 10),
material: MaterialService.CreateChrome()});
tetrahedron.initBuffers(gl, shaders);
//Create the dog
dog = AnimalService.CreateDog(gl, shaders);
cube = ShapeCreator.CreateCube({
texture: TextureService.CreateTexture({filename: [TextureService.Thwomp()]}),
shift: vec3(0,7,0),
scale: vec3(2, 2, 2),
material: MaterialService.CreateRuby()});
cube.initBuffers(gl, shaders);
//Add canvas to the HTML and then render
element[0].appendChild( canvas );
//Start the animation
animationTimer = $interval(self.render, 20);
};
/***
* This method makes appropriate changes for the camera
*/
self.adjustCamera = function()
{
self.eye = self.fixVector(self.eye);
self.lookat = self.fixVector(self.lookat);
camera.Position(self.eye);
camera.Lookat(self.lookat);
};
/***
* This is to prevent normalizing a 0,0,0 vector; Basically it just sets the values to very close to 0
*/
self.fixVector = function(v)
{
return v[0] === v[1] && v[0] === v[2] && v[0] === 0 ? vec3(0.001, 0.001, 0.001) : v;
};
/***
* This method makes appropriate changes for the lights in the scene
*/
self.adjustLights = function()
{
//Turn on power for the ambient lights
directionalLight.Power(self.ambientPower);
directionalLight.AmbientIntensity(self.ambientIntensity/10);
//Set sunlight intensity
pointLights[0].DiffuseIntensity(self.sunIntensity/10);
//Set Main light to application controlled settings
spotLights[0].Position(self.lightPos);
spotLights[0].Direction(self.lightDir);
spotLights[0].Cutoff(self.spotAngle/2);
spotLights[0].Attenuation(vec3(1, 0, self.lightFalloff/100));
//Lighthouse Controls and effects
spotLights[1].Power(self.lighthouse);
spotLights[1].Direction(vec3(Math.sin(lighthouseTheta+=0.01),0.3, Math.cos(lighthouseTheta)));
//Party Dog controls. Basically rotates the red, blue and green lights if the power is on
if(self.partyDog) partyDogMusic.play();
else partyDogMusic.pause();
pointLights[1].Power(self.partyDog && !pointLights[1].Power());
spotLights[2].Power(self.partyDog);
spotLights[2].Position(vec3(
3*Math.sin(partyDogPhi+=0.1)*Math.cos(partyDogTheta+=0.1),
2 + 3*Math.sin(partyDogPhi)*Math.sin(partyDogTheta),
3*Math.cos(partyDogPhi)));
spotLights[3].Power(self.partyDog);
spotLights[3].Position(vec3(3+3*Math.sin(-partyDogPhi)*Math.cos(partyDogTheta),
1 + 3*Math.sin(-partyDogPhi)*Math.sin(partyDogTheta),
3*Math.cos(-partyDogPhi)));
spotLights[4].Power(self.partyDog);
spotLights[4].Position(vec3(
3*Math.cos(partyDogPhi)*Math.sin(partyDogTheta),
2 + 3*Math.cos(partyDogPhi)*Math.cos(partyDogTheta),
3*Math.sin(partyDogPhi)));
};
/***
* Adds the lights into the scene
*/
self.addLights = function()
{
self.adjustLights();
gl.uniform1i( shaders.Lights.PointLightCount, pointLights.length );
gl.uniform1i( shaders.Lights.SpotLightCount, spotLights.length );
gl.uniform1i( shaders.RenderType, self.renderType);
if(typeof directionalLight !== "undefined")
directionalLight.UpdateLights(gl, shaders.Lights.DirectionalLight);
for(var i = 0; i < pointLights.length; i++)
pointLights[i].UpdateLights(gl, shaders.Lights.PointLights[i]);
for(i = 0; i < spotLights.length; i++)
spotLights[i].UpdateLights(gl, shaders.Lights.SpotLights[i]);
};
/***
* Adds the scene into eye space
*/
self.addPerspective = function()
{
self.adjustCamera();
camera.UpdateCamera(gl, shaders);
};
/***
* Clears the screen and renders the scene
*/
self.render = function()
{
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//Add perspective and lights to the scene
self.addPerspective();
self.addLights();
dog.update();
//Render all the objectse to the screen
dog.render();
sun.render(gl, shaders);
ground.render(gl, shaders);
sky.render(gl, shaders);
cube.render(gl, shaders);
tetrahedron.render(gl, shaders);
};
/***
* Initializes WebGL
*/
self.initWebGL = function(canvas)
{
gl = null;
try {
// Try to grab the standard context. If it fails, fallback to experimental.
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
} catch (e) {}
// If we don't have a GL context, give up now
if (!gl) {
alert("Unable to initialize WebGL. Your browser may not support it.");
gl = null;
}
return gl;
};
//Run Constructor
self.init();
}
};
}]);
angular.module("HW4").service("ShaderService",
[ '$rootScope', function( $rootScope ) {
var service = {
initShaders: function(gl, vertexShaderId, fragmentShaderId) {
var vertShdr, fragShdr, msg;
//Load Vertex shader from HTML
var vertElem = document.getElementById(vertexShaderId);
if (!vertElem) {
alert("Unable to load vertex shader " + vertexShaderId);
return -1;
}
else {
vertShdr = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShdr, vertElem.text);
gl.compileShader(vertShdr);
if (!gl.getShaderParameter(vertShdr, gl.COMPILE_STATUS)) {
msg = "Vertex shader failed to compile. The error log is:" + "<pre>" + gl.getShaderInfoLog(vertShdr) + "</pre>";
alert(msg);
return -1;
}
}
//Load frag shader from html
var fragElem = document.getElementById(fragmentShaderId);
if (!fragElem) {
alert("Unable to load frag shader " + fragmentShaderId);
return -1;
} else {
fragShdr = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShdr, fragElem.text);
gl.compileShader(fragShdr);
if (!gl.getShaderParameter(fragShdr, gl.COMPILE_STATUS)) {
msg = "Fragment shader failed to compile. The error log is:" + "<pre>" + gl.getShaderInfoLog(fragShdr) + "</pre>";
alert(msg);
return -1;
}
}
//Compile and link shader code
var program = gl.createProgram();
gl.attachShader(program, vertShdr);
gl.attachShader(program, fragShdr);
gl.linkProgram(program);
/**Vector shader variable locations**/
program.vNormal = gl.getAttribLocation( program, "vNormal" );
program.vPosition = gl.getAttribLocation( program, "vPosition" );
program.vTexCoord = gl.getAttribLocation( program, "vTexCoord" );
program.projectionMatrix = gl.getUniformLocation( program, "perspectiveMatrix" );
program.viewMatrix = gl.getUniformLocation( program, "viewMatrix" );
program.modelViewMatrix = gl.getUniformLocation( program, "modelViewMatrix" );
/**Fragment shader variable locations**/
program.TextureBank = gl.getUniformLocation( program, "textureBank" );
program.EyePosition = gl.getUniformLocation( program, "eyePosition" );
program.RenderType = gl.getUniformLocation( program, "renderType" );
program.TextureRepeat = gl.getUniformLocation( program, "textureRepeat");
var PointLight = function(id)
{
return {
Color: gl.getUniformLocation(program, "pointLight[" + id + "].Base.Color"),
Power: gl.getUniformLocation(program, "pointLight[" + id + "].Base.Power"),
AmbientIntensity: gl.getUniformLocation(program, "pointLight[" + id + "].Base.AmbientIntensity"),
DiffuseIntensity: gl.getUniformLocation(program, "pointLight[" + id + "].Base.DiffuseIntensity"),
Position: gl.getUniformLocation(program, "pointLight[" + id + "].Position"),
AttenuationConstant: gl.getUniformLocation(program, "pointLight[" + id + "].Attenuation.Constant"),
AttenuationLinear: gl.getUniformLocation(program, "pointLight[" + id + "].Attenuation.Linear"),
AttenuationExponential: gl.getUniformLocation(program, "pointLight[" + id + "].Attenuation.Exponential")
};
};
var SpotLight = function(id)
{
return {
Color: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Base.Color"),
Power: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Base.Power"),
AmbientIntensity: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Base.AmbientIntensity"),
DiffuseIntensity: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Base.DiffuseIntensity"),
Position: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Position"),
Direction: gl.getUniformLocation(program, "spotLight[" + id + "].Direction"),
Cutoff: gl.getUniformLocation(program, "spotLight[" + id + "].Cutoff"),
AttenuationConstant: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Attenuation.Constant"),
AttenuationLinear: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Attenuation.Linear"),
AttenuationExponential: gl.getUniformLocation(program, "spotLight[" + id + "].Base.Attenuation.Exponential")
};
};
var DirectionalLight =
{
Color: gl.getUniformLocation(program, "directionalLight.Base.Color"),
Power: gl.getUniformLocation(program, "directionalLight.Base.Power"),
AmbientIntensity: gl.getUniformLocation(program, "directionalLight.Base.AmbientIntensity"),
DiffuseIntensity: gl.getUniformLocation(program, "directionalLight.Base.DiffuseIntensity"),
Direction: gl.getUniformLocation(program, "directionalLight.Direction")
};
program.Lights =
{
PointLightCount: gl.getUniformLocation(program, "pointLightCount"),
SpotLightCount: gl.getUniformLocation(program, "spotLightCount"),
DirectionalLight: DirectionalLight,
PointLights: [PointLight(0), PointLight(1), PointLight(2), PointLight(3), PointLight(4), PointLight(5)],
SpotLights: [SpotLight(0), SpotLight(1), SpotLight(2), SpotLight(3), SpotLight(4), SpotLight(5)]
};
program.Material =
{
Color: gl.getUniformLocation(program, "material.Color"),
EmitColor: gl.getUniformLocation(program, "material.EmitColor"),
EmitIntensity: gl.getUniformLocation(program, "material.EmitIntensity"),
Shininess: gl.getUniformLocation(program, "material.Shininess"),
ShineIntensity: gl.getUniformLocation(program, "material.ShineIntensity")
};
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
msg = "Shader program failed to link. The error log is:" + "<pre>" + gl.getProgramInfoLog(program) + "</pre>";
alert(msg);
return -1;
}
//return shader
return program;
}
};
return service;
}]);
angular.module("HW4").service("ShapeCreator",
[ '$rootScope', function( $rootScope ) {
var service = function(){
/***
* Pulled from Angel's code. Creates a scaling matrix with the given length since it will always be applied
* to cubes
*/
var scale4 = function (x, y, z) {
var result = mat4();
result[0][0] = x;
result[1][1] = y;
result[2][2] = z;
return result;
};
/***
* Get the normals for each given vertex.
* @param points - The vertices associated with this object
* @param normals - The list we will fill, using the normals associated with the verticies
* @constructor
*/
var GetNormals = function(points, normals)
{
for (var i = 0; i < points.length / 3; i+=3)
{
var vertex1 = vec3(points[i], points[i + 1], points[i + 2]);
var vertex2 = vec3(points[i + 3], points[i + 4], points[i + 5]);
var crossProduct = normalize(cross(vertex1, vertex2));
for(var j = 0; j < 3; j++)
normals.push(crossProduct[0], crossProduct[1], crossProduct[2]);
}
return normals;
};
var self = $rootScope;
/***
* Creates a unit sphere(radius .5 at (0,0)) with the give
*/
var CreateSphere = function(properties)
{
/***
* Assigns indexes in order, to recreate the primatives that make up the sphere
*/
var assignVertexIndex = function(indexData, first, second)
{
indexData.push(first);
indexData.push(second);
indexData.push(first + 1);
indexData.push(second);
indexData.push(second + 1);
indexData.push(first + 1);
};
/***
* Assigns in linear order, the normals and the positions of each vertex
*/
var assignVertex = function(positionData, normalData, radius, x, y, z)
{
normalData.push(x);
normalData.push(y);
normalData.push(z);
positionData.push(radius * x);
positionData.push(radius * y);
positionData.push(radius * z);
};
/***
* Pushes the u,v data into the texture coordinate list
*/
var assignTexCoord = function(texCoordData, u, v)
{
texCoordData.push(u);
texCoordData.push(v);
};
/***
* Returns a shape with the properties of a sphere
*/
return CreateShape(properties,
function(position, normal, index, texCoord)
{
var latitudeBands = 100;
var longitudeBands = 100;
var radius = 0.5;
//Create latitude lines and then longitude lines and then calculate the vertex by using the triangle created by the center and intersection point
for (var latNumber = 0; latNumber <= latitudeBands; latNumber++)
{
var theta = latNumber * Math.PI / latitudeBands;
var sinTheta = Math.sin(theta);
var cosTheta = Math.cos(theta);
for (var longNumber = 0; longNumber <= longitudeBands; longNumber++)
{
var phi = longNumber * 2 * Math.PI / longitudeBands;
var sinPhi = Math.sin(phi);
var cosPhi = Math.cos(phi);
assignVertex(position, normal,
radius, cosPhi * sinTheta, //x
cosTheta, //y
sinPhi * sinTheta);//z
assignTexCoord(texCoord, 1 - (longNumber / longitudeBands), 1 - (latNumber/latitudeBands));
if(latNumber == latitudeBands || longNumber == longitudeBands) continue;
var first = (latNumber * (longitudeBands + 1)) + longNumber;
var second = first + longitudeBands + 1;
assignVertexIndex(index, first, second);
}
}
return 1;
});
};
/***
* Creates a tetrahedron with the given properties
* @constructor
*/
var CreateTetrahedron = function(properties)
{
//Returns a shape with the properties of a tetrahedron
return CreateShape(properties, function(position, normal, index, texCoord)
{
position.push(-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
0.0, -0.5, 0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
0.0, 0.5, 0.0,
0.5, -0.5, -0.5,
0.0, -0.5, 0.5,
0.0, 0.5, 0.0,
0.0, -0.5, 0.5,
-0.5, -0.5, -0.5,
0.0, 0.5, 0.0
);
GetNormals(position, normal);
index.push(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 );
texCoord.push(0, 0, 1, 0, 0.5, 1,
0, 0, 1, 0, 0.5, 1,
0, 0, 1, 0, 0.5, 1,
0, 0, 1, 0, 0.5, 1);
return 4;
});
};
/***
* Creates a plane with the given properties
* @constructor
*/
var CreatePlane = function(properties)
{
return CreateShape(properties, function(position, normal, index, texCoord)
{
position.push(-0.5, 0.0, -0.5,
0.5, 0.0, -0.5,
0.5, 0.0, 0.5,
-0.5, 0.0, 0.5);
normal.push(0,0,1,
0,0,1,
0,0,1,
0,0,1);
index.push(3, 1, 0, 3, 2, 1);
texCoord.push(0, 0, 1, 0, 1, 1, 0, 1);
return 1;
});
};
/***
* Creates a cube with the given properties
* @constructor
*/
var CreateCube = function(properties)
{
return CreateShape(properties, function(position, normal, index, texCoord)
{
position.push(// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0);
GetNormals(position, normal);
var temp = [3, 1, 0, 3, 2, 1];
//This assigns the indices in the proper order for the cube to draw itself correctly
for(var i = 0; i < 6; i++)
for(var j = 0; j < temp.length; j++)
index.push(4*i + temp[j]);
texCoord.push(
// Front face
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
// Back face
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// Top face
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
// Bottom face
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
// Right face
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
// Left face
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0);
return 6;
});
};
/***
* Creates a shape, capable of updating the shader variables, drawing itself and performing basic transformations
*/
var CreateShape = function(properties, constructor)
{
var positionData = [];
var normalData = [];
var indexData =[];
var texCoordData = [];
var positionBuffer;
var normalBuffer;
var texCoordBuffer;
var indexBuffer;
var numOfSides;
var material;
var texture;
var scaleMatrix = mat4(), shiftMatrix = mat4(), rotateMatrix = mat4();
var modelViewMatrix = mat4();
/***
* Creates a set of vertices representing a sphere. Takes Longitude and Latitude lines around a sphere at a
* given radius and creates a matrix of points
*/
var init = function() {
numOfSides = constructor(positionData, normalData, indexData, texCoordData);
//Set the shape properties
if(typeof properties !== "undefined")
{
if(typeof properties.texture !== "undefined") texture = properties.texture;
if(typeof properties.shift !== "undefined") setShift(properties.shift);
if(typeof properties.scale !== "undefined") setScale(properties.scale);
if(typeof properties.rotate !== "undefined") setRotate(properties.rotate);
if(typeof properties.material !== "undefined") setMaterial(properties.material);
}
};
/***
* Sets up the basic WebGL information including the buffers for normals and positions
*/
var initBuffers = function(gl, shaders)
{
if(typeof texture !== "undefined") texture.initBuffers(gl, shaders);
normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normalData), gl.STATIC_DRAW);
normalBuffer.itemSize = 3;
normalBuffer.numItems = normalData.length / 3;
positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positionData), gl.STATIC_DRAW);
positionBuffer.itemSize = 3;
positionBuffer.numItems = positionData.length / 3;
texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoordData), gl.STATIC_DRAW);
texCoordBuffer.itemSize = 2;
texCoordBuffer.numItems = texCoordData.length / 2;
indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), gl.STATIC_DRAW);
indexBuffer.itemSize = 1;
indexBuffer.numItems = indexData.length;
};
/***
* Scale the sphere
*
* scaleBy - vector with x, y, z scales
*/
var setScale = function(scaleBy)
{
scaleMatrix = scale4(scaleBy[0],scaleBy[1],scaleBy[2]);
};
/***
* Scale the sphere by one value on x, y, z
*
* scaleBy - A single value to scale the vector by
*/
var setScaleAll = function(scaleBy)
{
scaleMatrix = scale4(scaleBy,scaleBy,scaleBy);
};
/***
* Shifts the sphere by the given vector
*
* shiftBy - Vector of x,y, and z to shift the sphere by
*/
var setShift = function(shiftBy)
{
shiftMatrix = translate(shiftBy);
};
/***
* Rotates the sphere around it's own axis.
*
* rotateBy - vector with [0] as the rotation degrees, The next 3 indexs are the indexes to rotate around: [1] - x axis, [2] - y axis, [3] - z axis
*/
var setRotate = function(rotateBy)
{
rotateMatrix = rotate(rotateBy[0], rotateBy[1], rotateBy[2], rotateBy[3]);
};
/***
* Sets the material of this sphere
*/
var setMaterial = function(materialData)
{
material = materialData;
};
var setTexture = function(textureData)
{
texture = textureData;
};
/***
* Apply the given transformation into the ModelViewMatrix
*/
var applyTransform = function(transformation)
{
modelViewMatrix = mult(modelViewMatrix, transformation);
};
var addMaterial = function(gl, shaders)
{
if(typeof material !== "undefined") material.render(gl, shaders.Material);
};
/***
* Draws the sphere and applies the object space transformations
*/
var render = function(gl, shaders)
{
addMaterial(gl, shaders);
//Refresh the buffers
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer( shaders.vPosition, positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( shaders.vPosition );
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.vertexAttribPointer(shaders.vNormal, normalBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray( shaders.vNormal );
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(shaders.vTexCoord, texCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray( shaders.vTexCoord );
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
//Transform the object into it's place in the world. This would move a a body part into "dog" space technically.
modelViewMatrix = mult(modelViewMatrix, shiftMatrix);
modelViewMatrix = mult(modelViewMatrix, rotateMatrix);
modelViewMatrix = mult(modelViewMatrix, scaleMatrix);
//Push the transformation matrix to the shaders
gl.uniformMatrix4fv( shaders.modelViewMatrix, false, flatten(modelViewMatrix));
clearTransformations();
for(var i = 0; i < numOfSides; i++)
{
if(typeof texture !== "undefined") texture.render(gl, shaders, i);
else gl.bindTexture(gl.TEXTURE_2D, null);
gl.drawElements(gl.TRIANGLES, indexBuffer.numItems/numOfSides, gl.UNSIGNED_SHORT, i*2*indexBuffer.numItems/numOfSides);
}
};
var clearTransformations = function(){ modelViewMatrix = mat4(); };
init();
return {
initBuffers: initBuffers,
render: render,
applyTransform: applyTransform,
setRotate: setRotate
};
};
//Public methods in the Shape Creator service
return {
CreateSphere: CreateSphere,
CreatePlane: CreatePlane,
CreateTetrahedron: CreateTetrahedron,
CreateCube: CreateCube
};
};
return service();
}]);
angular.module("HW4").service("CameraService",
[ '$rootScope', function( $rootScope ) {
var service = {
CreateCamera: function()
{
var cameraPosition = vec3(), cameraDirection = vec3(), cameraUp = vec3(),
cameraNear, cameraFar, cameraFov, cameraAspect;
var perspectiveMatrix;
/***
* Set up the default values for the camera at the given position.
*/
var init = function()
{
Position(vec3(0,0,0));
Lookat(vec3(0, 0, 0));
Up(vec3(0, 1, 0));
FOV(80.0);
Aspect(1);
Near(0.01);
Far(1000.0);
};
/***
* Get or set the camera position
*/
var Position = function(pos)
{
if(typeof pos !== "undefined") cameraPosition = pos;
else return cameraPosition;
};
/***
* Get or set the camera's at value
*/
var Lookat = function(at)
{
if(typeof at !== "undefined") cameraDirection = at;
else return cameraDirection;
};
/***
* Get or set the Field of View for the camera
*/
var FOV = function(fov)
{
if(typeof fov !== "undefined") cameraFov = fov;
else return cameraFov;
};
/***
* Get or set the aspect ratio for the camera
*/
var Aspect = function(aspect)
{
if(typeof aspect !== "undefined") cameraAspect = aspect;
else return cameraAspect;
};
/***
* Get or set the near of the camera view
*/
var Near = function(near)
{
if(typeof near !== "undefined") cameraNear = near;
else return cameraNear;
};
/***
* Get or set the far of the camera view
*/
var Far = function(far)
{
if(typeof far !== "undefined") cameraFar = far;
else return cameraFar;
};
/***
* Get or set the up of the camera view
*/
var Up = function(up)
{
if(typeof up !== "undefined") cameraUp = up;
else return cameraUp;
};
/***
* Gets the current perspective Matrix
*/
var GetViewProjection = function()
{
return perspective(FOV(), Aspect(), Near(), Far());
};
/***
* Gets the current View Matrix
*/
var GetViewMatrix = function()
{
return lookAt(Position(), Lookat(), Up());
};
/***
* Updates the camera in the shader code
* @param gl - WebGL
* @param shaders - Shader program with all the variable locations
*/
var UpdateCamera = function(gl, shaders)
{
gl.uniformMatrix4fv( shaders.projectionMatrix, false, flatten(GetViewProjection()) );
gl.uniformMatrix4fv( shaders.viewMatrix, false, flatten(GetViewMatrix()) );
gl.uniform3fv( shaders.EyePosition, flatten(Position()) );
};
init();
return {
Position: Position,
Lookat: Lookat,
Aspect: Aspect,
FOV: FOV,
Near: Near,
Far: Far,
Up: Up,
UpdateCamera: UpdateCamera,
GetViewProjection: GetViewProjection,
GetViewMatrix: GetViewMatrix
};
}
};
return service;
}]);
angular.module("HW4").service("AnimalService",
['$rootScope', 'ShapeCreator', 'TextureService', 'MaterialService',
function($rootScope, ShapeCreator, TextureService, MaterialService) {
var service = {
/***
* Creates a dog out of spheres
*/
CreateDog: function(gl, shaders) {
var self = $rootScope;
var transformationStack = [];
/***
* This is a prototype method that creates a body part. It will act as a leaf in the scene graph
*/
var CreateBodyPart = function(shiftBy, scaleBy, rotateBy, material) {
var shape = ShapeCreator.CreateSphere({
shift: shiftBy,
scale: scaleBy,
rotate: rotateBy,
material: material,
texture: TextureService.CreateTexture({filename: [TextureService.Fur()]})
});
/***
* Constructor for the shape object
*/
var init = function() {
shape.initBuffers(gl, shaders);
};
/***
* Applies the transformation stack to the current object
*/
var update = function()
{
for (var i = 0; i < transformationStack.length; i++) {
shape.applyTransform(transformationStack[i]);
}
};
/***
* This is a function to interact with a specific case when an item needs to rotate around it's own origin.
* When creating a body part, it's established in the Dogs object space to reduce push and pop calls everywhere, since scaling and location and initial rotation should be constant.
* It was in hopes that it would be cleaner, but it introduced the problem of not being able to actually rotate around in object space
*/
var rotateInPlace = function(rotation){
shape.setRotate(rotation);
};
/***
* Render the shape
*/
var render = function() {
shape.render(gl, shaders);
};
init();
return {
update: update,
render: render,
rotateInPlace: rotateInPlace
};
};
/***
* A branch of the scene graph that creates a the face of the animal; including nose, ears, eyes
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var face = (function(shiftBy)
{
/***
* A branch of the scene graph on the face branch that creates the eyes: left and right
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var eyes = (function(shiftBy)
{
//Create Left Eye
var leftEye = CreateBodyPart(add(shiftBy, vec3(-0.1, 0, -0.3)), vec3(0.25,0.15,0.25), vec4(), MaterialService.CreateEyes());
var leftPupil = CreateBodyPart(add(shiftBy, vec3(-0.2, 0, -0.3)),vec3(0.125,0.125,0.125), vec4(), MaterialService.CreatePupil());
//Create Right Eye
var rightEye = CreateBodyPart(add(shiftBy, vec3(-0.1, 0, 0.3)), vec3(0.25,0.15,0.25), vec4(), MaterialService.CreateEyes());
var rightPupil = CreateBodyPart(add(shiftBy, vec3(-0.2, 0, 0.3)),vec3(0.125,0.125,0.125), vec4(), MaterialService.CreatePupil());
var update = function() {
leftEye.update();
leftPupil.update();
rightEye.update();
rightPupil.update();
};
var render = function() {
leftEye.render();
leftPupil.render();
rightEye.render();
rightPupil.render();
};
return {
update: update,
render: render
};
})(add(shiftBy, vec3(-0.125,0.25,0)));
/***
* A branch of the scene graph on the face branch that creates the nose: This includes a tip of the nose and a snout
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var nose = (function(shiftBy)
{
var snout = CreateBodyPart(shiftBy, vec3(0.75,0.25,0.25),vec4(),MaterialService.CreatePearl());
var noseTip = CreateBodyPart(add(shiftBy, vec3(-0.35, 0, 0)), vec3(0.125,0.125,0.125),vec4(),MaterialService.CreateRuby());
var render = function() {
snout.render();
noseTip.render();
};
var update = function() {
snout.update();
noseTip.update();
};
return {
update: update,
render: render
};
})(add(shiftBy, vec3(-0.5, 0, 0)));
/***
* A branch of the scene graph on the face branch that creates the eyes; left and right
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var ears = (function()
{
var leftEar = CreateBodyPart(add(shiftBy,vec3(0,-0.125,0.5)), vec3(0.5,1,0.125), vec4(), MaterialService.CreatePewter());
var rightEar = CreateBodyPart(add(shiftBy,vec3(0,-0.125,-0.5)), vec3(0.5,1,0.125), vec4(), MaterialService.CreatePewter());
var render = function() {
rightEar.render();
leftEar.render();
};
var update = function() {
leftEar.rotateInPlace(vec4(-10*Math.sin(self.position*-4),1,0,0));
leftEar.update();
//transformationStack.pop();
rightEar.rotateInPlace(vec4(10*Math.sin(self.position*-4),1,0,0));
rightEar.update();
//transformationStack.pop();
};
return {
update: update,
render: render
};
})(shiftBy);
/***
* A branch of the scene graph on the face branch that creates the head shape
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var head = CreateBodyPart(shiftBy, vec3(1, 1, 1), vec4(), MaterialService.CreateObsidian());
var update = function() {
head.update();
nose.update();
eyes.update();
ears.update();
};
var render = function() {
head.render();
nose.render();
ears.render();
eyes.render();
};
return {
update: update,
render: render
};
})(vec3(-1.5, 0.5, 0));
/***
* A branch of the scene graph that creates the legs of the animal: 4 legs- 2 rear, 2 front
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var legs = (function(shiftBy)
{
var legScale = vec3(0.25, 0.5, 0.25);
var rotation = 0;
/***
* A prototype for creating a rear leg branch in the scene graph
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var CreateBackLeg = function(shiftBy) {
var upper = CreateBodyPart(shiftBy, legScale, vec4(), MaterialService.CreateDogFur());
var lower = CreateBodyPart(add(shiftBy, vec3(0, -0.5,0)), legScale, vec4(), MaterialService.CreateDogFur());
var update = function() {
upper.rotateInPlace(vec4(20*Math.sin(rotation), 0, 0, 1));
lower.rotateInPlace(vec4(-20*Math.sin(rotation), 0, 0, 1));
upper.update();
lower.update();
};
var render = function() {
upper.render();
lower.render();
};
return {
update: update,
render: render
};
};
/***
* A prototype for creating a front leg branch in the scene graph
*
* shiftBy - the adjustment of the location of the center point of this part of the object.
*/
var CreateFrontLeg = function(shiftBy) {
var upper = CreateBodyPart(shiftBy, legScale, vec4(), MaterialService.CreateDogFur());
var lower = CreateBodyPart(add(shiftBy, vec3(0, -0.5,0)), legScale, vec4(), MaterialService.CreateDogFur());
var update = function() {
upper.update();
lower.rotateInPlace(vec4(20*Math.sin(rotation), 0, 0, 1));
lower.update();
};
var render = function() {
upper.render();
lower.render();
};
return {
update: update,
render: render
};
};
//Create each leg
var leftRear = CreateBackLeg(add(shiftBy, vec3(0.75,0,0.25)));
var rightRear = CreateBackLeg(add(shiftBy, vec3(0.75,0,-0.25)));
var leftFront = CreateFrontLeg(add(shiftBy, vec3(-0.75,0,0.25)));
var rightFront = CreateFrontLeg(add(shiftBy, vec3(-0.75,0,-0.25)));
var update = function() {
//Creates the rocking back and forth look on the legs to make it look like the dog is running
transformationStack.push(rotate(10*Math.sin(rotation=self.position*3), 0, 0, 1));
leftRear.update();
leftFront.update();
rightRear.update();
rightFront.update();
transformationStack.pop();
};
var render = function() {
leftRear.render();
leftFront.render();
rightRear.render();
rightFront.render();
};
return {
update: update,
render: render
};
})(vec3(0, -0.4, 0));
/***
* A branch of the scene graph that creates the body of the animal
*/
var body = CreateBodyPart(vec3(0, 0, 0), vec3(2.75, 1, 0.75),
vec4(), MaterialService.CreateChrome());
/***
* A branch of the scene graph that creates the tail of the animal
*/
var tail = CreateBodyPart(vec3(1.5, 0.5, 0), vec3(1, 0.25, 0.25),
vec4(45, 0, 0, 1), MaterialService.CreateDogFur());
/***
* Performs an update on the entire scene graph
*/
var update = function() {
//Calculate the next position.
self.position -= self.speed/1000;
transformationStack.push(mult(rotate(self.rotation, 0, 1, 0),translate(vec3(10*Math.sin(self.position), 0, 0))));
body.update();
face.update();
legs.update();
//wag tail
transformationStack.push(rotate(10*Math.sin(self.position*10), 1, 0, 0));
tail.update();
transformationStack.pop();
transformationStack.pop();
transformationStack.pop();
};
/***
* Draws the scene graph on screen
*
* lights - A list of lights in the scene
* camera - The camera that the user is looking through to see the scene
*/
var render = function() {
body.render();
face.render();
tail.render();
legs.render();
};
return {
update: update,
render: render
};
}
};
return service;
}
]);
angular.module("HW4").service("LightingService",
[ '$rootScope', function( $rootScope ) {
var service = function() {
/***
* Creates a base light with the given properties
* @param properties - An object containing values that can be applied to the light.
* - Color, AmbientIntensity, DiffuseIntensity, and Power are the names of the expected variables
* @returns Light object - {{Color: Function, AmbientIntensity: Function, DiffuseIntensity: Function, Power: Function, UpdateLights: Function}}
* @constructor
*/
var CreateLight = function(properties)
{
var lightColor = vec3(1,1,1), ambientIntensity = .4, diffuseIntensity = .9, powerOn = true;
/***
* Getters and setters for values that affect the base lighting of a light
*/
var Color = function(newColor) { if(typeof newColor !== "undefined") lightColor = newColor; else return lightColor; };
var AmbientIntensity = function(intensity) { if(typeof intensity !== "undefined") ambientIntensity = intensity; else return ambientIntensity; };
var DiffuseIntensity = function(intensity) { if(typeof intensity !== "undefined") diffuseIntensity = intensity; else return diffuseIntensity; };
var Power = function(on) { if(typeof on !== "undefined") powerOn = on; else return powerOn; };
/***
* If there is properties that were passed into the factory method, set the values to those properties.
* This uses a simple trick that passes the value set in the properties if properties is not null. If
* the value that is being passed in happens to be null, the setters will ignore it
*/
Color(typeof properties !== "undefined" ? properties.Color : properties);
AmbientIntensity(typeof properties !== "undefined" ? properties.AmbientIntensity : properties);
DiffuseIntensity(typeof properties !== "undefined" ? properties.DiffuseIntensity : properties);
Power(typeof properties !== "undefined" ? properties.Power : properties);
/***
* Pass the values of this light into the shader code
* @param gl - WebGL
* @param glLocation - The structure holding all the webGL variable locations
*/
var UpdateLights = function(gl,glLocation)
{
gl.uniform3fv( glLocation.Color, flatten(Color()) );
gl.uniform1f( glLocation.AmbientIntensity, AmbientIntensity() );
gl.uniform1f( glLocation.DiffuseIntensity, DiffuseIntensity() );
gl.uniform1i( glLocation.Power, Power() );
};
return{
Color: Color,
AmbientIntensity: AmbientIntensity,
DiffuseIntensity: DiffuseIntensity,
Power: Power,
UpdateLights: UpdateLights
};
};
/***
* Creates a Directional Light using the given properties
* @param properties - An object containing values that can be applied to the light.
* - Color, Direction, AmbientIntensity, DiffuseIntensity, and Power are the names of the expected variables
* @returns {{Color: Function, Direction: Function, AmbientIntensity: Function, DiffuseIntensity: Function, Power: Function, UpdateLights: Function}}
* @constructor
*/
var CreateDirectionalLight= function(properties)
{
var lightDirection = vec3(0, 1, 0), light = CreateLight(properties);
var Direction = function(newDirection) { if(typeof newDirection !== "undefined") lightDirection = newDirection; else return lightDirection; };
Direction(typeof properties !== "undefined" ? properties.Direction : properties);
/***
* Pass the values of this light into the shader code
* @param gl - WebGL
* @param glLocation - The structure holding all the webGL variable locations
*/
var UpdateLights = function(gl, glLocation)
{
gl.uniform3fv( glLocation.Direction, flatten(Direction()) );
light.UpdateLights(gl, glLocation);
};
return {
Color: light.Color,
Direction: Direction,
AmbientIntensity: light.AmbientIntensity,
DiffuseIntensity: light.DiffuseIntensity,
Power: light.Power,
UpdateLights: UpdateLights
};
};
/***
* Creates a Point Light using the given properties
* @param properties - An object containing values that can be applied to the light.
* - Color, Position, Attenuation(vec3), AmbientIntensity, DiffuseIntensity, and Power are the names of the expected variables
* @returns {{Color: Function, Position: Function, Attenuation: Function, AmbientIntensity: Function, DiffuseIntensity: Function, Power: Function, UpdateLights: Function}}
* @constructor
*/
var CreatePointLight= function(properties)
{
var lightPosition = vec3(0,0,0), attenuation = vec3(1, 0, 0.8);
var light = CreateLight(properties);
var Position = function(newPosition) { if(typeof newPosition !== "undefined") lightPosition = newPosition; else return lightPosition; };
var Attenuation = function(newAttenuation) { if(typeof newAttenuation !== "undefined") attenuation = newAttenuation; else return attenuation; };
Position(typeof properties !== "undefined" ? properties.Position : properties);
Attenuation(typeof properties !== "undefined" ? properties.Attenuation : properties);
/***
* Pass the values of this light into the shader code
* @param gl - WebGL
* @param glLocation - The structure holding all the webGL variable locations
*/
var UpdateLights = function(gl, glLocation)
{
light.UpdateLights(gl, glLocation);
gl.uniform3fv( glLocation.Position, flatten(Position()));
gl.uniform1f( glLocation.AttenuationConstant, Attenuation()[0] );
gl.uniform1f( glLocation.AttenuationLinear, Attenuation()[1] );
gl.uniform1f( glLocation.AttenuationExponential, Attenuation()[2] );
};
return {
Color: light.Color,
AmbientIntensity: light.AmbientIntensity,
DiffuseIntensity: light.DiffuseIntensity,
Power: light.Power,
Position: Position,
Attenuation: Attenuation,
UpdateLights: UpdateLights
};
};
/***
* Creates a Spot Light using the given properties
* @param properties - An object containing values that can be applied to the light.
* - Color, Direction, Position, Cutoff, Attenuation(vec3), AmbientIntensity, DiffuseIntensity, and Power are the names of the expected variables
* @returns {{Color: Function, Direction: Function, Position: Function, Cutoff: Function, Attenuation: Function, AmbientIntensity: Function, DiffuseIntensity: Function, Power: Function, UpdateLights: Function}}
* @constructor
*/
var CreateSpotLight = function(properties)
{
var lightDirection = vec3(0, 1, 0), lightCutoff = 2;
var light = CreatePointLight(properties);
var Direction = function(newDirection) { if(typeof newDirection !== "undefined") lightDirection = newDirection; else return lightDirection; };
var Cutoff = function(newCutoff) { if(typeof newCutoff !== "undefined") lightCutoff = newCutoff; else return lightCutoff; };
Direction(typeof properties !== "undefined" ? properties.Direction : properties);
Cutoff(typeof properties !== "undefined" ? properties.Cutoff : properties);
/***
* Pass the values of this light into the shader code
* @param gl - WebGL
* @param glLocation - The structure holding all the webGL variable locations
*/
var UpdateLights = function (gl, glLocation)
{
light.UpdateLights(gl, glLocation);
gl.uniform3fv( glLocation.Direction, flatten(Direction()) );
gl.uniform1f( glLocation.Cutoff, Math.cos(radians(Cutoff())) );
};
return {
Color: light.Color,
AmbientIntensity: light.AmbientIntensity,
DiffuseIntensity: light.DiffuseIntensity,
Position: light.Position,
Power: light.Power,
Attenuation: light.Attenuation,
Direction: Direction,
Cutoff: Cutoff,
UpdateLights: UpdateLights
};
};
return {
CreateDirectionalLight: CreateDirectionalLight,
CreatePointLight: CreatePointLight,
CreateSpotLight: CreateSpotLight
}
};
return service();
}]);
angular.module("HW4").service("MaterialService",
[ '$rootScope', function( $rootScope ) {
var service = function()
{
/***
* Creates a material with emissive, and shininess values
*/
var CreateMaterial = function(properties)
{
//Default values for a material object
var materialColor = vec3(), materialEmit = vec3(), materialShininess = 1,
emitIntensity = length(materialEmit) > 0.0 ? 0.3 : 0.0, shineIntensity = 0.3;
/***
* Getters and Setters for each property of the material
*/
var Color= function(newColor){ if(typeof newColor !== "undefined") materialColor = newColor; else return materialColor; };
var EmitColor= function(newColor){ if(typeof newColor !== "undefined") materialEmit = newColor; else return materialEmit; };
var Shininess= function(newShininess){ if(typeof newShininess !== "undefined") materialShininess = newShininess; else return materialShininess; };
var EmitIntensity= function(intensity){ if(typeof intensity !== "undefined") emitIntensity = intensity; else return emitIntensity; };
var ShineIntensity=function(intensity){ if(typeof intensity !== "undefined") shineIntensity = intensity; else return shineIntensity; };
/***
* Passes the material data to the shader code
* @param gl - WebGL
* @param glLocation - Shader Location value for the material
*/
var render = function(gl, glLocation)
{
gl.uniform3fv(glLocation.Color, flatten(Color()));
gl.uniform3fv(glLocation.EmitColor, flatten(EmitColor()));
gl.uniform1f(glLocation.EmitIntensity, EmitIntensity());
gl.uniform1f(glLocation.Shininess, Shininess());
gl.uniform1f(glLocation.ShineIntensity, ShineIntensity());
};
/***
* If there is properties that were passed into the factory method, set the values to those properties.
* This uses a simple trick that passes the value set in the properties if properties is not null. If
* the value that is being passed in happens to be null, the setters will ignore it
*/
Color(typeof properties !== "undefined" ? properties.Color : properties);
EmitColor(typeof properties !== "undefined" ? properties.EmitColor : properties);
Shininess(typeof properties !== "undefined" ? properties.Shininess : properties);
EmitIntensity(typeof properties !== "undefined" ? properties.EmitIntensity : properties);
ShineIntensity(typeof properties !== "undefined" ? properties.ShineIntensity : properties);
return {
Color: Color,
EmitColor: EmitColor,
Shininess: Shininess,
EmitIntensity: EmitIntensity,
ShineIntensity: ShineIntensity,
render: render
};
};
/***
* Creates a chrome material
* @constructor
*/
var CreateChrome = function()
{
return CreateMaterial({
Color: vec3(0.4, 0.4, 0.4),
ShineIntensity:.3,
Shininess: 20.8});
};
/***
* Creates a ruby material
* @constructor
*/
var CreateRuby = function()
{
return CreateMaterial({
Color: vec3(0.61424, 0.04136, 0.04136),
EmitColor: vec3(0.40, .2, .2),
Shininess: 76.8});
};
/***
* Creates an obsidian material
* @constructor
*/
var CreateObsidian = function()
{
return CreateMaterial({
Color: vec3(0.18275, 0.17, 0.22525),
Shininess: 38.4});
};
/***
* Creates a dog fur material
* @constructor
*/
var CreateDogFur= function()
{
return CreateMaterial({
Color: vec3(0.5, 0.25, 0.125),
Shininess: 10.0});
};
/***
* Creates a grass material
* @constructor
*/
var CreateGrass = function()
{
return CreateMaterial({
Color: vec3(0.5, 1,.5, 1 ),
Shininess: 10});
};
/***
* Creates a Pearl material
* @constructor
*/
var CreatePearl = function()
{
return CreateMaterial({
Color: vec3(1.0, 0.829, 0.829, 0.922),
EmitColor: vec3(0.5,0.5,0.5,1),
Shininess: 11.264});
};
/***
* Creates a Pewter material
* @constructor
*/
var CreatePewter = function()
{
return CreateMaterial({
Color: vec3(0.427451, 0.470588, 0.541176, 1.0),
EmitColor: vec3(0.9, 0.25, 0.125, 1),
Shininess: 9.84615 });
};
/***
* Create a material for eyes
* @constructor
*/
var CreateEyes = function()
{
return CreateMaterial({
Color: vec3(1, 1, 1, 1),
EmitColor: vec3(0.4,0.4,0.4,1),
Shininess: 11.264});
};
/***
* Creates a material for the pupils
*/
var CreatePupil = function()
{
return CreateMaterial({
Color: vec3(0.01,0.01,0.01, 1),
EmitColor: vec3( 0.2, 0, 0, 1),
ShineIntensity: 2,
Shininess: 32.0});
};
/***
* Create a material for the sky
*/
var CreateSky = function()
{
return CreateMaterial({Color: vec3( 0, 1, 1),
Shininess: 10});
};
/***
* Creates a material for the sun
*/
var CreateSun = function()
{
return CreateMaterial({Color: vec3( 1, 1, 0, 1 ),
Shininess: 10});
};
return {
CreateMaterial: CreateMaterial,
CreateChrome: CreateChrome,
CreateRuby: CreateRuby,
CreatePearl: CreatePearl,
CreatePewter: CreatePewter,
CreatePupil: CreatePupil,
CreateEyes: CreateEyes,
CreateObsidian: CreateObsidian,
CreateDogFur: CreateDogFur,
CreateGrass: CreateGrass,
CreateSky: CreateSky,
CreateSun: CreateSun
};
};
return service();
}]);
angular.module("HW4").service("TextureService",
[ '$rootScope', function( $rootScope ) {
var service = function(){
/***
* Creates a texture object with the given properties
*/
var CreateTexture = function(properties)
{
var images = [];
var textures = [];
var minification, magnification, sTactic, tTactic, repeat;
/***
* initializes the texture buffers and pushes data about each texture image associated with this texture object to the shaders
*/
var initBuffers = function(gl, shaders)
{
repeat = typeof properties.repeat !== "undefined" ? properties.repeat : 1;
minification = typeof properties.minification !== "undefined" ? properties.minification : gl.NEAREST;
magnification = properties.magnification;
sTactic = properties.sTactic;
tTactic = properties.sTactic;
for (var i = 0; i < properties.filename.length; i++)
loadImage(gl, shaders, properties.filename[i]);
};
/***
* Loads the image from the given filename and pushes its information to the shader
*/
var loadImage = function(gl, shaders, filename)
{
var texture = gl.createTexture();
var image = new Image();
image.isLoaded = false;
image.onload = function () {
image.isLoaded = true;
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
if(typeof minification !== "undefined") gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minification);
if(typeof magnification !== "undefined") gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magnification);
if(typeof sTactic !== "undefined") gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, sTactic);
if(typeof tTactic !== "undefined") gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, tTactic);
gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(gl.TEXTURE_2D, null);
};
image.crossOrigin = "anonymous";
image.src = filename;
textures.push(texture);
images.push(image);
};
/***
* Render the texture
*/
var render = function(gl, shaders, index)
{
index = index % images.length;
if(images[index].isLoaded)
{
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textures[index]);
gl.uniform1f(shaders.TextureRepeat, repeat);
gl.uniform1i(shaders.TextureBank, 0);
}
};
return {
initBuffers: initBuffers,
render: render
};
};
/***
* Filename for a fur texture
* @constructor
*/
var Fur= function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/fur.jpg";
};
/***
* Filename for a grass texture
* @constructor
*/
var Grass = function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/grass.jpg";
};
/***
* Filename for a face texture
* @constructor
*/
var Face = function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/face.jpg";
};
/***
* Filename for a broccoli in a tree texture
* @constructor
*/
var Broccoli = function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/brocolli.jpg";
};
/***
* Filename for a Mr Gravity texture
* @constructor
*/
var Gravity = function() {
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/gravity.jpg";
};
/***
* Filename for a tile texture
* @constructor
*/
var Tile = function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/tile.jpg";
};
/***
* Filename for a thwomp texture
* @constructor
*/
var Thwomp = function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/thwomp.jpg";
};
/***
* Filename for a sky texture
* @constructor
*/
var Sky = function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/sky.jpg";
};
/***
* Filename for a sun texture
* @constructor
*/
var Sun = function(){
return "https://dl.dropboxusercontent.com/u/6179029/HW4%20Files/sun.jpg";
};
return{
Fur: Fur,
Grass: Grass,
Face: Face,
Broccoli: Broccoli,
Gravity: Gravity,
Tile: Tile,
Thwomp: Thwomp,
Sky: Sky,
Sun: Sun,
CreateTexture: CreateTexture
};
};
return service();
}]);
//////////////////////////////////////////////////////////////////////////////
//
// Angel.js
//
// This file was created by Professor Edward Angel at the University of New Mexico
// I have no license or agreement with him directly, but as a student who used his book
// and this code that was offered in the book. Previous projects linked his online version of
// the script but it has since been taken down. His website is here: http://www.cs.unm.edu/~angel/
//////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Helper functions
//
function _argumentsToArray( args )
{
return [].concat.apply( [], Array.prototype.slice.apply(args) );
}
//----------------------------------------------------------------------------
function radians( degrees ) {
return degrees * Math.PI / 180.0;
}
//----------------------------------------------------------------------------
//
// Vector Constructors
//
function vec2()
{
var result = _argumentsToArray( arguments );
switch ( result.length ) {
case 0: result.push( 0.0 );
case 1: result.push( 0.0 );
}
return result.splice( 0, 2 );
}
function vec3()
{
var result = _argumentsToArray( arguments );
switch ( result.length ) {
case 0: result.push( 0.0 );
case 1: result.push( 0.0 );
case 2: result.push( 0.0 );
}
return result.splice( 0, 3 );
}
function vec4()
{
var result = _argumentsToArray( arguments );
switch ( result.length ) {
case 0: result.push( 0.0 );
case 1: result.push( 0.0 );
case 2: result.push( 0.0 );
case 3: result.push( 1.0 );
}
return result.splice( 0, 4 );
}
//----------------------------------------------------------------------------
//
// Matrix Constructors
//
function mat2()
{
var v = _argumentsToArray( arguments );
var m = [];
switch ( v.length ) {
case 0:
v[0] = 1;
case 1:
m = [
vec2( v[0], 0.0 ),
vec2( 0.0, v[0] )
];
break;
default:
m.push( vec2(v) ); v.splice( 0, 2 );
m.push( vec2(v) );
break;
}
m.matrix = true;
return m;
}
//----------------------------------------------------------------------------
function mat3()
{
var v = _argumentsToArray( arguments );
var m = [];
switch ( v.length ) {
case 0:
v[0] = 1;
case 1:
m = [
vec3( v[0], 0.0, 0.0 ),
vec3( 0.0, v[0], 0.0 ),
vec3( 0.0, 0.0, v[0] )
];
break;
default:
m.push( vec3(v) ); v.splice( 0, 3 );
m.push( vec3(v) ); v.splice( 0, 3 );
m.push( vec3(v) );
break;
}
m.matrix = true;
return m;
}
//----------------------------------------------------------------------------
function mat4()
{
var v = _argumentsToArray( arguments );
var m = [];
switch ( v.length ) {
case 0:
v[0] = 1;
case 1:
m = [
vec4( v[0], 0.0, 0.0, 0.0 ),
vec4( 0.0, v[0], 0.0, 0.0 ),
vec4( 0.0, 0.0, v[0], 0.0 ),
vec4( 0.0, 0.0, 0.0, v[0] )
];
break;
default:
m.push( vec4(v) ); v.splice( 0, 4 );
m.push( vec4(v) ); v.splice( 0, 4 );
m.push( vec4(v) ); v.splice( 0, 4 );
m.push( vec4(v) );
break;
}
m.matrix = true;
return m;
}
//----------------------------------------------------------------------------
//
// Generic Mathematical Operations for Vectors and Matrices
//
function equal( u, v )
{
if ( u.length != v.length ) { return false; }
if ( u.matrix && v.matrix ) {
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) { return false; }
for ( var j = 0; j < u[i].length; ++j ) {
if ( u[i][j] !== v[i][j] ) { return false; }
}
}
}
else if ( u.matrix && !v.matrix || !u.matrix && v.matrix ) {
return false;
}
else {
for ( var i = 0; i < u.length; ++i ) {
if ( u[i] !== v[i] ) { return false; }
}
}
return true;
}
//----------------------------------------------------------------------------
function add( u, v )
{
var result = [];
if ( u.matrix && v.matrix ) {
if ( u.length != v.length ) {
throw "add(): trying to add matrices of different dimensions";
}
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) {
throw "add(): trying to add matrices of different dimensions";
}
result.push( [] );
for ( var j = 0; j < u[i].length; ++j ) {
result[i].push( u[i][j] + v[i][j] );
}
}
result.matrix = true;
return result;
}
else if ( u.matrix && !v.matrix || !u.matrix && v.matrix ) {
throw "add(): trying to add matrix and non-matrix variables";
}
else {
if ( u.length != v.length ) {
throw "add(): vectors are not the same dimension";
}
for ( var i = 0; i < u.length; ++i ) {
result.push( u[i] + v[i] );
}
return result;
}
}
//----------------------------------------------------------------------------
function subtract( u, v )
{
var result = [];
if ( u.matrix && v.matrix ) {
if ( u.length != v.length ) {
throw "subtract(): trying to subtract matrices" +
" of different dimensions";
}
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) {
throw "subtract(): trying to subtact matrices" +
" of different dimensions";
}
result.push( [] );
for ( var j = 0; j < u[i].length; ++j ) {
result[i].push( u[i][j] - v[i][j] );
}
}
result.matrix = true;
return result;
}
else if ( u.matrix && !v.matrix || !u.matrix && v.matrix ) {
throw "subtact(): trying to subtact matrix and non-matrix variables";
}
else {
if ( u.length != v.length ) {
throw "subtract(): vectors are not the same length";
}
for ( var i = 0; i < u.length; ++i ) {
result.push( u[i] - v[i] );
}
return result;
}
}
//----------------------------------------------------------------------------
function mult( u, v )
{
var result = [];
if ( u.matrix && v.matrix ) {
if ( u.length != v.length ) {
throw "mult(): trying to add matrices of different dimensions";
}
for ( var i = 0; i < u.length; ++i ) {
if ( u[i].length != v[i].length ) {
throw "mult(): trying to add matrices of different dimensions";
}
}
for ( var i = 0; i < u.length; ++i ) {
result.push( [] );
for ( var j = 0; j < v.length; ++j ) {
var sum = 0.0;
for ( var k = 0; k < u.length; ++k ) {
sum += u[i][k] * v[k][j];
}
result[i].push( sum );
}
}
result.matrix = true;
return result;
}
else {
if ( u.length != v.length ) {
throw "mult(): vectors are not the same dimension";
}
for ( var i = 0; i < u.length; ++i ) {
result.push( u[i] * v[i] );
}
return result;
}
}
//----------------------------------------------------------------------------
//
// Basic Transformation Matrix Generators
//
function translate( x, y, z )
{
if ( Array.isArray(x) && x.length == 3 ) {
z = x[2];
y = x[1];
x = x[0];
}
var result = mat4();
result[0][3] = x;
result[1][3] = y;
result[2][3] = z;
return result;
}
//----------------------------------------------------------------------------
function rotate( angle, axis )
{
if ( !Array.isArray(axis) ) {
axis = [ arguments[1], arguments[2], arguments[3] ];
}
var v = normalize( axis );
var x = v[0];
var y = v[1];
var z = v[2];
var c = Math.cos( radians(angle) );
var omc = 1.0 - c;
var s = Math.sin( radians(angle) );
var result = mat4(
vec4( x*x*omc + c, x*y*omc - z*s, x*z*omc + y*s, 0.0 ),
vec4( x*y*omc + z*s, y*y*omc + c, y*z*omc - x*s, 0.0 ),
vec4( x*z*omc - y*s, y*z*omc + x*s, z*z*omc + c, 0.0 ),
vec4()
);
return result;
}
//----------------------------------------------------------------------------
function scale( x, y, z )
{
if ( Array.isArray(x) && x.length == 3 ) {
z = x[2];
y = x[1];
x = x[0];
}
var result = mat4();
result[0][0] = x;
result[1][1] = y;
result[2][2] = z;
return result;
}
//----------------------------------------------------------------------------
//
// ModelView Matrix Generators
//
function lookAt( eye, at, up )
{
if ( !Array.isArray(eye) || eye.length != 3) {
throw "lookAt(): first parameter [eye] must be an a vec3";
}
if ( !Array.isArray(at) || at.length != 3) {
throw "lookAt(): first parameter [at] must be an a vec3";
}
if ( !Array.isArray(up) || up.length != 3) {
throw "lookAt(): first parameter [up] must be an a vec3";
}
if ( equal(eye, at) ) {
return mat4();
}
var v = normalize( subtract(at, eye) ); // view direction vector
var n = normalize( cross(v, up) ); // perpendicular vector
var u = normalize( cross(n, v) ); // "new" up vector
v = negate( v );
var result = mat4(
vec4( n, -dot(n, eye) ),
vec4( u, -dot(u, eye) ),
vec4( v, -dot(v, eye) ),
vec4()
);
return result;
}
//----------------------------------------------------------------------------
//
// Projection Matrix Generators
//
function ortho( left, right, bottom, top, near, far )
{
if ( left == right ) { throw "ortho(): left and right are equal"; }
if ( bottom == top ) { throw "ortho(): bottom and top are equal"; }
if ( near == far ) { throw "ortho(): near and far are equal"; }
var w = right - left;
var h = top - bottom;
var d = far - near;
var result = mat4();
result[0][0] = 2.0 / w;
result[1][1] = 2.0 / h;
result[2][2] = -2.0 / d;
result[0][3] = -(left + right) / w;
result[1][3] = -(top + bottom) / h;
result[2][3] = -(near + far) / d;
return result;
}
//----------------------------------------------------------------------------
function perspective( fovy, aspect, near, far )
{
var f = 1.0 / Math.tan( radians(fovy) / 2 );
var d = far - near;
var result = mat4();
result[0][0] = f / aspect;
result[1][1] = f;
result[2][2] = -(near + far) / d;
result[2][3] = -2 * near * far / d;
result[3][2] = -1;
result[3][3] = 0.0;
return result;
}
//----------------------------------------------------------------------------
//
// Matrix Functions
//
function transpose( m )
{
if ( !m.matrix ) {
return "transpose(): trying to transpose a non-matrix";
}
var result = [];
for ( var i = 0; i < m.length; ++i ) {
result.push( [] );
for ( var j = 0; j < m[i].length; ++j ) {
result[i].push( m[j][i] );
}
}
result.matrix = true;
return result;
}
//----------------------------------------------------------------------------
//
// Vector Functions
//
function dot( u, v )
{
if ( u.length != v.length ) {
throw "dot(): vectors are not the same dimension";
}
var sum = 0.0;
for ( var i = 0; i < u.length; ++i ) {
sum += u[i] * v[i];
}
return sum;
}
//----------------------------------------------------------------------------
function negate( u )
{
result = [];
for ( var i = 0; i < u.length; ++i ) {
result.push( -u[i] );
}
return result;
}
//----------------------------------------------------------------------------
function cross( u, v )
{
if ( !Array.isArray(u) || u.length < 3 ) {
throw "cross(): first argument is not a vector of at least 3";
}
if ( !Array.isArray(v) || v.length < 3 ) {
throw "cross(): second argument is not a vector of at least 3";
}
var result = [
u[1]*v[2] - u[2]*v[1],
u[2]*v[0] - u[0]*v[2],
u[0]*v[1] - u[1]*v[0]
];
return result;
}
//----------------------------------------------------------------------------
function length( u )
{
return Math.sqrt( dot(u, u) );
}
//----------------------------------------------------------------------------
function normalize( u, excludeLastComponent )
{
if ( excludeLastComponent ) {
var last = u.pop();
}
var len = length( u );
if ( !isFinite(len) ) {
throw "normalize: vector " + u + " has zero length";
}
for ( var i = 0; i < u.length; ++i ) {
u[i] /= len;
}
if ( excludeLastComponent ) {
u.push( last );
}
return u;
}
//----------------------------------------------------------------------------
function mix( u, v, s )
{
if ( typeof s !== "number" ) {
throw "mix: the last paramter " + s + " must be a number";
}
if ( u.length != v.length ) {
throw "vector dimension mismatch";
}
var result = [];
for ( var i = 0; i < u.length; ++i ) {
result.push( s * u[i] + (1.0 - s) * v[i] );
}
return result;
}
//----------------------------------------------------------------------------
//
// Vector and Matrix functions
//
function scale( s, u )
{
if ( !Array.isArray(u) ) {
throw "scale: second parameter " + u + " is not a vector";
}
result = [];
for ( var i = 0; i < u.length; ++i ) {
result.push( s * u[i] );
}
return result;
}
//----------------------------------------------------------------------------
//
//
//
function flatten( v )
{
if ( v.matrix === true ) {
v = transpose( v );
}
var n = v.length;
var elemsAreArrays = false;
if ( Array.isArray(v[0]) ) {
elemsAreArrays = true;
n *= v[0].length;
}
var floats = new Float32Array( n );
if ( elemsAreArrays ) {
var idx = 0;
for ( var i = 0; i < v.length; ++i ) {
for ( var j = 0; j < v[i].length; ++j ) {
floats[idx++] = v[i][j];
}
}
}
else {
for ( var i = 0; i < v.length; ++i ) {
floats[i] = v[i];
}
}
return floats;
}
//----------------------------------------------------------------------------
var sizeof = {
'vec2' : new Float32Array( flatten(vec2()) ).byteLength,
'vec3' : new Float32Array( flatten(vec3()) ).byteLength,
'vec4' : new Float32Array( flatten(vec4()) ).byteLength,
'mat2' : new Float32Array( flatten(mat2()) ).byteLength,
'mat3' : new Float32Array( flatten(mat3()) ).byteLength,
'mat4' : new Float32Array( flatten(mat4()) ).byteLength
};