tile options for slice9 and tile

This commit is contained in:
2025-01-12 14:42:54 -06:00
parent 5feacca44c
commit 653748363f
2 changed files with 265 additions and 195 deletions

View File

@@ -959,7 +959,8 @@ function calc_image_size(img)
return [img.texture.width*img.rect.width, img.texture.height*img.rect.height];
}
function tile(image, rect = [0,0], color = Color.white)
function tile(image, rect = [0,0], color = Color.white, repeat = {})
{
if (!image) throw Error ('Need an image to render.')
if (typeof image === "string")
@@ -1004,13 +1005,14 @@ 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)
var tile_def = {repeat_x:true, repeat_y:true};
render.tile = function(image, rect, color = Color.white, tile = tile_def, 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);
var mesh = render._main.tile(image.texture, {x:0,y:0,width:image.texture.width,height:image.texture.height}, rect, tile);
current_queue.push({
type:'geometry',
mesh,
@@ -1022,12 +1024,20 @@ render.tile = function(image, rect, color = Color.white, pipeline = sprite_pipel
}
// slice is given in pixels
render.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.white, pipeline = sprite_pipeline) {
var slice9_info = {
tile_top:true,
tile_bottom:true,
tile_left:true,
tile_right:true,
tile_center_x:true,
tile_center_right:true
};
render.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.white, info = slice9_info, 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));
var mesh = render._main.slice9(image.texture, rect, clay.normalizeSpacing(slice), info);
current_queue.push({
type: 'geometry',
mesh,

View File

@@ -4314,171 +4314,313 @@ JSC_CCALL(gpu_compute_pipeline,
return SDL_GPUComputePipeline2js(js,pipeline);
)
static inline void add_quad(text_vert **verts, rect src, rect dst)
static inline void add_quad(text_vert **verts, rect *restrict src, rect *restrict dst)
{
text_vert v = (text_vert){
.pos = (HMM_Vec2){dst.x, dst.y},
.uv = (HMM_Vec2){src.x,src.y},
.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},
.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},
.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},
.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);
}
static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float tex_w, float tex_h, bool tile_x, bool tile_y)
{
// Convert the incoming UV rect into pixel coords
rect src_px = {
.x = src_uv.x * tex_w,
.y = src_uv.y * tex_h,
.w = src_uv.w * tex_w,
.h = src_uv.h * tex_h
};
// If src_px or dst is degenerate, early out
if (src_px.w <= 0.0f || src_px.h <= 0.0f || dst.w <= 0.0f || dst.h <= 0.0f)
return;
// How many full tiles horizontally/vertically?
// If not tiling in a dimension, we treat it as exactly 1 tile (no leftover).
float cols_f, rows_f;
float remain_wf = 0.0f;
float remain_hf = 0.0f;
if (tile_x) {
remain_wf = modff(dst.w / src_px.w, &cols_f);
} else {
// Only 1 "column" covering entire width, no leftover
cols_f = 1.0f;
remain_wf = 0.0f;
}
if (tile_y) {
remain_hf = modff(dst.h / src_px.h, &rows_f);
} else {
// Only 1 "row" covering entire height, no leftover
rows_f = 1.0f;
remain_hf = 0.0f;
}
int cols = (int)cols_f;
int rows = (int)rows_f;
// The leftover portion in screen coords (pixels)
float remain_dst_w = remain_wf * src_px.w;
float remain_dst_h = remain_hf * src_px.h;
// Build the UV rect for a “full” tile
rect src_full = src_uv;
// Partial leftover in UV
float remain_src_w_uv = remain_dst_w / tex_w;
float remain_src_h_uv = remain_dst_h / tex_h;
// For partial leftover in X dimension
rect src_partial_x = src_full;
src_partial_x.w = remain_src_w_uv;
// For partial leftover in Y dimension
rect src_partial_y = src_full;
src_partial_y.h = remain_src_h_uv;
// For partial leftover in both X & Y
rect src_partial_xy = src_full;
src_partial_xy.w = remain_src_w_uv;
src_partial_xy.h = remain_src_h_uv;
// Each tile is drawn 1:1 in screen coords
float tile_w = tile_x ? src_px.w : dst.w; // If not tiling horizontally, match the entire dst width
float tile_h = tile_y ? src_px.h : dst.h; // If not tiling vertically, match the entire dst height
rect curr_dst;
curr_dst.w = tile_w;
curr_dst.h = tile_h;
curr_dst.y = dst.y;
// Loop over rows
for (int y = 0; y < rows; y++) {
curr_dst.x = dst.x;
// Loop over columns
for (int x = 0; x < cols; x++) {
add_quad(verts, &src_full, &curr_dst);
curr_dst.x += tile_w;
}
// Right-side leftover tile (only if tile_x is true)
if (tile_x && remain_dst_w > 0.0f) {
rect partial_dst = {
.x = curr_dst.x, .y = curr_dst.y,
.w = remain_dst_w, .h = tile_h
};
add_quad(verts, &src_partial_x, &partial_dst);
}
curr_dst.y += tile_h;
}
// Bottom leftover row (only if tile_y is true)
if (tile_y && remain_dst_h > 0.0f) {
rect partial_row_dst;
partial_row_dst.w = tile_w;
partial_row_dst.h = remain_dst_h;
partial_row_dst.y = curr_dst.y;
partial_row_dst.x = dst.x;
// Full columns in 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 leftover corner (both X & Y leftover)
if (tile_x && remain_dst_w > 0.0f) {
rect partial_corner_dst = {
.x = partial_row_dst.x, .y = partial_row_dst.y,
.w = remain_dst_w, .h = remain_dst_h
};
add_quad(verts, &src_partial_xy, &partial_corner_dst);
}
}
}
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
rect dst = js2rect(js, argv[1]);
// Full texture in UV coords
rect src = {
.x = 0, .y = 0,
.w = 1, .h = 1
};
lrtb src_slice = js2lrtb(js,argv[2]);
// The “slice” LRTB in PIXELS, but we convert to UV below
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;*/
JS_GETPROP(js, size.x, jstex, width, number)
JS_GETPROP(js, size.y, jstex, height, number)
JSValue info = argv[3];
int tile_top, tile_bottom, tile_left, tile_right, center_x, center_y;
JS_GETPROP(js,tile_top, info, tile_top, bool)
JS_GETPROP(js,tile_bottom,info,tile_bottom,bool)
JS_GETPROP(js,tile_left,info,tile_left,bool)
JS_GETPROP(js,tile_right,info,tile_right,bool)
JS_GETPROP(js, center_x, info, tile_center_x, bool)
JS_GETPROP(js, center_y, info, tile_center_y, bool)
// Convert the slice edges from pixel to UV
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
// bottom-left corner (single quad)
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);
add_quad(&verts, &curr_src, &curr_dst);
// top left
// top-left corner (single quad)
curr_src = src;
curr_dst = dst;
curr_src.x = src.x;
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 = dst;
curr_dst.x = dst.x;
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);
curr_dst.w = dst_slice.l;
curr_dst.h = dst_slice.t;
add_quad(&verts, &curr_src, &curr_dst);
// center
// bottom-right corner (single quad)
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.y = src.y;
curr_src.w = src_slice.r;
curr_src.h = src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst.w - dst_slice.r;
curr_dst.y = dst.y;
curr_dst.w = dst_slice.r;
curr_dst.h = dst_slice.b;
add_quad(&verts, curr_src, curr_dst);
add_quad(&verts, &curr_src, &curr_dst);
// top right
// top-right corner (single quad)
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_src.h = src_slice.t;
curr_dst = dst;
curr_dst.x = dst.x + dst.w - dst_slice.r;
curr_dst.y = dst.y + dst.h - dst_slice.t;
curr_dst.w = dst_slice.r;
curr_dst.h = dst_slice.t;
add_quad(&verts, &curr_src, &curr_dst);
// left bar (tiled)
curr_src = src;
curr_src.x = src.x;
curr_src.y = src.y + src_slice.b;
curr_src.w = src_slice.l;
curr_src.h = src.h - src_slice.t - src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x;
curr_dst.y = dst.y + dst_slice.b;
curr_dst.w = dst_slice.l;
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
add_quad(&verts, curr_src, curr_dst);
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_left,tile_left);
// right bar (tiled)
curr_src = src;
curr_src.x = src.x + src.w - src_slice.r;
curr_src.y = src.y + src_slice.b;
curr_src.w = src_slice.r;
curr_src.h = src.h - src_slice.t - src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst.w - dst_slice.r;
curr_dst.y = dst.y + dst_slice.b;
curr_dst.w = dst_slice.r;
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_right,tile_right);
// bottom bar (tiled)
curr_src = src;
curr_src.x = src.x + src_slice.l;
curr_src.y = src.y;
curr_src.w = src.w - src_slice.l - src_slice.r;
curr_src.h = src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst_slice.l;
curr_dst.y = dst.y;
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
curr_dst.h = dst_slice.b;
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_bottom,tile_bottom);
// top bar (tiled)
curr_src = src;
curr_src.x = src.x + src_slice.l;
curr_src.y = src.y + src.h - src_slice.t;
curr_src.w = src.w - src_slice.l - src_slice.r;
curr_src.h = src_slice.t;
curr_dst = dst;
curr_dst.x = dst.x + dst_slice.l;
curr_dst.y = dst.y + dst.h - dst_slice.t;
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
curr_dst.h = dst_slice.t;
tile_region(&verts, curr_src, curr_dst, size.x, size.y,tile_top,tile_top);
// center (tiled)
curr_src = src;
curr_src.x = src.x + src_slice.l;
curr_src.y = src.y + src_slice.b;
curr_src.w = src.w - src_slice.l - src_slice.r;
curr_src.h = src.h - src_slice.t - src_slice.b;
curr_dst = dst;
curr_dst.x = dst.x + dst_slice.l;
curr_dst.y = dst.y + dst_slice.b;
curr_dst.w = dst.w - dst_slice.l - dst_slice.r;
curr_dst.h = dst.h - dst_slice.t - dst_slice.b;
tile_region(&verts, curr_src, curr_dst, size.x, size.y, center_x,center_y);
JSValue mesh = quads_to_mesh(js, verts);
arrfree(verts);
@@ -4493,96 +4635,14 @@ JSC_CCALL(gpu_tile,
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.
int tilex, tiley;
JSValue jstile = argv[3];
JS_GETPROP(js,tilex,jstile,repeat_x,bool)
JS_GETPROP(js,tiley,jstile,repeat_y,bool)
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);
}
}
tile_region(&verts, src_pixels, dst, size.x, size.y,tilex,tiley);
ret = quads_to_mesh(js,verts);
arrfree(verts);
)
@@ -4604,8 +4664,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),
MIST_FUNC_DEF(gpu, slice9, 3),
MIST_FUNC_DEF(gpu, tile, 4),
};
JSC_CCALL(renderpass_bind_pipeline,