You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
15 KiB
362 lines
15 KiB
;; Windowing and OpenGL access are provided by SDL
|
|
(import "SDL.cake"
|
|
&defs-only "Licenses.cake")
|
|
|
|
(c-import "stdio.h"
|
|
"SDL_syswm.h"
|
|
;; Use galogen-generated header. See (generate-gl-header)
|
|
&with-decls "gl46.h" "<stdbool.h>") ;; "GL/gl.h"
|
|
|
|
(register-module-license "Galogen" s-apache-license-string)
|
|
|
|
(def-type-alias-global gl-id (unsigned int))
|
|
|
|
(defun opengl-shader-was-compiled-sucessfully
|
|
(shader gl-id
|
|
helpful-name (addr (const char))
|
|
&return bool)
|
|
(var success int)
|
|
(var output-buffer (array 512 char))
|
|
(glGetShaderiv shader GL_COMPILE_STATUS (addr success))
|
|
(unless success
|
|
(glGetShaderInfoLog shader (sizeof output-buffer) null output-buffer)
|
|
(fprintf stderr "error: Shader '%s' compilation failed:\n%s\n"
|
|
helpful-name output-buffer)
|
|
(return false))
|
|
(return true))
|
|
|
|
(defun opengl-program-was-linked-sucessfully
|
|
(program gl-id
|
|
helpful-name (addr (const char))
|
|
&return bool)
|
|
(var success int)
|
|
(var output-buffer (array 512 char))
|
|
(glGetProgramiv program GL_LINK_STATUS (addr success))
|
|
(unless success
|
|
(glGetProgramInfoLog program (sizeof output-buffer) null output-buffer)
|
|
(fprintf stderr "error: Program '%s' link failed:\n%s\n"
|
|
helpful-name output-buffer)
|
|
(return false))
|
|
(return true))
|
|
|
|
(defun opengl-compile-link-shaders
|
|
(program-id-out (addr gl-id)
|
|
vertex-shader-code (addr (const char))
|
|
fragment-shader-code (addr (const char))
|
|
helpful-name (addr (const char))
|
|
&return bool)
|
|
(set (deref program-id-out) (glCreateProgram))
|
|
(var vertex-shader gl-id (glCreateShader GL_VERTEX_SHADER))
|
|
(defer (glDeleteShader vertex-shader))
|
|
(scope ;; Vertex shader
|
|
(glShaderSource vertex-shader 1 (addr vertex-shader-code) null)
|
|
(glCompileShader vertex-shader)
|
|
(unless (opengl-shader-was-compiled-sucessfully vertex-shader helpful-name)
|
|
(return false)))
|
|
(var fragment-shader gl-id (glCreateShader GL_FRAGMENT_SHADER))
|
|
(defer (glDeleteShader fragment-shader))
|
|
(scope ;; Fragment shader
|
|
(glShaderSource fragment-shader 1 (addr fragment-shader-code) null)
|
|
(glCompileShader fragment-shader)
|
|
(unless (opengl-shader-was-compiled-sucessfully fragment-shader helpful-name)
|
|
(return false)))
|
|
(scope ;; Link vertex and fragment shader
|
|
(glAttachShader (deref program-id-out) vertex-shader)
|
|
(glAttachShader (deref program-id-out) fragment-shader)
|
|
(glLinkProgram (deref program-id-out))
|
|
(unless (opengl-program-was-linked-sucessfully (deref program-id-out)
|
|
helpful-name)
|
|
(return false)))
|
|
(return true))
|
|
|
|
(comptime-cond
|
|
('auto-test
|
|
(defun test--opengl (&return int)
|
|
(var window (addr SDL_Window) null)
|
|
(unless (sdl-initialize-for-3d (addr window) "OpenGL" 640 480) (return 1))
|
|
(SDL_GL_SetSwapInterval 1) ;; Enable vsync
|
|
|
|
;; TODO: Support resizing with window resize callback
|
|
(glViewport 0 0 640 480)
|
|
|
|
(var shader-program gl-id (glCreateProgram))
|
|
(scope ;; Prepare program
|
|
(var vertex-shader gl-id (glCreateShader GL_VERTEX_SHADER))
|
|
(scope ;; Vertex shader
|
|
;; TODO: Generate this via a new Cakelisp language/dialect feature?
|
|
(var vertex-shader-code (addr (const char))
|
|
#"##version 460 core
|
|
layout (location = 0) in vec3 position;
|
|
layout (location = 1) in vec2 textureCoord;
|
|
|
|
out vec2 TexCoord;
|
|
void main()
|
|
{
|
|
gl_Position = vec4(position.x, position.y, position.z, 1.0);
|
|
TexCoord = textureCoord;
|
|
}#"#)
|
|
(glShaderSource vertex-shader 1 (addr vertex-shader-code) null)
|
|
(glCompileShader vertex-shader)
|
|
(unless (opengl-shader-was-compiled-sucessfully vertex-shader "Test vertex shader")
|
|
(sdl-shutdown window)
|
|
(return 1)))
|
|
|
|
(var fragment-shader gl-id (glCreateShader GL_FRAGMENT_SHADER))
|
|
(scope ;; Fragment shader
|
|
;; TODO: Generate this via a new Cakelisp language/dialect feature?
|
|
(var fragment-shader-code (addr (const char))
|
|
#"##version 460 core
|
|
out vec4 FragmentColor;
|
|
in vec2 TexCoord;
|
|
uniform sampler2D textureSampler;
|
|
void main()
|
|
{
|
|
FragmentColor = texture(textureSampler, TexCoord);
|
|
}#"#)
|
|
(glShaderSource fragment-shader 1 (addr fragment-shader-code) null)
|
|
(glCompileShader fragment-shader)
|
|
(unless (opengl-shader-was-compiled-sucessfully fragment-shader "Test fragment shader")
|
|
(sdl-shutdown window)
|
|
(return 1)))
|
|
|
|
(scope ;; Link vertex and fragment shader
|
|
(glAttachShader shader-program vertex-shader)
|
|
(glAttachShader shader-program fragment-shader)
|
|
(glLinkProgram shader-program)
|
|
(unless (opengl-program-was-linked-sucessfully shader-program "Test shader")
|
|
(sdl-shutdown window)
|
|
(return 1)))
|
|
|
|
(scope ;; Clean up shaders
|
|
(glDeleteShader vertex-shader)
|
|
(glDeleteShader fragment-shader)))
|
|
|
|
(var mesh-array-object gl-id)
|
|
(var texture gl-id)
|
|
(glGenVertexArrays 1 (addr mesh-array-object))
|
|
(var start-indices int 0)
|
|
(var num-indices int 0)
|
|
(scope
|
|
(defstruct vertex-data
|
|
;; Position
|
|
x float
|
|
y float
|
|
z float
|
|
;; Texture
|
|
s float
|
|
t float)
|
|
(var-static vertices (array vertex-data)
|
|
(array
|
|
(array 0.25f 0.25f 0.25f 0.f 0.f)
|
|
(array 0.75f 0.25f 0.25f 1.f 0.f)
|
|
(array 0.75f 0.75f 0.25f 1.f 1.f)
|
|
(array 0.25f 0.75f 0.25f 0.f 1.f)))
|
|
(var-static indices (array int)
|
|
(array 0 1 3
|
|
1 2 3))
|
|
(set num-indices (array-size indices))
|
|
|
|
;; From this point on, configuration will be saved in the vertex array object
|
|
(glBindVertexArray mesh-array-object)
|
|
|
|
;; Make our triangle vertex array
|
|
(var vertex-buffer-id gl-id)
|
|
(glGenBuffers 1 (addr vertex-buffer-id))
|
|
(glBindBuffer GL_ARRAY_BUFFER vertex-buffer-id)
|
|
(glBufferData GL_ARRAY_BUFFER (sizeof vertices) vertices GL_STATIC_DRAW)
|
|
|
|
(var layout-location int 0) ;; Should match vertex shader layout
|
|
(glVertexAttribPointer layout-location 3 GL_FLOAT
|
|
GL_FALSE ;; Normalize?
|
|
;; Stride
|
|
(sizeof (type vertex-data))
|
|
;; Buffer start offset
|
|
(type-cast 0 (addr void)))
|
|
(glEnableVertexAttribArray layout-location)
|
|
|
|
;; Make our index buffer array
|
|
(var index-buffer-id gl-id)
|
|
(glGenBuffers 1 (addr index-buffer-id))
|
|
(glBindBuffer GL_ELEMENT_ARRAY_BUFFER index-buffer-id)
|
|
(glBufferData GL_ELEMENT_ARRAY_BUFFER (sizeof indices) indices GL_STATIC_DRAW)
|
|
|
|
;; Textures
|
|
(glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_S GL_REPEAT)
|
|
(glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_T GL_REPEAT)
|
|
(glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_NEAREST_MIPMAP_NEAREST)
|
|
(glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER GL_NEAREST)
|
|
;; Generate a texture
|
|
(var width (const int) 256)
|
|
(var height (const int) 256)
|
|
(var checker-image (array (* 256 256 3) (unsigned char)) (array 0))
|
|
(each-in-range height y
|
|
(each-in-range width x
|
|
(set (at (+ (* y width 3) (* 3 x) 0) checker-image) (? (or (= 0 (mod (/ x 64) 2))
|
|
(= 0 (mod (/ y 64) 2)))
|
|
255 0))
|
|
(set (at (+ (* y width 3) (* 3 x) 1) checker-image) 163)
|
|
(set (at (+ (* y width 3) (* 3 x) 2) checker-image) 0)))
|
|
(glGenTextures 1 (addr texture))
|
|
(glBindTexture GL_TEXTURE_2D texture)
|
|
(glTexImage2D GL_TEXTURE_2D
|
|
0 ;; Num mips we are providing
|
|
GL_RGB ;; Destination format
|
|
width
|
|
height
|
|
0 ;; Legacy
|
|
GL_RGB ;; Source format
|
|
GL_UNSIGNED_BYTE
|
|
checker-image)
|
|
(glGenerateMipmap GL_TEXTURE_2D)
|
|
|
|
(var texture-coord-layout-location int 1) ;; Should match vertex shader layout
|
|
(glVertexAttribPointer texture-coord-layout-location
|
|
2 ;; Two texture coordinates
|
|
GL_FLOAT
|
|
GL_FALSE ;; Normalized?
|
|
(sizeof (type vertex-data)) ;; stride
|
|
;; Buffer start offset
|
|
(type-cast (offsetof (type vertex-data) s) (addr void)))
|
|
(glEnableVertexAttribArray texture-coord-layout-location))
|
|
|
|
(var exit-reason (addr (const char)) null)
|
|
(while (not exit-reason)
|
|
(var event SDL_Event)
|
|
(while (SDL_PollEvent (addr event))
|
|
(when (= (field event type) SDL_QUIT)
|
|
(set exit-reason "Window event"))
|
|
(when (and (= (field event type) SDL_WINDOWEVENT)
|
|
(= (field event window event) SDL_WINDOWEVENT_CLOSE)
|
|
(= (SDL_GetWindowID window) (field event window windowID)))
|
|
(set exit-reason "Window event")))
|
|
(var currentKeyStates (addr (const Uint8)) (SDL_GetKeyboardState null))
|
|
(when (at SDL_SCANCODE_ESCAPE currentKeyStates)
|
|
(set exit-reason "Escape pressed"))
|
|
|
|
(glClearColor 0.2f 0.2f 0.2f 1.f)
|
|
(glClear GL_COLOR_BUFFER_BIT)
|
|
|
|
(glUseProgram shader-program)
|
|
(glBindTexture GL_TEXTURE_2D texture)
|
|
(glBindVertexArray mesh-array-object)
|
|
(glDrawElements GL_TRIANGLES num-indices GL_UNSIGNED_INT
|
|
(type-cast 0 (addr void)))
|
|
(glBindVertexArray 0) ;; Unbind
|
|
|
|
(SDL_GL_SwapWindow window))
|
|
|
|
(when exit-reason
|
|
(fprintf stderr "Exiting. Reason: %s\n" exit-reason))
|
|
|
|
(sdl-shutdown window)
|
|
(return 0))))
|
|
|
|
;;
|
|
;; Building
|
|
;;
|
|
|
|
(comptime-cond
|
|
('Unix
|
|
(add-library-dependency "GL" "dl"))
|
|
('Windows
|
|
(add-static-link-objects "opengl32.lib")))
|
|
|
|
;; Most OpenGL loading libraries were not to my tastes. I decided on galogen
|
|
;; (http://galogen.gpfault.net/) because it's only two C++ files (not python or something else) and
|
|
;; reads directly from the Khronos spec (and I don't have to use my web browser for *****-sake)
|
|
;; See https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library
|
|
(add-dependency-git-submodule clone-galogen "https://github.com/google/galogen" "Dependencies/galogen")
|
|
|
|
(defun-comptime generate-gl-header (manager (ref ModuleManager) module (addr Module) &return bool)
|
|
(var galogen-source-file (addr (const char)) "Dependencies/galogen/galogen.cpp")
|
|
(var galogen-executable (array 512 char))
|
|
(unless (outputFilenameFromSourceFilename
|
|
(call-on c_str (field manager buildOutputDir))
|
|
"galogen"
|
|
;; Add to end of file for type.
|
|
(comptime-cond ('Windows "exe") (true null))
|
|
galogen-executable (sizeof galogen-executable))
|
|
(return false))
|
|
|
|
;; Build galogen (GL header code generator)
|
|
(when (fileIsMoreRecentlyModified galogen-source-file galogen-executable)
|
|
(comptime-cond
|
|
('Unix
|
|
(run-process-sequential-or
|
|
((call-on c_str (field manager environment compileTimeBuildCommand fileToExecute))
|
|
galogen-source-file "Dependencies/galogen/third_party/tinyxml2.cpp"
|
|
"--std=c++11" "-O3"
|
|
"-o" galogen-executable)
|
|
(Log "error: failed to build galogen. This uses the compile-time build command\n")
|
|
(return false))
|
|
(addExecutablePermission galogen-executable))
|
|
('Windows
|
|
(var galogen-output (array 512 char) (array 0))
|
|
(makeExecutableOutputArgument
|
|
galogen-output (sizeof galogen-output)
|
|
galogen-executable
|
|
(call-on c_str (field manager environment compileTimeBuildCommand fileToExecute)))
|
|
(run-process-sequential-or
|
|
((call-on c_str (field manager environment compileTimeBuildCommand fileToExecute))
|
|
galogen-source-file "Dependencies/galogen/third_party/tinyxml2.cpp"
|
|
"/Ox" "/EHsc" galogen-output)
|
|
(Log "error: failed to build galogen. This uses the compile-time build command\n")
|
|
(return false)))))
|
|
|
|
;; Use galogen to generate gl headers/source
|
|
;; TODO Use CURL or something to download the latest version from
|
|
;; https://raw.githubusercontent.com/KhronosGroup/OpenGL-Registry/master/xml/gl.xml
|
|
(var gl-specification (addr (const char)) "Dependencies/galogen/third_party/gl.xml")
|
|
|
|
;; TODO: Generate the version header name to match the version SDL.cake specifies
|
|
(var gl-generated-output-path (array 256 char) (array 0))
|
|
(unless (outputFilenameFromSourceFilename
|
|
(call-on c_str (field manager buildOutputDir))
|
|
"gl46"
|
|
null gl-generated-output-path (sizeof gl-generated-output-path))
|
|
(Log "error: failed to generate gl output filename\n")
|
|
(return false))
|
|
(var gl-generated-source-name (array 256 char) (array 0))
|
|
(PrintfBuffer gl-generated-source-name "%s.c" gl-generated-output-path)
|
|
(when (or (fileIsMoreRecentlyModified galogen-executable
|
|
gl-generated-source-name)
|
|
(fileIsMoreRecentlyModified gl-specification
|
|
gl-generated-source-name))
|
|
(var galogen-executable-path (array 256 char) (array 0))
|
|
(comptime-cond ('Unix (PrintfBuffer galogen-executable-path "./%s" galogen-executable))
|
|
('Windows (PrintfBuffer galogen-executable-path "%s" galogen-executable)))
|
|
(run-process-sequential-or
|
|
(galogen-executable-path
|
|
gl-specification
|
|
"--api" "gl" "--ver" "4.6" "--profile" "core"
|
|
"--filename" gl-generated-output-path)
|
|
(Log "error: failed to generate gl headers via galogen\n")
|
|
(return false)))
|
|
|
|
(scope ;; Add the generated file as a dependency
|
|
;; TODO: This needs to be cleaned up
|
|
(var gl-dependency ModuleDependency (array))
|
|
(set (field gl-dependency type) ModuleDependency_CFile)
|
|
(set (field gl-dependency name) "gl46.c")
|
|
|
|
(scope ;; Use this function as the blame token
|
|
;; TODO: Add __function__ for this
|
|
(var this-definition-name (addr (const char)) "generate-gl-header")
|
|
(var this-definition (addr ObjectDefinition)
|
|
(findObjectDefinition (field manager environment) this-definition-name))
|
|
(unless this-definition
|
|
(Logf "error: failed to find definition of %s to create blame token. Was it
|
|
renamed? Search for %s and replace it with the new name of the function it is defined in\n"
|
|
this-definition-name this-definition-name)
|
|
(return false))
|
|
(set (field gl-dependency blameToken) (path this-definition > definitionInvocation)))
|
|
(call-on push_back (path module > dependencies) gl-dependency))
|
|
|
|
(scope ;; Search paths for new dependency
|
|
;; Make sure Cakelisp can resolve to the file in the cache
|
|
(call-on push_back (path module > cSearchDirectories) (call-on c_str (field manager buildOutputDir)))
|
|
;; Make sure the source file can find its header
|
|
(call-on push_back (path module > cSearchDirectories) "."))
|
|
(return true))
|
|
|
|
(add-compile-time-hook-module pre-build generate-gl-header)
|
|
|