/* * TSDGLShader.js * Keynote HTML Player * * Created by Tungwei Cheng * Copyright (c) 2018-2019 Apple Inc. All rights reserved. */ // Uniforms var kTSDGLShaderUniformColor = "Color"; var kTSDGLShaderUniformDuration = "Duration"; var kTSDGLShaderUniformMotionBlurVector = "MotionBlurVector"; var kTSDGLShaderUniformMVPMatrix = "MVPMatrix"; var kTSDGLShaderUniformOpacity = "Opacity"; var kTSDGLShaderUniformParticleTexture = "ParticleTexture"; var kTSDGLShaderUniformPercent = "Percent"; var kTSDGLShaderUniformPreviousMVPMatrix = "PreviousMVPMatrix"; var kTSDGLShaderUniformTexture = "Texture"; var kTSDGLShaderUniformTextureMatrix = "TextureMatrix"; var kTSDGLShaderUniformTextureSize = "TextureSize"; var kTSDGLShaderUniformTexture2 = "Texture2"; var kTSDGLShaderUniformTexture2Matrix = "Texture2Matrix"; var kTSDGLShaderUniformTexture2Size = "Texture2Size"; var kTSDGLShaderUniformVelocityScale = "VelocityScale"; var kTSDGLShaderUniformVelocityTexture = "VelocityTexture"; // Attributes var kTSDGLShaderAttributeCenter = "Center"; // center point of this particle var kTSDGLShaderAttributeColor = "Color"; var kTSDGLShaderAttributeLifeSpan = "LifeSpan"; var kTSDGLShaderAttributeNormal = "Normal"; var kTSDGLShaderAttributeParticleTexCoord = "ParticleTexCoord"; var kTSDGLShaderAttributePosition = "Position"; var kTSDGLShaderAttributePreviousPosition = "PreviousPosition"; var kTSDGLShaderAttributeRotation = "Rotation"; var kTSDGLShaderAttributeScale = "Scale"; var kTSDGLShaderAttributeSpeed = "Speed"; var kTSDGLShaderAttributeTexCoord = "TexCoord"; var kTSDGLShaderUniformRotationMax = "RotationMax"; var kTSDGLShaderUniformSpeedMax = "SpeedMax"; var TSDGLShaderQualifierType = { Unknown: 0, ///< ERROR Int: 1, // < GLSL type "int" Float: 2, // < GLSL type "float" Vec2: 3, // < GLSL type "vec2" Vec3: 4, // < GLSL type "vec3" Vec4: 5, // < GLSL type "vec4" Mat3: 6, // < GLSL type "mat3" Mat4: 7 // < GLSL type "mat4" }; function TSDGLShaderQualifierTypeFromGLenum(type) { var result = TSDGLShaderQualifierType.Unknown; switch (type) { case GL_FLOAT: result = TSDGLShaderQualifierType.Float; break; case GL_FLOAT_VEC2: result = TSDGLShaderQualifierType.Vec2; break; case GL_FLOAT_VEC3: result = TSDGLShaderQualifierType.Vec3; break; case GL_FLOAT_VEC4: result = TSDGLShaderQualifierType.Vec4; break; case GL_BOOL: case GL_SAMPLER_2D: case GL_INT: result = TSDGLShaderQualifierType.Int; break; case GL_FLOAT_MAT3: result = TSDGLShaderQualifierType.Mat3; break; case GL_FLOAT_MAT4: result = TSDGLShaderQualifierType.Mat4; break; case GL_INT_VEC2: case GL_INT_VEC3: case GL_INT_VEC4: case GL_BOOL_VEC2: case GL_BOOL_VEC3: case GL_BOOL_VEC4: case GL_FLOAT_MAT2: case GL_SAMPLER_CUBE: default: console.log("Unimplemented GLenum type " + type); break; } return result; } var TSDGLShader = Class.create({ initialize: function(gl) { this.gl = gl; this._uniforms = {}; this.name = ""; this.programObject = null; this.isActive = false; this._uniformsNeedingUpdate = []; }, initWithDefaultTextureShader: function() { this.initWithShaderFileNames("defaultTexture", "defaultTexture"); this.setGLint(0, kTSDGLShaderUniformTexture); }, initWithDefaultTextureAndOpacityShader: function() { this.initWithShaderFileNames("defaultTexture", "defaultTextureAndOpacity"); }, initWithDefaultHorizontalBlurShader: function() { this.initWithShaderFileNames("horizontalGaussianBlur", "horizontalGaussianBlur"); this.setGLint(0, kTSDGLShaderUniformTexture); }, initWithDefaultVerticalBlurShader: function() { this.initWithShaderFileNames("verticalGaussianBlur", "verticalGaussianBlur"); this.setGLint(0, kTSDGLShaderUniformTexture); }, initWithContentsShader: function() { this.initWithShaderFileNames("contents", "contents"); }, initWithContentsAndOpacityShader: function() { this.initWithShaderFileNames("contentsAndOpacity", "contentsAndOpacity"); }, initWithShaderFileNames: function(vertexShaderFileName, fragmentShaderFileName) { var vertexString = KNWebGLShader[vertexShaderFileName].vertex; var fragmentString = KNWebGLShader[fragmentShaderFileName].fragment; this.initWithShaders(vertexString, fragmentString); }, initWithShaders: function(vertexString, fragmentString) { var gl = this.gl; var vertexShader = KNWebGLUtil.loadShader(gl, gl.VERTEX_SHADER, vertexString); var fragmentShader = KNWebGLUtil.loadShader(gl, gl.FRAGMENT_SHADER, fragmentString); this.programObject = KNWebGLUtil.createShaderProgram(gl, vertexShader, fragmentShader); this.p_updateUniformsAndAttributesFromShader(); }, p_updateUniformsAndAttributesFromShader: function() { var gl = this.gl; var programObject = this.programObject; var uniformsCount = -1; uniformsCount = gl.getProgramParameter(programObject, gl.ACTIVE_UNIFORMS); for (var i = 0; i < uniformsCount; i++) { var activeInfo = gl.getActiveUniform(programObject, i); var name = activeInfo.name; var type = activeInfo.type; var size = activeInfo.size; // Add uniform to cache var qualifierType = TSDGLShaderQualifierTypeFromGLenum(type); this.shaderQualifierForUniform(name, qualifierType); } // Update attributes var attributesCount = -1; attributesCount = gl.getProgramParameter(programObject, gl.ACTIVE_ATTRIBUTES); for (var i = 0; i < attributesCount; i++) { var activeInfo = gl.getActiveAttrib(programObject, i); var name = activeInfo.name; var type = activeInfo.type; var size = activeInfo.size; // Add attribute location to cache this.locationForAttribute(name); } }, shaderQualifierForUniform: function(uniform, qualifierType) { var gl = this.gl; var qualifier = this._uniforms[uniform]; if (!qualifier) { switch (qualifierType) { case TSDGLShaderQualifierType.Unknown: console.log("Unknown Shader Qualifier Type!"); break; case TSDGLShaderQualifierType.Int: qualifier = new TSDGLShaderQualifierInt(gl, uniform); break; case TSDGLShaderQualifierType.Float: qualifier = new TSDGLShaderQualifierFloat(gl, uniform); break; case TSDGLShaderQualifierType.Vec2: qualifier = new TSDGLShaderQualifierPoint2D(gl, uniform); break; case TSDGLShaderQualifierType.Vec3: qualifier = new TSDGLShaderQualifierPoint3D(gl, uniform); break; case TSDGLShaderQualifierType.Vec4: qualifier = new TSDGLShaderQualifierPoint4D(gl, uniform); break; case TSDGLShaderQualifierType.Mat3: qualifier = new TSDGLShaderQualifierMat3(gl, uniform); break; case TSDGLShaderQualifierType.Mat4: qualifier = new TSDGLShaderQualifierMat4(gl, uniform); break; } qualifier.updateUniformLocationWithShaderProgramObject(this.programObject); this._uniforms[uniform] = qualifier; } return qualifier; }, setGLint: function(newInt, uniform) { var qualifier = this.shaderQualifierForUniform(uniform, TSDGLShaderQualifierType.Int); qualifier.setProposedGLintValue(newInt); if (qualifier._needsUpdate) { this._uniformsNeedingUpdate.push(qualifier); } this.p_setQualifiersIfNecessary(); }, setGLFloat: function(newFloat, uniform) { var qualifier = this.shaderQualifierForUniform(uniform, TSDGLShaderQualifierType.Float); qualifier.setProposedGLfloatValue(newFloat); if (qualifier._needsUpdate) { this._uniformsNeedingUpdate.push(qualifier); } this.p_setQualifiersIfNecessary(); }, setPoint2D: function(newPoint2D, uniform) { var qualifier = this.shaderQualifierForUniform(uniform, TSDGLShaderQualifierType.Vec2); qualifier.setProposedGLPoint2DValue(newPoint2D); if (qualifier._needsUpdate) { this._uniformsNeedingUpdate.push(qualifier); } this.p_setQualifiersIfNecessary(); }, setMat4WithTransform3D: function(aTransform3D, uniform) { var qualifier = this.shaderQualifierForUniform(uniform, TSDGLShaderQualifierType.Mat4); qualifier.setProposedTransform3D(aTransform3D); if (qualifier._needsUpdate) { this._uniformsNeedingUpdate.push(qualifier); } this.p_setQualifiersIfNecessary(); }, locationForUniform: function(uniform) { var location; var shaderQualifier = this._uniforms[uniform]; if (shaderQualifier) { location = shaderQualifier._uniformLocation; } if (!location) { location = this.gl.getUniformLocation(this.programObject, uniform); } return location; }, locationForAttribute: function(attribute) { if (!this._attributeLocations) { this._attributeLocations = {}; } var location = this._attributeLocations[attribute]; if (location === undefined) { location = -1; } if (location < 0) { location = this.gl.getAttribLocation(this.programObject, attribute); this._attributeLocations[attribute] = location; } return location; }, p_setQualifiersIfNecessary: function() { if (!this.isActive) { return; } if (this._uniformsNeedingUpdate.length === 0) { return; } // Look through all the newly-set qualifiers for (var i = 0, length = this._uniformsNeedingUpdate.length; i < length; i++) { var proposedQualifier = this._uniformsNeedingUpdate[i]; if (proposedQualifier._uniformLocation === -1) { proposedQualifier.updateUniformLocationWithShaderProgramObject(this.programObject); } proposedQualifier.setGLUniformWithShader(this.gl, this); } this._uniformsNeedingUpdate = []; }, activate: function() { var gl = this.gl; if (!this.isActive) { gl.useProgram(this.programObject); this.isActive = true; } this.p_setQualifiersIfNecessary(); }, deactivate: function() { if (this.isActive) { //gl.useProgram(0); this.isActive = false; } } }); var TSDGLShaderQualifier = Class.create({ initialize: function(gl, qualifierName) { this.gl = gl; this._uniformLocation = -1; this._needsUpdate = true; this._name = qualifierName; }, updateUniformLocationWithShaderProgramObject: function(shaderProgramObject) { if (this._uniformLocation === -1) { this._uniformLocation = this.gl.getUniformLocation(shaderProgramObject, this._name); } } }); var TSDGLShaderQualifierInt = Class.create(TSDGLShaderQualifier, { initialize: function($super, gl, qualifierName) { this._GLintValue = 0; this._proposedGLintValue = 0; $super(gl, qualifierName); }, setProposedGLintValue: function(proposedGLintValue) { if (this._proposedGLintValue !== proposedGLintValue) { this._proposedGLintValue = proposedGLintValue; this._needsUpdate = true; } }, setGLUniformWithShader: function(gl, shader) { gl.uniform1i(this._uniformLocation, this._proposedGLintValue); this._GLintValue = this._proposedGLintValue; this._needsUpdate = false; } }); var TSDGLShaderQualifierFloat = Class.create(TSDGLShaderQualifier, { initialize: function($super, gl, qualifierName) { this._GLfloatValue = 0; this._proposedGLfloatValue = 0; $super(gl, qualifierName); }, setProposedGLfloatValue: function(proposedGLfloatValue) { if (this._proposedGLfloatValue !== proposedGLfloatValue) { this._proposedGLfloatValue = proposedGLfloatValue; this._needsUpdate = true; } }, setGLUniformWithShader: function(gl, shader) { gl.uniform1f(this._uniformLocation, this._proposedGLfloatValue); this._GLfloatValue = this._proposedGLfloatValue; this._needsUpdate = false; } }); var TSDGLShaderQualifierPoint2D = Class.create(TSDGLShaderQualifier, { initialize: function($super, gl, qualifierName) { this._GLPoint2DValue = {}; this._proposedGLPoint2DValue = {}; $super(gl, qualifierName); }, setProposedGLPoint2DValue: function(proposedGLPoint2DValue) { if (!(this._proposedGLPoint2DValue.x === proposedGLPoint2DValue.x && this._proposedGLPoint2DValue.y === proposedGLPoint2DValue.y)) { this._proposedGLPoint2DValue = proposedGLPoint2DValue; this._needsUpdate = true; } }, setGLUniformWithShader: function(gl, shader) { gl.uniform2fv(this._uniformLocation, [this._proposedGLPoint2DValue.x, this._proposedGLPoint2DValue.y]); this._GLPoint2DValue = this._proposedGLPoint2DValue; this._needsUpdate = false; } }); var TSDGLShaderQualifierPoint3D = Class.create(TSDGLShaderQualifier, { initialize: function($super, gl, qualifierName) { this._GLPoint3DValue = {}; this._proposedGLPoint3DValue = {}; $super(gl, qualifierName); }, setProposedGLPoint3DValue: function(proposedGLPoint3DValue) { if (!(this._proposedGLPoint3DValue.x === proposedGLPoint3DValue.x && this._proposedGLPoint3DValue.y === proposedGLPoint3DValue.y && this._proposedGLPoint3DValue.z === proposedGLPoint3DValue.z)) { this._proposedGLPoint3DValue = proposedGLPoint3DValue; this._needsUpdate = true; } }, setGLUniformWithShader: function(gl, shader) { gl.uniform3fv(this._uniformLocation, [this._proposedGLPoint3DValue.x, this._proposedGLPoint3DValue.y, this._proposedGLPoint3DValue.z]); this._GLPoint3DValue = this._proposedGLPoint3DValue; this._needsUpdate = false; } }); var TSDGLShaderQualifierPoint4D = Class.create(TSDGLShaderQualifier, { initialize: function($super, gl, qualifierName) { this._GLPoint4DValue = {}; this._proposedGLPoint4DValue = {}; $super(gl, qualifierName); }, setProposedGLPoint4DValue: function(proposedGLPoint4DValue) { if (!(this._proposedGLPoint4DValue.x === proposedGLPoint4DValue.x && this._proposedGLPoint4DValue.y === proposedGLPoint4DValue.y && this._proposedGLPoint4DValue.z === proposedGLPoint4DValue.z && this._proposedGLPoint4DValue.w === proposedGLPoint4DValue.w)) { this._proposedGLPoint4DValue = proposedGLPoint4DValue; this._needsUpdate = true; } }, setGLUniformWithShader: function(gl, shader) { gl.uniform4fv(this._uniformLocation, [this._proposedGLPoint4DValue.x, this._proposedGLPoint4DValue.y, this._proposedGLPoint4DValue.z, this._proposedGLPoint4DValue.w]); this._GLPoint4DValue = this._proposedGLPoint4DValue; this._needsUpdate = false; } }); var TSDGLShaderQualifierMat3 = Class.create(TSDGLShaderQualifier, { initialize: function($super, gl, qualifierName) { this._affineTransform = new Float32Array(9) ; this._proposedAffineTransform = new Float32Array(9); $super(gl, qualifierName); }, setProposedAffineTransform: function(proposedAffineTransform) { if (!CGAffineTransformEqualToTransform(this._proposedAffineTransform, proposedAffineTransform)) { this._proposedAffineTransform = proposedAffineTransform; this._needsUpdate = true; } }, setGLUniformWithShader: function(gl, shader) { var mat = [ this._proposedAffineTransform.a, this._proposedAffineTransform.b, 0, this._proposedAffineTransform.c, this._proposedAffineTransform.d, 0, this._proposedAffineTransform.tx, this._proposedAffineTransform.ty, 1 ]; gl.uniformMatrix3fv(this._uniformLocation, false, mat); this._affineTransform = this._proposedAffineTransform; this._needsUpdate = false; } }); var TSDGLShaderQualifierMat4 = Class.create(TSDGLShaderQualifier, { initialize: function($super, gl, qualifierName) { this._transform3D = new Float32Array(16); this._proposedTransform3D = new Float32Array(16); $super(gl, qualifierName); }, setProposedTransform3D: function(proposedTransform3D) { if (!CATransform3DEqualToTransform(this._proposedTransform3D, proposedTransform3D)) { this._proposedTransform3D = proposedTransform3D; this._needsUpdate = true; } }, setGLUniformWithShader: function(gl, shader) { gl.uniformMatrix4fv(this._uniformLocation, false, this._proposedTransform3D); this._transform3D = this._proposedTransform3D; this._needsUpdate = false; } });