mirror of
https://gitlab.com/Shinobi-Systems/ShinobiCE.git
synced 2025-03-09 15:40:15 +00:00
261 lines
7.3 KiB
JavaScript
261 lines
7.3 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* Generic WebGL backed canvas that sets up: a quad to paint a texture on, appropriate vertex/fragment shaders,
|
|
* scene parameters and other things. Specialized versions of this class can be created by overriding several
|
|
* initialization methods.
|
|
|
|
*/
|
|
|
|
var Script = require('./Script');
|
|
var error = require('../utils/error');
|
|
var makePerspective = require('../utils/glUtils').makePerspective;
|
|
var Matrix = require('sylvester.js').Matrix;
|
|
var Class = require('uclass');
|
|
|
|
|
|
var vertexShaderScript = Script.createFromSource("x-shader/x-vertex", `
|
|
attribute vec3 aVertexPosition;
|
|
attribute vec2 aTextureCoord;
|
|
uniform mat4 uMVMatrix;
|
|
uniform mat4 uPMatrix;
|
|
varying highp vec2 vTextureCoord;
|
|
void main(void) {
|
|
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
|
|
vTextureCoord = aTextureCoord;
|
|
}
|
|
`);
|
|
|
|
var fragmentShaderScript = Script.createFromSource("x-shader/x-fragment", `
|
|
precision highp float;
|
|
varying highp vec2 vTextureCoord;
|
|
uniform sampler2D texture;
|
|
void main(void) {
|
|
gl_FragColor = texture2D(texture, vTextureCoord);
|
|
}
|
|
`);
|
|
|
|
var WebGLCanvas = new Class({
|
|
|
|
initialize : function(canvas, size, useFrameBuffer) {
|
|
|
|
this.canvas = canvas;
|
|
this.size = size;
|
|
this.canvas.width = size.w;
|
|
this.canvas.height = size.h;
|
|
|
|
this.onInitWebGL();
|
|
this.onInitShaders();
|
|
this.initBuffers();
|
|
|
|
if (useFrameBuffer)
|
|
this.initFramebuffer();
|
|
|
|
this.onInitTextures();
|
|
this.initScene();
|
|
},
|
|
|
|
|
|
/**
|
|
* Initialize a frame buffer so that we can render off-screen.
|
|
*/
|
|
initFramebuffer : function() {
|
|
|
|
var gl = this.gl;
|
|
|
|
// Create framebuffer object and texture.
|
|
this.framebuffer = gl.createFramebuffer();
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
this.framebufferTexture = new Texture(this.gl, this.size, gl.RGBA);
|
|
|
|
// Create and allocate renderbuffer for depth data.
|
|
var renderbuffer = gl.createRenderbuffer();
|
|
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
|
|
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.size.w, this.size.h);
|
|
|
|
// Attach texture and renderbuffer to the framebuffer.
|
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.framebufferTexture.texture, 0);
|
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
* Initialize vertex and texture coordinate buffers for a plane.
|
|
*/
|
|
initBuffers : function () {
|
|
var tmp;
|
|
var gl = this.gl;
|
|
|
|
// Create vertex position buffer.
|
|
this.quadVPBuffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVPBuffer);
|
|
tmp = [
|
|
1.0, 1.0, 0.0,
|
|
-1.0, 1.0, 0.0,
|
|
1.0, -1.0, 0.0,
|
|
-1.0, -1.0, 0.0];
|
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
|
|
this.quadVPBuffer.itemSize = 3;
|
|
this.quadVPBuffer.numItems = 4;
|
|
|
|
/*
|
|
+--------------------+
|
|
| -1,1 (1) | 1,1 (0)
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| -1,-1 (3) | 1,-1 (2)
|
|
+--------------------+
|
|
*/
|
|
|
|
var scaleX = 1.0;
|
|
var scaleY = 1.0;
|
|
|
|
// Create vertex texture coordinate buffer.
|
|
this.quadVTCBuffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVTCBuffer);
|
|
tmp = [
|
|
scaleX, 0.0,
|
|
0.0, 0.0,
|
|
scaleX, scaleY,
|
|
0.0, scaleY,
|
|
];
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
|
|
},
|
|
|
|
|
|
mvIdentity : function () {
|
|
this.mvMatrix = Matrix.I(4);
|
|
},
|
|
|
|
mvMultiply : function(m) {
|
|
this.mvMatrix = this.mvMatrix.x(m);
|
|
},
|
|
|
|
mvTranslate : function (m) {
|
|
this.mvMultiply(Matrix.Translation($V([m[0], m[1], m[2]])).ensure4x4());
|
|
},
|
|
|
|
setMatrixUniforms : function () {
|
|
this.program.setMatrixUniform("uPMatrix", new Float32Array(this.perspectiveMatrix.flatten()));
|
|
this.program.setMatrixUniform("uMVMatrix", new Float32Array(this.mvMatrix.flatten()));
|
|
},
|
|
|
|
initScene : function() {
|
|
var gl = this.gl;
|
|
|
|
// Establish the perspective with which we want to view the
|
|
// scene. Our field of view is 45 degrees, with a width/height
|
|
// ratio of 640:480, and we only want to see objects between 0.1 units
|
|
// and 100 units away from the camera.
|
|
|
|
this.perspectiveMatrix = makePerspective(45, 1, 0.1, 100.0);
|
|
|
|
// Set the drawing position to the "identity" point, which is
|
|
// the center of the scene.
|
|
this.mvIdentity();
|
|
|
|
// Now move the drawing position a bit to where we want to start
|
|
// drawing the square.
|
|
this.mvTranslate([0.0, 0.0, -2.4]);
|
|
|
|
// Draw the cube by binding the array buffer to the cube's vertices
|
|
// array, setting attributes, and pushing it to GL.
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVPBuffer);
|
|
gl.vertexAttribPointer(this.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
|
|
|
|
// Set the texture coordinates attribute for the vertices.
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVTCBuffer);
|
|
gl.vertexAttribPointer(this.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
|
|
|
|
this.onInitSceneTextures();
|
|
|
|
this.setMatrixUniforms();
|
|
|
|
if (this.framebuffer) {
|
|
console.log("Bound Frame Buffer");
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
}
|
|
},
|
|
|
|
|
|
|
|
toString: function() {
|
|
return "WebGLCanvas Size: " + this.size;
|
|
},
|
|
|
|
checkLastError: function (operation) {
|
|
var err = this.gl.getError();
|
|
if (err != this.gl.NO_ERROR) {
|
|
var name = this.glNames[err];
|
|
name = (name !== undefined) ? name + "(" + err + ")":
|
|
("Unknown WebGL ENUM (0x" + value.toString(16) + ")");
|
|
if (operation) {
|
|
console.log("WebGL Error: %s, %s", operation, name);
|
|
} else {
|
|
console.log("WebGL Error: %s", name);
|
|
}
|
|
console.trace();
|
|
}
|
|
},
|
|
|
|
onInitWebGL: function () {
|
|
try {
|
|
this.gl = this.canvas.getContext("experimental-webgl");
|
|
} catch(e) {}
|
|
|
|
if (!this.gl) {
|
|
error("Unable to initialize WebGL. Your browser may not support it.");
|
|
}
|
|
if (this.glNames) {
|
|
return;
|
|
}
|
|
this.glNames = {};
|
|
for (var propertyName in this.gl) {
|
|
if (typeof this.gl[propertyName] == 'number') {
|
|
this.glNames[this.gl[propertyName]] = propertyName;
|
|
}
|
|
}
|
|
},
|
|
|
|
onInitShaders: function() {
|
|
this.program = new Program(this.gl);
|
|
this.program.attach(new Shader(this.gl, vertexShaderScript));
|
|
this.program.attach(new Shader(this.gl, fragmentShaderScript));
|
|
this.program.link();
|
|
this.program.use();
|
|
this.vertexPositionAttribute = this.program.getAttributeLocation("aVertexPosition");
|
|
this.gl.enableVertexAttribArray(this.vertexPositionAttribute);
|
|
this.textureCoordAttribute = this.program.getAttributeLocation("aTextureCoord");;
|
|
this.gl.enableVertexAttribArray(this.textureCoordAttribute);
|
|
},
|
|
|
|
onInitTextures: function () {
|
|
var gl = this.gl;
|
|
this.texture = new Texture(gl, this.size, gl.RGBA);
|
|
},
|
|
|
|
onInitSceneTextures: function () {
|
|
this.texture.bind(0, this.program, "texture");
|
|
},
|
|
|
|
drawScene: function() {
|
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
},
|
|
|
|
readPixels: function(buffer) {
|
|
var gl = this.gl;
|
|
gl.readPixels(0, 0, this.size.w, this.size.h, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = WebGLCanvas;
|