render: framebuffer: Add default projection matrix for convenience.
[chickadee.git] / chickadee / render.scm
1 ;;; Chickadee Game Toolkit
2 ;;; Copyright © 2016 David Thompson <davet@gnu.org>
3 ;;;
4 ;;; Chickadee is free software: you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published
6 ;;; by the Free Software Foundation, either version 3 of the License,
7 ;;; or (at your option) any later version.
8 ;;;
9 ;;; Chickadee is distributed in the hope that it will be useful, but
10 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ;;; General Public License for more details.
13 ;;;
14 ;;; You should have received a copy of the GNU General Public License
15 ;;; along with this program. If not, see
16 ;;; <http://www.gnu.org/licenses/>.
17
18 ;;; Commentary:
19 ;;
20 ;; High-level rendering API.
21 ;;
22 ;;; Code:
23
24 (define-module (chickadee render)
25 #:use-module (chickadee math matrix)
26 #:use-module (chickadee render gpu)
27 #:use-module (chickadee render blend)
28 #:use-module (chickadee render framebuffer)
29 #:use-module (chickadee render shader)
30 #:use-module (chickadee render texture)
31 #:use-module (chickadee render buffer)
32 #:use-module (chickadee render viewport)
33 #:export (current-viewport
34 current-framebuffer
35 current-blend-mode
36 current-depth-test
37 current-texture
38 current-projection
39 with-viewport
40 with-framebuffer
41 with-blend-mode
42 with-depth-test
43 with-texture
44 with-projection
45 gpu-apply
46 gpu-apply*
47 gpu-apply/instanced*
48 gpu-apply/instanced))
49
50 (define *current-viewport* null-viewport)
51 (define *current-framebuffer* null-framebuffer)
52 (define *current-blend-mode* 'replace)
53 (define *current-depth-test* #f)
54 (define *current-projection* (make-identity-matrix4))
55 (define *current-textures* (make-vector 32 null-texture))
56
57 (define (current-viewport)
58 *current-viewport*)
59
60 (define (current-framebuffer)
61 *current-framebuffer*)
62
63 (define (current-blend-mode)
64 *current-blend-mode*)
65
66 (define (current-depth-test)
67 *current-depth-test*)
68
69 (define (current-texture i)
70 (vector-ref *current-textures* i))
71
72 (define (current-projection)
73 *current-projection*)
74
75 (define-syntax-rule (with (name value) body ...)
76 (let ((prev name))
77 (dynamic-wind
78 (lambda () (set! name value))
79 (lambda () body ...)
80 (lambda () (set! name prev)))))
81
82 (define-syntax-rule (with-viewport viewport body ...)
83 (with (*current-viewport* viewport) body ...))
84
85 (define-syntax-rule (with-framebuffer framebuffer body ...)
86 (with (*current-framebuffer* framebuffer)
87 ;; As a convenience, initialize the viewport and projection
88 ;; matrix as well so that the user doesn't have to explicitly
89 ;; make a viewport and/or projection matrix unless they
90 ;; actually want to do fancy viewport manipulations.
91 (with-viewport (framebuffer-viewport framebuffer)
92 (with-projection (framebuffer-projection framebuffer)
93 body ...))))
94
95 (define-syntax-rule (with-blend-mode blend-mode body ...)
96 (with (*current-blend-mode* blend-mode) body ...))
97
98 (define-syntax-rule (with-depth-test depth-test body ...)
99 (with (*current-depth-test* depth-test) body ...))
100
101 (define-syntax-rule (with-texture n texture body ...)
102 (let ((prev (vector-ref *current-textures* n)))
103 (dynamic-wind
104 (lambda () (vector-set! *current-textures* n texture))
105 (lambda () body ...)
106 (lambda () (vector-set! *current-textures* n prev)))))
107
108 (define-syntax-rule (with-shader shader body ...)
109 (with (*current-shader* shader)
110 (initialize-uniforms)
111 body ...))
112
113 (define-syntax-rule (with-vertex-array vertex-array body ...)
114 (with (*current-vertex-array* vertex-array) body ...))
115
116 (define-syntax-rule (with-projection matrix body ...)
117 (with (*current-projection* matrix) body ...))
118
119 ;; (define (initialize-uniforms)
120 ;; (hash-for-each (lambda (name uniform)
121 ;; (unless (hash-get-handle *current-uniforms* name)
122 ;; (hash-set! *current-uniforms* name
123 ;; (uniform-default-value uniform))))
124 ;; (shader-uniforms *current-shader*)))
125
126 ;; (define-syntax uniform-let
127 ;; (syntax-rules ()
128 ;; ((_ () body ...) (begin body ...))
129 ;; ((_ ((name value) . rest) body ...)
130 ;; (let ((uniform (shader-uniform (current-shader) name))
131 ;; (prev (hash-ref *current-uniforms* name)))
132 ;; (if uniform
133 ;; (dynamic-wind
134 ;; (lambda ()
135 ;; (hash-set! *current-uniforms* name value))
136 ;; (lambda ()
137 ;; (uniform-let rest body ...))
138 ;; (lambda ()
139 ;; (hash-set! *current-uniforms* name prev)))
140 ;; (error "no such uniform: " name))))))
141
142 ;; (define (uniform-ref name)
143 ;; (uniform-value (shader-uniform (current-shader) name)))
144
145 (define (keyword->string kw)
146 (symbol->string (keyword->symbol kw)))
147
148 (define-syntax uniform-apply
149 (lambda (x)
150 (syntax-case x ()
151 ((_ shader ()) (datum->syntax x #t))
152 ((_ shader (name value . rest))
153 (with-syntax ((sname (datum->syntax x (keyword->string
154 (syntax->datum #'name)))))
155 #'(begin
156 (set-uniform-value! (shader-uniform shader sname) value)
157 (uniform-apply shader rest)))))))
158
159 (define-syntax-rule (gpu-prepare shader vertex-array uniforms)
160 (begin
161 ;; It's important that the framebuffer is set before setting the
162 ;; viewport because applying a new viewport will clear the current
163 ;; framebuffer.
164 (gpu-state-set! *framebuffer-state* (current-framebuffer))
165 (gpu-state-set! *viewport-state* (current-viewport))
166 (gpu-state-set! *blend-mode-state* (current-blend-mode))
167 (gpu-state-set! *depth-test-state* (current-depth-test))
168 (gpu-state-set! *shader-state* shader)
169 (let loop ((i 0))
170 (when (< i 32)
171 (texture-set! i (current-texture i))
172 (loop (1+ i))))
173 (uniform-apply shader uniforms)
174 (hash-for-each (lambda (name uniform)
175 (when (eq? 'sampler-2d (uniform-type uniform))
176 (set-uniform-value! uniform (uniform-value uniform))))
177 (shader-uniforms shader))))
178
179 (define-syntax-rule (gpu-apply* shader vertex-array count . uniforms)
180 (begin
181 (gpu-prepare shader vertex-array uniforms)
182 (render-vertices vertex-array count)))
183
184 (define-syntax-rule (gpu-apply shader vertex-array uniforms ...)
185 (gpu-apply* shader vertex-array #f uniforms ...))
186
187 (define-syntax-rule (gpu-apply/instanced* shader vertex-array count instances .
188 uniforms)
189 (begin
190 (gpu-prepare shader vertex-array uniforms)
191 (render-vertices/instanced vertex-array instances count)))
192
193 (define-syntax-rule (gpu-apply/instanced shader vertex-array instances
194 uniforms ...)
195 (gpu-apply/instanced* shader vertex-array #f instances uniforms ...))