render: sprite: Revamp rendering process.
authorDavid Thompson <dthompson2@worcester.edu>
Mon, 7 Jan 2019 03:50:45 +0000 (22:50 -0500)
committerDavid Thompson <dthompson2@worcester.edu>
Mon, 7 Jan 2019 03:55:37 +0000 (22:55 -0500)
Add support for tinting and simplify batch rendering.

chickadee/render/sprite.scm
doc/api.texi

index c9a777f..6b6cf2a 100644 (file)
@@ -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))))
 
 \f
 ;;;
@@ -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))))
index b1881bd..fc1f8dd 100644 (file)
@@ -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