stoe/src/render/shader/glsl-compiler.lisp
Renaud Casenave-Péré a79a06ef00 Add a facility to create shaders from lisp code
For now, only the global variables are generated by lisp code. The code
of the main function is specified as is in a string.
2014-11-02 16:52:43 +09:00

92 lines
3.5 KiB
Common Lisp

#|
This file is a part of stoe project.
Copyright (c) 2014 Renaud Casenave-Péré (renaud@casenave-pere.fr)
|#
(in-package :cl-user)
(defpackage stoe.shader.glsl-compiler
(:nicknames :glsl-compiler)
(:use :cl :utils)
(:export :make-shader))
(in-package :stoe.shader.glsl-compiler)
(defvar +default-version+ '(330 :core))
(defvar +profile-names+ '(:core :compatibility))
(defvar +interpolation-qualifiers+ '(:flat :noperspective :smooth))
(defstruct sh-version
(number 0 :read-only t)
(profile nil :read-only t))
(defstruct sh-variable
(name "" :read-only t)
(type :int :read-only t)
(qualifier :in :read-only t)
(array 1 :read-only t)
(location nil :read-only t)
(interp nil :read-only t))
(defstruct (shader (:constructor %make-shader))
(name "" :read-only t)
(version nil :read-only t)
(inputs nil :read-only t)
(outputs nil :read-only t)
(uniforms nil :read-only t)
(code "" :read-only t))
(defun compile-shader-version (version)
(format nil "#version ~a~@[ ~(~a~)~]~%" (sh-version-number version) (sh-version-profile version)))
(defun fix-name-convention (cl-name)
"Convert a variable name in common-lisp convention to a glsl compliant name."
(substitute #\_ #\- cl-name))
(defun compile-shader-variables (vars)
(apply #'concatenate 'string
(loop for var in vars
collect (format nil "~@[layout (location = ~a) ~]~@[~(~a~) ~]~(~a~) ~(~a~) ~(~a~)~[~;~:;[~:*~a]~];~%"
(sh-variable-location var)
(sh-variable-interp var)
(sh-variable-qualifier var)
(sh-variable-type var)
(fix-name-convention (sh-variable-name var))
(sh-variable-array var)))))
(defun compile-shader-main (body)
(format nil "void main ()~%{~%~a~%}~%" body))
(defun process-version (spec)
(make-sh-version :number (if (numberp (car spec)) (car spec) (car +default-version+))
:profile (if (and (cdr spec) (member (cadr spec) +profile-names+))
(cadr spec)
(car +profile-names+))))
(defun process-variable-1 (qualifier spec)
(make-sh-variable :name (symbol-name (car spec))
:type (cadr spec)
:qualifier qualifier
:array (getf (cddr spec) :array 1)
:location (getf (cddr spec) :location nil)
:interp (safe-first (member (getf (cddr spec) :interp) +interpolation-qualifiers+))))
(defun process-variables (qualifier specs)
(loop for spec in specs
collect (process-variable-1 qualifier spec)))
(defun compile-shader (version inputs outputs uniforms body)
"Compile a shader's data into a string containing the shader in glsl."
(concatenate 'string
(compile-shader-version version)
(compile-shader-variables inputs)
(compile-shader-variables outputs)
(compile-shader-variables uniforms)
(compile-shader-main body)))
(defun make-shader (name args body)
(let* ((version (process-version (safe-list (getf args :version))))
(inputs (process-variables :in (getf args :in)))
(outputs (process-variables :out (getf args :out)))
(uniforms (process-variables :uniform (getf args :uniform)))
(code (compile-shader version inputs outputs uniforms (safe-first body))))
(%make-shader :name (symbol-name name) :version version :inputs inputs
:outputs outputs :uniforms uniforms :code code)))