Little nomenclature and do->loop clean ups.
[cl-glfw/jecs.git] / lib / opengl-convenience.lisp
blobbd9571a302fb32415a23d85a80ef0a6c50cb2b1e
1 (in-package #:opengl)
3 (defmacro with-new-list (list mode &body forms)
4 "New/End-List wrapper."
5 `(progn
6 (gl:new-list ,list ,mode)
7 (unwind-protect (progn ,@forms)
8 (gl:end-list))))
10 (defmacro with-push-name (name &body forms)
11 "Select name push/pop wrapper"
12 `(progn
13 (gl:push-name ,name)
14 (unwind-protect (progn ,@forms)
15 (gl:pop-name))))
17 (defmacro with-begin (mode &body forms)
18 "Immediate mode block wrapper."
19 `(progn
20 (gl:begin ,mode)
21 (unwind-protect (progn ,@forms)
22 (gl:end))))
24 (defmacro with-push-attrib ((&rest attrib-bits) &body forms)
25 "Pushes the bitwise or of attrib-bits, executing forms and clean up with pop-attrib."
26 `(progn
27 (gl:push-attrib (logior ,@attrib-bits))
28 (unwind-protect (progn ,@forms)
29 (gl:pop-attrib))))
31 (defmacro with-push-matrix (&body forms)
32 "Pushes the current matrix onto the stack, executes forms and clean up with pop-matrix."
33 `(progn
34 (gl:push-matrix)
35 (unwind-protect (progn ,@forms)
36 (gl:pop-matrix))))
38 (defmacro with-setup-projection (&body forms)
39 "Switch to projection mode, load identity, execute forms and return to modelview matrix mode."
40 `(progn
41 (gl:matrix-mode gl:+projection+)
42 (gl:load-identity)
43 (unwind-protect (progn ,@forms)
44 (gl:matrix-mode gl:+modelview+))))
46 (defmacro with-projection-matrix ((&body projection-matrix-setup-forms)
47 &body forms)
48 "Temporarily change the projection matrix:
49 Saves the current projection matrix,
50 loads identity in the projection matrix,
51 runs projection-matrix-setup-forms in projection matrix-mode,
52 runs forms in modelview matrix-mode,
53 restores the saved projection-matrix
54 and leaves in modelview matrix-mode."
55 `(progn
56 (gl:matrix-mode gl:+projection+)
57 (gl:push-matrix)
58 (gl:load-identity)
59 (unwind-protect
60 (progn
61 ,@projection-matrix-setup-forms
62 (gl:matrix-mode gl:+modelview+)
63 ,@forms)
64 (gl:matrix-mode gl:+projection+)
65 (gl:pop-matrix)
66 (gl:matrix-mode gl:+modelview+))))
68 ;; 1.1 Conveniences
70 (defmacro with-push-client-attrib ((&rest attrib-bits) &body forms)
71 "Pushes the bitwise or of the client attrib-bits, executing forms and clean up with pop-client-attrib."
72 `(progn
73 (gl:push-client-attrib (logior ,@attrib-bits))
74 (unwind-protect (progn ,@forms)
75 (gl:pop-client-attrib))))
77 ;; 1.5 Conveniences
78 (defmacro with-begin-query ((target id) &body forms)
79 `(progn
80 (gl:begin-query ,target ,id)
81 (unwind-protect (progn ,@forms)
82 (gl:end-query))))
84 (defmacro with-map-buffer ((target access) &body forms)
85 "Executes forms with VBO buffer mapped as with gl:map-buffer, unmapping cleanly afterwards."
86 (let ((ntarget (gensym "TARGET-")))
87 `(let ((,ntarget ,target))
88 (gl:map-buffer ,ntarget ,access)
89 (unwind-protect (progn ,@forms)
90 (gl:unmap-buffer ,ntarget)))))
92 (defmacro with-bind-buffer ((target buffer) &body forms)
93 "Executes forms with the VBO buffer bound with gl:bind-buffer, cleanly restoring to previous state afterwards."
94 (let ((ntarget (gensym "TARGET-"))
95 (saved-buffer-v (gensym "SAVED-BUFFER-V-")))
96 `(let ((,ntarget ,target))
97 (cffi:with-foreign-object (,saved-buffer-v 'gl:int)
98 (cond
99 ((eql ,ntarget gl:+array-buffer+) (gl:get-integerv gl:+array-buffer-binding+ ,saved-buffer-v))
100 ((eql ,ntarget gl:+element-array-buffer+) (gl:get-integerv gl:+element-array-buffer-binding+ ,saved-buffer-v))
101 ((eql ,ntarget gl:+pixel-pack-buffer+) (gl:get-integerv gl:+pixel-pack-buffer-binding+ ,saved-buffer-v))
102 ((eql ,ntarget gl:+pixel-unpack-buffer+) (gl:get-integerv gl:+pixel-unpack-buffer-binding+ ,saved-buffer-v)))
103 (gl:bind-buffer ,ntarget ,buffer)
104 (unwind-protect (progn ,@forms)
105 (gl:bind-buffer ,ntarget (cffi:mem-ref ,saved-buffer-v 'gl:int)))))))
107 ;; ARB_vertex_buffer_object
109 (defmacro with-map-buffer-arb ((target access) &body forms)
110 "Executes forms with VBO buffer mapped as with gl:map-buffer-arb, unmapping cleanly afterwards."
111 (let ((ntarget (gensym "TARGET-")))
112 `(let ((,ntarget ,target))
113 (gl:map-buffer-arb ,ntarget ,access)
114 (unwind-protect (progn ,@forms)
115 (gl:unmap-buffer-arb ,ntarget)))))
117 (defmacro with-bind-buffer-arb ((target buffer) &body forms)
118 "Executes forms with the VBO buffer bound with gl:bind-buffer-arb, cleanly restoring to previous state afterwards."
119 (let ((ntarget (gensym "TARGET-"))
120 (saved-buffer-v (gensym "SAVED-BUFFER-V-")))
121 `(let ((,ntarget ,target))
122 (cffi:with-foreign-object (,saved-buffer-v 'gl:int)
123 (cond
124 ((eql ,ntarget gl:+array-buffer-arb+) (gl:get-integerv gl:+array-buffer-binding-arb+ ,saved-buffer-v))
125 ((eql ,ntarget gl:+element-array-buffer-arb+) (gl:get-integerv gl:+element-array-buffer-binding-arb+ ,saved-buffer-v))
126 ((eql ,ntarget gl:+pixel-pack-buffer-arb+) (gl:get-integerv gl:+pixel-pack-buffer-binding-arb+ ,saved-buffer-v))
127 ((eql ,ntarget gl:+pixel-unpack-buffer-arb+) (gl:get-integerv gl:+pixel-unpack-buffer-binding-arb+ ,saved-buffer-v)))
128 (gl:bind-buffer-arb ,ntarget ,buffer)
129 (unwind-protect (progn ,@forms)
130 (gl:bind-buffer-arb ,ntarget (cffi:mem-ref ,saved-buffer-v 'gl:int)))))))
133 ;; common (between ARB and 2.0) shader stuff
137 (define-condition shader-object-error (error)
138 ((object-handle :initarg :object-handle :reader object-handle)
139 (info-log :initarg :info-log :reader info-log))
140 (:report (lambda (condition stream)
141 (format stream "~a Error with ~a:~%~a~%"
142 (type-of condition)
143 (object-handle condition)
144 (info-log condition)))))
145 (define-condition shader-compile-error (shader-object-error) ())
146 (define-condition program-link-error (shader-object-error) ())
148 ;; 2.0 and ARB_shader_objects/ARB_vertex_shader/ARB_fragment_shader conveniences
150 (eval-when (:compile-toplevel :load-toplevel :execute)
151 (defmacro define-arb-and-non-arb (&body form)
152 "Expands out code with (?extension with-arb without-arb) markers in it.
153 Care should be taken that it doesn't interfere with code with ` , markers in it."
154 (let ((extension nil))
155 (declare (special extension))
156 (labels ((expand-extension (form)
157 (if (listp form)
158 (if (eql (first form) '?extension)
159 (if extension (second form) (third form))
160 (mapcar #'expand-extension form))
161 form)))
162 `(progn
163 ,@(let ((extension t))
164 (declare (special extension))
165 (expand-extension form)) ; undefined function: EXPAND-EXTENSION
166 ,@(expand-extension form))))))
168 (define-arb-and-non-arb
169 (defun (?extension shader-source-from-stream-arb shader-source-from-stream) (handle in)
170 (declare (type stream in))
171 (let* ((lines (loop for line = (read-line in nil) while line collecting line))
172 (c-lines (cffi:foreign-alloc :string :initial-contents lines)))
173 ((?extension gl:shader-source-arb gl:shader-source) handle (length lines) c-lines (cffi:null-pointer))))
175 (defun (?extension check-compiled-shader-arb check-compiled-shader) (handle)
176 "Given a shader handle that has been compiled, checks and flags in a lisp-friendly manner
177 their compile status. Errors are signalled using an error with the shader log, success will
178 just warn with the contents of the program-log, if present."
179 (cffi:with-foreign-objects ((log-length 'gl:sizei)
180 (compile-status 'gl:int))
181 (?extension
182 (gl:get-object-parameter-iv-arb handle gl:+object-info-log-length-arb+ log-length)
183 (gl:get-shader-iv handle gl:+info-log-length+ log-length))
184 (?extension
185 (gl:get-object-parameter-iv-arb handle gl:+object-compile-status-arb+ compile-status)
186 (gl:get-shader-iv handle gl:+compile-status+ compile-status))
187 (let ((shader-log (when (> (cffi:mem-ref log-length 'gl:sizei) 1)
188 (cffi:with-foreign-pointer-as-string (str 4096)
189 ((?extension gl:get-info-log-arb gl:get-shader-info-log) handle (cffi:mem-ref log-length 'gl:sizei) (cffi:null-pointer) str)
190 (cffi:foreign-string-to-lisp str (cffi:mem-ref log-length 'gl:sizei))))))
191 (if (= (cffi:mem-ref compile-status 'gl:int)
192 gl:+true+)
193 (when shader-log (warn shader-log))
194 (error 'shader-compile-error :info-log shader-log :object-handle handle)))))
196 (defun (?extension make-shader-arb make-shader) (type source)
197 "Convenience function to create a shader of type given from source, which may either be a
198 pathname of a file to load from, or a string of the shader source directly. Returns handle of the new shader."
199 (let ((handle ((?extension gl:create-shader-object-arb gl:create-shader) type)))
200 (if (typep source 'pathname)
201 (with-open-file (in source :direction :input)
202 ((?extension shader-source-from-stream-arb shader-source-from-stream) handle in))
203 (with-input-from-string (in source)
204 ((?extension shader-source-from-stream-arb shader-source-from-stream) handle in)))
205 ((?extension gl:compile-shader-arb gl:compile-shader) handle)
206 ((?extension check-compiled-shader-arb check-compiled-shader) handle)
207 handle))
209 (defun (?extension check-linked-program-arb check-linked-program) (handle)
210 "Given a program handle that has been linked, checks and flags in a lisp-friendly manner
211 their link status. Errors are signalled using an error with the program log, success will
212 just warn with the contents of the program-log, if present."
213 (cffi:with-foreign-objects ((log-length 'gl:sizei)
214 (link-status 'gl:int))
215 (?extension
216 (gl:get-object-parameter-iv-arb handle gl:+object-info-log-length-arb+ log-length)
217 (gl:get-program-iv handle gl:+info-log-length+ log-length))
218 (?extension
219 (gl:get-object-parameter-iv-arb handle gl:+object-link-status-arb+ link-status)
220 (gl:get-program-iv handle gl:+link-status+ link-status))
221 (let ((program-log (when (> (cffi:mem-ref log-length 'gl:sizei) 1)
222 (cffi:with-foreign-pointer-as-string (str 4096)
223 ((?extension gl:get-info-log-arb gl:get-program-info-log) handle (cffi:mem-ref log-length 'gl:sizei) (cffi:null-pointer) str)
224 (cffi:foreign-string-to-lisp str (cffi:mem-ref log-length 'gl:sizei))))))
225 (if (= (cffi:mem-ref link-status 'gl:int)
226 gl:+true+)
227 (when program-log (warn program-log))
228 (error 'program-link-error :info-log program-log :object-handle handle)))))
230 (defun (?extension make-program-arb make-program) (&rest shader-handles)
231 "Given shader handles, creates a program, attaches any shaders given and links the program."
232 (let ((handle ((?extension gl:create-program-object-arb gl:create-program))))
233 (dolist (shader-handle shader-handles)
234 ((?extension gl:attach-object-arb gl:attach-shader) handle shader-handle))
235 (when shader-handles
236 ((?extension gl:link-program-arb gl:link-program) handle)
237 ((?extension check-linked-program-arb check-linked-program) handle))
238 handle))
240 (defparameter (?extension *fallback-synchronizing-program-arb* *fallback-synchronizing-program*) nil)
242 (defun (?extension fallback-synchronizing-program-arb fallback-synchronizing-program) ()
243 (or (?extension *fallback-synchronizing-program-arb* *fallback-synchronizing-program*)
244 (setf (?extension *fallback-synchronizing-program-arb* *fallback-synchronizing-program*)
245 ((?extension make-program-arb make-program)
246 ((?extension make-shader-arb make-shader) (?extension gl:+vertex-shader-arb+ gl:+vertex-shader+)
247 "varying float position;
248 void main()
250 gl_Position=ftransform();
251 position=gl_Position.x+gl_Position.y+gl_Position.z;
254 ((?extension make-shader-arb make-shader) (?extension gl:+fragment-shader-arb+ gl:+fragment-shader+)
255 "varying float position;
256 void main() {
257 float intensity=mod(position*4.0,1.0) > 0.5 ? 1.0 : 0.1;
258 gl_FragColor=vec4(intensity,intensity,0.0,1.0);
259 }")))))
261 (defun (?extension synchronizing-program-arb synchronizing-program) (program-name &rest shader-type-sources)
262 "Creates a managed shader program that will poll shader files on disk
263 and load them automatically if their write-times change. This can be used for
264 easily developing shader code while the program is running. If the shader code
265 does not compile for any reason, errors will be displayed on the *error-output*
266 and a program specified by gl:*fallback-synchronizing-program* will be returned
267 instead (a black and yellow striped static pattern).
269 For an example, please see examples/synchronized-shader.lisp."
270 (defvar *synchronizing-shader-programs* (make-hash-table :test 'equal))
271 ;; structure of an entry:
272 ;; program-name: (program-object-handle shader-entry+ ... )
273 ;; shader-entry: (shader-object-handle source-file last-compile-time)
275 ;; make-structure
276 (when (not (nth-value 1 (gethash program-name *synchronizing-shader-programs*)))
277 (let* ((program ((?extension create-program-object-arb create-program)))
278 (shaders (mapcar #'(lambda (type-source)
279 (let ((shader
280 ((?extension create-shader-object-arb create-shader) (first type-source))))
281 ((?extension attach-object-arb attach-shader) program shader)
282 shader))
283 shader-type-sources)))
284 (setf (gethash program-name *synchronizing-shader-programs*)
285 (list program (mapcar #'(lambda (shader type-source)
286 (list shader (second type-source) 0))
287 shaders
288 shader-type-sources)))))
289 (let ((program-spec (gethash program-name *synchronizing-shader-programs*)))
290 ;; compile out-of-date shaders
291 (when (some #'(lambda (shader-entry)
292 (destructuring-bind (shader source last-time) shader-entry
293 (let ((now-time (file-write-date source)))
294 (when (> now-time last-time)
295 (with-open-file (in source :direction :input)
296 ((?extension shader-source-from-stream-arb shader-source-from-stream) shader in))
297 ((?extension gl:compile-shader-arb gl:compile-shader) shader)
298 (handler-case
299 ((?extension check-compiled-shader-arb check-compiled-shader) shader)
300 (shader-object-error (e)
301 (let ((*print-escape* nil))
302 (print-object e *error-output*))))
303 (setf (third shader-entry) now-time)
304 t))))
305 (second program-spec))
306 ;; re-link if anything was out of date
307 ((?extension gl:link-program-arb gl:link-program) (first program-spec))
308 (handler-case
309 ((?extension check-linked-program-arb check-linked-program) (first program-spec))
310 (shader-object-error (e)
311 (let ((*print-escape* nil))
312 (print-object e *error-output*)))))
314 ;; check link status: return or fallback
315 (if (cffi:with-foreign-object (link-status 'gl:int)
316 ((?extension gl:get-object-parameter-iv-arb gl:get-program-iv)
317 (first program-spec)
318 (?extension gl:+object-link-status-arb+ gl:+link-status+)
319 link-status)
320 (eql (cffi:mem-ref link-status 'gl:int) gl:+true+))
321 (first program-spec)
322 ((?extension fallback-synchronizing-program-arb fallback-synchronizing-program)))))
324 (defmacro (?extension with-use-program-arb with-use-program) (name &body forms)
325 "Executes forms using the shader program named. And cleanly use no-program afterwards."
326 (let ((current-program (gensym "CURRENT-PROGRAM-")))
327 `(let ((,current-program (?extension
328 (gl:get-handle-arb gl:+program-object-arb+)
329 (cffi:with-foreign-object (current-program-v 'gl:int)
330 (gl:get-integerv gl:+current-program+ current-program-v)
331 (cffi:mem-ref current-program-v 'gl:int)))))
332 ((?extension gl:use-program-object-arb gl:use-program) ,name)
333 (unwind-protect (progn ,@forms)
334 ((?extension gl:use-program-object-arb gl:use-program) ,current-program))))))
337 (defun clear-synchronizing-shaders ()
338 (makunbound '#:*synchronizing-shader-programs*))
343 (export '(with-new-list with-push-name with-begin with-push-attrib with-push-matrix with-setup-projection with-projection-matrix
344 ;; 1.1
345 with-push-client-attrib
346 ;; ARB_vertex_buffer_object
347 with-map-buffer-arb with-bind-buffer-arb
348 ;; 1.5
349 with-begin-query
350 with-map-buffer with-bind-buffer
351 ;; ARB_vertex_shader/ARB_fragment_shader/ARB_shader_objects
352 shader-source-from-stream-arb check-compiled-shader-arb make-shader-arb synchronizing-shader-arb
353 check-linked-program-arb make-program-arb
354 with-use-program-arb synchronizing-program-arb
355 fallback-synchronizing-program-arb
356 *fallback-synchronizing-program-arb*
357 ;; 2.0
358 shader-source-from-stream check-compiled-shader make-shader synchronizing-shader
359 check-linked-program make-program
360 with-use-program synchronizing-program
361 fallback-synchronizing-program
362 *fallback-synchronizing-program*
364 clear-synchronizing-shaders))