summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Thompson <dthompson2@worcester.edu>2019-01-06 22:50:45 -0500
committerDavid Thompson <dthompson2@worcester.edu>2019-01-06 22:55:37 -0500
commita9671d07fd97dd4fe010647c84bc2e6a87d6687c (patch)
treecef79b9ec73768cdbe8051093487b9f4b22e71e8
parente6f37f4d005229264ee25e4424553776d8e0f0d8 (diff)
render: sprite: Revamp rendering process.
Add support for tinting and simplify batch rendering.
-rw-r--r--chickadee/render/sprite.scm264
-rw-r--r--doc/api.texi13
2 files changed, 152 insertions, 125 deletions
diff --git a/chickadee/render/sprite.scm b/chickadee/render/sprite.scm
index c9a777f..6b6cf2a 100644
--- a/chickadee/render/sprite.scm
+++ b/chickadee/render/sprite.scm
@@ -24,6 +24,7 @@
#:use-module (chickadee math rect)
#:use-module (chickadee math vector)
#:use-module (chickadee render)
+ #:use-module (chickadee render color)
#:use-module (chickadee render shader)
#:use-module (chickadee render texture)
#:use-module (chickadee render buffer)
@@ -33,7 +34,7 @@
draw-nine-patch*
draw-nine-patch))
-(define default-shader
+(define unbatched-sprite-shader
(delay
(strings->shader
"
@@ -54,9 +55,10 @@ void main(void) {
in vec2 fragTex;
uniform sampler2D colorTexture;
+uniform vec4 tint;
void main (void) {
- gl_FragColor = texture2D(colorTexture, fragTex);
+ gl_FragColor = texture2D(colorTexture, fragTex) * tint;
}
")))
@@ -95,7 +97,7 @@ void main (void) {
`((0 . ,(force pos))
(1 . ,(force tex))))))
(mvp (make-null-matrix4)))
- (lambda (texture region world-matrix blend-mode shader texture-region)
+ (lambda (texture region world-matrix blend-mode texture-region tint)
(with-mapped-typed-buffer (force pos)
(let* ((x1 (rect-x region))
(y1 (rect-y region))
@@ -126,7 +128,8 @@ void main (void) {
(f32vector-set! bv 15 t1)))
(with-blend-mode blend-mode
(with-texture 0 texture
- (gpu-apply shader (force vertex-array)
+ (gpu-apply (force unbatched-sprite-shader) (force vertex-array)
+ #:tint tint
#:mvp (if world-matrix
(begin
(matrix4-mult! mvp world-matrix
@@ -140,12 +143,11 @@ void main (void) {
;;;
(define-record-type <sprite-batch>
- (%make-sprite-batch texture blend-mode shader size capacity index-buffer
+ (%make-sprite-batch texture blend-mode size capacity index-buffer
position-buffer texture-buffer vertex-array)
sprite-batch?
(texture sprite-batch-texture set-sprite-batch-texture!)
(blend-mode sprite-batch-blend-mode set-sprite-batch-blend-mode!)
- (shader sprite-batch-shader set-sprite-batch-shader!)
(size sprite-batch-size set-sprite-batch-size!)
(capacity sprite-batch-capacity set-sprite-batch-capacity!)
(index-buffer sprite-batch-index-buffer set-sprite-batch-index-buffer!)
@@ -158,7 +160,7 @@ void main (void) {
'unsigned-int
(* capacity 6)
#:target 'index))
- (stride 16) ; 4 f32s, 2 for vertex, 2 for texcoord
+ (stride 32) ; 8 f32s, 2 for vertex, 2 for texcoord, 4 for tint color
(buffer (make-buffer #f
#:name "sprite batch buffer"
#:length (* capacity stride 4)
@@ -174,9 +176,17 @@ void main (void) {
#:type 'vec2
#:component-type 'float
#:length (* capacity 4)
- #:offset (/ stride 2)))
+ #:offset 8))
+ (tint (make-typed-buffer #:name "batched-sprite-tint"
+ #:buffer buffer
+ #:type 'vec4
+ #:component-type 'float
+ #:length (* capacity 4)
+ #:offset 16))
(va (make-vertex-array #:indices index
- #:attributes `((0 . ,pos) (1 . ,tex)))))
+ #:attributes `((0 . ,pos)
+ (1 . ,tex)
+ (2 . ,tint)))))
(set-sprite-batch-capacity! batch capacity)
(set-sprite-batch-index-buffer! batch index)
(set-sprite-batch-position-buffer! batch pos)
@@ -185,7 +195,7 @@ void main (void) {
(define (make-sprite-batch capacity)
"Make a sprite batch that can hold CAPACITY sprites."
- (let ((batch (%make-sprite-batch #f #f #f 0 0 #f #f #f #f)))
+ (let ((batch (%make-sprite-batch #f #f 0 0 #f #f #f #f)))
(init-sprite-batch batch capacity)
batch))
@@ -214,13 +224,43 @@ void main (void) {
"Reset BATCH to size 0."
(set-sprite-batch-texture! batch #f)
(set-sprite-batch-blend-mode! batch #f)
- (set-sprite-batch-shader! batch #f)
(set-sprite-batch-size! batch 0))
(define (sprite-batch-begin! batch)
(map-typed-buffer! (sprite-batch-index-buffer batch))
(map-typed-buffer! (sprite-batch-position-buffer batch)))
+(define batched-sprite-shader
+ (delay
+ (strings->shader
+ "
+#version 130
+
+in vec2 position;
+in vec2 tex;
+in vec4 tint;
+out vec2 fragTex;
+out vec4 fragTint;
+uniform mat4 mvp;
+
+void main(void) {
+ fragTex = tex;
+ fragTint = tint;
+ gl_Position = mvp * vec4(position.xy, 0.0, 1.0);
+}
+"
+ "
+#version 130
+
+in vec2 fragTex;
+in vec4 fragTint;
+uniform sampler2D colorTexture;
+
+void main (void) {
+ gl_FragColor = texture2D(colorTexture, fragTex) * fragTint;
+}
+")))
+
(define (sprite-batch-flush! batch)
"Render the contents of BATCH and clear the cache."
(unless (zero? (sprite-batch-size batch))
@@ -228,103 +268,95 @@ void main (void) {
(with-texture 0 (sprite-batch-texture batch)
(unmap-typed-buffer! (sprite-batch-index-buffer batch))
(unmap-typed-buffer! (sprite-batch-position-buffer batch))
- (gpu-apply* (sprite-batch-shader batch)
+ (gpu-apply* (force batched-sprite-shader)
(sprite-batch-vertex-array batch)
(* (sprite-batch-size batch) 6)
#:mvp (current-projection))
(sprite-batch-reset! batch)))))
-(define sprite-batch-add!
- (let ((world1 (vec2 0.0 0.0))
- (world2 (vec2 0.0 0.0))
- (world3 (vec2 0.0 0.0))
- (world4 (vec2 0.0 0.0))
- (offset-bv (make-u32vector 1)))
- (define (set-offset offset)
- (u32vector-set! offset-bv 0 offset))
- (define (offset)
- (u32vector-ref offset-bv 0))
- (lambda (batch texture region world-matrix blend-mode
- shader texture-region)
- ;; Expand the buffers when necessary.
- (when (sprite-batch-full? batch)
- (double-sprite-batch-size! batch))
- ;; Flush the batch if any GL state needs changing.
- (unless (and (eq? (sprite-batch-texture batch) texture)
- (eq? (sprite-batch-blend-mode batch) blend-mode)
- (eq? (sprite-batch-shader batch) shader))
- (sprite-batch-flush! batch)
- (sprite-batch-begin! batch)
- (set-sprite-batch-texture! batch texture)
- (set-sprite-batch-blend-mode! batch blend-mode)
- (set-sprite-batch-shader! batch shader))
- (let ((size (sprite-batch-size batch)))
- (let* ((indices (typed-buffer-data (sprite-batch-index-buffer batch)))
- (vertices (typed-buffer-data (sprite-batch-position-buffer batch)))
- (texcoords (typed-buffer-data (sprite-batch-texture-buffer batch)))
- (local-x1 (rect-x region))
- (local-y1 (rect-y region))
- (local-x2 (+ local-x1 (rect-width region)))
- (local-y2 (+ local-y1 (rect-height region)))
- (s1 (rect-x texture-region))
- (t1 (rect-y texture-region))
- (s2 (+ (rect-x texture-region) (rect-width texture-region)))
- (t2 (+ (rect-y texture-region) (rect-height texture-region))))
- (set-vec2! world1 local-x1 local-y1)
- (set-vec2! world2 local-x2 local-y1)
- (set-vec2! world3 local-x2 local-y2)
- (set-vec2! world4 local-x1 local-y2)
- (when world-matrix
- (transform! world-matrix world1)
- (transform! world-matrix world2)
- (transform! world-matrix world3)
- (transform! world-matrix world4))
- ;; Add indices.
- (set-offset (* size 4))
- (let ((index-vertex-offset (offset)))
- (set-offset (* size 6))
- (u32vector-set! indices (offset) index-vertex-offset)
- (u32vector-set! indices (+ (offset) 1) (+ index-vertex-offset 3))
- (u32vector-set! indices (+ (offset) 2) (+ index-vertex-offset 2))
- (u32vector-set! indices (+ (offset) 3) index-vertex-offset)
- (u32vector-set! indices (+ (offset) 4) (+ index-vertex-offset 2))
- (u32vector-set! indices (+ (offset) 5) (+ index-vertex-offset 1)))
- ;; Add vertices.
- (set-offset (* size 16))
- ;; Bottom-left
- (f32vector-set! vertices (offset) (vec2-x world1))
- (f32vector-set! vertices (+ (offset) 1) (vec2-y world1))
- ;; Bottom-right
- (f32vector-set! vertices (+ (offset) 4) (vec2-x world2))
- (f32vector-set! vertices (+ (offset) 5) (vec2-y world2))
- ;; Top-right
- (f32vector-set! vertices (+ (offset) 8) (vec2-x world3))
- (f32vector-set! vertices (+ (offset) 9) (vec2-y world3))
- ;; Top-left
- (f32vector-set! vertices (+ (offset) 12) (vec2-x world4))
- (f32vector-set! vertices (+ (offset) 13) (vec2-y world4))
- ;; Add texture coordinates.
- ;; Bottom-left
- (f32vector-set! texcoords (+ (offset) 2) s1)
- (f32vector-set! texcoords (+ (offset) 3) t2)
- ;; Bottom-right
- (f32vector-set! texcoords (+ (offset) 6) s2)
- (f32vector-set! texcoords (+ (offset) 7) t2)
- ;; Top-right
- (f32vector-set! texcoords (+ (offset) 10) s2)
- (f32vector-set! texcoords (+ (offset) 11) t1)
- ;; Top-left
- (f32vector-set! texcoords (+ (offset) 14) s1)
- (f32vector-set! texcoords (+ (offset) 15) t1)
- (set-sprite-batch-size! batch (1+ size)))))))
+(define (sprite-batch-add! batch texture region world-matrix blend-mode
+ texture-region tint)
+ ;; Expand the buffers when necessary.
+ (when (sprite-batch-full? batch)
+ (double-sprite-batch-size! batch))
+ ;; Flush the batch if any GL state needs changing.
+ (unless (and (eq? (sprite-batch-texture batch) texture)
+ (eq? (sprite-batch-blend-mode batch) blend-mode))
+ (sprite-batch-flush! batch)
+ (sprite-batch-begin! batch)
+ (set-sprite-batch-texture! batch texture)
+ (set-sprite-batch-blend-mode! batch blend-mode))
+ (let ((size (sprite-batch-size batch)))
+ (let* ((indices (typed-buffer-data (sprite-batch-index-buffer batch)))
+ (vertices (typed-buffer-data (sprite-batch-position-buffer batch)))
+ (index-offset (* size 6))
+ (offset (* size 32))
+ (minx (rect-x region))
+ (miny (rect-y region))
+ (maxx (+ minx (rect-width region)))
+ (maxy (+ miny (rect-height region)))
+ (x1 (transform-x world-matrix minx miny))
+ (y1 (transform-y world-matrix minx miny))
+ (x2 (transform-x world-matrix maxx miny))
+ (y2 (transform-y world-matrix maxx miny))
+ (x3 (transform-x world-matrix maxx maxy))
+ (y3 (transform-y world-matrix maxx maxy))
+ (x4 (transform-x world-matrix minx maxy))
+ (y4 (transform-y world-matrix minx maxy))
+ (s1 (rect-x texture-region))
+ (t1 (rect-y texture-region))
+ (s2 (+ (rect-x texture-region) (rect-width texture-region)))
+ (t2 (+ (rect-y texture-region) (rect-height texture-region))))
+ ;; Add indices.
+ (let ((index-vertex-offset (* size 4)))
+ (u32vector-set! indices index-offset index-vertex-offset)
+ (u32vector-set! indices (+ index-offset 1) (+ index-vertex-offset 3))
+ (u32vector-set! indices (+ index-offset 2) (+ index-vertex-offset 2))
+ (u32vector-set! indices (+ index-offset 3) index-vertex-offset)
+ (u32vector-set! indices (+ index-offset 4) (+ index-vertex-offset 2))
+ (u32vector-set! indices (+ index-offset 5) (+ index-vertex-offset 1)))
+ ;; Add vertices.
+ ;; Bottom-left
+ (f32vector-set! vertices offset x1)
+ (f32vector-set! vertices (+ offset 1) y1)
+ ;; Bottom-right
+ (f32vector-set! vertices (+ offset 8) x2)
+ (f32vector-set! vertices (+ offset 9) y2)
+ ;; Top-right
+ (f32vector-set! vertices (+ offset 16) x3)
+ (f32vector-set! vertices (+ offset 17) y3)
+ ;; Top-left
+ (f32vector-set! vertices (+ offset 24) x4)
+ (f32vector-set! vertices (+ offset 25) y4)
+ ;; Add texture coordinates.
+ ;; Bottom-left
+ (f32vector-set! vertices (+ offset 2) s1)
+ (f32vector-set! vertices (+ offset 3) t2)
+ ;; Bottom-right
+ (f32vector-set! vertices (+ offset 10) s2)
+ (f32vector-set! vertices (+ offset 11) t2)
+ ;; Top-right
+ (f32vector-set! vertices (+ offset 18) s2)
+ (f32vector-set! vertices (+ offset 19) t1)
+ ;; Top-left
+ (f32vector-set! vertices (+ offset 26) s1)
+ (f32vector-set! vertices (+ offset 27) t1)
+ ;; Add tint.
+ (let ((bv ((@@ (chickadee render color) unwrap-color) tint))
+ (byte-offset (* offset 4)))
+ (bytevector-copy! bv 0 vertices (+ byte-offset 16) 16)
+ (bytevector-copy! bv 0 vertices (+ byte-offset 48) 16)
+ (bytevector-copy! bv 0 vertices (+ byte-offset 80) 16)
+ (bytevector-copy! bv 0 vertices (+ byte-offset 112) 16))
+ (set-sprite-batch-size! batch (1+ size)))))
(define *batch?* #f)
(define %batch (delay (make-sprite-batch 256)))
-(define (draw-sprite-batched texture region world-matrix blend-mode shader
- texture-region)
+(define (draw-sprite-batched texture region world-matrix blend-mode
+ texture-region tint)
(sprite-batch-add! (force %batch) texture region world-matrix blend-mode
- shader texture-region))
+ texture-region tint))
(define-syntax-rule (with-batched-sprites body ...)
"Use batched rendering for all draw-sprite calls within BODY."
@@ -341,14 +373,14 @@ void main (void) {
(set! *batch?* #f)))))
(define* (draw-sprite* texture rect matrix #:key
+ (tint white)
(blend-mode 'alpha)
- (texcoords (texture-gl-tex-rect texture))
- (shader (force default-shader)))
+ (texcoords (texture-gl-tex-rect texture)))
(if *batch?*
(draw-sprite-batched texture rect matrix blend-mode
- shader texcoords)
+ texcoords tint)
(draw-sprite-unbatched texture rect matrix blend-mode
- shader texcoords)))
+ texcoords tint)))
(define %null-vec2 (vec2 0.0 0.0))
(define %default-scale (vec2 1.0 1.0))
@@ -358,12 +390,12 @@ void main (void) {
(lambda* (texture
position
#:key
+ (tint white)
(origin %null-vec2)
(scale %default-scale)
(rotation 0.0)
(blend-mode 'alpha)
- (rect (texture-gl-rect texture))
- (shader (force default-shader)))
+ (rect (texture-gl-rect texture)))
"Draw TEXTURE at POSITION.
Optionally, other transformations may be applied to the sprite.
@@ -371,19 +403,19 @@ ROTATION specifies the angle to rotate the sprite, in radians. SCALE
specifies the scaling factor as a 2D vector. All transformations are
applied relative to ORIGIN, a 2D vector.
-By default, alpha blending is used but can be changed by specifying
-BLEND-MODE.
+TINT specifies the color to multiply against all the sprite's pixels.
+By default white is used, which does no tinting at all.
-Advanced users may pass SHADER to change the way the sprite is
-rendered entirely."
+By default, alpha blending is used but can be changed by specifying
+BLEND-MODE."
(matrix4-2d-transform! matrix
#:origin origin
#:position position
#:rotation rotation
#:scale scale)
(draw-sprite* texture rect matrix
- #:blend-mode blend-mode
- #:shader shader))))
+ #:tint tint
+ #:blend-mode blend-mode))))
;;;
@@ -402,8 +434,7 @@ rendered entirely."
(bottom-margin margin)
(left-margin margin)
(right-margin margin)
- (blend-mode 'alpha)
- (shader (force default-shader)))
+ (blend-mode 'alpha))
(let* ((x (rect-x rect))
(y (rect-y rect))
(w (rect-width rect))
@@ -439,8 +470,7 @@ rendered entirely."
(set-rect-height! texcoords (- t2 t1))
(draw-sprite* texture %rect matrix
#:texcoords texcoords
- #:blend-mode blend-mode
- #:shader shader))
+ #:blend-mode blend-mode))
(with-batched-sprites
;; bottom-left
(draw-piece border-x1 border-y1 fill-x1 fill-y1
@@ -483,8 +513,7 @@ rendered entirely."
(origin %null-vec2)
(rotation 0.0)
(scale %default-scale)
- (blend-mode 'alpha)
- (shader (force default-shader)))
+ (blend-mode 'alpha))
"Draw a \"nine patch\" sprite. A nine patch sprite renders
TEXTURE on the rectangular area RECT whose stretchable areas are
defined by the given margin measurements. The corners are never
@@ -513,5 +542,4 @@ LEFT-MARGIN, and RIGHT-MARGIN arguments may be used."
#:bottom-margin bottom-margin
#:left-margin left-margin
#:right-margin right-margin
- #:blend-mode blend-mode
- #:shader shader))))
+ #:blend-mode blend-mode))))
diff --git a/doc/api.texi b/doc/api.texi
index b1881bd..fc1f8dd 100644
--- a/doc/api.texi
+++ b/doc/api.texi
@@ -1334,8 +1334,8 @@ stored in textures (@pxref{Textures}) and can be used to draw sprites
via the @code{draw-sprite} procedure.
@deffn {Procedure} draw-sprite @var{texture} @var{position} @
- [#:origin] [#:scale] [#:rotation] [#:blend-mode alpha] @
- [#:rect] [#:shader]
+ [#:tint white] [#:origin] [#:scale] [#:rotation] [#:blend-mode alpha] @
+ [#:rect]
Draw @var{texture} at @var{position}.
@@ -1345,14 +1345,14 @@ Optionally, other transformations may be applied to the sprite.
transformations are applied relative to @var{origin}, a 2D vector,
which defaults to the lower-left corner.
+@var{tint} specifies the color to multiply against all the sprite's
+pixels. By default white is used, which does no tinting at all.
+
Alpha blending is used by default but the blending method can be
changed by specifying @var{blend-mode}.
The area drawn to is as big as the texture, by default. To draw to an
arbitrary section of the screen, specify @var{rect}.
-
-Finally, advanced users may specify @var{shader} to change the way the
-sprite is rendered entirely.
@end deffn
It's not uncommon to need to draw hundreds or thousands of sprites
@@ -1411,8 +1411,7 @@ artifacts.
@deffn {Procedure} draw-nine-patch @var{texture} @var{rect} @
[#:margin 0] [#:top-margin margin] [#:bottom-margin margin] @
[#:left-margin margin] [#:right-margin margin] @
- [#:origin] [#:scale] [#:rotation] [#:blend-mode alpha] @
- [#:shader]
+ [#:origin] [#:scale] [#:rotation] [#:blend-mode alpha]
Draw a nine patch sprite. A nine patch sprite renders @var{texture}
as a @var{width} x @var{height} rectangle whose stretchable areas are