From cf1b09a12f39767c1cdf2b9cee81c396dc8b52cd Mon Sep 17 00:00:00 2001 From: David Thompson Date: Fri, 5 Jan 2018 15:56:26 -0500 Subject: render: sprite: Allow passing custom transformation matrices. * chickadee/render/sprite.scm (draw-sprite-unbatched, draw-sprite-batched): Replace 'scale' and 'rotation' arguments with a 'matrix' argument. (sprite-batch-add!): Ditto. Also, correctly transform sprite vertices. This was previously not handling rotations properly. (%default-offset): New variable. (draw-sprite, draw-nine-patch): Change default values for 'rotation' and 'scale'. Add 'matrix' and 'offset' arguments. * chickadee/render/font.scm (%default-offset): New variable. (draw-text): Change default values for 'rotation' and 'scale'. Add 'offset' and 'matrix' arguments. --- chickadee/render/font.scm | 11 +- chickadee/render/sprite.scm | 298 +++++++++++++++++++++++++------------------- 2 files changed, 180 insertions(+), 129 deletions(-) diff --git a/chickadee/render/font.scm b/chickadee/render/font.scm index 31670ad..bac5056 100644 --- a/chickadee/render/font.scm +++ b/chickadee/render/font.scm @@ -295,10 +295,17 @@ extension must be either .xml or .fnt." (define (font-ref font char) (hashv-ref (font-chars font) char)) +(define %default-offset (vec2 0.0 0.0)) + (define draw-text (let ((cursor (vec2 0.0 0.0)) (char-pos (vec2 0.0 0.0))) - (lambda* (font text position #:key scale rotation (blend-mode 'alpha)) + (lambda* (font text position #:key + (rotation 0) + (scale 1.0) + matrix + (offset %default-offset) + (blend-mode 'alpha)) "Draw the string TEXT with the first character starting at POSITION using FONT." ;; TODO: Respect kerning. @@ -310,8 +317,10 @@ POSITION using FONT." (set-vec2-y! char-pos (+ (vec2-y cursor) (vec2-y offset))) (draw-sprite (font-char-texture-region char) char-pos + #:offset offset #:scale scale #:rotation rotation + #:matrix matrix #:blend-mode blend-mode) ;; Move forward to where the next character needs to be drawn. (set-vec2-x! cursor diff --git a/chickadee/render/sprite.scm b/chickadee/render/sprite.scm index b07a969..38b1594 100644 --- a/chickadee/render/sprite.scm +++ b/chickadee/render/sprite.scm @@ -80,15 +80,13 @@ void main (void) { #:attributes `((0 . ,(force position-buffer)) (1 . ,(force texcoord-buffer)))))) - (tmp-matrix (make-null-matrix4)) - (mvp (make-null-matrix4)) - (position (vec2 0 0))) - (lambda (texture region scale rotation blend-mode shader texture-region) + (mvp (make-null-matrix4))) + (lambda (texture region world-matrix blend-mode shader texture-region) (with-mapped-typed-buffer (force position-buffer) - (let* ((x1 0) - (y1 0) - (x2 (rect-width region)) - (y2 (rect-height region)) + (let* ((x1 (rect-x region)) + (y1 (rect-y region)) + (x2 (+ x1 (rect-width region))) + (y2 (+ y1 (rect-height region))) (bv (typed-buffer-data (force position-buffer)))) (f32vector-set! bv 0 x1) (f32vector-set! bv 1 y1) @@ -112,21 +110,15 @@ void main (void) { (f32vector-set! bv 5 t2) (f32vector-set! bv 6 s1) (f32vector-set! bv 7 t2))) - (matrix4-identity! mvp) - (when rotation - (matrix4-rotate-z! tmp-matrix rotation) - (matrix4-mult! mvp mvp tmp-matrix)) - (when scale - (matrix4-scale! tmp-matrix scale) - (matrix4-mult! mvp mvp tmp-matrix)) - (set-vec2-x! position (rect-x region)) - (set-vec2-y! position (rect-y region)) - (matrix4-translate! tmp-matrix position) - (matrix4-mult! mvp mvp tmp-matrix) - (matrix4-mult! mvp mvp (current-projection)) (with-blend-mode blend-mode (with-texture 0 texture - (gpu-apply shader (force vertex-array) #:mvp mvp)))))) + (gpu-apply shader (force vertex-array) + #:mvp (if world-matrix + (begin + (matrix4-mult! mvp world-matrix + (current-projection)) + mvp) + (current-projection)))))))) ;;; @@ -223,17 +215,16 @@ void main (void) { (sprite-batch-reset! batch))))) (define sprite-batch-add! - (let ((tmp-matrix (make-null-matrix4)) - (matrix (make-null-matrix4)) - (position (vec2 0.0 0.0)) - (world1 (vec2 0.0 0.0)) + (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 scale rotation blend-mode + (lambda (batch texture region world-matrix blend-mode shader texture-region) ;; Expand the buffers when necessary. (when (sprite-batch-full? batch) @@ -251,93 +242,74 @@ void main (void) { (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))) - (rx (rect-x region)) - (ry (rect-y region)) - (local-x1 0.0) - (local-y1 0.0) - (local-x2 (rect-width region)) - (local-y2 (rect-height region)) + (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-left texture-region)) (t1 (rect-bottom texture-region)) (s2 (rect-right texture-region)) (t2 (rect-top texture-region))) - (if (or rotation scale) - (begin - (matrix4-identity! matrix) - (when rotation - (matrix4-rotate-z! tmp-matrix rotation) - (matrix4-mult! matrix matrix tmp-matrix)) - (when scale - (matrix4-scale! tmp-matrix scale) - (matrix4-mult! matrix matrix tmp-matrix)) - (set-vec2-x! position rx) - (set-vec2-y! position ry) - (matrix4-translate! tmp-matrix position) - (matrix4-mult! matrix matrix tmp-matrix) - (set-vec2-x! world1 local-x1) - (set-vec2-y! world1 local-y1) - (set-vec2-x! world2 local-x2) - (set-vec2-y! world2 local-y2) - (transform! matrix world1) - (transform! matrix world2)) - (begin - (set-vec2-x! world1 (+ local-x1 rx)) - (set-vec2-y! world1 (+ local-y1 ry)) - (set-vec2-x! world2 (+ local-x2 rx)) - (set-vec2-y! world2 (+ local-y2 ry)))) - (let ((world-x1 (vec2-x world1)) - (world-y1 (vec2-y world1)) - (world-x2 (vec2-x world2)) - (world-y2 (vec2-y world2))) - ;; 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 8)) ;; 4 vertices, 2 floats per vertex - ;; Bottom-left - (f32vector-set! vertices (offset) world-x1) - (f32vector-set! vertices (+ (offset) 1) world-y1) - ;; Bottom-right - (f32vector-set! vertices (+ (offset) 2) world-x2) - (f32vector-set! vertices (+ (offset) 3) world-y1) - ;; Top-right - (f32vector-set! vertices (+ (offset) 4) world-x2) - (f32vector-set! vertices (+ (offset) 5) world-y2) - ;; Top-left - (f32vector-set! vertices (+ (offset) 6) world-x1) - (f32vector-set! vertices (+ (offset) 7) world-y2) - ;; Add texture coordinates. - (set-offset (* size 8)) - ;; Bottom-left - (f32vector-set! texcoords (offset) s1) - (f32vector-set! texcoords (+ (offset) 1) t1) - ;; Bottom-right - (f32vector-set! texcoords (+ (offset) 2) s2) - (f32vector-set! texcoords (+ (offset) 3) t1) - ;; Top-right - (f32vector-set! texcoords (+ (offset) 4) s2) - (f32vector-set! texcoords (+ (offset) 5) t2) - ;; Top-left - (f32vector-set! texcoords (+ (offset) 6) s1) - (f32vector-set! texcoords (+ (offset) 7) t2) - (set-sprite-batch-size! batch (1+ size)))))))) + (set-vec2-x! world1 local-x1) + (set-vec2-y! world1 local-y1) + (set-vec2-x! world2 local-x2) + (set-vec2-y! world2 local-y1) + (set-vec2-x! world3 local-x2) + (set-vec2-y! world3 local-y2) + (set-vec2-x! world4 local-x1) + (set-vec2-y! world4 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 8)) ;; 4 vertices, 2 floats per vertex + ;; Bottom-left + (f32vector-set! vertices (offset) (vec2-x world1)) + (f32vector-set! vertices (+ (offset) 1) (vec2-y world1)) + ;; Bottom-right + (f32vector-set! vertices (+ (offset) 2) (vec2-x world2)) + (f32vector-set! vertices (+ (offset) 3) (vec2-y world2)) + ;; Top-right + (f32vector-set! vertices (+ (offset) 4) (vec2-x world3)) + (f32vector-set! vertices (+ (offset) 5) (vec2-y world3)) + ;; Top-left + (f32vector-set! vertices (+ (offset) 6) (vec2-x world4)) + (f32vector-set! vertices (+ (offset) 7) (vec2-y world4)) + ;; Add texture coordinates. + (set-offset (* size 8)) + ;; Bottom-left + (f32vector-set! texcoords (offset) s1) + (f32vector-set! texcoords (+ (offset) 1) t1) + ;; Bottom-right + (f32vector-set! texcoords (+ (offset) 2) s2) + (f32vector-set! texcoords (+ (offset) 3) t1) + ;; Top-right + (f32vector-set! texcoords (+ (offset) 4) s2) + (f32vector-set! texcoords (+ (offset) 5) t2) + ;; Top-left + (f32vector-set! texcoords (+ (offset) 6) s1) + (f32vector-set! texcoords (+ (offset) 7) t2) + (set-sprite-batch-size! batch (1+ size))))))) (define *batch?* #f) (define %batch (delay (make-sprite-batch 256))) -(define (draw-sprite-batched texture region - scale rotation blend-mode shader +(define (draw-sprite-batched texture region world-matrix blend-mode shader texture-region) - (sprite-batch-add! (force %batch) texture region - scale rotation blend-mode shader - texture-region)) + (sprite-batch-add! (force %batch) texture region world-matrix blend-mode + shader texture-region)) (define-syntax-rule (with-batched-sprites body ...) "Use batched rendering for all draw-sprite calls within BODY." @@ -361,22 +333,40 @@ void main (void) { (@@ (chickadee render texture) texture-region-gl-rect)) (define %default-texcoords (make-rect 0.0 0.0 1.0 1.0)) +(define %default-offset (vec2 0.0 0.0)) (define draw-sprite - (let ((rect (make-rect 0.0 0.0 0.0 0.0))) - (lambda* (texture region #:key - scale rotation (blend-mode 'alpha) - (texcoords - (if (texture-region? texture) - (texture-region-gl-rect texture) - %default-texcoords)) - (shader (force default-shader))) + (let ((rect (make-rect 0.0 0.0 0.0 0.0)) + (tmp-matrix (make-null-matrix4)) + (%matrix (make-null-matrix4))) + (lambda* (texture + region + #:key + (scale 1.0) + (rotation 0) + matrix + (offset %default-offset) + (blend-mode 'alpha) + (texcoords + (if (texture-region? texture) + (texture-region-gl-rect texture) + %default-texcoords)) + (shader (force default-shader))) "Draw TEXTURE over the area defined by the rect REGION. Instead of a rect, REGION may be a vec2 representing the position of the sprite, in which case the width and height of the sprite corresponds to the size of the texture. TEXTURE may be a texture or a texture -region. ROTATION specifies by how many radians the sprite will be -rotated. SCALE specifies the scaling factor. By default, alpha +region. + +ROTATION specifies the angle to rotate the sprite, in radians. SCALE +specifies the scaling factor. No scaling or rotation is applied by +default. Alternatively, MATRIX may be specified, in which case +ROTATION and SCALE are ignored and the given transformation matrix is +used instead. All transformations are applied relative to the lower +left corner of the sprite by default. This can be changed by +specifying an OFFSET vector. + +By default, alpha blending is used but can be changed by setting BLEND-MODE. Finally, advanced users may pass SHADER to change the way the sprite is rendered entirely." @@ -386,20 +376,66 @@ rendered entirely." (texture (if (texture-region? texture) (texture-region-texture texture) texture)) - (region (if (rect? region) - region - (begin - (rect-move-vec2! rect region) - (set-rect-width! rect (f32vector-ref size 0)) - (set-rect-height! rect (f32vector-ref size 1)) - rect)))) + (matrix (cond + (matrix matrix) ; user-specified matrix + ((or rotation scale) ; compute matrix on-the-fly + (matrix4-identity! %matrix) + (unless (zero? rotation) + (matrix4-rotate-z! tmp-matrix rotation) + (matrix4-mult! %matrix %matrix tmp-matrix)) + (unless (= scale 1.0) + (matrix4-scale! tmp-matrix scale) + (matrix4-mult! %matrix %matrix tmp-matrix)) + (matrix4-translate! tmp-matrix region) + (matrix4-mult! %matrix %matrix tmp-matrix) + %matrix) + ;; No transformation needed, in which case we + ;; use no matrix at all in order to save cycles + ;; and not waste time multiplying against the + ;; identity matrix. + (else #f)))) + (cond + ((and (rect? region) + (not matrix) + (zero? rotation) ; no rotation + (= scale 1.0)) ; no scale + ;; We won't be using a transformation matrix. + ;; Just apply the offset. + (set-rect-x! rect (- (rect-x region) (vec2-x offset))) + (set-rect-y! rect (- (rect-y region) (vec2-y offset))) + (set-rect-width! rect (rect-width region)) + (set-rect-height! rect (rect-height region))) + ((rect? region) + ;; We will be using a transformation matrix, so + ;; ignore the region's X and Y coordinates as + ;; those will be accounted for in the + ;; translation matrix. + (set-rect-x! rect (- (vec2-x offset))) + (set-rect-y! rect (- (vec2-y offset))) + (set-rect-width! rect (rect-width region)) + (set-rect-height! rect (rect-height region))) + ((and (not matrix) + (zero? rotation) + (= scale 1.0)) + ;; No region specified and no transformation + ;; matrix. Use texture width/height for the + ;; dimensions of the region. + (set-rect-x! rect (- (vec2-x region) (vec2-x offset))) + (set-rect-y! rect (- (vec2-y region) (vec2-y offset))) + (set-rect-width! rect (f32vector-ref size 0)) + (set-rect-height! rect (f32vector-ref size 1))) + (else + ;; No region specified but we will be using a + ;; transformation matrix. + (set-rect-x! rect (- (vec2-x offset))) + (set-rect-y! rect (- (vec2-y offset))) + (set-rect-width! rect (f32vector-ref size 0)) + (set-rect-height! rect (f32vector-ref size 1)))) (if *batch?* - (draw-sprite-batched texture region - scale rotation blend-mode shader - texcoords) - (draw-sprite-unbatched texture region - scale rotation blend-mode shader - texcoords)))))) + (draw-sprite-batched texture rect matrix blend-mode + shader texcoords) + (draw-sprite-unbatched texture rect matrix blend-mode + shader texcoords)))))) ;;; @@ -412,7 +448,11 @@ rendered entirely." (lambda* (texture region #:key (margin 0) (top-margin margin) (bottom-margin margin) (left-margin margin) (right-margin margin) - scale rotation (blend-mode 'alpha) + (offset %default-offset) + (rotation 0) + (scale 1.0) + matrix + (blend-mode 'alpha) (shader (force default-shader))) "Draw a \"nine patch\" sprite. A nine patch sprite renders TEXTURE as a WIDTH x HEIGHT rectangle whose stretchable areas are @@ -463,8 +503,10 @@ LEFT-MARGIN, and RIGHT-MARGIN arguments may be used." (set-rect-height! trect (- t2 t1)) (draw-sprite texture rect #:texcoords trect + #:offset offset #:scale scale #:rotation rotation + #:matrix matrix #:blend-mode blend-mode #:shader shader)) (with-batched-sprites -- cgit v1.2.3