diff options
Diffstat (limited to 'chickadee/graphics/sprite.scm')
-rw-r--r-- | chickadee/graphics/sprite.scm | 681 |
1 files changed, 457 insertions, 224 deletions
diff --git a/chickadee/graphics/sprite.scm b/chickadee/graphics/sprite.scm index 29c1088..60f2066 100644 --- a/chickadee/graphics/sprite.scm +++ b/chickadee/graphics/sprite.scm @@ -1,5 +1,5 @@ ;;; Chickadee Game Toolkit -;;; Copyright © 2016, 2019, 2020, 2021 David Thompson <dthompson2@worcester.edu> +;;; Copyright © 2016, 2019, 2020, 2021, 2024 David Thompson <dthompson2@worcester.edu> ;;; ;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; you may not use this file except in compliance with the License. @@ -13,21 +13,46 @@ ;;; See the License for the specific language governing permissions and ;;; limitations under the License. +;;; Commentary: +;; +;; 2D sprite rendering. +;; +;;; Code: + (define-module (chickadee graphics sprite) + #:use-module (chickadee data bytestruct) + #:use-module (chickadee graphics) + #:use-module (chickadee graphics buffer) + #:use-module (chickadee graphics color) + #:use-module (chickadee graphics pipeline) + #:use-module (chickadee graphics shader) + #:use-module (chickadee graphics texture) + #:use-module (chickadee math matrix) + #:use-module (chickadee math rect) + #:use-module (chickadee math vector) + #:use-module (chickadee utils) + #:use-module (ice-9 format) + #:use-module (ice-9 match) #:use-module (rnrs bytevectors) #:use-module (srfi srfi-4) #:use-module (srfi srfi-9) + #:use-module (srfi srfi-9 gnu) #:use-module (srfi srfi-11) - #:use-module (chickadee math matrix) - #:use-module (chickadee math rect) - #:use-module (chickadee math vector) - #:use-module (chickadee graphics blend) - #:use-module (chickadee graphics color) - #:use-module (chickadee graphics engine) - #:use-module (chickadee graphics shader) - #:use-module (chickadee graphics texture) - #:use-module (chickadee graphics buffer) - #:export (draw-sprite* + #:export (make-sprite + sprite? + sprite-texture-view + sprite-rect + sprite-rect-uv + + list->sprite-sheet + sprite-sheet + tileset + load-tileset + sprite-sheet? + sprite-sheet-length + sprite-sheet-ref + + draw-sprite* draw-sprite make-sprite-batch @@ -35,108 +60,325 @@ sprite-batch-texture set-sprite-batch-texture! sprite-batch-clear! + sprite-batch-end! sprite-batch-add* + sprite-batch-set!* sprite-batch-add! + sprite-batch-set! draw-sprite-batch* - draw-sprite-batch - - with-batched-sprites - draw-nine-patch* - draw-nine-patch)) - -(define-geometry-type <sprite-vertex> - sprite-vertex-ref - sprite-vertex-set! - sprite-vertex-append! - (position vec2) - (texture vec2)) - -(define-graphics-variable sprite-geometry - (make-geometry <sprite-vertex> 4 #:index-capacity 6)) -(define-graphics-variable sprite-model-matrix (make-null-matrix4)) -(define-graphics-variable sprite-mvp-matrix (make-null-matrix4)) -(define-graphics-variable sprite-shader - (strings->shader - " + draw-sprite-batch)) + + +;;; +;;; Sprite sheets +;;; + +(define-record-type <sprite> + (%make-sprite texture-view rect rect-uv) + sprite? + (texture-view sprite-texture-view) + (rect sprite-rect) + (rect-uv sprite-rect-uv)) + +(define (make-sprite texture-view rect) + (unless (texture-view-2d? texture-view) + (error "expected 2D texture view" texture-view)) + (let* ((width (texture-view-width texture-view)) + (height (texture-view-height texture-view)) + (uv (make-rect (/ (rect-x rect) width) + (/ (rect-y rect) height) + (/ (rect-width rect) width) + (/ (rect-height rect) height)))) + (%make-sprite texture-view rect uv))) + +(define-record-type <sprite-sheet> + (%make-sprite-sheet texture-view sprites) + sprite-sheet? + (texture-view sprite-sheet-texture-view) + (sprites sprite-sheet-sprites)) + +(define (print-sprite-sheet sheet port) + (format port + "#<sprite-sheet texture-view: ~a size: ~d>" + (sprite-sheet-texture-view sheet) + (vector-length (sprite-sheet-sprites sheet)))) + +(set-record-type-printer! <sprite-sheet> print-sprite-sheet) + +(define (list->sprite-sheet texture-view rects) + "Return a new sprite sheet containing RECTS, a list of +rectangles describing the various sprites within TEXTURE-VIEW." + (let ((v (make-vector (length rects)))) + (let loop ((i 0) (rects rects)) + (match rects + (() (%make-sprite-sheet texture-view v)) + ((rect . rest) + (vector-set! v i (make-sprite texture-view rect)) + (loop (1+ i) rest)))))) + +(define (sprite-sheet texture-view . rects) + "Return a new sprite sheet containing RECTS, a list of +rectangles describing the various sprites within TEXTURE-VIEW." + (list->sprite-sheet texture-view rects)) + +(define (sprite-sheet-length sprite-sheet) + "Return the number of sprites in the SPRITE-SHEET." + (vector-length (sprite-sheet-sprites sprite-sheet))) + +(define (sprite-sheet-ref sprite-sheet index) + "Return the sprite associated with INDEX in +SPRITE-SHEET." + (vector-ref (sprite-sheet-sprites sprite-sheet) index)) + +(define* (texture-tileset-dimensions texture-view tile-width tile-height + #:key (margin 0) (spacing 0)) + (values (inexact->exact + (ceiling (/ (- (texture-view-width texture-view) margin) + (+ tile-width spacing)))) + (inexact->exact + (ceiling (/ (- (texture-view-height texture-view) margin) + (+ tile-height spacing)))))) + +(define* (tileset texture-view tile-width tile-height + #:key (margin 0) (spacing 0)) + "Return a new sprite sheet that splits TEXTURE-VIEW into a grid of +TILE-WIDTH by TILE-HEIGHT sprites. Optionally, each tile may have +SPACING pixels of horizontal and vertical space between surrounding +tiles and the entire image may have MARGIN pixels of empty space +around its border." + (call-with-values (lambda () + (texture-tileset-dimensions texture-view tile-width tile-height + #:margin margin + #:spacing spacing)) + (lambda (columns rows) + (let ((v (make-vector (* rows columns)))) + (define (make-tile tx ty) + (let* ((x (+ (* tx (+ tile-width spacing)) margin)) + (y (+ (* ty (+ tile-height spacing)) margin))) + (make-sprite texture-view (make-rect x y tile-width tile-height)))) + (for-range ((x columns) + (y rows)) + (vector-set! v (+ x (* y columns)) (make-tile x y))) + (%make-sprite-sheet texture-view v))))) + +(define* (load-tileset file-name tile-width tile-height #:key + (margin 0) + (spacing 0) + transparent-color) + "Return a new texture atlas that splits the texture loaded from the +file FILE-NAME into a grid of TILE-WIDTH by TILE-HEIGHT rectangles. +See load-image and split-texture for information about all keyword +arguments." + (tileset (texture-view + (load-image file-name #:transparent-color transparent-color)) + tile-width + tile-height + #:margin margin + #:spacing spacing)) + + +;;; +;;; Sprite streaming +;;; + +(define-record-type <sprite-state> + (make-sprite-state shader uniforms sampler matrix + color-target-cache bindings rect-cache) + sprite-state? + (shader sprite-state-shader) + (uniforms sprite-state-uniforms) + (sampler sprite-state-sampler) + (matrix sprite-state-matrix) + (color-target-cache sprite-state-color-target-cache) + (bindings sprite-state-bindings) + (rect-cache sprite-state-rect-cache)) + +(define (sprite-color-target state blend-mode) + (let ((cache (sprite-state-color-target-cache state))) + (or (hashq-ref cache blend-mode) + (let ((color-target (make-color-target #:blend-mode blend-mode))) + (hashq-set! cache blend-mode color-target) + color-target)))) + +(define (%sprite-rect state texture) + (let ((cache (sprite-state-rect-cache state))) + (or (hashq-ref cache texture) + (let ((rect (make-rect 0.0 0.0 + (texture-width texture) + (texture-height texture)))) + (hashq-set! cache texture rect) + rect)))) + +(define-bytestruct <sprite-vertex> + (struct (position <vec2>) + (texture <vec2>) + (color <color>))) + +(define-bytestruct <sprite-uniforms> + (struct (mvp <matrix4>))) + +(define %sprite-vertex-layout + (vector (make-vertex-buffer-layout + #:stride (* 8 4) + #:attributes (vector + (make-vertex-attribute + #:format 'float32x2) + (make-vertex-attribute + #:format 'float32x2 + #:offset (* 2 4)) + (make-vertex-attribute + #:format 'float32x4 + #:offset (* 4 4)))))) + +(define %sprite-binding-layout + (vector (make-texture-layout) + (make-sampler-layout) + (make-buffer-layout))) + +(define-graphics-variable sprite-state + (make-sprite-state + (make-shader + (lambda (lang) + (values " #ifdef GLSL330 layout (location = 0) in vec2 position; layout (location = 1) in vec2 tex; +layout (location = 2) in vec4 tint; #elif defined(GLSL130) in vec2 position; in vec2 tex; +in vec4 tint; #elif defined(GLSL120) attribute vec2 position; attribute vec2 tex; +attribute vec4 tint; #endif #ifdef GLSL120 varying vec2 fragTex; +varying vec4 fragTint; #else out vec2 fragTex; +out vec4 fragTint; +#endif + +#ifdef GLSL120 +uniform mat4 matrix; +#else +layout (std140) uniform Sprite +{ + mat4 matrix; +}; #endif -uniform mat4 mvp; void main(void) { fragTex = tex; - gl_Position = mvp * vec4(position.xy, 0.0, 1.0); + fragTint = tint; + gl_Position = matrix * vec4(position.xy, 0.0, 1.0); } " - " + " #ifdef GLSL120 varying vec2 fragTex; +varying vec4 fragTint; #else in vec2 fragTex; +in vec4 fragTint; #endif #ifdef GLSL330 out vec4 fragColor; +#else +#define fragColor gl_FragColor +#define texture texture2D #endif + uniform sampler2D colorTexture; -uniform vec4 tint; void main (void) { -#ifdef GLSL330 - fragColor = texture(colorTexture, fragTex) * tint; -#else - gl_FragColor = texture2D(colorTexture, fragTex) * tint; -#endif + fragColor = texture(colorTexture, fragTex) * fragTint; } ")) + #:name "Sprite shader") + (make-buffer (* 16 4) + #:name "Sprite uniform buffer" + #:usage '(uniform)) + (make-sampler #:name "Sprite sampler") + (make-null-matrix4) + (make-hash-table) + (make-vector 3 #f) + (make-hash-table))) + +(define %default-texcoords (make-rect 0.0 0.0 1.0 1.0)) + +(define-inlinable (sprite-set! vertices voffset indices ioffset i rect texcoords tint matrix) + (let* ((minx (rect-x rect)) + (miny (rect-y rect)) + (maxx (+ minx (rect-width rect))) + (maxy (+ miny (rect-height rect))) + (x1 (matrix4-transform-x matrix minx miny)) + (y1 (matrix4-transform-y matrix minx miny)) + (x2 (matrix4-transform-x matrix maxx miny)) + (y2 (matrix4-transform-y matrix maxx miny)) + (x3 (matrix4-transform-x matrix maxx maxy)) + (y3 (matrix4-transform-y matrix maxx maxy)) + (x4 (matrix4-transform-x matrix minx maxy)) + (y4 (matrix4-transform-y matrix minx maxy)) + (s1 (rect-x texcoords)) + (t1 (rect-y texcoords)) + (s2 (+ s1 (rect-width texcoords))) + (t2 (+ t1 (rect-height texcoords))) + (r (color-r tint)) + (g (color-g tint)) + (b (color-b tint)) + (a (color-a tint))) + (define-syntax-rule (set-vertex! j x* y* u v r* g* b* a*) + (dbuffer-pack! <sprite-vertex> + (((position x) x*) + ((position y) y*) + ((texture x) u) + ((texture y) v) + ((color r) r*) + ((color g) g*) + ((color b) b*) + ((color a) a*)) + vertices + (+ voffset (* j (bytestruct-sizeof <sprite-vertex>))))) + (set-vertex! 0 x1 y1 s1 t1 r g b a) + (set-vertex! 1 x2 y2 s2 t1 r g b a) + (set-vertex! 2 x3 y3 s2 t2 r g b a) + (set-vertex! 3 x4 y4 s1 t2 r g b a) + (dbuffer-pack-indices-quad! indices ioffset i))) -(define* (draw-sprite* texture - rect - matrix - #:key +(define-inlinable (sprite-append! vertices indices i rect texcoords tint matrix) + (let ((voffset (dbuffer-reserve! vertices (* (bytestruct-sizeof <sprite-vertex>) 4))) + (ioffset (dbuffer-reserve! indices (* 6 4)))) + (sprite-set! vertices voffset indices ioffset i rect texcoords tint matrix))) + +(define* (draw-sprite* texture-view rect matrix #:key (tint white) (blend-mode blend:alpha) - (texcoords (texture-gl-tex-rect texture))) - (let ((shader (graphics-variable-ref sprite-shader)) - (geometry (graphics-variable-ref sprite-geometry)) - (mvp (graphics-variable-ref sprite-mvp-matrix))) - (with-geometry geometry - (let* ((x1 (rect-x rect)) - (y1 (rect-y rect)) - (x2 (+ x1 (rect-width rect))) - (y2 (+ y1 (rect-height rect))) - (s1 (rect-x texcoords)) - (t1 (rect-y texcoords)) - (s2 (+ (rect-x texcoords) (rect-width texcoords))) - (t2 (+ (rect-y texcoords) (rect-height texcoords)))) - (sprite-vertex-append! geometry - (x1 y1 s1 t1) - (x2 y1 s2 t1) - (x2 y2 s2 t2) - (x1 y2 s1 t2)) - (geometry-index-append! geometry 0 3 2 0 2 1))) - (with-graphics-state ((g:blend-mode blend-mode) - (g:texture-0 texture)) - (shader-apply shader - (geometry-vertex-array geometry) - #:tint tint - #:mvp (if matrix - (begin - (matrix4-mult! mvp matrix - (current-projection)) - mvp) - (current-projection)))))) + (texcoords %default-texcoords)) + (match (graphics-variable-ref sprite-state) + ((and state ($ <sprite-state> shader uniforms sampler _ _ bindings)) + (vector-set! bindings 0 texture-view) + (vector-set! bindings 1 sampler) + (vector-set! bindings 2 uniforms) + (call-with-values + (lambda () + (stream-draw #:count 4 + #:shader shader + #:color-target (sprite-color-target state blend-mode) + #:vertex-layout %sprite-vertex-layout + #:binding-layout %sprite-binding-layout + #:bindings bindings)) + (lambda (vertices indices i) + (when (eq? i 0) + (let ((bv (map-buffer uniforms 'write 0 + (bytestruct-sizeof <sprite-uniforms>)))) + (bytestruct-pack! <sprite-uniforms> + (((mvp) (current-projection))) + bv 0) + (unmap-buffer uniforms))) + (sprite-append! vertices indices i rect texcoords tint matrix)))))) (define %null-vec2 (vec2 0.0 0.0)) (define %default-scale (vec2 1.0 1.0)) @@ -147,12 +389,12 @@ void main (void) { #:key (blend-mode blend:alpha) (origin %null-vec2) - (rect (texture-gl-rect texture)) + rect (rotation 0.0) (scale %default-scale) (shear %default-shear) (tint white)) - "Draw TEXTURE at POSITION. + "Draw TEXTURE, a 2D texture or texture view, at POSITION. Optionally, other transformations may be applied to the sprite. ROTATION specifies the angle to rotate the sprite, in radians. SCALE @@ -164,14 +406,17 @@ By default white is used, which does no tinting at all. By default, alpha blending is used but can be changed by specifying BLEND-MODE." - (let ((matrix (graphics-variable-ref sprite-model-matrix))) + (let* ((state (graphics-variable-ref sprite-state)) + (matrix (sprite-state-matrix state)) + (rect (or rect (%sprite-rect state texture)))) (matrix4-2d-transform! matrix #:origin origin #:position position #:rotation rotation #:scale scale #:shear shear) - (draw-sprite* texture rect matrix + (draw-sprite* (if (texture? texture) (texture-view texture) texture) + rect matrix #:tint tint #:blend-mode blend-mode))) @@ -180,152 +425,101 @@ BLEND-MODE." ;;; Sprite Batches ;;; -(define-geometry-type <batched-sprite-vertex> - batched-sprite-ref - batched-sprite-set! - batched-sprite-append! - (position vec2) - (texture vec2) - (tint vec4)) - -(define-graphics-variable sprite-batch-shader - (strings->shader - " -#ifdef GLSL330 -layout (location = 0) in vec2 position; -layout (location = 1) in vec2 tex; -layout (location = 2) in vec4 tint; -#elif defined(GLSL130) -in vec2 position; -in vec2 tex; -in vec4 tint; -#elif defined(GLSL120) -attribute vec2 position; -attribute vec2 tex; -attribute vec4 tint; -#endif -#ifdef GLSL120 -varying vec2 fragTex; -varying vec4 fragTint; -#else -out vec2 fragTex; -out vec4 fragTint; -#endif -uniform mat4 mvp; - -void main(void) { - fragTex = tex; - fragTint = tint; - gl_Position = mvp * vec4(position.xy, 0.0, 1.0); -} -" - " -#ifdef GLSL120 -varying vec2 fragTex; -varying vec4 fragTint; -#else -in vec2 fragTex; -in vec4 fragTint; -#endif -#ifdef GLSL330 -out vec4 fragColor; -#endif -uniform sampler2D colorTexture; - -void main (void) { -#ifdef GLSL330 - fragColor = texture(colorTexture, fragTex) * fragTint; -#else - gl_FragColor = texture2D(colorTexture, fragTex) * fragTint; -#endif -} -")) - (define-record-type <sprite-batch> - (%make-sprite-batch texture geometry size) + (%make-sprite-batch size texture vertices indices buffers pipeline + uniforms sampler bindings matrix) sprite-batch? + (size sprite-batch-size set-sprite-batch-size!) (texture sprite-batch-texture set-sprite-batch-texture!) - (geometry sprite-batch-geometry) - (size sprite-batch-size set-sprite-batch-size!)) + (vertices sprite-batch-vertices) + (indices sprite-batch-indices) + ;; A vector of 1 to vector-set! before each draw. + (buffers sprite-batch-buffers) + (pipeline sprite-batch-pipeline) + (uniforms sprite-batch-uniforms) + (sampler sprite-batch-sampler) + (bindings sprite-batch-bindings) + (matrix sprite-batch-matrix)) -(define* (make-sprite-batch texture #:key (capacity 256)) - "Make a sprite batch that can hold CAPACITY sprites before needing -to resize." - (%make-sprite-batch texture - (make-geometry <batched-sprite-vertex> (* capacity 4) - #:index-capacity (* capacity 6)) - 0)) +(define* (make-sprite-batch texture #:key + (capacity 256) + (blend-mode blend:alpha)) + "Make a sprite batch with enough storage to hold CAPACITY sprites +initially. By default, alpha blending is used when rendering but can +be changed by specifying BLEND-MODE." + (let ((shader (sprite-state-shader (graphics-variable-ref sprite-state))) + (color-target (make-color-target #:blend-mode blend-mode))) + (%make-sprite-batch 0 texture + (make-dbuffer + #:name "Sprite batch vertices" + #:capacity (* capacity 4)) + (make-dbuffer + #:name "Sprite batch indices" + #:capacity (* capacity 6)) + (vector #f) + (make-render-pipeline + #:name "Sprite batch" + #:shader shader + #:color-target color-target + #:vertex-layout %sprite-vertex-layout + #:binding-layout %sprite-binding-layout) + (make-buffer (* 16 4) + #:name "Sprite batch uniform buffer" + #:usage '(uniform)) + (make-sampler #:name "Sprite batch sampler") + (make-vector 3 #f) + (make-null-matrix4)))) (define (sprite-batch-clear! batch) "Reset BATCH to size 0." - (set-sprite-batch-size! batch 0) - (geometry-begin! (sprite-batch-geometry batch))) + (set-sprite-batch-size! batch 0)) -(define (sprite-batch-flush! batch) - "Submit the contents of BATCH to the GPU." - (geometry-end! (sprite-batch-geometry batch))) +(define* (sprite-batch-set!* batch i rect matrix + #:key + (tint white) + (texcoords %default-texcoords)) + (match batch + (($ <sprite-batch> size texture vertices indices) + (unless (< -1 i size) + (error "sprite batch index out of range" i)) + (unless (dbuffer-mapped? vertices) + (dbuffer-map! vertices) + (dbuffer-map! indices)) + (let ((voffset (* i (bytestruct-sizeof <sprite-vertex>) 4)) + (ioffset (* i 6 4))) + (sprite-set! vertices voffset indices ioffset (* i 4) rect texcoords tint matrix))))) (define* (sprite-batch-add* batch rect matrix #:key (tint white) - texture-region) + (texcoords %default-texcoords)) "Add RECT, transformed by MATRIX, to BATCH. To render a subsection -of the batch's texture, a texture object whose parent is the batch -texture may be specified via the TEXTURE-REGION argument." - (let* ((geometry (sprite-batch-geometry batch)) - (vertex-offset (geometry-vertex-count geometry <batched-sprite-vertex>)) - (minx (rect-x rect)) - (miny (rect-y rect)) - (maxx (+ minx (rect-width rect))) - (maxy (+ miny (rect-height rect))) - (x1 (matrix4-transform-x matrix minx miny)) - (y1 (matrix4-transform-y matrix minx miny)) - (x2 (matrix4-transform-x matrix maxx miny)) - (y2 (matrix4-transform-y matrix maxx miny)) - (x3 (matrix4-transform-x matrix maxx maxy)) - (y3 (matrix4-transform-y matrix maxx maxy)) - (x4 (matrix4-transform-x matrix minx maxy)) - (y4 (matrix4-transform-y matrix minx maxy)) - (texcoords (texture-gl-tex-rect - (or texture-region - (sprite-batch-texture batch)))) - (s1 (rect-x texcoords)) - (t1 (rect-y texcoords)) - (s2 (+ (rect-x texcoords) (rect-width texcoords))) - (t2 (+ (rect-y texcoords) (rect-height texcoords))) - (r (color-r tint)) - (g (color-g tint)) - (b (color-b tint)) - (a (color-a tint))) - (batched-sprite-append! geometry - (x1 y1 s1 t1 r g b a) - (x2 y2 s2 t1 r g b a) - (x3 y3 s2 t2 r g b a) - (x4 y4 s1 t2 r g b a)) - (geometry-index-append! geometry - vertex-offset - (+ vertex-offset 3) - (+ vertex-offset 2) - vertex-offset - (+ vertex-offset 2) - (+ vertex-offset 1)) - (set-sprite-batch-size! batch (+ (sprite-batch-size batch) 1)))) - -(define* (sprite-batch-add! batch - position +of the batch's texture, specify a TEXCOORDS rect in texture uv +coordinates." + (match batch + (($ <sprite-batch> size texture vertices indices) + (unless (dbuffer-mapped? vertices) + (dbuffer-map! vertices) + (dbuffer-map! indices)) + (sprite-append! vertices indices (* size 4) rect texcoords tint matrix) + (set-sprite-batch-size! batch (+ size 1))))) + +;; TODO: Specify sub-region in pixel coordinates. +(define* (sprite-batch-add! batch position #:key (origin %null-vec2) (rotation 0.0) (scale %default-scale) (shear %null-vec2) - texture-region - (tint white)) - "Add sprite to BATCH at POSITION. To render a subsection of the -batch's texture, a texture object whose parent is the batch texture -may be specified via the TEXTURE-REGION argument." - (let ((matrix (graphics-variable-ref sprite-model-matrix)) - (rect (texture-gl-rect - (or texture-region (sprite-batch-texture batch))))) + (tint white) + rect + (texcoords %default-texcoords)) + "Add sprite to BATCH at POSITION. To render a subsection +of the batch's texture, specify a TEXCOORDS rect in texture uv +coordinates." + (let* ((state (graphics-variable-ref sprite-state)) + (matrix (sprite-state-matrix state)) + (rect (or rect (%sprite-rect state (sprite-batch-texture batch))))) (matrix4-2d-transform! matrix #:origin origin #:position position @@ -334,35 +528,74 @@ may be specified via the TEXTURE-REGION argument." #:shear shear) (sprite-batch-add* batch rect matrix #:tint tint - #:texture-region texture-region))) + #:texcoords texcoords))) + +(define* (sprite-batch-set! batch i position + #:key + (origin %null-vec2) + (rotation 0.0) + (scale %default-scale) + (shear %null-vec2) + (tint white) + rect + (texcoords %default-texcoords)) + "Overwrite sprite I in BATCH with a new sprite at POSITION. To render +a subsection of the batch's texture, specify a TEXCOORDS rect in +texture uv coordinates." + (let* ((state (graphics-variable-ref sprite-state)) + (matrix (sprite-state-matrix state)) + (rect (or rect (%sprite-rect state (sprite-batch-texture batch))))) + (matrix4-2d-transform! matrix + #:origin origin + #:position position + #:rotation rotation + #:scale scale + #:shear shear) + (sprite-batch-set!* batch i rect matrix + #:tint tint + #:texcoords texcoords))) -(define* (draw-sprite-batch* batch matrix #:key (blend-mode blend:alpha)) +(define (draw-sprite-batch* batch matrix) "Render the contents of BATCH." - (let ((shader (graphics-variable-ref sprite-batch-shader)) - (mvp (graphics-variable-ref sprite-mvp-matrix))) - (sprite-batch-flush! batch) - (matrix4-mult! mvp matrix (current-projection)) - (with-graphics-state ((g:blend-mode blend-mode) - (g:texture-0 (sprite-batch-texture batch))) - (let ((geometry (sprite-batch-geometry batch))) - (shader-apply* shader - (geometry-vertex-array geometry) - 0 - (geometry-index-count geometry) - #:mvp mvp))))) + (match batch + (($ <sprite-batch> size texture vertices indices vertex-buffers pipeline + uniforms sampler bindings mvp) + (when (dbuffer-mapped? vertices) + (dbuffer-unmap! vertices) + (dbuffer-unmap! indices)) + (unless (eq? size 0) + (let ((view (if (texture-view? texture) + texture + (texture-view texture)))) + (matrix4-mult! mvp matrix (current-projection)) + (let ((bv (map-buffer uniforms 'write 0 + (bytestruct-sizeof <sprite-uniforms>)))) + (bytestruct-pack! <sprite-uniforms> + (((mvp) mvp)) + bv 0) + (unmap-buffer uniforms)) + (vector-set! vertex-buffers 0 (dbuffer-buffer vertices)) + (vector-set! bindings 0 view) + (vector-set! bindings 1 sampler) + (vector-set! bindings 2 uniforms) + (draw (* size 6) + #:pipeline pipeline + #:index-buffer (dbuffer-buffer indices) + #:vertex-buffers vertex-buffers + #:bindings bindings)))))) (define* (draw-sprite-batch batch #:key (position %null-vec2) (origin %null-vec2) (scale %default-scale) - (rotation 0.0) - (blend-mode blend:alpha)) + (rotation 0.0)) "Render the contents of BATCH." - (let ((matrix (graphics-variable-ref sprite-model-matrix))) + (let* ((state (graphics-variable-ref sprite-state)) + (matrix (sprite-state-matrix state))) (matrix4-2d-transform! matrix #:origin origin #:position position #:rotation rotation #:scale scale) - (draw-sprite-batch* batch matrix #:blend-mode blend-mode))) + (draw-sprite-batch* batch matrix))) |