Skip to content

Commit

Permalink
Base framework progress
Browse files Browse the repository at this point in the history
  • Loading branch information
tatjam committed Jul 7, 2022
1 parent ce2a36b commit b07daa3
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 38 deletions.
15 changes: 15 additions & 0 deletions res/shader/fullscreen.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#version 330
out vec4 FragColor;

in vec2 vTex;

uniform sampler2D tex;

void main()
{
vec2 coord = vTex;

coord = vec2(vTex.x, 1.0 - vTex.y);

FragColor = vec4(coord.x, coord.y, 0.0, 1.0);
}
13 changes: 13 additions & 0 deletions res/shader/fullscreen.vs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#version 330
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTex;

out vec2 vTex;

uniform mat4 tform;

void main()
{
vTex = aTex;
gl_Position = tform * vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
49 changes: 49 additions & 0 deletions src/engine/base.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import nimgl/[glfw, opengl]
import graphics/sprite
import graphics/renderer as rnd

var should_quit* = false
var renderer*: Renderer = nil

var update_fnc*: proc(dt: float, w: GLFWWindow) = nil
var render_fnc*: proc() = nil
var quit_fnc*: proc(w: GLFWWindow) = nil

proc launch_game*() =
assert glfwInit()

glfwWindowHint(GLFWContextVersionMajor, 3)
glfwWindowHint(GLFWContextVersionMinor, 3)
glfwWindowHint(GLFWOpenglForwardCompat, GLFW_TRUE) # Used for Mac
glfwWindowHint(GLFWOpenglProfile, GLFW_OPENGL_CORE_PROFILE)
glfwWindowHint(GLFWResizable, GLFW_FALSE)

let w: GLFWWindow = glfwCreateWindow(800, 600, "Minijam 110")
if w == nil:
quit(-1)

w.makeContextCurrent()

assert glInit()

renderer = create_renderer("res/shader/fullscreen")

var dt = 0.0
# Launch the main loop
while not should_quit:
glfwPollEvents()

update_fnc(dt, w)

glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)

renderer.render()
if render_fnc != nil: render_fnc()

w.swapBuffers()

if quit_fnc != nil: quit_fnc(w)

w.destroyWindow()
glfwTerminate()
2 changes: 1 addition & 1 deletion src/engine/graphics.nim
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include "graphics/sprite.nim"

17 changes: 17 additions & 0 deletions src/engine/graphics/camera.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# A simple 2D (fullscreen always) camera and transformation functions

import glm

type Camera* = ref object
center: Vec2f
# Scale is pixels / unit, rotation in radians
scale, rotation: float

proc create_camera*(): Camera =
return Camera(center: vec2f(0, 0), scale: 1.0, rotation: 0.0)

# Obtains a transform matrix such that the world is rendered
proc get_transform_matrix*(cam: Camera): Mat4f =
result = result.translate(vec3f(cam.center, 0.0))
.rotate(cam.rotation, 0, 0, 1)
.scale(cam.scale, cam.scale, cam.scale)
44 changes: 44 additions & 0 deletions src/engine/graphics/gl_rectangle.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import nimgl/opengl
import system

# A simple rectangle with UVs
let vertices: array[0..19, float32] = [
# x y z u v
0.0'f32, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 0.0, 1.0, 0.0
]

let indices: array[0..5, int32] = [
0'i32, 1, 3, 1, 2, 3
]

var vao, vbo, ebo: GLuint = 0

proc get_rectangle(): GLuint =
if vao == 0:
glGenVertexArrays(1, addr vao)
glGenBuffers(1, addr vbo)
glGenBuffers(1, addr ebo)

glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, vertices.len * float32.sizeof, unsafeAddr vertices, GL_STATIC_DRAW)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.len * int32.sizeof, unsafeAddr indices, GL_STATIC_DRAW)

# position attribute
glVertexAttribPointer(0'u32, 3'i32, EGL_FLOAT, false, 5 * float32.sizeof, cast[pointer](0))
glEnableVertexAttribArray(0)

glVertexAttribPointeR(1'u32, 2'i32, EGL_FLOAT, false, 5 * float32.sizeof, cast[pointer](3 * float32.sizeof))
glEnableVertexAttribArray(1)

return vao

proc draw_rectangle*() =
glBindVertexArray(get_rectangle())
glDrawElements(GL_TRIANGLES, 6.GLsizei, GL_UNSIGNED_INT, nil)

23 changes: 23 additions & 0 deletions src/engine/graphics/renderer.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Implements generic, layered drawing for drawables, binding
# only the most basic uniforms in the shaders:
#
import camera
import shader
import gl_rectangle
import glm

type Renderer* = ref object
camera*: Camera
fullscreen_shader*: Shader

proc create_renderer*(shader_path: string): Renderer =
let shader = load_shader(shader_path)
let camera = create_camera()
return Renderer(camera: camera, fullscreen_shader: shader)

proc render*(renderer: Renderer) =
renderer.fullscreen_shader.use()
var tform = translate(mat4f(), -1.0, -1.0, 0.0).scale(2.0, 2.0, 2.0)
renderer.fullscreen_shader.set_mat4("tform", tform)
draw_rectangle()

