This commit is contained in:
2025-07-14 09:52:25 -05:00
parent d1c7ff768d
commit 19ce1008b1
5 changed files with 441 additions and 194 deletions

View File

@@ -812,11 +812,25 @@ JSC_CCALL(geometry_tilemap_to_data,
// Get tilemap properties
double offset_x, offset_y, size_x, size_y;
double pos_x = 0, pos_y = 0;
JS_GETPROP(js, offset_x, tilemap_obj, offset_x, number)
JS_GETPROP(js, offset_y, tilemap_obj, offset_y, number)
JS_GETPROP(js, size_x, tilemap_obj, size_x, number)
JS_GETPROP(js, size_y, tilemap_obj, size_y, number)
// Get position properties (optional, default to 0)
JSValue pos_x_val = JS_GetPropertyStr(js, tilemap_obj, "pos_x");
if (!JS_IsNull(pos_x_val)) {
JS_ToFloat64(js, &pos_x, pos_x_val);
}
JS_FreeValue(js, pos_x_val);
JSValue pos_y_val = JS_GetPropertyStr(js, tilemap_obj, "pos_y");
if (!JS_IsNull(pos_y_val)) {
JS_ToFloat64(js, &pos_y, pos_y_val);
}
JS_FreeValue(js, pos_y_val);
JSValue tiles_array = JS_GetPropertyStr(js, tilemap_obj, "tiles");
if (!JS_IsArray(js, tiles_array)) {
JS_FreeValue(js, tiles_array);
@@ -870,8 +884,8 @@ JSC_CCALL(geometry_tilemap_to_data,
// x and y are array indices, need to convert to logical coordinates
float logical_x = x + offset_x;
float logical_y = y + offset_y;
float world_x = logical_x * size_x;
float world_y = logical_y * size_y;
float world_x = logical_x * size_x + pos_x;
float world_y = logical_y * size_y + pos_y;
// Set vertex positions (4 corners of the tile)
int base = vertex_idx * 2;
@@ -957,6 +971,277 @@ JSC_CCALL(geometry_tilemap_to_data,
free(index_data);
)
JSC_CCALL(geometry_sprites_to_data,
JSValue sprites_array = argv[0];
if (!JS_IsArray(js, sprites_array)) {
return JS_ThrowTypeError(js, "sprites must be an array");
}
int sprite_count = JS_ArrayLength(js, sprites_array);
if (sprite_count == 0) {
return JS_NewObject(js);
}
// Allocate buffers - 4 vertices per sprite
int vertex_count = sprite_count * 4;
int index_count = sprite_count * 6;
float *xy_data = malloc(vertex_count * 2 * sizeof(float));
float *uv_data = malloc(vertex_count * 2 * sizeof(float));
SDL_FColor *color_data = malloc(vertex_count * sizeof(SDL_FColor));
uint16_t *index_data = malloc(index_count * sizeof(uint16_t));
// Generate vertices
int vertex_idx = 0;
int index_idx = 0;
for (int i = 0; i < sprite_count; i++) {
JSValue sprite = JS_GetPropertyUint32(js, sprites_array, i);
// Get sprite properties
JSValue pos_val = JS_GetPropertyStr(js, sprite, "pos");
JSValue texture_val = JS_GetPropertyStr(js, sprite, "texture");
JSValue color_val = JS_GetPropertyStr(js, sprite, "color");
double width = 32, height = 32, anchor_x = 0.5, anchor_y = 0.5;
// Try to get width/height from sprite first, otherwise use texture dimensions
JSValue width_val = JS_GetPropertyStr(js, sprite, "width");
if (!JS_IsNull(width_val)) {
JS_ToFloat64(js, &width, width_val);
} else if (!JS_IsNull(texture_val)) {
// Get width from texture
JSValue texture_width = JS_GetPropertyStr(js, texture_val, "width");
if (!JS_IsNull(texture_width)) {
JS_ToFloat64(js, &width, texture_width);
}
JS_FreeValue(js, texture_width);
}
JS_FreeValue(js, width_val);
JSValue height_val = JS_GetPropertyStr(js, sprite, "height");
if (!JS_IsNull(height_val)) {
JS_ToFloat64(js, &height, height_val);
} else if (!JS_IsNull(texture_val)) {
// Get height from texture
JSValue texture_height = JS_GetPropertyStr(js, texture_val, "height");
if (!JS_IsNull(texture_height)) {
JS_ToFloat64(js, &height, texture_height);
}
JS_FreeValue(js, texture_height);
}
JS_FreeValue(js, height_val);
JSValue anchor_x_val = JS_GetPropertyStr(js, sprite, "anchor_x");
if (!JS_IsNull(anchor_x_val)) {
JS_ToFloat64(js, &anchor_x, anchor_x_val);
}
JS_FreeValue(js, anchor_x_val);
JSValue anchor_y_val = JS_GetPropertyStr(js, sprite, "anchor_y");
if (!JS_IsNull(anchor_y_val)) {
JS_ToFloat64(js, &anchor_y, anchor_y_val);
}
JS_FreeValue(js, anchor_y_val);
HMM_Vec2 pos = js2vec2(js, pos_val);
// Calculate sprite corners with anchor
float half_w = width * 0.5f;
float half_h = height * 0.5f;
float anchor_offset_x = width * anchor_x - half_w;
float anchor_offset_y = height * anchor_y - half_h;
float left = pos.x - half_w - anchor_offset_x;
float right = pos.x + half_w - anchor_offset_x;
float bottom = pos.y - half_h - anchor_offset_y;
float top = pos.y + half_h - anchor_offset_y;
// Set vertex positions (4 corners of the sprite)
int base = vertex_idx * 2;
xy_data[base + 0] = left; // bottom-left
xy_data[base + 1] = bottom;
xy_data[base + 2] = right; // bottom-right
xy_data[base + 3] = bottom;
xy_data[base + 4] = left; // top-left
xy_data[base + 5] = top;
xy_data[base + 6] = right; // top-right
xy_data[base + 7] = top;
// Get UV coordinates from texture (if available)
if (!JS_IsNull(texture_val)) {
JSValue rect_val = JS_GetPropertyStr(js, texture_val, "rect");
if (!JS_IsNull(rect_val)) {
rect uv_rect = js2rect(js, rect_val);
// Get texture dimensions to normalize pixel coordinates
double tex_width = 1.0, tex_height = 1.0;
JSValue texture_width = JS_GetPropertyStr(js, texture_val, "width");
JSValue texture_height = JS_GetPropertyStr(js, texture_val, "height");
if (!JS_IsNull(texture_width)) {
JS_ToFloat64(js, &tex_width, texture_width);
}
if (!JS_IsNull(texture_height)) {
JS_ToFloat64(js, &tex_height, texture_height);
}
JS_FreeValue(js, texture_width);
JS_FreeValue(js, texture_height);
// The rect contains pixel coordinates, normalize them
float u0 = uv_rect.x / tex_width;
float v0 = uv_rect.y / tex_height;
float u1 = (uv_rect.x + uv_rect.w) / tex_width;
float v1 = (uv_rect.y + uv_rect.h) / tex_height;
// Set UVs based on normalized texture rect
uv_data[base + 0] = u0; uv_data[base + 1] = v1; // bottom-left
uv_data[base + 2] = u1; uv_data[base + 3] = v1; // bottom-right
uv_data[base + 4] = u0; uv_data[base + 5] = v0; // top-left
uv_data[base + 6] = u1; uv_data[base + 7] = v0; // top-right
JS_FreeValue(js, rect_val);
} else {
// Default UVs (0-1)
uv_data[base + 0] = 0.0f; uv_data[base + 1] = 1.0f;
uv_data[base + 2] = 1.0f; uv_data[base + 3] = 1.0f;
uv_data[base + 4] = 0.0f; uv_data[base + 5] = 0.0f;
uv_data[base + 6] = 1.0f; uv_data[base + 7] = 0.0f;
}
} else {
// Default UVs (0-1)
uv_data[base + 0] = 0.0f; uv_data[base + 1] = 1.0f;
uv_data[base + 2] = 1.0f; uv_data[base + 3] = 1.0f;
uv_data[base + 4] = 0.0f; uv_data[base + 5] = 0.0f;
uv_data[base + 6] = 1.0f; uv_data[base + 7] = 0.0f;
}
// Set colors
SDL_FColor default_color = {1.0f, 1.0f, 1.0f, 1.0f};
if (!JS_IsNull(color_val)) {
HMM_Vec4 color = js2color(js, color_val);
default_color.r = color.r;
default_color.g = color.g;
default_color.b = color.b;
default_color.a = color.a;
}
for (int j = 0; j < 4; j++) {
color_data[vertex_idx + j] = default_color;
}
// Set indices (two triangles per sprite)
uint16_t base_idx = vertex_idx;
index_data[index_idx++] = base_idx + 0; // triangle 1: 0,1,2
index_data[index_idx++] = base_idx + 1;
index_data[index_idx++] = base_idx + 2;
index_data[index_idx++] = base_idx + 1; // triangle 2: 1,3,2
index_data[index_idx++] = base_idx + 3;
index_data[index_idx++] = base_idx + 2;
vertex_idx += 4;
JS_FreeValue(js, pos_val);
JS_FreeValue(js, texture_val);
JS_FreeValue(js, color_val);
JS_FreeValue(js, sprite);
}
// Create result object with blob data
ret = JS_NewObject(js);
// Create blobs for each data type
JSValue xy_blob = js_new_blob_stoned_copy(js, xy_data, vertex_count * 2 * sizeof(float));
JSValue uv_blob = js_new_blob_stoned_copy(js, uv_data, vertex_count * 2 * sizeof(float));
JSValue color_blob = js_new_blob_stoned_copy(js, color_data, vertex_count * sizeof(SDL_FColor));
JSValue index_blob = js_new_blob_stoned_copy(js, index_data, index_count * sizeof(uint16_t));
JS_SetPropertyStr(js, ret, "xy", xy_blob);
JS_SetPropertyStr(js, ret, "xy_stride", JS_NewInt32(js, 2 * sizeof(float)));
JS_SetPropertyStr(js, ret, "uv", uv_blob);
JS_SetPropertyStr(js, ret, "uv_stride", JS_NewInt32(js, 2 * sizeof(float)));
JS_SetPropertyStr(js, ret, "color", color_blob);
JS_SetPropertyStr(js, ret, "color_stride", JS_NewInt32(js, sizeof(SDL_FColor)));
JS_SetPropertyStr(js, ret, "indices", index_blob);
JS_SetPropertyStr(js, ret, "num_vertices", JS_NewInt32(js, vertex_count));
JS_SetPropertyStr(js, ret, "num_indices", JS_NewInt32(js, index_count));
JS_SetPropertyStr(js, ret, "size_indices", JS_NewInt32(js, 2)); // using uint16_t
free(xy_data);
free(uv_data);
free(color_data);
free(index_data);
)
JSC_CCALL(geometry_transform_xy_blob,
// argv[0] = xy blob (contains vertex positions as float pairs)
// argv[1] = camera transform parameters [a, c, e, f]
JSValue xy_blob = argv[0];
if (!js_is_blob(js, xy_blob)) {
return JS_ThrowTypeError(js, "First argument must be an XY blob");
}
JSValue camera_params = argv[1];
if (!JS_IsArray(js, camera_params) || JS_ArrayLength(js, camera_params) != 4) {
return JS_ThrowTypeError(js, "Second argument must be an array of 4 camera transform parameters [a, c, e, f]");
}
// Get camera transform parameters
double a, c, e, f;
JSValue a_val = JS_GetPropertyUint32(js, camera_params, 0);
JSValue c_val = JS_GetPropertyUint32(js, camera_params, 1);
JSValue e_val = JS_GetPropertyUint32(js, camera_params, 2);
JSValue f_val = JS_GetPropertyUint32(js, camera_params, 3);
JS_ToFloat64(js, &a, a_val);
JS_ToFloat64(js, &c, c_val);
JS_ToFloat64(js, &e, e_val);
JS_ToFloat64(js, &f, f_val);
JS_FreeValue(js, a_val);
JS_FreeValue(js, c_val);
JS_FreeValue(js, e_val);
JS_FreeValue(js, f_val);
// Get blob data
size_t xy_size;
float *xy_data = (float*)js_get_blob_data(js, &xy_size, xy_blob);
if (!xy_data) {
return JS_ThrowTypeError(js, "Failed to get XY blob data");
}
// Calculate number of vertices (each vertex has 2 floats: x, y)
int vertex_count = xy_size / (2 * sizeof(float));
if (vertex_count * 2 * sizeof(float) != xy_size) {
return JS_ThrowTypeError(js, "XY blob size is not a multiple of vertex size");
}
// Allocate new buffer for transformed coordinates
float *transformed_xy = malloc(xy_size);
if (!transformed_xy) {
return JS_ThrowTypeError(js, "Failed to allocate memory for transformed coordinates");
}
// Apply camera transformation to each vertex
for (int i = 0; i < vertex_count; i++) {
float world_x = xy_data[i * 2 + 0];
float world_y = xy_data[i * 2 + 1];
// Apply 2D affine transformation: screen = world * camera_matrix
float screen_x = a * world_x + c;
float screen_y = e * world_y + f;
transformed_xy[i * 2 + 0] = screen_x;
transformed_xy[i * 2 + 1] = screen_y;
}
// Create new blob with transformed data
JSValue transformed_blob = js_new_blob_stoned_copy(js, transformed_xy, xy_size);
free(transformed_xy);
ret = transformed_blob;
)
static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_intersection, 2),
MIST_FUNC_DEF(geometry, rect_intersects, 2),
@@ -969,6 +1254,8 @@ static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_move, 2),
MIST_FUNC_DEF(geometry, rect_transform, 2),
MIST_FUNC_DEF(geometry, tilemap_to_data, 1),
MIST_FUNC_DEF(geometry, sprites_to_data, 1),
MIST_FUNC_DEF(geometry, transform_xy_blob, 2),
MIST_FUNC_DEF(gpu, tile, 4),
MIST_FUNC_DEF(gpu, slice9, 3),
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),