|
|
|
;; Windowing and OpenGL access are provided by SDL
|
|
|
|
(import "SDL.cake")
|
|
|
|
|
|
|
|
(c-import "stdio.h"
|
|
|
|
"SDL.h"
|
|
|
|
"SDL_syswm.h"
|
|
|
|
;; Use galogen-generated header. See (generate-gl-header)
|
|
|
|
&with-decls "gl46.h") ;; "GL/gl.h"
|
|
|
|
|
|
|
|
(def-type-alias-global gl-id (unsigned int))
|
|
|
|
|
|
|
|
(defun opengl-shader-was-compiled-sucessfully (shader gl-id &return bool)
|
|
|
|
(var success int)
|
|
|
|
(var output-buffer ([] 512 char))
|
|
|
|
(glGetShaderiv shader GL_COMPILE_STATUS (addr success))
|
|
|
|
(unless success
|
|
|
|
(glGetShaderInfoLog shader (sizeof output-buffer) null output-buffer)
|
|
|
|
(fprintf stderr "error: Shader compilation failed:\n%s\n" output-buffer)
|
|
|
|
(return false))
|
|
|
|
(return true))
|
|
|
|
|
|
|
|
(defun opengl-program-was-linked-sucessfully (program gl-id &return bool)
|
|
|
|
(var success int)
|
|
|
|
(var output-buffer ([] 512 char))
|
|
|
|
(glGetProgramiv program GL_LINK_STATUS (addr success))
|
|
|
|
(unless success
|
|
|
|
(glGetProgramInfoLog program (sizeof output-buffer) null output-buffer)
|
|
|
|
(fprintf stderr "error: Program link failed:\n%s\n" output-buffer)
|
|
|
|
(return false))
|
|
|
|
(return true))
|
|
|
|
|
|
|
|
(comptime-cond
|
|
|
|
('auto-test
|
|
|
|
(defun test--opengl (&return int)
|
|
|
|
(var window (* 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 (* (const char))
|
|
|
|
"#version 460 core\n
|
|
|
|
layout (location = 0) in vec3 position;\n
|
|
|
|
void main()\n
|
|
|
|
{\n
|
|
|
|
gl_Position = vec4(position.x, position.y, position.z, 1.0);\n
|
|
|
|
}")
|
|
|
|
(glShaderSource vertex-shader 1 (addr vertex-shader-code) null)
|
|
|
|
(glCompileShader vertex-shader)
|
|
|
|
(unless (opengl-shader-was-compiled-sucessfully 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 (* (const char))
|
|
|
|
"#version 460 core\n
|
|
|
|
out vec4 FragmentColor;\n
|
|
|
|
void main()\n
|
|
|
|
{\n
|
|
|
|
FragmentColor = vec4(1.f, 0.5f, 0.2f, 1.0f);\n
|
|
|
|
}")
|
|
|
|
(glShaderSource fragment-shader 1 (addr fragment-shader-code) null)
|
|
|
|
(glCompileShader fragment-shader)
|
|
|
|
(unless (opengl-shader-was-compiled-sucessfully 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)
|
|
|
|
(sdl-shutdown window)
|
|
|
|
(return 1)))
|
|
|
|
|
|
|
|
(scope ;; Clean up shaders
|
|
|
|
(glDeleteShader vertex-shader)
|
|
|
|
(glDeleteShader fragment-shader)))
|
|
|
|
|
|
|
|
(var mesh-array-object gl-id)
|
|
|
|
(glGenVertexArrays 1 (addr mesh-array-object))
|
|
|
|
(var start-indices int 0)
|
|
|
|
(var num-indices int 0)
|
|
|
|
(scope
|
|
|
|
(var-static vertices ([] float)
|
|
|
|
(array
|
|
|
|
0.25f 0.25f 0.25f
|
|
|
|
0.75f 0.25f 0.25f
|
|
|
|
0.75f 0.75f 0.25f
|
|
|
|
0.25f 0.75f 0.25f))
|
|
|
|
(var-static indices ([] 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
|
|
|
|
(* 3 (sizeof float))
|
|
|
|
;; Buffer start offset
|
|
|
|
(type-cast 0 (* 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))
|
|
|
|
|
|
|
|
(var exit-reason (* (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")))
|
|
|
|
(var currentKeyStates (* (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)
|
|
|
|
(glBindVertexArray mesh-array-object)
|
|
|
|
(glDrawElements GL_TRIANGLES num-indices GL_UNSIGNED_INT
|
|
|
|
(type-cast 0 (* 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-linker-options "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 (& ModuleManager) module (* Module) &return bool)
|
|
|
|
(var galogen-source-file (* (const char)) "Dependencies/galogen/galogen.cpp")
|
|
|
|
(var galogen-executable (* (const char))
|
|
|
|
(comptime-cond ('Unix "galogen") ('Windows "galogen.exe")))
|
|
|
|
|
|
|
|
;; 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
|
|
|
|
(run-process-sequential-or
|
|
|
|
((call-on c_str (field manager environment compileTimeBuildCommand fileToExecute))
|
|
|
|
galogen-source-file "Dependencies/galogen/third_party/tinyxml2.cpp"
|
|
|
|
"/Ox" "/EHsc")
|
|
|
|
(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 (* (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 ([] 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 ([] 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 ([] 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 (* (const char)) "generate-gl-header")
|
|
|
|
(var this-definition (* 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)
|