"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var stardust_core_1 = require("stardust-core"); var stardust_core_2 = require("stardust-core"); var generator_1 = require("./generator"); var stardust_core_3 = require("stardust-core"); var stardust_core_4 = require("stardust-core"); var WebGLUtils = require("./webglutils"); var WebGLPlatformMarkProgram = /** @class */ (function () { function WebGLPlatformMarkProgram(GL, spec, shader, asUniform, viewType, mode) { this._GL = GL; var generator = new generator_1.Generator(spec, shader, asUniform, viewType, mode); this._program = WebGLUtils.compileProgram(this._GL, generator.getVertexCode(), generator.getFragmentCode()); this._uniformLocations = new stardust_core_2.Dictionary(); this._attribLocations = new stardust_core_2.Dictionary(); this._textures = new stardust_core_2.Dictionary(); this._currentTextureUnit = 0; } WebGLPlatformMarkProgram.prototype.use = function () { this._GL.useProgram(this._program); }; WebGLPlatformMarkProgram.prototype.setUniform = function (name, type, value) { var location = this.getUniformLocation(name); if (location == null) { return; } var GL = this._GL; if (type.primitive == "float") { var va = value; switch (type.primitiveCount) { case 1: GL.uniform1f(location, value); break; case 2: GL.uniform2f(location, va[0], va[1]); break; case 3: GL.uniform3f(location, va[0], va[1], va[2]); break; case 4: GL.uniform4f(location, va[0], va[1], va[2], va[3]); break; } } if (type.primitive == "int") { var va = value; switch (type.primitiveCount) { case 1: GL.uniform1i(location, value); break; case 2: GL.uniform2i(location, va[0], va[1]); break; case 3: GL.uniform3i(location, va[0], va[1], va[2]); break; case 4: GL.uniform4i(location, va[0], va[1], va[2], va[3]); break; } } }; WebGLPlatformMarkProgram.prototype.setTexture = function (name, texture) { var GL = this._GL; if (!this._textures.has(name)) { var newTexture = GL.createTexture(); var unit = this._currentTextureUnit++; this._textures.set(name, { data: null, texture: newTexture, unit: unit }); GL.bindTexture(GL.TEXTURE_2D, newTexture); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); GL.bindTexture(GL.TEXTURE_2D, null); this.use(); this.setUniform(name, stardust_core_1.types.int, unit); } var cache = this._textures.get(name); var newData = texture.getTextureData(); if (cache.data == newData) { return; } else { cache.data = newData; // We need non-power-of-2 textures and floating point texture support. GL.bindTexture(GL.TEXTURE_2D, cache.texture); switch (newData.type) { case "f32": { GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, newData.width, newData.height, 0, GL.RGBA, GL.FLOAT, newData.data); break; } case "u8": { GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, newData.width, newData.height, 0, GL.RGBA, GL.FLOAT, newData.data); break; } case "HTMLImageElement": case "HTMLCanvasElement": { GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.FLOAT, newData.data); break; } } GL.bindTexture(GL.TEXTURE_2D, null); this.use(); if (newData.dimensions == 1) { this.setUniform(name + "_length", stardust_core_1.types.float, newData.width); } if (newData.dimensions == 2) { this.setUniform(name + "_width", stardust_core_1.types.float, newData.width); this.setUniform(name + "_height", stardust_core_1.types.float, newData.height); } } }; WebGLPlatformMarkProgram.prototype.bindTextures = function () { var GL = this._GL; this._textures.forEach(function (cache) { GL.activeTexture(GL.TEXTURE0 + cache.unit); GL.bindTexture(GL.TEXTURE_2D, cache.texture); }); }; WebGLPlatformMarkProgram.prototype.unbindTextures = function () { var GL = this._GL; this._textures.forEach(function (cache) { GL.activeTexture(GL.TEXTURE0 + cache.unit); GL.bindTexture(GL.TEXTURE_2D, null); }); }; WebGLPlatformMarkProgram.prototype.getUniformLocation = function (name) { if (this._uniformLocations.has(name)) { return this._uniformLocations.get(name); } else { var location_1 = this._GL.getUniformLocation(this._program, name); this._uniformLocations.set(name, location_1); return location_1; } }; WebGLPlatformMarkProgram.prototype.getAttribLocation = function (name) { if (this._attribLocations.has(name)) { return this._attribLocations.get(name); } else { var location_2 = this._GL.getAttribLocation(this._program, name); if (location_2 < 0) { location_2 = null; } this._attribLocations.set(name, location_2); return location_2; } }; return WebGLPlatformMarkProgram; }()); var WebGLPlatformMarkData = /** @class */ (function (_super) { __extends(WebGLPlatformMarkData, _super); function WebGLPlatformMarkData() { return _super !== null && _super.apply(this, arguments) || this; } return WebGLPlatformMarkData; }(stardust_core_1.PlatformMarkData)); exports.WebGLPlatformMarkData = WebGLPlatformMarkData; var WebGLPlatformMark = /** @class */ (function (_super) { __extends(WebGLPlatformMark, _super); function WebGLPlatformMark(platform, GL, mark, spec, shader, bindings, shiftBindings) { var _this = _super.call(this) || this; _this._platform = platform; _this._GL = GL; _this._mark = mark; _this._bindings = bindings; _this._shiftBindings = shiftBindings; _this._spec = spec; _this._shader = shader; var flattenedInfo = stardust_core_2.Compiler.Transforms.flattenEmits(spec); _this._specFlattened = flattenedInfo.specification; _this._flattenedVertexIndexVariable = flattenedInfo.indexVariable; _this._flattenedVertexCount = flattenedInfo.count; _this._program = new WebGLPlatformMarkProgram(GL, _this._specFlattened, _this._shader, function (name) { return _this.isUniform(name); }, _this._platform.viewInfo.type, generator_1.GenerateMode.NORMAL); _this._programPick = new WebGLPlatformMarkProgram(GL, _this._specFlattened, _this._shader, function (name) { return _this.isUniform(name); }, _this._platform.viewInfo.type, generator_1.GenerateMode.PICK); _this.initializeUniforms(); return _this; } WebGLPlatformMark.prototype.initializeUniforms = function () { for (var name_1 in this._specFlattened.input) { if (this.isUniform(name_1)) { var binding = this._bindings.get(name_1); if (binding.bindingType == stardust_core_1.BindingType.VALUE) { this.updateUniform(name_1, binding.specValue); } if (binding.bindingType == stardust_core_1.BindingType.TEXTURE) { this.updateTexture(name_1, binding.textureValue); } } } }; WebGLPlatformMark.prototype.initializeBuffers = function () { var _this = this; var GL = this._GL; var data = new WebGLPlatformMarkData(); data.buffers = new stardust_core_2.Dictionary(); this._bindings.forEach(function (binding, name) { if (!_this.isUniform(name)) { var location_3 = _this._program.getAttribLocation(name); if (location_3 != null) { data.buffers.set(name, GL.createBuffer()); } } }); data.buffers.set(this._flattenedVertexIndexVariable, GL.createBuffer()); if (this._programPick) { data.buffers.set("s3_pick_index", GL.createBuffer()); } data.ranges = []; return data; }; // Is the input attribute compiled as uniform? WebGLPlatformMark.prototype.isUniform = function (name) { // Extra variables we add are always not uniforms. if (name == this._flattenedVertexIndexVariable) { return false; } if (this._bindings.get(name) == null) { if (this._shiftBindings.get(name) == null) { throw new stardust_core_3.RuntimeError("attribute " + name + " is not specified."); } else { return (this._bindings.get(this._shiftBindings.get(name).name).bindingType != stardust_core_1.BindingType.FUNCTION); } } else { // Look at the binding to determine. return this._bindings.get(name).bindingType != stardust_core_1.BindingType.FUNCTION; } }; WebGLPlatformMark.prototype.updateUniform = function (name, value) { var binding = this._bindings.get(name); var type = binding.valueType; this._program.use(); this._program.setUniform(name, type, value); if (this._programPick) { this._programPick.use(); this._programPick.setUniform(name, type, value); } }; WebGLPlatformMark.prototype.updateTexture = function (name, value) { this._program.setTexture(name, value); if (this._programPick) { this._programPick.setTexture(name, value); } }; WebGLPlatformMark.prototype.uploadData = function (datas) { var buffers = this.initializeBuffers(); buffers.ranges = []; var repeatBegin = this._spec.repeatBegin || 0; var repeatEnd = this._spec.repeatEnd || 0; var GL = this._GL; var bindings = this._bindings; var rep = this._flattenedVertexCount; var totalCount = 0; datas.forEach(function (data) { var n = data.length; if (n == 0) { buffers.ranges.push(null); return; } else { var c1 = totalCount; totalCount += n + repeatBegin + repeatEnd; var c2 = totalCount; buffers.ranges.push([c1 * rep, c2 * rep]); } }); this._bindings.forEach(function (binding, name) { var buffer = buffers.buffers.get(name); if (buffer == null) { return; } var type = binding.valueType; var array = new Float32Array(type.primitiveCount * totalCount * rep); var currentIndex = 0; var multiplier = type.primitiveCount * rep; datas.forEach(function (data) { if (data.length == 0) { return; } for (var i = 0; i < repeatBegin; i++) { binding.fillBinary([data[0]], rep, array.subarray(currentIndex, currentIndex + multiplier)); currentIndex += multiplier; } binding.fillBinary(data, rep, array.subarray(currentIndex, currentIndex + data.length * multiplier)); currentIndex += data.length * multiplier; for (var i = 0; i < repeatEnd; i++) { binding.fillBinary([data[data.length - 1]], rep, array.subarray(currentIndex, currentIndex + multiplier)); currentIndex += multiplier; } }); GL.bindBuffer(GL.ARRAY_BUFFER, buffer); GL.bufferData(GL.ARRAY_BUFFER, array, GL.STATIC_DRAW); }); // The vertex index attribute. var array = new Float32Array(totalCount * rep); for (var i = 0; i < totalCount * rep; i++) { array[i] = i % rep; } GL.bindBuffer(GL.ARRAY_BUFFER, buffers.buffers.get(this._flattenedVertexIndexVariable)); GL.bufferData(GL.ARRAY_BUFFER, array, GL.STATIC_DRAW); // The pick index attribute. if (this._programPick) { var array_1 = new Float32Array(totalCount * rep * 4); for (var i = 0; i < totalCount * rep; i++) { var index = Math.floor(i / rep); array_1[i * 4 + 0] = (index & 0xff) / 255.0; array_1[i * 4 + 1] = ((index & 0xff00) >> 8) / 255.0; array_1[i * 4 + 2] = ((index & 0xff0000) >> 16) / 255.0; array_1[i * 4 + 3] = ((index & 0xff000000) >> 24) / 255.0; } GL.bindBuffer(GL.ARRAY_BUFFER, buffers.buffers.get("s3_pick_index")); GL.bufferData(GL.ARRAY_BUFFER, array_1, GL.STATIC_DRAW); } return buffers; }; // Render the graphics. WebGLPlatformMark.prototype.renderBase = function (buffers, mode, onRender) { var _this = this; if (buffers.ranges.length > 0) { var GL_1 = this._GL; var spec = this._specFlattened; var bindings = this._bindings; // Decide which program to use var program_1 = this._program; if (mode == generator_1.GenerateMode.PICK) { program_1 = this._programPick; } program_1.use(); var minOffset_1 = 0; var maxOffset_1 = 0; this._shiftBindings.forEach(function (shift, name) { if (shift.offset > maxOffset_1) { maxOffset_1 = shift.offset; } if (shift.offset < minOffset_1) { minOffset_1 = shift.offset; } }); // Assign attributes to buffers for (var name_2 in spec.input) { var attributeLocation = program_1.getAttribLocation(name_2); if (attributeLocation == null) { continue; } if (this._shiftBindings.has(name_2)) { var shift = this._shiftBindings.get(name_2); GL_1.bindBuffer(GL_1.ARRAY_BUFFER, buffers.buffers.get(shift.name)); GL_1.enableVertexAttribArray(attributeLocation); var type = bindings.get(shift.name).valueType; GL_1.vertexAttribPointer(attributeLocation, type.primitiveCount, type.primitive == "float" ? GL_1.FLOAT : GL_1.INT, false, 0, type.size * (shift.offset - minOffset_1) * this._flattenedVertexCount); } else { GL_1.bindBuffer(GL_1.ARRAY_BUFFER, buffers.buffers.get(name_2)); GL_1.enableVertexAttribArray(attributeLocation); if (name_2 == this._flattenedVertexIndexVariable) { GL_1.vertexAttribPointer(attributeLocation, 1, GL_1.FLOAT, false, 0, 4 * -minOffset_1 * this._flattenedVertexCount); } else { var type = bindings.get(name_2).valueType; GL_1.vertexAttribPointer(attributeLocation, type.primitiveCount, type.primitive == "float" ? GL_1.FLOAT : GL_1.INT, false, 0, type.size * -minOffset_1 * this._flattenedVertexCount); } } } // For pick mode, assign the pick index buffer if (mode == generator_1.GenerateMode.PICK) { var attributeLocation = program_1.getAttribLocation("s3_pick_index"); GL_1.bindBuffer(GL_1.ARRAY_BUFFER, buffers.buffers.get("s3_pick_index")); GL_1.enableVertexAttribArray(attributeLocation); GL_1.vertexAttribPointer(attributeLocation, 4, GL_1.FLOAT, false, 0, 0); } // Set view uniforms var viewInfo = this._platform.viewInfo; var pose = this._platform.pose; var cameraPosition = this._platform.cameraPosition; switch (viewInfo.type) { case generator_1.ViewType.VIEW_2D: { GL_1.uniform4f(program_1.getUniformLocation("s3_view_params"), 2.0 / viewInfo.width, -2.0 / viewInfo.height, -1, +1); } break; case generator_1.ViewType.VIEW_3D: { GL_1.uniform4f(program_1.getUniformLocation("s3_view_params"), 1.0 / Math.tan(viewInfo.fovY / 2.0) / viewInfo.aspectRatio, 1.0 / Math.tan(viewInfo.fovY / 2.0), (viewInfo.near + viewInfo.far) / (viewInfo.near - viewInfo.far), (2.0 * viewInfo.near * viewInfo.far) / (viewInfo.near - viewInfo.far)); if (pose) { // Rotation and position. GL_1.uniform4f(program_1.getUniformLocation("s3_view_rotation"), pose.rotation.x, pose.rotation.y, pose.rotation.z, pose.rotation.w); GL_1.uniform3f(program_1.getUniformLocation("s3_view_position"), pose.position.x, pose.position.y, pose.position.z); } else { GL_1.uniform4f(program_1.getUniformLocation("s3_view_rotation"), 0, 0, 0, 1); GL_1.uniform3f(program_1.getUniformLocation("s3_view_position"), 0, 0, 0); } GL_1.uniform3f(program_1.getUniformLocation("s3_camera_position"), cameraPosition.x, cameraPosition.y, cameraPosition.z); } break; case generator_1.ViewType.VIEW_MATRIX: { GL_1.uniformMatrix4fv(program_1.getUniformLocation("s3_view_matrix"), false, viewInfo.viewMatrix); GL_1.uniformMatrix4fv(program_1.getUniformLocation("s3_projection_matrix"), false, viewInfo.projectionMatrix); if (pose) { // Rotation and position. GL_1.uniform4f(program_1.getUniformLocation("s3_view_rotation"), pose.rotation.x, pose.rotation.y, pose.rotation.z, pose.rotation.w); GL_1.uniform3f(program_1.getUniformLocation("s3_view_position"), pose.position.x, pose.position.y, pose.position.z); } else { GL_1.uniform4f(program_1.getUniformLocation("s3_view_rotation"), 0, 0, 0, 1); GL_1.uniform3f(program_1.getUniformLocation("s3_view_position"), 0, 0, 0); } GL_1.uniform3f(program_1.getUniformLocation("s3_camera_position"), cameraPosition.x, cameraPosition.y, cameraPosition.z); } break; } // For pick, set the mark index if (mode == generator_1.GenerateMode.PICK) { GL_1.uniform1f(program_1.getUniformLocation("s3_pick_index_alpha"), this._pickIndex / 255.0); } program_1.bindTextures(); // Draw arrays buffers.ranges.forEach(function (range, index) { if (onRender) { onRender(index); } if (range != null) { program_1.use(); program_1.bindTextures(); GL_1.drawArrays(GL_1.TRIANGLES, range[0], range[1] - range[0] - (maxOffset_1 - minOffset_1) * _this._flattenedVertexCount); } }); program_1.unbindTextures(); // Unbind attributes for (var name_3 in spec.input) { var attributeLocation = program_1.getAttribLocation(name_3); if (attributeLocation != null) { GL_1.disableVertexAttribArray(attributeLocation); } } // Unbind the pick index buffer if (mode == generator_1.GenerateMode.PICK) { var attributeLocation = program_1.getAttribLocation("s3_pick_index"); GL_1.disableVertexAttribArray(attributeLocation); } } }; WebGLPlatformMark.prototype.setPickIndex = function (index) { this._pickIndex = index; }; WebGLPlatformMark.prototype.render = function (buffers, onRender) { if (this._platform.renderMode == generator_1.GenerateMode.PICK) { this.setPickIndex(this._platform.assignPickIndex(this._mark)); } this.renderBase(buffers, this._platform.renderMode, onRender); }; return WebGLPlatformMark; }(stardust_core_1.PlatformMark)); exports.WebGLPlatformMark = WebGLPlatformMark; var WebGLPlatform = /** @class */ (function (_super) { __extends(WebGLPlatform, _super); function WebGLPlatform(GL) { var _this = _super.call(this) || this; _this._GL = GL; _this.set2DView(500, 500); _this.setCameraPosition(new stardust_core_1.Vector3()); _this.setPose(new stardust_core_4.Pose()); _this._renderMode = generator_1.GenerateMode.NORMAL; _this._pickFramebuffer = null; return _this; } Object.defineProperty(WebGLPlatform.prototype, "viewInfo", { get: function () { return this._viewInfo; }, enumerable: true, configurable: true }); Object.defineProperty(WebGLPlatform.prototype, "pose", { get: function () { return this._pose; }, enumerable: true, configurable: true }); Object.defineProperty(WebGLPlatform.prototype, "cameraPosition", { get: function () { return this._cameraPosition; }, enumerable: true, configurable: true }); Object.defineProperty(WebGLPlatform.prototype, "renderMode", { get: function () { return this._renderMode; }, enumerable: true, configurable: true }); WebGLPlatform.prototype.getPickFramebuffer = function (width, height) { if (this._pickFramebuffer == null || width != this._pickFramebufferWidth || height != this._pickFramebufferHeight) { var GL = this._GL; this._pickFramebuffer = GL.createFramebuffer(); this._pickFramebufferWidth = width; this._pickFramebufferHeight = height; GL.bindFramebuffer(GL.FRAMEBUFFER, this._pickFramebuffer); this._pickFramebufferTexture = GL.createTexture(); GL.bindTexture(GL.TEXTURE_2D, this._pickFramebufferTexture); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR); GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, width, height, 0, GL.RGBA, GL.UNSIGNED_BYTE, null); GL.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, this._pickFramebufferTexture, 0); GL.bindTexture(GL.TEXTURE_2D, null); GL.bindFramebuffer(GL.FRAMEBUFFER, null); } return this._pickFramebuffer; }; WebGLPlatform.prototype.beginPicking = function (width, height) { this._renderMode = generator_1.GenerateMode.PICK; var GL = this._GL; var fb = this.getPickFramebuffer(width, height); GL.bindFramebuffer(GL.FRAMEBUFFER, fb); GL.clearColor(1, 1, 1, 1); GL.clear(GL.COLOR_BUFFER_BIT); GL.disable(GL.BLEND); this._pickMarks = []; }; WebGLPlatform.prototype.assignPickIndex = function (mark) { var idx = this._pickMarks.indexOf(mark); if (idx >= 0) { return idx; } else { var num = this._pickMarks.length; this._pickMarks.push(mark); return num; } }; WebGLPlatform.prototype.endPicking = function () { var GL = this._GL; GL.bindFramebuffer(GL.FRAMEBUFFER, null); GL.enable(GL.BLEND); this._renderMode = generator_1.GenerateMode.NORMAL; }; WebGLPlatform.prototype.getPickingPixel = function (x, y) { if (this._pickMarks == null || x < 0 || y < 0 || x >= this._pickFramebufferWidth || y >= this._pickFramebufferHeight) { return null; } var GL = this._GL; var fb = this._pickFramebuffer; GL.bindFramebuffer(GL.FRAMEBUFFER, fb); var data = new Uint8Array(4); GL.readPixels(x, this._pickFramebufferHeight - 1 - y, 1, 1, GL.RGBA, GL.UNSIGNED_BYTE, data); GL.bindFramebuffer(GL.FRAMEBUFFER, null); var offset = data[0] + (data[1] << 8) + (data[2] << 16); if (offset >= 16777215) { return null; } return [this._pickMarks[data[3]], offset]; }; WebGLPlatform.prototype.set2DView = function (width, height) { this._viewInfo = { type: generator_1.ViewType.VIEW_2D, width: width, height: height }; }; WebGLPlatform.prototype.set3DView = function (fovY, aspectRatio, near, far) { if (near === void 0) { near = 0.1; } if (far === void 0) { far = 1000; } this._viewInfo = { type: generator_1.ViewType.VIEW_3D, fovY: fovY, aspectRatio: aspectRatio, near: near, far: far }; }; WebGLPlatform.prototype.setWebVRView = function (viewMatrix, projectionMatrix) { this._viewInfo = { type: generator_1.ViewType.VIEW_MATRIX, viewMatrix: viewMatrix, projectionMatrix: projectionMatrix }; }; WebGLPlatform.prototype.setMatrixView = function (viewMatrix, projectionMatrix) { this._viewInfo = { type: generator_1.ViewType.VIEW_MATRIX, viewMatrix: viewMatrix, projectionMatrix: projectionMatrix }; }; WebGLPlatform.prototype.setCameraPosition = function (cameraPosition) { this._cameraPosition = cameraPosition; }; WebGLPlatform.prototype.setPose = function (pose) { this._pose = pose; }; WebGLPlatform.prototype.compile = function (mark, spec, shader, bindings, shiftBindings) { return new WebGLPlatformMark(this, this._GL, mark, spec, shader, bindings, shiftBindings); }; return WebGLPlatform; }(stardust_core_1.Platform)); exports.WebGLPlatform = WebGLPlatform; //# sourceMappingURL=webgl.js.map