From dec26f6b4149700d936c2efb58f3a9061b56bfbe Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 8 Jan 2026 13:08:14 -0600 Subject: [PATCH] enhanced 2d drawables --- film2d.cm | 15 ++++++++-- particles2d.cm | 4 ++- sdl_gpu.cm | 77 ++++++++++++++++++++++++++++++++++++++------------ sprite.cm | 6 ++-- text2d.cm | 2 ++ tilemap2d.cm | 4 ++- 6 files changed, 83 insertions(+), 25 deletions(-) diff --git a/film2d.cm b/film2d.cm index 8865da10..7b4edfcd 100644 --- a/film2d.cm +++ b/film2d.cm @@ -219,7 +219,7 @@ function _batch_drawables(drawables) { if (d.type == 'sprite') { var tex = d.texture || d.image - var mat = d.material || default_mat + var mat = d.material || {blend: 'alpha', sampler: d.filter || 'nearest'} if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) { current.sprites.push(d) @@ -232,9 +232,12 @@ function _batch_drawables(drawables) { var tex = d.texture || d.image var mat = d.material || default_mat var particles = d.particles || [] + var emitter_opacity = d.opacity != null ? d.opacity : 1 + var emitter_tint = d.tint || {r: 1, g: 1, b: 1, a: 1} for (var p = 0; p < particles.length; p++) { var part = particles[p] + var pc = part.color || {r: 1, g: 1, b: 1, a: 1} var sprite = { type: 'sprite', pos: part.pos, @@ -242,7 +245,9 @@ function _batch_drawables(drawables) { height: (d.height || 16) * (part.scale || 1), anchor_x: 0.5, anchor_y: 0.5, - color: part.color || {r: 1, g: 1, b: 1, a: 1} + color: pc, + opacity: emitter_opacity, + tint: emitter_tint } if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) { @@ -259,6 +264,8 @@ function _batch_drawables(drawables) { var tile_h = d.tile_height || 1 var off_x = d.offset_x || 0 var off_y = d.offset_y || 0 + var tilemap_opacity = d.opacity != null ? d.opacity : 1 + var tilemap_tint = d.tint || {r: 1, g: 1, b: 1, a: 1} for (var x = 0; x < tiles.length; x++) { if (!tiles[x]) continue @@ -278,7 +285,9 @@ function _batch_drawables(drawables) { height: tile_h, anchor_x: 0.5, anchor_y: 0.5, - color: {r: 1, g: 1, b: 1, a: 1} + color: {r: 1, g: 1, b: 1, a: 1}, + opacity: tilemap_opacity, + tint: tilemap_tint } // Batching diff --git a/particles2d.cm b/particles2d.cm index 77364564..1e5022e5 100644 --- a/particles2d.cm +++ b/particles2d.cm @@ -123,7 +123,9 @@ var factory = function(props) { plane: 'default', layer: 0, groups: [], - particles: [] + particles: [], + opacity: 1, + tint: {r: 1, g: 1, b: 1, a: 1} } var data = {} diff --git a/sdl_gpu.cm b/sdl_gpu.cm index 58941537..4fb8177f 100644 --- a/sdl_gpu.cm +++ b/sdl_gpu.cm @@ -803,6 +803,14 @@ function _build_sprite_vertices(sprites, camera) { var ay = s.anchor_y || 0 var c = s.color || white + // Apply tint and opacity + var tint = s.tint || white + var opacity = s.opacity != null ? s.opacity : 1 + var final_r = c.r * tint.r + var final_g = c.g * tint.g + var final_b = c.b * tint.b + var final_a = c.a * (tint.a != null ? tint.a : 1) * opacity + // Apply anchor var x = px - w * ax var y = py - h * ay @@ -812,6 +820,33 @@ function _build_sprite_vertices(sprites, camera) { var v0 = s.uv_rect ? s.uv_rect.y : 0 var u1 = s.uv_rect ? (s.uv_rect.x + s.uv_rect.width) : 1 var v1 = s.uv_rect ? (s.uv_rect.y + s.uv_rect.height) : 1 + + // Apply UV transform (offset, scale, rotate) + var uv = s.uv + if (uv) { + var uv_off = uv.offset || {x: 0, y: 0} + var uv_scale = uv.scale || {x: 1, y: 1} + // Apply scale and offset to UVs + u0 = u0 * uv_scale.x + uv_off.x + v0 = v0 * uv_scale.y + uv_off.y + u1 = u1 * uv_scale.x + uv_off.x + v1 = v1 * uv_scale.y + uv_off.y + } + + // Apply flip + var flip = s.flip + if (flip) { + if (flip.x) { + var tmp = u0 + u0 = u1 + u1 = tmp + } + if (flip.y) { + var tmp = v0 + v0 = v1 + v1 = tmp + } + } // Quad vertices (bottom-left, bottom-right, top-right, top-left) // v0: bottom-left @@ -819,40 +854,40 @@ function _build_sprite_vertices(sprites, camera) { vertex_data.wf(y) vertex_data.wf(u0) vertex_data.wf(v1) // Flip V - vertex_data.wf(c.r) - vertex_data.wf(c.g) - vertex_data.wf(c.b) - vertex_data.wf(c.a) + vertex_data.wf(final_r) + vertex_data.wf(final_g) + vertex_data.wf(final_b) + vertex_data.wf(final_a) // v1: bottom-right vertex_data.wf(x + w) vertex_data.wf(y) vertex_data.wf(u1) vertex_data.wf(v1) // Flip V - vertex_data.wf(c.r) - vertex_data.wf(c.g) - vertex_data.wf(c.b) - vertex_data.wf(c.a) + vertex_data.wf(final_r) + vertex_data.wf(final_g) + vertex_data.wf(final_b) + vertex_data.wf(final_a) // v2: top-right vertex_data.wf(x + w) vertex_data.wf(y + h) vertex_data.wf(u1) vertex_data.wf(v0) // Flip V - vertex_data.wf(c.r) - vertex_data.wf(c.g) - vertex_data.wf(c.b) - vertex_data.wf(c.a) + vertex_data.wf(final_r) + vertex_data.wf(final_g) + vertex_data.wf(final_b) + vertex_data.wf(final_a) // v3: top-left vertex_data.wf(x) vertex_data.wf(y + h) vertex_data.wf(u0) vertex_data.wf(v0) // Flip V - vertex_data.wf(c.r) - vertex_data.wf(c.g) - vertex_data.wf(c.b) - vertex_data.wf(c.a) + vertex_data.wf(final_r) + vertex_data.wf(final_g) + vertex_data.wf(final_b) + vertex_data.wf(final_a) // Indices (two triangles) index_data.w16(vertex_count + 0) @@ -1274,12 +1309,14 @@ function _flush_draws(cmd_buffer, pass, draws, camera, target) { // Sprite batch handling var tex_path = draw.texture || '_white' var blend = draw.material ? draw.material.blend : 'alpha' + var sampler = draw.material ? draw.material.sampler : 'nearest' // Check if we can append to current batch if (current_batch && current_batch.type == 'sprites' && current_batch.texture_path == tex_path && - current_batch.blend == blend) { + current_batch.blend == blend && + current_batch.sampler == sampler) { // Append sprites if (draw.geometry && draw.geometry.sprites) { @@ -1296,6 +1333,7 @@ function _flush_draws(cmd_buffer, pass, draws, camera, target) { type: 'sprites', texture_path: tex_path, blend: blend, + sampler: sampler, sprites: [] } @@ -1359,11 +1397,14 @@ function _render_batch(cmd_buffer, pass, batch, camera, target) { // Select pipeline var pipeline = batch.blend == 'add' ? _pipelines.sprite_add : _pipelines.sprite_alpha + // Select sampler based on filter + var sampler = (batch.sampler == 'linear') ? _sampler_linear : _sampler_nearest + // Draw pass.bind_pipeline(pipeline) pass.bind_vertex_buffers(0, [{buffer: vb, offset: 0}]) pass.bind_index_buffer({buffer: ib, offset: 0}, 16) - pass.bind_fragment_samplers(0, [{texture: tex, sampler: _sampler_nearest}]) + pass.bind_fragment_samplers(0, [{texture: tex, sampler: sampler}]) cmd_buffer.push_vertex_uniform_data(0, proj) pass.draw_indexed(geom.index_count, 1, 0, 0, 0) } diff --git a/sprite.cm b/sprite.cm index 9aa39137..e5b8ff2b 100644 --- a/sprite.cm +++ b/sprite.cm @@ -47,11 +47,13 @@ return function(props) { height: 1, anchor_x: 0, anchor_y: 0, - flip_x: false, - flip_y: false, + flip: {x: false, y: false}, rotation: 0, color: {r: 1, g: 1, b: 1, a: 1}, opacity: 1, + tint: {r: 1, g: 1, b: 1, a: 1}, + filter: 'nearest', + uv: {offset: {x: 0, y: 0}, scale: {x: 1, y: 1}, rotate: 0}, plane: 'default', layer: 0, groups: [], diff --git a/text2d.cm b/text2d.cm index ef34f51f..5af65764 100644 --- a/text2d.cm +++ b/text2d.cm @@ -35,6 +35,8 @@ return function(props) { font: "fonts/dos", size: 16, color: {r: 1, g: 1, b: 1, a: 1}, + opacity: 1, + tint: {r: 1, g: 1, b: 1, a: 1}, mode: null, sdf: null, outline_width: null, diff --git a/tilemap2d.cm b/tilemap2d.cm index 75e5ae13..fda5cd07 100644 --- a/tilemap2d.cm +++ b/tilemap2d.cm @@ -49,7 +49,9 @@ return function(props) { layer: 0, groups: [], tile_width: 1, - tile_height: 1 + tile_height: 1, + opacity: 1, + tint: {r: 1, g: 1, b: 1, a: 1} } var data = {}