Compare commits

..

No commits in common. "abace393ff3c2d78d99e8714e3ee0540ce5186df" and "11e0f2f0dfb0a833c9158d11fcbe46b1926003a5" have entirely different histories.

15 changed files with 681 additions and 338 deletions

View file

@ -8,6 +8,5 @@ A game engine written in Common Lisp.
- [cl-opengl](https://github.com/3b/cl-opengl) - [cl-opengl](https://github.com/3b/cl-opengl)
- [trivial-types](https://github.com/m2ym/trivial-types) - [trivial-types](https://github.com/m2ym/trivial-types)
- [objective-lisp](https://bytes.keithhacks.cyou/keith/objective-lisp) - [objective-lisp](https://bytes.keithhacks.cyou/keith/objective-lisp)
- [superfluous-parentheses](https://bytes.keithhacks.cyou/keith/superfluous-parentheses)
- [3d-vectors](https://github.com/Shinmera/3d-vectors) - [3d-vectors](https://github.com/Shinmera/3d-vectors)
- [3d-matrices](https://github.com/Shinmera/3d-matrices) - [3d-matrices](https://github.com/Shinmera/3d-matrices)

View file

@ -1,34 +0,0 @@
;;;; behaviour-scripts
;;;; a proof-of-concept for scripting dialogue and other behaviours
(ql:quickload :generators)
(use-package :generators)
(defun dialogue-running (handle)
(cdr handle))
(defun example-conversation ()
(make-generator ()
(yield :dialogue "Hello! This is an example conversation. I will now ask you a test question.")
(case (yield :ask-question "Nice of the princess to invite us over for a picnic, eh Luigi?")
(:spaghetti
(yield :dialogue "This is the correct response.")
(let ((dialogue-handle
(yield :async-dialogue "Blah blah blah tutorial stuff. I'll listen for signals while I'm talking.")))
(loop do (case (yield :await-signal)
(:tutorial-failure
(yield :dialogue "No, no, no! The electron frobnicator goes IN the other thingy!!!"))
(:tutorial-success
(if (dialogue-running dialogue-handle)
(progn
(yield :dialogue "Wow, you won't even listen to my help? I see how it is.")
(yield :set-emotion :offended))
(yield :dialogue "Good! That concludes the tutorial."))
(loop-finish))
(:earthquake
(yield :dialogue "The FUCK was that???")))))
(yield :walk-to-exit))
(:penis
(yield :dialogue "This is an incorrect response, but it was received correctly."))
(t
(yield :error "The yield statement returned an unexpected result.")))))

View file

@ -1,84 +0,0 @@
;;; Implementation of smooth pixelart scaling.
;;; This is basically the equivalent of CSS `image-rendering: pixelated`
;; native render resolution
(defvar *render-width* 384)
(defvar *render-height* 256)
;; window resolution
(defvar *window-width* 1200)
(defvar *window-height* 800)
(defvar *render-tex* (gl:gen-texture))
(defvar *render-fb* (gl:gen-framebuffer))
(defvar *upscale-tex* (gl:gen-texture))
(defvar *upscale-fb* (gl:gen-framebuffer))
(defvar *upscale-width*)
(defvar *upscale-height*)
(defmacro with-bind-texture ((target texture) &body body)
`(progn
(gl:bind-texture ,target ,texture)
,@body
(gl:bind-texture ,target 0)))
(defmacro with-bind-framebuffer ((target framebuffer) &body body)
`(progn
(gl:bind-framebuffer ,target ,framebuffer)
,@body
(gl:bind-framebuffer ,target 0)))
(defun init-textures ()
"Initialize textures. Call this whenever the window or render resolution changes."
;; generate the render texture
(with-bind-texture (:texture-2d *render-tex*)
(gl:tex-image-2d
:texture-2d 0 :rgba
*render-width* *render-height*
0 :rgba :unsigned-byte (cffi:null-pointer))
(gl:tex-parameter :texture-2d :texture-wrap-s :clamp-to-edge)
(gl:tex-parameter :texture-2d :texture-wrap-t :clamp-to-edge)
(gl:tex-parameter :texture-2d :texture-min-filter :nearest)
(gl:tex-parameter :texture-2d :texture-mag-filter :nearest))
(with-bind-framebuffer (:framebuffer *render-fb*)
(gl:framebuffer-texture-2d :framebuffer :color-attachment0 :texture-2d *render-tex* 0))
;; generate the upscale texture, which is scaled to an integer multiple of the render size
(setf
*upscale-width* (* (ceiling *window-width* *render-width*) *render-width*)
*upscale-height* (* (ceiling *window-height* *render-height*) *render-height*))
(with-bind-texture (:texture-2d *upscale-tex*)
(gl:tex-image-2d
:texture-2d 0 :rgba
*upscale-width* *upscale-height*
0 :rgba :unsigned-byte (cffi:null-pointer))
(gl:tex-parameter :texture-2d :texture-wrap-s :clamp-to-edge)
(gl:tex-parameter :texture-2d :texture-wrap-t :clamp-to-edge)
(gl:tex-parameter :texture-2d :texture-min-filter :linear)
(gl:tex-parameter :texture-2d :texture-mag-filter :linear))
(with-bind-framebuffer (:framebuffer *upscale-fb*)
(gl:framebuffer-texture-2d :framebuffer :color-attachment0 :texture-2d *upscale-tex* 0)))
(defun before-render ()
(gl:bind-framebuffer :framebuffer *render-fb*)
(gl:viewport 0 0 *render-width* *render-height*))
(defun after-render ()
;; upscale with nearest-neighbour
(gl:bind-framebuffer :framebuffer *upscale-fb*)
(gl:viewport 0 0 *upscale-width* *upscale-height*)
(gl:disable :depth-test)
(gl:enable :texture-2d)
(gl:bind-texture :texture-2d *render-tex*)
(draw-full-screen-quad)
;; downscale with linear, render to window
(gl:bind-framebuffer :framebuffer 0)
(gl:viewport 0 0 *window-width* *window-height*)
(gl:bind-texture :texture-2d *upscale-tex*)
(draw-full-screen-quad)
(gl:bind-texture :texture-2d 0))
(defun draw-full-screen-quad ()
"Draw a quad from (-1,-1) to (1,1). Essentially draw a texture stretched across the entire viewport."
(not-implemented))

View file

@ -6,7 +6,7 @@
:description "A game engine written in Common Lisp." :description "A game engine written in Common Lisp."
:author "~keith" :author "~keith"
:license "GNU AGPLv3" :license "GNU AGPLv3"
:depends-on ("sdl2" "sdl2-image" "cl-opengl" "trivial-types" "objective-lisp" "3d-vectors" "3d-matrices" "superfluous-parentheses") :depends-on ("sdl2" "cl-opengl" "trivial-types" "objective-lisp" "3d-vectors" "3d-matrices")
:components :components
((:module "wh-engine" ((:module "wh-engine"
:components ((:file "package") :components ((:file "package")
@ -19,8 +19,7 @@
(:file "systems") (:file "systems")
(:module "render" (:module "render"
:components ((:file "render-system") :components ((:file "render-system")
(:file "resources") (:file "shader")
(:file "text")
(:file "drawable") (:file "drawable")
(:file "render-target") (:file "render-target")
(:file "view"))) (:file "view")))

View file

@ -70,11 +70,7 @@
(matrix :documentation "Local-to-parent-space transformation matrix." (matrix :documentation "Local-to-parent-space transformation matrix."
:reader matrix :reader matrix
:type mat3 :type mat3
:initform (meye 3)) :initform (meye 3)))
(world-matrix :documentation "Local-to-world-space transformation matrix."
:reader world-matrix
:type mat3
:initform (meye 3)))
(:documentation "Base class for entities in the game.")) (:documentation "Base class for entities in the game."))
(defmethod make-load-form ((this actor) &optional environment) (defmethod make-load-form ((this actor) &optional environment)
@ -82,160 +78,159 @@
(defmethod scene ((this actor)) (defmethod scene ((this actor))
"The scene containing this actor." "The scene containing this actor."
(deref-sus-pointer #[this :slot scene])) (deref-sus-pointer (o! this :slot scene)))
(defmethod parent ((this actor)) (defmethod parent ((this actor))
"This actor's parent." "This actor's parent."
(deref-sus-pointer #[this :slot parent])) (deref-sus-pointer (o! this :slot parent)))
(defmethod tree-active-p ((this actor)) (defmethod tree-active-p ((this actor))
"Whether or not this actor and all its parents are active." "Whether or not this actor and all its parents are active."
(and #[this active-p] (not #[this :slot blocked-p]))) (and (o! this active-p) (not (o! this :slot blocked-p))))
(defmethod apply-to-tree ((this actor) fun) (defmethod apply-to-tree ((this actor) fun)
"Apply fun to this actor and all its children recursively." "Apply fun to this actor and all its children recursively."
(funcall fun this) (funcall fun this)
(loop for child in #[this children] (loop for child in (o! this children)
when (typep child 'actor) when (typep child 'actor)
do #[child (apply-to-tree fun)])) do (o! child (apply-to-tree fun))))
(defmethod print-object ((this actor) stream) (defmethod print-object ((this actor) stream)
(print-unreadable-object (this stream :type t :identity t) (print-unreadable-object (this stream :type t :identity t)
(format stream "~D ~S" (format stream "~D ~S"
#[this :slot id] #[this :slot name]))) (o! this :slot id) (o! this :slot name))))
(defmethod get-component ((this actor) component-class) (defmethod get-component ((this actor) component-class)
"Get a component of the specified class attached to this object." "Get a component of the specified class attached to this object."
(find-if (lambda (component) (typep component component-class)) (find-if (lambda (component) (typep component component-class))
#[this components])) (o! this components)))
(defmethod add-component ((this actor) component) (defmethod add-component ((this actor) component)
"Add a component to this object." "Add a component to this object."
(let ((component-class (class-of component))) (let ((component-class (class-of component)))
(when #[this (get-component component-class)] (when (o! this (get-component component-class))
(error "~S already has a component of class ~S" this component-class)) (error "~S already has a component of class ~S" this component-class))
(push component #[this :slot components]) (push component (o! this :slot components))
#[component (attach this)]) (o! component (attach this)))
component) component)
(defmethod add-child ((this actor) child) (defmethod add-child ((this actor) child)
"Add a child to this object." "Add a child to this object."
(when #[child parent] (when (o! child parent)
(error "~S is already a child of ~S" child #[child parent])) (error "~S is already a child of ~S" child (o! child parent)))
(unless (find-if (lambda (x) (etypecase x (unless (find-if (lambda (x) (etypecase x
(actor (eq x child)) (actor (eq x child))
(id-ref (eql (id-ref-actor x) #[child id])))) (id-ref (eql (id-ref-actor x) (o! child id)))))
#[this :slot children]) (o! this :slot children))
(push child #[this :slot children])) (push child (o! this :slot children)))
(setf #[child :slot parent] (make-weak-pointer this)) (setf (o! child :slot parent) (make-weak-pointer this))
#[child (parent-changed)] (o! child (parent-changed))
child) child)
(defmethod remove-child ((this actor) child) (defmethod remove-child ((this actor) child)
"Remove a child from this object." "Remove a child from this object."
(unless (eq #[child parent] this) (unless (eq (o! child parent) this)
(error "~S is not a child of ~S" child this)) (error "~S is not a child of ~S" child this))
(setf #[this :slot children] (setf (o! this :slot children)
(delete-if (lambda (x) (etypecase x (delete-if (lambda (x) (etypecase x
(actor (eq x child)) (actor (eq x child))
(id-ref (eql (id-ref-actor x) #[child id])))) (id-ref (eql (id-ref-actor x) (o! child id)))))
#[this :slot children] :count 1)) (o! this :slot children) :count 1))
(setf #[child :slot parent] nil) (setf (o! child :slot parent) nil)
#[child (parent-changed)] (o! child (parent-changed))
child) child)
(defmethod recompute-blocked-p ((this actor)) (defmethod recompute-blocked-p ((this actor))
"Determine if any ancestors of this actor are deactivated." "Determine if any ancestors of this actor are deactivated."
(setf #[this :slot blocked-p] (setf (o! this :slot blocked-p)
(when #[this parent] (or (not #[this parent active-p]) #[this parent :slot blocked-p])))) (when (o! this parent) (or (not (o! this parent active-p)) (o! this parent :slot blocked-p)))))
(defmethod has-tag ((this actor) tag) (defmethod has-tag ((this actor) tag)
"Check if this object has the specified tag." "Check if this object has the specified tag."
(find tag #[this tags])) (find tag (o! this tags)))
(defmethod add-tag ((this actor) tag) (defmethod add-tag ((this actor) tag)
"Add a tag to this object." "Add a tag to this object."
(pushnew tag #[this :slot tags])) (pushnew tag (o! this :slot tags)))
(defmethod remove-tag ((this actor) tag) (defmethod remove-tag ((this actor) tag)
"Remove a tag from this object." "Remove a tag from this object."
(setf #[this :slot tags] (remove tag #[this :slot tags]))) (setf (o! this :slot tags) (remove tag (o! this :slot tags))))
(defmethod parent-changed ((this actor)) (defmethod parent-changed ((this actor))
"Called when the actor's parent is changed." "Called when the actor's parent is changed."
#[this (recompute-blocked-p)] (o! this (recompute-blocked-p))
#[this (recompute-matrix)] (loop for component in (o! this components)
(loop for component in #[this components] do (o! component (parent-changed))))
do #[component (parent-changed)]))
(defmethod deactivate ((this actor) &key origin) (defmethod deactivate ((this actor) &key origin)
"Deactivate this object." "Deactivate this object."
#[this (recompute-blocked-p)] (o! this (recompute-blocked-p))
(unless origin (unless origin
(setf #[this :slot active-p] nil)) (setf (o! this :slot active-p) nil))
(loop for component in #[this components] (loop for component in (o! this components)
do #[component (deactivate :origin (or origin this))]) do (o! component (deactivate :origin (or origin this))))
(loop for child in #[this children] (loop for child in (o! this children)
do #[child (deactivate :origin (or origin this))])) do (o! child (deactivate :origin (or origin this)))))
(defmethod activate ((this actor) &key origin) (defmethod activate ((this actor) &key origin)
"Activate this object." "Activate this object."
#[this (recompute-blocked-p)] (o! this (recompute-blocked-p))
(unless origin (unless origin
(setf #[this :slot active-p] t)) (setf (o! this :slot active-p) t))
(when #[this tree-active-p] (when (o! this tree-active-p)
(loop for child in #[this children] (loop for child in (o! this children)
when #[child active-p] when (o! child active-p)
do #[child (activate :origin (or origin this))]) do (o! child (activate :origin (or origin this))))
(loop for component in #[this components] (loop for component in (o! this components)
when #[component active-p] when (o! component active-p)
do #[component (activate :origin (or origin this))]) do (o! component (activate :origin (or origin this))))
)) ))
(defmethod resume ((this actor)) (defmethod resume ((this actor))
"Initialize or restore this actor's state." "Initialize or restore this actor's state."
;; Restore self ;; Restore self
(when (typep #[this :slot scene] 'id-ref) (when (typep (o! this :slot scene) 'id-ref)
;; relink to scene ;; relink to scene
(let ((scene (get-scene (id-ref-scene #[this :slot scene])))) (let ((scene (get-scene (id-ref-scene (o! this :slot scene)))))
(setf #[this :slot scene] nil) (setf (o! this :slot scene) nil)
#[scene (add-actor this)])) (o! scene (add-actor this))))
(when (typep #[this :slot parent] 'id-ref) (when (typep (o! this :slot parent) 'id-ref)
;; relink to parent ;; relink to parent
(let ((parent #[this scene (get-actor (id-ref-actor #[this :slot parent]))])) (let ((parent (o! this scene (get-actor (id-ref-actor (o! this :slot parent))))))
(setf #[this :slot parent] nil) (setf (o! this :slot parent) nil)
#[parent (add-child this)])) (o! parent (add-child this))))
(loop for entry on #[this :slot children] (loop for entry on (o! this :slot children)
when (typep (car entry) 'id-ref) when (typep (car entry) 'id-ref)
do (rplaca entry (dereferize (car entry)))) do (rplaca entry (dereferize (car entry))))
;; Restore components ;; Restore components
(loop for component in #[this components] (loop for component in (o! this components)
do #[component (resume)]) do (o! component (resume)))
;; Restore children ;; Restore children
(loop for child in #[this children] (loop for child in (o! this children)
do #[child (resume)])) do (o! child (resume))))
(defmethod suspend ((this actor)) (defmethod suspend ((this actor))
"Prepare this actor for serialization." "Prepare this actor for serialization."
;; Suspend children ;; Suspend children
(loop for child in #[this children] (loop for child in (o! this children)
do #[child (suspend)]) do (o! child (suspend)))
;; Suspend components ;; Suspend components
(loop for component in #[this components] (loop for component in (o! this components)
do #[component (suspend)]) do (o! component (suspend)))
;; Suspend self ;; Suspend self
(loop for child-cell on #[this :slot children] (loop for child-cell on (o! this :slot children)
when (typep (car child-cell) 'actor) when (typep (car child-cell) 'actor)
do (rplaca child-cell (referize (car child-cell)))) do (rplaca child-cell (referize (car child-cell))))
(referize-setf #[this :slot scene]) (referize-setf (o! this :slot scene))
(referize-setf #[this :slot parent])) (referize-setf (o! this :slot parent)))
(defmethod update ((this actor)) (defmethod update ((this actor))
"Update this actor's components." "Update this actor's components."
(loop for component in #[this components] (loop for component in (o! this components)
do (when #[component active-p] do (when (o! component active-p)
(unless #[component started-p] #[component (start)]) (unless (o! component started-p) (o! component (start)))
#[component (update)]))) (o! component (update)))))
(defmethod destroy ((this actor)) (defmethod destroy ((this actor))
"Mark this object for unloading." "Mark this object for unloading."
@ -258,32 +253,24 @@
;; Transform ;; Transform
(defmethod initialize-instance :after ((this actor) &key) (defmethod initialize-instance :after ((this actor) &key)
#[this (recompute-matrix)]) (o! this (recompute-matrix)))
(defmethod recompute-matrix ((this actor)) (defmethod recompute-matrix ((this actor))
"Recompute the local-to-parent-space matrix and local-to-world-space matrix." "Recompute the local-to-parent-space matrix."
(let ((rs (sin #[this rotation])) (let ((rs (sin (o! this rotation)))
(rc (cos #[this rotation])) (rc (cos (o! this rotation)))
(sx (vx2 #[this scale])) (sx (vx2 (o! this scale)))
(sy (vy2 #[this scale])) (sy (vy2 (o! this scale)))
(tx (vx2 #[this location])) (tx (vx2 (o! this location)))
(ty (vy2 #[this location]))) (ty (vy2 (o! this location))))
(with-fast-matref (m #[this :slot matrix] 3) (with-fast-matref (m (o! this :slot matrix) 3)
(setf (m 0 0) (* sx rc) (setf (m 0 0) (* sx rc)
(m 0 1) (* sy (- rs)) (m 0 1) (* sy (- rs))
(m 0 2) tx (m 0 2) tx
(m 1 0) (* sx rs) (m 1 0) (* sx rs)
(m 1 1) (* sy rc) (m 1 1) (* sy rc)
(m 1 2) ty) (m 1 2) ty)
)) )))
;; world matrix
(setf #[this :slot world-matrix]
(if #[this parent]
(m* #[this parent world-matrix] #[this matrix])
(mcopy3 #[this matrix])))
;; make children update world matrix too
(loop for child in #[this children]
do #[child (recompute-matrix)]))
(defmethod (setf location) (new-val (this actor)) (defmethod (setf location) (new-val (this actor))
"The actor's location relative to its parent." "The actor's location relative to its parent."

View file

@ -18,8 +18,8 @@
(defun input-system-update () (defun input-system-update ()
()) ())
(defparameter *keyspec-parsing-table* nil) (defvar *keyspec-parsing-table* nil)
(defparameter *keycode-translation-reverse-table* nil) (defvar *keycode-translation-reverse-table* nil)
(defun setup-keycode-translation-table (&rest rest) (defun setup-keycode-translation-table (&rest rest)
(let ((table (make-hash-table :size (+ (truncate (length rest) 2) 64))) (let ((table (make-hash-table :size (+ (truncate (length rest) 2) 64)))
(reverse (make-hash-table)) (reverse (make-hash-table))
@ -45,7 +45,7 @@
(setf *keycode-translation-reverse-table* reverse) (setf *keycode-translation-reverse-table* reverse)
(setf *keyspec-parsing-table* parsing) (setf *keyspec-parsing-table* parsing)
table)) table))
(defparameter *keycode-translation-table* (defvar *keycode-translation-table*
(setup-keycode-translation-table #x08 :backspace (setup-keycode-translation-table #x08 :backspace
#x09 :tab #x09 :tab
#x0d :return #x0d :return
@ -225,7 +225,8 @@
(key nil :type (or symbol character cons string null)) (key nil :type (or symbol character cons string null))
(controlp nil :type boolean) (controlp nil :type boolean)
(metap nil :type boolean) (metap nil :type boolean)
(shiftp nil :type boolean)) (shiftp nil :type boolean)
(superp nil :type boolean))
(defmacro keyspec (keyspec expr) (defmacro keyspec (keyspec expr)
"Compare EXPR to an Emacs-style KEYSPEC." "Compare EXPR to an Emacs-style KEYSPEC."
@ -248,11 +249,13 @@
(case (schar ks 0) (case (schar ks 0)
(#\C `(not (keypress-controlp ,var))) (#\C `(not (keypress-controlp ,var)))
(#\M `(not (keypress-metap ,var))) (#\M `(not (keypress-metap ,var)))
(#\S `(not (keypress-shiftp ,var)))) (#\S `(not (keypress-shiftp ,var)))
(#\X `(not (keypress-superp ,var))))
(case (schar ks 0) (case (schar ks 0)
(#\C `(keypress-controlp ,var)) (#\C `(keypress-controlp ,var))
(#\M `(keypress-metap ,var)) (#\M `(keypress-metap ,var))
(#\S `(keypress-shiftp ,var)))) (#\S `(keypress-shiftp ,var))
(#\X `(keypress-superp ,var))))
collect it collect it
and do (setf ks (subseq ks 2)) and do (setf ks (subseq ks 2))
(setf invertp nil) (setf invertp nil)
@ -283,7 +286,8 @@
(make-keypress :key (gethash (sdl2:sym-value key-sym) *keycode-translation-table*) (make-keypress :key (gethash (sdl2:sym-value key-sym) *keycode-translation-table*)
:controlp (/= (logand sdl-mod #x00C0) 0) :controlp (/= (logand sdl-mod #x00C0) 0)
:metap (/= (logand sdl-mod #x0300) 0) :metap (/= (logand sdl-mod #x0300) 0)
:shiftp (/= (logand sdl-mod #x0003) 0)))) :shiftp (/= (logand sdl-mod #x0003) 0)
:superp (/= (logand sdl-mod #x0C00) 0))))
(defun on-key-down (key-sym) (defun on-key-down (key-sym)
(let ((keypress (translate-sdl-key-sym key-sym))) (let ((keypress (translate-sdl-key-sym key-sym)))

View file

@ -79,15 +79,17 @@
(setf this-tick (sdl2:get-ticks)) (setf this-tick (sdl2:get-ticks))
(setf *delta-time* (* (- this-tick prev-tick) 0.001)) (setf *delta-time* (* (- this-tick prev-tick) 0.001))
(setf prev-tick this-tick) (setf prev-tick this-tick)
;; (format t "~%Δt = ~S (~S FPS)~%" *delta-time* (/ 1.0 *delta-time*)) ;(format t "~%Δt = ~S (~S FPS)~%" *delta-time* (/ 1.0 *delta-time*))
;; update ;; update
(loop for scene in *world* (loop for scene in *world*
do (o! scene (update))) do (o! scene (update)))
;; (format t "game=~S " (/ (- (sdl2:get-performance-counter) prev-profiling-tick) profiling-scale)) ;(format t "game=~S " (/ (- (sdl2:get-performance-counter) prev-profiling-tick) profiling-scale))
,@(loop for system in *world-systems* ,@(loop for system in *world-systems*
append `((,(third system)) append `((,(third system))
;; (format t ,(format nil "~S~A" (first system) "=~S ") #|
;; (/ (- (sdl2:get-performance-counter) prev-profiling-tick) profiling-scale)) (format t ,(format nil "~S~A" (first system) "=~S ")
(/ (- (sdl2:get-performance-counter) prev-profiling-tick) profiling-scale))
|#
)) ))
(sdl2:gl-swap-window win)) (sdl2:gl-swap-window win))
(:keydown (:keydown

View file

@ -47,9 +47,9 @@
,(m 2 0) ,(m 2 1) 0.0 ,(m 2 2)) ,(m 2 0) ,(m 2 1) 0.0 ,(m 2 2))
))) )))
(defvar *view-width* 512 (defvar *view-width* 384
"View-space width in pixels.") "View-space width in pixels.")
(defvar *view-height* 384 (defvar *view-height* 256
"View-space height in pixels.") "View-space height in pixels.")
(defvar *view-ppu* 64 (defvar *view-ppu* 64
"Pixels in view-space per unit in world-space.") "Pixels in view-space per unit in world-space.")
@ -64,9 +64,7 @@
(defun sort-world-views () (defun sort-world-views ()
"Re-sort the *world-views* list by render pass." "Re-sort the *world-views* list by render pass."
(sort *world-views* #'< :key (lambda (v) #[(deref-pointer v) render-pass]))) (sort *world-views* #'< :key (lambda (v) (o! (deref-pointer v) render-pass))))
(declaim (special *projection*))
(let (render-target win-width win-height) (let (render-target win-width win-height)
(defun render-system-init () (defun render-system-init ()
@ -74,10 +72,8 @@
win-height (* *view-height* *pixel-scale*) win-height (* *view-height* *pixel-scale*)
render-target (make-instance 'render-target :width *view-width* :height *view-height*)) render-target (make-instance 'render-target :width *view-width* :height *view-height*))
(sdl2-image:init '(:png :jpg :tif))
;; change render target mode ;; change render target mode
(gl:bind-texture :texture-2d #[render-target render-texture]) (gl:bind-texture :texture-2d (o! render-target render-texture))
(gl:tex-parameter :texture-2d :texture-min-filter :nearest) (gl:tex-parameter :texture-2d :texture-min-filter :nearest)
(gl:tex-parameter :texture-2d :texture-mag-filter :nearest) (gl:tex-parameter :texture-2d :texture-mag-filter :nearest)
(gl:bind-texture :texture-2d 0) (gl:bind-texture :texture-2d 0)
@ -94,18 +90,18 @@
(defun render-system-update () (defun render-system-update ()
;; draw to render texture ;; draw to render texture
#[render-target (bind-for-rendering)] (o! render-target (bind-for-rendering))
(gl:clear :color-buffer) (gl:clear :color-buffer)
(gl:enable :depth-test) (gl:enable :depth-test)
(let ((render-pass nil)) (let ((render-pass nil))
(loop for view-ptr in *world-views* (loop for view-ptr in *world-views*
for view = (ensure-live (weak-pointer-value view-ptr)) for view = (ensure-live (weak-pointer-value view-ptr))
when (and #[view active-p] #[view actor tree-active-p]) when (and (o! view active-p) (o! view actor tree-active-p))
do (progn do (progn
(unless (eql #[view render-pass] render-pass) (unless (eql (o! view render-pass) render-pass)
(setf render-pass #[view render-pass]) (setf render-pass (o! view render-pass))
(gl:clear :depth-buffer)) (gl:clear :depth-buffer))
#[view (render-view *world-drawables*)]))) (o! view (render-view *world-drawables*)))))
(gl:flush) (gl:flush)
;; now draw to window ;; now draw to window
@ -114,7 +110,7 @@
(gl:clear :color-buffer) (gl:clear :color-buffer)
(gl:disable :depth-test) (gl:disable :depth-test)
(gl:enable :texture-2d) (gl:enable :texture-2d)
(gl:bind-texture :texture-2d #[render-target render-texture]) (gl:bind-texture :texture-2d (o! render-target render-texture))
(gl:matrix-mode :modelview) (gl:matrix-mode :modelview)
(gl:load-identity) (gl:load-identity)

View file

@ -1,97 +0,0 @@
;;;; wh-engine/render/resources.lisp
;;;; shader & texture resource management
(in-package wh-engine/render)
(defclass texture ()
((texture :documentation "The OpenGL texture ID."
:type fixnum
:initarg :texture
:reader texture)
(path :documentation "The path to the file this texture was loaded from."
:type (or string null)
:initarg :path
:reader path)
(alphap :documentation "Whether or not this texture has an alpha channel."
:type boolean
:initarg :alphap
:reader alphap)
(wrap-mode :documentation "The wrap mode for this texture."
:type keyword
:initarg :wrap-mode
:accessor wrap-mode)
(filterp :documentation "Whether or not this texture has filtering enabled."
:type boolean
:initarg :filterp
:accessor filterp))
(:documentation "A class representing a texture."))
(defmethod make-load-form ((this texture) &optional environment)
`(fetch-texture ,#[this path] :alphap ,#[this alphap] :wrap-mode ,#[this wrap-mode] :filterp ,#[this filterp]))
(defmethod (setf wrap-mode) (new-val (texture texture))
(setf #[texture :slot wrap-mode] new-val)
(let ((old (gl:get-integer :texture-binding-2d)))
(unwind-protect
(progn (gl:bind-texture :texture-2d #[texture texture])
(gl:tex-parameter :texture-2d :texture-wrap-s new-val)
(gl:tex-parameter :texture-2d :texture-wrap-t new-val))
(gl:bind-texture :texture-2d old))))
(defmethod (setf filterp) (new-val (texture texture))
(setf #[texture :slot filterp] new-val)
(let ((old (gl:get-integer :texture-binding-2d)))
(unwind-protect
(progn (gl:bind-texture :texture-2d #[texture texture])
(gl:tex-parameter :texture-2d :texture-min-filter (if new-val :linear :nearest))
(gl:tex-parameter :texture-2d :texture-mag-filter (if new-val :linear :nearest)))
(gl:bind-texture :texture-2d old))))
(defvar *fallback-texdata* nil)
(defun load-image-gracefully (path)
"Return an SDL surface for the given image file, or a fallback magenta-and-black checkerboard."
(declare (type (or string pathname) path))
(unless *fallback-texdata*
(setf *fallback-texdata* (cffi:foreign-alloc :uint32 :count 4))
(setf (cffi:mem-aref *fallback-texdata* :uint32 0) #xFF00FFFF
(cffi:mem-aref *fallback-texdata* :uint32 1) #x000000FF
(cffi:mem-aref *fallback-texdata* :uint32 2) #x000000FF
(cffi:mem-aref *fallback-texdata* :uint32 3) #xFF00FFFF))
(handler-case (sdl2-image:load-image path)
(sdl2-image:sdl-image-error (c)
(warn "Failed to load image: ~S~%" path)
(sdl2:create-rgb-surface-with-format-from *fallback-texdata* 2 2 32 8 :format sdl2:+pixelformat-rgba8888+))))
(defun make-texture (path &key (alphap t) (wrap-mode :repeat) (filterp nil))
"Create a texture instance from a file."
(declare (type (or string pathname) path)
(type boolean alphap filterp)
(type keyword wrap-mode))
(let* ((raw-surf (load-image-gracefully path))
(surf (sdl2:convert-surface-format raw-surf (if alphap :abgr8888 :bgr888)))
(texture (gl:gen-texture)))
(sdl2:free-surface raw-surf)
(gl:bind-texture :texture-2d texture)
(gl:tex-image-2d :texture-2d 0 (if alphap :rgba8 :rgb8)
(sdl2:surface-width surf) (sdl2:surface-height surf)
0 (if alphap :rgba :rgb) :unsigned-byte
(sdl2:surface-pixels surf))
(gl:tex-parameter :texture-2d :texture-wrap-s wrap-mode)
(gl:tex-parameter :texture-2d :texture-wrap-t wrap-mode)
(gl:tex-parameter :texture-2d :texture-min-filter (if filterp :linear :nearest))
(gl:tex-parameter :texture-2d :texture-mag-filter (if filterp :linear :nearest))
(gl:bind-texture :texture-2d 0)
(sdl2:free-surface surf)
(make-instance 'texture
:texture texture
:path path
:alphap alphap
:wrap-mode wrap-mode
:filterp filterp)))
(defvar *texture-registry* (make-hash-table :test #'equal))
(defun fetch-texture (path &rest make-args)
"Try to fetch the texture for PATH from the registry, or create it using MAKE-ARGS if it doesn't exist."
(declare (type (or string pathname) path))
(or (gethash path *texture-registry*)
(setf (gethash path *texture-registry*)
(apply #'make-texture path make-args))))

View file

@ -0,0 +1,494 @@
;;;; wh-engine/render/shader.lisp
;;;; Lisp class for holding & handling shaders.
(in-package wh-engine/render)
(defun replace-xref-vars (forms vars replace-fun)
(loop for elt on forms
for form = (car elt)
if (symbolp form)
do (let ((var-entry (find form vars :key #'car)))
(when var-entry
(rplaca elt (funcall replace-fun var-entry))))
else if (consp form)
do (replace-xref-vars (cdr form) vars replace-fun)))
(defvar *vert-builtins* '((*vertex-id* . "gl_VertexID")
(*instance-id* . "gl_InstanceID")
(*draw-id* . "gl_DrawID")
(*base-vertex* . "gl_BaseVertex")
(*base-instance* . "gl_BaseInstance")
;; out
(*position* . "gl_Position")
(*point-size* . "gl_PointSize")
(*clip-distance* . "gl_ClipDistance")))
(defvar *frag-builtins* '((*frag-coord* . "gl_FragCoord")
(*front-facing* . "gl_FrontFacing")
(*point-coord* . "gl_PointCoord")
(*sample-id* . "gl_SampleID")
(*sample-position* . "gl_SamplePosition")
(*sample-mask-in* . "gl_SampleMaskIn")
(*clip-distance* . "gl_ClipDistance")
(*primitive-id* . "gl_PrimitiveID")
(*layer* . "gl_Layer")
(*viewport-index* . "gl_ViewportIndex")
;; out
(*frag-depth* . "gl_FragDepth")
(*sample-mask* . "gl_SampleMask")))
(defvar *infix-ops* '((+ "+" t)
(- "-" t)
(* "*" t)
(/ "/" t)
(mod "%" t)
(= "==" t)
(eq "==" nil)
(eql "==" nil)
(equal "==" nil)
(/= "!=" nil)
(< "<" t)
(> ">" t)
(<= "<=" t)
(>= ">=" t)
(<< "<<" nil)
(>> ">>" nil)
(ash "<<" nil)
(logand "&" t)
(logior "|" t)
(logxor "^" t)
(and "&&" t)
(or "||" t)))
(defvar *types* '((:void . "void")
(:bool . "bool")
(:int . "int")
(:uint . "uint")
(:float . "float")
(:double . "double")
(:vec2 . "vec2")
(:vec3 . "vec3")
(:vec4 . "vec4")
(:double-vec2 . "dvec2")
(:double-vec3 . "dvec3")
(:double-vec4 . "dvec4")
(:bool-vec2 . "bvec2")
(:bool-vec3 . "bvec3")
(:bool-vec4 . "bvec4")
(:int-vec2 . "ivec2")
(:int-vec3 . "ivec3")
(:int-vec4 . "ivec4")
(:uint-vec2 . "uvec2")
(:uint-vec3 . "uvec3")
(:uint-vec4 . "uvec4")
(:mat2 . "mat2")
(:mat3 . "mat3")
(:mat4 . "mat4")
(:sampler-1d . "sampler1D")
(:depth-sampler-1d . "sampler1DShadow")
(:array-sampler-1d . "sampler1DArray")
(:depth-array-sampler-1d . "sampler1DArrayShadow")
(:sampler-2d . "sampler2D")
(:depth-sampler-2d . "sampler2DShadow")
(:array-sampler-2d . "sampler2DArray")
(:depth-array-sampler-2d . "sampler2DArrayShadow")
(:multisampler-2d . "sampler2DMS")
(:array-multisampler-2d . "sampler2DMSArray")
(:rect-sampler-2d . "sampler2DRect")
(:depth-rect-sampler-2d . "sampler2DRectShadow")
(:sampler-3d . "sampler3D")
(:cube-sampler . "samplerCube")
(:depth-cube-sampler . "samplerCubeShadow")
(:array-cube-sampler . "samplerCubeArray")
(:depth-array-cube-sampler . "samplerCubeArrayShadow")
(:buffer-sampler . "samplerBuffer")))
(defvar *shader-funs* '((bool . "bool")
(int . "int")
(uint . "uint")
(float . "float")
(double . "double")
(vec2 . "vec2")
(vec3 . "vec3")
(vec4 . "vec4")
(radians . "radians")
(degrees . "degrees")
(sin . "sin")
(cos . "cos")
(tan . "tan")
(asin . "asin")
(acos . "acos")
(atan . "atan")
(sinh . "sinh")
(cosh . "cosh")
(tanh . "tanh")
(asinh . "asinh")
(acosh . "acosh")
(atanh . "atanh")
(expt . "pow")
(exp . "exp")
(log . "log")
(sqrt . "sqrt")
(abs . "abs")
(signum . "sign")
(floor . "floor")
(ffloor . "floor")
(truncate . "trunc")
(ftruncate . "trunc")
(round . "round")
(fround . "round")
(ceiling . "ceil")
(fceiling . "ceil")
(mod . "mod")
(min . "min")
(max . "max")
(linear-blend . "mix")
(step . "step")
(smooth-step . "smoothstep")
(float-nan-p . "isnan")
(float-infinity-p . "isinf")
(vlength . "length")
(vdistance . "distance")
(v. . "dot")
(vc . "cross")
(v2norm . "normalize")
(vforward . "faceforward")
(vreflect . "reflect")
(vrefract . "refract")
(mtranspose . "transpose")
(mdet . "determinant")
(minv . "inverse")
(v< . "lessThan")
(v<= . "lessThanEqual")
(v> . "greaterThan")
(v>= . "greaterThanEqual")
(v= . "equal")
(v/= . "notEqual")
(texture-size . "textureSize")
(texture-lod . "textureQueryLod")
(texture-levels . "textureQueryLevels")
(texture-samples . "textureSamples")
(sample-texture . "texture")
(sample-texture-raw . "texelFetch")))
(defun swizzlep (sym)
(let ((sym-str (string-downcase (symbol-name sym))))
(and (char= (char sym-str 0) #\v)
(loop for c across (subseq sym-str 1)
always (member c '(#\x #\y #\z #\w) :test #'char=))
(subseq sym-str 1))))
(defun conv-shader-type (type)
(or (cdr (assoc type *types*))
(error "Invalid type specifier: ~S" type)))
(defvar *shader-var-suffix* 0)
(defun conv-shader-var (sym &optional gen-suffix)
(concatenate 'string
(substitute-if-not #\_ (lambda (c) (or (alpha-char-p c) (digit-char-p c)))
(remove #\- (string-downcase (string-capitalize (symbol-name sym)) :end 1)))
(if gen-suffix
(write-to-string (incf *shader-var-suffix*))
"")))
(defun conv-shader-form (form vars funs)
(flet ((conv-form (x) (conv-shader-form x vars funs)))
(etypecase form
(cons
(let* ((sym (car form))
(args (cdr form))
(infix-op (assoc sym *infix-ops*))
(swizzle (swizzlep sym)))
(cond
(infix-op
;; special case for -, which is also a unary op
(if (and (eq sym '-) (= (length args) 1))
(format nil "(-~A)"
(conv-form (first args)))
(progn
(unless (or (= (length args) 2) (and (nth 2 infix-op) (> (length args) 2)))
(error "Invalid # of args for shader infix-op: ~S" infix-op))
;; don't wanna repeat this a bajillion times, so just splice the
;; GLSL version of the operator into the format control string
(format nil (concatenate 'string "(~{~A~^ " (nth 1 infix-op) " ~})")
(loop for x in args collect (conv-form x))))))
;; the unary operators ! and ~
((member sym '(not lognot))
(unless (= (length args) 1)
(error "Invalid # of args for shader unary-op: ~S" sym))
(format nil (if (eq sym 'not) "(!~A)" "(~~~A)")
(conv-form (first args))))
;; hijack the (exprA, exprB, ...) to allow multi-setf
((eq sym 'setf)
(unless (= (mod (length args) 2) 0)
(error "Invalid # of args for shader setf"))
(format nil "(~{~A = ~A~^, ~})"
(loop for x in args collect (conv-form x))))
((eq sym 'nth)
(unless (= (length args) 2)
(error "Invalid # of args for shader nth"))
(format nil "~A[~A]"
(conv-form (second args))
(conv-form (first args))))
((eq sym 'aref)
(unless (>= (length args) 2)
(error "Invalid # of args for shader aref"))
(format nil "~A~{[~A]~}"
(conv-form (first args))
(loop for x in (rest args) collect (conv-form x))))
;; non-statement (if) is the ternary operator
((eq sym 'if)
(unless (= (length args) 3)
(error "Invalid # of args for shader if"))
(format nil "(~A ? ~A : ~A)"
(conv-form (first args))
(conv-form (second args))
(conv-form (third args))))
;; WHY IS THIS A FUNCTION HISS
((eq sym 'length)
(unless (= (length args) 1)
(error "Invalid # of args for shader length"))
(format nil "~A.length()"
(conv-form (first args))))
;; apparently you can do this, (exprA, exprB) is non-statement progn
;; thanks C
((eq sym 'progn)
(unless (>= (length args) 1)
(error "Invalid # of args for shader progn"))
(format nil "(~{~A~^, ~})"
(loop for x in args collect (conv-form x))))
(swizzle
(unless (= (length args) 1)
(error "Invalid # of args for shader swizzle"))
(format nil "~A.~A"
(conv-form (first args))
swizzle))
(t
(format nil "~A(~{~A~^, ~})"
(or (cdr (assoc sym funs)) (error "Invalid shader fun: ~S" sym))
(loop for x in args collect (conv-form x)))))))
(boolean (if form "true" "false"))
(symbol (or (cdr (assoc form vars)) (error "Invalid shader var: ~S" form)))
(integer (format nil "~D" form))
(float (format nil "~F" form)))))
(defun conv-shader-stmt (stmt vars funs returnp)
(labels ((conv-form (x) (conv-shader-form x vars funs))
(conv-stmt (x r) (conv-shader-stmt x vars funs r))
(loop-conv-stmts (list r)
(loop for x on list
collect (conv-stmt (car x) (and r (not (cdr x)))))))
(let* ((sym (when (consp stmt) (car stmt)))
(args (when (consp stmt) (cdr stmt))))
(case sym
((progn)
(format nil "{~%~{~A~}}~%"
(loop-conv-stmts args returnp)))
((if)
(unless (<= 2 (length args) 3)
(error "Invalid # of args for shader if"))
(format nil "if (~A) ~{~%~A~^else~}"
(conv-form (first args))
(loop-conv-stmts (rest args) returnp)))
;; handle both forms in one case clause
((when unless)
(unless (>= (length args) 2)
(error "Invalid # of args for shader when/unless"))
(format nil "if (~:[~;!(~]~A~2:*~:[~;)~]~1*) {~%~{~A~}}~%"
(eq sym 'unless)
(conv-form (first args))
(loop-conv-stmts (rest args) returnp)))
((cond)
(unless (>= (length args) 1)
(error "Invalid # of args for shader cond"))
(format nil "~{~:[if (~A) ~;~1*~]{~%~{~A~}}~^ else ~}~%"
(loop for clause in args
nconc (list (eq (first clause) t)
(conv-form (first clause))
(loop-conv-stmts (rest clause) returnp)))))
((case)
(unless (>= (length args) 2)
(error "Invalid # of args for shader case"))
(format nil "switch (~A) {~%~{~:[~{case ~A:~%~}~;default:~%~1*~]~{~A~}break;~%~}}~%"
(conv-form (first args))
(loop for clause in (rest args)
nconc (list (eq (car clause) t)
(if (listp (car clause))
(loop for x in (car clause) collect (conv-form x))
(list (conv-form (car clause))))
(loop-conv-stmts (cdr clause) returnp)))))
((return)
(unless (<= 0 (length args) 1)
(error "Invalid # of args for shader return"))
(format nil "return~:[~; ~A~];~%"
args
(and args (conv-form (first args)))))
((break continue discard)
(when args
(error "Invalid # of args for shader break"))
(format nil "~(~A~);~%" sym))
((while)
(unless (>= (length args) 1)
(error "Invalid # of args for shader while"))
(format nil "while (~A) {~%~{~A~}}~%"
(conv-form (first args))
(loop-conv-stmts (rest args) returnp)))
((dotimes)
(unless (and (>= (length args) 1) (= (length (first args)) 2))
(error "Invalid # of args for shader dotimes"))
(let ((new-vars (cons (cons (caar args) (conv-shader-var (caar args) t)) vars)))
(format nil "for (int ~A = 0; ~1:*~A < ~A; ~2:*~A++~1*) {~%~{~A~}}~%"
(conv-shader-form (caar args) new-vars funs)
(conv-shader-form (cadar args) new-vars funs)
(loop for x on (cdr args)
collect (conv-shader-stmt (car x) new-vars funs
(and returnp (not (cdr x))))))))
((for)
(unless (and (>= (length args) 1) (= (length (first args)) 3))
(error "Invalid # of args for shader for"))
(format nil "for (~A;~A;~A) {~%~{~A~}}~%"
(conv-form (caar args))
(conv-form (cadar args))
(conv-form (caddar args))
(loop-conv-stmts (cdr args) returnp)))
((let let*)
(unless (>= (length args) 1)
(error "Invalid # of args for shader let"))
(let ((new-vars (nconc (loop for decl in (first args)
if (consp (car decl))
collect (cons (caar decl) (conv-shader-var (caar decl) t))
else
collect (cons (car decl) (conv-shader-var (car decl) t)))
vars)))
(format nil "{~%~{~A ~A~:[~; = ~A~];~%~}~{~A~}}~%"
(loop for decl in (first args)
if (consp (car decl))
nconc (list
(conv-shader-type (cadar decl))
(conv-shader-form (caar decl) new-vars funs)
t (conv-shader-form (cadr decl) new-vars funs))
else
nconc (list
(conv-shader-type (cadr decl))
(conv-shader-form (car decl) new-vars funs)
nil))
(loop for x on (rest args)
collect (conv-shader-stmt (car x) new-vars funs
(and returnp (not (cdr x))))))))
((setf)
(unless (= (mod (length args) 2) 0)
(error "Invalid # of args for shader setf"))
(format nil "~{~A = ~A;~%~}"
(loop for x in args collect (conv-form x))))
(t
(format nil "~:[~;return ~]~A;~%" returnp (conv-form stmt)))))))
(defun conv-shader-fun (name return-type params body vars funs)
(let ((new-vars (nconc (loop for decl in params
collect (cons (car decl) (conv-shader-var (car decl))))
vars)))
(format nil "~A ~A(~{~A ~A~^, ~}) {~%~{~A~}}"
(conv-shader-type return-type) name
(loop for decl in params
nconc (list (conv-shader-type (cadr decl))
(conv-shader-form (car decl) new-vars funs)))
(loop for x on body
collect (conv-shader-stmt (car x) new-vars funs
(not (or (cdr x) (eq return-type :void))))))))
(defun conv-shader-io
(mode name &key type
(location nil)
(component nil)
(binding nil)
(top-left-origin-p nil)
(pixel-coords-p nil)
&allow-other-keys)
(format nil "~:[~;~1:*layout(~{~A~^, ~}) ~]~(~A~) ~A ~A;~%"
(loop for val in (list location component binding top-left-origin-p pixel-coords-p)
for key in (list "location" "component" "binding" "origin_upper_left" "pixel_center_integer")
if (eq val t)
collect key
else if val
collect (format nil "~A=~D" key val))
mode (conv-shader-type type) (conv-shader-var name)))
(defun conv-shader-part (name part version in-vars out-vars uniform-vars funs)
(apply #'concatenate 'string
(format nil "#version ~D core~%// GLSL-ified Lisp shader: ~S~S~%" version name part)
(nconc
(loop for var in in-vars
collect (apply #'conv-shader-io :in var))
(loop for var in out-vars
collect (apply #'conv-shader-io :out var))
(loop for var in uniform-vars
collect (apply #'conv-shader-io :uniform var))
funs)))
(defun transpile-shader (name &key
version
((:in vert-inputs))
((:inter inter-vars))
((:out frag-outputs))
((:uniform uniform-vars))
((:vert vert-body))
((:frag frag-body)))
"Translate Lisp shader code to GLSL."
;; create implicit inter vars
(replace-xref-vars frag-body vert-inputs
(lambda (var-entry)
(let* ((orig-var (car var-entry))
(inter-var-entry (find orig-var inter-vars
:key (lambda (e) (getf (cdr e) :orig-var)))))
(if inter-var-entry
(car inter-var-entry)
(let ((new-var (gensym (symbol-name orig-var))))
(push `(,new-var :orig-var ,orig-var :type ,(getf (cdr var-entry) :type))
inter-vars)
(push `(setf ,new-var ,orig-var) vert-body)
new-var)))))
(let* ((vert-vars (nconc (loop for var-entry in (append inter-vars vert-inputs uniform-vars)
collect (cons (car var-entry) (conv-shader-var (car var-entry))))
*vert-builtins*))
(frag-vars (nconc (loop for var-entry in (append frag-outputs inter-vars uniform-vars)
collect (cons (car var-entry) (conv-shader-var (car var-entry))))
*frag-builtins*))
(vert-uniforms ()) (frag-uniforms ())
vert-glsl frag-glsl)
(replace-xref-vars vert-body uniform-vars
(lambda (var-entry)
(pushnew var-entry vert-uniforms)
(car var-entry)))
(replace-xref-vars frag-body uniform-vars
(lambda (var-entry)
(pushnew var-entry frag-uniforms)
(car var-entry)))
(setf vert-glsl
(conv-shader-part name :vert version
vert-inputs inter-vars vert-uniforms
(list (conv-shader-fun "main" :void () vert-body vert-vars *shader-funs*)))
frag-glsl
(conv-shader-part name :frag version
inter-vars frag-outputs frag-uniforms
(list (conv-shader-fun "main" :void () frag-body frag-vars *shader-funs*))))
`(:vert ,vert-glsl :frag ,frag-glsl
(:in ,vert-inputs :inter ,inter-vars :out ,frag-outputs :uniform ,uniform-vars
:version ,version :vert ,vert-body :frag ,frag-body))))
(defmacro define-shader (name (&key
((:in (&rest vert-inputs)) ())
((:inter (&rest inter-vars)) ())
((:out (&rest frag-outputs)) ())
((:uniform (&rest uniform-vars)) ()))
&key
((:vert (&body vert-body)))
((:frag (&body frag-body)))
version)
"Define a GLSL shader with Lisp code."
`(defparameter ,name (transpile-shader ',name
:version ,version
:in ',vert-inputs
:inter ',inter-vars
:out ',frag-outputs
:uniform ',uniform-vars
:vert ',vert-body :frag ',frag-body)))

View file

@ -0,0 +1,12 @@
#version 330 core
in vec2 uv;
out vec4 FragColour;
uniform sampler2D mainTex;
uniform vec4 colour;
void main() {
FragColour = texture(mainTex, uv) * colour;
}

View file

@ -0,0 +1,26 @@
;;;; basic-shader.lisp (Lisp shader code)
(in-package wh-engine/render)
(define-shader basic-shader
(:in ((vert-pos :type :vec3 :location 0)
(vert-uv :type :vec2 :location 1))
:out ((*frag-colour* :type :vec4))
:uniform ((model :type :mat4)
(view :type :mat4)
(proj :type :mat4)
(main-tex :type :sampler-2d)
(colour :type :vec4)))
:version 330
:vert ((setf *position* (* proj view model (vec4 vert-pos 1.0))))
:frag ((setf *frag-colour* (* (sample-texture main-tex vert-uv) colour))))
(define-shader render-target-blit-shader
(:in ((vert-pos :type :vec2 :location 0))
:out ((*frag-colour* :type :vec4)
(*frag-depth* :type :float))
:uniform ((main-tex :type :sampler-2d)
(depth-tex :type :depth-sampler-2d)))
:version 330
:vert ((setf *position* (vec4 vert-pos 0.0 1.0)))
:frag ((setf *frag-colour* (sample-texture main-tex vert-pos)
*frag-depth* (sample-texture depth-tex vert-pos))))

View file

@ -0,0 +1,15 @@
#version 330 core
layout (location = 0) in vec3 vert_pos;
layout (location = 1) in vec2 vert_uv;
out vec2 uv;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
void main() {
gl_Position = proj * view * model * vec4(vert_pos, 1.0);
uv = vert_uv;
}

View file

@ -0,0 +1,14 @@
#version 330 core
in vec2 uv;
out vec4 FragColour;
out float gl_FragDepth;
uniform sampler2D mainTex;
uniform sampler2DShadow depthTex;
void main() {
FragColour = texture(mainTex, uv);
gl_FragDepth = texture(depthTex, uv);
}

View file

@ -0,0 +1,10 @@
#version 330 core
layout (location = 0) in vec2 vert_pos;
out vec2 uv;
void main() {
gl_Position = vec4(vert_pos, 0.0, 1.0);
uv = vert_pos;
}