WebGL rotating F: Difference between revisions

m
m (→‎{{header|Phix}}: no teapot)
m (→‎{{header|Wren}}: Minor tidy)
 
(19 intermediate revisions by 5 users not shown)
Line 11:
;Optional extra
Allow toggling between the F shape and a classic Utah teapot.
 
 
=={{header|J}}==
 
Near as I can tell, the webgl requirement means that the task is asking for a wrapper for javascript (possibly encapsulated in a library).
 
Here, we'll use ray marching as that "feels sort of like J":
 
<syntaxhighlight lang=javascript>webgl_close=: {{ wd'pclose'}}
wd {{)n
pc webgl; cc w webview;
pmove 0 0 518 518;
pshow;
set w html *
<html><head><title>test</title></head>
<script>
function paint(t) {
gl.uniform1f(timeNdx, t/1e3);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(paint);
}
function run(){
c= document.querySelector("#c");
gl= c.getContext("webgl");
P= gl.createProgram();
function setShader(typ, src) {
shader= gl.createShader(typ);
gl.shaderSource(shader, src);
gl.compileShader(shader);
gl.attachShader(P, shader);
}
setShader(gl.VERTEX_SHADER, "attribute vec4 pos;void main(){gl_Position=pos;}")
setShader(gl.FRAGMENT_SHADER, `
precision mediump float;
uniform vec2 res;
uniform float time;
mat3 rotateY(float theta) {
float c= cos(theta);
float s= sin(theta);
return mat3(
vec3(c, 0, s),
vec3(0, 1, 0),
vec3(-s, 0, c)
);
}
const float MAX_DIST= 100.0;
float sdBox(vec3 position, vec3 box, vec3 offset) {
vec3 q= abs(position - offset) - box/2.0;
return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
float sdScene(vec3 p) { // distance to closest object
float dist= sdBox(p, vec3(1,4,1), vec3(0, -0.5, 0));
dist= min(dist, sdBox(p, vec3(1.5,1,1), vec3(0.75, -0.5, 0)));
return min(dist, sdBox(p, vec3(2.5,1,1), vec3(0.75, 1.5, 0)));
}
float rayMarch(vec3 ro, vec3 rd, float depth, float end) {
for (int i= 0; i< 100 /* MAX_MARCHING_STEPS */; i++) {
if (depth < end) depth+= sdScene(ro + depth * rd);
}
return depth;
}
vec3 calcNormal(in vec3 p) {
vec2 e= vec2(1.0, -1.0) * 0.0005; // epsilon
return normalize(
e.xyy * sdScene(p + e.xyy) +
e.yyx * sdScene(p + e.yyx) +
e.yxy * sdScene(p + e.yxy) +
e.xxx * sdScene(p + e.xxx));
}
void main(){
vec2 uv= (gl_FragCoord.xy-.5*res)/res.x;
mat3 rot= rotateY(time);
vec3 backgroundColor= vec3(0.835, 1, 1);
vec3 color;
vec3 ro= vec3(0, 0, 7)*rot; // ray origin that represents camera position
vec3 rd= normalize(vec3(uv, -1)*rot); // ray direction
float sd= rayMarch(ro, rd, 0.0 /* MIN_DIST */, MAX_DIST); // closest object
if (sd > MAX_DIST) {
color= backgroundColor; // ray didn't hit anything
} else {
vec3 p= ro + rd * sd; // point on cube we discovered from ray marching
vec3 normal= calcNormal(p);
vec3 lightPosition= vec3(2, 2, 7)*rot;
vec3 lightDirection= normalize(lightPosition - p);
float dif= clamp(dot(normal, lightDirection), 0.3, 1.); // diffuse reflection
color= dif * vec3(1,0,0) + backgroundColor * .2; // Add a bit of background color to the diffuse color
}
gl_FragColor= vec4(color, 1.0);
}
`); /* end of FRAGMENT_SHADER */
gl.linkProgram(P);
posNdx= gl.getAttribLocation(P, "pos");
timeNdx= gl.getUniformLocation(P, "time");
posBuffer= gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, -1,1, 1,-1, 1,1]), gl.STATIC_DRAW);
gl.viewport(0,0,c.width,c.height);
gl.useProgram(P);
gl.enableVertexAttribArray(posNdx);
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.vertexAttribPointer(posNdx, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(gl.getUniformLocation(P, "res"), c.width, c.height);
requestAnimationFrame(paint);
}
</script>
</head><body onload="run()" style="margin: 0">
<canvas id="c" width=500 height=500></canvas>
</body></html>
}}</syntaxhighlight>
 
