A => testing/data/7x4_font.png +0 -0
A => testing/test_spritepainter.sc +71 -0
@@ 0,0 1,71 @@
+
+using import glm
+
+import ..tukan.use
+using import tukan.SpritePainter
+using import tukan.GLMain
+using import tukan.gl
+using import tukan.bitmap
+
+let glmain =
+ GLMain
+ title = "SpritePainter"
+ resizable = true
+
+global t = 0.0
+
+global painter = (SpritePainter)
+let spritesheet =
+ load-bitmap
+ .. module-dir "/data/7x4_font.png"
+ components = 4
+ #flip = false
+global texture_spritesheet = (GL.Texture2D)
+'setup texture_spritesheet
+ bitmap = spritesheet
+let sz = spritesheet.size
+va-map
+ inline (rc)
+ 'register painter rc sz
+ ivec4 0 0 5 8
+ ivec4 6 0 (6 + 4) 8
+ ivec4 10 0 (10 + 5) 8
+
+@@ 'on GLMain.on-draw
+fn draw (time size glmain)
+ GL.BindFramebuffer GL.FRAMEBUFFER 0
+ GL.Viewport 0 0 (i32 size.x) (i32 size.y)
+ GL.ClearColor 0 0 0.5 1
+ GL.Clear
+ |
+ GL.COLOR_BUFFER_BIT
+ GL.DEPTH_BUFFER_BIT
+ GL.STENCIL_BUFFER_BIT
+
+ t += (/ 60.0)
+
+ let invsz = (8 * (/ 2 (vec2 size)))
+ painter.transform =
+ mat4
+ \ invsz.x 0 0 0
+ \ 0 invsz.y 0 0
+ \ 0 0 1 0
+ \ -1 -1 0 1
+
+ from (methodsof painter) let sprite
+ sprite 0 (vec2 0 0)
+ vec2 5 8
+ #angle = t
+ sprite 1 (vec2 6 0)
+ vec2 4 8
+ #angle = t
+ sprite 2 (vec2 11 0)
+ vec2 5 8
+ #angle = t
+ GL.BindTextureUnit 0 texture_spritesheet
+ 'draw painter
+
+'run glmain
+
+;
+
M testing/test_vertexpainter.sc +1 -1
@@ 29,7 29,7 @@ fn draw (time size glmain)
t += (/ 60.0)
- from (methodsof painter) let clear color screen rotate AABB \
+ from (methodsof painter) let screen color screen rotate AABB \
translate quad perspective scale light flush wireframe moveto lineto \
closepath fill stroke
# setup view
A => tukan/SpritePainter.sc +204 -0
@@ 0,0 1,204 @@
+
+using import struct
+using import enum
+using import glm
+using import Array
+using import Option
+using import Rc
+
+using import .gl
+using import .rotation
+using import .stage
+
+# 16 bytes
+struct Sprite plain
+ # uv coordinates within texture (0..1)
+ rect : vec4
+
+# 4 kilobytes
+#let ColorCount = 256
+#struct Palette plain
+ color : (array vec4 ColorCount)
+
+# 32 bytes
+struct DrawCommand plain
+ # target rectangle relative to pivot at 0,0
+ rect : vec4
+ # origin of pivot
+ origin : vec2
+ # rotation angle (0 = no rotation)
+ angle : f32
+ # index of sprite
+ sprite : u32
+
+let
+ # 1 MB worth of sprites (256x256)
+ MaxSpriteCount = (1 * (1 << 20) // (sizeof Sprite))
+ # 8 MB worth of draw commands per frame
+ MaxCommandCount = (8 * (1 << 20) // (sizeof DrawCommand))
+
+let shaders =
+ do
+ using import glsl
+
+ let
+ DrawCommandBinding = 0
+ SpriteBinding = 1
+ TransformLocation = 2
+ SamplerLocation = 3
+
+ buffer b_commands :
+ struct "DrawCommands" plain
+ elements : (array DrawCommand)
+ binding = DrawCommandBinding
+ buffer b_sprites :
+ struct "Sprites" plain
+ elements : (array Sprite)
+ binding = SpriteBinding
+ uniform u_transform : mat4
+ location = TransformLocation
+
+ inout io_uv : vec2
+
+ fn vertex-main ()
+ cmd := b_commands.elements @ gl_InstanceID
+ rect := (deref cmd.rect)
+ origin := (deref cmd.origin)
+ angle := (deref cmd.angle)
+ sprite := b_sprites.elements @ cmd.sprite
+ uv := (deref sprite.rect)
+ let vxid = (deref gl_VertexID)
+
+ let rotmtx =
+ do
+ let ca sa = (cos angle) (sin angle)
+ mat2 ca sa -sa ca
+ let pos uv =
+ switch vxid
+ case 0
+ _ rect.xy uv.xy
+ case 1
+ _ rect.zy uv.zy
+ case 2
+ _ rect.zw uv.zw
+ case 3
+ _ rect.xw uv.xw
+ default
+ _ (vec2 0 0) (vec2 0 0)
+ io_uv.out = uv
+ gl_Position =
+ u_transform *
+ vec4 (origin + rotmtx * pos) 0 1
+
+ uniform u_smp : sampler2D
+ location = SamplerLocation
+ out fragment-color : vec4
+
+ fn fragment-main ()
+ let uv = io_uv.in
+ fragment-color = (texture u_smp uv)
+
+ locals;
+
+struct SpritePainter
+ let SpriteArray = (Array Sprite)
+ let DrawCommandArray = (Array DrawCommand)
+
+ sprites : SpriteArray
+ commands : DrawCommandArray
+ buffer_sprites : GL.Buffer # Sprite
+ buffer_commands : GL.Buffer # DrawCommand
+ vertex_array : GL.VertexArray
+ program : GL.Program
+ transform : mat4
+ sprites_dirty = true
+
+ fn create ()
+ local self =
+ super-type.__typecall this-type
+ call
+ attach-shaders (deref self.program)
+ vertex = shaders.vertex-main
+ fragment = shaders.fragment-main
+ #debug = true
+ 'reserve self.sprites MaxSpriteCount
+ 'reserve self.commands MaxCommandCount
+ GL.NamedBufferData self.buffer_sprites
+ (sizeof Sprite) * MaxSpriteCount
+ \ null GL.STREAM_DRAW
+ GL.NamedBufferData self.buffer_commands
+ (sizeof DrawCommand) * MaxCommandCount
+ \ null GL.STREAM_DRAW
+ GL.UseProgram self.program
+ GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
+ bindingof shaders.b_commands
+ self.buffer_commands
+ 0
+ ((sizeof DrawCommand) * MaxCommandCount) as i64
+ GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
+ bindingof shaders.b_sprites
+ self.buffer_sprites
+ 0
+ ((sizeof Sprite) * MaxSpriteCount) as i64
+ GL.Uniform shaders.u_smp 0
+ GL.UseProgram 0
+ deref self
+
+ inline __typecall (cls)
+ create;
+
+ fn... register (self, rect : vec4)
+ 'append self.sprites
+ Sprite
+ rect = rect
+ case (self, rect : ivec4, size : ivec2)
+ this-function self
+ (vec4 rect) / (vec2 size) . xyxy
+
+ fn invalidate (self)
+ self.sprites_dirty = true
+
+ fn... sprite
+ case (self, id : u32, origin : vec2, size : f32, angle : f32 = 0.0)
+ this-function self id origin (vec4 0 0 size size) angle
+ case (self, id : u32, origin : vec2, size : vec2, angle : f32 = 0.0)
+ this-function self id origin (vec4 0 0 size) angle
+ case (self, id : u32, origin : vec2, rect : vec4, angle : f32 = 0.0)
+ 'append self.commands
+ DrawCommand
+ rect = rect
+ origin = origin
+ angle = angle
+ sprite = id
+
+ fn draw (self)
+ if (empty? self.commands)
+ return;
+ if self.sprites_dirty
+ if (not (empty? self.sprites))
+ self.sprites_dirty = false
+ GL.NamedBufferSubData self.buffer_sprites 0
+ ((sizeof Sprite) * (countof self.sprites)) as i64
+ & (self.sprites @ 0)
+ GL.NamedBufferSubData self.buffer_commands 0
+ ((sizeof DrawCommand) * (countof self.commands)) as i64
+ & (self.commands @ 0)
+ GL.UseProgram self.program
+ GL.Uniform shaders.u_transform self.transform
+ GL.BindVertexArray self.vertex_array
+ GL.DrawArraysInstanced GL.TRIANGLE_FAN 0 4
+ (countof self.commands) as i32
+ 'clear self.commands
+
+ #fn... screen (self, size : vec2)
+ let aspect = (size.y / size.x)
+ scale self
+ vec3 aspect 1 1
+ #case (self, size : ivec2)
+ this-function self (vec2 size)
+
+do
+ let SpritePainter
+
+ locals;
+
M tukan/gl.sc +14 -0
@@ 834,6 834,11 @@ fn blit-framebuffer (source target size
inline vec-type-uniform-func (f)
inline "glUniformVecType" (location value)
f location (unpack value)
+@@ memo
+inline mat-type-uniform-func (f)
+ inline "glUniformMatrixType" (location value)
+ let ET = ((typeof value) . ElementType)
+ f location 1 GL_FALSE (bitcast &value (mutable @ET))
fn get-uniform-dispatch-by-element-type (ET)
if (ET < gsampler) `[GL.Uniform1i]
@@ 851,6 856,15 @@ fn get-uniform-dispatch-by-element-type
case uvec2 `[(vec-type-uniform-func GL.Uniform2ui)]
case uvec3 `[(vec-type-uniform-func GL.Uniform3ui)]
case uvec4 `[(vec-type-uniform-func GL.Uniform4ui)]
+ case mat2 `[(mat-type-uniform-func GL.UniformMatrix2fv)]
+ case mat2x3 `[(mat-type-uniform-func GL.UniformMatrix2x3fv)]
+ case mat2x4 `[(mat-type-uniform-func GL.UniformMatrix2x4fv)]
+ case mat3x2 `[(mat-type-uniform-func GL.UniformMatrix3x2fv)]
+ case mat3 `[(mat-type-uniform-func GL.UniformMatrix3fv)]
+ case mat3x4 `[(mat-type-uniform-func GL.UniformMatrix3x4fv)]
+ case mat4x2 `[(mat-type-uniform-func GL.UniformMatrix4x2fv)]
+ case mat4x3 `[(mat-type-uniform-func GL.UniformMatrix4x3fv)]
+ case mat4 `[(mat-type-uniform-func GL.UniformMatrix4fv)]
default
inline errmsg (location value)
static-error "unsupported uniform type"