Permalink
Browse files

Impl simple-gl framework

0 parents commit dd2dbd646fc10cc3e118df2c470c2d0c5a860c1d @LastLeaf committed Nov 25, 2017
Showing with 375 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +28 −0 index.html
  3. +28 −0 package.json
  4. +14 −0 src/game.js
  5. +256 −0 src/simple-gl/index.js
  6. +5 −0 src/simple-gl/rect.f.glsl
  7. +6 −0 src/simple-gl/rect.v.glsl
  8. +35 −0 webpack.config.js
@@ -0,0 +1,3 @@
+/node_modules
+/dist
+/npm-debug.log
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
+ <title>TRICOLORS: a puzzle game</title>
+</head>
+<body style="background: black">
+ <canvas id="game" width="1920" height="1080"></canvas>
+ <script src="./dist/game.js"></script>
+ <script>
+ var gameCanvas = document.getElementById('game')
+ gameCanvas.style.background = 'black'
+ gameCanvas.style.position = 'fixed'
+ gameCanvas.style.margin = 'auto'
+ gameCanvas.style.top = '0'
+ gameCanvas.style.bottom = '0'
+ gameCanvas.style.left = '0'
+ gameCanvas.style.right = '0'
+ var resizeCb = function(){
+ var scale = Math.min(document.documentElement.clientWidth / 1920, document.documentElement.clientHeight / 1080)
+ gameCanvas.style.transform = 'scale(' + scale + ',' + scale + ')'
+ }
+ resizeCb()
+ window.onresize = resizeCb
+ game.init(gameCanvas)
+ </script>
+</body>
+</html>
@@ -0,0 +1,28 @@
+{
+ "name": "tricolors",
+ "version": "0.1.0",
+ "description": "TRICOLORS: a puzzle game",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "webpack"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/LastLeaf/tricolors.git"
+ },
+ "author": "Fu Boquan <bqfu@163.com>",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/LastLeaf/tricolors/issues"
+ },
+ "homepage": "https://github.com/LastLeaf/tricolors#readme",
+ "devDependencies": {
+ "ajv": "^5.5.0",
+ "babel-core": "^6.26.0",
+ "babel-loader": "^7.1.2",
+ "babel-preset-env": "^1.6.1",
+ "raw-loader": "^0.5.1",
+ "webpack": "^3.8.1"
+ }
+}
@@ -0,0 +1,14 @@
+import { SimpleGL } from './simple-gl'
+
+const COLOR_R = [1, 0, 0, 1]
+const COLOR_G = [0, 1, 0, 1]
+const COLOR_B = [0, 0, 1, 1]
+
+export const init = (canvas) => {
+ const stage = new SimpleGL(canvas)
+ const root = stage.getRootContainer()
+ const rect1 = stage.createRect(100, 100, 500, 500).color(...COLOR_R)
+ const rect2 = stage.createRect(275, 500, 500, 500).color(...COLOR_G).blendMode('ONE', 'ONE')
+ const rect3 = stage.createRect(500, 100, 500, 500).color(...COLOR_B).blendMode('ONE', 'ONE')
+ root.append(rect1).append(rect2).append(rect3)
+}
@@ -0,0 +1,256 @@
+const rectVs = require('./rect.v.glsl')
+const rectFs = require('./rect.f.glsl')
+
+const createVertexShader = (gl, src) => {
+ let shaderLog = ''
+ const shader = gl.createShader(gl.VERTEX_SHADER)
+ gl.shaderSource(shader, src)
+ gl.compileShader(shader)
+ if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ shaderLog = gl.getShaderInfoLog(shader)
+ gl.deleteShader(shader)
+ throw new Error('Failed initializing WebGL vertex shader: ' + shaderLog)
+ }
+ return shader
+}
+
+const createFragmentShader = (gl, src) => {
+ let shaderLog = ''
+ const shader = gl.createShader(gl.FRAGMENT_SHADER)
+ gl.shaderSource(shader, src)
+ gl.compileShader(shader)
+ if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ shaderLog = gl.getShaderInfoLog(shader)
+ gl.deleteShader(shader)
+ throw new Error('Failed initializing WebGL fragment shader: ' + shaderLog)
+ }
+ return shader
+}
+
+const createShaderProgram = (gl, vs, fs) => {
+ var shaderProgram = gl.createProgram()
+ gl.attachShader(shaderProgram, vs)
+ gl.attachShader(shaderProgram, fs)
+ gl.linkProgram(shaderProgram)
+ if(!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+ throw new Error('Failed initializing WebGL shader program.')
+ }
+ return shaderProgram
+}
+
+class SimpleAttribBuffer {
+ constructor(gl, shaderProgram, attribName, size, count) {
+ this._gl = gl
+ this._size = size
+ this._count = count
+ const glBuf = this._glBuf = gl.createBuffer()
+ const buf = this._buf = new window.Float32Array(size * count)
+ gl.bindBuffer(gl.ARRAY_BUFFER, glBuf)
+ gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW)
+ const attribLocation = gl.getAttribLocation(shaderProgram, attribName)
+ gl.enableVertexAttribArray(attribLocation)
+ gl.vertexAttribPointer(attribLocation, size, gl.FLOAT, false, 0, 0)
+ }
+ getFloat32Array() {
+ return this._buf
+ }
+ write() {
+ const gl = this._gl
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._glBuf)
+ gl.bufferData(gl.ARRAY_BUFFER, this._buf, gl.DYNAMIC_DRAW)
+ }
+}
+
+class SimpleUniform {
+ constructor(gl, shaderProgram, uniformName, size) {
+ this._gl = gl
+ this._size = size
+ this._loc = gl.getUniformLocation(shaderProgram, uniformName)
+ }
+ set(x, y, z, w) {
+ const gl = this._gl
+ const size = this._size
+ if (size === 1) {
+ gl.uniform1f(this._loc, x)
+ } else if (size === 2) {
+ gl.uniform2f(this._loc, x, y)
+ } else if (size === 3) {
+ gl.uniform3f(this._loc, x, y, z)
+ } else {
+ gl.uniform4f(this._loc, x, y, z, w)
+ }
+ }
+}
+
+class SimpleGLObject {
+ constructor(simpleGL) {
+ this._gl = simpleGL._gl
+ }
+ blendMode(s, d) {
+ const gl = this._gl
+ this._blendMode = [gl[s], gl[d]]
+ return this
+ }
+ _prepareObject() {
+ const gl = this._gl
+ const blendMode = this._blendMode
+ if (blendMode) {
+ gl.blendFunc(blendMode[0], blendMode[1])
+ } else {
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
+ }
+ }
+}
+
+class SimpleRect extends SimpleGLObject {
+ static _init(simpleGL) {
+ const gl = simpleGL._gl
+ const vs = createVertexShader(gl, rectVs)
+ const fs = createFragmentShader(gl, rectFs)
+ const shaderProgram = createShaderProgram(gl, vs, fs)
+ gl.useProgram(shaderProgram)
+ const uCanvasSize = gl.getUniformLocation(shaderProgram, 'uCanvasSize')
+ gl.uniform2f(uCanvasSize, simpleGL._width, simpleGL._height)
+ simpleGL._rectGL = {
+ shaderProgram,
+ aPoint: new SimpleAttribBuffer(gl, shaderProgram, 'aPoint', 2, 4),
+ uColor: new SimpleUniform(gl, shaderProgram, 'uColor', 4),
+ }
+ }
+ constructor(simpleGL, x = 0, y = 0, w = 0, h = 0) {
+ super(simpleGL)
+ this._simpleGL = simpleGL
+ this._gl = simpleGL._gl
+ this.x = x
+ this.y = y
+ this.w = w
+ this.h = h
+ this.r = 0
+ this.g = 0
+ this.b = 0
+ this.a = 0
+ this.childNodes = []
+ }
+ color(r, g, b, a) {
+ this.r = r
+ this.g = g
+ this.b = b
+ this.a = a
+ return this
+ }
+ pos(x, y) {
+ this.x = x
+ this.y = y
+ return this
+ }
+ size(w, h) {
+ this.w = w
+ this.h = h
+ return this
+ }
+ _draw(relX, relY) {
+ const gl = this._gl
+ const info = this._simpleGL._rectGL
+ gl.useProgram(info.shaderProgram)
+ this._prepareObject()
+ const aPoint = info.aPoint.getFloat32Array()
+ const {x, y, w, h, r, g, b, a} = this
+ aPoint[0] = relX + x
+ aPoint[1] = relY + y
+ aPoint[2] = relX + x
+ aPoint[3] = relY + y + h
+ aPoint[4] = relX + x + w
+ aPoint[5] = relY + y + h
+ aPoint[6] = relX + x + w
+ aPoint[7] = relY + y
+ info.aPoint.write()
+ info.uColor.set(r, g, b, a)
+ gl.drawArrays(gl.TRIANGLE_FAN, 0, 4)
+ }
+}
+
+class SimpleContainer extends SimpleGLObject {
+ constructor(simpleGL, x, y) {
+ super(simpleGL)
+ this._gl = simpleGL._gl
+ this.x = x
+ this.y = y
+ this._children = []
+ }
+ pos(x, y) {
+ this.x = x
+ this.y = y
+ return this
+ }
+ append(child) {
+ this._children.push(child)
+ return this
+ }
+ prepend(child) {
+ this._children.unshift(child)
+ return this
+ }
+ insert(child, before) {
+ const index = this._children.indexOf(before)
+ if (index >= 0) this._children.splice(index, 0, child)
+ return this
+ }
+ remove(child) {
+ const index = this._children.indexOf(child)
+ if (index >= 0) this._children.splice(index, 1)
+ return this
+ }
+ _draw(relX, relY) {
+ this._children.forEach((node) => {
+ node._draw(relX + this.x, relY + this.y)
+ })
+ }
+}
+
+export class SimpleGL {
+ constructor(canvas) {
+ const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
+ gl.enable(gl.BLEND)
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
+ gl.viewport(0, 0, canvas.width, canvas.height)
+ gl.clearColor(0.0, 0.0, 0.0, 0.0)
+ gl.clearDepth(1)
+ this._canvas = canvas
+ this._gl = gl
+ this._width = canvas.width
+ this._height = canvas.height
+ this._rootContainer = new SimpleContainer(this, 0, 0)
+ SimpleRect._init(this)
+ this.setAutoUpdate(true)
+ }
+ setAutoUpdate(status) {
+ this._autoUpdate = status
+ if (status) {
+ const checkUpdate = () => {
+ this._scheduledChecking = false
+ if (!this._autoUpdate) return
+ requestAnimationFrame(checkUpdate)
+ this._scheduledChecking = true
+ this.update()
+ }
+ if (!this._scheduledChecking) {
+ requestAnimationFrame(checkUpdate)
+ this._scheduledChecking = true
+ }
+ }
+ }
+ update() {
+ const gl = this._gl
+ gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT)
+ this._rootContainer._draw(0, 0)
+ }
+ getRootContainer() {
+ return this._rootContainer
+ }
+ createContainer(...args) {
+ return new SimpleContainer(this, ...args)
+ }
+ createRect(...args) {
+ return new SimpleRect(this, ...args)
+ }
+}
@@ -0,0 +1,5 @@
+uniform highp vec4 uColor;
+
+void main(void) {
+ gl_FragColor = uColor;
+}
@@ -0,0 +1,6 @@
+attribute vec2 aPoint;
+uniform vec2 uCanvasSize;
+
+void main(void) {
+ gl_Position = vec4(aPoint * mat2( 2.0/uCanvasSize.x,0, 0,-2.0/uCanvasSize.y ) + vec2(-1, 1), 0, 1);
+}
@@ -0,0 +1,35 @@
+var webpack = require('webpack')
+var path = require('path')
+
+module.exports = {
+ entry: './src/game.js',
+ output: {
+ filename: 'game.js',
+ library: 'game',
+ path: path.resolve(__dirname, 'dist')
+ },
+ devtool: 'source-map',
+ target: 'web',
+ module: {
+ rules: [{
+ test: /\.glsl$/,
+ use: {
+ loader: 'raw-loader'
+ }
+ }, {
+ test: /\.js$/,
+ exclude: /(node_modules|bower_components)/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: ['babel-preset-env']
+ }
+ }
+ }]
+ },
+ // plugins: [
+ // new webpack.optimize.UglifyJsPlugin({
+ // mangle: true
+ // })
+ // ]
+}

0 comments on commit dd2dbd6

Please sign in to comment.