WebGL rotating F: Difference between revisions
Content added Content deleted
m (pasting type correction) |
(Added Wren) |
||
Line 424: | Line 424: | ||
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
||
<!--</lang>--> |
<!--</lang>--> |
||
=={{header|Wren}}== |
|||
{{libheader|FreeGLUT}} |
|||
{{libheader|Wren-math}} |
|||
The following is based on the WebGL tutorial javascript animation code [https://webglfundamentals.org/webgl/lessons/webgl-animation.html here] but is written as an OpenGL ES 2.0 desktop application. It just uses plain colors, without any textures and looks exactly the same as the WebGL version when run. |
|||
Although the task description says to avoid it, I've used Freeglut to create and manage a desktop window (but no drawing nor callbacks) as that's what I'm most familiar with. However, if that's a problem I can try and use something else (such as SDL). |
|||
Assuming it's started from a terminal session, the only way to kill the program is by pressing Ctrl-C. The window close button doesn't work, probably due to the use of ''usleep'' to create a delay between drawing frames. It does work if I use a freeglut timer function instead but that creates callback issues (the Wren VM is not re-entrant) and I wanted to minimize the use of freeglut in any case. |
|||
As with any Open GL aplication, the Wren code needs to be embedded in a host application which can communicate directly with the relevant libraries and which I've written here in C. |
|||
<lang ecmascript>/* webgl_rotating_F.wren */ |
|||
import "./math" for Math |
|||
var GL_ARRAY_BUFFER = 0x8892 |
|||
var GL_STATIC_DRAW = 0x88E4 |
|||
var GL_VERTEX_SHADER = 0x8B31 |
|||
var GL_FRAGMENT_SHADER = 0x8B30 |
|||
var GL_COLOR_BUFFER_BIT = 0x4000 |
|||
var GL_DEPTH_BUFFER_BIT = 0x0100 |
|||
var GL_CULL_FACE = 0x0B44 |
|||
var GL_DEPTH_TEST = 0x0B71 |
|||
var GL_UNSIGNED_BYTE = 0x1403 |
|||
var GL_FLOAT = 0x1406 |
|||
var GL_TRIANGLES = 0x0004 |
|||
var GLUT_ACTION_ON_WINDOW_CLOSE = 0x01F9 |
|||
var GLUT_ACTION_GLUTMAINLOOP_RETURNS = 0x0001 |
|||
var GLUT_SINGLE = 0x0000 |
|||
var GLUT_RGB = 0x0000 |
|||
var GLUT_DEPTH = 0x0010 |
|||
class Gl { |
|||
foreign static bufferData(target, size, data, usage) |
|||
foreign static createShader(type) |
|||
foreign static shaderSource(shader, count, string, length) |
|||
foreign static compileShader(shader) |
|||
foreign static createProgram() |
|||
foreign static attachShader(program, shader) |
|||
foreign static linkProgram(program) |
|||
foreign static getAttribLocation(program, name) |
|||
foreign static getUniformLocation(program, name) |
|||
foreign static genBuffers(n, buffers) |
|||
foreign static bindBuffer(target, buffer) |
|||
foreign static viewport(x, y, width, height) |
|||
foreign static clear(mask) |
|||
foreign static enable(cap) |
|||
foreign static useProgram(program) |
|||
foreign static drawArrays(mode, first, count) |
|||
foreign static enableVertexAttribArray(index) |
|||
foreign static vertexAttribPtr(index, size, type, normalized, stride, ptr) |
|||
foreign static uniformMatrix4fv(location, count, transpose, value) |
|||
foreign static flush() |
|||
} |
|||
class Glut { |
|||
foreign static initDisplayMode(mode) |
|||
foreign static initWindowSize(width, height) |
|||
foreign static createWindow(name) |
|||
foreign static setOption(eWhat, value) |
|||
} |
|||
class M4 { |
|||
static perspective(fieldOfViewInRadians, aspect, near, far, dst) { |
|||
if (!dst) dst = List.filled(16, 0) |
|||
var f = (Num.pi * 0.5 - 0.5 * fieldOfViewInRadians).tan |
|||
var rangeInv = 1 / (near - far) |
|||
dst[ 0] = f / aspect |
|||
dst[ 1] = 0 |
|||
dst[ 2] = 0 |
|||
dst[ 3] = 0 |
|||
dst[ 4] = 0 |
|||
dst[ 5] = f |
|||
dst[ 6] = 0 |
|||
dst[ 7] = 0 |
|||
dst[ 8] = 0 |
|||
dst[ 9] = 0 |
|||
dst[10] = (near + far) * rangeInv |
|||
dst[11] = -1 |
|||
dst[12] = 0 |
|||
dst[13] = 0 |
|||
dst[14] = near * far * rangeInv * 2 |
|||
dst[15] = 0 |
|||
return dst |
|||
} |
|||
static translate(m, tx, ty, tz, dst) { |
|||
if (!dst) dst = List.filled(16, 0) |
|||
var m00 = m[0] |
|||
var m01 = m[1] |
|||
var m02 = m[2] |
|||
var m03 = m[3] |
|||
var m10 = m[1 * 4 + 0] |
|||
var m11 = m[1 * 4 + 1] |
|||
var m12 = m[1 * 4 + 2] |
|||
var m13 = m[1 * 4 + 3] |
|||
var m20 = m[2 * 4 + 0] |
|||
var m21 = m[2 * 4 + 1] |
|||
var m22 = m[2 * 4 + 2] |
|||
var m23 = m[2 * 4 + 3] |
|||
var m30 = m[3 * 4 + 0] |
|||
var m31 = m[3 * 4 + 1] |
|||
var m32 = m[3 * 4 + 2] |
|||
var m33 = m[3 * 4 + 3] |
|||
if (m != dst) { |
|||
dst[ 0] = m00 |
|||
dst[ 1] = m01 |
|||
dst[ 2] = m02 |
|||
dst[ 3] = m03 |
|||
dst[ 4] = m10 |
|||
dst[ 5] = m11 |
|||
dst[ 6] = m12 |
|||
dst[ 7] = m13 |
|||
dst[ 8] = m20 |
|||
dst[ 9] = m21 |
|||
dst[10] = m22 |
|||
dst[11] = m23 |
|||
} |
|||
dst[12] = m00 * tx + m10 * ty + m20 * tz + m30 |
|||
dst[13] = m01 * tx + m11 * ty + m21 * tz + m31 |
|||
dst[14] = m02 * tx + m12 * ty + m22 * tz + m32 |
|||
dst[15] = m03 * tx + m13 * ty + m23 * tz + m33 |
|||
return dst |
|||
} |
|||
static xRotate(m, angleInRadians, dst) { |
|||
if (!dst) dst = List.filled(16, 0) |
|||
var m10 = m[4] |
|||
var m11 = m[5] |
|||
var m12 = m[6] |
|||
var m13 = m[7] |
|||
var m20 = m[8] |
|||
var m21 = m[9] |
|||
var m22 = m[10] |
|||
var m23 = m[11] |
|||
var c = angleInRadians.cos |
|||
var s = angleInRadians.sin |
|||
dst[4] = c * m10 + s * m20 |
|||
dst[5] = c * m11 + s * m21 |
|||
dst[6] = c * m12 + s * m22 |
|||
dst[7] = c * m13 + s * m23 |
|||
dst[8] = c * m20 - s * m10 |
|||
dst[9] = c * m21 - s * m11 |
|||
dst[10] = c * m22 - s * m12 |
|||
dst[11] = c * m23 - s * m13 |
|||
if (m != dst) { |
|||
dst[ 0] = m[ 0] |
|||
dst[ 1] = m[ 1] |
|||
dst[ 2] = m[ 2] |
|||
dst[ 3] = m[ 3] |
|||
dst[12] = m[12] |
|||
dst[13] = m[13] |
|||
dst[14] = m[14] |
|||
dst[15] = m[15] |
|||
} |
|||
return dst |
|||
} |
|||
static yRotate(m, angleInRadians, dst) { |
|||
if (!dst) dst = List.filled(16, 0) |
|||
var m00 = m[0 * 4 + 0] |
|||
var m01 = m[0 * 4 + 1] |
|||
var m02 = m[0 * 4 + 2] |
|||
var m03 = m[0 * 4 + 3] |
|||
var m20 = m[2 * 4 + 0] |
|||
var m21 = m[2 * 4 + 1] |
|||
var m22 = m[2 * 4 + 2] |
|||
var m23 = m[2 * 4 + 3] |
|||
var c = angleInRadians.cos |
|||
var s = angleInRadians.sin |
|||
dst[ 0] = c * m00 - s * m20 |
|||
dst[ 1] = c * m01 - s * m21 |
|||
dst[ 2] = c * m02 - s * m22 |
|||
dst[ 3] = c * m03 - s * m23 |
|||
dst[ 8] = c * m20 + s * m00 |
|||
dst[ 9] = c * m21 + s * m01 |
|||
dst[10] = c * m22 + s * m02 |
|||
dst[11] = c * m23 + s * m03 |
|||
if (m != dst) { |
|||
dst[ 4] = m[ 4] |
|||
dst[ 5] = m[ 5] |
|||
dst[ 6] = m[ 6] |
|||
dst[ 7] = m[ 7] |
|||
dst[12] = m[12] |
|||
dst[13] = m[13] |
|||
dst[14] = m[14] |
|||
dst[15] = m[15] |
|||
} |
|||
return dst |
|||
} |
|||
static zRotate(m, angleInRadians, dst) { |
|||
if (!dst) dst = List.filled(16, 0) |
|||
var m00 = m[0 * 4 + 0] |
|||
var m01 = m[0 * 4 + 1] |
|||
var m02 = m[0 * 4 + 2] |
|||
var m03 = m[0 * 4 + 3] |
|||
var m10 = m[1 * 4 + 0] |
|||
var m11 = m[1 * 4 + 1] |
|||
var m12 = m[1 * 4 + 2] |
|||
var m13 = m[1 * 4 + 3] |
|||
var c = angleInRadians.cos |
|||
var s = angleInRadians.sin |
|||
dst[ 0] = c * m00 + s * m10 |
|||
dst[ 1] = c * m01 + s * m11 |
|||
dst[ 2] = c * m02 + s * m12 |
|||
dst[ 3] = c * m03 + s * m13 |
|||
dst[ 4] = c * m10 - s * m00 |
|||
dst[ 5] = c * m11 - s * m01 |
|||
dst[ 6] = c * m12 - s * m02 |
|||
dst[ 7] = c * m13 - s * m03 |
|||
if (m != dst) { |
|||
dst[ 8] = m[ 8] |
|||
dst[ 9] = m[ 9] |
|||
dst[10] = m[10] |
|||
dst[11] = m[11] |
|||
dst[12] = m[12] |
|||
dst[13] = m[13] |
|||
dst[14] = m[14] |
|||
dst[15] = m[15] |
|||
} |
|||
return dst |
|||
} |
|||
static scale(m, sx, sy, sz, dst) { |
|||
if (!dst) dst = List.filled(16, 0) |
|||
dst[ 0] = sx * m[0 * 4 + 0] |
|||
dst[ 1] = sx * m[0 * 4 + 1] |
|||
dst[ 2] = sx * m[0 * 4 + 2] |
|||
dst[ 3] = sx * m[0 * 4 + 3] |
|||
dst[ 4] = sy * m[1 * 4 + 0] |
|||
dst[ 5] = sy * m[1 * 4 + 1] |
|||
dst[ 6] = sy * m[1 * 4 + 2] |
|||
dst[ 7] = sy * m[1 * 4 + 3] |
|||
dst[ 8] = sz * m[2 * 4 + 0] |
|||
dst[ 9] = sz * m[2 * 4 + 1] |
|||
dst[10] = sz * m[2 * 4 + 2] |
|||
dst[11] = sz * m[2 * 4 + 3] |
|||
if (m != dst) { |
|||
dst[12] = m[12] |
|||
dst[13] = m[13] |
|||
dst[14] = m[14] |
|||
dst[15] = m[15] |
|||
} |
|||
return dst |
|||
} |
|||
} |
|||
class C { |
|||
foreign static usleep(usec) |
|||
} |
|||
// Fill the buffer with the values that define a letter 'F'. |
|||
var setGeometry = Fn.new { |
|||
Gl.bufferData( |
|||
GL_ARRAY_BUFFER, |
|||
4, // size of 32 bit float |
|||
[ |
|||
// left column front |
|||
0, 0, 0, |
|||
0, 150, 0, |
|||
30, 0, 0, |
|||
0, 150, 0, |
|||
30, 150, 0, |
|||
30, 0, 0, |
|||
// top rung front |
|||
30, 0, 0, |
|||
30, 30, 0, |
|||
100, 0, 0, |
|||
30, 30, 0, |
|||
100, 30, 0, |
|||
100, 0, 0, |
|||
// middle rung front |
|||
30, 60, 0, |
|||
30, 90, 0, |
|||
67, 60, 0, |
|||
30, 90, 0, |
|||
67, 90, 0, |
|||
67, 60, 0, |
|||
// left column back |
|||
0, 0, 30, |
|||
30, 0, 30, |
|||
0, 150, 30, |
|||
0, 150, 30, |
|||
30, 0, 30, |
|||
30, 150, 30, |
|||
// top rung back |
|||
30, 0, 30, |
|||
100, 0, 30, |
|||
30, 30, 30, |
|||
30, 30, 30, |
|||
100, 0, 30, |
|||
100, 30, 30, |
|||
// middle rung back |
|||
30, 60, 30, |
|||
67, 60, 30, |
|||
30, 90, 30, |
|||
30, 90, 30, |
|||
67, 60, 30, |
|||
67, 90, 30, |
|||
// top |
|||
0, 0, 0, |
|||
100, 0, 0, |
|||
100, 0, 30, |
|||
0, 0, 0, |
|||
100, 0, 30, |
|||
0, 0, 30, |
|||
// top rung right |
|||
100, 0, 0, |
|||
100, 30, 0, |
|||
100, 30, 30, |
|||
100, 0, 0, |
|||
100, 30, 30, |
|||
100, 0, 30, |
|||
// under top rung |
|||
30, 30, 0, |
|||
30, 30, 30, |
|||
100, 30, 30, |
|||
30, 30, 0, |
|||
100, 30, 30, |
|||
100, 30, 0, |
|||
// between top rung and middle |
|||
30, 30, 0, |
|||
30, 60, 30, |
|||
30, 30, 30, |
|||
30, 30, 0, |
|||
30, 60, 0, |
|||
30, 60, 30, |
|||
// top of middle rung |
|||
30, 60, 0, |
|||
67, 60, 30, |
|||
30, 60, 30, |
|||
30, 60, 0, |
|||
67, 60, 0, |
|||
67, 60, 30, |
|||
// right of middle rung |
|||
67, 60, 0, |
|||
67, 90, 30, |
|||
67, 60, 30, |
|||
67, 60, 0, |
|||
67, 90, 0, |
|||
67, 90, 30, |
|||
// bottom of middle rung. |
|||
30, 90, 0, |
|||
30, 90, 30, |
|||
67, 90, 30, |
|||
30, 90, 0, |
|||
67, 90, 30, |
|||
67, 90, 0, |
|||
// right of bottom |
|||
30, 90, 0, |
|||
30, 150, 30, |
|||
30, 90, 30, |
|||
30, 90, 0, |
|||
30, 150, 0, |
|||
30, 150, 30, |
|||
// bottom |
|||
0, 150, 0, |
|||
0, 150, 30, |
|||
30, 150, 30, |
|||
0, 150, 0, |
|||
30, 150, 30, |
|||
30, 150, 0, |
|||
// left side |
|||
0, 0, 0, |
|||
0, 0, 30, |
|||
0, 150, 30, |
|||
0, 0, 0, |
|||
0, 150, 30, |
|||
0, 150, 0 |
|||
], |
|||
GL_STATIC_DRAW |
|||
) |
|||
} |
|||
// Fill the buffer with colors for the 'F'. |
|||
var setColors = Fn.new { |
|||
Gl.bufferData( |
|||
GL_ARRAY_BUFFER, |
|||
1, // size of unsigned byte |
|||
[ |
|||
// left column front |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
// top rung front |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
// middle rung front |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
200, 70, 120, |
|||
// left column back |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
// top rung back |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
// middle rung back |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
80, 70, 200, |
|||
// top |
|||
70, 200, 210, |
|||
70, 200, 210, |
|||
70, 200, 210, |
|||
70, 200, 210, |
|||
70, 200, 210, |
|||
70, 200, 210, |
|||
// top rung right |
|||
200, 200, 70, |
|||
200, 200, 70, |
|||
200, 200, 70, |
|||
200, 200, 70, |
|||
200, 200, 70, |
|||
200, 200, 70, |
|||
// under top rung |
|||
210, 100, 70, |
|||
210, 100, 70, |
|||
210, 100, 70, |
|||
210, 100, 70, |
|||
210, 100, 70, |
|||
210, 100, 70, |
|||
// between top rung and middle |
|||
210, 160, 70, |
|||
210, 160, 70, |
|||
210, 160, 70, |
|||
210, 160, 70, |
|||
210, 160, 70, |
|||
210, 160, 70, |
|||
// top of middle rung |
|||
70, 180, 210, |
|||
70, 180, 210, |
|||
70, 180, 210, |
|||
70, 180, 210, |
|||
70, 180, 210, |
|||
70, 180, 210, |
|||
// right of middle rung |
|||
100, 70, 210, |
|||
100, 70, 210, |
|||
100, 70, 210, |
|||
100, 70, 210, |
|||
100, 70, 210, |
|||
100, 70, 210, |
|||
// bottom of middle rung. |
|||
76, 210, 100, |
|||
76, 210, 100, |
|||
76, 210, 100, |
|||
76, 210, 100, |
|||
76, 210, 100, |
|||
76, 210, 100, |
|||
// right of bottom |
|||
140, 210, 80, |
|||
140, 210, 80, |
|||
140, 210, 80, |
|||
140, 210, 80, |
|||
140, 210, 80, |
|||
140, 210, 80, |
|||
// bottom |
|||
90, 130, 110, |
|||
90, 130, 110, |
|||
90, 130, 110, |
|||
90, 130, 110, |
|||
90, 130, 110, |
|||
90, 130, 110, |
|||
// left side |
|||
160, 160, 220, |
|||
160, 160, 220, |
|||
160, 160, 220, |
|||
160, 160, 220, |
|||
160, 160, 220, |
|||
160, 160, 220 |
|||
], |
|||
GL_STATIC_DRAW |
|||
) |
|||
} |
|||
var Program |
|||
var PositionLocation |
|||
var ColorLocation |
|||
var MatrixLocation |
|||
var PositionBuffer |
|||
var ColorBuffer |
|||
var init = Fn.new { |
|||
// create vertex shader |
|||
var vShaderSource = """ |
|||
#version 100 |
|||
attribute vec4 a_position; |
|||
attribute vec4 a_color; |
|||
uniform mat4 u_matrix; |
|||
varying vec4 v_color; |
|||
void main() { |
|||
// Multiply the position by the matrix. |
|||
gl_Position = u_matrix * a_position; |
|||
// Pass the color to the fragment shader. |
|||
v_color = a_color; |
|||
} |
|||
""" |
|||
var vShader = Gl.createShader(GL_VERTEX_SHADER) |
|||
if (vShader == 0) Fiber.abort("Error creating vertex shader.") |
|||
Gl.shaderSource(vShader, 1, vShaderSource, null) |
|||
Gl.compileShader(vShader) |
|||
// create fragment shader |
|||
var fShaderSource = """ |
|||
#version 100 |
|||
precision mediump float; |
|||
// Passed in from the vertex shader. |
|||
varying vec4 v_color; |
|||
void main() { |
|||
gl_FragColor = v_color; |
|||
} |
|||
""" |
|||
var fShader = Gl.createShader(GL_FRAGMENT_SHADER) |
|||
if (fShader == 0) Fiber.abort("Error creating fragment shader.") |
|||
Gl.shaderSource(fShader, 1, fShaderSource, null) |
|||
Gl.compileShader(fShader) |
|||
// setup GLSL program |
|||
Program = Gl.createProgram() |
|||
Gl.attachShader(Program, vShader) |
|||
Gl.attachShader(Program, fShader) |
|||
Gl.linkProgram(Program) |
|||
// look up where the vertex data needs to go |
|||
PositionLocation = Gl.getAttribLocation(Program, "a_position") |
|||
ColorLocation = Gl.getAttribLocation(Program, "a_color") |
|||
// lookup uniforms |
|||
MatrixLocation = Gl.getUniformLocation(Program, "u_matrix") |
|||
// generate 2 buffer object names |
|||
var buffers = List.filled(2, 0) |
|||
Gl.genBuffers(2, buffers) |
|||
// Create a buffer to put positions in |
|||
PositionBuffer = buffers[0] |
|||
// Bind it to ARRAY_BUFFER |
|||
Gl.bindBuffer(GL_ARRAY_BUFFER, PositionBuffer) |
|||
// Put geometry data into buffer |
|||
setGeometry.call() |
|||
// Create a buffer to put colors in |
|||
ColorBuffer = buffers[1] |
|||
// Bind it to ARRAY_BUFFER |
|||
Gl.bindBuffer(GL_ARRAY_BUFFER, ColorBuffer) |
|||
// Put color data into buffer |
|||
setColors.call() |
|||
} |
|||
var Translation = [0, 0, -360] |
|||
var Rotation = [Math.radians(190), Math.radians(40), Math.radians(320)] |
|||
var Scale = [1, 1, 1] |
|||
var FieldOfViewRadians = Math.radians(60) |
|||
var WindowWidth = 640 |
|||
var WindowHeight = 480 |
|||
var drawScene // recursive function |
|||
drawScene = Fn.new { |
|||
Rotation[1] = Rotation[1] + 0.01 |
|||
if (Rotation[1] >= 360) Rotation[1] = 0 |
|||
// tell GLES how to convert from clip space to pixels |
|||
Gl.viewport(0, 0, WindowWidth, WindowHeight) |
|||
// clear the window and the depth buffer |
|||
Gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) |
|||
// turn on culling. By default backfacing triangles will be culled |
|||
Gl.enable(GL_CULL_FACE) |
|||
// Enable the depth buffer |
|||
Gl.enable(GL_DEPTH_TEST) |
|||
// Tell it to use our program (pair of shaders) |
|||
Gl.useProgram(Program) |
|||
// Turn on the position attribute |
|||
Gl.enableVertexAttribArray(PositionLocation) |
|||
// Bind the position buffer. |
|||
Gl.bindBuffer(GL_ARRAY_BUFFER, PositionBuffer) |
|||
// tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER) |
|||
var size = 3 // 3 components per iteration |
|||
var type = GL_FLOAT // the data is 32 bit floats |
|||
var normalize = false // don't normalize the data |
|||
var stride = 0 // 0 = move forward size * sizeof(type) each iteration to get the next position |
|||
var offset = 0 // start at the beginning of the buffer |
|||
Gl.vertexAttribPtr(PositionLocation, size, type, normalize, stride, offset) |
|||
// Turn on the color attribute |
|||
Gl.enableVertexAttribArray(ColorLocation) |
|||
// Bind the color buffer. |
|||
Gl.bindBuffer(GL_ARRAY_BUFFER, ColorBuffer) |
|||
// Tell the attribute how to get data out of colorBuffer (ARRAY_BUFFER) |
|||
Gl.vertexAttribPtr(ColorLocation, size, type, normalize, stride, offset) |
|||
// compute the matrices |
|||
var aspect = WindowWidth / WindowHeight |
|||
var matrix = M4.perspective(FieldOfViewRadians, aspect, 1, 2000, null) |
|||
matrix = M4.translate(matrix, Translation[0], Translation[1], Translation[2], null) |
|||
matrix = M4.xRotate(matrix, Rotation[0], null) |
|||
matrix = M4.yRotate(matrix, Rotation[1], null) |
|||
matrix = M4.zRotate(matrix, Rotation[2], null) |
|||
matrix = M4.scale(matrix, Scale[0], Scale[1], Scale[2], null) |
|||
// set the matrix |
|||
Gl.uniformMatrix4fv(MatrixLocation, 1, false, matrix) |
|||
// draw the geometry |
|||
var primitiveType = GL_TRIANGLES |
|||
var count = 16 * 6 |
|||
Gl.drawArrays(primitiveType, offset, count) |
|||
Gl.flush() |
|||
C.usleep(10000) // wait for 10 msec |
|||
drawScene.call() |
|||
} |
|||
Glut.initDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH) |
|||
Glut.initWindowSize(640, 480) |
|||
Glut.createWindow("Rotating F") |
|||
Glut.setOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS) |
|||
init.call() |
|||
drawScene.call()</lang> |
|||
<br> |
|||
We now embed the above code in the following C program, compile and run. |
|||
<lang c>/* gcc webgl_rotating_F.c -o webgl_rotating_F -lglut -lGLESv2 -lm -lwren */ |
|||
#include <stdlib.h> |
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
#include <GLES2/gl2.h> |
|||
#include <GL/freeglut.h> |
|||
#include <unistd.h> |
|||
#include "wren.h" |
|||
/* Gl functions */ |
|||
void C_bufferData(WrenVM* vm) { |
|||
GLenum target = (GLenum)wrenGetSlotDouble(vm, 1); |
|||
GLsizeiptr size = (GLsizeiptr)wrenGetSlotDouble(vm, 2); |
|||
int count = wrenGetListCount(vm, 3); |
|||
GLenum usage = (GLenum)wrenGetSlotDouble(vm, 4); |
|||
float data[count]; |
|||
for (int i = 0; i < count; ++i) { |
|||
wrenGetListElement(vm, 3, i, 1); |
|||
data[i] = (float)wrenGetSlotDouble(vm, 1); |
|||
if (size == 1) data[i] /= 255.0; |
|||
} |
|||
glBufferData(target, 4 * count, data, usage); |
|||
} |
|||
void C_createShader(WrenVM* vm) { |
|||
GLenum shaderType = (GLenum)wrenGetSlotDouble(vm, 1); |
|||
GLuint shader = glCreateShader(shaderType); |
|||
wrenSetSlotDouble(vm, 0, (double)shader); |
|||
} |
|||
void C_shaderSource(WrenVM* vm) { |
|||
GLuint shader = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
GLsizei count = (GLsizei)wrenGetSlotDouble(vm, 2); |
|||
// assume for simplicity there will always be one shader string and the fourth parameter will be null |
|||
const GLchar *string = (const GLchar *)wrenGetSlotString(vm, 3); |
|||
glShaderSource(shader, count, &string, 0); |
|||
} |
|||
void C_compileShader(WrenVM* vm) { |
|||
GLuint shader = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
glCompileShader(shader); |
|||
// check shader compiled ok |
|||
GLint shader_compiled; |
|||
glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_compiled); |
|||
if (shader_compiled != GL_TRUE) { |
|||
GLsizei log_length = 0; |
|||
GLchar message[1024]; |
|||
glGetShaderInfoLog(shader, 1024, &log_length, message); |
|||
printf("%s\n", message); |
|||
} |
|||
} |
|||
void C_createProgram(WrenVM* vm) { |
|||
GLuint program = glCreateProgram(); |
|||
wrenSetSlotDouble(vm, 0, (double)program); |
|||
} |
|||
void C_attachShader(WrenVM* vm) { |
|||
GLuint program = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
GLuint shader = (GLuint)wrenGetSlotDouble(vm, 2); |
|||
glAttachShader(program, shader); |
|||
} |
|||
void C_linkProgram(WrenVM* vm) { |
|||
GLuint program = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
// check program linked ok |
|||
glBindAttribLocation(program, 0, "position"); |
|||
glBindAttribLocation(program, 1, "texcoord"); |
|||
glBindAttribLocation(program, 2, "normal"); |
|||
glBindAttribLocation(program, 3, "color"); |
|||
glLinkProgram(program); |
|||
GLint program_linked; |
|||
glGetProgramiv(program, GL_LINK_STATUS, &program_linked); |
|||
if (program_linked != GL_TRUE) { |
|||
GLsizei log_length = 0; |
|||
GLchar message[1024]; |
|||
glGetProgramInfoLog(program, 1024, &log_length, message); |
|||
printf("%s\n", message); |
|||
} |
|||
} |
|||
void C_getAttribLocation(WrenVM* vm) { |
|||
GLuint program = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
const GLchar *name = (const GLchar *)wrenGetSlotString(vm, 2); |
|||
GLint location = glGetAttribLocation(program, name); |
|||
wrenSetSlotDouble(vm, 0, (double)location); |
|||
} |
|||
void C_getUniformLocation(WrenVM* vm) { |
|||
GLuint program = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
const GLchar *name = (const GLchar *)wrenGetSlotString(vm, 2); |
|||
GLint location = glGetUniformLocation(program, name); |
|||
wrenSetSlotDouble(vm, 0, (double)location); |
|||
} |
|||
void C_genBuffers(WrenVM* vm) { |
|||
GLsizei n = (GLsizei)wrenGetSlotDouble(vm, 1); |
|||
int count = wrenGetListCount(vm, 2); |
|||
GLuint buffers[count]; |
|||
glGenBuffers(n, buffers); |
|||
for (int i = 0; i < count; ++i) { |
|||
wrenSetSlotDouble(vm, 1, (double)buffers[i]); |
|||
wrenSetListElement(vm, 2, i, 1); |
|||
} |
|||
} |
|||
void C_bindBuffer(WrenVM* vm) { |
|||
GLenum target = (GLenum)wrenGetSlotDouble(vm, 1); |
|||
GLuint buffer = (GLuint)wrenGetSlotDouble(vm, 2); |
|||
glBindBuffer(target, buffer); |
|||
} |
|||
void C_viewport(WrenVM* vm) { |
|||
GLint x = (GLint)wrenGetSlotDouble(vm, 1); |
|||
GLint y = (GLint)wrenGetSlotDouble(vm, 2); |
|||
GLsizei width = (GLsizei)wrenGetSlotDouble(vm, 3); |
|||
GLsizei height = (GLsizei)wrenGetSlotDouble(vm, 4); |
|||
glViewport(x, y, width, height); |
|||
} |
|||
void C_clear(WrenVM* vm) { |
|||
glClearColor(1, 1, 1, 1); // always clear to white background |
|||
GLbitfield mask = (GLbitfield)wrenGetSlotDouble(vm, 1); |
|||
glClear(mask); |
|||
} |
|||
void C_enable(WrenVM* vm) { |
|||
GLenum cap = (GLenum)wrenGetSlotDouble(vm, 1); |
|||
glEnable(cap); |
|||
} |
|||
void C_useProgram(WrenVM* vm) { |
|||
GLuint program = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
glUseProgram(program); |
|||
} |
|||
void C_drawArrays(WrenVM* vm) { |
|||
GLenum mode = (GLenum)wrenGetSlotDouble(vm, 1); |
|||
GLint first = (GLint)wrenGetSlotDouble(vm, 2); |
|||
GLsizei count = (GLsizei)wrenGetSlotDouble(vm, 3); |
|||
glDrawArrays(mode, first, count); |
|||
} |
|||
void C_enableVertexAttribArray(WrenVM* vm) { |
|||
GLuint index = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
glEnableVertexAttribArray(index); |
|||
} |
|||
void C_vertexAttribPtr(WrenVM* vm) { |
|||
GLuint index = (GLuint)wrenGetSlotDouble(vm, 1); |
|||
GLint size = (GLint)wrenGetSlotDouble(vm, 2); |
|||
GLenum type = (GLenum)wrenGetSlotDouble(vm, 3); |
|||
GLboolean normalized = (GLboolean)wrenGetSlotBool(vm, 4); |
|||
GLsizei stride = (GLsizei)wrenGetSlotDouble(vm, 5); |
|||
// assume for simplicity sixth parameter will always be 0 |
|||
glVertexAttribPointer(index, size, type, normalized, stride, 0); |
|||
} |
|||
void C_uniformMatrix4fv(WrenVM* vm) { |
|||
GLint location = (GLint)wrenGetSlotDouble(vm, 1); |
|||
GLsizei count = (GLsizei)wrenGetSlotDouble(vm, 2); |
|||
GLboolean transpose = (GLboolean)wrenGetSlotBool(vm, 3); |
|||
int len = wrenGetListCount(vm, 4); |
|||
GLfloat value[len]; |
|||
for (int i = 0; i < len; ++i) { |
|||
wrenGetListElement(vm, 4, i, 1); |
|||
value[i] = (GLfloat)wrenGetSlotDouble(vm, 1); |
|||
} |
|||
glUniformMatrix4fv(location, count, transpose, value); |
|||
} |
|||
void C_flush(WrenVM* vm) { |
|||
glFlush(); |
|||
} |
|||
/* Glut functions */ |
|||
void C_initDisplayMode(WrenVM* vm) { |
|||
unsigned int mode = (unsigned int)wrenGetSlotDouble(vm, 1); |
|||
glutInitDisplayMode(mode); |
|||
} |
|||
void C_initWindowSize(WrenVM* vm) { |
|||
int width = (int)wrenGetSlotDouble(vm, 1); |
|||
int height = (int)wrenGetSlotDouble(vm, 2); |
|||
glutInitWindowSize(width, height); |
|||
} |
|||
void C_createWindow(WrenVM* vm) { |
|||
const char *name = wrenGetSlotString(vm, 1); |
|||
glutCreateWindow(name); |
|||
} |
|||
void C_setOption(WrenVM* vm) { |
|||
GLenum eWhat = (GLenum)wrenGetSlotDouble(vm, 1); |
|||
int value = (int)wrenGetSlotDouble(vm, 2); |
|||
glutSetOption(eWhat, value); |
|||
} |
|||
/* C function */ |
|||
void C_usleep(WrenVM* vm) { |
|||
useconds_t usec = (useconds_t)wrenGetSlotDouble(vm, 1); |
|||
usleep(usec); |
|||
} |
|||
WrenForeignMethodFn bindForeignMethod( |
|||
WrenVM* vm, |
|||
const char* module, |
|||
const char* className, |
|||
bool isStatic, |
|||
const char* signature) { |
|||
if (strcmp(module, "main") == 0) { |
|||
if (strcmp(className, "Gl") == 0) { |
|||
if (isStatic && strcmp(signature, "bufferData(_,_,_,_)") == 0) return C_bufferData; |
|||
if (isStatic && strcmp(signature, "createShader(_)") == 0) return C_createShader; |
|||
if (isStatic && strcmp(signature, "shaderSource(_,_,_,_)") == 0) return C_shaderSource; |
|||
if (isStatic && strcmp(signature, "compileShader(_)") == 0) return C_compileShader; |
|||
if (isStatic && strcmp(signature, "createProgram()") == 0) return C_createProgram; |
|||
if (isStatic && strcmp(signature, "attachShader(_,_)") == 0) return C_attachShader; |
|||
if (isStatic && strcmp(signature, "linkProgram(_)") == 0) return C_linkProgram; |
|||
if (isStatic && strcmp(signature, "getAttribLocation(_,_)") == 0) return C_getAttribLocation; |
|||
if (isStatic && strcmp(signature, "getUniformLocation(_,_)") == 0) return C_getUniformLocation; |
|||
if (isStatic && strcmp(signature, "genBuffers(_,_)") == 0) return C_genBuffers; |
|||
if (isStatic && strcmp(signature, "bindBuffer(_,_)") == 0) return C_bindBuffer; |
|||
if (isStatic && strcmp(signature, "viewport(_,_,_,_)") == 0) return C_viewport; |
|||
if (isStatic && strcmp(signature, "clear(_)") == 0) return C_clear; |
|||
if (isStatic && strcmp(signature, "enable(_)") == 0) return C_enable; |
|||
if (isStatic && strcmp(signature, "useProgram(_)") == 0) return C_useProgram; |
|||
if (isStatic && strcmp(signature, "drawArrays(_,_,_)") == 0) return C_drawArrays; |
|||
if (isStatic && strcmp(signature, "flush()") == 0) return C_flush; |
|||
if (isStatic && strcmp(signature, "enableVertexAttribArray(_)") == 0) return C_enableVertexAttribArray; |
|||
if (isStatic && strcmp(signature, "vertexAttribPtr(_,_,_,_,_,_)") == 0) return C_vertexAttribPtr; |
|||
if (isStatic && strcmp(signature, "uniformMatrix4fv(_,_,_,_)") == 0) return C_uniformMatrix4fv; |
|||
} else if (strcmp(className, "Glut") == 0) { |
|||
if (isStatic && strcmp(signature, "initDisplayMode(_)") == 0) return C_initDisplayMode; |
|||
if (isStatic && strcmp(signature, "initWindowSize(_,_)") == 0) return C_initWindowSize; |
|||
if (isStatic && strcmp(signature, "createWindow(_)") == 0) return C_createWindow; |
|||
if (isStatic && strcmp(signature, "setOption(_,_)") == 0) return C_setOption; |
|||
} else if (strcmp(className, "C") == 0) { |
|||
if (isStatic && strcmp(signature, "usleep(_)") == 0) return C_usleep; |
|||
} |
|||
} |
|||
return NULL; |
|||
} |
|||
static void writeFn(WrenVM* vm, const char* text) { |
|||
printf("%s", text); |
|||
} |
|||
void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) { |
|||
switch (errorType) { |
|||
case WREN_ERROR_COMPILE: |
|||
printf("[%s line %d] [Error] %s\n", module, line, msg); |
|||
break; |
|||
case WREN_ERROR_STACK_TRACE: |
|||
printf("[%s line %d] in %s\n", module, line, msg); |
|||
break; |
|||
case WREN_ERROR_RUNTIME: |
|||
printf("[Runtime Error] %s\n", msg); |
|||
break; |
|||
} |
|||
} |
|||
char *readFile(const char *fileName) { |
|||
FILE *f = fopen(fileName, "r"); |
|||
fseek(f, 0, SEEK_END); |
|||
long fsize = ftell(f); |
|||
rewind(f); |
|||
char *script = malloc(fsize + 1); |
|||
fread(script, 1, fsize, f); |
|||
fclose(f); |
|||
script[fsize] = 0; |
|||
return script; |
|||
} |
|||
static void loadModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result) { |
|||
if( result.source) free((void*)result.source); |
|||
} |
|||
WrenLoadModuleResult loadModule(WrenVM* vm, const char* name) { |
|||
WrenLoadModuleResult result = {0}; |
|||
if (strcmp(name, "random") != 0 && strcmp(name, "meta") != 0) { |
|||
result.onComplete = loadModuleComplete; |
|||
char fullName[strlen(name) + 6]; |
|||
strcpy(fullName, name); |
|||
strcat(fullName, ".wren"); |
|||
result.source = readFile(fullName); |
|||
} |
|||
return result; |
|||
} |
|||
int main(int argc, char **argv) { |
|||
glutInit(&argc, argv); |
|||
glutInitContextVersion(2, 0); |
|||
WrenConfiguration config; |
|||
wrenInitConfiguration(&config); |
|||
config.writeFn = &writeFn; |
|||
config.errorFn = &errorFn; |
|||
config.bindForeignMethodFn = &bindForeignMethod; |
|||
config.loadModuleFn = &loadModule; |
|||
WrenVM* vm = wrenNewVM(&config); |
|||
const char* module = "main"; |
|||
const char* fileName = "webgl_rotating_F.wren"; |
|||
char *script = readFile(fileName); |
|||
WrenInterpretResult result = wrenInterpret(vm, module, script); |
|||
switch (result) { |
|||
case WREN_RESULT_COMPILE_ERROR: |
|||
printf("Compile Error!\n"); |
|||
break; |
|||
case WREN_RESULT_RUNTIME_ERROR: |
|||
printf("Runtime Error!\n"); |
|||
break; |
|||
case WREN_RESULT_SUCCESS: |
|||
break; |
|||
} |
|||
glutMainLoop(); |
|||
wrenFreeVM(vm); |
|||
free(script); |
|||
return 0; |
|||
}</lang> |