1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/vlib/gg/gg.v

542 lines
13 KiB
V
Raw Normal View History

2020-01-23 23:04:46 +03:00
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
2019-06-23 05:21:30 +03:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
2019-06-22 21:20:28 +03:00
module gg
import (
stbi
glm
gl
gx
os
glfw
)
2019-06-22 21:20:28 +03:00
pub struct Vec2 {
2019-08-23 00:00:31 +03:00
pub:
2019-06-22 21:20:28 +03:00
x int
y int
}
pub fn (v Vec2) str() string {
return 'Vec2{ x: $v.x y: $v.y }'
}
2019-06-22 21:20:28 +03:00
pub fn vec2(x, y int) Vec2 {
res := Vec2 {
2019-08-23 00:00:31 +03:00
x: x
y: y
2019-06-22 21:20:28 +03:00
}
return res
}
pub fn init_gg() {
glfw.init_glfw()
2019-06-22 21:20:28 +03:00
gl.init_glad()
}
pub struct Cfg {
2019-08-23 00:00:31 +03:00
pub:
2019-06-22 21:20:28 +03:00
width int
height int
2019-08-23 00:00:31 +03:00
use_ortho bool
2019-06-22 21:20:28 +03:00
retina bool
2019-10-23 12:17:08 +03:00
resizable bool
2019-10-22 23:06:56 +03:00
2019-06-22 21:20:28 +03:00
font_size int
font_path string
2019-08-23 00:00:31 +03:00
create_window bool
window_user_ptr voidptr
window_title string
always_on_top bool
scale int
2019-06-22 21:20:28 +03:00
}
pub struct GG {
2019-06-22 21:20:28 +03:00
shader gl.Shader
// use_ortho bool
2019-08-04 10:46:49 +03:00
vao u32
2019-06-22 21:20:28 +03:00
rect_vao u32
rect_vbo u32
line_vao u32
line_vbo u32
2019-08-04 10:46:49 +03:00
vbo u32
2019-06-22 21:20:28 +03:00
scale int // retina = 2 , normal = 1
2019-11-11 17:18:32 +03:00
//pub:
2019-08-23 00:00:31 +03:00
pub mut:
2019-11-11 17:18:32 +03:00
width int
height int
window &glfw.Window
2019-08-23 00:00:31 +03:00
render_fn fn()
2019-06-22 21:20:28 +03:00
}
2019-06-22 21:20:28 +03:00
// fn new_context(width, height int, use_ortho bool, font_size int) *GG {
pub fn new_context(cfg Cfg) &GG {
2019-12-04 13:08:28 +03:00
mut window := &glfw.Window(0)
if cfg.create_window {
2019-10-23 12:17:08 +03:00
if cfg.resizable {
glfw.window_hint(C.GLFW_RESIZABLE, 1)
} else {
glfw.window_hint(C.GLFW_RESIZABLE, 0)
2019-12-04 13:08:28 +03:00
}
window = glfw.create_window(glfw.WinCfg{
2019-08-23 00:00:31 +03:00
title: cfg.window_title
width: cfg.width
height: cfg.height
ptr: cfg.window_user_ptr
always_on_top: cfg.always_on_top
})
window.make_context_current()
init_gg()
2019-08-23 00:00:31 +03:00
}
2019-06-22 21:20:28 +03:00
shader := gl.new_shader('simple')
shader.use()
2019-08-23 00:00:31 +03:00
if cfg.use_ortho {
2019-06-22 21:20:28 +03:00
projection := glm.ortho(0, cfg.width, cfg.height, 0)
shader.set_mat4('projection', projection)
}
else {
2019-09-14 23:54:14 +03:00
// TODO move to function (allow volt functions to return arrays without allocations)
2019-06-22 21:20:28 +03:00
// i := glm.identity3()
shader.set_mat4('projection', glm.identity())
}
2019-07-15 23:44:26 +03:00
vao := gl.gen_vertex_array()
//println('new gg context VAO=$VAO')
2019-07-15 23:44:26 +03:00
vbo := gl.gen_buffer()
2019-06-22 21:20:28 +03:00
mut scale := 1
if cfg.retina {
scale = 2
}
2020-02-02 04:54:41 +03:00
gl.enable(C.GL_SCISSOR_TEST)
2019-07-12 07:42:34 +03:00
//gl.enable(GL_BLEND)
//# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//println('new gg text context VAO=$VAO')
2019-07-12 07:42:34 +03:00
//gl.bind_vao(VAO)
//gl.bind_buffer(GL_ARRAY_BUFFER, VBO)
//gl.enable_vertex_attrib_array(0)
//gl.vertex_attrib_pointer(0, 4, GL_FLOAT, false, 4, 0)
2019-08-23 00:00:31 +03:00
todo_remove_me(cfg, scale)
return &GG {
2019-08-23 00:00:31 +03:00
shader: shader
width: cfg.width
height: cfg.height
vao: vao
vbo: vbo
window: window
2019-10-22 23:06:56 +03:00
2019-06-22 21:20:28 +03:00
// /line_vao: gl.gen_vertex_array()
// /line_vbo: gl.gen_buffer()
//text_ctx: new_context_text(cfg, scale),
2019-06-22 21:20:28 +03:00
scale: scale
// use_ortho: use_ortho
}
2019-06-22 21:20:28 +03:00
// ctx.init_rect_vao()
//return ctx
2019-06-22 21:20:28 +03:00
}
2019-08-23 00:00:31 +03:00
/*
pub fn (gg &GG) render_loop() bool {
2019-08-23 00:00:31 +03:00
for !gg.window.show_close() {
gg.render_fn()
gg.window.swap_buffers()
glfw.wait_events()
2019-08-23 00:00:31 +03:00
}
}
*/
2019-08-23 00:00:31 +03:00
pub fn clear(color gx.Color) {
2019-10-22 23:06:56 +03:00
gl.clear_color(color.r, color.g, color.b, 255)
gl.clear()
2019-08-23 00:00:31 +03:00
}
2019-08-23 00:00:31 +03:00
pub fn (gg &GG) render() {
gg.window.swap_buffers()
glfw.wait_events()
2019-08-23 00:00:31 +03:00
}
2019-06-25 22:36:44 +03:00
pub fn (ctx &GG) draw_triangle(x1, y1, x2, y2, x3, y3 f32, c gx.Color) {
2019-06-22 21:20:28 +03:00
// println('draw_triangle $x1,$y1 $x2,$y2 $x3,$y3')
ctx.shader.use()
ctx.shader.set_color('color', c)
vertices := [
x1, y1, 0,
x2, y2, 0,
x3, y3, 0,
] !
// bind the Vertex Array Object first, then bind and set vertex buffer(s),
// and then configure vertex attributes(s).
2019-08-04 10:46:49 +03:00
gl.bind_vao(ctx.vao)
2019-08-23 00:00:31 +03:00
gl.set_vbo(ctx.vbo, vertices, C.GL_STATIC_DRAW)
gl.vertex_attrib_pointer(0, 3, C.GL_FLOAT, false, 3, 0)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(0)
// gl.bind_buffer(GL_ARRAY_BUFFER, uint(0))
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO,
// but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs
// (nor VBOs) when it's not directly necessary.
// gl.bind_vertex_array(uint(0))
// gl.bind_vertex_array(ctx.VAO)
2019-08-23 00:00:31 +03:00
gl.draw_arrays(C.GL_TRIANGLES, 0, 3)
2019-06-22 21:20:28 +03:00
}
2019-06-25 22:36:44 +03:00
pub fn (ctx &GG) draw_triangle_tex(x1, y1, x2, y2, x3, y3 f32, c gx.Color) {
2019-06-22 21:20:28 +03:00
ctx.shader.use()
ctx.shader.set_color('color', c)
ctx.shader.set_int('has_texture', 1)
vertices := [
x1, y1, 0, 0, 0, 0, 1, 1,
x2, y2, 0, 0, 0, 0, 1, 0,
x3, y3, 0, 0, 0, 0, 0, 0,
] !
2019-08-04 10:46:49 +03:00
gl.bind_vao(ctx.vao)
2019-08-23 00:00:31 +03:00
gl.set_vbo(ctx.vbo, vertices, C.GL_STATIC_DRAW)
2019-06-22 21:20:28 +03:00
// position attribute
2019-08-23 00:00:31 +03:00
gl.vertex_attrib_pointer(0, 3, C.GL_FLOAT, false, 3, 0)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(0)
// color attribute
2019-08-23 00:00:31 +03:00
gl.vertex_attrib_pointer(1, 3, C.GL_FLOAT, false, 8, 3)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(1)
// texture attribute
2019-08-23 00:00:31 +03:00
gl.vertex_attrib_pointer(2, 2, C.GL_FLOAT, false, 8, 6)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(2)
// /
// gl.draw_arrays(GL_TRIANGLES, 0, 3)
2019-08-23 00:00:31 +03:00
gl.draw_elements(C.GL_TRIANGLES, 6, C.GL_UNSIGNED_INT, 0)
2019-06-22 21:20:28 +03:00
}
2019-06-26 18:49:50 +03:00
pub fn (ctx &GG) draw_rect(x, y, w, h f32, c gx.Color) {
2019-06-22 21:20:28 +03:00
// println('gg.draw_rect($x,$y,$w,$h)')
// wrong order
// // ctx.draw_triangle(x, y, x + w, y, x + w, y + h, c)
// // ctx.draw_triangle(x, y, x, y + h, x + w, y + h, c)
2019-09-14 23:54:14 +03:00
// good order. counter clockwise
2019-06-22 21:20:28 +03:00
// ctx.draw_triangle(x, y, x, y + h, x + w, y + h, c)
// ctx.draw_triangle(x, y, x + w, y + h, x + w, y, c)
ctx.draw_rect2(x, y, w, h, c)
}
2020-01-30 03:26:45 +03:00
// Useful for debugging meshes.
pub fn set_mode_wireframe() {
C.glPolygonMode(C.GL_FRONT_AND_BACK, C.GL_LINE)
}
pub fn set_mode_point() {
C.glPolygonMode(C.GL_FRONT_AND_BACK, C.GL_POINT)
}
pub fn set_mode_fill() {
C.glPolygonMode(C.GL_FRONT_AND_BACK, C.GL_FILL)
}
2019-08-23 00:00:31 +03:00
/*
2019-06-22 21:20:28 +03:00
fn (ctx mut GG) init_rect_vao() {
2019-08-23 00:00:31 +03:00
2019-06-22 21:20:28 +03:00
ctx.rect_vao = gl.gen_vertex_array()
ctx.rect_vbo = gl.gen_buffer()
vertices := [
x + w, y, 0,
x + w, y + h, 0,
x, y + h, 0,
x, y, 0,
] !
indices := [
0, 1, 3,// first triangle
1, 2, 3// second triangle
] !
gl.bind_vao(ctx.rect_vao)
2019-08-23 00:00:31 +03:00
gl.set_vbo(ctx.rect_vbo, vertices, C.GL_STATIC_DRAW)
2019-06-22 21:20:28 +03:00
ebo := gl.gen_buffer()
// ///////
2019-08-23 00:00:31 +03:00
gl.set_ebo(ebo, indices, C.GL_STATIC_DRAW)
}
2019-06-22 21:20:28 +03:00
*/
2019-06-26 18:49:50 +03:00
pub fn (ctx &GG) draw_rect2(x, y, w, h f32, c gx.Color) {
2019-08-04 10:46:49 +03:00
C.glDeleteBuffers(1, &ctx.vao)
C.glDeleteBuffers(1, &ctx.vbo)
2019-06-22 21:20:28 +03:00
ctx.shader.use()
ctx.shader.set_color('color', c)
ctx.shader.set_int('has_texture', 0)
// 4--1
// 3--2
2019-08-23 00:00:31 +03:00
$if linux {
2019-06-22 21:20:28 +03:00
// y += h
2019-08-23 00:00:31 +03:00
}
2019-06-22 21:20:28 +03:00
vertices := [
x + w, y, 0,
x + w, y + h, 0,
x, y + h, 0,
x, y, 0,
] !
indices := [
0, 1, 3,// first triangle
1, 2, 3// second triangle
] !
2019-08-04 10:46:49 +03:00
gl.bind_vao(ctx.vao)
2019-08-23 00:00:31 +03:00
gl.set_vbo(ctx.vbo, vertices, C.GL_STATIC_DRAW)
2019-06-22 21:20:28 +03:00
ebo := gl.gen_buffer()
// ///////
2019-08-23 00:00:31 +03:00
gl.set_ebo(ebo, indices, C.GL_STATIC_DRAW)// !!! LEAKS
2019-06-22 21:20:28 +03:00
// /////
2019-08-23 00:00:31 +03:00
gl.vertex_attrib_pointer(0, 3, C.GL_FLOAT, false, 3, 0)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(0)
// gl.bind_vao(ctx.rect_vao)
2019-08-04 10:46:49 +03:00
gl.bind_vao(ctx.vao)
2019-08-23 00:00:31 +03:00
gl.draw_elements(C.GL_TRIANGLES, 6, C.GL_UNSIGNED_INT, 0)
2019-06-22 21:20:28 +03:00
C.glDeleteBuffers(1, &ebo)
}
2019-08-23 00:00:31 +03:00
fn todo_remove_me(cfg Cfg, scale int) {
2019-06-22 21:20:28 +03:00
// Can only have text in ortho mode
if !cfg.use_ortho {
return
2019-06-22 21:20:28 +03:00
}
width := cfg.width * scale
height := cfg.height * scale
//font_size := cfg.font_size * scale
2019-08-23 00:00:31 +03:00
gl.enable(C.GL_BLEND)
//# glBlendFunc(C.GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2019-06-22 21:20:28 +03:00
shader := gl.new_shader('text')
shader.use()
projection := glm.ortho(0, width, 0, height)// 0 at BOT
// projection_new := ortho(0, width, 0, height)// 0 at BOT
// projection := gl.ortho(0, width,height,0) // 0 at TOP
shader.set_mat4('projection', projection)
2019-07-15 23:44:26 +03:00
vao := gl.gen_vertex_array()
//println('new gg text context VAO=$VAO')
2019-07-15 23:44:26 +03:00
vbo := gl.gen_buffer()
gl.bind_vao(vao)
2019-08-23 00:00:31 +03:00
gl.bind_buffer(C.GL_ARRAY_BUFFER, vbo)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(0)
2019-08-23 00:00:31 +03:00
gl.vertex_attrib_pointer(0, 4, C.GL_FLOAT, false, 4, 0)
2019-06-22 21:20:28 +03:00
}
fn update() {
// # ui__post_empty_event();
}
2019-08-23 00:00:31 +03:00
pub fn post_empty_event() {
glfw.post_empty_event()
}
2019-06-22 21:20:28 +03:00
pub fn (c GG) circle(x, y, r int) {
}
fn (c GG) fill_color(color gx.Color) {
}
fn (c GG) fill() {
}
fn (c GG) move_to(x, y int) {
}
fn (c GG) line_to(x, y int) {
}
fn (c GG) stroke_width(size int) {
}
fn (c GG) stroke_color(color gx.Color) {
}
fn (c GG) stroke() {
}
fn (c GG) save() {
}
fn (c GG) restore() {
}
fn (c GG) intersect_scissor(x, y, w, h int) {
}
fn (c GG) translate(x, y int) {
}
fn (c GG) create_font(name, file string) int {
return 0
}
fn (c GG) text(x, y int, text string) {
}
fn (c GG) text_box(x, y, max int, text string) {
}
fn (c GG) font_face(f string) {
}
fn (c GG) font_size(size int) {
}
fn (c GG) text_align(a int) {
}
pub fn (ctx &GG) create_image(file string) u32 {
return create_image(file)
}
2019-06-22 21:20:28 +03:00
pub fn create_image(file string) u32 {
2020-01-12 21:54:40 +03:00
//println('gg create image "$file"')
2019-06-22 21:20:28 +03:00
if file.contains('twitch') {
return u32(0)// TODO
}
if !os.exists(file) {
2019-06-22 21:20:28 +03:00
println('gg create image no such file "$file"')
return u32(0)
}
texture := gl.gen_texture()
img := stbi.load(file)
gl.bind_2d_texture(texture)
img.tex_image_2d()
2019-08-23 00:00:31 +03:00
gl.generate_mipmap(C.GL_TEXTURE_2D)
2019-06-22 21:20:28 +03:00
img.free()
return texture
}
pub fn create_image_from_memory(buf byteptr) u32 {
texture := gl.gen_texture()
img := stbi.load_from_memory(buf)
// TODO copy pasta
gl.bind_2d_texture(texture)
img.tex_image_2d()
gl.generate_mipmap(C.GL_TEXTURE_2D)
img.free()
2019-06-22 21:20:28 +03:00
return texture
}
pub fn (ctx &GG) draw_line(x, y, x2, y2 f32, color gx.Color) {
2020-01-31 05:43:46 +03:00
ctx.use_color_shader(color)
vertices := [x, y, x2, y2] !
2020-01-31 05:43:46 +03:00
ctx.bind_vertices(vertices)
2019-08-23 00:00:31 +03:00
gl.draw_arrays(C.GL_LINES, 0, 2)
2019-06-22 21:20:28 +03:00
}
2020-01-27 22:42:32 +03:00
pub fn (ctx &GG) draw_arc(x, y, r, start_angle, end_angle f32, segments int, color gx.Color) {
2020-01-31 05:43:46 +03:00
ctx.use_color_shader(color)
mut vertices := arc_vertices(x, y, r, start_angle, end_angle, segments)
ctx.bind_vertices(vertices)
2020-01-27 22:42:32 +03:00
gl.draw_arrays(C.GL_LINE_STRIP, 0, segments + 1)
}
2020-01-30 03:26:45 +03:00
pub fn (ctx &GG) draw_filled_arc(x, y, r, start_angle, end_angle f32, segments int, color gx.Color) {
2020-01-31 05:43:46 +03:00
ctx.use_color_shader(color)
2020-01-30 03:26:45 +03:00
mut vertices := []f32
vertices << [x, y]
2020-01-31 05:43:46 +03:00
vertices << arc_vertices(x, y, r, start_angle, end_angle, segments)
ctx.bind_vertices(vertices)
2020-01-30 03:26:45 +03:00
gl.draw_arrays(C.GL_TRIANGLE_FAN, 0, segments + 2)
}
pub fn (ctx &GG) draw_circle(x, y, r f32, color gx.Color) {
2020-01-31 05:43:46 +03:00
ctx.draw_filled_arc(x, y, r, 0, 360, 24 + int(r / 2), color)
}
pub fn (ctx &GG) draw_rounded_rect(x, y, w, h, r f32, color gx.Color) {
ctx.use_color_shader(color)
mut vertices := []f32
segments := 6 + int(r / 8)
// Create a rounded rectangle using a triangle fan mesh.
vertices << [x + (w/2.0), y + (h/2.0)]
vertices << arc_vertices(x + w - r, y + h - r, r, 0, 90, segments)
vertices << arc_vertices(x + r, y + h - r, r, 90, 180, segments)
vertices << arc_vertices(x + r, y + r, r, 180, 270, segments)
vertices << arc_vertices(x + w - r, y + r, r, 270, 360, segments)
// Finish the loop by going back to the first vertex
vertices << [vertices[2], vertices[3]]
ctx.bind_vertices(vertices)
gl.draw_arrays(C.GL_TRIANGLE_FAN, 0, segments * 4 + 6)
2020-01-30 03:26:45 +03:00
}
2020-02-02 04:55:29 +03:00
pub fn (ctx &GG) draw_empty_rounded_rect(x, y, w, h, r f32, color gx.Color) {
ctx.use_color_shader(color)
mut vertices := []f32
segments := 6 + int(r / 8)
vertices << arc_vertices(x + w - r, y + h - r, r, 0, 90, segments)
vertices << arc_vertices(x + r, y + h - r, r, 90, 180, segments)
vertices << arc_vertices(x + r, y + r, r, 180, 270, segments)
vertices << arc_vertices(x + w - r, y + r, r, 270, 360, segments)
ctx.bind_vertices(vertices)
gl.draw_arrays(C.GL_LINE_STRIP, 0, segments * 4 + 1)
}
/*
pub fn (c &GG) draw_gray_line(x, y, x2, y2 f32) {
c.draw_line(x, y, x2, y2, gx.Gray)
2019-06-22 21:20:28 +03:00
}
pub fn (c &GG) draw_vertical(x, y, height int) {
c.draw_line(x, y, x, y + height)
}
*/
2019-06-22 21:20:28 +03:00
//ctx.gg.draw_line(center + prev_x, center+prev_y, center + x*10.0, center+y)
2019-06-25 22:36:44 +03:00
// fn (ctx &GG) draw_image(x, y, w, h f32, img stbi.Image) {
pub fn (ctx &GG) draw_image(x, y, w, h f32, tex_id u32) {
// NB: HACK to ensure same state ... TODO: remove next line
ctx.draw_empty_rect(0,0,0,0, gx.white)
last_array_buffer := 0
last_texture := 0
C.glGetIntegerv(C.GL_ARRAY_BUFFER_BINDING, &last_array_buffer)
C.glGetIntegerv(C.GL_TEXTURE_BINDING_2D, &last_texture)
2019-06-22 21:20:28 +03:00
// println('DRAW IMAGE $x $y $w $h $tex_id')
ctx.shader.use()
// ctx.shader.set_color('color', c)
ctx.shader.set_int('has_texture', 1)
// 4--1
// | |
// 3--2
vertices := [
x + w, y, 0, 1, 0, 0, 1, 1,
x + w, y + h, 0, 0, 1, 0, 1, 0,
x, y + h, 0, 0, 0, 1, 0, 0,
x, y, 0, 1, 1, 0, 0, 1,
] !
indices := [
0, 1, 3,// first triangle
1, 2, 3// second triangle
] !
// VAO := gl.gen_vertex_array()
// VBO := gl.gen_buffer()
C.glEnable(C.GL_TEXTURE_2D)
2019-08-04 10:46:49 +03:00
gl.bind_vao(ctx.vao)
2019-08-23 00:00:31 +03:00
gl.set_vbo(ctx.vbo, vertices, C.GL_STATIC_DRAW)
2019-06-22 21:20:28 +03:00
ebo := gl.gen_buffer()
2019-08-23 00:00:31 +03:00
gl.set_ebo(ebo, indices, C.GL_STATIC_DRAW)
gl.vertex_attrib_pointer(0, 3, C.GL_FLOAT, false, 8, 0)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(0)
2019-08-23 00:00:31 +03:00
gl.vertex_attrib_pointer(1, 3, C.GL_FLOAT, false, 8, 3)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(1)
2019-08-23 00:00:31 +03:00
gl.vertex_attrib_pointer(2, 2, C.GL_FLOAT, false, 8, 6)
2019-06-22 21:20:28 +03:00
gl.enable_vertex_attrib_array(2)
gl.bind_2d_texture(tex_id)
2019-08-04 10:46:49 +03:00
gl.bind_vao(ctx.vao)
2019-08-23 00:00:31 +03:00
gl.draw_elements(C.GL_TRIANGLES, 6, C.GL_UNSIGNED_INT, 0)
C.glDisable(C.GL_TEXTURE_2D)
// restore state
C.glBindBuffer(C.GL_ARRAY_BUFFER, last_array_buffer)
C. glBindTexture(C.GL_TEXTURE_2D, last_texture)
2019-06-22 21:20:28 +03:00
}
pub fn (c &GG) draw_empty_rect(x, y, w, h f32, color gx.Color) {
c.draw_line(x, y, x + w, y, color)
c.draw_line(x, y, x, y + h, color)
c.draw_line(x, y + h, x + w, y + h, color)
c.draw_line(x + w, y, x + w, y + h, color)
2019-06-22 21:20:28 +03:00
}
2020-02-02 04:54:41 +03:00
pub fn scissor(x, y, w, h f32) {
C.glScissor(x, y, w, h)
}