64 changes: 64 additions & 0 deletions src/engine/graphics/shader.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import nimgl/opengl
import glm

type Shader* = distinct GLuint

proc toString(str: openArray[char]): string =
result = newStringOfCap(len(str))
for ch in str:
if ch != '\0': add(result, ch)


proc get_errors(sh: GLuint, for_program: bool): string =
var success: GLint
var infolog: array[1024, char]
if for_program:
glGetProgramiv(sh, GL_LINK_STATUS, addr success)
if success == 0:
glGetShaderInfoLog(sh, 1024, nil, addr infolog[0])
else:
glGetShaderiv(sh, GL_COMPILE_STATUS, addr success)
if success == 0:
glGetShaderInfoLog(sh, 1024, nil, addr infolog[0])

# "Safe" as we have zero padding by OpenGL
if success == 1: result = "OK" else: result = toString(infolog)

# Pass the path without .vs or .fs
proc load_shader*(path: string): Shader =
let vshader = readFile(path & ".vs").cstring
let fshader = readFile(path & ".fs").cstring

var vert, frag: GLuint
vert = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vert, 1.GLsizei, unsafeAddr vshader, nil)
glCompileShader(vert)
var msg = get_errors(vert, false)
echo "Compile vertex " & path & ".vs: " & msg

frag = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(frag, 1.GLsizei, unsafeAddr fshader, nil)
glCompileShader(frag)
msg = get_errors(frag, false)
echo "Compile fragment " & path & ".fs: " & msg

let sh = glCreateProgram()
glAttachShader(sh, vert)
glAttachShader(sh, frag)
glLinkProgram(sh)
msg = get_errors(frag, false)
echo "Link shader " & path & ": " & msg

glDeleteShader(vert)
glDeleteShader(frag)

return cast[Shader](sh)

proc gl(shader: Shader) : GLuint =
return cast[Gluint](shader)

proc use*(shader: Shader) =
glUseProgram(shader.gl)

proc set_mat4*(shader: Shader, name: cstring, mat: var Mat4f) =
glUniformMatrix4fv(glGetUniformLocation(shader.gl, name), 1.GLsizei, false, mat.caddr)
8 changes: 2 additions & 6 deletions src/engine/graphics/sprite.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import stb_image/read as stbi
import nimgl/opengl
import glm


# An sprite allows displaying of an image, optionally clipped and colored
type Sprite* = ref object
texture_id: GLuint
Expand All @@ -12,7 +13,6 @@ type Sprite* = ref object
color*: Vec4f



proc create_sprite*(image: string): Sprite =
var width, height, nCh : int
var data = stbi.load(image, width, height, nCh, 4)
Expand All @@ -27,17 +27,13 @@ proc create_sprite*(image: string): Sprite =
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST.GLint)

# The OpenGL bindings are a bit annoying with the types of enums!
glTexImage2D(GL_TEXTURE_2D, 0.GLint, GL_RGBA.GLint, width.GLsizei,
glTexImage2D(GL_TEXTURE_2D, 0'i32, GL_RGBA.GLint, width.GLsizei,
height.GLsizei, 0.GLint, GL_RGBA, GL_UNSIGNED_BYTE, addr data)

return Sprite(texture_id: tex, texture_width: width, texture_height: height,
clip: vec4f(0, 0, 1, 1), color: vec4f(1.0, 1.0, 1.0, 1.0))



proc draw(spr: Sprite, pos: Vec2f) =
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, spr.texture_id)


# An animated sprite uses sprite to display an image that has animation
37 changes: 6 additions & 31 deletions src/main.nim
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
import nimgl/[glfw, opengl]

import "engine/graphics/sprite"
import "engine/base.nim"


proc keyProc(window: GLFWWindow, key: int32, scancode: int32,
action: int32, mods: int32): void {.cdecl.} =
if key == GLFWKey.ESCAPE and action == GLFWPress:
window.setWindowShouldClose(true)
proc update(dt: float, w: GLFWWindow) =
if w.windowShouldClose:
should_quit = true

proc main() =
assert glfwInit()

glfwWindowHint(GLFWContextVersionMajor, 3)
glfwWindowHint(GLFWContextVersionMinor, 3)
glfwWindowHint(GLFWOpenglForwardCompat, GLFW_TRUE) # Used for Mac
glfwWindowHint(GLFWOpenglProfile, GLFW_OPENGL_CORE_PROFILE)
glfwWindowHint(GLFWResizable, GLFW_FALSE)
update_fnc = update

let w: GLFWWindow = glfwCreateWindow(800, 600, "NimGL")
if w == nil:
quit(-1)

discard w.setKeyCallback(keyProc)
w.makeContextCurrent()

assert glInit()

while not w.windowShouldClose:
glfwPollEvents()
glClearColor(0.68f, 1f, 0.34f, 1f)
glClear(GL_COLOR_BUFFER_BIT)
w.swapBuffers()

w.destroyWindow()
glfwTerminate()

main()
launch_game()

0 comments on commit b07daa3

Please sign in to comment.