Initial commit
This commit is contained in:
commit
b94e6113b0
|
@ -0,0 +1 @@
|
|||
compiled/
|
|
@ -0,0 +1,336 @@
|
|||
#lang racket/gui
|
||||
|
||||
(provide (struct-out window)
|
||||
(struct-out frame-event)
|
||||
(struct-out key-event)
|
||||
(struct-out scene)
|
||||
(except-out (struct-out sprite) sprite)
|
||||
(rename-out [sprite <sprite>] [make-sprite sprite])
|
||||
update-scene
|
||||
update-sprites
|
||||
2d-world)
|
||||
|
||||
(require data/order)
|
||||
(require data/splay-tree)
|
||||
(require data/queue)
|
||||
(require sgl/gl)
|
||||
(require sgl/gl-vectors)
|
||||
|
||||
(require (prefix-in image: 2htdp/image))
|
||||
(require (prefix-in pict: pict))
|
||||
|
||||
(require prospect)
|
||||
(require prospect/route)
|
||||
(require prospect/ground)
|
||||
|
||||
(require "texture.rkt")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Shared state maintained by world. Describes current window dimensions.
|
||||
(struct window (width height) #:transparent)
|
||||
|
||||
;; Message sent by world. Describes frame about to be rendered.
|
||||
(struct frame-event (counter timestamp) #:transparent)
|
||||
|
||||
;; Message sent by world. Describes a key event. Key is a sealed
|
||||
;; key-event%.
|
||||
(struct key-event (code key) #:transparent)
|
||||
|
||||
;; Shared state maintained by program. Prelude and postlude are to be
|
||||
;; sealed instruction lists. It is an error to have more than exactly
|
||||
;; one active such record at a given time.
|
||||
(struct scene (prelude postlude) #:transparent)
|
||||
|
||||
;; Shared state maintained by program. Z is to be a number, negative
|
||||
;; toward camera. Instructions to be a sealed instruction list.
|
||||
(struct sprite (z instructions) #:transparent)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define (update-scene prelude postlude)
|
||||
(patch-seq (retract (scene ? ?) #:meta-level 1)
|
||||
(assert (scene (seal prelude) (seal postlude)) #:meta-level 1)))
|
||||
|
||||
(define (make-sprite z instructions)
|
||||
(sprite z (seal instructions)))
|
||||
|
||||
(define (update-sprites . ss)
|
||||
(patch-seq* (cons (retract (sprite ? ?) #:meta-level 1)
|
||||
(map (lambda (s) (assert s #:meta-level 1)) ss))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(struct compiled-instructions (render-thunk resources))
|
||||
|
||||
(define-namespace-anchor ns-anchor)
|
||||
(define ns (namespace-anchor->namespace ns-anchor))
|
||||
|
||||
(define (compile-instructions instrs)
|
||||
(define-values (code resources) (instruction->racket-code `(begin ,@instrs)))
|
||||
(define render-thunk (eval `(lambda () ,code) ns))
|
||||
(compiled-instructions render-thunk resources))
|
||||
|
||||
(define (compiled-instructions-dispose! i)
|
||||
(when i
|
||||
(for [(resource (compiled-instructions-resources i))]
|
||||
(send resource dispose))))
|
||||
|
||||
(define (instructions->racket-code instrs)
|
||||
(define-values (code-rev resources)
|
||||
(for/fold [(code-rev '()) (resources '())] [(instr (in-list instrs))]
|
||||
(define-values (new-code new-resources) (instruction->racket-code instr))
|
||||
(values (cons new-code code-rev) (append new-resources resources))))
|
||||
(values (reverse code-rev) resources))
|
||||
|
||||
(define (color-number? n)
|
||||
(and (number? n)
|
||||
(<= 0.0 n 1.0)))
|
||||
|
||||
(define (instruction->racket-code instr)
|
||||
(match instr
|
||||
[`(rotate ,(? number? deg))
|
||||
(values `(glRotated ,deg) '())]
|
||||
[`(scale ,(? number? x) ,(? number? y))
|
||||
(values `(glScaled ,x ,y 1) '())]
|
||||
[`(translate ,(? number? x) ,(? number? y))
|
||||
(values `(glTranslated ,x ,y 0) '())]
|
||||
[`(color ,(? color-number? r) ,(? color-number? g) ,(? color-number? b) ,(? color-number? a))
|
||||
(values `(glColor4d ,r ,g ,b ,a) '())]
|
||||
[`(texture ,i)
|
||||
(define tex (image->texture i))
|
||||
(values `(draw-gl-face ,tex) (list tex))]
|
||||
[`(push-matrix ,instr ...)
|
||||
(define-values (code resources) (instructions->racket-code instr))
|
||||
(values `(begin (glPushMatrix) ,@code (glPopMatrix)) resources)]
|
||||
[`(begin ,instr ...)
|
||||
(define-values (code resources) (instructions->racket-code instr))
|
||||
(values `(begin ,@code (void)) resources)]
|
||||
[other
|
||||
(error 'instruction->racket-code "unknown render instruction: ~v" other)]))
|
||||
|
||||
(define (image->bitmap i)
|
||||
(cond
|
||||
[(image:image? i)
|
||||
(define w (image:image-width i))
|
||||
(define h (image:image-height i))
|
||||
(define bm (make-object bitmap% w h #f #t))
|
||||
(define dc (send bm make-dc))
|
||||
(send i draw dc
|
||||
0 0
|
||||
0 0
|
||||
w h
|
||||
0 0
|
||||
#f)
|
||||
bm]
|
||||
[(pict:pict? i)
|
||||
(pict:pict->bitmap i)]
|
||||
[(is-a? i bitmap%)
|
||||
i]
|
||||
[else
|
||||
(error 'image->bitmap "unsupported image type ~v" i)]))
|
||||
|
||||
(define (image->texture i)
|
||||
(define bm (image->bitmap i))
|
||||
(new texture% [bitmap bm]))
|
||||
|
||||
;; (define (lerp a b v) (+ (* v a) (* (- 1 v) b)))
|
||||
|
||||
(define (draw-gl-face texture)
|
||||
(send texture bind-texture)
|
||||
(glBegin GL_QUADS)
|
||||
(glNormal3d 0 0 -1)
|
||||
(glTexCoord2i 0 0)
|
||||
(glVertex3d 0 0 0)
|
||||
(glTexCoord2i 1 0)
|
||||
(glVertex3d 1 0 0)
|
||||
(glTexCoord2i 1 1)
|
||||
(glVertex3d 1 1 0)
|
||||
(glTexCoord2i 0 1)
|
||||
(glVertex3d 0 1 0)
|
||||
(glEnd))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define empty-instructions (compile-instructions '()))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define scene-projection (compile-projection (?! (scene ? ?))))
|
||||
(define sprite-projection (compile-projection (?! (sprite ? ?))))
|
||||
|
||||
(define sprite-order
|
||||
(order 'sprite-order
|
||||
sprite?
|
||||
(lambda (a b) (and (= (sprite-z a) (sprite-z b))
|
||||
(eq? (sprite-instructions a)
|
||||
(sprite-instructions b))))
|
||||
(lambda (a b) (or (> (sprite-z a) (sprite-z b))
|
||||
(and (= (sprite-z a) (sprite-z b))
|
||||
(< (eq-hash-code (sprite-instructions a))
|
||||
(eq-hash-code (sprite-instructions b))))))))
|
||||
|
||||
(define (remove-sprite! sprites s)
|
||||
(compiled-instructions-dispose! (splay-tree-ref sprites s #f))
|
||||
(splay-tree-remove! sprites s))
|
||||
|
||||
(define (add-sprite! sprites s)
|
||||
(define instrs `((color 1 1 1 1)
|
||||
(push-matrix ,@(seal-contents (sprite-instructions s)))))
|
||||
(define i (compile-instructions instrs))
|
||||
(splay-tree-set! sprites s i))
|
||||
|
||||
(define (render-scene! prelude sprites postlude)
|
||||
((compiled-instructions-render-thunk prelude))
|
||||
(let loop ((iter (splay-tree-iterate-first sprites)))
|
||||
(when iter
|
||||
((compiled-instructions-render-thunk (splay-tree-iterate-value sprites iter)))
|
||||
(loop (splay-tree-iterate-next sprites iter))))
|
||||
((compiled-instructions-render-thunk postlude)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define world-canvas%
|
||||
(class canvas%
|
||||
(inherit refresh with-gl-context swap-gl-buffers)
|
||||
|
||||
(init boot-actions)
|
||||
|
||||
(define counter 0)
|
||||
(define start-time (current-inexact-milliseconds))
|
||||
(define/public (sim-time)
|
||||
(- (current-inexact-milliseconds) start-time))
|
||||
|
||||
(define initialised? #f)
|
||||
|
||||
(define near-depth 10) ;; 2.5D
|
||||
(define far-depth 15) ;; 2.5D
|
||||
|
||||
(define prelude empty-instructions)
|
||||
(define sprites (make-splay-tree sprite-order))
|
||||
(define postlude empty-instructions)
|
||||
(define fullscreen? #f)
|
||||
|
||||
(define world (make-world boot-actions))
|
||||
(define event-queue (make-queue))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define (inject-event! e)
|
||||
(enqueue! event-queue e))
|
||||
|
||||
(define (deliver-event e)
|
||||
(clean-transition (world-handle-event e world)))
|
||||
|
||||
(define (quiesce!)
|
||||
(let loop ((txn #f) (need-poll? #t))
|
||||
(match txn
|
||||
[#f ;; inert
|
||||
(if (queue-empty? event-queue)
|
||||
(when need-poll? (loop (deliver-event #f) #f))
|
||||
(loop (deliver-event (dequeue! event-queue)) #t))]
|
||||
[(transition new-world actions)
|
||||
(set! world new-world)
|
||||
(for-each process-action! actions)
|
||||
(loop #f #t)])))
|
||||
|
||||
(define (process-action! a)
|
||||
(match a
|
||||
[(? patch? p)
|
||||
(process-scene-updates! p)
|
||||
(process-sprite-updates! p)
|
||||
(process-stop-requests! p)
|
||||
(process-fullscreen-requests! p)]
|
||||
[(message body) (void)]))
|
||||
|
||||
(define (process-scene-updates! p)
|
||||
(define-values (added removed) (patch-project/set/single p scene-projection))
|
||||
(when (not (set-empty? removed))
|
||||
(set! prelude empty-instructions)
|
||||
(set! postlude empty-instructions))
|
||||
(for [(s added)]
|
||||
(match-define (scene (seal pre) (seal post)) s)
|
||||
(set! prelude (compile-instructions pre))
|
||||
(set! post (compile-instructions post))))
|
||||
|
||||
(define (process-sprite-updates! p)
|
||||
(define-values (added removed) (patch-project/set/single p sprite-projection))
|
||||
;; Remove old sprites first, to recycle their texture identifiers (if any)
|
||||
(for [(s removed)] (remove-sprite! sprites s))
|
||||
(for [(s added)] (add-sprite! sprites s)))
|
||||
|
||||
(define (process-stop-requests! p)
|
||||
(when (matcher-match-value (patch-added p) 'stop #f)
|
||||
(send (send this get-top-level-window) show #f)))
|
||||
|
||||
(define (process-fullscreen-requests! p)
|
||||
(define changed? #f)
|
||||
(when (matcher-match-value (patch-removed p) 'fullscreen #f)
|
||||
(set! changed? #t)
|
||||
(set! fullscreen? #f))
|
||||
(when (matcher-match-value (patch-added p) 'fullscreen #f)
|
||||
(set! changed? #t)
|
||||
(set! fullscreen? #t))
|
||||
(when changed?
|
||||
(send (send this get-top-level-window) fullscreen fullscreen?)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(define/override (on-paint)
|
||||
(with-gl-context
|
||||
(lambda ()
|
||||
(inject-event! (message (frame-event counter (sim-time))))
|
||||
(set! counter (+ counter 1))
|
||||
(quiesce!)
|
||||
(unless initialised?
|
||||
(glBlendFunc GL_ONE GL_ONE_MINUS_SRC_ALPHA) ;; premultiplied
|
||||
(glEnable GL_BLEND)
|
||||
(glEnable GL_TEXTURE_2D)
|
||||
(glClearColor 0 0 0 1)
|
||||
(set! initialised? #t))
|
||||
(glClear GL_COLOR_BUFFER_BIT)
|
||||
(glLoadIdentity)
|
||||
(glTranslated 0 0 (- near-depth))
|
||||
(render-scene! prelude sprites postlude)
|
||||
(glFlush)
|
||||
(swap-gl-buffers)))
|
||||
(queue-callback (lambda () (refresh)) #f))
|
||||
|
||||
(define/override (on-size width height)
|
||||
(with-gl-context
|
||||
(lambda ()
|
||||
(inject-event! (patch-seq (retract (window ? ?))
|
||||
(assert (window width height))))
|
||||
(quiesce!)
|
||||
(glViewport 0 0 width height)
|
||||
(glMatrixMode GL_PROJECTION)
|
||||
(glLoadIdentity)
|
||||
(glOrtho 0 width height 0 0.1 100)
|
||||
(glMatrixMode GL_MODELVIEW)
|
||||
(glLoadIdentity)))
|
||||
(refresh))
|
||||
|
||||
(define/override (on-char key)
|
||||
(with-gl-context
|
||||
(lambda ()
|
||||
(inject-event! (message (key-event (send key get-key-code) (seal key))))
|
||||
(quiesce!))))
|
||||
|
||||
(super-new (style '(gl no-autoclear)))))
|
||||
|
||||
(define (2d-world #:width [width #f]
|
||||
#:height [height #f]
|
||||
. boot-actions)
|
||||
(define frame (new frame%
|
||||
[style '(fullscreen-button)]
|
||||
[label "prospect-gl"]
|
||||
[width (or width 640)]
|
||||
[height (or height 480)]))
|
||||
(define c (new world-canvas%
|
||||
[parent frame]
|
||||
[boot-actions boot-actions]))
|
||||
(unless (send (send (send c get-dc) get-gl-context) ok?)
|
||||
(error '2d-world "OpenGL context failed to initialize"))
|
||||
(send c focus)
|
||||
(send frame show #t)
|
||||
(yield 'wait))
|
|
@ -0,0 +1,41 @@
|
|||
#lang racket
|
||||
|
||||
(require prospect)
|
||||
(require 2htdp/image)
|
||||
(require "../2d.rkt")
|
||||
|
||||
(define window-projection (compile-projection (at-meta (?! (window ? ?)))))
|
||||
|
||||
(2d-world (spawn (lambda (e s)
|
||||
(match e
|
||||
[(? patch? p)
|
||||
(define-values (added removed) (patch-project/set/single p window-projection))
|
||||
(transition s (for/list [(w added)]
|
||||
(match-define (window width height) w)
|
||||
(update-scene `((push-matrix (scale ,width ,height)
|
||||
(texture ,(rectangle 1 1 "solid" "white"))))
|
||||
`())))]
|
||||
[_ #f]))
|
||||
(void)
|
||||
(sub (window ? ?) #:meta-level 1)
|
||||
;; (assert 'fullscreen #:meta-level 1)
|
||||
)
|
||||
(spawn (lambda (e s) #f)
|
||||
(void)
|
||||
(update-sprites (sprite 0 `((translate 50 50)
|
||||
(scale 50 50)
|
||||
(texture ,(circle 50 "solid" "orange"))
|
||||
))
|
||||
(sprite -1 `((translate 60 60)
|
||||
(scale 50 50)
|
||||
(texture ,(circle 50 "solid" "green"))
|
||||
))))
|
||||
(spawn (lambda (e s)
|
||||
(match e
|
||||
[(message _)
|
||||
(transition s (assert 'stop #:meta-level 1))]
|
||||
[_ #f]))
|
||||
(void)
|
||||
(sub (key-event #\q ?) #:meta-level 1))
|
||||
)
|
||||
(exit 0)
|
|
@ -0,0 +1,49 @@
|
|||
#lang racket/gui
|
||||
|
||||
(provide texture%)
|
||||
|
||||
(require sgl/gl)
|
||||
(require sgl/gl-vectors)
|
||||
|
||||
(define texture%
|
||||
(class object%
|
||||
(init [(initial-bitmap bitmap)])
|
||||
(field [width 0]
|
||||
[height 0]
|
||||
[textures #f])
|
||||
|
||||
(define/public (get-width) width)
|
||||
(define/public (get-height) height)
|
||||
|
||||
(define/public (bind-texture)
|
||||
(when (not textures) (error 'bind-texture "Attempt to use disposed texture%"))
|
||||
(glBindTexture GL_TEXTURE_2D (gl-vector-ref textures 0)))
|
||||
|
||||
(define/public (load-from-bitmap! bitmap)
|
||||
(when textures (dispose))
|
||||
(set! textures (glGenTextures 1))
|
||||
(bind-texture)
|
||||
(define image-data
|
||||
(let ()
|
||||
(set! width (send bitmap get-width))
|
||||
(set! height (send bitmap get-height))
|
||||
(define dc (new bitmap-dc% [bitmap bitmap]))
|
||||
(define pixels (* width height))
|
||||
(define vec (make-gl-ubyte-vector (* pixels 4)))
|
||||
(define data (make-bytes (* pixels 4)))
|
||||
(send dc get-argb-pixels 0 0 width height data #f #t) ;; premultiplied
|
||||
(for ((i (in-range pixels)))
|
||||
(for ((j (in-range 4)))
|
||||
(gl-vector-set! vec (+ (* i 4) j) (bytes-ref data (+ (* i 4) (- 3 j))))))
|
||||
vec))
|
||||
(glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_LINEAR)
|
||||
(glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER GL_LINEAR)
|
||||
(glTexImage2D GL_TEXTURE_2D 0 4 width height 0 GL_BGRA GL_UNSIGNED_BYTE image-data))
|
||||
|
||||
(define/public (dispose)
|
||||
(when textures
|
||||
(glDeleteTextures textures)
|
||||
(set! textures #f)))
|
||||
|
||||
(super-new)
|
||||
(load-from-bitmap! initial-bitmap)))
|
Loading…
Reference in New Issue