working gpu renderer

This commit is contained in:
2025-07-29 15:15:18 -05:00
parent 09c3d5cc4e
commit e5c19e7e80
7 changed files with 738 additions and 2003 deletions

View File

@@ -6,9 +6,42 @@ var video = use('sdl_video')
var surface = use('surface')
var sdl_gpu = use('sdl_gpu')
var io = use('io')
var geometry = use('geometry')
var os = use('os')
// suppose your rendersurface is W×H pixels, and
// your camera.viewport = { x:0, y:0, width:1, height:1 };
// so vpW = W*1, vpH = H*1
def vpW = 640
def vpH = 360
// how many worldunits from center to left/right?
// if zoom = 1 means 1 worldunit == 1 pixel:
def halfW = vpW * 0.5;
def halfH = vpH * 0.5;
// define your clippingvolume in cameraspace:
def l = -halfW, r = +halfW;
def b = -halfH, t = +halfH;
def n = 0, f = 1;
// Metal wants z in [0,1], so we use the “zerotoone” variant:
function makeOrthoMetal(l,r,b,t,n,f){
return [
2/(r-l), 0, 0, 0,
0, 2/(t-b), 0, 0,
0, 0, 1/(f-n), 0,
-(r+l)/(r-l), -(t+b)/(t-b), -n/(f-n), 1
]
}
def P = makeOrthoMetal(l,r,b,t,n,f);
var Pblob = geometry.array_blob(P)
log.console(Pblob.length)
var driver = "vulkan"
switch(os.platform()) {
case "Linux":
@@ -63,7 +96,7 @@ var default_window = {
// Basic properties
title: "Prosperon Window",
width: 640,
height: 480,
height: 360,
// Position - can be numbers or "centered"
x: null, // SDL_WINDOWPOS_null by default
@@ -122,6 +155,7 @@ var device = new sdl_gpu.gpu({
})
device.claim_window(window)
device.set_swapchain(window, 'sdr', 'vsync')
var shader_type = device.shader_format()[0]
shader_type = 'msl'
var std_sampler = new sdl_gpu.sampler(device, default_sampler)
@@ -130,6 +164,25 @@ var std_sampler = new sdl_gpu.sampler(device, default_sampler)
var shader_cache = {}
var pipeline_cache = {}
function upload(copypass, buffer, toblob)
{
var trans = new sdl_gpu.transfer_buffer(device, {
size: toblob.length/8,
usage:"upload"
})
trans.copy_blob(device, toblob)
copypass.upload_to_buffer({
transfer_buffer: trans,
offset:0
}, {
buffer: buffer,
offset: 0,
size: toblob.length/8
})
}
function make_shader(sh_file)
{
var file = `shaders/${shader_type}/${sh_file}.${shader_type}`
@@ -147,210 +200,26 @@ function make_shader(sh_file)
entrypoint: shader_type == "msl" ? "main0" : "main"
}
shader.gpu = context.make_shader(shader)
shader.gpu = new sdl_gpu.shader(device, shader)
shader.reflection = refl;
shader_cache[file] = shader
shader.file = sh_file
return shader
}
function load_shader(vert, frag, pipeline_config) {
// Default to sprite pipeline config if not provided
pipeline_config = pipeline_config || sprite_pipeline_config
// Check cache first
var cache_key = `${vert}:${frag}:${json.encode(pipeline_config)}`
if (pipeline_cache[cache_key])
return pipeline_cache[cache_key]
// Load shader reflections
var vert_reflection = load_shader_reflection(vert, 'vert')
var frag_reflection = load_shader_reflection(frag, 'frag')
if (!vert_reflection || !frag_reflection)
return null
// Load shaders based on platform
var vert_path = `accio/shaders/${shader_type}/${vert}.vert.${shader_type}`
var frag_path = `accio/shaders/${shader_type}/${frag}.frag.${shader_type}`
if (!io.exists(vert_path) || !io.exists(frag_path)) {
log.error(`Shaders not found: ${vert_path}, ${frag_path}`)
return null
}
var vert_code = io.slurpbytes(vert_path)
var frag_code = io.slurpbytes(frag_path)
// Extract shader info from reflection
var vert_shader_info = {
stage: "vertex",
code: vert_code,
entrypoint: "main0",
format: shader_type,
num_samplers: 0,
num_textures: 0,
num_storage_buffers: 0,
num_uniform_buffers: 0
}
log.console(json.encode(vert_reflection))
// Count fragment shader resources
var num_samplers = 0
var num_textures = 0
if (frag_reflection.separate_images)
num_textures = frag_reflection.separate_images.length
if (frag_reflection.separate_samplers)
num_samplers = frag_reflection.separate_samplers.length
var frag_shader_info = {
stage: "fragment",
code: frag_code,
entrypoint: "main0",
format: shader_type,
num_samplers: num_samplers,
num_textures: num_textures,
num_storage_buffers: 0,
num_uniform_buffers: 0
}
// Check shader cache
var vert_cache_key = `${vert}:vert`
var frag_cache_key = `${frag}:frag`
var vert_shader = shader_cache[vert_cache_key]
if (!vert_shader) {
log.console(json.encode(vert_shader_info))
vert_shader = new sdl_gpu.shader(device, vert_shader_info)
if (!vert_shader) {
log.error(`Failed to create vertex shader: ${vert}`)
return null
}
shader_cache[vert_cache_key] = vert_shader
}
var frag_shader = shader_cache[frag_cache_key]
if (!frag_shader) {
frag_shader = new sdl_gpu.shader(device, frag_shader_info)
if (!frag_shader) {
log.error(`Failed to create fragment shader: ${frag}`)
return null
}
shader_cache[frag_cache_key] = frag_shader
}
// Create graphics pipeline using composition
var pipeline_info = create_pipeline_config({
vertex: vert_shader,
fragment: frag_shader,
blend: pipeline_config.blend,
target: pipeline_config.target,
label: pipeline_config.label
})
// Ensure target is properly set
if (!pipeline_info.target || !pipeline_info.target.color_targets) {
log.error("Pipeline target configuration is missing or invalid")
log.console("pipeline_config:", json.encode(pipeline_config))
return null
}
// Set up vertex input layout based on reflection
var vertex_stride = 0
var vertex_attributes = []
// Build vertex attributes from reflection
if (vert_reflection.inputs) {
// Add attributes from reflection
for (var input of vert_reflection.inputs) {
var format = "float2" // default
var size = 8
if (input.type == "vec2") {
format = "float2"
size = 8
} else if (input.type == "vec4") {
format = "float4"
size = 16
}
vertex_attributes.push({
location: input.location,
buffer_slot: 0,
format: format,
offset: vertex_stride
})
vertex_stride += size
}
}
pipeline_info.vertex_buffer_descriptions = [{
slot: 0,
pitch: vertex_stride,
input_rate: "vertex",
instance_step_rate: 0
}]
pipeline_info.vertex_attributes = vertex_attributes
log.console("Creating pipeline with info:")
log.console(json.encode(pipeline_info))
log.console("Target info:")
log.console(json.encode(pipeline_info.target))
log.console("Has color_targets?", pipeline_info.target.color_targets ? "yes" : "no")
var pipeline = new sdl_gpu.graphics_pipeline(device, pipeline_info)
if (!pipeline) {
log.error(`Failed to create graphics pipeline for ${vert}/${frag}`)
return null
}
// Cache the pipeline
pipeline_cache[cache_key] = pipeline
return pipeline
}
// Load default sprite shaders
var sprite_gpu_pipeline = load_shader('sprite', 'sprite')
// Public function to get shader pipelines
prosperon.get_shader_pipeline = function(vert, frag, pipeline_config) {
return load_shader(vert, frag, pipeline_config)
}
// Export pipeline components for custom configs
prosperon.pipeline = {
depth_states: {
default: default_depth_state,
depth_test: {
compare: "less",
test: true,
write: true,
bias: 0,
bias_slope_scale: 0,
bias_clamp: 0
}
},
blend_states: {
disabled: disabled_blend_state,
alpha: alpha_blend_state,
additive: {
enabled: true,
src_rgb: "src_alpha",
dst_rgb: "one",
op_rgb: "add",
src_alpha: "one",
dst_alpha: "one",
op_alpha: "add"
}
},
stencil_states: {
default: default_stencil_state
},
create_config: create_pipeline_config
var usepipe
function load_pipeline(config)
{
log.console(usepipe)
if (usepipe) return usepipe
config.vertex = make_shader(config.vertex).gpu
config.fragment = make_shader(config.fragment).gpu
log.console(json.encode(config))
log.console("ANOTHER NEW PIPELINE")
log.console(config)
usepipe = new sdl_gpu.graphics_pipeline(device, config)
log.console(usepipe)
return usepipe
}
// Initialize ImGui with the window and renderer
@@ -361,7 +230,7 @@ var io = use('io');
var rasterize = use('rasterize');
var time = use('time')
var tilemap = use('tilemap')
var geometry = use('geometry')
var res = use('resources')
var input = use('input')
@@ -452,83 +321,6 @@ function create_pipeline_config(options) {
return config
}
// Sprite pipeline configuration
var sprite_pipeline_config = {
blend: alpha_blend_state,
target: {
color_targets: [{
format: "rgba8",
blend: alpha_blend_state
}],
depth: "d32 float s8" // C code expects "depth" not "depth_stencil_format"
},
label: "sprite pipeline"
}
function updateCameraMatrix(cam) {
def win_w = logical.width
def win_h = logical.height
def view_w = (cam.size?.[0] ?? win_w) / cam.zoom
def view_h = (cam.size?.[1] ?? win_h) / cam.zoom
def ox = cam.pos[0] - view_w * (cam.anchor?.[0] ?? 0)
def oy = cam.pos[1] - view_h * (cam.anchor?.[1] ?? 0)
def vx = (cam.viewport?.x ?? 0) * win_w
def vy = (cam.viewport?.y ?? 0) * win_h
def vw = (cam.viewport?.width ?? 1) * win_w
def vh = (cam.viewport?.height ?? 1) * win_h
def sx = vw / view_w
def sy = vh / view_h // flip-Y later
/* affine matrix that SDL wants (Y going down) */
cam.a = sx
cam.c = vx - sx * ox
cam.e = -sy // <-- minus = flip Y
cam.f = vy + vh + sy * oy
/* convenience inverses */
cam.ia = 1 / cam.a
cam.ic = -cam.c / cam.a
cam.ie = 1 / cam.e
cam.if = -cam.f / cam.e
camera = cam
}
//---- forward transform ----
function worldToScreenPoint([x,y], camera) {
return {
x: camera.a * x + camera.c,
y: camera.e * y + camera.f
};
}
//---- inverse transform ----
function screenToWorldPoint(pos, camera) {
return {
x: camera.ia * pos[0] + camera.ic,
y: camera.ie * pos[1] + camera.if
};
}
//---- rectangle (two corner) ----
function worldToScreenRect({x,y,width,height}, camera) {
// map bottom-left and top-right
def x1 = camera.a * x + camera.c;
def y1 = camera.e * y + camera.f;
def x2 = camera.a * (x + width) + camera.c;
def y2 = camera.e * (y + height) + camera.f;
return {
x:Math.min(x1,x2),
y:Math.min(y1,y2),
width:Math.abs(x2-x1),
height:Math.abs(y2-y1)
}
}
var gameactor
var images = {}
@@ -632,94 +424,121 @@ prosperon.input = function(fn)
poll_input()
}
var sprite_pipeline = {
vertex: "sprite.vert",
fragment: "sprite.frag",
cull: "none",
target: {
color_targets: [
{format: device.swapchain_format(window), blend:disabled_blend_state}
],
},
vertex_buffer_descriptions: [ { slot:0, input_rate: "vertex", instance_step_rate: 0,
pitch: 8},
{slot:1, input_rate:"vertex", instance_step_rate: 0, pitch: 8},
{slot:2, input_rate:"vertex", instance_step_rate: 0, pitch: 16}
],
vertex_attributes: [
{ location: 0, buffer_slot: 0, format: "float2", offset: 0},
{ location: 1, buffer_slot: 1, format: "float2", offset: 0},
{ location: 2, buffer_slot: 2, format: "float4", offset: 0}
],
primitive: "triangle",
blend: alpha_blend_state
}
var pipey = load_pipeline(sprite_pipeline)
prosperon.create_batch = function create_batch(draw_cmds, done) {
var cmd_buffer = device.acquire_cmd_buffer()
var swapchain_texture = cmd_buffer.acquire_swapchain(window)
var img = graphics.texture("pockle")
var gpu_tex = get_img_gpu(img)
var geom = geometry.sprites_to_data([{
pos: {x: 0, y: 0},
texture: img,
color: {r:1,g:1,b:1,a:1}
}])
var pipeline = pipey
device.upload(cmd_buffer, [geom])
var geom = geometry.make_rect_quad({x:-320,y:-180,width:640,height:360})
geom.indices = geometry.make_quad_indices(1)
var pass = cmd_buffer.render_pass({
color_targets: [{
texture: swapchain_texture,
clear_color: {r:0.1, g:0.1, b:0.15, a:1},
load_op: "clear",
store_op: "store"
}]
var cmd_buffer = device.acquire_cmd_buffer()
cmd_buffer.push_vertex_uniform_data(0, Pblob)
var pos_buffer = new sdl_gpu.buffer(device, {
vertex: true,
size: geom.xy.length/8
})
var uv_buffer = new sdl_gpu.buffer(device, {
vertex: true,
size: geom.uv.length/8
})
var color_buffer = new sdl_gpu.buffer(device, {
vertex: true,
size: geom.color.length/8
})
if (!swapchain_texture) {
cmd_buffer.cancel()
} else {
var index_buffer = new sdl_gpu.buffer(device, {
index: true,
size: geom.indices.length/8
})
var cpy_pass = cmd_buffer.copy_pass()
upload(cpy_pass, pos_buffer, geom.xy)
upload(cpy_pass, uv_buffer, geom.uv)
upload(cpy_pass, color_buffer, geom.color)
upload(cpy_pass, index_buffer, geom.indices)
if (!img.gpu) {
img.gpu = new sdl_gpu.texture(device, {
width: img.width,
height: img.height,
layers: 1,
mip_levels: 1,
samples: 0,
type: "2d",
format: "rgba8",
sampler: true,
})
var tbuf = new sdl_gpu.transfer_buffer(device, {
size: img.cpu.pixels.length/8,
usage: "upload"
})
tbuf.copy_blob(device, img.cpu.pixels)
cpy_pass.upload_to_texture({
transfer_buffer: tbuf,
offset: 0,
pixels_per_row: img.cpu.width,
rows_per_lay: img.cpu.height,
}, {
texture: img.gpu,
mip_level: 0,
layer: 0,
x: 0, y: 0, z: 0,
w: img.cpu.width,
h: img.cpu.height,
d: 1
}, false);
}
// Begin render pass
var render_pass = cmd_buffer.render_pass({
color_targets: [{
texture: swapchain_texture,
clear_color: {r: 0.1, g: 0.1, b: 0.15, a: 1.0},
load_op: "clear",
store_op: "store"
}]
})
cpy_pass.end()
var pass = cmd_buffer.swapchain_pass(window)
render_pass.bind_pipeline(sprite_gpu_pipeline)
// Set viewport
render_pass.viewport({
pass.viewport({
x: 0, y: 0,
w: logical.width,
h: logical.height
width: 640,
height: 360
})
// Process draw_image commands
for (var cmd of draw_cmds) {
if (cmd.cmd != "draw_image") continue
var img = graphics.texture(cmd.image)
var gpu_tex = get_img_gpu(img)
if (!gpu_tex) continue
// Set up sprite dimensions
cmd.rect.width ??= img.width
cmd.rect.height ??= img.height
cmd.rect.width = cmd.rect.width * (cmd.scale?.x ?? 1)
cmd.rect.height = cmd.rect.height * (cmd.scale?.y ?? 1)
cmd.rect = worldToScreenRect(cmd.rect, camera)
var geom = geometry.sprites_to_data([{
pos: {x: cmd.rect.x, y: cmd.rect.y},
texture: img,
color: {r:1,g:1,b:1,a:1}
}])
log.console(json.encode(geom))
render_pass.bind_buffers(0, [geom], device)
// Bind texture and sampler
render_pass.bind_samplers(false, 0, [{
texture: gpu_tex,
sampler: std_sampler
}])
// Draw the sprite
log.console("DRWA INDEX")
render_pass.draw(geom.num_vertices, 1, 0, 0, 0) // 6 vertices, 1 instance
log.console("DREW")
}
pass.bind_pipeline(pipeline)
pass.bind_buffers(0, [{buffer:pos_buffer,offset:0}, {buffer:uv_buffer, offset:0}, {buffer:color_buffer, offset:0}])
pass.bind_index_buffer({buffer:index_buffer,offset:0}, 16)
pass.bind_samplers(false, 0, [{texture:img.gpu, sampler:std_sampler}])
pass.draw_indexed(6, 1, 0, 0, 0)
render_pass.end()
pass.end()
cmd_buffer.submit()
if (done) done()

View File

@@ -153,8 +153,6 @@ void actor_free(cell_rt *actor)
JS_FreeAtom(js, actor->actor_sym);
SDL_RemoveTimer(actor->ar);
for (int i = 0; i < arrlen(actor->js_swapchains); i++)
JS_FreeValue(js, actor->js_swapchains[i]);
/* Free timer callbacks stored in actor */
for (int i = 0; i < hmlen(actor->timers); i++) {
@@ -162,7 +160,6 @@ void actor_free(cell_rt *actor)
}
hmfree(actor->timers);
arrfree(actor->js_swapchains);
arrfree(actor->module_registry);
/* Free all letters in the queue */

View File

@@ -65,7 +65,6 @@ mi_heap_t *heap;
void *init_wota;
ModuleEntry *module_registry;
JSValue *js_swapchains;
/* Protects JSContext usage */
SDL_Mutex *mutex; /* for everything else */

View File

@@ -1436,34 +1436,6 @@ JSC_CCALL(os_power_state,
return JS_NULL;
)
JSC_CCALL(os_cull_sprites,
ret = JS_NewArray(js);
int n = 0;
JSValue sprites = argv[0];
shader_globals info = {0}; // TODO: get this as a JS object
rect camera_rect = {0};
camera_rect.x = info.camera_pos_world.x - info.render_size.x/2.0;
camera_rect.y = info.camera_pos_world.y - info.render_size.y/2.0;
camera_rect.w = info.render_size.x;
camera_rect.h = info.render_size.y;
int len = JS_ArrayLength(js,sprites);
for (int i = 0; i < len; i++) {
JSValue sub = JS_GetPropertyUint32(js,sprites,i);
transform *t;
JS_GETATOM(js,t,sub,transform,transform)
rect sprite = transform2rect(t);
if (SDL_HasRectIntersectionFloat(&sprite, &camera_rect)) {
JS_SetPropertyUint32(js,ret,n,JS_DupValue(js,sub));
n++;
}
JS_FreeValue(js,sub);
}
)
static const JSCFunctionListEntry js_util_funcs[] = {
MIST_FUNC_DEF(os, guid, 0),
MIST_FUNC_DEF(os, insertion_sort, 2),
@@ -1524,7 +1496,6 @@ static const JSCFunctionListEntry js_graphics_funcs[] = {
MIST_FUNC_DEF(os, make_texture, 1),
MIST_FUNC_DEF(os, make_gif, 1),
MIST_FUNC_DEF(os, make_aseprite, 1),
MIST_FUNC_DEF(os, cull_sprites, 2),
MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_line_prim, 5),
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),

View File

@@ -56,24 +56,6 @@ struct lrtb {
typedef struct lrtb lrtb;
typedef struct text_vert text_vert;
// Shader globals structure for camera transformations
#pragma pack(push, 1)
typedef struct shader_globals {
HMM_Mat4 world_to_projection;
HMM_Mat4 projection_to_world;
HMM_Mat4 world_to_view;
HMM_Mat4 view_to_projection;
HMM_Vec3 camera_pos_world;
HMM_Vec3 camera_dir_world;
float viewport_min_z;
float viewport_max_z;
HMM_Vec2 viewport_size;
HMM_Vec2 viewport_offset;
HMM_Vec2 render_size;
float time;
} shader_globals;
#pragma pack(pop)
// Common macros for property access
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \

View File

@@ -971,6 +971,34 @@ JSC_CCALL(geometry_tilemap_to_data,
free(index_data);
)
static void print_buffers(float *xy_data, float *uv_data, SDL_FColor *color_data, uint16_t *index_data, int vertex_count, int index_count)
{
printf("=== GEOMETRY BUFFERS ===\n");
printf("XY Data (%d vertices):\n", vertex_count);
for (int i = 0; i < vertex_count; i++) {
printf(" Vertex %d: x=%.2f, y=%.2f\n", i, xy_data[i*2], xy_data[i*2+1]);
}
printf("\nUV Data:\n");
for (int i = 0; i < vertex_count; i++) {
printf(" Vertex %d: u=%.4f, v=%.4f\n", i, uv_data[i*2], uv_data[i*2+1]);
}
printf("\nColor Data:\n");
for (int i = 0; i < vertex_count; i++) {
printf(" Vertex %d: r=%.2f, g=%.2f, b=%.2f, a=%.2f\n",
i, color_data[i].r, color_data[i].g, color_data[i].b, color_data[i].a);
}
printf("\nIndex Data (%d indices):\n", index_count);
for (int i = 0; i < index_count; i += 3) {
printf(" Triangle %d: %d, %d, %d\n", i/3, index_data[i], index_data[i+1], index_data[i+2]);
}
printf("========================\n");
}
JSC_CCALL(geometry_sprites_to_data,
JSValue sprites_array = argv[0];
if (!JS_IsArray(js, sprites_array)) {
@@ -1318,6 +1346,108 @@ JSC_CCALL(geometry_renderitem_clear,
return JS_NULL;
)
JSC_CCALL(geometry_array_blob,
JSValue arr = argv[0];
size_t len = JS_ArrayLength(js,arr);
float data[len];
for (int i = 0; i < len; i++) {
JSValue val = JS_GetPropertyUint32(js,arr,i);
data[i] = js2number(js, val);
JS_FreeValue(js,val);
}
return js_new_blob_stoned_copy(js, data, sizeof(float)*len);
)
JSC_CCALL(geometry_make_quad_indices,
int quads;
if (JS_ToInt32(js, &quads, argv[0]) < 0) {
return JS_ThrowTypeError(js, "quads must be a number");
}
if (quads < 0) {
return JS_ThrowTypeError(js, "quads must be >= 0");
}
int index_count = quads * 6;
uint16_t *index_data = malloc(index_count * sizeof(uint16_t));
int index_idx = 0;
for (int i = 0; i < quads; i++) {
uint16_t base_idx = i * 4;
index_data[index_idx++] = base_idx + 0;
index_data[index_idx++] = base_idx + 1;
index_data[index_idx++] = base_idx + 2;
index_data[index_idx++] = base_idx + 1;
index_data[index_idx++] = base_idx + 3;
index_data[index_idx++] = base_idx + 2;
}
JSValue result = js_new_blob_stoned_copy(js, index_data, index_count * sizeof(uint16_t));
free(index_data);
return result;
)
JSC_CCALL(geometry_make_rect_quad,
rect r = js2rect(js, argv[0]);
// Optional UV rect (default to 0,0,1,1)
rect uv = {0, 0, 1, 1};
if (argc > 1 && !JS_IsNull(argv[1])) {
uv = js2rect(js, argv[1]);
}
// Optional color (default to white)
SDL_FColor color = {1.0f, 1.0f, 1.0f, 1.0f};
if (argc > 2 && !JS_IsNull(argv[2])) {
HMM_Vec4 c = js2color(js, argv[2]);
color.r = c.r;
color.g = c.g;
color.b = c.b;
color.a = c.a;
}
// Allocate buffers for 1 quad (4 vertices)
int vertex_count = 4;
int index_count = 6;
float xy_data[vertex_count*2];
float uv_data[vertex_count*2];
SDL_FColor color_data[vertex_count];
// Set vertex positions (4 corners)
xy_data[0] = r.x; xy_data[1] = r.y; // bottom-left
xy_data[2] = r.x + r.w; xy_data[3] = r.y; // bottom-right
xy_data[4] = r.x; xy_data[5] = r.y + r.h; // top-left
xy_data[6] = r.x + r.w; xy_data[7] = r.y + r.h; // top-right
// Set UV coordinates
uv_data[0] = uv.x; uv_data[1] = uv.y + uv.h; // bottom-left
uv_data[2] = uv.x + uv.w; uv_data[3] = uv.y + uv.h; // bottom-right
uv_data[4] = uv.x; uv_data[5] = uv.y; // top-left
uv_data[6] = uv.x + uv.w; uv_data[7] = uv.y; // top-right
// Set colors
for (int i = 0; i < 4; i++) {
color_data[i] = color;
}
// Create result object
ret = JS_NewObject(js);
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));
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, "num_vertices", JS_NewInt32(js, vertex_count));
)
static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_intersection, 2),
MIST_FUNC_DEF(geometry, rect_intersects, 2),
@@ -1339,6 +1469,9 @@ static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, renderitem_push, 3),
MIST_FUNC_DEF(geometry, renderitem_sort, 0),
MIST_FUNC_DEF(geometry, renderitem_clear, 0),
MIST_FUNC_DEF(geometry, array_blob, 2),
MIST_FUNC_DEF(geometry, make_quad_indices, 1),
MIST_FUNC_DEF(geometry, make_rect_quad, 3),
};
JSValue js_geometry_use(JSContext *js) {

File diff suppressed because it is too large Load Diff