jsdom, examples: add more WebGL APIs; add interactive 3D cube example running on VJS (#12562)

pull/12571/head
playX 2021-11-24 21:31:39 +03:00 committed by GitHub
parent ff95cf18d4
commit b0bc112168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 586 additions and 1 deletions

View File

@ -0,0 +1,7 @@
Interactive 3D cube using DOM & WebGL API.
# Compiling
```
v -b js_browser examples/js_dom_cube/cube.js.v
```
Then you can open `index.html` with your favourite browser.

View File

@ -0,0 +1,452 @@
import jsdom
import math
const (
vert_code = 'attribute vec3 position;uniform mat4 Pmatrix;uniform mat4 Vmatrix;uniform mat4 Mmatrix;attribute vec3 color;varying vec3 vColor;void main(void) {gl_Position = Pmatrix * Vmatrix * Mmatrix * vec4(position,1.);vColor = color;}
'
frag_code = 'precision mediump float;varying vec3 vColor;void main(void) {gl_FragColor = vec4(vColor, 1.);}
'
vertices = [
f32(-1),
-1,
-1,
1,
-1,
-1,
1,
1,
-1,
-1,
1,
-1,
-1,
-1,
1,
1,
-1,
1,
1,
1,
1,
-1,
1,
1,
-1,
-1,
-1,
-1,
1,
-1,
-1,
1,
1,
-1,
-1,
1,
1,
-1,
-1,
1,
1,
-1,
1,
1,
1,
1,
-1,
1,
-1,
-1,
-1,
-1,
-1,
1,
1,
-1,
1,
1,
-1,
-1,
-1,
1,
-1,
-1,
1,
1,
1,
1,
1,
1,
1,
-1,
]
colors = [
f32(5),
3,
7,
5,
3,
7,
5,
3,
7,
5,
3,
7,
1,
1,
3,
1,
1,
3,
1,
1,
3,
1,
1,
3,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
1,
0,
1,
1,
0,
1,
1,
0,
1,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
0,
1,
0,
]
indices = [
u16(0),
1,
2,
0,
2,
3,
4,
5,
6,
4,
6,
7,
8,
9,
10,
8,
10,
11,
12,
13,
14,
12,
14,
15,
16,
17,
18,
16,
18,
19,
20,
21,
22,
20,
22,
23,
]
amortization = 0.95
)
fn get_webgl() (&JS.HTMLCanvasElement, JS.WebGLRenderingContext) {
elem := jsdom.document.getElementById('myCanvas'.str) or { panic('cannot get canvas') }
match elem {
JS.HTMLCanvasElement {
webgl := elem.getContext('experimental-webgl'.str, js_undefined()) or {
panic('context not found')
}
match webgl {
JS.WebGLRenderingContext {
return elem, webgl
}
else {
panic('cannot get webgl')
}
}
}
else {
panic('not an canvas')
}
}
}
fn get_projection(angle f64, a f64, z_min f64, z_max f64) []f64 {
ang := math.tan((angle * 0.5) * math.pi / 180)
return [
0.5 / ang,
0,
0,
0,
0,
0.5 * a / ang,
0,
0,
0,
0,
-(z_max + z_min) / (z_max - z_min),
-1,
0,
0,
(-2 * z_max * z_min) / (z_max - z_min),
0,
]
}
fn JS.Math.cos(JS.Number) JS.Number
fn JS.Math.sin(JS.Number) JS.Number
fn rotate_x(mut m []f64, angle f64) {
c := math.cos(angle)
s := math.sin(angle)
mv1 := m[1]
mv5 := m[5]
mv9 := m[9]
m[1] = m[1] * c - m[2] * s
m[5] = m[5] * c - m[6] * s
m[9] = m[9] * c - m[10] * s
m[2] = m[2] * c + mv1 * s
m[6] = m[6] * c + mv5 * s
m[10] = m[10] * c + mv9 * s
}
fn rotate_y(mut m []f64, angle f64) {
c := math.cos(angle)
s := math.sin(angle)
mv0 := m[0]
mv4 := m[4]
mv8 := m[8]
m[0] = c * m[0] + s * m[2]
m[4] = c * m[4] + s * m[6]
m[8] = c * m[8] + s * m[10]
m[2] = c * m[2] - s * mv0
m[6] = c * m[6] - s * mv4
m[10] = c * m[10] - s * mv8
}
struct State {
mut:
drag bool
gl JS.WebGLRenderingContext
canvas JS.HTMLCanvasElement
old_x f64
old_y f64
dx f64
dy f64
theta f64
phi f64
time_old f64
mo_matrix []f64
view_matrix []f64
proj_matrix []f64
pmatrix JS.WebGLUniformLocation
vmatrix JS.WebGLUniformLocation
mmatrix JS.WebGLUniformLocation
index_buffer JS.WebGLBuffer
}
fn animate(mut state State, time f64) {
if !state.drag {
state.dx = state.dx * amortization
state.dy = state.dy * amortization
state.theta += state.dx
state.phi += state.dy
}
state.mo_matrix[0] = 1
state.mo_matrix[1] = 0
state.mo_matrix[2] = 0
state.mo_matrix[3] = 0
state.mo_matrix[4] = 0
state.mo_matrix[5] = 1
state.mo_matrix[6] = 0
state.mo_matrix[7] = 0
state.mo_matrix[8] = 0
state.mo_matrix[9] = 0
state.mo_matrix[10] = 1
state.mo_matrix[11] = 0
state.mo_matrix[12] = 0
state.mo_matrix[13] = 0
state.mo_matrix[14] = 0
state.mo_matrix[15] = 1
// println('${state.theta} ${state.phi}')
rotate_x(mut state.mo_matrix, state.phi)
rotate_y(mut state.mo_matrix, state.theta)
state.time_old = time
state.gl.enable(jsdom.gl_depth_test())
state.gl.clearColor(0.5, 0.5, 0.5, 0.9)
state.gl.clearDepth(1.0)
state.gl.viewport(0.0, 0.0, state.canvas.width, state.canvas.height)
state.gl.clear(JS.Number(int(jsdom.gl_color_buffer_bit()) | int(jsdom.gl_depth_buffer_bit())))
state.gl.uniformMatrix4fv(state.pmatrix, JS.Boolean(false), state.proj_matrix.to_number_array())
state.gl.uniformMatrix4fv(state.vmatrix, JS.Boolean(false), state.view_matrix.to_number_array())
state.gl.uniformMatrix4fv(state.mmatrix, JS.Boolean(false), state.mo_matrix.to_number_array())
state.gl.bindBuffer(jsdom.gl_element_array_buffer(), state.index_buffer)
state.gl.drawElements(jsdom.gl_triangles(), indices.len, jsdom.gl_unsigned_short(),
0)
jsdom.window().requestAnimationFrame(fn [mut state] (time JS.Number) {
animate(mut state, f64(time))
})
}
fn main() {
canvas, gl := get_webgl()
vertex_buffer := gl.createBuffer() ?
gl.bindBuffer(jsdom.gl_array_buffer(), vertex_buffer)
gl.bufferData(jsdom.gl_array_buffer(), float32_array(vertices), jsdom.gl_static_draw())
color_buffer := gl.createBuffer() ?
gl.bindBuffer(jsdom.gl_array_buffer(), color_buffer)
gl.bufferData(jsdom.gl_array_buffer(), float32_array(colors), jsdom.gl_static_draw())
index_buffer := gl.createBuffer() ?
gl.bindBuffer(jsdom.gl_element_array_buffer(), index_buffer)
gl.bufferData(jsdom.gl_element_array_buffer(), uint16_array(indices), jsdom.gl_static_draw())
vert_shader := gl.createShader(jsdom.gl_vertex_shader()) ?
gl.shaderSource(vert_shader, vert_code.str)
gl.compileShader(vert_shader)
if !bool(JS.Boolean(gl.getShaderParameter(vert_shader, jsdom.gl_compile_status()))) {
panic('An error occurred when compiling vertex shader: ${string(gl.getShaderInfoLog(vert_shader))}')
}
frag_shader := gl.createShader(jsdom.gl_fragment_shader()) ?
gl.shaderSource(frag_shader, frag_code.str)
gl.compileShader(frag_shader)
if !bool(JS.Boolean(gl.getShaderParameter(frag_shader, jsdom.gl_compile_status()))) {
panic('An error occurred when compiling fragment shader: ${string(gl.getShaderInfoLog(frag_shader))}')
}
shader_program := gl.createProgram() ?
gl.attachShader(shader_program, vert_shader)
gl.attachShader(shader_program, frag_shader)
gl.linkProgram(shader_program)
if !bool(JS.Boolean(gl.getProgramParameter(shader_program, jsdom.gl_link_status()))) {
panic('unable to initialize the shader program: ${string(gl.getProgramInfoLog(shader_program))}')
}
pmatrix := gl.getUniformLocation(shader_program, 'Pmatrix'.str) ?
vmatrix := gl.getUniformLocation(shader_program, 'Vmatrix'.str) ?
mmatrix := gl.getUniformLocation(shader_program, 'Mmatrix'.str) ?
gl.bindBuffer(jsdom.gl_array_buffer(), vertex_buffer)
position := gl.getAttribLocation(shader_program, 'position'.str)
gl.vertexAttribPointer(position, JS.Number(3), jsdom.gl_float(), JS.Boolean(false),
JS.Number(0), JS.Number(0))
gl.enableVertexAttribArray(position)
gl.bindBuffer(jsdom.gl_array_buffer(), color_buffer)
color := gl.getAttribLocation(shader_program, 'color'.str)
gl.vertexAttribPointer(color, JS.Number(3), jsdom.gl_float(), JS.Boolean(false), JS.Number(0),
JS.Number(0))
gl.enableVertexAttribArray(color)
gl.useProgram(shader_program)
mut proj_matrix := get_projection(40.0, f64(canvas.width) / f64(canvas.height), 1.0,
100.0)
mut mo_matrix := [f64(1), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
mut view_matrix := [f64(1), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
view_matrix[14] = view_matrix[14] - 6
mut state := State{false, gl, canvas, 0, 0, 0, 0, 0, 0, 0, mo_matrix, view_matrix, proj_matrix, pmatrix, vmatrix, mmatrix, index_buffer}
canvas.addEventListener('mousedown'.str, fn [mut state] (e JS.Event) {
state.drag = true
match e {
JS.MouseEvent {
state.old_x = f64(e.pageX)
state.old_y = f64(e.pageY)
e.preventDefault()
}
else {}
}
}, JS.EventListenerOptions{})
canvas.addEventListener('mouseup'.str, fn [mut state] (e JS.Event) {
state.drag = false
}, JS.EventListenerOptions{})
canvas.addEventListener('mouseout'.str, fn [mut state] (e JS.Event) {
state.drag = false
}, JS.EventListenerOptions{})
canvas.addEventListener('mousemove'.str, fn [mut state] (e JS.Event) {
if !state.drag {
return
}
match e {
JS.MouseEvent {
state.dx = (f64(e.pageX) - state.old_x) * 2.0 * math.pi / f64(state.canvas.width)
state.dy = (f64(e.pageY) - state.old_y) * 2.0 * math.pi / f64(state.canvas.height)
state.theta += state.dx
state.phi += state.dy
state.old_x = f64(e.pageX)
state.old_y = f64(e.pageY)
e.preventDefault()
}
else {
panic('not a mouse event??')
}
}
}, JS.EventListenerOptions{})
animate(mut state, 0)
}

View File

@ -0,0 +1,4 @@
<body>
<canvas width="570" height="570" id="myCanvas"></canvas>
<script src="cube.js"></script>
</body>

View File

@ -406,6 +406,16 @@ pub fn (a array) to_js_array() JS.Array {
return tmp
}
pub fn (a array) to_number_array() JS.Array {
tmp := JS.Array.prototype.constructor()
for i in 0 .. a.len {
elem := a.arr.get(i)
_ := elem
#tmp.push(Number(elem.valueOf()));
}
return tmp
}
type EveryFn = fn (JS.Number, JS.Number) JS.Boolean
type BigEveryFn = fn (JS.BigInt, JS.Number) JS.Boolean

View File

@ -576,6 +576,8 @@ pub fn on_device_orientation(cb fn (win JS.Window, ev JS.DeviceOrientationEvent)
return clos
}
pub type AnimationFrameCallback = fn (JS.Number)
pub interface JS.Window {
JS.EventTarget
closed JS.Boolean
@ -610,6 +612,7 @@ pub interface JS.Window {
scroll(x JS.Number, y JS.Number)
scrollBy(x JS.Number, y JS.Number)
scrollTo(x JS.Number, y JS.Number)
requestAnimationFrame(callback AnimationFrameCallback)
mut:
name string
opener JS.Any
@ -735,8 +738,109 @@ pub interface JS.WebGLRenderingContext {
bufferData(target JS.Number, data JS.TypedArray, usage JS.Number)
shaderSource(shader JS.WebGLShader, source JS.String)
getShaderParameter(shader JS.WebGLShader, pname JS.Number) JS.Any
vertexAttribPointer(index JS.Number, size JS.Number, typ JS.Number, normalized JS.Boolean, stride JS.Number, offset JS.Number)
getAttribLocation(program JS.WebGLProgram, name JS.String) JS.Number
useProgram(program JS.WebGLProgram)
getUniformLocation(program JS.WebGLProgram, name JS.String) ?JS.WebGLUniformLocation
uniformMatrix2fv(location JS.WebGLUniformLocation, transpose JS.Boolean, value JS.Array)
uniformMatrix3fv(location JS.WebGLUniformLocation, transpose JS.Boolean, value JS.Array)
uniformMatrix4fv(location JS.WebGLUniformLocation, transpose JS.Boolean, value JS.Array)
getProgramInfoLog(program JS.WebGLProgram) JS.String
getShaderInfoLog(shader JS.WebGLShader) JS.String
viewport(x JS.Number, y JS.Number, width JS.Number, height JS.Number)
}
pub interface JS.WebGL2RenderingContext {
JS.WebGLRenderingContext
}
pub fn gl_vertex_shader() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.VERTEX_SHADER;
return num
}
pub fn gl_fragment_shader() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.FRAGMENT_SHADER;
return num
}
pub fn gl_element_array_buffer() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.ELEMENT_ARRAY_BUFFER;
return num
}
pub fn gl_array_buffer() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.ARRAY_BUFFER;
return num
}
pub fn gl_color_buffer_bit() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.COLOR_BUFFER_BIT;
return num
}
pub fn gl_depth_buffer_bit() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.COLOR_BUFFER_BIT;
return num
}
pub fn gl_triangles() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.TRIANGLES;
return num
}
pub fn gl_unsigned_short() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.UNSIGNED_SHORT;
return num
}
pub fn gl_static_draw() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.STATIC_DRAW;
return num
}
pub fn gl_link_status() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.LINK_STATUS;
return num
}
pub fn gl_compile_status() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.COMPILE_STATUS;
return num
}
pub fn gl_float() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.FLOAT;
return num
}
pub fn gl_depth_test() JS.Number {
mut num := JS.Number{}
#num = WebGLRenderingContext.DEPTH_TEST;
return num
}

View File

@ -3184,7 +3184,15 @@ fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) {
}
g.writeln('return s; })()')
} else {
g.write("\"$text\"")
g.write('"')
for char in text {
if char == `\n` {
g.write('\\n')
} else {
g.write('$char.ascii_str()')
}
}
g.write('"')
}
if true || should_cast {
g.write(')')