mirror of
https://github.com/transatoshi-mw/grin-web-wallet.git
synced 2025-10-06 15:52:47 +00:00
1932 lines
58 KiB
JavaScript
Executable File
1932 lines
58 KiB
JavaScript
Executable File
// Use strict
|
|
"use strict";
|
|
|
|
|
|
// Classes
|
|
|
|
// Logo class
|
|
class Logo {
|
|
|
|
// Public
|
|
|
|
// Constructor
|
|
constructor(application, message, wakeLock) {
|
|
|
|
// Set application
|
|
this.application = application;
|
|
|
|
// Set message
|
|
this.message = message;
|
|
|
|
// Get main display
|
|
this.mainDisplay = $("main");
|
|
|
|
// Get logo display
|
|
this.logoDisplay = this.mainDisplay.children().children("div.logo");
|
|
|
|
// Get canvas
|
|
this.canvas = this.logoDisplay.children().get(0);
|
|
|
|
// Set closing
|
|
this.closing = false;
|
|
|
|
// Set frame duration
|
|
this.frameDuration = 0;
|
|
|
|
// Set frame duration variation
|
|
this.frameDurationVariation = 0;
|
|
|
|
// Set shader program
|
|
this.shaderProgram = Logo.NO_SHADER_PROGRAM;
|
|
|
|
// Set buffers
|
|
this.buffers = [];
|
|
|
|
// Set textures
|
|
this.textures = [];
|
|
|
|
// Set can show
|
|
this.canShow = false;
|
|
|
|
// Set loaded
|
|
this.loaded = false;
|
|
|
|
// Set compatible
|
|
this.compatible = true;
|
|
|
|
// Set first time show
|
|
this.firstTimeShow = true;
|
|
|
|
// Set context lost
|
|
this.contextLost = false;
|
|
|
|
// Set position
|
|
this.position = Logo.INITIAL_POSITION;
|
|
|
|
// Set orientation
|
|
this.orientation = Logo.INITIAL_ORIENTATION;
|
|
|
|
// Set velocity X
|
|
this.velocityX = 0;
|
|
|
|
// Set velocity Y
|
|
this.velocityY = 0;
|
|
|
|
// Set restore context frame
|
|
this.restoreContextFrame = Logo.NO_RESTORE_CONTEXT_FRAME;
|
|
|
|
// Set close timeout
|
|
this.closeTimeout = Logo.NO_CLOSE_TIMEOUT;
|
|
|
|
// Set start recording rotation
|
|
this.startRecordingRotation = false;
|
|
|
|
// Set recorded rotation
|
|
this.recordedRotation = 0;
|
|
|
|
// Set Tetris
|
|
this.tetris = new Tetris(this.application, this.message, wakeLock);
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Get WebGL context
|
|
this.gl = this.canvas.getContext("webgl", {
|
|
|
|
// Alpha
|
|
"alpha": true,
|
|
|
|
// Antialiad
|
|
"antialias": true,
|
|
|
|
// Depth
|
|
"depth": true,
|
|
|
|
// Power preference
|
|
"powerPreference": "default",
|
|
|
|
// Stencil
|
|
"stencil": false
|
|
});
|
|
|
|
// Check if WebGL isn't supported
|
|
if(this.gl === Logo.NO_WEBGL)
|
|
|
|
// Get experimental WebGL context
|
|
this.gl = this.canvas.getContext("experimental-webgl", {
|
|
|
|
// Alpha
|
|
"alpha": true,
|
|
|
|
// Antialias
|
|
"antialias": true,
|
|
|
|
// Depth
|
|
"depth": true,
|
|
|
|
// Power preference
|
|
"powerPreference": "default",
|
|
|
|
// Stencil
|
|
"stencil": false
|
|
});
|
|
|
|
// Check if WebGL is supported
|
|
if(this.gl !== Logo.NO_WEBGL) {
|
|
|
|
// Window unload event
|
|
$(window).on("unload", function() {
|
|
|
|
// Close and don't hide the logo
|
|
self.close(false);
|
|
});
|
|
|
|
// Update canvas size
|
|
this.updateCanvasSize();
|
|
|
|
// Get WEBGL lose context extension
|
|
var webglLoseContext = self.gl.getExtension("WEBGL_lose_context");
|
|
|
|
// Canvas WebGL context lost event
|
|
$(this.canvas).on("webglcontextlost", function(event) {
|
|
|
|
// Set context lost
|
|
self.contextLost = true;
|
|
|
|
// Check if not closing
|
|
if(self.closing === false) {
|
|
|
|
// Check if getting WEBGL lose context extension was successful
|
|
if(webglLoseContext !== Logo.NO_WEBGL_EXTENSION) {
|
|
|
|
// Prevent default
|
|
event.preventDefault();
|
|
|
|
// Set restore context frame
|
|
self.restoreContextFrame = requestAnimationFrame(function() {
|
|
|
|
// Set restore context frame to no restore context frame
|
|
self.restoreContextFrame = Logo.NO_RESTORE_CONTEXT_FRAME;
|
|
|
|
// Set close timeout
|
|
self.closeTimeout = setTimeout(function() {
|
|
|
|
// Set close timeout to no close timeout
|
|
self.closeTimeout = Logo.NO_CLOSE_TIMEOUT;
|
|
|
|
// Close
|
|
self.close();
|
|
|
|
}, Logo.CLOSE_IF_NOT_RESTORED_MILLISECONDS);
|
|
|
|
// Check if context is lost
|
|
if(self.gl.isContextLost() === true) {
|
|
|
|
// Restore context
|
|
webglLoseContext.restoreContext();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Close
|
|
self.close();
|
|
}
|
|
}
|
|
|
|
// Canvas WebGL context restored event
|
|
}).on("webglcontextrestored", function() {
|
|
|
|
// Check if restore context frame exists
|
|
if(self.restoreContextFrame !== Logo.NO_RESTORE_CONTEXT_FRAME) {
|
|
|
|
// Cancel restore context frame
|
|
cancelAnimationFrame(self.restoreContextFrame);
|
|
|
|
// Set restore context frame to no restore context frame
|
|
self.restoreContextFrame = Logo.NO_RESTORE_CONTEXT_FRAME;
|
|
}
|
|
|
|
// Check if close timeout exists
|
|
if(self.closeTimeout !== Logo.NO_CLOSE_TIMEOUT) {
|
|
|
|
// Clear close timeout
|
|
clearTimeout(self.closeTimeout);
|
|
|
|
// Set close timeout to no close timeout
|
|
self.closeTimeout = Logo.NO_CLOSE_TIMEOUT;
|
|
}
|
|
|
|
// Check if not closing
|
|
if(self.closing === false) {
|
|
|
|
// Cleanup
|
|
self.cleanUp();
|
|
|
|
// Initialize
|
|
self.initialize(self.canvas["width"], self.canvas["height"]).then(function() {
|
|
|
|
// Clear context lost
|
|
self.contextLost = false;
|
|
|
|
// Update size
|
|
self.updateSize();
|
|
|
|
// Draw
|
|
self.draw();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Clear context lost
|
|
self.contextLost = false;
|
|
|
|
// Close
|
|
self.close();
|
|
});
|
|
}
|
|
});
|
|
|
|
// Initialize
|
|
this.initialize(this.canvas["width"], this.canvas["height"]).then(function() {
|
|
|
|
// Run
|
|
self.run();
|
|
|
|
// Window resize event
|
|
$(window).on("resize", function() {
|
|
|
|
// Update size
|
|
self.updateSize();
|
|
|
|
// Draw
|
|
self.draw();
|
|
});
|
|
|
|
// Update size
|
|
self.updateSize();
|
|
|
|
// Draw
|
|
self.draw();
|
|
|
|
// Set last pointer state
|
|
self.lastPointerState = Logo.NO_POINTER_STATE;
|
|
|
|
// Document pointer move and mouse move logo event
|
|
$(document).on("pointermove mousemove.logo", function(event) {
|
|
|
|
// Check if event is a pointer move
|
|
if(event["type"] === "pointermove")
|
|
|
|
// Turn off document mouse move logo event
|
|
$(document).off("mousemove.logo");
|
|
|
|
// Check if not closing
|
|
if(self.closing === false) {
|
|
|
|
// Check if event's coordinates are within the screen
|
|
if(event["pageX"] < self.mainDisplay.width() && event["pageY"] < self.mainDisplay.height()) {
|
|
|
|
// Get current timestamp
|
|
var currentTimestamp = Date.now();
|
|
|
|
// Check if logo is showing, message isn't showing, and Tetris isn't showing
|
|
if(self.logoDisplay.hasClass("notLoaded") === false && self.logoDisplay.hasClass("hide") === false && self.mainDisplay.hasClass("logo") === true && self.logoDisplay.is(":visible") === true && self.message.isShown() === false && self.tetris.isShown() === false) {
|
|
|
|
// Check if primary pointer button is pressed
|
|
if((event["buttons"] & Common.PRIMARY_POINTER_BUTTON_BITMASK) !== 0) {
|
|
|
|
// Set start recording rotation
|
|
self.startRecordingRotation = true;
|
|
|
|
// Check if last pointer state exists
|
|
if(self.lastPointerState !== Logo.NO_POINTER_STATE) {
|
|
|
|
// Get timestamp change of pointer movement
|
|
var timestampChange = (currentTimestamp - self.lastPointerState["Timestamp"]) / Common.MILLISECONDS_IN_A_SECOND;
|
|
|
|
// Check if timestamp change isn't zero
|
|
if(timestampChange !== 0) {
|
|
|
|
// Get percent of where pointer was is in the canvas
|
|
var oldPercentX = (self.lastPointerState["Position X"] - $(self.canvas).offset()["left"]) / $(self.canvas).width();
|
|
var oldPercentY = (self.lastPointerState["Position Y"] - $(self.canvas).offset()["top"]) / $(self.canvas).height();
|
|
|
|
// Get percent of where pointer currently is in the canvas
|
|
var newPercentX = (event["pageX"] - $(self.canvas).offset()["left"]) / $(self.canvas).width();
|
|
var newPercentY = (event["pageY"] - $(self.canvas).offset()["top"]) / $(self.canvas).height();
|
|
|
|
// Get minimum and maximum percents in the X axis
|
|
if(oldPercentX <= newPercentX) {
|
|
var minimumPercentX = oldPercentX;
|
|
var maximumPercentX = newPercentX;
|
|
}
|
|
else {
|
|
var minimumPercentX = newPercentX;
|
|
var maximumPercentX = oldPercentX;
|
|
}
|
|
|
|
// Get minimum and maximum percents in the Y axis
|
|
if(oldPercentY <= newPercentY) {
|
|
var minimumPercentY = oldPercentY;
|
|
var maximumPercentY = newPercentY;
|
|
}
|
|
else {
|
|
var minimumPercentY = newPercentY;
|
|
var maximumPercentY = oldPercentY;
|
|
}
|
|
|
|
// Check if pointer moved over the logo
|
|
if(Logo.boundingBoxesCollide(minimumPercentX, minimumPercentY, maximumPercentX, maximumPercentY, Logo.LOGO_PERCENT_SIZE["Minimum X"], Logo.LOGO_PERCENT_SIZE["Minimum Y"], Logo.LOGO_PERCENT_SIZE["Maximum X"], Logo.LOGO_PERCENT_SIZE["Maximum Y"]) === true) {
|
|
|
|
// Get pointer distances
|
|
var distanceX = (event["pageX"] - self.lastPointerState["Position X"]) / $(document).width() * Logo.DISTANCE_RATIO_TO_SCREEN_WIDTH;
|
|
var distanceY = (event["pageY"] - self.lastPointerState["Position Y"]) / $(document).height() * Logo.DISTANCE_RATIO_TO_SCREEN_HEIGHT;
|
|
|
|
// Set pointer velocities
|
|
self.velocityX = distanceX / timestampChange * Logo.POINTER_VELOCITY_SCALE_FACTOR;
|
|
self.velocityY = distanceY / timestampChange * Logo.POINTER_VELOCITY_SCALE_FACTOR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update last pointer state
|
|
self.lastPointerState = {
|
|
|
|
// Timestamp
|
|
"Timestamp": currentTimestamp,
|
|
|
|
// Position X
|
|
"Position X": event["pageX"],
|
|
|
|
// Position Y
|
|
"Position Y": event["pageY"]
|
|
};
|
|
}
|
|
}
|
|
});
|
|
|
|
// Set loaded
|
|
self.loaded = true;
|
|
|
|
// Set that logo display is loaded
|
|
self.logoDisplay.removeClass("notLoaded");
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Close
|
|
self.close();
|
|
});
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Close
|
|
self.close();
|
|
}
|
|
}
|
|
|
|
// Show
|
|
show() {
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Set timeout
|
|
setTimeout(function() {
|
|
|
|
// Set can show
|
|
self.canShow = true;
|
|
|
|
}, Logo.INITIAL_SHOW_DELAY_MILLISECONDS);
|
|
|
|
// Show logo display
|
|
this.logoDisplay.removeClass("hide");
|
|
|
|
// Logo display transition end or timeout event
|
|
this.logoDisplay.transitionEndOrTimeout(function() {
|
|
|
|
// Set logo display to transition at the correct speed
|
|
self.logoDisplay.addClass("normalTransitionSpeed");
|
|
|
|
}, "opacity");
|
|
}
|
|
|
|
// Hide
|
|
hide() {
|
|
|
|
// Hide logo display
|
|
this.logoDisplay.addClass("hide");
|
|
}
|
|
|
|
// Allow showing
|
|
allowShowing() {
|
|
|
|
// Check if compatible
|
|
if(this.compatible === true)
|
|
|
|
// Allow main display to show logo
|
|
this.mainDisplay.addClass("logo");
|
|
}
|
|
|
|
// Private
|
|
|
|
// Update size
|
|
updateSize() {
|
|
|
|
// Update canvas size
|
|
this.updateCanvasSize();
|
|
|
|
// Update last dimensions
|
|
this.lastWidth = window["innerWidth"];
|
|
this.lastHeight = window["innerHeight"];
|
|
|
|
// Resize
|
|
this.resize(this.canvas["width"], this.canvas["height"]);
|
|
}
|
|
|
|
// Prevent showing
|
|
preventShowing() {
|
|
|
|
// Clear compatible
|
|
this.compatible = false;
|
|
|
|
// Don't allow main display to show logo
|
|
this.mainDisplay.removeClass("logo");
|
|
}
|
|
|
|
// Update canvas size
|
|
updateCanvasSize() {
|
|
|
|
// Update canvas size
|
|
this.canvas["width"] = this.canvas["clientWidth"];
|
|
this.canvas["height"] = this.canvas["clientHeight"];
|
|
}
|
|
|
|
// Initialize
|
|
initialize(width, height) {
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Set matrix array type
|
|
glMatrix["glMatrix"].setMatrixArrayType(Array);
|
|
|
|
// Check if getting EXT texture filter anisotropic extension was successful
|
|
var extTextureFilterAnisotropic = self.gl.getExtension("EXT_texture_filter_anisotropic");
|
|
if(extTextureFilterAnisotropic !== Logo.NO_WEBGL_EXTENSION) {
|
|
|
|
// Add anisotropic constants
|
|
self.gl["constructor"]["prototype"]["MAX_TEXTURE_MAX_ANISOTROPY_EXT"] = extTextureFilterAnisotropic["MAX_TEXTURE_MAX_ANISOTROPY_EXT"];
|
|
self.gl["constructor"]["prototype"]["TEXTURE_MAX_ANISOTROPY_EXT"] = extTextureFilterAnisotropic["TEXTURE_MAX_ANISOTROPY_EXT"];
|
|
}
|
|
|
|
// Set anisotropy
|
|
self.anisotropy = Logo.DEFAULT_ANISOTROPY;
|
|
|
|
// Get max anisotropy
|
|
if(typeof self.gl["MAX_TEXTURE_MAX_ANISOTROPY_EXT"] !== "undefined")
|
|
self.anisotropy = self.gl.getParameter(self.gl["MAX_TEXTURE_MAX_ANISOTROPY_EXT"]);
|
|
|
|
// Limit anisotropy
|
|
self.anisotropy = Math.min(self.anisotropy, Logo.MAX_ANISOTROPY);
|
|
|
|
// Set viewport size
|
|
self.gl.viewport(0, 0, width, height);
|
|
|
|
// Enable back face culling
|
|
self.gl.enable(self.gl["CULL_FACE"]);
|
|
self.gl.cullFace(self.gl["BACK"]);
|
|
self.gl.frontFace(self.gl["CCW"]);
|
|
|
|
// Enable depth testing
|
|
self.gl.enable(self.gl["DEPTH_TEST"]);
|
|
|
|
// Use closer fragments from depth test
|
|
self.gl.depthFunc(self.gl["LESS"]);
|
|
|
|
// Enabe writing to depth buffer
|
|
self.gl.depthMask(true);
|
|
|
|
// Enable alpha blending
|
|
self.gl.enable(self.gl["BLEND"]);
|
|
self.gl.blendFunc(self.gl["SRC_ALPHA"], self.gl["ONE_MINUS_SRC_ALPHA"]);
|
|
|
|
// Set clear color
|
|
self.gl.clearColor(0, 0, 0, 0);
|
|
|
|
// Load vertex shader source, fragment shader source, and model
|
|
Promise.all([$.ajax({
|
|
|
|
// URL
|
|
"url": "." + getResource(Logo.VERTEX_SHADER_FILE_LOCATION),
|
|
|
|
// Data type
|
|
"dataType": "text"
|
|
|
|
}), $.ajax({
|
|
|
|
// URL
|
|
"url": "." + getResource(Logo.FRAGMENT_SHADER_FILE_LOCATION),
|
|
|
|
// Data type
|
|
"dataType": "text"
|
|
|
|
}), $.getJSON("." + getResource(Logo.MODEL_FILE_LOCATION))]).then(function(resources) {
|
|
|
|
// Check if closing
|
|
if(self.closing === true) {
|
|
|
|
// Reject
|
|
reject();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Get vertex shader source
|
|
var vertexShaderSource = resources[0];
|
|
|
|
// Create vertex shader
|
|
var vertexShader = self.gl.createShader(self.gl["VERTEX_SHADER"]);
|
|
|
|
// Prepend constants to vertex shader source
|
|
vertexShaderSource = Logo.SHADER_CONSTANTS + "\n" + vertexShaderSource;
|
|
|
|
// Compile vertex shader
|
|
self.gl.shaderSource(vertexShader, vertexShaderSource);
|
|
self.gl.compileShader(vertexShader);
|
|
|
|
// Check if compiling vertex shader failed
|
|
if(self.gl.getShaderParameter(vertexShader, self.gl["COMPILE_STATUS"]) === false) {
|
|
|
|
// Delete vertex shader
|
|
self.gl.deleteShader(vertexShader);
|
|
|
|
// Reject error
|
|
reject("Compiling vertex shader failed.");
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Get fragment shader source
|
|
var fragmentShaderSource = resources[1];
|
|
|
|
// Create fragment shader
|
|
var fragmentShader = self.gl.createShader(self.gl["FRAGMENT_SHADER"]);
|
|
|
|
// Prepend constants to fragment shader source
|
|
fragmentShaderSource = Logo.SHADER_CONSTANTS + "\n" + fragmentShaderSource;
|
|
|
|
// Compile fragment shader
|
|
self.gl.shaderSource(fragmentShader, fragmentShaderSource);
|
|
self.gl.compileShader(fragmentShader);
|
|
|
|
// Check if compiling fragment shader failed
|
|
if(self.gl.getShaderParameter(fragmentShader, self.gl["COMPILE_STATUS"]) === false) {
|
|
|
|
// Delete vertex shader
|
|
self.gl.deleteShader(vertexShader);
|
|
|
|
// Delete fragment shader
|
|
self.gl.deleteShader(fragmentShader);
|
|
|
|
// Reject error
|
|
reject("Compiling fragment shader failed.");
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Create shader program
|
|
self.shaderProgram = self.gl.createProgram();
|
|
|
|
// Attach vertex shader to shader program
|
|
self.gl.attachShader(self.shaderProgram, vertexShader);
|
|
|
|
// Attach fragment shader to shader program
|
|
self.gl.attachShader(self.shaderProgram, fragmentShader);
|
|
|
|
// Link shader program
|
|
self.gl.linkProgram(self.shaderProgram);
|
|
|
|
// Check if linking shader program failed
|
|
if(self.gl.getProgramParameter(self.shaderProgram, self.gl["LINK_STATUS"]) === false) {
|
|
|
|
// Delete vertex shader
|
|
self.gl.deleteShader(vertexShader);
|
|
|
|
// Delete fragment shader
|
|
self.gl.deleteShader(fragmentShader);
|
|
|
|
// Reject error
|
|
reject("Linking shader program failed.");
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Delete vertex shader
|
|
self.gl.deleteShader(vertexShader);
|
|
|
|
// Delete fragment shader
|
|
self.gl.deleteShader(fragmentShader);
|
|
|
|
// Use shader program
|
|
self.gl.useProgram(self.shaderProgram);
|
|
|
|
// Get model
|
|
var model = resources[2];
|
|
|
|
// Create index buffer
|
|
var indexBuffer = self.gl.createBuffer();
|
|
|
|
// Append index buffer to list of buffers
|
|
self.buffers.push(indexBuffer);
|
|
|
|
// Bind index buffer
|
|
self.gl.bindBuffer(self.gl["ELEMENT_ARRAY_BUFFER"], indexBuffer);
|
|
|
|
// Move data into index buffer
|
|
self.gl.bufferData(self.gl["ELEMENT_ARRAY_BUFFER"], new Uint16Array(model["Textured Models"][0]["Indices"]), self.gl["STATIC_DRAW"]);
|
|
|
|
// Create positions buffer
|
|
var positionsBuffer = self.gl.createBuffer();
|
|
|
|
// Append positions buffer to list of buffers
|
|
self.buffers.push(positionsBuffer);
|
|
|
|
// Bind positions buffer
|
|
self.gl.bindBuffer(self.gl["ARRAY_BUFFER"], positionsBuffer);
|
|
|
|
// Move data into positions buffer
|
|
self.gl.bufferData(self.gl["ARRAY_BUFFER"], new Float32Array(model["Textured Models"][0]["Positions"]), self.gl["STATIC_DRAW"]);
|
|
|
|
// Get position attribute location in the shader program
|
|
var positionAttribute = self.gl.getAttribLocation(self.shaderProgram, "position");
|
|
|
|
// Set position attribute to the positions buffer
|
|
self.gl.vertexAttribPointer(positionAttribute, 3, self.gl["FLOAT"], false, 0, 0);
|
|
|
|
// Enable position attribute
|
|
self.gl.enableVertexAttribArray(positionAttribute);
|
|
|
|
// Create text coordinates buffer
|
|
var textureCoordinatesBuffer = self.gl.createBuffer();
|
|
|
|
// Append texture coordinates buffer to list of buffers
|
|
self.buffers.push(textureCoordinatesBuffer);
|
|
|
|
// Bind texture coordinates buffer
|
|
self.gl.bindBuffer(self.gl["ARRAY_BUFFER"], textureCoordinatesBuffer);
|
|
|
|
// Move data into texture coordinates buffer
|
|
self.gl.bufferData(self.gl["ARRAY_BUFFER"], new Float32Array(model["Textured Models"][0]["Texture Coordinates"]), self.gl["STATIC_DRAW"]);
|
|
|
|
// Get texture coordinate attribute location in the shader program
|
|
var textureCoordinateAttribute = self.gl.getAttribLocation(self.shaderProgram, "textureCoordinate");
|
|
|
|
// Set texture coordinate attribute to the texture coordinates buffer
|
|
self.gl.vertexAttribPointer(textureCoordinateAttribute, 2, self.gl["FLOAT"], false, 0, 0);
|
|
|
|
// Enable texture coordinate attribute
|
|
self.gl.enableVertexAttribArray(textureCoordinateAttribute);
|
|
|
|
// Create normals buffer
|
|
var normalsBuffer = self.gl.createBuffer();
|
|
|
|
// Append normals buffer to list of buffers
|
|
self.buffers.push(normalsBuffer);
|
|
|
|
// Bind normals buffer
|
|
self.gl.bindBuffer(self.gl["ARRAY_BUFFER"], normalsBuffer);
|
|
|
|
// Move data into normals buffer
|
|
self.gl.bufferData(self.gl["ARRAY_BUFFER"], new Float32Array(model["Textured Models"][0]["Normals"]), self.gl["STATIC_DRAW"]);
|
|
|
|
// Get normal attribute location in the shader program
|
|
var normalAttribute = self.gl.getAttribLocation(self.shaderProgram, "normal");
|
|
|
|
// Set normal attribute to the texture coordinates buffer
|
|
self.gl.vertexAttribPointer(normalAttribute, 3, self.gl["FLOAT"], false, 0, 0);
|
|
|
|
// Enable normal attribute
|
|
self.gl.enableVertexAttribArray(normalAttribute);
|
|
|
|
// Create tangents buffer
|
|
var tangentsBuffer = self.gl.createBuffer();
|
|
|
|
// Append tangents buffer to list of buffers
|
|
self.buffers.push(tangentsBuffer);
|
|
|
|
// Bind tangents buffer
|
|
self.gl.bindBuffer(self.gl["ARRAY_BUFFER"], tangentsBuffer);
|
|
|
|
// Move data into tangents buffer
|
|
self.gl.bufferData(self.gl["ARRAY_BUFFER"], new Float32Array(model["Textured Models"][0]["Tangents"]), self.gl["STATIC_DRAW"]);
|
|
|
|
// Get tangent attribute location in the shader program
|
|
var tangentAttribute = self.gl.getAttribLocation(self.shaderProgram, "tangent");
|
|
|
|
// Set tangent attribute to the texture coordinates buffer
|
|
self.gl.vertexAttribPointer(tangentAttribute, 3, self.gl["FLOAT"], false, 0, 0);
|
|
|
|
// Enable tangent attribute
|
|
self.gl.enableVertexAttribArray(tangentAttribute);
|
|
|
|
// Create bitangents buffer
|
|
var bitangentsBuffer = self.gl.createBuffer();
|
|
|
|
// Append bitangents buffer to list of buffers
|
|
self.buffers.push(bitangentsBuffer);
|
|
|
|
// Bind bitangents buffer
|
|
self.gl.bindBuffer(self.gl["ARRAY_BUFFER"], bitangentsBuffer);
|
|
|
|
// Move data into bitangents buffer
|
|
self.gl.bufferData(self.gl["ARRAY_BUFFER"], new Float32Array(model["Textured Models"][0]["Bitangents"]), self.gl["STATIC_DRAW"]);
|
|
|
|
// Get bitangent attribute location in the shader program
|
|
var bitangentAttribute = self.gl.getAttribLocation(self.shaderProgram, "bitangent");
|
|
|
|
// Set bitangent attribute to the texture coordinates buffer
|
|
self.gl.vertexAttribPointer(bitangentAttribute, 3, self.gl["FLOAT"], false, 0, 0);
|
|
|
|
// Enable bitangent attribute
|
|
self.gl.enableVertexAttribArray(bitangentAttribute);
|
|
|
|
// Load model's diffuse texture
|
|
self.loadTexture((model["Textured Models"][0]["Diffuse Texture Index"] === Logo.NO_TEXTURE_INDEX) ? "" : model["Textures"][model["Textured Models"][0]["Diffuse Texture Index"]]).then(function(diffuseTexture) {
|
|
|
|
// Check if closing
|
|
if(self.closing === true) {
|
|
|
|
// Reject
|
|
reject();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Get material diffuse map location in the shader
|
|
var materialDiffuseMapLocation = self.gl.getUniformLocation(self.shaderProgram, "material.diffuseMap");
|
|
|
|
// Load material diffuse map in the shader
|
|
self.gl.uniform1i(materialDiffuseMapLocation, 0);
|
|
|
|
// Bind diffuse map in the shader
|
|
self.gl.activeTexture(self.gl["TEXTURE0"]);
|
|
self.gl.bindTexture(self.gl["TEXTURE_2D"], diffuseTexture);
|
|
|
|
// Load model's specular texture
|
|
self.loadTexture((model["Textured Models"][0]["Specular Texture Index"] === Logo.NO_TEXTURE_INDEX) ? "" : model["Textures"][model["Textured Models"][0]["Specular Texture Index"]]).then(function(specularTexture) {
|
|
|
|
// Check if closing
|
|
if(self.closing === true) {
|
|
|
|
// Reject
|
|
reject();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Get material has specular map location in the shader
|
|
var materialHasSpecularMapLocation = self.gl.getUniformLocation(self.shaderProgram, "material.hasSpecularMap");
|
|
|
|
// Load material has specular map in the shader
|
|
self.gl.uniform1i(materialHasSpecularMapLocation, specularTexture !== Logo.INVALID_TEXTURE);
|
|
|
|
// Check if specular texture exists
|
|
if(specularTexture !== Logo.INVALID_TEXTURE) {
|
|
|
|
// Get material specular map location in the shader
|
|
var materialSpecularMapLocation = self.gl.getUniformLocation(self.shaderProgram, "material.specularMap");
|
|
|
|
// Load material specular map in the shader
|
|
self.gl.uniform1i(materialSpecularMapLocation, 1);
|
|
|
|
// Bind specular map in the shader
|
|
self.gl.activeTexture(self.gl["TEXTURE1"]);
|
|
self.gl.bindTexture(self.gl["TEXTURE_2D"], specularTexture);
|
|
}
|
|
|
|
// Load model's emission texture
|
|
self.loadTexture((model["Textured Models"][0]["Emission Texture Index"] === Logo.NO_TEXTURE_INDEX) ? "" : model["Textures"][model["Textured Models"][0]["Emission Texture Index"]]).then(function(emissionTexture) {
|
|
|
|
// Check if closing
|
|
if(self.closing === true) {
|
|
|
|
// Reject
|
|
reject();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Get material has emission map location in the shader
|
|
var materialHasEmissionMapLocation = self.gl.getUniformLocation(self.shaderProgram, "material.hasEmissionMap");
|
|
|
|
// Load material has emission map in the shader
|
|
self.gl.uniform1i(materialHasEmissionMapLocation, emissionTexture !== Logo.INVALID_TEXTURE);
|
|
|
|
// Check if emission texture exists
|
|
if(emissionTexture !== Logo.INVALID_TEXTURE) {
|
|
|
|
// Get material emission map location in the shader
|
|
var materialEmissionMapLocation = self.gl.getUniformLocation(self.shaderProgram, "material.emissionMap");
|
|
|
|
// Load material emission map in the shader
|
|
self.gl.uniform1i(materialEmissionMapLocation, 2);
|
|
|
|
// Bind emission map in the shader
|
|
self.gl.activeTexture(self.gl["TEXTURE2"]);
|
|
self.gl.bindTexture(self.gl["TEXTURE_2D"], emissionTexture);
|
|
}
|
|
|
|
// Load model's normal texture
|
|
self.loadTexture((model["Textured Models"][0]["Normal Texture Index"] === Logo.NO_TEXTURE_INDEX) ? "" : model["Textures"][model["Textured Models"][0]["Normal Texture Index"]]).then(function(normalTexture) {
|
|
|
|
// Check if closing
|
|
if(self.closing === true) {
|
|
|
|
// Reject
|
|
reject();
|
|
|
|
// Return
|
|
return;
|
|
}
|
|
|
|
// Get material has normal map location in the shader
|
|
var materialHasNormalMapLocation = self.gl.getUniformLocation(self.shaderProgram, "material.hasNormalMap");
|
|
|
|
// Load material has normal map in the shader
|
|
self.gl.uniform1i(materialHasNormalMapLocation, normalTexture !== Logo.INVALID_TEXTURE);
|
|
|
|
// Check if normal texture exists
|
|
if(normalTexture !== Logo.INVALID_TEXTURE) {
|
|
|
|
// Get material normal map location in the shader
|
|
var materialNormalMapLocation = self.gl.getUniformLocation(self.shaderProgram, "material.normalMap");
|
|
|
|
// Load material normal map in the shader
|
|
self.gl.uniform1i(materialNormalMapLocation, 3);
|
|
|
|
// Bind normal map in the shader
|
|
self.gl.activeTexture(self.gl["TEXTURE3"]);
|
|
self.gl.bindTexture(self.gl["TEXTURE_2D"], normalTexture);
|
|
}
|
|
|
|
// Get material shininess location in the shader
|
|
var materialShininessLocation = self.gl.getUniformLocation(self.shaderProgram, "material.shininess");
|
|
|
|
// Load material shininess in the shader
|
|
self.gl.uniform1f(materialShininessLocation, model["Textured Models"][0]["Shininess"]);
|
|
|
|
// Create view matrix
|
|
var viewMatrix = glMatrix["mat4"].create();
|
|
|
|
// Get view matrix location in the shader program
|
|
var viewMatrixLocation = self.gl.getUniformLocation(self.shaderProgram, "viewMatrix");
|
|
|
|
// Load view matrix in the shader program
|
|
self.gl.uniformMatrix4fv(viewMatrixLocation, false, viewMatrix);
|
|
|
|
// Update projection matrix
|
|
self.updateProjectionMatrix(width, height);
|
|
|
|
// Go through all lights
|
|
var lightTypeLocations = [];
|
|
var lightPositionLocations = [];
|
|
var lightConstantAttenuationFactorLocations = [];
|
|
var lightLinearAttenuationFactorLocations = [];
|
|
var lightQuadraticAttenuationFactorLocations = [];
|
|
var lightDirectionLocations = [];
|
|
var lightInnerCutOffAngleLocations = [];
|
|
var lightOuterCutOffAngleLocations = [];
|
|
var lightAmbientColorLocations = [];
|
|
var lightDiffuseColorLocations = [];
|
|
var lightSpecularColorLocations = [];
|
|
for(var i = 0; i < Logo.MAX_NUMBER_OF_LIGHTS; ++i) {
|
|
|
|
// Get light type location in the shader
|
|
lightTypeLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].type"));
|
|
|
|
// Get light position location in the shader
|
|
lightPositionLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].position"));
|
|
|
|
// Get gight constant attenuation factor location in the shader
|
|
lightConstantAttenuationFactorLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].constantAttenuationFactor"));
|
|
|
|
// Get light linear attenuation factor location in the shader
|
|
lightLinearAttenuationFactorLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].linearAttenuationFactor"));
|
|
|
|
// Get light quadratic attenuation factor location in the shader
|
|
lightQuadraticAttenuationFactorLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].quadraticAttenuationFactor"));
|
|
|
|
// Get light direction location in the shader
|
|
lightDirectionLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].direction"));
|
|
|
|
// Get light inner cut off angle location in the shader
|
|
lightInnerCutOffAngleLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].innerCutOffAngle"));
|
|
|
|
// Get light outer cut off angle location in the shader
|
|
lightOuterCutOffAngleLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].outerCutOffAngle"));
|
|
|
|
// Get light ambient color location in the shader
|
|
lightAmbientColorLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].ambientColor"));
|
|
|
|
// Get light diffuse color location in the shader
|
|
lightDiffuseColorLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].diffuseColor"));
|
|
|
|
// Get light specular color location in the shader
|
|
lightSpecularColorLocations.push(self.gl.getUniformLocation(self.shaderProgram, "lights[" + i.toFixed() + "].specularColor"));
|
|
}
|
|
|
|
// Create lights
|
|
var lights = [
|
|
Logo.createDirectionalLight(glMatrix["vec3"].normalize(glMatrix["vec3"].create(), glMatrix["vec3"].fromValues(0, 0, -1)), glMatrix["vec3"].fromValues(0.5, 0.5, 0.5), glMatrix["vec3"].fromValues(0.3, 0.3, 0.3), glMatrix["vec3"].fromValues(1, 1, 1))
|
|
];
|
|
|
|
// Go through all lights
|
|
for(var i = 0; i < lights["length"]; ++i) {
|
|
|
|
// Get light
|
|
var light = lights[i];
|
|
|
|
// Load light type
|
|
self.gl.uniform1i(lightTypeLocations[i], light["Type"]);
|
|
|
|
// Check if light is a point light
|
|
if(light["Type"] === Logo.POINT_LIGHT_TYPE) {
|
|
|
|
// Load light position
|
|
self.gl.uniform3fv(lightPositionLocations[i], light["Position"]);
|
|
|
|
// Load light constant attenuation factor
|
|
self.gl.uniform1f(lightConstantAttenuationFactorLocations[i], light["Constant Attenuation Factor"]);
|
|
|
|
// Load light linear attenuation factor
|
|
self.gl.uniform1f(lightLinearAttenuationFactorLocations[i], light["Linear Attenuation Factor"]);
|
|
|
|
// Load light quadratic attenuation factor
|
|
self.gl.uniform1f(lightQuadraticAttenuationFactorLocations[i], light["Quadratic Attenuation Factor"]);
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Check if light is a directional light
|
|
if(light["Type"] === Logo.DIRECTIONAL_LIGHT_TYPE)
|
|
|
|
// Load light direction
|
|
self.gl.uniform3fv(lightDirectionLocations[i], light["Direction"]);
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Check if light is a spot light
|
|
if(light["Type"] === Logo.SPOT_LIGHT_TYPE) {
|
|
|
|
// Load light position
|
|
self.gl.uniform3fv(lightPositionLocations[i], light["Position"]);
|
|
|
|
// Load light constant attenuation factor
|
|
self.gl.uniform1f(lightConstantAttenuationFactorLocations[i], light["Constant Attenuation Factor"]);
|
|
|
|
// Load light linear attenuation factor
|
|
self.gl.uniform1f(lightLinearAttenuationFactorLocations[i], light["Linear Attenuation Factor"]);
|
|
|
|
// Load light quadratic attenuation factor
|
|
self.gl.uniform1f(lightQuadraticAttenuationFactorLocations[i], light["Quadratic Attenuation Factor"]);
|
|
|
|
// Load light direction
|
|
self.gl.uniform3fv(lightDirectionLocations[i], light["Direction"]);
|
|
|
|
// Load light inner cut off angle
|
|
self.gl.uniform1f(lightInnerCutOffAngleLocations[i], light["Inner Cut Off Angle"]);
|
|
|
|
// Load light outer cut off angle
|
|
self.gl.uniform1f(lightOuterCutOffAngleLocations[i], light["Outer Cut Off Angle"]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load light ambient color
|
|
self.gl.uniform3fv(lightAmbientColorLocations[i], light["Ambient Color"]);
|
|
|
|
// Load light diffuse color
|
|
self.gl.uniform3fv(lightDiffuseColorLocations[i], light["Diffuse Color"]);
|
|
|
|
// Load light specular color
|
|
self.gl.uniform3fv(lightSpecularColorLocations[i], light["Specular Color"]);
|
|
}
|
|
|
|
// Check if some lights aren't used
|
|
if(i !== Logo.MAX_NUMBER_OF_LIGHTS)
|
|
|
|
// Load next light's type as invalid
|
|
self.gl.uniform1i(lightTypeLocations[i], Logo.INVALID_LIGHT_TYPE);
|
|
|
|
// Set number of indices
|
|
self.numberOfIndices = model["Textured Models"][0]["Indices"]["length"];
|
|
|
|
// Resolve
|
|
resolve();
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject("Loading normal texture failed.");
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject("Loading emission texture failed.");
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject("Loading specular texture failed.");
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject("Loading diffuse texture failed.");
|
|
});
|
|
|
|
// Catch errors
|
|
}).catch(function(error) {
|
|
|
|
// Reject error
|
|
reject("Loading resources failed.");
|
|
});
|
|
});
|
|
}
|
|
|
|
// Close
|
|
close(hideLogo = true) {
|
|
|
|
// Check if not already closing
|
|
if(this.closing === false) {
|
|
|
|
// Set closing
|
|
this.closing = true;
|
|
|
|
// Check if hiding logo
|
|
if(hideLogo === true) {
|
|
|
|
// Prevent showing
|
|
this.preventShowing();
|
|
}
|
|
|
|
// Clean up
|
|
this.cleanUp();
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
cleanUp() {
|
|
|
|
// Check if WebGL is supported
|
|
if(this.gl !== Logo.NO_WEBGL) {
|
|
|
|
// Check if shader program exists
|
|
if(this.shaderProgram !== Logo.NO_SHADER_PROGRAM) {
|
|
|
|
// Stop using shader
|
|
this.gl.useProgram(Logo.NO_SHADER_PROGRAM);
|
|
|
|
// Check if context wasn't lost
|
|
if(this.contextLost === false) {
|
|
|
|
// Delete shader program
|
|
this.gl.deleteProgram(this.shaderProgram);
|
|
}
|
|
|
|
// Set shader program to no shader program
|
|
this.shaderProgram = Logo.NO_SHADER_PROGRAM;
|
|
}
|
|
|
|
// Check if context wasn't lost
|
|
if(this.contextLost === false) {
|
|
|
|
// Go through all buffers
|
|
for(var i = 0; i < this.buffers["length"]; ++i) {
|
|
|
|
// Delete buffer
|
|
this.gl.deleteBuffer(this.buffers[i]);
|
|
}
|
|
}
|
|
|
|
// Empty buffers
|
|
this.buffers = [];
|
|
|
|
// Check if context wasn't lost
|
|
if(this.contextLost === false) {
|
|
|
|
// Go through all textures
|
|
for(var i = 0; i < this.textures["length"]; ++i) {
|
|
|
|
// Delete texture
|
|
this.gl.deleteTexture(this.textures[i]);
|
|
}
|
|
}
|
|
|
|
// Empty textures
|
|
this.textures = [];
|
|
}
|
|
}
|
|
|
|
// Run
|
|
run(currentTime = 0) {
|
|
|
|
// Check if last frame time hasn't been set
|
|
if(typeof this.lastFrameTime === "undefined")
|
|
|
|
// Set last frame time
|
|
this.lastFrameTime = currentTime;
|
|
|
|
// Set frame duration
|
|
this.frameDuration = currentTime - this.lastFrameTime;
|
|
|
|
// Set frame duration variation
|
|
this.frameDurationVariation = this.frameDuration * Logo.TIMING_FRAMES_PER_SECOND;
|
|
|
|
// Set last frame time
|
|
this.lastFrameTime = currentTime;
|
|
|
|
// Can if can show and loaded
|
|
if(this.canShow === true && this.loaded === true) {
|
|
|
|
// Check if first time show
|
|
if(this.firstTimeShow === true) {
|
|
|
|
// Clear first time show
|
|
this.firstTimeShow = false;
|
|
|
|
// Set initial X velocity
|
|
this.velocityX = Logo.INITIAL_X_VELOCITY;
|
|
}
|
|
|
|
// Check if X or Y velocity exists
|
|
if(this.velocityX !== 0 || this.velocityY !== 0) {
|
|
|
|
// Limit frame duration variation
|
|
this.frameDurationVariation = Math.min(Math.max(this.frameDurationVariation, Logo.MINIMUM_FRAME_DURATION_VARIATION), Logo.MAXIMUM_FRAME_DURATION_VARIATION);
|
|
|
|
// Rotate orientation on global X axis proportional to Y velocity
|
|
var rotationX = glMatrix["quat"].fromEuler(glMatrix["quat"].create(), this.velocityY * Logo.VELOCITY_APPLIED_SCALE_FACTOR * this.frameDurationVariation, 0, 0);
|
|
|
|
glMatrix["quat"].normalize(this.orientation, glMatrix["quat"].multiply(this.orientation, rotationX, this.orientation));
|
|
|
|
// Update Y velocity
|
|
this.velocityY *= 1 - Math.min(Math.max(Logo.VELOCITY_DEGRADE_SCALE_FACTOR * this.frameDurationVariation, 0), 1);
|
|
|
|
// Check if Y velocity is too small
|
|
if(Math.abs(this.velocityY) < Logo.MINIMUM_VELOCITY_THRESHOLD)
|
|
|
|
// Clear Y velocity
|
|
this.velocityY = 0;
|
|
|
|
// Rotate orientation on global Y axis proportional to X velocity
|
|
var rotationY = glMatrix["quat"].fromEuler(glMatrix["quat"].create(), 0, this.velocityX * Logo.VELOCITY_APPLIED_SCALE_FACTOR * this.frameDurationVariation, 0);
|
|
|
|
glMatrix["quat"].normalize(this.orientation, glMatrix["quat"].multiply(this.orientation, rotationY, this.orientation));
|
|
|
|
// Update X velocity
|
|
this.velocityX *= 1 - Math.min(Math.max(Logo.VELOCITY_DEGRADE_SCALE_FACTOR * this.frameDurationVariation, 0), 1);
|
|
|
|
// Check if X velocity is too small
|
|
if(Math.abs(this.velocityX) < Logo.MINIMUM_VELOCITY_THRESHOLD)
|
|
|
|
// Clear X velocity
|
|
this.velocityX = 0;
|
|
|
|
// Check if recording rotation
|
|
if(this.startRecordingRotation === true) {
|
|
|
|
// Update recorded rotation
|
|
this.recordedRotation += this.velocityX * Logo.VELOCITY_APPLIED_SCALE_FACTOR * this.frameDurationVariation;
|
|
|
|
// Check if recorded rotation exceeds Tetris threshold
|
|
if(Math.abs(this.recordedRotation) >= Logo.TETRIS_ROTATION_THRESHOLD) {
|
|
|
|
// Set start recording rotation to false
|
|
this.startRecordingRotation = false;
|
|
|
|
// Reset recorded rotation
|
|
this.recordedRotation = 0;
|
|
|
|
// Check if logo is showing, create or unlock display is shown, message display isn't shown, application isn't showing loading, application isn't disabled, and messages are allowed
|
|
if(this.logoDisplay.hasClass("notLoaded") === false && this.logoDisplay.hasClass("hide") === false && this.mainDisplay.hasClass("logo") === true && this.logoDisplay.is(":visible") === true && (this.application.isCreateDisplayShown() === true || this.application.isUnlockDisplayShown() === true) && this.message.isShown() === false && this.application.isShowingLoading() === false && this.application.isDisabled() === false && this.message.getAllowed() === true) {
|
|
|
|
// Show tetris
|
|
this.tetris.show();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw
|
|
this.draw();
|
|
}
|
|
|
|
// Otherwise check if frame duration variation is too large
|
|
else if(this.frameDurationVariation >= Logo.FORCE_REDRAW_FRAME_DURATION_VARIATION_THRESHOLD) {
|
|
|
|
// Draw
|
|
this.draw();
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Go through number of times to draw
|
|
for(var i = 0; i < Logo.FORCE_REDRAW_NUMBER_OF_TIMES - 1; ++i) {
|
|
|
|
// Set timeout
|
|
setTimeout(function() {
|
|
|
|
// Draw
|
|
self.draw();
|
|
|
|
}, i * Logo.FORCE_REDRAW_INTERVAL_SECONDS * Common.MILLISECONDS_IN_A_SECOND);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if not closing
|
|
if(this.closing === false) {
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Request animation frame
|
|
requestAnimationFrame(function(currentTime) {
|
|
|
|
// Run
|
|
self.run(currentTime / Common.MILLISECONDS_IN_A_SECOND);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Draw
|
|
draw() {
|
|
|
|
// Check if context wasn't lost and not closing
|
|
if(this.contextLost === false && this.closing === false) {
|
|
|
|
// Check if window dimensions changed without triggering a resize event
|
|
if(window["innerWidth"] !== this.lastWidth || window["innerHeight"] !== this.lastHeight) {
|
|
|
|
// Update size
|
|
this.updateSize();
|
|
}
|
|
|
|
// Clear color and depth buffers
|
|
this.gl.clear(this.gl["COLOR_BUFFER_BIT"] | this.gl["DEPTH_BUFFER_BIT"]);
|
|
|
|
// Create model matrix
|
|
var modelMatrix = Logo.createModelMatrix(this.position, this.orientation, glMatrix["vec3"].fromValues(1, 1, 1));
|
|
|
|
// Get model matrix location in the shader program
|
|
var modelMatrixLocation = this.gl.getUniformLocation(this.shaderProgram, "modelMatrix");
|
|
|
|
// Load model matrix in the shader program
|
|
this.gl.uniformMatrix4fv(modelMatrixLocation, false, modelMatrix);
|
|
|
|
// Draw model
|
|
this.gl.drawElements(this.gl["TRIANGLES"], this.numberOfIndices, this.gl["UNSIGNED_SHORT"], 0);
|
|
}
|
|
}
|
|
|
|
// Resize
|
|
resize(width, height) {
|
|
|
|
// Check if context wasn't lost and not closing
|
|
if(this.contextLost === false && this.closing === false) {
|
|
|
|
// Set viewport size
|
|
this.gl.viewport(0, 0, width, height);
|
|
|
|
// Update projection matrix
|
|
this.updateProjectionMatrix(width, height);
|
|
}
|
|
}
|
|
|
|
// Update projection matrix
|
|
updateProjectionMatrix(width, height) {
|
|
|
|
// Check if context wasn't lost and not closing
|
|
if(this.contextLost === false && this.closing === false) {
|
|
|
|
// Create projection matrix
|
|
var projectionMatrix = glMatrix["mat4"].perspective(glMatrix["mat4"].create(), glMatrix["glMatrix"].toRadian((height <= Logo.SMALL_HEIGHT_THRESHOLD) ? Logo.SMALL_HEIGHT_FIELD_OF_VIEW : Logo.FIELD_OF_VIEW), width / height, Logo.NEAR_PLANE, Logo.FAR_PLANE);
|
|
|
|
// Get projection matrix location in the shader program
|
|
var projectionMatrixLocation = this.gl.getUniformLocation(this.shaderProgram, "projectionMatrix");
|
|
|
|
// Load projection matrix in the shader program
|
|
this.gl.uniformMatrix4fv(projectionMatrixLocation, false, projectionMatrix);
|
|
}
|
|
}
|
|
|
|
// Create model matrix
|
|
static createModelMatrix(translation, orientation, scale) {
|
|
|
|
// Return scaled, rotated, and translated matrix
|
|
return glMatrix["mat4"].fromRotationTranslationScale(glMatrix["mat4"].create(), orientation, translation, scale);
|
|
}
|
|
|
|
// Load texture
|
|
loadTexture(file) {
|
|
|
|
// Set self
|
|
var self = this;
|
|
|
|
// Return promise
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
// Check if file is invalid
|
|
if(file["length"] === 0)
|
|
|
|
// Resolve invalid texture
|
|
resolve(Logo.INVALID_TEXTURE);
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Create image
|
|
var image = new Image();
|
|
|
|
// Image load event
|
|
$(image).on("load", function() {
|
|
|
|
// Check if image has dimensions that aren't powers of two
|
|
if((image["width"] & (image["width"] - 1)) !== 0 || (image["height"] & (image["height"] - 1)) !== 0) {
|
|
|
|
// Get new dimensions to make its dimensions powers of two
|
|
var canvasWidth = Math.pow(2, Math.ceil(Math.log(image["width"]) / Math.log(2)));
|
|
var canvasHeight = Math.pow(2, Math.ceil(Math.log(image["height"]) / Math.log(2)));
|
|
|
|
// Create canvas and get its context
|
|
var context = $("<canvas></canvas>").attr({
|
|
|
|
// Width
|
|
"width": canvasWidth.toFixed(),
|
|
|
|
// Height
|
|
"height": canvasHeight.toFixed()
|
|
|
|
}).get(0).getContext("2d");
|
|
|
|
// Smooth image when resizing
|
|
context["imageSmoothingEnabled"] = true;
|
|
context["mozImageSmoothingEnabled"] = true;
|
|
context["webkitImageSmoothingEnabled"] = true;
|
|
context["msImageSmoothingEnabled"] = true;
|
|
context["imageSmoothingQuality"] = "high";
|
|
|
|
// Draw image on canvas
|
|
context.drawImage(image, 0, 0, canvasWidth, canvasHeight);
|
|
|
|
// Get the image's data from the canvas
|
|
var imageData = context.getImageData(0, 0, canvasWidth, canvasHeight)["data"];
|
|
|
|
// Create texture
|
|
var texture = self.gl.createTexture();
|
|
|
|
// Append texture to list of textures
|
|
self.textures.push(texture);
|
|
|
|
// Bind texture
|
|
var maxTextureUnit = self.gl.getParameter(self.gl["MAX_COMBINED_TEXTURE_IMAGE_UNITS"]);
|
|
self.gl.activeTexture(self.gl["TEXTURE0"] + maxTextureUnit - 1);
|
|
self.gl.bindTexture(self.gl["TEXTURE_2D"], texture);
|
|
|
|
// Set texture wrapping
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_WRAP_S"], self.gl["CLAMP_TO_EDGE"]);
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_WRAP_T"], self.gl["CLAMP_TO_EDGE"]);
|
|
|
|
// Set texture filtering
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_MIN_FILTER"], self.gl["LINEAR_MIPMAP_LINEAR"]);
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_MAG_FILTER"], self.gl["LINEAR"]);
|
|
|
|
// Set max anisotropy
|
|
if(typeof self.gl["TEXTURE_MAX_ANISOTROPY_EXT"] !== "undefined")
|
|
self.gl.texParameterf(self.gl["TEXTURE_2D"], self.gl["TEXTURE_MAX_ANISOTROPY_EXT"], self.anisotropy);
|
|
|
|
// Create texture
|
|
self.gl.texImage2D(self.gl["TEXTURE_2D"], 0, self.gl["RGBA"], canvasWidth, canvasHeight, 0, self.gl["RGBA"], self.gl["UNSIGNED_BYTE"], new Uint8Array(imageData));
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Create texture
|
|
var texture = self.gl.createTexture();
|
|
|
|
// Append texture to list of textures
|
|
self.textures.push(texture);
|
|
|
|
// Bind texture
|
|
var maxTextureUnit = self.gl.getParameter(self.gl["MAX_COMBINED_TEXTURE_IMAGE_UNITS"]);
|
|
self.gl.activeTexture(self.gl["TEXTURE0"] + maxTextureUnit - 1);
|
|
self.gl.bindTexture(self.gl["TEXTURE_2D"], texture);
|
|
|
|
// Set texture wrapping
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_WRAP_S"], self.gl["CLAMP_TO_EDGE"]);
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_WRAP_T"], self.gl["CLAMP_TO_EDGE"]);
|
|
|
|
// Set texture filtering
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_MIN_FILTER"], self.gl["LINEAR_MIPMAP_LINEAR"]);
|
|
self.gl.texParameteri(self.gl["TEXTURE_2D"], self.gl["TEXTURE_MAG_FILTER"], self.gl["LINEAR"]);
|
|
|
|
// Set max anisotropy
|
|
if(typeof self.gl["TEXTURE_MAX_ANISOTROPY_EXT"] !== "undefined")
|
|
self.gl.texParameterf(self.gl["TEXTURE_2D"], self.gl["TEXTURE_MAX_ANISOTROPY_EXT"], self.anisotropy);
|
|
|
|
// Create texture
|
|
self.gl.texImage2D(self.gl["TEXTURE_2D"], 0, self.gl["RGBA"], self.gl["RGBA"], self.gl["UNSIGNED_BYTE"], image);
|
|
}
|
|
|
|
// Create mipmaps
|
|
self.gl.generateMipmap(self.gl["TEXTURE_2D"]);
|
|
|
|
// Resolve texture
|
|
resolve(texture);
|
|
|
|
// Image error event
|
|
}).on("error", function(error) {
|
|
|
|
// Reject error
|
|
reject(error);
|
|
});
|
|
|
|
// Set image source
|
|
image["src"] = file;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Create directional light
|
|
static createDirectionalLight(direction, ambientColor, diffuseColor, specularColor) {
|
|
|
|
// Return directional light
|
|
return {
|
|
|
|
// Type
|
|
"Type": Logo.DIRECTIONAL_LIGHT_TYPE,
|
|
|
|
// Direction
|
|
"Direction": direction,
|
|
|
|
// Ambient color
|
|
"Ambient Color": ambientColor,
|
|
|
|
// Diffuse color
|
|
"Diffuse Color": diffuseColor,
|
|
|
|
// Specular color
|
|
"Specular Color": specularColor
|
|
};
|
|
}
|
|
|
|
// Create point light
|
|
static createPointLight(position, constantAttenuationFactor, linearAttenuationFactor, quadraticAttenuationFactor, ambientColor, diffuseColor, specularColor) {
|
|
|
|
// Return point light
|
|
return {
|
|
|
|
// Type
|
|
"Type": Logo.POINT_LIGHT_TYPE,
|
|
|
|
// Position
|
|
"Position": position,
|
|
|
|
// Constant attenuation factor
|
|
"Constant Attenuation Factor": constantAttenuationFactor,
|
|
|
|
// Linear attenuation factor
|
|
"Linear Attenuation Factor": linearAttenuationFactor,
|
|
|
|
// Quadratic attenuation factor
|
|
"Quadratic Attenuation Factor": quadraticAttenuationFactor,
|
|
|
|
// Ambient color
|
|
"Ambient Color": ambientColor,
|
|
|
|
// Diffuse color
|
|
"Diffuse Color": diffuseColor,
|
|
|
|
// Specular color
|
|
"Specular Color": specularColor
|
|
};
|
|
}
|
|
|
|
// Create spot light
|
|
static createSpotLight(position, direction, constantAttenuationFactor, linearAttenuationFactor, quadraticAttenuationFactor, innerCutOffAngle, outerCutOffAngle, ambientColor, diffuseColor, specularColor) {
|
|
|
|
// Return spot light
|
|
return {
|
|
|
|
// Type
|
|
"Type": Logo.SPOT_LIGHT_TYPE,
|
|
|
|
// Position
|
|
"Position": position,
|
|
|
|
// Direction
|
|
"Direction": direction,
|
|
|
|
// Constant attenuation factor
|
|
"Constant Attenuation Factor": constantAttenuationFactor,
|
|
|
|
// Linear attenuation factor
|
|
"Linear Attenuation Factor": linearAttenuationFactor,
|
|
|
|
// Quadratic attenuation factor
|
|
"Quadratic Attenuation Factor": quadraticAttenuationFactor,
|
|
|
|
// Inner cut off angle
|
|
"Inner Cut Off Angle": Math.cos(glMatrix["glMatrix"].toRadian(innerCutOffAngle)),
|
|
|
|
// Outer cut off angle
|
|
"Outer Cut Off Angle": Math.cos(glMatrix["glMatrix"].toRadian(outerCutOffAngle)),
|
|
|
|
// Ambient color
|
|
"Ambient Color": ambientColor,
|
|
|
|
// Diffuse color
|
|
"Diffuse Color": diffuseColor,
|
|
|
|
// Specular color
|
|
"Specular Color": specularColor
|
|
};
|
|
}
|
|
|
|
// Bounding boxes collide
|
|
static boundingBoxesCollide(minimumXOne, minimumYOne, maximumXOne, maximumYOne, minimumXTwo, minimumYTwo, maximumXTwo, maximumYTwo) {
|
|
|
|
// Check if an X axis collision occurred
|
|
if((minimumXOne <= minimumXTwo && maximumXOne >= minimumXTwo) || (minimumXOne <= maximumXTwo && maximumXOne >= maximumXTwo) || (minimumXOne >= minimumXTwo && maximumXOne <= maximumXTwo))
|
|
|
|
// Check if a Y axis collision occurred
|
|
if((minimumYOne <= minimumYTwo && maximumYOne >= minimumYTwo) || (minimumYOne <= maximumYTwo && maximumYOne >= maximumYTwo) || (minimumYOne >= minimumYTwo && maximumYOne <= maximumYTwo))
|
|
|
|
// Return true
|
|
return true;
|
|
|
|
// Return false
|
|
return false;
|
|
}
|
|
|
|
// No WebGL
|
|
static get NO_WEBGL() {
|
|
|
|
// Return no WebGL
|
|
return null;
|
|
}
|
|
|
|
// No WebGL extension
|
|
static get NO_WEBGL_EXTENSION() {
|
|
|
|
// Return no WebGL extension
|
|
return null;
|
|
}
|
|
|
|
// Timing frames per second
|
|
static get TIMING_FRAMES_PER_SECOND() {
|
|
|
|
// Return timing frames per second
|
|
return 60;
|
|
}
|
|
|
|
// No shader program
|
|
static get NO_SHADER_PROGRAM() {
|
|
|
|
// Return no shader program
|
|
return null;
|
|
}
|
|
|
|
// Field of view
|
|
static get FIELD_OF_VIEW() {
|
|
|
|
// Return field of view
|
|
return 70;
|
|
}
|
|
|
|
// Small height field of view
|
|
static get SMALL_HEIGHT_FIELD_OF_VIEW() {
|
|
|
|
// Return small height field of view
|
|
return 55;
|
|
}
|
|
|
|
// Small height threshold
|
|
static get SMALL_HEIGHT_THRESHOLD() {
|
|
|
|
// Return small height threshold
|
|
return 300;
|
|
}
|
|
|
|
// Near plane
|
|
static get NEAR_PLANE() {
|
|
|
|
// Return near plane
|
|
return 10;
|
|
}
|
|
|
|
// Far plane
|
|
static get FAR_PLANE() {
|
|
|
|
// Return far plane
|
|
return 50;
|
|
}
|
|
|
|
// Default anisotropy
|
|
static get DEFAULT_ANISOTROPY() {
|
|
|
|
// Return default anisotropy
|
|
return 1;
|
|
}
|
|
|
|
// Max anisotropy
|
|
static get MAX_ANISOTROPY() {
|
|
|
|
// Return max anisotropy
|
|
return 4;
|
|
}
|
|
|
|
// Max number of lights
|
|
static get MAX_NUMBER_OF_LIGHTS() {
|
|
|
|
// Return max lights
|
|
return 1;
|
|
}
|
|
|
|
// Invalid light type
|
|
static get INVALID_LIGHT_TYPE() {
|
|
|
|
// Return invalid light type
|
|
return 0;
|
|
}
|
|
|
|
// Directional light type
|
|
static get DIRECTIONAL_LIGHT_TYPE() {
|
|
|
|
// Return directional light type
|
|
return Logo.INVALID_LIGHT_TYPE + 1;
|
|
}
|
|
|
|
// Point light type
|
|
static get POINT_LIGHT_TYPE() {
|
|
|
|
// Return point light type
|
|
return Logo.DIRECTIONAL_LIGHT_TYPE + 1;
|
|
}
|
|
|
|
// Spot light type
|
|
static get SPOT_LIGHT_TYPE() {
|
|
|
|
// Return spot light type
|
|
return Logo.POINT_LIGHT_TYPE + 1;
|
|
}
|
|
|
|
// Shader constants
|
|
static get SHADER_CONSTANTS() {
|
|
|
|
// Return invalid light type
|
|
return "const int INVALID_LIGHT_TYPE = " + Logo.INVALID_LIGHT_TYPE + ";\n" +
|
|
|
|
// Directional light type
|
|
"const int DIRECTIONAL_LIGHT_TYPE = " + Logo.DIRECTIONAL_LIGHT_TYPE + ";\n" +
|
|
|
|
// Point light type
|
|
"const int POINT_LIGHT_TYPE = " + Logo.POINT_LIGHT_TYPE + ";\n" +
|
|
|
|
// Spot light type
|
|
"const int SPOT_LIGHT_TYPE = " + Logo.SPOT_LIGHT_TYPE + ";\n" +
|
|
|
|
// Max number of lights
|
|
"const int MAX_NUMBER_OF_LIGHTS = " + Logo.MAX_NUMBER_OF_LIGHTS + ";";
|
|
}
|
|
|
|
// No texture index
|
|
static get NO_TEXTURE_INDEX() {
|
|
|
|
// Return no texture index
|
|
return -1;
|
|
}
|
|
|
|
// Invalid texture
|
|
static get INVALID_TEXTURE() {
|
|
|
|
// Return invalid texture
|
|
return null;
|
|
}
|
|
|
|
// No pointer state
|
|
static get NO_POINTER_STATE() {
|
|
|
|
// Return no pointer state
|
|
return null;
|
|
}
|
|
|
|
// Logo percent size
|
|
static get LOGO_PERCENT_SIZE() {
|
|
|
|
// Return logo percent size
|
|
return {
|
|
|
|
// Minimum X
|
|
"Minimum X": 0.212,
|
|
|
|
// Minimum Y
|
|
"Minimum Y": 0.360,
|
|
|
|
// Maximum X
|
|
"Maximum X": 0.794,
|
|
|
|
// Maximum Y
|
|
"Maximum Y": 0.646
|
|
};
|
|
}
|
|
|
|
// Velocity applied scale factor
|
|
static get VELOCITY_APPLIED_SCALE_FACTOR() {
|
|
|
|
// Return velocity applied scale factor
|
|
return 0.0012;
|
|
}
|
|
|
|
// Velocity degrade scale factor
|
|
static get VELOCITY_DEGRADE_SCALE_FACTOR() {
|
|
|
|
// Return velocity degrade scale factor
|
|
return 0.06;
|
|
}
|
|
|
|
// Minimum velocity threshold
|
|
static get MINIMUM_VELOCITY_THRESHOLD() {
|
|
|
|
// Return minimum velocity threshold
|
|
return 0.01;
|
|
}
|
|
|
|
// Vertex shader file location
|
|
static get VERTEX_SHADER_FILE_LOCATION() {
|
|
|
|
// Return vertex shader file location
|
|
return "./shaders/logo.vert";
|
|
}
|
|
|
|
// Fragment shader file location
|
|
static get FRAGMENT_SHADER_FILE_LOCATION() {
|
|
|
|
// Return fragment shader file location
|
|
return "./shaders/logo.frag";
|
|
}
|
|
|
|
// Model file location
|
|
static get MODEL_FILE_LOCATION() {
|
|
|
|
// Return model file location
|
|
return "./models/mwc.json";
|
|
}
|
|
|
|
// Minimum frame duration variation
|
|
static get MINIMUM_FRAME_DURATION_VARIATION() {
|
|
|
|
// Return minimum frame duration variation
|
|
return -2;
|
|
}
|
|
|
|
// Maximum frame duration variation
|
|
static get MAXIMUM_FRAME_DURATION_VARIATION() {
|
|
|
|
// Return maximum frame duration variation
|
|
return 2;
|
|
}
|
|
|
|
// Initial X velocity
|
|
static get INITIAL_X_VELOCITY() {
|
|
|
|
// Return initial X velocity
|
|
return 13500;
|
|
}
|
|
|
|
// Initial position
|
|
static get INITIAL_POSITION() {
|
|
|
|
// Return initial position
|
|
return glMatrix["vec3"].fromValues(0, 0, -30);
|
|
}
|
|
|
|
// Initial orientation
|
|
static get INITIAL_ORIENTATION() {
|
|
|
|
// Return initial orientation
|
|
return glMatrix["quat"].normalize(glMatrix["quat"].create(), glMatrix["quat"].setAxisAngle(glMatrix["quat"].create(), glMatrix["vec3"].fromValues(0, 1, 0), glMatrix["glMatrix"].toRadian(-90)));
|
|
}
|
|
|
|
// Pointer velocity scale factor
|
|
static get POINTER_VELOCITY_SCALE_FACTOR() {
|
|
|
|
// Return pointer velocity scale factor
|
|
return 0.6;
|
|
}
|
|
|
|
// Force redraw frame duration variation threshold
|
|
static get FORCE_REDRAW_FRAME_DURATION_VARIATION_THRESHOLD() {
|
|
|
|
// Return force redraw frame duration variation threshold
|
|
return 20;
|
|
}
|
|
|
|
// Force redraw number of times
|
|
static get FORCE_REDRAW_NUMBER_OF_TIMES() {
|
|
|
|
// Return force redraw number of times
|
|
return 10;
|
|
}
|
|
|
|
// Force redraw interval seconds
|
|
static get FORCE_REDRAW_INTERVAL_SECONDS() {
|
|
|
|
// Return force redraw interval seconds
|
|
return 1;
|
|
}
|
|
|
|
// Distance ratio to screen width
|
|
static get DISTANCE_RATIO_TO_SCREEN_WIDTH() {
|
|
|
|
// Return distance ratio to screen width
|
|
return 600;
|
|
}
|
|
|
|
// Distance ratio to screen height
|
|
static get DISTANCE_RATIO_TO_SCREEN_HEIGHT() {
|
|
|
|
// Return distance ratio to screen height
|
|
return 600;
|
|
}
|
|
|
|
// Initial show delay milliseconds
|
|
static get INITIAL_SHOW_DELAY_MILLISECONDS() {
|
|
|
|
// Return initial show delay milliseconds
|
|
return 70;
|
|
}
|
|
|
|
// No restore context frame
|
|
static get NO_RESTORE_CONTEXT_FRAME() {
|
|
|
|
// Return no context frame
|
|
return null;
|
|
}
|
|
|
|
// No close timeout
|
|
static get NO_CLOSE_TIMEOUT() {
|
|
|
|
// Return no close timeout
|
|
return null;
|
|
}
|
|
|
|
// Close if not restored milliseconds
|
|
static get CLOSE_IF_NOT_RESTORED_MILLISECONDS() {
|
|
|
|
// Return close if not restored milliseconds
|
|
return 50;
|
|
}
|
|
|
|
// Tetris rotation threshold
|
|
static get TETRIS_ROTATION_THRESHOLD() {
|
|
|
|
// Return Tetris rotation threshold
|
|
return 180 * 3;
|
|
}
|
|
}
|
|
|
|
|
|
// Main function
|
|
|
|
// Set global object's logo
|
|
globalThis["Logo"] = Logo;
|