diff --git a/scripts/modules/sdl_render.js b/scripts/modules/sdl_render.js index d4f48ad2..c8ed7a7b 100644 --- a/scripts/modules/sdl_render.js +++ b/scripts/modules/sdl_render.js @@ -73,21 +73,21 @@ render.ellipse = function(pos, radiuses, color = Color.white) } -render.line = function(a, b, color = Color.white) +render.line = function(points, color = Color.white) { context.draw_color(color) - context.line([a,b]) + context.line(points) } render.point = function(pos, color = Color.white) { context.draw_color(color) - context.point(pos) + context.point([pos]) } render.rectangle = function(rect, color = Color.white) { - + } render.geometry = function(mesh, pipeline) @@ -95,40 +95,6 @@ render.geometry = function(mesh, pipeline) } -render.queue = function(cmd) -{ - if (cmd.color && cmd.color != current_color) { - current_color = cmd.color - context.draw_color(current_color) - } - - switch(cmd.type) { - case "rectangle": - - context. - break; - case "geometry": - context.geometry(cmd.texture, cmd.mesh) - break; - - case "line": - context.line(cmd.points, cmd.color) - break; - - case "point": - context.points(cmd.points, cmd.color) - break; - - case "scissor": - context.clip(cmd.rect) - break; - - case "viewport": - context.viewport(cmd.rect) - break; - } -} - render.get_image = function(rect) { return context.get_image(rect) @@ -145,4 +111,9 @@ render.present = function() context.present() } +render.camera = function(cam) +{ + context.camera(cam); +} + return render diff --git a/source/jsffi.c b/source/jsffi.c index 28483445..5bf896be 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -1664,15 +1664,19 @@ shader_globals camera_globals(JSContext *js, JSValue camera) HMM_Mat4 view; if (ortho) { + float left = -anchor.x * size.x; + float bottom = -anchor.y * size.y; + float right = left + size.x; + float top = bottom + size.y; + proj = HMM_Orthographic_RH_NO( - -size.x*0.5, 0.5*size.x, - size.y*0.5, -0.5*size.y, // flip these negative signs based on backend + left, right, + bottom, top, -1.0f, 1.0f ); proj.Columns[3].x += size.x * anchor.x; proj.Columns[3].y += size.y * anchor.y; - view = HMM_Translate((HMM_Vec3){ -transform->pos.x, -transform->pos.y, 0.0f }); } else { proj = HMM_Perspective_RH_NO(fov, size.x/size.y,near_z,far_z); @@ -2710,6 +2714,34 @@ static const JSCFunctionListEntry js_SDL_Window_funcs[] = { MIST_FUNC_DEF(window, mouse_grab, 1), }; +static inline SDL_FPoint renderer_world_to_screen(renderer_ctx *ctx, HMM_Vec2 p) +{ + HMM_Vec4 clip = HMM_MulM4V4( + ctx->cam.world_to_projection, + (HMM_Vec4){ p.x, p.y, 0.0f, 1.0f }); + + float inv_w = 1.0f / clip.w; + float ndc_x = clip.x * inv_w; /* −1 … +1 */ + float ndc_y = clip.y * inv_w; + + float scr_x = (ndc_x * 0.5f + 0.5f) * ctx->cam.render_size.x; + float scr_y = (1.0f - (ndc_y * 0.5f + 0.5f)) * ctx->cam.render_size.y; + + return (SDL_FPoint){ scr_x, scr_y }; +} + +static inline rect renderer_worldrect_to_screen(renderer_ctx *ctx, rect r) +{ + SDL_FPoint tl = renderer_world_to_screen(ctx, (HMM_Vec2){r.x, r.y}); + SDL_FPoint br = renderer_world_to_screen(ctx, (HMM_Vec2){r.x+r.w, r.y+r.h}); + + r.x = tl.x; + r.y = tl.y; + r.w = br.x-tl.x; + r.h = br.y-tl.y; + return r; +} + JSC_CCALL(SDL_Renderer_clear, SDL_Renderer *renderer = js2renderer_ctx(js,self)->sdl; SDL_RenderClear(renderer); @@ -2764,39 +2796,48 @@ JSC_CCALL(renderer_get_image, ) JSC_CCALL(renderer_line, - SDL_Renderer *r = js2renderer_ctx(js,self)->sdl; + if (!JS_IsArray(js, argv[0])) return JS_ThrowReferenceError(js, "First arugment must be an array of points."); + + renderer_ctx *ctx = js2renderer_ctx(js,self); + SDL_Renderer *r = ctx->sdl; - if (JS_IsArray(js,argv[0])) { - int len = js_arrlen(js,argv[0]); - HMM_Vec2 points[len]; - assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint)); - for (int i = 0; i < len; i++) { - JSValue val = JS_GetPropertyUint32(js,argv[0],i); - points[i] = js2vec2(js,val); - JS_FreeValue(js,val); - } - SDL_RenderLines(r,points,len); + int len = js_arrlen(js,argv[0]); + if (len < 2) return JS_ThrowReferenceError(js, "Must provide at least 2 points to render."); + + SDL_FPoint points[len]; + assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint)); + for (int i = 0; i < len; i++) { + JSValue val = JS_GetPropertyUint32(js,argv[0],i); + HMM_Vec2 wpt = js2vec2(js,val); + JS_FreeValue(js,val); + + points[i] = renderer_world_to_screen(ctx, wpt); } + + SDL_RenderLines(r,points,len); ) JSC_CCALL(renderer_point, - SDL_Renderer *r = js2renderer_ctx(js,self)->sdl; + renderer_ctx *ctx = js2renderer_ctx(js, self); + SDL_Renderer *r = ctx->sdl; - if (JS_IsArray(js,argv[0])) { - int len = js_arrlen(js,argv[0]); - HMM_Vec2 points[len]; - assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint)); - for (int i = 0; i < len; i++) { - JSValue val = JS_GetPropertyUint32(js,argv[0],i); - points[i] = js2vec2(js,val); - JS_FreeValue(js,val); + if (JS_IsArray(js, argv[0])) { + int len = js_arrlen(js, argv[0]); + SDL_FPoint pts[len]; + + for (int i = 0; i < len; ++i) { + JSValue val = JS_GetPropertyUint32(js, argv[0], i); + HMM_Vec2 w = js2vec2(js, val); + JS_FreeValue(js, val); + pts[i] = renderer_world_to_screen(ctx, w); } - SDL_RenderPoints(r, points, len); + SDL_RenderPoints(r, pts, len); return JS_UNDEFINED; } - HMM_Vec2 point = transform_point(r, js2vec2(js,argv[0]), &cam_mat); - SDL_RenderPoint(r,point.x,point.y); + HMM_Vec2 w = js2vec2(js, argv[0]); + SDL_FPoint p = renderer_world_to_screen(ctx, w); + SDL_RenderPoint(r, p.x, p.y); ) // Function to translate a list of 2D points @@ -2993,8 +3034,61 @@ JSC_CCALL(renderer_make_sprite_mesh, JS_SetProperty(js, ret, count, number2js(js, count)); ) +JSC_CCALL(renderer_texture_9grid, + renderer_ctx *ctx = js2renderer_ctx(js, self); + SDL_Renderer *r = ctx->sdl; + + SDL_Texture *tex = js2SDL_Texture(js, argv[0]); + + rect src = js2rect(js, argv[1]); + lrtb extents = js2lrtb(js, argv[2]); + float scale = 1; + + rect dst = js2rect(js,argv[3]); + + dst = renderer_worldrect_to_screen(ctx, dst); + + if (!SDL_RenderTexture9Grid(r, tex, &src, + extents.l, extents.r, extents.t, extents.b, + scale, &dst)) + return JS_ThrowReferenceError(js, "SDL_RenderTexture9Grid: %s", SDL_GetError()); +) + JSC_CCALL(renderer_texture, - + renderer_ctx *ctx = js2renderer_ctx(js, self); + SDL_Renderer *r = ctx->sdl; + + SDL_Texture *tex = js2SDL_Texture(js, argv[0]); + rect src = js2rect(js,argv[1]); + transform *xf = js2transform (js, argv[2]); + HMM_Vec2 k = js2vec2 (js, argv[3]); /* default (0,0) */ + + HMM_Mat3 m3 = transform2mat3(xf); /* includes rot & scale */ + + HMM_Vec2 right = mat_right(m3); /* local +X in world */ + HMM_Vec2 up = mat_up (m3); /* local +Y in world (Y-up)*/ + + HMM_Vec2 r2 = HMM_AddV2(right, HMM_MulV2F(up, k.x)); + HMM_Vec2 u2 = HMM_AddV2(up, HMM_MulV2F(right,k.y)); + + r2 = HMM_MulV2F(r2, src.w); + u2 = HMM_MulV2F(u2, src.h); + + HMM_Vec2 origin_w = { xf->pos.x, xf->pos.y }; + HMM_Vec2 right_w = HMM_AddV2(origin_w, r2); + HMM_Vec2 down_w = HMM_AddV2(origin_w, u2); /* SDL wants “down” */ + + SDL_FPoint origin = renderer_world_to_screen(ctx, origin_w); + SDL_FPoint rightp = renderer_world_to_screen(ctx, right_w); + SDL_FPoint downp = renderer_world_to_screen(ctx, down_w); + + if (!SDL_RenderTextureAffine(r, tex, + &src, /* full image */ + &origin, + &rightp, + &downp)) + return JS_ThrowReferenceError(js, + "SDL_RenderTextureAffine: %s", SDL_GetError()); ) static const JSCFunctionListEntry js_renderer_ctx_funcs[] = { diff --git a/tests/camera.js b/tests/camera.js index 9c401705..e5fa9be0 100644 --- a/tests/camera.js +++ b/tests/camera.js @@ -1,4 +1,4 @@ -var render = use('sdl_render') +var render = use('render') var os = use('os') render.initialize({ @@ -38,8 +38,10 @@ var hudcam = { function loop() { render.clear(Color.red) -// render.camera(camera) - render.line([0,0],[50,50]) + render.camera(hudcam) + render.line([[0,0],[100,50]]) + render.point([100,100]) + render.image("button_grey", [100,100]) // draw.rectangle({x:50,y:-50,width:50,height:50}) render.present() $_.delay(loop, 1/60)