In other words, we construct the letter F using three rectangular blocks near the origin of our coordinate system. And we construct a rectangle covering the screen using triangle strip. Finally, for each pixel on the screen we project a vector representing a potential pixel from our camera position towards the blocks.
 
In ray marching we find the minimum distance from an origin point to any point in the geometric structure, move in the ray direction that distance and repeat (in this case we repeat this operation 100 time for each pixel). After these iterations we have either shot past the geometry or we're within some small epsilon distance from the surface of the object. We can inspect the distance the array has progressed to determine whether we should use background color or surface color. And we can check distances with some minor variations on this ray to determine the orientation of the surface at that point.
 
Finally, to achieve the "rotating" effect, we spin our camera (and light source) so that it rotates around the blocks which form our letter "F", with the camera always facing towards the origin.
 
Tested in J9.03 and a J9.04 beta.
 
See also: https://tinyurl.com/rotatingF for a j-playground variation of this implementation (hit <code>Run</code> in the upper right corner).
 
=={{header|Julia}}==
Makie can use OpenGL as a backend for plotting (GLMakie). The code plots an 'F' as three rectangular solid-shaped scatter plots with overlapping markers, then rotates the resulting graph.
<syntaxhighlight lang="julia">using Colors
using GeometryBasics: Rect3f
using GLMakie
 
topbar = vec([Point3f(i / 5, j / 5, k / 5) for i in 1:7, j in 8:3:28, k in 45:50])
stem = vec([Point3f(i / 5, j / 5, k / 5) for i in 1:7, j in 1:7, k in 1:8:56])
midbar = vec([Point3f(i / 5, j / 5, k / 5) for i in 1:7, j in 8:2:21, k in 25:29])
fig = Figure(resolution = (800, 400))
pos = fig[1, 1]
meshscatter(pos, topbar; marker = Rect3f(Vec3f(-0.5), Vec3f(16)), transparency = true,
color = [RGBA(topbar[i]..., 0.5) for i in 1:length(topbar)])
meshscatter!(pos, stem; marker = Rect3f(Vec3f(-0.5), Vec3f(16)), transparency = true,
color = [RGBA(stem[i]..., 0.5) for i in 1:length(stem)])
meshscatter!(pos, midbar; marker = Rect3f(Vec3f(-0.5), Vec3f(16)), transparency = true,
color = [RGBA(midbar[i]..., 0.5) for i in 1:length(midbar)])
 
for _ in 1:28
display(fig.scene)
rotate!(Accum, fig.scene, 0.25)
sleep(0.25)
end
</syntaxhighlight>
 
=={{header|Phix}}==
You can run this online [http://phix.x10.mx/p2js/HelloF.htm here] (which gives me around 10% CPU load, with space toggling the timer for no extra load). No teapot, sorry.
<!--<langsyntaxhighlight Phixlang="phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\pGUI\HelloF.exw
Line 67 ⟶ 211:
-- with the right size, type, and possibly stride settings, and finally glDrawArrays() with the desired mode, here GL_TRIANGLES, not
-- forgetting the total length. Of course there are all sorts of other things to achieve smoothing, rotation, lighting effects, and
-- so on, but this quick little intro endeth here. The following notes are all very specifcspecific to this particular program.
--
-- First define a 2D 'F' as follows
Line 269 ⟶ 413:
<span style="color: #000080;font-style:italic;">// Put some text in the center of a canvas/pixel array.
// note this is a (bit of a|competecomplete) hack, see docs...</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">100</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">h</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">26</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">cdx</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">glCanvasSpecialText</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cd_canvas</span><span style="color: #0000FF;">,</span><span style="color: #000000;">w</span><span style="color: #0000FF;">,</span><span style="color: #000000;">h</span><span style="color: #0000FF;">,</span><span style="color: #000000;">20</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Hello!"</span><span style="color: #0000FF;">)</span>
Line 397 ⟶ 541:
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<!--</langsyntaxhighlight>-->
 
=={{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.
<syntaxhighlight lang="wren">/* 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()</syntaxhighlight>
<br>
We now embed the above code in the following C program, compile and run.
<syntaxhighlight 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;
}</syntaxhighlight>
9,476

edits