140ec665a9f15df3047c90019c7f08f6402d1fdc
[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 as well so that
88 ;; the user doesn't have to explicitly make a viewport unless
89 ;; they actually want to do fancy viewport manipulations.
90 (with-viewport (framebuffer-viewport framebuffer)
91 body ...)))
92
93 (define-syntax-rule (with-blend-mode blend-mode body ...)
94 (with (*current-blend-mode* blend-mode) body ...))
95
96 (define-syntax-rule (with-depth-test depth-test body ...)
97 (with (*current-depth-test* depth-test) body ...))
98
99 (define-syntax-rule (with-texture n texture body ...)
100 (let ((prev (vector-ref *current-textures* n)))
101 (dynamic-wind
102 (lambda () (vector-set! *current-textures* n texture))
103 (lambda () body ...)
104 (lambda () (vector-set! *current-textures* n prev)))))
105
106 (define-syntax-rule (with-shader shader body ...)
107 (with (*current-shader* shader)
108 (initialize-uniforms)
109 body ...))
110
111 (define-syntax-rule (with-vertex-array vertex-array body ...)
112 (with (*current-vertex-array* vertex-array) body ...))
113
114 (define-syntax-rule (with-projection matrix body ...)
115 (with (*current-projection* matrix) body ...))
116
117 ;; (define (initialize-uniforms)
118 ;; (hash-for-each (lambda (name uniform)
119 ;; (unless (hash-get-handle *current-uniforms* name)
120 ;; (hash-set! *current-uniforms* name
121 ;; (uniform-default-value uniform))))
122 ;; (shader-uniforms *current-shader*)))
123
124 ;; (define-syntax uniform-let
125 ;; (syntax-rules ()
126 ;; ((_ () body ...) (begin body ...))
127 ;; ((_ ((name value) . rest) body ...)
128 ;; (let ((uniform (shader-uniform (current-shader) name))
129 ;; (prev (hash-ref *current-uniforms* name)))
130 ;; (if uniform
131 ;; (dynamic-wind
132 ;; (lambda ()
133 ;; (hash-set! *current-uniforms* name value))
134 ;; (lambda ()
135 ;; (uniform-let rest body ...))
136 ;; (lambda ()
137 ;; (hash-set! *current-uniforms* name prev)))
138 ;; (error "no such uniform: " name))))))
139
140 ;; (define (uniform-ref name)
141 ;; (uniform-value (shader-uniform (current-shader) name)))
142
143 (define (keyword->string kw)
144 (symbol->string (keyword->symbol kw)))
145
146 (define-syntax uniform-apply
147 (lambda (x)
148 (syntax-case x ()
149 ((_ shader ()) (datum->syntax x #t))
150 ((_ shader (name value . rest))
151 (with-syntax ((sname (datum->syntax x (keyword->string
152 (syntax->datum #'name)))))
153 #'(begin
154 (set-uniform-value! (shader-uniform shader sname) value)
155 (uniform-apply shader rest)))))))
156
157 (define-syntax-rule (gpu-prepare shader vertex-array uniforms)
158 (begin
159 ;; It's important that the framebuffer is set before setting the
160 ;; viewport because applying a new viewport will clear the current
161 ;; framebuffer.
162 (gpu-state-set! *framebuffer-state* (current-framebuffer))
163 (gpu-state-set! *viewport-state* (current-viewport))
164 (gpu-state-set! *blend-mode-state* (current-blend-mode))
165 (gpu-state-set! *depth-test-state* (current-depth-test))
166 (gpu-state-set! *shader-state* shader)
167 (let loop ((i 0))
168 (when (< i 32)
169 (texture-set! i (current-texture i))
170 (loop (1+ i))))
171 (uniform-apply shader uniforms)
172 (hash-for-each (lambda (name uniform)
173 (when (eq? 'sampler-2d (uniform-type uniform))
174 (set-uniform-value! uniform (uniform-value uniform))))
175 (shader-uniforms shader))))
176
177 (define-syntax-rule (gpu-apply* shader vertex-array count . uniforms)
178 (begin
179 (gpu-prepare shader vertex-array uniforms)
180 (render-vertices vertex-array count)))
181
182 (define-syntax-rule (gpu-apply shader vertex-array uniforms ...)
183 (gpu-apply* shader vertex-array #f uniforms ...))
184
185 (define-syntax-rule (gpu-apply/instanced* shader vertex-array count instances .
186 uniforms)
187 (begin
188 (gpu-prepare shader vertex-array uniforms)
189 (render-vertices/instanced vertex-array instances count)))
190
191 (define-syntax-rule (gpu-apply/instanced shader vertex-array instances
192 uniforms ...)
193 (gpu-apply/instanced* shader vertex-array #f instances uniforms ...))