diff --git a/scripts/core/_sdl_video.js b/scripts/core/_sdl_video.js index 769b512f..947fd356 100644 --- a/scripts/core/_sdl_video.js +++ b/scripts/core/_sdl_video.js @@ -439,6 +439,35 @@ function handle_renderer(msg) { if (!msg.data || !msg.data.pos) return {error: "Missing pos"}; return {data: ren.coordsToWindow(msg.data.pos)}; + case 'batch': + // Execute a batch of operations + if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"}; + + var results = []; + for (var i = 0; i < msg.data.length; i++) { + var cmd = msg.data[i]; + if (!cmd.op) { + results.push({error: "Command at index " + i + " missing op"}); + continue; + } + + // Create a temporary message object for the command + var temp_msg = { + kind: 'renderer', + id: msg.id, + op: cmd.op, + prop: cmd.prop, + value: cmd.value, + data: cmd.data + }; + + // Recursively call handle_renderer for each command + var result = handle_renderer(temp_msg); + results.push(result); + } + + return {results: results}; + default: return {error: "Unknown renderer operation: " + msg.op}; } diff --git a/scripts/modules/draw2d.js b/scripts/modules/draw2d.js index a4820f81..a20362a3 100644 --- a/scripts/modules/draw2d.js +++ b/scripts/modules/draw2d.js @@ -1,4 +1,3 @@ -var render = use('render') var graphics = use('graphics') var math = use('math') var util = use('util') @@ -11,20 +10,78 @@ A collection of 2D drawing functions that operate in screen space. Provides prim for lines, rectangles, text, sprite drawing, etc. Immediate mode. ` -/*var whiteimage = {} -whiteimage = graphics.make_surface([1,1]) -whiteimage.rect({x:0,y:0,width:1,height:1}, [1,1,1,1]) -render.load_texture(whiteimage) -*/ +// Draw command accumulator +var commands = [] -if (render.point) - draw.point = function(pos,size,opt = {color:Color.white}, pipeline) { - render.settings(opt) - render.pipeline(pipeline) - render.point([pos]) +// Renderer info set by moth +var renderer_actor = null +var renderer_id = null + +// Set the renderer +draw.set_renderer = function(actor, id) { + renderer_actor = actor + renderer_id = id +} + +// Clear accumulated commands +draw.clear = function() { + commands = [] +} + +// Get accumulated commands +draw.get_commands = function() { + return commands +} + +// Flush all commands to renderer +draw.flush = function() { + if (!renderer_actor || !renderer_id || commands.length === 0) return + + // Convert commands to batch format + var batch_data = commands.map(function(cmd) { + return { + op: cmd.op, + prop: cmd.prop, + value: cmd.value, + data: cmd.data + } + }) + + // Send all commands in a single batch message + send(renderer_actor, { + kind: "renderer", + id: renderer_id, + op: "batch", + data: batch_data + }) + + // Clear commands after sending + commands = [] +} + +// Helper to add a command +function add_command(cmd) { + commands.push(cmd) +} + +// Drawing functions +draw.point = function(pos, size, opt = {color: Color.white}, pipeline) { + if (opt.color) { + add_command({ + kind: "renderer", + id: renderer_id, + op: "set", + prop: "drawColor", + value: opt.color + }) } -else - draw.point = function() { throw new Error('Backend cannot draw points.') } + add_command({ + kind: "renderer", + id: renderer_id, + op: "point", + data: {points: [pos]} + }) +} draw.point[prosperon.DOC] = ` :param pos: A 2D position ([x, y]) where the point should be drawn. :param size: The size of the point. @@ -81,7 +138,14 @@ function software_ellipse(pos, radii, opt) [cx + x, cy + y], [cx - x, cy + y], [cx + x, cy - y], [cx - x, cy - y] ].filter(pt => within_wedge(pt[0]-cx, pt[1]-cy, start, end, full_circle)) - if (pts.length) render.point(pts) + if (pts.length) { + add_command({ + kind: "renderer", + id: renderer_id, + op: "point", + data: {points: pts} + }) + } } while (px < py) { @@ -139,7 +203,14 @@ function software_ellipse(pos, radii, opt) } } } - if (strips.length) render.rects(strips) + if (strips.length) { + add_command({ + kind: "renderer", + id: renderer_id, + op: "rects", + data: {rects: strips} + }) + } } var ellipse_def = { @@ -150,17 +221,20 @@ var ellipse_def = { thickness: 1, } -if (render.ellipse) - draw.ellipse = function(pos, radii, def, pipeline) { - } -else - draw.ellipse = function(pos, radii, def, pipeline) { - var opt = def ? {...ellipse_def, ...def} : ellipse_def - render.settings(opt) - render.pipeline(pipeline) - if (opt.thickness <= 0) opt.thickness = Math.max(radii[0], radii[1]) - software_ellipse(pos, radii, opt) +draw.ellipse = function(pos, radii, def, pipeline) { + var opt = def ? {...ellipse_def, ...def} : ellipse_def + if (opt.color) { + add_command({ + kind: "renderer", + id: renderer_id, + op: "set", + prop: "drawColor", + value: opt.color + }) } + if (opt.thickness <= 0) opt.thickness = Math.max(radii[0], radii[1]) + software_ellipse(pos, radii, opt) +} var line_def = { color: Color.white, @@ -176,9 +250,21 @@ draw.line = function(points, def, pipeline) else opt = line_def - render.settings(opt) - render.pipeline(pipeline) - render.line(points) + if (opt.color) { + add_command({ + kind: "renderer", + id: renderer_id, + op: "set", + prop: "drawColor", + value: opt.color + }) + } + add_command({ + kind: "renderer", + id: renderer_id, + op: "line", + data: {points: points} + }) } draw.cross = function render_cross(pos, size, def, pipe) { @@ -203,14 +289,24 @@ draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, def function software_outline_rect(rect, thickness) { if (thickness <= 0) { - render.rectangle(rect); + add_command({ + kind: "renderer", + id: renderer_id, + op: "fillRect", + data: {rect: rect} + }) return; } /* stroke swallows the whole thing → fill instead */ if ((thickness << 1) >= rect.width || (thickness << 1) >= rect.height) { - render.rectangle(rect) // filled + add_command({ + kind: "renderer", + id: renderer_id, + op: "fillRect", + data: {rect: rect} + }) return } @@ -219,14 +315,21 @@ function software_outline_rect(rect, thickness) x1 = rect.x + rect.width, y1 = rect.y + rect.height - render.rects([ - { x:x0, y:y0, width:rect.width, height:thickness }, // top - { x:x0, y:y1-thickness, width:rect.width, height:thickness }, // bottom - { x:x0, y:y0+thickness, width:thickness, - height:rect.height - (thickness<<1) }, // left - { x:x1-thickness, y:y0+thickness, width:thickness, - height:rect.height - (thickness<<1) } // right - ]) + add_command({ + kind: "renderer", + id: renderer_id, + op: "rects", + data: { + rects: [ + { x:x0, y:y0, width:rect.width, height:thickness }, // top + { x:x0, y:y1-thickness, width:rect.width, height:thickness }, // bottom + { x:x0, y:y0+thickness, width:thickness, + height:rect.height - (thickness<<1) }, // left + { x:x1-thickness, y:y0+thickness, width:thickness, + height:rect.height - (thickness<<1) } // right + ] + } + }) } /* ------------------------------------------------------------------------ @@ -260,16 +363,23 @@ function software_round_rect(rect, radius, thickness = 1) const r_in = radius - thickness /* straight bands (top/bottom/left/right) ------------------------- */ - render.rects([ - { x:x0 + radius, y:y0, width:rect.width - (radius << 1), - height:thickness }, // top - { x:x0 + radius, y:y1 - thickness + 1, - width:rect.width - (radius << 1), height:thickness }, // bottom - { x:x0, y:y0 + radius, width:thickness, - height:rect.height - (radius << 1) }, // left - { x:x1 - thickness + 1, y:y0 + radius, width:thickness, - height:rect.height - (radius << 1) } // right - ]) + add_command({ + kind: "renderer", + id: renderer_id, + op: "rects", + data: { + rects: [ + { x:x0 + radius, y:y0, width:rect.width - (radius << 1), + height:thickness }, // top + { x:x0 + radius, y:y1 - thickness + 1, + width:rect.width - (radius << 1), height:thickness }, // bottom + { x:x0, y:y0 + radius, width:thickness, + height:rect.height - (radius << 1) }, // left + { x:x1 - thickness + 1, y:y0 + radius, width:thickness, + height:rect.height - (radius << 1) } // right + ] + } + }) /* corner arcs ---------------------------------------------------- */ const strips = [] @@ -296,7 +406,12 @@ function software_round_rect(rect, radius, thickness = 1) ) } - render.rects(strips) + add_command({ + kind: "renderer", + id: renderer_id, + op: "rects", + data: {rects: strips} + }) } /* ------------------------------------------------------------------------ @@ -312,18 +427,32 @@ function software_fill_round_rect(rect, radius) y1 = rect.y + rect.height - 1 /* main column */ - render.rects([ - { x:x0 + radius, y:y0, width:rect.width - (radius << 1), - height:rect.height } - ]) + add_command({ + kind: "renderer", + id: renderer_id, + op: "rects", + data: { + rects: [ + { x:x0 + radius, y:y0, width:rect.width - (radius << 1), + height:rect.height } + ] + } + }) /* side columns */ - render.rects([ - { x:x0, y:y0 + radius, width:radius, - height:rect.height - (radius << 1) }, - { x:x1 - radius + 1, y:y0 + radius, width:radius, - height:rect.height - (radius << 1) } - ]) + add_command({ + kind: "renderer", + id: renderer_id, + op: "rects", + data: { + rects: [ + { x:x0, y:y0 + radius, width:radius, + height:rect.height - (radius << 1) }, + { x:x1 - radius + 1, y:y0 + radius, width:radius, + height:rect.height - (radius << 1) } + ] + } + }) /* corner caps */ const cx_l = x0 + radius, cx_r = x1 - radius @@ -342,7 +471,12 @@ function software_fill_round_rect(rect, radius) ) } - render.rects(caps) + add_command({ + kind: "renderer", + id: renderer_id, + op: "rects", + data: {rects: caps} + }) } var rect_def = { @@ -352,8 +486,15 @@ var rect_def = { } draw.rectangle = function render_rectangle(rect, def, pipeline) { var opt = def ? {...rect_def, ...def} : rect_def - render.settings(opt) - render.pipeline(pipeline) + if (opt.color) { + add_command({ + kind: "renderer", + id: renderer_id, + op: "set", + prop: "drawColor", + value: opt.color + }) + } var t = opt.thickness|0 @@ -361,7 +502,12 @@ draw.rectangle = function render_rectangle(rect, def, pipeline) { if (opt.radius) software_fill_round_rect(rect, opt.radius) else - render.rectangle(rect) + add_command({ + kind: "renderer", + id: renderer_id, + op: "fillRect", + data: {rect: rect} + }) return } @@ -387,7 +533,7 @@ draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info if (typeof image === "string") image = graphics.texture(image) - render.slice9(image, rect, slice, slice9_info, pipeline); + // TODO: Implement slice9 rendering via SDL video actor } draw.slice9[prosperon.DOC] = ` :param image: An image object or string path to a texture. @@ -416,8 +562,9 @@ draw.image = function image(image, rect = [0,0], rotation = 0, anchor = [0,0], s rect.width ??= image.texture.width rect.height ??= image.texture.height info ??= image_info; - render.settings(info) - render.image(image, rect, rotation, anchor, shear, info) + + // TODO: Handle texture loading and sending texture_id + // For now, we skip image rendering as it requires texture management } function software_circle(pos, radius) @@ -460,7 +607,8 @@ var sysfont = graphics.get_font('fonts/c64.ttf', 8) draw.text = function text(text, rect, font = sysfont, size = 0, color = Color.white, wrap = 0, pipeline) { if (typeof font === 'string') font = graphics.get_font(font) var mesh = graphics.make_text_buffer(text, rect, 0, color, wrap, font) - render.geometry(font, mesh) + + // TODO: Handle text rendering via geometry } draw.text[prosperon.DOC] = ` :param text: The string to draw. @@ -473,4 +621,4 @@ draw.text[prosperon.DOC] = ` :return: None ` -return draw +return draw \ No newline at end of file