diff --git a/scripts/render.js b/scripts/render.js index e294d825..eccf1bc4 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -861,7 +861,7 @@ render.text = function text(text, rect, font = prosperon.font, size = 0, color = current_queue.push({ type: 'geometry', - mesh: mesh, + mesh, image: font, texture:font.texture, pipeline, @@ -1004,9 +1004,39 @@ render.images = function images(image, rects) for (var rect of rects) render.image(image,rect); } +render.tile = function(image, rect, color = Color.white, pipeline = sprite_pipeline) +{ + if (!image) throw Error ('Need an image to render.') + if (typeof image === "string") + image = game.texture(image); + + var mesh = render._main.tile(image.texture, {x:0,y:0,width:image.texture.width,height:image.texture.height}, rect); + current_queue.push({ + type:'geometry', + mesh, + image, + pipeline, + first_index:0, + num_indices:mesh.num_indices + }); +} + // slice is given in pixels -render.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.white) { - render.image(image,rect,undefined,color); +render.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.white, pipeline = sprite_pipeline) { + if (!image) throw Error ('Need an image to render.') + if (typeof image === "string") + image = game.texture(image); + + var mesh = render._main.slice9(image.texture, rect, clay.normalizeSpacing(slice)); + current_queue.push({ + type: 'geometry', + mesh, + image, + pipeline, + first_index:0, + num_indices:mesh.num_indices + }); +// render.image(image,rect,undefined,color); // render._main.slice9(image.texture, rect, slice); }; diff --git a/source/jsffi.c b/source/jsffi.c index b002cb28..50fb745d 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -1005,7 +1005,6 @@ SDL_GPUStencilOpState js2SDL_GPUStencilOpState(JSContext *js, JSValue v) return state; } - typedef struct lrtb lrtb; lrtb js2lrtb(JSContext *js, JSValue v) @@ -1645,16 +1644,8 @@ JSValue make_quad_indices_buffer(JSContext *js, int quads) return JS_DupValue(js,idx_buffer); } -JSC_CCALL(os_make_text_buffer, - const char *s = JS_ToCString(js, argv[0]); - rect rectpos = js2rect(js,argv[1]); - float size = js2number(js,argv[2]); - font *f = js2font(js,argv[5]); - if (!size) size = f->height; - colorf c = js2color(js,argv[3]); - int wrap = js2number(js,argv[4]); - HMM_Vec2 startpos = {.x = rectpos.x, .y = rectpos.y }; - text_vert *buffer = renderText(s, startpos, f, size, c, wrap); +JSValue quads_to_mesh(JSContext *js, text_vert *buffer) +{ size_t verts = arrlen(buffer); HMM_Vec2 *pos = malloc(arrlen(buffer)*sizeof(HMM_Vec2)); @@ -1675,9 +1666,7 @@ JSC_CCALL(os_make_text_buffer, size_t count = verts/2*3; JSValue jsidx = make_quad_indices_buffer(js, quads); - JS_FreeCString(js, s); - arrfree(buffer); - ret = JS_NewObject(js); + JSValue ret = JS_NewObject(js); JS_SetProperty(js, ret, pos_atom, jspos); JS_SetProperty(js, ret, uv_atom, jsuv); JS_SetProperty(js, ret, color_atom, jscolor); @@ -1686,6 +1675,21 @@ JSC_CCALL(os_make_text_buffer, JS_SetProperty(js,ret,num_indices_atom, number2js(js,count)); return ret; +} + +JSC_CCALL(os_make_text_buffer, + const char *s = JS_ToCString(js, argv[0]); + rect rectpos = js2rect(js,argv[1]); + float size = js2number(js,argv[2]); + font *f = js2font(js,argv[5]); + if (!size) size = f->height; + colorf c = js2color(js,argv[3]); + int wrap = js2number(js,argv[4]); + HMM_Vec2 startpos = {.x = rectpos.x, .y = rectpos.y }; + text_vert *buffer = renderText(s, startpos, f, size, c, wrap); + ret = quads_to_mesh(js,buffer); + JS_FreeCString(js, s); + arrfree(buffer); ) JSC_CCALL(spline_catmull, @@ -4310,6 +4314,279 @@ JSC_CCALL(gpu_compute_pipeline, return SDL_GPUComputePipeline2js(js,pipeline); ) +static inline void add_quad(text_vert **verts, rect src, rect dst) +{ + text_vert v = (text_vert){ + .pos = (HMM_Vec2){dst.x, dst.y}, + .uv = (HMM_Vec2){src.x,src.y}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); + + v = (text_vert){ + .pos = (HMM_Vec2){dst.x+dst.w, dst.y}, + .uv = (HMM_Vec2){src.x+src.w,src.y}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); + + v = (text_vert){ + .pos = (HMM_Vec2){dst.x, dst.y+dst.h}, + .uv = (HMM_Vec2){src.x,src.y+src.h}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); + + v = (text_vert){ + .pos = (HMM_Vec2){dst.x+dst.w, dst.y+dst.h}, + .uv = (HMM_Vec2){src.x+src.w,src.y+src.h}, + .color = (HMM_Vec4){1,1,1,1} + }; + arrput(*verts, v); +} + +JSC_CCALL(gpu_slice9, + JSValue jstex = argv[0]; + rect dst = js2rect(js,argv[1]); + rect src = (rect){ + .x = 0, + .y = 0, + .w = 1, + .h = 1 + }; + lrtb src_slice = js2lrtb(js,argv[2]); + lrtb dst_slice = src_slice; + + HMM_Vec2 size; + JS_GETPROP(js,size.x,jstex, width, number) + JS_GETPROP(js,size.y,jstex,height,number) + +/* src.x /= size.x; + src.w /= size.x; + src.y /= size.y; + src.h /= size.y;*/ + src_slice.l /= size.x; + src_slice.r /= size.x; + src_slice.t /= size.y; + src_slice.b /= size.y; + + text_vert *verts = NULL; + + rect curr_src; + rect curr_dst; + + // bottom left + curr_src = src; + curr_src.w = src_slice.l; + curr_src.h = src_slice.b; + curr_dst = dst; + curr_dst.w = dst_slice.l; + curr_dst.h = dst_slice.b; + add_quad(&verts,curr_src, curr_dst); + + // top left + curr_src = src; + curr_dst = dst; + curr_src.y = src.y + src.h - src_slice.t; + curr_src.h = src_slice.t; + curr_src.w = src_slice.l; + curr_dst.y = dst.y + dst.h - dst_slice.t; + curr_dst.h = dst_slice.t; + curr_dst.w = dst_slice.l; + add_quad(&verts,curr_src,curr_dst); + + // left bar + curr_src = src; + curr_dst = dst; + curr_src.y = src.y + src_slice.b; + curr_src.h = src.h - src_slice.t - src_slice.b; + curr_src.w = src_slice.l; + curr_dst.y = dst.y + dst_slice.b; + curr_dst.h = dst.h - dst_slice.t - dst_slice.b; + curr_dst.w = dst_slice.l; + add_quad(&verts,curr_src,curr_dst); + + // bottom bar + curr_src = src; + curr_dst = dst; + curr_src.x = src.x + src_slice.l; + curr_src.h = src_slice.b; + curr_src.w = src.w - src_slice.l - src_slice.r; + curr_dst.x = dst.x + dst_slice.l; + curr_dst.h = dst_slice.b; + curr_dst.w = dst.w - dst_slice.l - dst_slice.r; + add_quad(&verts,curr_src,curr_dst); + + // top bar + curr_src = src; + curr_dst = dst; + curr_src.x = src.x + src_slice.l; + curr_src.h = src_slice.t; + curr_src.y = src.y + src.h - src_slice.t; + curr_src.w = src.w - src_slice.l - src_slice.r; + curr_dst.x = dst.x + dst_slice.l; + curr_dst.h = dst_slice.t; + curr_dst.y = dst.y + dst.h - dst_slice.t; + curr_dst.w = dst.w - dst_slice.l - dst_slice.r; + add_quad(&verts,curr_src,curr_dst); + + // center + curr_src = src; + curr_dst = dst; + curr_src.x = src.x + src_slice.l; + curr_src.w = src.w - src_slice.l - src_slice.r; + curr_src.y = src.y + src_slice.b; + curr_src.h = src.h - src_slice.t - src_slice.b; + curr_dst.x = dst.x + dst_slice.l; + curr_dst.w = dst.w - dst_slice.l - dst_slice.r; + curr_dst.y = dst.y + dst_slice.b; + curr_dst.h = dst.h - dst_slice.t - dst_slice.b; + add_quad(&verts, curr_src, curr_dst); + + // bottom right + curr_src = src; + curr_dst = dst; + curr_src.x = src.x + src.w - src_slice.r; + curr_src.w = src_slice.r; + curr_src.h = src_slice.b; + curr_dst.x = dst.x + dst.w - dst_slice.r; + curr_dst.w = dst_slice.r; + curr_dst.h = dst_slice.b; + add_quad(&verts, curr_src, curr_dst); + + // top right + curr_src = src; + curr_dst = dst; + curr_src.x = src.x + src.w - src_slice.r; + curr_src.w = src_slice.r; + curr_src.y = src.y + src.h - src_slice.t; + curr_src.h = src_slice.t; + curr_dst.x = dst.x + dst.w - dst_slice.r; + curr_dst.w = dst_slice.r; + curr_dst.y = dst.y + dst.h - dst_slice.t; + curr_dst.h = dst_slice.t; + add_quad(&verts, curr_src, curr_dst); + + // right bar + curr_src = src; + curr_dst = dst; + curr_src.x = src.x + src.w - src_slice.r; + curr_src.w = src_slice.r; + curr_src.y = src.y + src_slice.b; + curr_src.h = src.h - src_slice.t - src_slice.b; + curr_dst.x = dst.x + dst.w - dst_slice.r; + curr_dst.w = dst_slice.r; + curr_dst.y = dst.y + dst_slice.b; + curr_dst.h = dst.h - dst_slice.t - dst_slice.b; + add_quad(&verts, curr_src, curr_dst); + + JSValue mesh = quads_to_mesh(js, verts); + arrfree(verts); + ret = mesh; +) + +JSC_CCALL(gpu_tile, + HMM_Vec2 size; + JSValue jstex = argv[0]; + JS_GETPROP(js,size.x,jstex,width,number) + JS_GETPROP(js, size.y, jstex, height, number) + + rect src_pixels = js2rect(js, argv[1]); // 'src' as pixel dimensions + rect dst = js2rect(js, argv[2]); // 'dst' as screen coords + + // Determine how many full tiles horizontally/vertically + float fcols, frows; + float remaining_w = modff(dst.w / src_pixels.w, &fcols); + float remaining_h = modff(dst.h / src_pixels.h, &frows); + + // The partial leftover in pixel dimensions + float remain_src_w = remaining_w * src_pixels.w; + float remain_src_h = remaining_h * src_pixels.h; + + // The corresponding leftover area in screen coords (since src -> 1:1 draw) + float remain_dst_w = remain_src_w; + float remain_dst_h = remain_src_h; + + int cols = (int)fcols; + int rows = (int)frows; + + // Convert the whole source rect from pixels into UV coords + // so add_quad can draw the correct portion of the texture. + rect src_full; + src_full.x = src_pixels.x / size.x; + src_full.y = src_pixels.y / size.y; + src_full.w = src_pixels.w / size.x; + src_full.h = src_pixels.h / size.y; + + // For partial leftover in UV coords + float remain_src_w_uv = remain_src_w / size.x; + float remain_src_h_uv = remain_src_h / size.y; + + // We'll set up some "partial" src rect variants: + rect src_partial_x = src_full; // partial in the X dimension + src_partial_x.w = remain_src_w_uv; + + rect src_partial_y = src_full; // partial in the Y dimension + src_partial_y.h = remain_src_h_uv; + + rect src_partial_xy = src_full; // partial in both X and Y + src_partial_xy.w = remain_src_w_uv; + src_partial_xy.h = remain_src_h_uv; + + // Now we tile in screen coords. Each tile is as wide/high as src_pixels (1:1). + // The UV coords for a "full" tile use src_full; + // for partial leftover tiles, we use the partial variants above. + text_vert *verts = NULL; + + float tile_w = src_pixels.w; + float tile_h = src_pixels.h; + + rect curr_dst; + curr_dst.w = tile_w; + curr_dst.h = tile_h; + curr_dst.y = dst.y; + + // Place the full rows & columns + for (int y = 0; y < rows; y++) { + curr_dst.x = dst.x; + for (int x = 0; x < cols; x++) { + add_quad(&verts, src_full, curr_dst); + curr_dst.x += tile_w; + } + // Place the partial tile on the right edge, if any + if (remain_dst_w > 0.0f) { + rect partial_dst = { curr_dst.x, curr_dst.y, remain_dst_w, tile_h }; + add_quad(&verts, src_partial_x, partial_dst); + } + curr_dst.y += tile_h; + } + + // Now place the leftover row (if any) + if (remain_dst_h > 0.0f) { + rect partial_row_dst; + partial_row_dst.y = curr_dst.y; + partial_row_dst.w = tile_w; + partial_row_dst.h = remain_dst_h; + partial_row_dst.x = dst.x; + + // Full columns in this leftover row + for (int x = 0; x < cols; x++) { + add_quad(&verts, src_partial_y, partial_row_dst); + partial_row_dst.x += tile_w; + } + + // Partial corner tile: leftover on the right plus leftover row + if (remain_dst_w > 0.0f) { + rect partial_corner_dst = { partial_row_dst.x, partial_row_dst.y, + remain_dst_w, remain_dst_h }; + add_quad(&verts, src_partial_xy, partial_corner_dst); + } + } + + ret = quads_to_mesh(js,verts); + arrfree(verts); +) + static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { MIST_FUNC_DEF(gpu, claim_window, 1), MIST_FUNC_DEF(gpu, make_pipeline, 1), // loads pipeline state into an object @@ -4327,6 +4604,8 @@ static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { MIST_FUNC_DEF(gpu, wait_for_fences, 2), MIST_FUNC_DEF(gpu, query_fence, 1), MIST_FUNC_DEF(gpu, shader_format, 0), + MIST_FUNC_DEF(gpu, slice9, 4), + MIST_FUNC_DEF(gpu, tile, 3), }; JSC_CCALL(renderpass_bind_pipeline,