update for begin-mode -> primitive-type
[clinton/guile-figl.git] / examples / particle-system / vbo.scm
CommitLineData
c64c0f81
AW
1#!/usr/bin/env guile
2!#
3
62c85b51
DH
4;;; figl
5;;; Copyright (C) 2013 Andy Wingo <wingo@pobox.com>
6;;;
7;;; Figl is free software: you can redistribute it and/or modify it
8;;; under the terms of the GNU Lesser General Public License as
9;;; published by the Free Software Foundation, either version 3 of the
10;;; License, or (at your option) any later version.
11;;;
12;;; Figl is distributed in the hope that it will be useful, but WITHOUT
13;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
15;;; Public License for more details.
16;;;
17;;; You should have received a copy of the GNU Lesser General Public
18;;; License along with this program. If not, see
19;;; <http://www.gnu.org/licenses/>.
20
c64c0f81
AW
21(use-modules (figl glut)
22 (figl gl)
23 (figl glu)
24 (ice-9 match)
25 (ice-9 format)
26 (figl contrib packed-struct))
27
28(define-packed-struct color-vertex
29 (x float)
30 (y float)
31 (z float)
32
33 (r float)
34 (g float)
35 (b float))
36
37(define-packed-struct particle
38 (x float)
39 (y float)
40 (z float)
41 (vx float)
42 (vy float)
43 (vz float))
44
45(define *vertices* (make-packed-array color-vertex 0))
46(define *particles* (make-packed-array particle 0))
47
48;; Vertex buffer object.
49(define *vbo* 0)
50
51(define (draw-particles)
52 (gl-bind-buffer (version-1-5 array-buffer) *vbo*)
53
54 (update-gl-buffer-data (version-1-5 array-buffer)
55 *vertices*)
56
57 (gl-enable-client-state (enable-cap vertex-array))
58 (gl-enable-client-state (enable-cap color-array))
59 (set-gl-vertex-array (vertex-pointer-type float)
60 #f
61 #:stride (packed-struct-size color-vertex)
62 #:offset (packed-struct-offset color-vertex x))
63 (set-gl-color-array (color-pointer-type float)
64 #f
65 #:stride (packed-struct-size color-vertex)
66 #:offset (packed-struct-offset color-vertex r))
54ead4dd 67 (gl-draw-arrays (primitive-type quads) 0
c64c0f81
AW
68 (packed-array-length *vertices* color-vertex))
69 (gl-disable-client-state (enable-cap color-array))
70 (gl-disable-client-state (enable-cap vertex-array))
71
72 (gl-bind-buffer (version-1-5 array-buffer) 0))
73
74(define (update-quads)
75 (unpack-each
76 *particles*
77 particle
78 (lambda (n x y z vx vy vz)
79 (let ((r (/ (abs vx) 5))
80 (g (/ (abs vy) 5))
81 (b (/ (abs vz) 5))
82 (x- (- x 0.5))
83 (y- (- y 0.5))
84 (x+ (+ x 0.5))
85 (y+ (+ y 0.5))
86 (base (* n 4)))
87 (pack *vertices* base color-vertex
88 x- y- z
89 r g b)
90 (pack *vertices* (+ base 1) color-vertex
91 x+ y- z
92 r g b)
93 (pack *vertices* (+ base 2) color-vertex
94 x+ y+ z
95 r g b)
96 (pack *vertices* (+ base 3) color-vertex
97 x- y+ z
98 r g b)))))
99
100(define (update-particles dt)
101 (let ((half-dt-squared (* 0.5 dt dt)))
102 (repack-each
103 *particles*
104 particle
105 (lambda (n x y z vx vy vz)
106 (let* ((distance-squared (+ (* x x) (* y y) (* z z)))
107 (distance (sqrt distance-squared))
108 (F (/ -200 distance-squared))
109 (ax (* F (/ x distance)))
110 (ay (* F (/ y distance)))
111 (az (* F (/ z distance))))
112 (values (+ x (* vx dt) (* ax half-dt-squared))
113 (+ y (* vy dt) (* ay half-dt-squared))
114 (+ z (* vz dt) (* az half-dt-squared))
115 (+ vx (* ax dt))
116 (+ vy (* ay dt))
117 (+ vz (* az dt))))))))
118
119(define (prepare-particles n)
120 (set! *particles* (make-packed-array particle n))
121 (set! *vertices* (make-packed-array color-vertex (* n 4)))
122
123 (pack-each
124 *particles*
125 particle
126 (lambda (n)
127 (values
128 ;; Position.
129 (* (random:normal) 30)
130 (* (random:normal) 30)
131 (* (random:normal) 30)
132
133 ;; Velocity.
134 (* (random:normal) 2)
135 (* (random:normal) 2)
136 (* (random:normal) 2))))
137
138 (update-quads)
139
140 (gl-delete-buffer *vbo*) ; no-op if 0
141 (set! *vbo* (gl-generate-buffer))
142
143 (gl-bind-buffer (version-1-5 array-buffer) *vbo*)
144 (set-gl-buffer-data (version-1-5 array-buffer)
145 *vertices*
146 (version-1-5 stream-draw))
147 (gl-bind-buffer (version-1-5 array-buffer) 0))
148
149(define main-window #f)
150
151(define (make-fps-accumulator period)
152 (let* ((frame-count 0)
153 (last-fps-time (get-internal-real-time))
154 (last-fps-run-time (get-internal-run-time))
155 (last-frame-time (get-internal-real-time))
156 (max-frame-time (get-internal-real-time))
157 (last-frame-count 0)
158 (jiffies-per-sec (exact->inexact internal-time-units-per-second))
159 (jiffies-per-ms (/ jiffies-per-sec 1000)))
160 (lambda ()
161 (let ((now (get-internal-real-time)))
162 (set! frame-count (1+ frame-count))
163 (when (> (- now last-frame-time) max-frame-time)
164 (set! max-frame-time (- now last-frame-time)))
165 (set! last-frame-time now)
166 (when (> (- now last-fps-time) period)
167 (let* ((run (get-internal-run-time))
168 (frames (- frame-count last-frame-count))
169 (secs (/ (- now last-fps-time) jiffies-per-sec))
170 (fps (/ frames secs))
171 (ms-per-frame (/ (* secs 1000) frames))
172 (max-ms-per-frame (/ max-frame-time jiffies-per-ms))
173 (cpu (* 100.0 (/ (- run last-fps-run-time)
174 (- now last-fps-time)))))
175 (format
176 (current-error-port)
177 ";;; ~a frames in ~,2f sec = ~,2f fps; ~,2f ms/frame, ~,2f ms max; ~,2f% CPU\n"
178 frames secs fps ms-per-frame max-ms-per-frame cpu)
179 (set! last-frame-count frame-count)
180 (set! max-frame-time 0)
181 (set! last-fps-time now)
182 (set! last-fps-run-time run)))))))
183
184(define accumulate-fps!
185 (make-fps-accumulator (* 2 internal-time-units-per-second)))
186
187(define (draw-axis scale)
188 ;; Could disable lighting and depth test.
54ead4dd 189 (gl-begin (primitive-type lines)
c64c0f81
AW
190 (gl-color 1 0 0)
191 (gl-vertex 0 0 0)
192 (gl-vertex scale 0 0)
193
194 (gl-color 0 1 0)
195 (gl-vertex 0 0 0)
196 (gl-vertex 0 scale 0)
197
198 (gl-color 0 0 1)
199 (gl-vertex 0 0 0)
200 (gl-vertex 0 0 scale)))
201
202(define (on-display)
203 (accumulate-fps!)
204 (gl-clear (clear-buffer-mask color-buffer depth-buffer))
205
206 (draw-axis 10)
207
208 (draw-particles)
209
210 ;; With double-buffering, swap-buffers will wait for the frame to be shown,
211 ;; which limits this program to the frame rate.
212 (swap-buffers))
213
214(define (on-reshape width height)
215 (pk 'reshape! width height)
216 (gl-viewport 0 0 width height)
217
218 ;; Projection matrix.
219 (set-gl-matrix-mode (matrix-mode projection))
220 (gl-load-identity)
221 (glu-perspective 60 (/ width height) 0.1 1000))
222
223(define full-screen? #f)
224(define running? #t)
225
226(define (on-keyboard keycode x y)
227 (let ((c (integer->char keycode)))
228 (case c
229 ((#\f)
230 (set! full-screen? (not full-screen?))
231 (full-screen main-window full-screen?))
232 ((#\esc #\etx #\q)
233 (format #t "~s pressed; quitting.\n" c)
234 (exit))
235 ((#\+)
236 ;; The rotations are a hack to re-orient so that a translation in the Z
237 ;; axis will move us towards the origin.
238 (gl-rotate -10 1 0 0)
239 (gl-translate 0 0 10)
240 (gl-rotate 10 1 0 0))
241 ((#\-)
242 (gl-rotate -10 1 0 0)
243 (gl-translate 0 0 -10)
244 (gl-rotate 10 1 0 0))
245 ((#\space)
246 (set! running? (not running?))
247 (set-idle-callback (and running? (lambda () (on-idle)))))
248 (else
249 (pk 'keyboard c x y)))))
250
251(define (on-special keycode x y)
252 (pk 'special keycode x y))
253
254(define (on-mouse button state x y)
255 (pk 'mouse button state x y))
256
257(define (on-motion x y)
258 (pk 'motion x y))
259
260(define (on-visibility visible?)
261 (pk 'visible visible?))
262
263(define (on-idle)
264 (set-gl-matrix-mode (matrix-mode modelview))
265 ;; Rotate the camera 0.05 degree per frame about the Z axis. At 60 fps that's
266 ;; 120 seconds per full rotation. Rotating about the Z axis keeps all the
267 ;; quads facing our way.
268 (gl-rotate 0.05 0 0 1)
269 (update-particles 0.016)
270 (update-quads)
271 (post-redisplay main-window))
272
273(define (register-glut-callbacks)
274 ;; The trampolines allow the handlers to be overridden at runtime by
275 ;; an attached Guile REPL client.
276 (set-display-callback (lambda () (on-display)))
277 (set-reshape-callback (lambda (w h) (on-reshape w h)))
278 (set-keyboard-callback (lambda (k x y) (on-keyboard k x y)))
279 (set-special-callback (lambda (k x y) (on-special k x y)))
280 (set-mouse-callback (lambda (b s x y) (on-mouse b s x y)))
281 (set-motion-callback (lambda (x y) (on-motion x y)))
282 (set-visibility-callback (lambda (visible?) (on-visibility visible?)))
283 (set-idle-callback (lambda () (on-idle))))
284
285(define (main args)
286 (let ((args
287 (initialize-glut args
288 #:window-size '(640 . 480)
289 #:display-mode (display-mode rgba alpha double
290 depth))))
291 (set! main-window (make-window "particle-system"))
292 (register-glut-callbacks)
293 (set-gl-clear-color 0 0 0 1)
294 (set-gl-clear-depth 1)
295 #;
296 (set-gl-shade-model (shading-model smooth))
297
298 ;; Resetting the modelview matrix mode leaves the camera at the origin,
299 ;; oriented to look down the Z axis. Rotate up a bit so that we can see the
300 ;; Z axis.
301 (set-gl-matrix-mode (matrix-mode modelview))
302 (gl-load-identity)
303 (gl-translate 0 0 -100)
304 (gl-rotate 10 1 0 0)
305
306 (gl-enable (enable-cap depth-test))
307
308 (set! *random-state* (random-state-from-platform))
309 (prepare-particles (match args
310 ((_) 1000)
311 ((_ n) (string->number n))))
312
313 (glut-main-loop)))
314
315(when (batch-mode?)
316 (exit (main (program-arguments))))