Files
cell/source/jsffi.c
John Alanbrook b93a5a3ac0
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
refactor draw2d and render
2025-04-25 17:56:17 -05:00

7211 lines
213 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "jsffi.h"
#include "font.h"
#include "datastream.h"
#include "stb_ds.h"
#include "stb_image.h"
#include "stb_rect_pack.h"
#define STB_DXT_IMPLEMENTATION
#include "stb_dxt.h"
#include "string.h"
#include "spline.h"
#include <assert.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include "render.h"
#include "model.h"
#include "HandmadeMath.h"
#include "par/par_streamlines.h"
#include "par/par_shapes.h"
#include <stdint.h>
#include "cute_aseprite.h"
#include "cgltf.h"
#include "physfs.h"
#include "prosperon.h"
#include "qjs_dmon.h"
#include "qjs_nota.h"
#include "qjs_wota.h"
#include "qjs_enet.h"
#include "qjs_soloud.h"
#include "qjs_qr.h"
void gui_input(SDL_Event *e);
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/utsname.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#endif
#endif
#include "wildmatch.h"
#include "freelist.h"
#include "sprite.h"
#include "rtree.h"
typedef struct rtree rtree;
#include <SDL3/SDL.h>
#include <SDL3/SDL_gpu.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL_loadso.h>
#include <SDL3/SDL_cpuinfo.h>
#define JS_SetProperty(js, tar, str, val) JS_SetPropertyStr(js, tar, #str, val)
#define JS_GetProperty(js, tar, atom) JS_GetPropertyStr(js, tar, #atom)
int randombytes(void *buf, size_t n);
static int trace = 0;
#ifdef __APPLE__
#include <Accelerate/Accelerate.h>
//#else
//#include <cblas.h>
#endif
#define UPPER_MASK 0x80000000
#define LOWER_MASK 0x7fffffff
#define TEMPERING_MASK_B 0x9d2c5680
#define TEMPERING_MASK_C 0xefc60000
inline static void m_seedRand(MTRand* rand, uint32_t seed) {
/* set initial seeds to mt[STATE_VECTOR_LENGTH] using the generator
* from Line 25 of Table 1 in: Donald Knuth, "The Art of Computer
* Programming," Vol. 2 (2nd Ed.) pp.102.
*/
rand->mt[0] = seed & 0xffffffff;
for(rand->index=1; rand->index<STATE_VECTOR_LENGTH; rand->index++) {
rand->mt[rand->index] = (6069 * rand->mt[rand->index-1]) & 0xffffffff;
}
}
uint32_t genRandLong(MTRand* rand) {
uint32_t y;
static uint32_t mag[2] = {0x0, 0x9908b0df}; /* mag[x] = x * 0x9908b0df for x = 0,1 */
if(rand->index >= STATE_VECTOR_LENGTH || rand->index < 0) {
/* generate STATE_VECTOR_LENGTH words at a time */
int32_t kk;
if(rand->index >= STATE_VECTOR_LENGTH+1 || rand->index < 0) {
m_seedRand(rand, 4357);
}
for(kk=0; kk<STATE_VECTOR_LENGTH-STATE_VECTOR_M; kk++) {
y = (rand->mt[kk] & UPPER_MASK) | (rand->mt[kk+1] & LOWER_MASK);
rand->mt[kk] = rand->mt[kk+STATE_VECTOR_M] ^ (y >> 1) ^ mag[y & 0x1];
}
for(; kk<STATE_VECTOR_LENGTH-1; kk++) {
y = (rand->mt[kk] & UPPER_MASK) | (rand->mt[kk+1] & LOWER_MASK);
rand->mt[kk] = rand->mt[kk+(STATE_VECTOR_M-STATE_VECTOR_LENGTH)] ^ (y >> 1) ^ mag[y & 0x1];
}
y = (rand->mt[STATE_VECTOR_LENGTH-1] & UPPER_MASK) | (rand->mt[0] & LOWER_MASK);
rand->mt[STATE_VECTOR_LENGTH-1] = rand->mt[STATE_VECTOR_M-1] ^ (y >> 1) ^ mag[y & 0x1];
rand->index = 0;
}
y = rand->mt[rand->index++];
y ^= (y >> 11);
y ^= (y << 7) & TEMPERING_MASK_B;
y ^= (y << 15) & TEMPERING_MASK_C;
y ^= (y >> 18);
return y;
}
double genRand(MTRand* rand) {
return((double)genRandLong(rand) / (uint32_t)0xffffffff);
}
typedef struct texture_vertex {
float x, y, z;
float u, v;
uint8_t r, g, b,a;
} texture_vertex;
#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)
static inline size_t typed_array_bytes(JSTypedArrayEnum type) {
switch(type) {
case JS_TYPED_ARRAY_UINT8C:
case JS_TYPED_ARRAY_INT8:
case JS_TYPED_ARRAY_UINT8:
return 1;
case JS_TYPED_ARRAY_INT16:
case JS_TYPED_ARRAY_UINT16:
return 2;
case JS_TYPED_ARRAY_INT32:
case JS_TYPED_ARRAY_UINT32:
case JS_TYPED_ARRAY_FLOAT32:
return 4;
case JS_TYPED_ARRAY_BIG_INT64:
case JS_TYPED_ARRAY_BIG_UINT64:
case JS_TYPED_ARRAY_FLOAT64:
return 8;
default:
return 0; // Return 0 for unknown types
}
}
#define JS_GETNUM(JS,VAL,I,TO,TYPE) { \
JSValue val = JS_GetPropertyUint32(JS,VAL,I); \
TO = js2##TYPE(JS, val); \
JS_FreeValue(JS, val); } \
static inline int js2bool(JSContext *js, JSValue v) { return JS_ToBool(js,v);}
static inline const char *js2cstring(JSContext *js, JSValue v) { return JS_ToCString(js,v); }
JSValue number2js(JSContext *js, double g) { return JS_NewFloat64(js,g); }
double js2number(JSContext *js, JSValue v) {
double g;
JS_ToFloat64(js, &g, v);
if (isnan(g)) g = 0;
return g;
}
JSValue js_getpropertyuint32(JSContext *js, JSValue v, unsigned int i)
{
JSValue ret = JS_GetPropertyUint32(js,v,i);
JS_FreeValue(js,ret);
return ret;
}
double js_getnum_uint32(JSContext *js, JSValue v, unsigned int i)
{
JSValue val = JS_GetPropertyUint32(js,v,i);
double ret = js2number(js, val);
JS_FreeValue(js,val);
return ret;
}
static HMM_Mat3 cam_mat;
double js_getnum_str(JSContext *js, JSValue v, const char *str)
{
JSValue val = JS_GetPropertyStr(js,v,str);
double ret = js2number(js,val);
JS_FreeValue(js,val);
return ret;
}
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
TARGET = js2##TYPE(JS, __##PROP##__v); \
JS_FreeValue(JS,__##PROP##__v); }\
#define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#ATOM); \
TARGET = js2##TYPE(JS, __##PROP##__v); \
JS_FreeValue(JS,__##PROP##__v); }\
#define JS_SETATOM(JS, TARGET, ATOM, VALUE, TYPE) JS_SetProperty(JS, TARGET, #ATOM, TYPE##2js(JS, VALUE));
int JS_GETBOOL(JSContext *js, JSValue v, const char *prop)
{
JSValue __v = JS_GetPropertyStr(js,v,prop);
int r = JS_ToBool(js,__v);
JS_FreeValue(js,__v);
return r;
}
JSValue js_getproperty(JSContext *js, JSValue v, const char *prop)
{
JSValue ret = JS_GetPropertyStr(js, v, prop);
JS_FreeValue(js,ret);
return ret;
}
void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr)
{
free(ptr);
}
JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index)
{
JSValue tstack[3];
tstack[1] = JS_UNDEFINED;
tstack[2] = JS_UNDEFINED;
if (copy)
tstack[0] = JS_NewArrayBufferCopy(js,data,size);//, make_gpu_buffer, NULL, 1);
else
tstack[0] = JS_NewArrayBuffer(js,data,size,free_gpu_buffer, NULL, 0);
JSValue ret = JS_NewTypedArray(js, 3, tstack, type);
JS_SetPropertyStr(js,ret,"stride", number2js(js,typed_array_bytes(type)*elements));
JS_SetPropertyStr(js,ret,"elen", number2js(js,typed_array_bytes(type)));
JS_SetPropertyStr(js,ret,"index", JS_NewBool(js,index));
JS_FreeValue(js,tstack[0]);
return ret;
}
void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size)
{
size_t o, len, bytes, msize;
JSValue buf = JS_GetTypedArrayBuffer(js, argv, &o, &len, &bytes);
void *data = JS_GetArrayBuffer(js, &msize, buf);
JS_FreeValue(js,buf);
if (stride) *stride = js_getnum_str(js, argv, "stride");
if (size) *size = msize;
return data;
}
typedef struct {
JSValue val;
void *ptr;
size_t size;
int need_new;
} BufferCheckResult;
static BufferCheckResult get_or_extend_buffer(
JSContext *js,
JSValue old_mesh,
const char *prop,
size_t needed_size,
int type,
int elements_per_item,
int copy,
int index
) {
BufferCheckResult res = { JS_UNDEFINED, NULL, 0, 0 };
if (!JS_IsUndefined(old_mesh)) {
JSValue old_buf = JS_GetProperty(js, old_mesh, prop);
if (!JS_IsUndefined(old_buf)) {
size_t old_size;
void *data = get_gpu_buffer(js, old_buf, NULL, &old_size);
if (data && old_size >= needed_size) {
// Old buffer is large enough
res.val = old_buf; // keep it
res.ptr = data;
res.size = old_size;
return res;
}
JS_FreeValue(js, old_buf);
}
}
// If we reach here, we need a new buffer
res.need_new = 1;
return res;
}
#ifndef _WIN32
#include <sys/resource.h>
#endif
struct lrtb {
float l;
float r;
float t;
float b;
};
static SDL_GPUDevice *global_gpu;
static SDL_Window *global_window;
SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v)
{
SDL_GPUGraphicsPipelineTargetInfo info = {0};
return info;
}
SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v)
{
int n = js2number(js,v);
switch(n) {
case 1: return SDL_GPU_SAMPLECOUNT_1;
case 2: return SDL_GPU_SAMPLECOUNT_2;
case 4: return SDL_GPU_SAMPLECOUNT_4;
case 8: return SDL_GPU_SAMPLECOUNT_8;
}
return -1;
}
#define JS2ENUM(NAME, RETS, VALS) \
int js2##NAME(JSContext *js, JSValue v) { \
if (JS_IsUndefined(v)) return 0; \
const char *str = JS_ToCString(js, v); \
int *rets = (RETS); \
const char **vals = (VALS); \
/* Compute how many entries are in the arrays */ \
int n = (int)(sizeof((RETS)) / sizeof((RETS)[0])); \
for(int i = 0; i < n; i++) \
if(!strcmp(vals[i], str)) { \
JS_FreeCString(js, str); \
return rets[i]; \
} \
JS_FreeCString(js, str); \
return 0; \
}
static int rets_SDL_GPUSwapchainComposition[] = {
SDL_GPU_SWAPCHAINCOMPOSITION_SDR,
SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR,
SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR,
// SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084
};
static const char *vals_SDL_GPUSwapchainComposition[] = {
"sdr",
"linear",
"hdr",
// "hdr10"
};
JS2ENUM(SDL_GPUSwapchainComposition, rets_SDL_GPUSwapchainComposition, vals_SDL_GPUSwapchainComposition)
static int rets_SDL_FlipMode[] = {
SDL_FLIP_NONE,
SDL_FLIP_HORIZONTAL,
SDL_FLIP_VERTICAL
};
static const char *vals_SDL_FlipMode[] = {
"none",
"horizontal",
"vertical"
};
JS2ENUM(SDL_FlipMode, rets_SDL_FlipMode, vals_SDL_FlipMode)
/* -------------------------------------------------------
1) SDL_GPUBlendFactor
------------------------------------------------------- */
static int rets_SDL_GPUBlendFactor[] = {
SDL_GPU_BLENDFACTOR_INVALID,
SDL_GPU_BLENDFACTOR_ZERO,
SDL_GPU_BLENDFACTOR_ONE,
SDL_GPU_BLENDFACTOR_SRC_COLOR,
SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
SDL_GPU_BLENDFACTOR_DST_COLOR,
SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR,
SDL_GPU_BLENDFACTOR_SRC_ALPHA,
SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
SDL_GPU_BLENDFACTOR_DST_ALPHA,
SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA,
SDL_GPU_BLENDFACTOR_CONSTANT_COLOR,
SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR,
SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE
};
static const char *vals_SDL_GPUBlendFactor[] = {
"invalid",
"zero",
"one",
"src_color",
"one_minus_src_color",
"dst_color",
"one_minus_dst_color",
"src_alpha",
"one_minus_src_alpha",
"dst_alpha",
"one_minus_dst_alpha",
"constant_color",
"one_minus_constant_color",
"src_alpha_saturate"
};
JS2ENUM(SDL_GPUBlendFactor, rets_SDL_GPUBlendFactor, vals_SDL_GPUBlendFactor)
/* -------------------------------------------------------
2) SDL_GPUBlendOp
------------------------------------------------------- */
static int rets_SDL_GPUBlendOp[] = {
SDL_GPU_BLENDOP_INVALID,
SDL_GPU_BLENDOP_ADD,
SDL_GPU_BLENDOP_SUBTRACT,
SDL_GPU_BLENDOP_REVERSE_SUBTRACT,
SDL_GPU_BLENDOP_MIN,
SDL_GPU_BLENDOP_MAX
};
static const char *vals_SDL_GPUBlendOp[] = {
"invalid",
"add",
"subtract",
"reverse_subtract",
"min",
"max"
};
JS2ENUM(SDL_GPUBlendOp, rets_SDL_GPUBlendOp, vals_SDL_GPUBlendOp)
/* -------------------------------------------------------
3) SDL_GPUCompareOp
------------------------------------------------------- */
static int rets_SDL_GPUCompareOp[] = {
SDL_GPU_COMPAREOP_INVALID,
SDL_GPU_COMPAREOP_NEVER,
SDL_GPU_COMPAREOP_LESS,
SDL_GPU_COMPAREOP_EQUAL,
SDL_GPU_COMPAREOP_LESS_OR_EQUAL,
SDL_GPU_COMPAREOP_GREATER,
SDL_GPU_COMPAREOP_NOT_EQUAL,
SDL_GPU_COMPAREOP_GREATER_OR_EQUAL,
SDL_GPU_COMPAREOP_ALWAYS
};
static const char *vals_SDL_GPUCompareOp[] = {
"invalid",
"never",
"less",
"equal",
"less_or_equal",
"greater",
"not_equal",
"greater_or_equal",
"always"
};
JS2ENUM(SDL_GPUCompareOp, rets_SDL_GPUCompareOp, vals_SDL_GPUCompareOp)
/* -------------------------------------------------------
4) SDL_GPUCullMode
------------------------------------------------------- */
static int rets_SDL_GPUCullMode[] = {
SDL_GPU_CULLMODE_NONE,
SDL_GPU_CULLMODE_FRONT,
SDL_GPU_CULLMODE_BACK
};
static const char *vals_SDL_GPUCullMode[] = {
"none",
"front",
"back"
};
JS2ENUM(SDL_GPUCullMode, rets_SDL_GPUCullMode, vals_SDL_GPUCullMode)
/* -------------------------------------------------------
5) SDL_GPUFillMode
------------------------------------------------------- */
static int rets_SDL_GPUFillMode[] = {
SDL_GPU_FILLMODE_FILL,
SDL_GPU_FILLMODE_LINE
};
static const char *vals_SDL_GPUFillMode[] = {
"fill",
"line"
};
JS2ENUM(SDL_GPUFillMode, rets_SDL_GPUFillMode, vals_SDL_GPUFillMode)
/* -------------------------------------------------------
6) SDL_GPUFilter
------------------------------------------------------- */
static int rets_SDL_GPUFilter[] = {
SDL_GPU_FILTER_NEAREST,
SDL_GPU_FILTER_LINEAR
};
static const char *vals_SDL_GPUFilter[] = {
"nearest",
"linear"
};
JS2ENUM(SDL_GPUFilter, rets_SDL_GPUFilter, vals_SDL_GPUFilter)
/* -------------------------------------------------------
7) SDL_GPUFrontFace
------------------------------------------------------- */
static int rets_SDL_GPUFrontFace[] = {
SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE,
SDL_GPU_FRONTFACE_CLOCKWISE
};
static const char *vals_SDL_GPUFrontFace[] = {
"counter_clockwise",
"clockwise"
};
JS2ENUM(SDL_GPUFrontFace, rets_SDL_GPUFrontFace, vals_SDL_GPUFrontFace)
/* -------------------------------------------------------
8) SDL_GPULoadOp
------------------------------------------------------- */
static int rets_SDL_GPULoadOp[] = {
SDL_GPU_LOADOP_LOAD,
SDL_GPU_LOADOP_CLEAR,
SDL_GPU_LOADOP_DONT_CARE
};
static const char *vals_SDL_GPULoadOp[] = {
"load",
"clear",
"dont_care"
};
JS2ENUM(SDL_GPULoadOp, rets_SDL_GPULoadOp, vals_SDL_GPULoadOp)
/* -------------------------------------------------------
9) SDL_GPUPresentMode
------------------------------------------------------- */
static int rets_SDL_GPUPresentMode[] = {
SDL_GPU_PRESENTMODE_VSYNC,
SDL_GPU_PRESENTMODE_IMMEDIATE,
SDL_GPU_PRESENTMODE_MAILBOX
};
static const char *vals_SDL_GPUPresentMode[] = {
"vsync",
"immediate",
"mailbox"
};
JS2ENUM(SDL_GPUPresentMode, rets_SDL_GPUPresentMode, vals_SDL_GPUPresentMode)
/* -------------------------------------------------------
10) SDL_GPUPrimitiveType
------------------------------------------------------- */
static int rets_SDL_GPUPrimitiveType[] = {
SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP,
SDL_GPU_PRIMITIVETYPE_LINELIST,
SDL_GPU_PRIMITIVETYPE_LINESTRIP,
SDL_GPU_PRIMITIVETYPE_POINTLIST
};
static const char *vals_SDL_GPUPrimitiveType[] = {
"triangle",
"trianglestrip",
"line",
"linestrip",
"point"
};
JS2ENUM(SDL_GPUPrimitiveType, rets_SDL_GPUPrimitiveType, vals_SDL_GPUPrimitiveType)
/* -------------------------------------------------------
11) SDL_GPUSamplerAddressMode
------------------------------------------------------- */
static int rets_SDL_GPUSamplerAddressMode[] = {
SDL_GPU_SAMPLERADDRESSMODE_REPEAT,
SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT,
SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE
};
static const char *vals_SDL_GPUSamplerAddressMode[] = {
"repeat",
"mirrored_repeat",
"clamp"
};
JS2ENUM(SDL_GPUSamplerAddressMode, rets_SDL_GPUSamplerAddressMode, vals_SDL_GPUSamplerAddressMode)
/* -------------------------------------------------------
12) SDL_GPUSamplerMipmapMode
------------------------------------------------------- */
static int rets_SDL_GPUSamplerMipmapMode[] = {
SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,
SDL_GPU_SAMPLERMIPMAPMODE_LINEAR
};
static const char *vals_SDL_GPUSamplerMipmapMode[] = {
"nearest",
"linear"
};
JS2ENUM(SDL_GPUSamplerMipmapMode, rets_SDL_GPUSamplerMipmapMode, vals_SDL_GPUSamplerMipmapMode)
/* -------------------------------------------------------
13) SDL_GPUStencilOp
------------------------------------------------------- */
static int rets_SDL_GPUStencilOp[] = {
SDL_GPU_STENCILOP_INVALID,
SDL_GPU_STENCILOP_KEEP,
SDL_GPU_STENCILOP_ZERO,
SDL_GPU_STENCILOP_REPLACE,
SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP,
SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP,
SDL_GPU_STENCILOP_INVERT,
SDL_GPU_STENCILOP_INCREMENT_AND_WRAP,
SDL_GPU_STENCILOP_DECREMENT_AND_WRAP
};
static const char *vals_SDL_GPUStencilOp[] = {
"invalid",
"keep",
"zero",
"replace",
"increment_and_clamp",
"decrement_and_clamp",
"invert",
"increment_and_wrap",
"decrement_and_wrap"
};
JS2ENUM(SDL_GPUStencilOp, rets_SDL_GPUStencilOp, vals_SDL_GPUStencilOp)
static int rets_SDL_GPUTextureType[] = {
SDL_GPU_TEXTURETYPE_2D, /**< The texture is a 2-dimensional image. */
SDL_GPU_TEXTURETYPE_2D_ARRAY, /**< The texture is a 2-dimensional array image. */
SDL_GPU_TEXTURETYPE_3D, /**< The texture is a 3-dimensional image. */
SDL_GPU_TEXTURETYPE_CUBE, /**< The texture is a cube image. */
SDL_GPU_TEXTURETYPE_CUBE_ARRAY /**< The texture is a cube array image. */
};
static const char *vals_SDL_GPUTextureType[] = {
"2d",
"2d array",
"3d",
"cube",
"cube array"
};
JS2ENUM(SDL_GPUTextureType, rets_SDL_GPUTextureType, vals_SDL_GPUTextureType)
/* -------------------------------------------------------
14) SDL_GPUStoreOp
------------------------------------------------------- */
static int rets_SDL_GPUStoreOp[] = {
SDL_GPU_STOREOP_STORE,
SDL_GPU_STOREOP_DONT_CARE,
SDL_GPU_STOREOP_RESOLVE,
SDL_GPU_STOREOP_RESOLVE_AND_STORE
};
static const char *vals_SDL_GPUStoreOp[] = {
"store",
"dont_care",
"resolve",
"resolve_and_store"
};
JS2ENUM(SDL_GPUStoreOp, rets_SDL_GPUStoreOp, vals_SDL_GPUStoreOp)
/* -------------------------------------------------------
X) SDL_GPUTextureType
------------------------------------------------------- */
static int rets_SDL_GPUTextureFormat[] = {
SDL_GPU_TEXTUREFORMAT_INVALID,
/* Unsigned Normalized Float Color Formats */
SDL_GPU_TEXTUREFORMAT_A8_UNORM,
SDL_GPU_TEXTUREFORMAT_R8_UNORM,
SDL_GPU_TEXTUREFORMAT_R8G8_UNORM,
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
SDL_GPU_TEXTUREFORMAT_R16_UNORM,
SDL_GPU_TEXTUREFORMAT_R16G16_UNORM,
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM,
SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM,
SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM,
SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM,
SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM,
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM,
/* Compressed Unsigned Normalized Float Color Formats */
SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM,
SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM,
SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM,
SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM,
SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM,
SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM,
/* Compressed Signed Float Color Formats */
SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT,
/* Compressed Unsigned Float Color Formats */
SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT,
/* Signed Normalized Float Color Formats */
SDL_GPU_TEXTUREFORMAT_R8_SNORM,
SDL_GPU_TEXTUREFORMAT_R8G8_SNORM,
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM,
SDL_GPU_TEXTUREFORMAT_R16_SNORM,
SDL_GPU_TEXTUREFORMAT_R16G16_SNORM,
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM,
/* Signed Float Color Formats */
SDL_GPU_TEXTUREFORMAT_R16_FLOAT,
SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT,
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT,
SDL_GPU_TEXTUREFORMAT_R32_FLOAT,
SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT,
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT,
/* Unsigned Float Color Formats */
SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT,
/* Unsigned Integer Color Formats */
SDL_GPU_TEXTUREFORMAT_R8_UINT,
SDL_GPU_TEXTUREFORMAT_R8G8_UINT,
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT,
SDL_GPU_TEXTUREFORMAT_R16_UINT,
SDL_GPU_TEXTUREFORMAT_R16G16_UINT,
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT,
SDL_GPU_TEXTUREFORMAT_R32_UINT,
SDL_GPU_TEXTUREFORMAT_R32G32_UINT,
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT,
/* Signed Integer Color Formats */
SDL_GPU_TEXTUREFORMAT_R8_INT,
SDL_GPU_TEXTUREFORMAT_R8G8_INT,
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT,
SDL_GPU_TEXTUREFORMAT_R16_INT,
SDL_GPU_TEXTUREFORMAT_R16G16_INT,
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT,
SDL_GPU_TEXTUREFORMAT_R32_INT,
SDL_GPU_TEXTUREFORMAT_R32G32_INT,
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT,
/* SRGB Unsigned Normalized Color Formats */
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB,
/* Compressed SRGB Unsigned Normalized Color Formats */
SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB,
/* Depth Formats */
SDL_GPU_TEXTUREFORMAT_D16_UNORM,
SDL_GPU_TEXTUREFORMAT_D24_UNORM,
SDL_GPU_TEXTUREFORMAT_D32_FLOAT,
SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT,
SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT,
/* Compressed ASTC Normalized Float Color Formats*/
SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM,
SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM,
/* Compressed SRGB ASTC Normalized Float Color Formats*/
SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB,
SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB,
/* Compressed ASTC Signed Float Color Formats*/
SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT,
SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT
};
static const char *vals_SDL_GPUTextureFormat[] = {
"invalid",
"a8",
"r8",
"rg8",
"rgba8",
"r16",
"rg16",
"rgba16",
"r10g10b10a2",
"b5g6r5",
"b5g5r5a1",
"b4g4r4a4",
"b8g8r8a8",
"bc1",
"bc2",
"bc3",
"bc4",
"bc5",
"bc7",
"bc6h float",
"bc6h ufloat",
"r8 snorm",
"rg8 snorm",
"rgba8 snorm",
"r16 snorm",
"rg16 snorm",
"rgba16 snorm",
"r16 float",
"rg16 float",
"rgba16 float",
"r32 float",
"rg32 float",
"rgba32 float",
"r11g11b10",
"r8 uint",
"rg8 uint",
"rgba8 uint",
"r16 uint",
"rg16 uint",
"rgba16 uint",
"r32 uint",
"rg32 uint",
"rgba32 uint",
"r8 int",
"rg8 int",
"rgba8 int",
"r16 int",
"rg16 int",
"rgba16 int",
"r32 int",
"rg32 int",
"rgba32 int",
"rgba8 srgb",
"b8g8r8a8 srgb",
"bc1 srgb",
"bc2 srgb",
"bc3 srgb",
"bc7 srgb",
"d16",
"d24",
"d32 float",
"d24 s8",
"d32 float s8",
"astc 4x4",
"astc 5x4",
"astc 5x5",
"astc 6x5",
"astc 6x6",
"astc 8x5",
"astc 8x6",
"astc 8x8",
"astc 10x5",
"astc 10x6",
"astc 10x8",
"astc 10x10",
"astc 12x10",
"astc 12x12",
"astc 4x4 srgb",
"astc 5x4 srgb",
"astc 5x5 srgb",
"astc 6x5 srgb",
"astc 6x6 srgb",
"astc 8x5 srgb",
"astc 8x6 srgb",
"astc 8x8 srgb",
"astc 10x5 srgb",
"astc 10x6 srgb",
"astc 10x8 srgb",
"astc 10x10 srgb",
"astc 12x10 srgb",
"astc 12x12 srgb",
"astc 4x4 float",
"astc 5x4 float",
"astc 5x5 float",
"astc 6x5 float",
"astc 6x6 float",
"astc 8x5 float",
"astc 8x6 float",
"astc 8x8 float",
"astc 10x5 float",
"astc 10x6 float",
"astc 10x8 float",
"astc 10x10 float",
"astc 12x10 float",
"astc 12x12 float"
};
JS2ENUM(SDL_GPUTextureFormat, rets_SDL_GPUTextureFormat, vals_SDL_GPUTextureFormat)
SDL_GPUColorTargetBlendState js2SDL_GPUColorTargetBlendState(JSContext *js, JSValue v)
{
SDL_GPUColorTargetBlendState state = {0};
JS_GETPROP(js,state.src_color_blendfactor,v,src_rgb,SDL_GPUBlendFactor);
JS_GETPROP(js,state.dst_color_blendfactor,v,dst_rgb,SDL_GPUBlendFactor);
JS_GETPROP(js,state.src_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor);
JS_GETPROP(js,state.dst_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor);
JS_GETPROP(js,state.color_blend_op,v,op_rgb,SDL_GPUBlendOp)
JS_GETPROP(js,state.alpha_blend_op,v,op_alpha,SDL_GPUBlendOp)
JS_GETPROP(js,state.enable_blend,v,enabled,bool)
return state;
}
SDL_GPUColorTargetDescription js2SDL_GPUColorTargetDescription(JSContext *js, JSValue v)
{
SDL_GPUColorTargetDescription dsc = {0};
JS_GETPROP(js,dsc.format,v,format,SDL_GPUTextureFormat)
JS_GETPROP(js,dsc.blend_state,v,blend,SDL_GPUColorTargetBlendState)
return dsc;
}
SDL_GPUStencilOpState js2SDL_GPUStencilOpState(JSContext *js, JSValue v)
{
SDL_GPUStencilOpState state;
memset(&state, 0, sizeof(state));
JSValue compare_val = JS_GetPropertyStr(js, v, "compare");
if(!JS_IsUndefined(compare_val)) state.compare_op = js2SDL_GPUCompareOp(js, compare_val);
JS_FreeValue(js, compare_val);
JSValue fail_val = JS_GetPropertyStr(js, v, "fail");
if(!JS_IsUndefined(fail_val)) state.fail_op = js2SDL_GPUStencilOp(js, fail_val);
JS_FreeValue(js, fail_val);
JSValue depth_fail_val = JS_GetPropertyStr(js, v, "depth_fail");
if(!JS_IsUndefined(depth_fail_val)) state.depth_fail_op = js2SDL_GPUStencilOp(js, depth_fail_val);
JS_FreeValue(js, depth_fail_val);
JSValue pass_val = JS_GetPropertyStr(js, v, "pass");
if(!JS_IsUndefined(pass_val)) state.pass_op = js2SDL_GPUStencilOp(js, pass_val);
JS_FreeValue(js, pass_val);
return state;
}
typedef struct lrtb lrtb;
lrtb js2lrtb(JSContext *js, JSValue v)
{
lrtb ret = {0};
JS_GETATOM(js,ret.l,v,l,number)
JS_GETATOM(js,ret.r,v,r,number)
JS_GETATOM(js,ret.b,v,b,number)
JS_GETATOM(js,ret.t,v,t,number)
return ret;
}
JSValue vec22js(JSContext *js,HMM_Vec2 v)
{
JSValue array = JS_NewArray(js);
JS_SetPropertyUint32(js, array,0,number2js(js,v.x));
JS_SetPropertyUint32(js, array,1,number2js(js,v.y));
return array;
}
char *js2strdup(JSContext *js, JSValue v) {
const char *str = JS_ToCString(js, v);
char *ret = strdup(str);
JS_FreeCString(js, str);
return ret;
}
#include "qjs_macros.h"
void SDL_Window_free(JSRuntime *rt, SDL_Window *w)
{
SDL_DestroyWindow(w);
}
typedef struct renderer_ctx {
SDL_Renderer *sdl;
shader_globals cam;
} renderer_ctx;
void renderer_ctx_free(JSRuntime *rt, renderer_ctx *ctx)
{
SDL_DestroyRenderer(ctx->sdl);
free(ctx);
}
void SDL_Surface_free(JSRuntime *rt, SDL_Surface *s) {
if (s->flags & SDL_SURFACE_PREALLOCATED)
free(s->pixels);
SDL_DestroySurface(s);
}
void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam)
{
SDL_CloseCamera(cam);
}
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d)
{
SDL_DestroyGPUDevice(d);
}
void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c)
{
}
QJSCLASS(renderer_ctx,)
void SDL_GPUComputePass_free(JSRuntime *rt, SDL_GPUComputePass *c) { }
void SDL_GPUCopyPass_free(JSRuntime *rt, SDL_GPUCopyPass *c) { }
void SDL_GPURenderPass_free(JSRuntime *rt, SDL_GPURenderPass *c) { }
#define GPURELEASECLASS(NAME) \
void SDL_GPU##NAME##_free(JSRuntime *rt, SDL_GPU##NAME *c) { \
SDL_ReleaseGPU##NAME(global_gpu, c); } \
QJSCLASS(SDL_GPU##NAME,) \
static JSClassID js_sprite_id;
static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) {
sprite *sp = JS_GetOpaque(val, js_sprite_id);
if (!sp) return;
JS_MarkValue(rt, sp->image, mark_func);
}
QJSCLASSMARK(sprite,)
static JSClassID js_transform_id;
static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) {
transform *t = JS_GetOpaque(val, js_transform_id);
if (!t) return;
// Mark the JSValue references stored in your struct
// JS_MarkValue(rt, t->self, mark_func);
JS_MarkValue(rt, t->change_hook, mark_func);
JS_MarkValue(rt, t->jsparent, mark_func);
// Mark the array elements
for (int i = 0; i < arrlen(t->jschildren); i++)
JS_MarkValue(rt, t->jschildren[i], mark_func);
}
QJSCLASSMARK(transform,)
QJSCLASS(font,)
QJSCLASS(datastream,)
QJSCLASS(SDL_Window,)
QJSCLASS(SDL_Camera,)
void SDL_Texture_free(JSRuntime *rt, SDL_Texture *t){
SDL_DestroyTexture(t);
}
QJSCLASS(SDL_Texture,
JS_SetPropertyStr(js, j, "width", number2js(js,n->w));
JS_SetPropertyStr(js,j,"height",number2js(js,n->h));
)
QJSCLASS(SDL_Surface,
JS_SetProperty(js, j, width_atom, number2js(js,n->w));
JS_SetProperty(js,j,height_atom,number2js(js,n->h));
)
QJSCLASS(SDL_GPUDevice,)
GPURELEASECLASS(Buffer)
GPURELEASECLASS(ComputePipeline)
GPURELEASECLASS(GraphicsPipeline)
GPURELEASECLASS(Sampler)
GPURELEASECLASS(Shader)
GPURELEASECLASS(Texture)
GPURELEASECLASS(TransferBuffer)
GPURELEASECLASS(Fence)
QJSCLASS(SDL_GPUCommandBuffer,)
QJSCLASS(SDL_GPUComputePass,)
QJSCLASS(SDL_GPUCopyPass,)
QJSCLASS(SDL_GPURenderPass,)
QJSCLASS(SDL_Cursor,)
static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f)
{
PHYSFS_close(f);
}
QJSCLASS(PHYSFS_File,)
void rtree_free(JSRuntime *rt, rtree *tree)
{
rtree_destroy(tree);
}
QJSCLASS(rtree,)
int js_arrlen(JSContext *js,JSValue v) {
if (JS_IsUndefined(v)) return 0;
int len;
len = js_getnum_str(js,v,"length");
return len;
}
// Unpacks a typed array javascript object. If it has a gpu property, returns it, too. Otherwise, if requested, makes one.
void *gpu_buffer_unpack(JSContext *js, SDL_GPUDevice *device, JSValue buffer, size_t *size, void **send_data, SDL_GPUBuffer **send_gpu)
{
size_t o, len, bytes, msize;
JSValue buf = JS_GetTypedArrayBuffer(js, buffer, &o, &len, &bytes);
void *data = JS_GetArrayBuffer(js, &msize, buf);
JS_FreeValue(js,buf);
if (size) *size = msize;
if (send_gpu) {
JSValue gpu = JS_GetPropertyStr(js,buffer,"gpu");
*send_gpu = js2SDL_GPUBuffer(js,gpu);
if (!*send_gpu) {
JSValue idx = JS_GetPropertyStr(js,buffer, "index");
Uint32 usage = JS_ToBool(js,idx) ? SDL_GPU_BUFFERUSAGE_INDEX : SDL_GPU_BUFFERUSAGE_VERTEX;
JS_FreeValue(js,idx);
*send_gpu = SDL_CreateGPUBuffer(device, &(SDL_GPUBufferCreateInfo) { .usage=usage,.size=msize});
if (!*send_gpu) printf("COULDN'T MAKE GPU BUFFER: %s\n", SDL_GetError());
JS_SetPropertyStr(js, buffer, "gpu", SDL_GPUBuffer2js(js,*send_gpu));
}
JS_FreeValue(js,gpu);
}
if (send_data)
*send_data = data;
return data;
}
void *get_typed_buffer(JSContext *js, JSValue argv, size_t *len)
{
size_t o,bytes,size;
JSValue buf = JS_GetTypedArrayBuffer(js,argv,&o,len,&bytes);
void *data = JS_GetArrayBuffer(js,&size,buf);
JS_FreeValue(js,buf);
return data;
}
float *js2floatarray(JSContext *js, JSValue v) {
JSClassID class = JS_GetClassID(v);
if (class == JS_CLASS_FLOAT32_ARRAY) {
size_t len;
return get_typed_buffer(js,v, &len);
}
// switch(p->class_id){}
int len = js_arrlen(js,v);
float *array = malloc(sizeof(float)*len);
for (int i = 0; i < len; i++)
array[i] = js_getnum_uint32(js,v,i);
return array;
}
JSValue angle2js(JSContext *js,double g) {
return number2js(js,g*HMM_RadToTurn);
}
double js2angle(JSContext *js,JSValue v) {
double n = js2number(js,v);
return n * HMM_TurnToRad;
}
typedef HMM_Vec4 colorf;
colorf js2color(JSContext *js,JSValue v) {
if (JS_IsUndefined(v)) return (colorf){1,1,1,1};
JSValue c[4];
for (int i = 0; i < 4; i++) c[i] = JS_GetPropertyUint32(js,v,i);
float a = JS_IsUndefined(c[3]) ? 1.0 : js2number(js,c[3]);
colorf color = {
.r = js2number(js,c[0]),
.g = js2number(js,c[1]),
.b = js2number(js,c[2]),
.a = a,
};
for (int i = 0; i < 4; i++) JS_FreeValue(js,c[i]);
return color;
}
SDL_FColor js2SDL_FColor(JSContext *js, JSValue v)
{
colorf color = js2color(js,v);
return (SDL_FColor){color.r,color.g,color.b,color.a};
}
SDL_GPUTextureSamplerBinding js2SDL_GPUTextureSamplerBinding(JSContext *js, JSValue v)
{
SDL_GPUTextureSamplerBinding b;
JS_GETPROP(js, b.texture, v, texture, SDL_GPUTexture)
JS_GETPROP(js, b.sampler, v, sampler, SDL_GPUSampler)
return b;
}
JSValue color2js(JSContext *js, colorf color)
{
JSValue arr = JS_NewArray(js);
JS_SetPropertyUint32(js, arr,0,number2js(js,(double)color.r));
JS_SetPropertyUint32(js, arr,1,number2js(js,(double)color.g));
JS_SetPropertyUint32(js, arr,2,number2js(js,(double)color.b));
JS_SetPropertyUint32(js, arr,3,number2js(js,(double)color.a));
return arr;
}
HMM_Vec2 js2vec2(JSContext *js,JSValue v)
{
HMM_Vec2 v2;
v2.X = js_getnum_uint32(js,v,0);
v2.Y = js_getnum_uint32(js,v,1);
return v2;
}
HMM_Vec3 js2vec3(JSContext *js,JSValue v)
{
HMM_Vec3 v3;
v3.x = js_getnum_uint32(js, v,0);
v3.y = js_getnum_uint32(js, v,1);
v3.z = js_getnum_uint32(js, v,2);
return v3;
}
float *js2floats(JSContext *js, JSValue v, size_t *len)
{
*len = js_arrlen(js,v);
float *arr = malloc(sizeof(float)* *len);
for (int i = 0; i < *len; i++)
arr[i] = js_getnum_uint32(js,v,i);
return arr;
}
double *js2doubles(JSContext *js, JSValue v, size_t *len)
{
*len = js_arrlen(js,v);
double *arr = malloc(sizeof(double)* *len);
for (int i = 0; i < *len; i++)
arr[i] = js_getnum_uint32(js,v,i);
return arr;
}
HMM_Vec3 js2vec3f(JSContext *js, JSValue v)
{
HMM_Vec3 vec;
if (JS_IsArray(js, v))
return js2vec3(js,v);
else
vec.x = vec.y = vec.z = js2number(js,v);
return vec;
}
JSValue vec32js(JSContext *js, HMM_Vec3 v)
{
JSValue array = JS_NewArray(js);
JS_SetPropertyUint32(js, array,0,number2js(js,v.x));
JS_SetPropertyUint32(js, array,1,number2js(js,v.y));
JS_SetPropertyUint32(js, array,2,number2js(js,v.z));
return array;
}
JSValue vec3f2js(JSContext *js, HMM_Vec3 v)
{
return vec32js(js,v);
}
JSValue quat2js(JSContext *js, HMM_Quat q)
{
JSValue arr = JS_NewArray(js);
JS_SetPropertyUint32(js, arr, 0, number2js(js,q.x));
JS_SetPropertyUint32(js, arr,1,number2js(js,q.y));
JS_SetPropertyUint32(js, arr,2,number2js(js,q.z));
JS_SetPropertyUint32(js, arr,3,number2js(js,q.w));
return arr;
}
HMM_Vec4 js2vec4(JSContext *js, JSValue v)
{
HMM_Vec4 v4;
for (int i = 0; i < 4; i++)
v4.e[i] = js_getnum_uint32(js, v,i);
return v4;
}
double arr_vec_length(JSContext *js,JSValue v)
{
int len = js_arrlen(js,v);
switch(len) {
case 2: return HMM_LenV2(js2vec2(js,v));
case 3: return HMM_LenV3(js2vec3(js,v));
case 4: return HMM_LenV4(js2vec4(js,v));
}
double sum = 0;
for (int i = 0; i < len; i++)
sum += pow(js_getnum_uint32(js, v, i), 2);
return sqrt(sum);
}
HMM_Quat js2quat(JSContext *js,JSValue v)
{
return js2vec4(js,v).quat;
}
JSValue vec42js(JSContext *js, HMM_Vec4 v)
{
JSValue array = JS_NewArray(js);
for (int i = 0; i < 4; i++)
JS_SetPropertyUint32(js, array,i,number2js(js,v.e[i]));
return array;
}
HMM_Vec2 *js2cpvec2arr(JSContext *js,JSValue v) {
HMM_Vec2 *arr = NULL;
int n = js_arrlen(js,v);
arrsetlen(arr,n);
for (int i = 0; i < n; i++)
arr[i] = js2vec2(js,js_getpropertyuint32(js, v, i));
return arr;
}
JSValue vecarr2js(JSContext *js,HMM_Vec2 *points, int n) {
JSValue array = JS_NewArray(js);
for (int i = 0; i < n; i++)
JS_SetPropertyUint32(js, array,i,vec22js(js,points[i]));
return array;
}
rect js2rect(JSContext *js,JSValue v) {
if (JS_IsUndefined(v)) return (rect){0,0,1,1};
rect rect;
JS_GETATOM(js,rect.x,v,x,number)
JS_GETATOM(js,rect.y,v,y,number)
JS_GETATOM(js,rect.w,v,width,number)
JS_GETATOM(js,rect.h,v,height,number)
float anchor_x, anchor_y;
JS_GETATOM(js, anchor_x, v, anchor_x, number)
JS_GETATOM(js, anchor_y, v, anchor_y, number)
rect.y -= anchor_y*rect.h;
rect.x -= anchor_x*rect.w;
return rect;
}
rect transform_rect(SDL_Renderer *ren, rect in, HMM_Mat3 *t)
{
HMM_Vec3 bottom_left = (HMM_Vec3){in.x,in.y,1.0};
HMM_Vec3 transformed_bl = HMM_MulM3V3(*t, bottom_left);
in.x = transformed_bl.x;
in.y = transformed_bl.y;
in.y = in.y - in.h; // should be done for any platform that draws rectangles from top left
return in;
}
HMM_Vec2 transform_point(SDL_Renderer *ren, HMM_Vec2 in, HMM_Mat3 *t)
{
rect logical;
SDL_GetRenderLogicalPresentationRect(ren, &logical);
in.y *= -1;
in.y += logical.h;
in.x -= t->Columns[2].x;
in.y -= t->Columns[2].y;
return in;
}
JSValue rect2js(JSContext *js,rect rect) {
JSValue obj = JS_NewObject(js);
JS_SETATOM(js, obj, x, rect.x, number);
JS_SETATOM(js, obj, y, rect.y, number);
JS_SETATOM(js, obj, width, rect.w, number);
JS_SETATOM(js, obj, height, rect.h, number);
return obj;
}
JSValue ints2js(JSContext *js,int *ints) {
JSValue arr = JS_NewArray(js);
for (int i = 0; i < arrlen(ints); i++)
JS_SetPropertyUint32(js, arr,i, number2js(js,ints[i]));
return arr;
}
int vec_between(HMM_Vec2 p, HMM_Vec2 a, HMM_Vec2 b) {
HMM_Vec2 n;
n.x = b.x - a.x;
n.y = b.y - a.y;
n = HMM_NormV2(n);
return HMM_DotV2(n, HMM_SubV2(p,a)) > 0 && HMM_DotV2(HMM_MulV2F(n, -1), HMM_SubV2(p,b)) > 0;
}
/* Determines between which two points in 'segs' point 'p' falls.
0 indicates 'p' comes before the first point.
arrlen(segs) indicates it comes after the last point.
*/
int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) {
float shortest = slop < 0 ? INFINITY : slop;
int best = -1;
for (int i = 0; i < arrlen(segs) - 1; i++) {
float a = (segs[i + 1].y - segs[i].y) / (segs[i + 1].x - segs[i].x);
float c = segs[i].y - (a * segs[i].x);
float b = -1;
float dist = fabsf(a * p.x + b * p.y + c) / sqrt(pow(a, 2) + 1);
if (dist > shortest) continue;
int between = vec_between(p, segs[i], segs[i + 1]);
if (between) {
shortest = dist;
best = i + 1;
} else {
if (i == 0 && HMM_DistV2(p,segs[0]) < slop) {
shortest = dist;
best = i;
} else if (i == arrlen(segs) - 2 && HMM_DistV2(p,arrlast(segs)) < slop) {
shortest = dist;
best = arrlen(segs);
}
}
}
if (best == 1) {
HMM_Vec2 n;
n.x = segs[1].x - segs[0].x;
n.y = segs[1].y - segs[0].y;
n = HMM_NormV2(n);
if (HMM_DotV2(n, HMM_SubV2(p,segs[0])) < 0 ){
if (HMM_DistV2(p, segs[0]) >= slop)
best = -1;
else
best = 0;
}
}
if (best == arrlen(segs) - 1) {
HMM_Vec2 n;
n.x = segs[best - 1].x - segs[best].x;
n.y = segs[best - 1].y - segs[best - 1].y;
n = HMM_NormV2(n);
if (HMM_DotV2(n, HMM_SubV2(p, segs[best])) < 0) {
if (HMM_DistV2(p, segs[best]) >= slop)
best = -1;
else
best = arrlen(segs);
}
}
return best;
}
JSValue make_quad_indices_buffer(JSContext *js, int quads)
{
prosperon_rt *rt = JS_GetContextOpaque(js);
int count = quads*6;
if (!JS_IsUndefined(rt->idx_buffer) && rt->idx_count >= count)
return JS_DupValue(js,rt->idx_buffer);
int verts = quads*4;
uint16_t *indices = malloc(sizeof(*indices)*count);
for (int i = 0, v = 0; v < verts; i +=6, v += 4) {
indices[i] = v;
indices[i+1] = v+2;
indices[i+2] = v+1;
indices[i+3] = v+2;
indices[i+4] = v+3;
indices[i+5] = v+1;
}
if (!JS_IsUndefined(rt->idx_buffer))
JS_FreeValue(js,rt->idx_buffer);
rt->idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1);
rt->idx_count = count;
return JS_DupValue(js,rt->idx_buffer);
}
struct quad_buffers {
HMM_Vec2 *pos;
HMM_Vec2 *uv;
HMM_Vec4 *color;
int verts;
};
struct quad_buffers quad_buffers_new(int verts)
{
struct quad_buffers b;
b.verts = verts;
b.pos = malloc(sizeof(HMM_Vec2)*verts);
b.uv = malloc(sizeof(HMM_Vec2)*verts);
b.color = malloc(sizeof(HMM_Vec4)*verts);
return b;
}
JSValue quadbuffers_to_mesh(JSContext *js, struct quad_buffers buffers)
{
JSValue jspos = make_gpu_buffer(js, buffers.pos, sizeof(HMM_Vec2)*buffers.verts, JS_TYPED_ARRAY_FLOAT32, 2, 0, 0);
JSValue jsuv = make_gpu_buffer(js, buffers.uv, sizeof(HMM_Vec2)*buffers.verts, JS_TYPED_ARRAY_FLOAT32, 2,0,0);
JSValue jscolor = make_gpu_buffer(js, buffers.color, sizeof(HMM_Vec4)*buffers.verts, JS_TYPED_ARRAY_FLOAT32, 4,0,0);
size_t quads = buffers.verts/4;
size_t count = buffers.verts/2*3;
JSValue jsidx = make_quad_indices_buffer(js, quads);
JSValue ret = JS_NewObject(js);
JS_SetProperty(js, ret, pos, jspos);
JS_SetProperty(js, ret, uv, jsuv);
JS_SetProperty(js, ret, color, jscolor);
JS_SetProperty(js, ret, indices, jsidx);
JS_SetProperty(js, ret, vertices, number2js(js, buffers.verts));
JS_SetProperty(js,ret,num_indices, number2js(js,count));
return ret;
}
JSValue quads_to_mesh(JSContext *js, text_vert *buffer)
{
size_t verts = arrlen(buffer);
HMM_Vec2 *pos = malloc(arrlen(buffer)*sizeof(HMM_Vec2));
HMM_Vec2 *uv = malloc(arrlen(buffer)*sizeof(HMM_Vec2));
HMM_Vec4 *color = malloc(arrlen(buffer)*sizeof(HMM_Vec4));
for (int i = 0; i < arrlen(buffer); i++) {
pos[i] = buffer[i].pos;
uv[i] = buffer[i].uv;
color[i] = buffer[i].color;
}
JSValue jspos = make_gpu_buffer(js, pos, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0);
JSValue jsuv = make_gpu_buffer(js, uv, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0);
JSValue jscolor = make_gpu_buffer(js, color, sizeof(HMM_Vec4)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 4,0,0);
size_t quads = verts/4;
size_t count = verts/2*3;
JSValue jsidx = make_quad_indices_buffer(js, quads);
JSValue ret = JS_NewObject(js);
JS_SetProperty(js, ret, pos, jspos);
JS_SetProperty(js, ret, uv, jsuv);
JS_SetProperty(js, ret, color, jscolor);
JS_SetProperty(js, ret, indices, jsidx);
JS_SetProperty(js, ret, vertices, number2js(js, verts));
JS_SetProperty(js,ret,num_indices, 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,
HMM_Vec2 *points = js2cpvec2arr(js,argv[0]);
float param = js2number(js,argv[1]);
HMM_Vec2 *samples = catmull_rom_ma_v2(points,param);
if (!samples)
ret = JS_UNDEFINED;
else
ret = vecarr2js(js,samples, arrlen(samples));
arrfree(points);
arrfree(samples);
)
JSC_CCALL(spline_bezier,
HMM_Vec2 *points = js2cpvec2arr(js,argv[0]);
float param = js2number(js,argv[1]);
HMM_Vec2 *samples = catmull_rom_ma_v2(points,param);
if (!samples)
ret = JS_UNDEFINED;
else
ret = vecarr2js(js,samples, arrlen(samples));
arrfree(samples);
arrfree(points);
)
static const JSCFunctionListEntry js_spline_funcs[] = {
MIST_FUNC_DEF(spline, catmull, 2),
MIST_FUNC_DEF(spline, bezier, 2)
};
shader_globals camera_globals(JSContext *js, JSValue camera)
{
shader_globals data = {0};
if (JS_IsUndefined(camera))
return data;
HMM_Vec2 size;
transform *transform;
double fov = 0;
int ortho;
double near_z = 0;
double far_z = 0;
HMM_Vec2 anchor;
JS_GETPROP(js, size, camera, size, vec2)
JS_GETPROP(js, transform, camera, transform, transform)
JS_GETPROP(js, fov, camera, fov, number)
JS_GETPROP(js, ortho, camera,ortho,bool)
JS_GETPROP(js,near_z,camera,near_z,number)
JS_GETPROP(js,far_z,camera,far_z,number)
JS_GETPROP(js, anchor, camera, anchor, vec2)
HMM_Mat4 proj;
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(
left, right,
bottom, top,
-1.0f, 1.0f
);
proj.Columns[3].x += size.x * anchor.x;
proj.Columns[3].y += size.y * anchor.y;
}
else {
proj = HMM_Perspective_RH_NO(fov, size.x/size.y,near_z,far_z);
proj.Columns[1] = HMM_MulV4F(proj.Columns[1], -1.0f);
}
view = HMM_MulM4(
HMM_InvTranslate(HMM_Translate(transform->pos)),
HMM_InvRotate(HMM_QToM4(transform->rotation))
);
// Update your shader globals
data.world_to_projection = HMM_MulM4(proj, view);
data.projection_to_world = HMM_InvGeneralM4(data.world_to_projection);
data.camera_pos_world = transform->pos;
data.viewport_min_z = near_z;
data.viewport_max_z = far_z;
data.render_size = size;
data.world_to_view = view;
data.view_to_projection = proj;
data.camera_dir_world = HMM_NormV3(HMM_QVRot((HMM_Vec3){0,0,-1},transform->rotation));
data.viewport_size = (HMM_Vec2){0.5,0.5};
data.viewport_offset = (HMM_Vec2){0,0};
data.time = SDL_GetTicksNS() / 1000000000.0f;
return data;
}
static JSValue floats2array(JSContext *js, float *vals, size_t len) {
JSValue arr = JS_NewArray(js);
for (size_t i = 0; i < len; i++) {
JS_SetPropertyUint32(js, arr, i, number2js(js, vals[i]));
}
return arr;
}
JSValue js_math_dot(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t alen, blen;
float *a = js2floats(js,argv[0], &alen);
float *b = js2floats(js,argv[1], &blen);
float dot = 0;
size_t len = alen < blen? alen : blen;
for (size_t i = 0; i < len; i++)
dot += a[i] * b[i];
free(a);
free(b);
return number2js(js,dot);
}
JSValue js_math_project(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
// We'll work up to the smaller length
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
// Compute dot products: a·b and b·b
float ab = 0, bb = 0;
for (size_t i = 0; i < len; i++) {
ab += a[i] * b[i];
bb += b[i] * b[i];
}
// Build the result array
float *proj = (float*)malloc(sizeof(float) * len);
if (!proj) {
free(a);
free(b);
return JS_EXCEPTION; // or some error
}
float scale = (bb != 0.0f) ? (ab / bb) : 0.0f;
for (size_t i = 0; i < len; i++)
proj[i] = scale * b[i];
JSValue ret = floats2array(js, proj, len);
free(a);
free(b);
free(proj);
return ret;
}
// ---------------------------
// math_midpoint(a, b)
// midpoint = (a + b) / 2
// returns new vector (array)
// dimension = min(alen, blen)
// ---------------------------
JSValue js_math_midpoint(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
float *m = (float*)malloc(sizeof(float) * len);
if (!m) {
free(a);
free(b);
return JS_EXCEPTION;
}
for (size_t i = 0; i < len; i++)
m[i] = (a[i] + b[i]) * 0.5f;
JSValue ret = floats2array(js, m, len);
free(a);
free(b);
free(m);
return ret;
}
// ---------------------------
// math_distance(a, b)
// Euclidean distance = sqrt( Σ (b[i]-a[i])^2 )
// dimension = min(alen, blen)
// returns scalar (number)
// ---------------------------
JSValue js_math_distance(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
float distSq = 0.0f;
for (size_t i = 0; i < len; i++) {
float diff = b[i] - a[i];
distSq += diff * diff;
}
float dist = sqrtf(distSq);
free(a);
free(b);
return number2js(js, dist);
}
// ---------------------------
// math_angle(a)
// In 2D: angle from x-axis via atan2(y, x).
// For higher dimensions, there's no single "angle" in the usual sense.
// We'll only return angle if len >= 2, using first two coords.
// Or strictly require len==2, your call.
// ---------------------------
JSValue js_math_angle(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t alen;
float *a = js2floats(js, argv[0], &alen);
if (!a || alen < 2) {
free(a);
return JS_UNDEFINED;
}
// If you want to be strict, require alen == 2
// if (alen != 2) {
// free(a);
// return JS_UNDEFINED;
// }
float angle = atan2f(a[1], a[0]);
free(a);
return number2js(js, angle);
}
/* Given a series of points p, computes a new series with them expanded on either side by d */
/*
HMM_Vec2 *inflatepoints(HMM_Vec2 *p, float d, int n)
{
if (d == 0) {
HMM_Vec2 *ret = NULL;
arraddn(ret,n);
for (int i = 0; i < n; i++)
ret[i] = p[i];
return ret;
}
parsl_position par_v[n];
uint16_t spine_lens[] = {n};
for (int i = 0; i < n; i++) {
par_v[i].x = p[i].x;
par_v[i].y = p[i].y;
};
parsl_context *par_ctx = parsl_create_context((parsl_config){
.thickness = d,
.flags= PARSL_FLAG_ANNOTATIONS,
.u_mode = PAR_U_MODE_DISTANCE
});
parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){
.num_vertices = n,
.num_spines = 1,
.vertices = par_v,
.spine_lengths = spine_lens,
.closed = 0,
});
HMM_Vec2 *ret = NULL;
arraddn(ret,mesh->num_vertices);
for (int i = 0; i < mesh->num_vertices; i++) {
ret[i].x = mesh->positions[i].x;
ret[i].y = mesh->positions[i].y;
};
return ret;
}
JSC_CCALL(math_inflate,
HMM_Vec2 *p = js2cpvec2arr(js,argv[0]);
double d = js2number(js,argv[1]);
HMM_Vec2 *infl = inflatepoints(p,d, js_arrlen(js,argv[0]));
ret = vecarr2js(js,infl, arrlen(infl));
arrfree(infl);
arrfree(p);
)
*/
JSC_CCALL(math_rotate,
HMM_Vec2 vec = js2vec2(js,argv[0]);
double angle = js2angle(js, argv[1]);
HMM_Vec2 pivot = JS_IsUndefined(argv[2]) ? v2zero : js2vec2(js,argv[2]);
// vec = vec - pivot
// cblas_saxpy(2, -1.0f, pivot.e, 1, vec.e, 1);
vec = HMM_SubV2(vec,pivot);
// Length of the vector (r)
// float r = sqrtf(cblas_sdot(2, vec.e, 1, vec.e, 1));
float r = HMM_LenV2(vec);
// Update angle
angle += atan2f(vec.y, vec.x);
// Update vec to rotated position
vec.x = r * cosf(angle);
vec.y = r * sinf(angle);
// vec = vec + pivot
// cblas_saxpy(2, 1.0f, pivot.e, 1, vec.e, 1);
vec = HMM_AddV2(vec,pivot);
// Convert back to JS and return
return vec22js(js, vec);
)
JSC_CCALL(math_norm,
int len = js_arrlen(js,argv[0]);
switch(len) {
case 2: return vec22js(js,HMM_NormV2(js2vec2(js,argv[0])));
case 3: return vec32js(js, HMM_NormV3(js2vec3(js,argv[0])));
case 4: return vec42js(js,HMM_NormV4(js2vec4(js,argv[0])));
}
double length = arr_vec_length(js,argv[0]);
JSValue newarr = JS_NewArray(js);
for (int i = 0; i < len; i++)
JS_SetPropertyUint32(js, newarr, i, number2js(js,js_getnum_uint32(js, argv[0],i)/length));
ret = newarr;
)
JSC_CCALL(math_angle_between,
int len = js_arrlen(js,argv[0]);
switch(len) {
case 2: return angle2js(js,HMM_AngleV2(js2vec2(js,argv[0]), js2vec2(js,argv[1])));
case 3: return angle2js(js,HMM_AngleV3(js2vec3(js,argv[0]), js2vec3(js,argv[1])));
case 4: return angle2js(js,HMM_AngleV4(js2vec4(js,argv[0]), js2vec4(js,argv[1])));
}
return JS_ThrowReferenceError(js, "Input array must have a length between 2 and 4.");
)
JSC_CCALL(math_lerp,
double s = js2number(js,argv[0]);
double f = js2number(js,argv[1]);
double t = js2number(js,argv[2]);
ret = number2js(js,(f-s)*t+s);
)
int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
JSC_CCALL(math_gcd, ret = number2js(js,gcd(js2number(js,argv[0]), js2number(js,argv[1]))); )
JSC_CCALL(math_lcm,
double a = js2number(js,argv[0]);
double b = js2number(js,argv[1]);
ret = number2js(js,(a*b)/gcd(a,b));
)
JSC_CCALL(math_clamp,
double x = js2number(js,argv[0]);
double l = js2number(js,argv[1]);
double h = js2number(js,argv[2]);
return number2js(js,x > h ? h : x < l ? l : x);
)
JSC_CCALL(math_angledist,
double a1 = js2number(js,argv[0]);
double a2 = js2number(js,argv[1]);
a1 = fmod(a1,1);
a2 = fmod(a2,1);
double dist = a2-a1;
if (dist == 0) return number2js(js,dist);
if (dist > 0) {
if (dist > 0.5) return number2js(js,dist-1);
return number2js(js,dist);
}
if (dist < -0.5) return number2js(js,dist+1);
return number2js(js,dist);
)
JSC_CCALL(math_length, return number2js(js,arr_vec_length(js,argv[0])); )
double rand_range(JSContext *js, double min, double max)
{
MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand;
return genRand(&mrand) * (max-min)+min;
}
JSC_CCALL(math_jitter,
double n = js2number(js,argv[0]);
double pct = js2number(js,argv[1]);
return number2js(js,n + (rand_range(js,-pct,pct)*n));
)
JSC_CCALL(math_mean,
double len = js_arrlen(js,argv[0]);
double sum = 0;
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
return number2js(js,sum/len);
)
JSC_CCALL(math_sum,
double sum = 0.0;
int len = js_arrlen(js,argv[0]);
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
return number2js(js,sum);
)
JSC_CCALL(math_sigma,
int len = js_arrlen(js,argv[0]);
double sum = 0;
for (int i = 0; i < len; i++)
sum += js_getnum_uint32(js, argv[0], i);
double mean = sum/(double)len;
double sq_diff = 0;
for (int i = 0; i < len; i++) {
double x = js_getnum_uint32(js, argv[0],i);
sq_diff += pow(x-mean, 2);
}
double variance = sq_diff/((double)len);
return number2js(js,sqrt(variance));
)
JSC_CCALL(math_median,
int len = js_arrlen(js,argv[0]);
double arr[len];
double temp;
for (int i = 0; i < len; i++)
arr[i] = js_getnum_uint32(js, argv[0], i);
for (int i = 0; i < len-1; i++) {
for (int j = i+1; j < len; j++) {
if (arr[i] > arr[j]) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
if (len % 2 == 0) return number2js(js,(arr[len/2-1] + arr[len/2])/2.0);
return number2js(js,arr[len/2]);
)
JSC_CCALL(math_from_to,
HMM_Vec2 from = js2vec2(js,argv[0]);
HMM_Vec2 to = js2vec2(js,argv[1]);
float space = js2number(js,argv[2]);
float from_offset = js2number(js,argv[3]);
float to_offset = js2number(js,argv[4]);
HMM_Vec2 dir = HMM_NormV2(HMM_SubV2(to, from));
from = HMM_AddV2(from, HMM_MulV2F(dir, from_offset));
to = HMM_SubV2(to,HMM_MulV2F(dir, to_offset));
float length = HMM_DistV2(from, to);
int steps = floor(length/space);
float stepsize = length/(steps+1.0);
ret = JS_NewArray(js);
JS_SetPropertyUint32(js,ret,0,vec22js(js,from));
for (int i = 1; i < steps+1; i++) {
HMM_Vec2 val = HMM_AddV2(from, HMM_MulV2F(dir, i*stepsize));
JS_SetPropertyUint32(js, ret, i, vec22js(js,val));
}
JS_SetPropertyUint32(js, ret, steps+1, vec22js(js,to));
)
JSC_CCALL(math_rand,
MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand;
return JS_NewFloat64(js, genRand(&mrand))
)
JSC_CCALL(math_randi,
MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand;
return JS_NewUint32(js, genRandLong(&mrand))
)
JSC_CCALL(math_srand,
MTRand mrand = ((prosperon_rt*)JS_GetContextOpaque(js))->mrand;
if (argc < 1)
m_seedRand(&mrand, time(NULL));
else
m_seedRand(&mrand, js2number(js,argv[0]));
)
JSValue js_math_direction(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t alen, blen;
float *a = js2floats(js, argv[0], &alen);
float *b = js2floats(js, argv[1], &blen);
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
}
float *dir = (float*)malloc(sizeof(float) * len);
if (!dir) {
free(a);
free(b);
return JS_EXCEPTION; // or some error
}
// Compute (b - a)
for (size_t i = 0; i < len; i++)
dir[i] = b[i] - a[i];
// Compute magnitude of dir
float mag = 0.0f;
for (size_t i = 0; i < len; i++)
mag += dir[i] * dir[i];
mag = sqrtf(mag);
// Normalize if possible
if (mag > 0.0f) {
for (size_t i = 0; i < len; i++)
dir[i] /= mag;
} else {
// Optional: if a == b, direction is zero
// you might want to do something else here
// (like return all zeros).
}
JSValue ret = floats2array(js, dir, len);
free(a);
free(b);
free(dir);
return ret;
}
JSValue js_math_reflect(JSContext *js, JSValue self, int argc, JSValue *argv) {
size_t vlen, nlen;
float *vec = js2floats(js, argv[0], &vlen);
float *norm = js2floats(js, argv[1], &nlen);
if (!vec || !norm) {
free(vec);
free(norm);
return JS_UNDEFINED;
}
size_t len = (vlen < nlen) ? vlen : nlen;
if (len == 0) {
free(vec);
free(norm);
return JS_UNDEFINED;
}
// 1) Normalize the planeNormal
// p = planeNormal / |planeNormal|
float mag = 0.0f;
for (size_t i = 0; i < len; i++)
mag += norm[i] * norm[i];
mag = sqrtf(mag);
// If the plane normal is zero-length, no reflection is well-defined
if (mag == 0.0f) {
// Return original vec or undefined, your choice.
// Here, let's just return a copy of the original vec.
JSValue retNoReflect = floats2array(js, vec, vlen);
free(vec);
free(norm);
return retNoReflect;
}
float *pnorm = (float*)malloc(sizeof(float) * len);
if (!pnorm) {
free(vec);
free(norm);
return JS_EXCEPTION;
}
for (size_t i = 0; i < len; i++)
pnorm[i] = norm[i] / mag;
// 2) Compute dot = vec•pnorm
float dot = 0.0f;
for (size_t i = 0; i < len; i++)
dot += vec[i] * pnorm[i];
// 3) reflect = vec - 2*dot*pnorm
float *ref = (float*)malloc(sizeof(float) * len);
if (!ref) {
free(vec);
free(norm);
free(pnorm);
return JS_EXCEPTION;
}
for (size_t i = 0; i < len; i++)
ref[i] = vec[i] - 2.0f * dot * pnorm[i];
// Now return 'ref' as an array
JSValue ret = floats2array(js, ref, len);
free(vec);
free(norm);
free(pnorm);
free(ref);
return ret;
}
static const JSCFunctionListEntry js_math_funcs[] = {
MIST_FUNC_DEF(math, dot,2),
MIST_FUNC_DEF(math, project,2),
MIST_FUNC_DEF(math, rotate, 3),
MIST_FUNC_DEF(math, midpoint, 2),
MIST_FUNC_DEF(math, reflect, 0),
MIST_FUNC_DEF(math, distance, 2),
MIST_FUNC_DEF(math, direction, 2),
MIST_FUNC_DEF(math, angle, 1),
MIST_FUNC_DEF(math, norm, 1),
MIST_FUNC_DEF(math, angle_between, 2),
MIST_FUNC_DEF(math, lerp, 3),
MIST_FUNC_DEF(math, gcd, 2),
MIST_FUNC_DEF(math, lcm, 2),
MIST_FUNC_DEF(math, clamp, 3),
MIST_FUNC_DEF(math, angledist, 2),
MIST_FUNC_DEF(math, jitter, 2),
MIST_FUNC_DEF(math, mean, 1),
MIST_FUNC_DEF(math, sum, 1),
MIST_FUNC_DEF(math, sigma, 1),
MIST_FUNC_DEF(math, median, 1),
MIST_FUNC_DEF(math, length, 1),
MIST_FUNC_DEF(math, from_to, 5),
MIST_FUNC_DEF(math, rand, 0),
MIST_FUNC_DEF(math, randi, 0),
MIST_FUNC_DEF(math, srand,0),
};
#define JS_HMM_FN(OP, HMM, SIGN) \
JSC_CCALL(array_##OP, \
int len = js_arrlen(js,self); \
if (!JS_IsArray(js, argv[0])) { \
double n = js2number(js,argv[0]); \
JSValue arr = JS_NewArray(js); \
for (int i = 0; i < len; i++) \
JS_SetPropertyUint32(js, arr, i, number2js(js,js_getnum_uint32(js, self,i) SIGN n)); \
return arr; \
} \
switch(len) { \
case 2: \
return vec22js(js,HMM_##HMM##V2(js2vec2(js,self), js2vec2(js,argv[0]))); \
case 3: \
return vec32js(js, HMM_##HMM##V3(js2vec3(js,self), js2vec3(js,argv[0]))); \
case 4: \
return vec42js(js,HMM_##HMM##V4(js2vec4(js,self), js2vec4(js,argv[0]))); \
} \
\
JSValue arr = JS_NewArray(js); \
for (int i = 0; i < len; i++) { \
double a = js_getnum_uint32(js, self,i); \
double b = js_getnum_uint32(js, argv[0],i); \
JS_SetPropertyUint32(js, arr, i, number2js(js,a SIGN b)); \
} \
return arr; \
) \
JS_HMM_FN(add, Add, +)
JS_HMM_FN(sub, Sub, -)
JS_HMM_FN(div, Div, /)
JS_HMM_FN(scale, Mul, *)
JSC_CCALL(array_lerp,
double t = js2number(js,argv[1]);
int len = js_arrlen(js,self);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < len; i++) {
double from = js_getnum_uint32(js, self, i);
double to = js_getnum_uint32(js, argv[0], i);
JS_SetPropertyUint32(js, arr, i, number2js(js,(to - from) * t + from));
}
return arr;
)
JSValue js_array_get_x(JSContext *js, JSValue self) { return JS_GetPropertyUint32(js,self,0); }
JSValue js_array_set_x(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,0,val); return JS_UNDEFINED; }
JSValue js_array_get_y(JSContext *js, JSValue self) { return JS_GetPropertyUint32(js,self,1); }
JSValue js_array_set_y(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,1,val); return JS_UNDEFINED; }
JSValue js_array_get_xy(JSContext *js, JSValue self)
{
JSValue arr = JS_NewArray(js);
JS_SetPropertyUint32(js,arr,0,JS_GetPropertyUint32(js,self,0));
JS_SetPropertyUint32(js,arr,1,JS_GetPropertyUint32(js,self,1));
return arr;
}
JSValue js_array_set_xy(JSContext *js, JSValue self, JSValue v)
{
JS_SetPropertyUint32(js,self,0,JS_GetPropertyUint32(js,v,0));
JS_SetPropertyUint32(js,self,1,JS_GetPropertyUint32(js,v,1));
return JS_UNDEFINED;
}
// Single-value accessors
JSValue js_array_get_r(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 0); }
JSValue js_array_set_r(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 0, val); return JS_UNDEFINED; }
JSValue js_array_get_g(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 1); }
JSValue js_array_set_g(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 1, val); return JS_UNDEFINED; }
JSValue js_array_get_b(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 2); }
JSValue js_array_set_b(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 2, val); return JS_UNDEFINED; }
JSValue js_array_get_a(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 3); }
JSValue js_array_set_a(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 3, val); return JS_UNDEFINED; }
// Multi-value accessors
JSValue js_array_get_rgb(JSContext *js, JSValue self)
{
JSValue arr = JS_NewArray(js);
JS_SetPropertyUint32(js, arr, 0, JS_GetPropertyUint32(js, self, 0));
JS_SetPropertyUint32(js, arr, 1, JS_GetPropertyUint32(js, self, 1));
JS_SetPropertyUint32(js, arr, 2, JS_GetPropertyUint32(js, self, 2));
return arr;
}
JSValue js_array_set_rgb(JSContext *js, JSValue self, JSValue val)
{
JS_SetPropertyUint32(js, self, 0, JS_GetPropertyUint32(js, val, 0));
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
return JS_UNDEFINED;
}
JSValue js_array_get_rgba(JSContext *js, JSValue self)
{
JSValue arr = JS_NewArray(js);
JS_SetPropertyUint32(js, arr, 0, JS_GetPropertyUint32(js, self, 0));
JS_SetPropertyUint32(js, arr, 1, JS_GetPropertyUint32(js, self, 1));
JS_SetPropertyUint32(js, arr, 2, JS_GetPropertyUint32(js, self, 2));
JS_SetPropertyUint32(js, arr, 3, JS_GetPropertyUint32(js, self, 3));
return arr;
}
JSValue js_array_set_rgba(JSContext *js, JSValue self, JSValue val)
{
JS_SetPropertyUint32(js, self, 0, JS_GetPropertyUint32(js, val, 0));
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
JS_SetPropertyUint32(js, self, 3, JS_GetPropertyUint32(js, val, 3));
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_array_funcs[] = {
PROTO_FUNC_DEF(array, add, 1),
PROTO_FUNC_DEF(array, sub, 1),
PROTO_FUNC_DEF(array, div,1),
PROTO_FUNC_DEF(array, scale, 1),
PROTO_FUNC_DEF(array, lerp, 2),
JS_CGETSET_DEF("x", js_array_get_x,js_array_set_x),
JS_CGETSET_DEF("y", js_array_get_y, js_array_set_y),
JS_CGETSET_DEF("xy", js_array_get_xy, js_array_set_xy),
JS_CGETSET_DEF("r", js_array_get_r, js_array_set_r),
JS_CGETSET_DEF("g", js_array_get_g, js_array_set_g),
JS_CGETSET_DEF("b", js_array_get_b, js_array_set_b),
JS_CGETSET_DEF("a", js_array_get_a, js_array_set_a),
JS_CGETSET_DEF("rgb", js_array_get_rgb, js_array_set_rgb),
JS_CGETSET_DEF("rgba", js_array_get_rgba, js_array_set_rgba),
};
JSC_CCALL(number_lerp,
double a = js2number(js,self);
double b = js2number(js,argv[0]);
double t = js2number(js,argv[1]);
return number2js(js, (b-a)*t+a);
)
static const JSCFunctionListEntry js_number_funcs[] = {
PROTO_FUNC_DEF(number, lerp, 2),
};
#define JS_SDL_PROP(JS, VAL, SDLPROP, PROP) \
{ \
JSValue v = JS_GetPropertyStr(JS,VAL,#PROP); \
const char *str = JS_ToCString(JS, v); \
SDL_SetAppMetadataProperty(SDLPROP, str); \
JS_FreeCString(JS,str); \
JS_FreeValue(js,v); \
} \
JSC_CCALL(os_engine_start,
if (SDL_GetCurrentThreadID() != main_thread)
return JS_ThrowInternalError(js, "This can only be called from the root actor.");
if (global_window)
return JS_ThrowReferenceError(js, "The engine was already started.");
JSValue p = argv[0];
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_NAME_STRING, name)
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_VERSION_STRING, version)
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_IDENTIFIER_STRING, identifier)
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_CREATOR_STRING, creator)
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_COPYRIGHT_STRING, copyright)
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_URL_STRING, url)
JS_SDL_PROP(js, p, SDL_PROP_APP_METADATA_TYPE_STRING, type)
const char *title;
JS_GETPROP(js,title,argv[0],title,cstring)
SDL_Window *new = SDL_CreateWindow(title, js2number(js, js_getproperty(js,argv[0], "width")), js2number(js,js_getproperty(js,argv[0], "height")), SDL_WINDOW_RESIZABLE);
JS_FreeCString(js,title);
if (!new) return JS_ThrowReferenceError(js, "Couldn't open window: %s\n", SDL_GetError());
SDL_StartTextInput(new);
global_window = new;
return SDL_Window2js(js,new);
)
static JSValue js_keymod(JSContext *js)
{
SDL_Keymod modstate = SDL_GetModState();
JSValue ret = JS_NewObject(js);
if (SDL_KMOD_CTRL & modstate)
JS_SetPropertyStr(js,ret,"ctrl", JS_NewBool(js,1));
if (SDL_KMOD_SHIFT & modstate)
JS_SetPropertyStr(js,ret,"shift", JS_NewBool(js,1));
if (SDL_KMOD_ALT & modstate)
JS_SetPropertyStr(js,ret,"alt", JS_NewBool(js,1));
if (SDL_KMOD_GUI & modstate)
JS_SetPropertyStr(js,ret,"super", JS_NewBool(js,1));
if (SDL_KMOD_NUM & modstate)
JS_SetPropertyStr(js,ret,"numlock", JS_NewBool(js,1));
if (SDL_KMOD_CAPS & modstate)
JS_SetPropertyStr(js,ret,"caps", JS_NewBool(js,1));
if (SDL_KMOD_SCROLL & modstate)
JS_SetPropertyStr(js,ret,"scrolllock", JS_NewBool(js,1));
if (SDL_KMOD_MODE & modstate)
JS_SetPropertyStr(js,ret,"mode", JS_NewBool(js,1));
return ret;
}
JSC_CCALL(camera_list,
int num;
SDL_CameraID *ids = SDL_GetCameras(&num);
if (num == 0) return JS_UNDEFINED;
JSValue jsids = JS_NewArray(js);
for (int i = 0; i < num; i++)
JS_SetPropertyUint32(js,jsids, i, number2js(js,ids[i]));
return jsids;
)
JSC_CCALL(camera_open,
int id = js2number(js,argv[0]);
SDL_Camera *cam = SDL_OpenCamera(id, NULL);
if (!cam) ret = JS_ThrowReferenceError(js, "Could not open camera %d: %s\n", id, SDL_GetError());
else
ret = SDL_Camera2js(js,cam);
)
JSC_CCALL(camera_name,
const char *name = SDL_GetCameraName(js2number(js,argv[0]));
if (!name) return JS_ThrowReferenceError(js, "Could not get camera name from id %d.", (int)js2number(js,argv[0]));
return JS_NewString(js, name);
)
JSC_CCALL(camera_position,
SDL_CameraPosition pos = SDL_GetCameraPosition(js2number(js,argv[0]));
switch(pos) {
case SDL_CAMERA_POSITION_UNKNOWN: return JS_NewString(js,"unknown");
case SDL_CAMERA_POSITION_FRONT_FACING: return JS_NewString(js,"front");
case SDL_CAMERA_POSITION_BACK_FACING: return JS_NewString(js,"back");
}
)
static const JSCFunctionListEntry js_camera_funcs[] = {
MIST_FUNC_DEF(camera, list, 0),
MIST_FUNC_DEF(camera, open, 1),
MIST_FUNC_DEF(camera, name, 1),
MIST_FUNC_DEF(camera, position, 1),
};
JSC_SCALL(SDL_Window_make_renderer,
SDL_Window *win = js2SDL_Window(js,self);
renderer_ctx *ctx = malloc(sizeof(*ctx));
ctx->sdl = SDL_CreateRenderer(win, NULL);
ctx->cam = (shader_globals){0};
if (!ctx->sdl) {
free(ctx);
return JS_ThrowReferenceError(js, "Error creating renderer: %s",SDL_GetError());
}
SDL_SetRenderDrawBlendMode(ctx->sdl, SDL_BLENDMODE_BLEND);
return renderer_ctx2js(js,ctx);
)
SDL_GPUShaderFormat js2SDL_GPUShaderFormat(JSContext *js, JSValue v)
{
const char *format_str = JS_ToCString(js, v);
SDL_GPUShaderFormat format = SDL_GPU_SHADERFORMAT_SPIRV; // default to SPIR-V
if (format_str) {
if (!strcmp(format_str, "private")) format = SDL_GPU_SHADERFORMAT_PRIVATE;
else if (!strcmp(format_str, "spv")) format = SDL_GPU_SHADERFORMAT_SPIRV;
else if (!strcmp(format_str, "dxbc")) format = SDL_GPU_SHADERFORMAT_DXBC;
else if (!strcmp(format_str, "dxil")) format = SDL_GPU_SHADERFORMAT_DXIL;
else if (!strcmp(format_str, "msl")) format = SDL_GPU_SHADERFORMAT_MSL;
else if (!strcmp(format_str, "metallib")) format = SDL_GPU_SHADERFORMAT_METALLIB;
JS_FreeCString(js, format_str);
}
return format;
}
JSC_CCALL(SDL_Window_make_gpu,
if (global_gpu)
return JS_ThrowReferenceError(js, "A gpu has already been created somewhere.");
const char *name = JS_ToCString(js,argv[1]);
SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, name);
JS_FreeCString(js,name);
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, JS_ToBool(js,argv[0]));
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, 1);
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, 1);
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, 1);
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, 1);
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN, 1);
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, 1);
SDL_GPUDevice *gpu = SDL_CreateGPUDeviceWithProperties(props);
SDL_DestroyProperties(props);
if (!gpu) return JS_ThrowReferenceError(js, "Could not create GPU device! %s", SDL_GetError());
global_gpu = gpu;
return SDL_GPUDevice2js(js,gpu);
)
JSC_CCALL(SDL_Window_fullscreen,
SDL_SetWindowFullscreen(js2SDL_Window(js,self), SDL_WINDOW_FULLSCREEN)
)
JSValue js_SDL_Window_keyboard_shown(JSContext *js, JSValue self) {
SDL_Window *window = js2SDL_Window(js,self);
return JS_NewBool(js,SDL_ScreenKeyboardShown(window));
}
JSValue js_window_theme(JSContext *js, JSValue self)
{
return JS_UNDEFINED;
}
JSValue js_window_safe_area(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
rect r;
SDL_GetWindowSafeArea(w, &r);
return rect2js(js,r);
}
JSValue js_window_bordered(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowBordered(w, JS_ToBool(js,argv[0]));
return JS_UNDEFINED;
}
JSValue js_window_get_title(JSContext *js, JSValue self)
{
SDL_Window *w = js2SDL_Window(js,self);
const char *title = SDL_GetWindowTitle(w);
return JS_NewString(js,title);
}
JSValue js_window_set_title(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
const char *title = JS_ToCString(js,val);
SDL_SetWindowTitle(w,title);
JS_FreeCString(js,title);
return JS_UNDEFINED;
}
JSValue js_window_get_size(JSContext *js, JSValue self)
{
SDL_Window *win = js2SDL_Window(js,self);
int w, h;
SDL_GetWindowSize(win, &w, &h);
return vec22js(js, (HMM_Vec2){w,h});
}
JSValue js_window_set_size(JSContext *js, JSValue self, JSValue val)
{
SDL_Window *w = js2SDL_Window(js,self);
HMM_Vec2 size = js2vec2(js,val);
SDL_SetWindowSize(w,size.x,size.y);
return JS_UNDEFINED;
}
JSValue js_window_set_icon(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_Surface *s = js2SDL_Surface(js,argv[0]);
if (!SDL_SetWindowIcon(w,s))
return JS_ThrowReferenceError(js, "could not set window icon: %s", SDL_GetError());
return JS_UNDEFINED;
}
JSValue js_window_mouse_grab(JSContext *js, JSValue self, int argc, JSValue *argv)
{
SDL_Window *w = js2SDL_Window(js,self);
SDL_SetWindowMouseGrab(w, JS_ToBool(js,argv[0]));
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_SDL_Window_funcs[] = {
MIST_FUNC_DEF(SDL_Window, fullscreen, 0),
MIST_FUNC_DEF(SDL_Window, make_renderer, 1),
MIST_FUNC_DEF(SDL_Window, make_gpu, 2),
MIST_FUNC_DEF(SDL_Window, keyboard_shown, 0),
MIST_FUNC_DEF(window, theme, 0),
MIST_FUNC_DEF(window, safe_area, 0),
MIST_FUNC_DEF(window, bordered, 1),
MIST_FUNC_DEF(window, set_icon, 1),
CGETSET_ADD(window, title),
CGETSET_ADD(window, size),
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);
)
JSC_CCALL(SDL_Renderer_present,
SDL_Renderer *ren = js2renderer_ctx(js,self)->sdl;
SDL_RenderPresent(ren);
)
JSC_CCALL(SDL_Renderer_draw_color,
SDL_Renderer *renderer = js2renderer_ctx(js,self)->sdl;
colorf color = js2color(js,argv[0]);
SDL_SetRenderDrawColorFloat(renderer, color.r,color.g,color.b,color.a);
)
SDL_GPUColorTargetInfo js2SDL_GPUColorTargetInfo(JSContext *js, JSValue v)
{
SDL_GPUColorTargetInfo info = {0};
JS_GETPROP(js, info.texture, v, texture, SDL_GPUTexture)
JS_GETPROP(js,info.mip_level,v,mip_level,number)
JS_GETPROP(js,info.layer_or_depth_plane, v, layer, number)
JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp)
JS_GETPROP(js,info.store_op,v,store,SDL_GPUStoreOp)
JS_GETPROP(js,info.resolve_mip_level,v,resolve_mip_level,number)
JS_GETPROP(js,info.resolve_layer,v,resolve_layer,number)
JS_GETPROP(js,info.clear_color,v,clear,SDL_FColor)
return info;
}
JSC_CCALL(renderer_load_texture,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
SDL_Surface *surf = js2SDL_Surface(js,argv[0]);
if (!surf) return JS_ThrowReferenceError(js, "Surface was not a surface.");
SDL_Texture *tex = SDL_CreateTextureFromSurface(r,surf);
if (!tex) return JS_ThrowReferenceError(js, "Could not create texture from surface: %s", SDL_GetError());
ret = SDL_Texture2js(js,tex);
JS_SetProperty(js,ret,width, number2js(js,tex->w));
JS_SetProperty(js,ret,height, number2js(js,tex->h));
)
JSC_CCALL(renderer_get_image,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
SDL_Surface *surf = NULL;
if (!JS_IsUndefined(argv[0])) {
rect rect = js2rect(js,argv[0]);
surf = SDL_RenderReadPixels(r,&rect);
} else
surf = SDL_RenderReadPixels(r,NULL);
if (!surf) return JS_ThrowReferenceError(js, "could not make surface from renderer");
return SDL_Surface2js(js,surf);
)
JSC_CCALL(renderer_line,
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;
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,
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]);
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, pts, len);
return JS_UNDEFINED;
}
HMM_Vec2 w = js2vec2(js, argv[0]);
SDL_FPoint p = renderer_world_to_screen(ctx, w);
SDL_RenderPoint(r, p.x, p.y);
)
JSC_CCALL(renderer_rects,
renderer_ctx *ctx = js2renderer_ctx(js, self);
SDL_Renderer *r = ctx->sdl;
/* array-of-rectangles case */
if (JS_IsArray(js, argv[0])) {
int len = js_arrlen(js, argv[0]);
if (len <= 0) return JS_UNDEFINED;
SDL_FRect rects[len];
for (int i = 0; i < len; ++i) {
JSValue val = JS_GetPropertyUint32(js, argv[0], i);
rect w = js2rect(js, val);
JS_FreeValue(js, val);
w = renderer_worldrect_to_screen(ctx, w);
rects[i] = w;
}
if (!SDL_RenderFillRects(r, rects, len))
return JS_ThrowReferenceError(js, "SDL_RenderFillRects: %s", SDL_GetError());
return JS_UNDEFINED;
}
/* single-rect path */
rect w = js2rect(js, argv[0]);
w = renderer_worldrect_to_screen(ctx, w);
if (!SDL_RenderFillRects(r, &w, 1))
return JS_ThrowReferenceError(js, "SDL_RenderFillRects: %s", SDL_GetError());
)
// Should take a single struct with pos, color, uv, and indices arrays
JSC_CCALL(renderer_geometry,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
JSValue pos = JS_GetPropertyStr(js,argv[1], "pos");
JSValue color = JS_GetPropertyStr(js,argv[1], "color");
JSValue uv = JS_GetPropertyStr(js,argv[1], "uv");
JSValue indices = JS_GetPropertyStr(js,argv[1], "indices");
int vertices = js_getnum_str(js, argv[1], "vertices");
int count = js_getnum_str(js, argv[1], "count");
size_t pos_stride, indices_stride, uv_stride, color_stride;
void *posdata = get_gpu_buffer(js,pos, &pos_stride, NULL);
void *idxdata = get_gpu_buffer(js,indices, &indices_stride, NULL);
void *uvdata = get_gpu_buffer(js,uv, &uv_stride, NULL);
void *colordata = get_gpu_buffer(js,color,&color_stride, NULL);
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
HMM_Vec2 *trans_pos = malloc(vertices*sizeof(HMM_Vec2));
memcpy(trans_pos,posdata, sizeof(HMM_Vec2)*vertices);
for (int i = 0; i < vertices; i++)
trans_pos[i] = HMM_MulM3V3(cam_mat, (HMM_Vec3){trans_pos[i].x, trans_pos[i].y, 1}).xy;
if (!SDL_RenderGeometryRaw(r, tex, trans_pos, pos_stride,colordata,color_stride,uvdata, uv_stride, vertices, idxdata, count, indices_stride))
ret = JS_ThrowReferenceError(js, "Error rendering geometry: %s",SDL_GetError());
free(trans_pos);
JS_FreeValue(js,pos);
JS_FreeValue(js,color);
JS_FreeValue(js,uv);
JS_FreeValue(js,indices);
)
JSC_CCALL(renderer_logical_size,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
HMM_Vec2 v = js2vec2(js,argv[0]);
SDL_SetRenderLogicalPresentation(r,v.x,v.y,SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
)
JSC_CCALL(renderer_viewport,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
if (JS_IsUndefined(argv[0]))
SDL_SetRenderViewport(r,NULL);
else {
rect view = js2rect(js,argv[0]);
SDL_SetRenderViewport(r,&view);
}
)
JSC_CCALL(renderer_clip,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
if (JS_IsUndefined(argv[0]))
SDL_SetRenderClipRect(r,NULL);
else {
rect view = js2rect(js,argv[0]);
SDL_SetRenderClipRect(r,&view);
}
)
JSC_CCALL(renderer_scale,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
HMM_Vec2 v = js2vec2(js,argv[0]);
SDL_SetRenderScale(r, v.x, v.y);
)
JSC_CCALL(renderer_vsync,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
return JS_NewBool(js, SDL_SetRenderVSync(r,js2number(js,argv[0])));
)
// This returns the coordinates inside the
JSC_CCALL(renderer_coords,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
HMM_Vec2 pos, coord;
pos = js2vec2(js,argv[0]);
SDL_RenderCoordinatesFromWindow(r,pos.x,pos.y, &coord.x, &coord.y);
return vec22js(js,coord);
)
JSC_CCALL(renderer_camera,
renderer_ctx *ctx = js2renderer_ctx(js,self);
JSValue camera = argv[0];
ctx->cam = camera_globals(js, argv[0]);
)
JSC_CCALL(renderer_screen2world,
HMM_Mat3 inv = HMM_InvGeneralM3(cam_mat);
HMM_Vec3 pos = js2vec3(js,argv[0]);
return vec22js(js, HMM_MulM3V3(inv, pos).xy);
)
JSC_CCALL(renderer_target,
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
if (JS_IsUndefined(argv[0]))
SDL_SetRenderTarget(r, NULL);
else {
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
SDL_SetRenderTarget(r,tex);
}
)
// Given an array of sprites, make the necessary geometry
// A sprite is expected to have:
// transform: a transform encoding position and rotation. its scale is in pixels - so a scale of 1 means the image will draw only on a single pixel.
// image: a standard prosperon image of a surface, rect, and texture
// color: the color this sprite should be hued by
JSC_CCALL(renderer_make_sprite_mesh,
JSValue sprites = argv[0];
size_t quads = js_arrlen(js,argv[0]);
size_t verts = quads*4;
size_t count = quads*6;
HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts);
HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts);
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
for (int i = 0; i < quads; i++) {
JSValue sub = JS_GetPropertyUint32(js,sprites,i);
JSValue jstransform = JS_GetProperty(js,sub,transform);
JSValue jssrc = JS_GetProperty(js,sub,src);
JSValue jscolor = JS_GetProperty(js,sub,color);
HMM_Vec4 color;
rect src;
if (JS_IsUndefined(jssrc))
src = (rect){.x = 0, .y = 0, .w = 1, .h = 1};
else
src = js2rect(js,jssrc);
if (JS_IsUndefined(jscolor))
color = (HMM_Vec4){1,1,1,1};
else
color = js2vec4(js,jscolor);
// Calculate the base index for the current quad
size_t base = i * 4;
// Define the UV coordinates based on the source rectangle
uvdata[base + 0] = (HMM_Vec2){ src.x, src.y + src.h };
uvdata[base + 1] = (HMM_Vec2){ src.x + src.w, src.y + src.h };
uvdata[base + 2] = (HMM_Vec2){ src.x, src.y };
uvdata[base + 3] = (HMM_Vec2){ src.x + src.w, src.y };
colordata[base] = color;
colordata[base+1] = color;
colordata[base+2] = color;
colordata[base+3] = color;
JS_FreeValue(js,jstransform);
JS_FreeValue(js,sub);
JS_FreeValue(js,jscolor);
JS_FreeValue(js,jssrc);
}
ret = JS_NewObject(js);
JS_SetProperty(js, ret, pos, make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
JS_SetProperty(js, ret, uv, make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
JS_SetProperty(js, ret, color, make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0));
JS_SetProperty(js, ret, indices, make_quad_indices_buffer(js, quads));
JS_SetProperty(js, ret, vertices, number2js(js, verts));
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_Texture *tex = js2SDL_Texture(js,argv[0]);
rect src = js2rect(js,argv[1]);
rect dst = js2rect(js,argv[2]);
double angle;
JS_ToFloat64(js, &angle, argv[3]);
HMM_Vec2 anchor = js2vec2(js, argv[4]);
SDL_RenderTextureRotated(ctx->sdl, tex, &src, &dst, angle, &anchor, SDL_FLIP_NONE);
)
JSC_CCALL(renderer_sprite,
renderer_ctx *ctx = js2renderer_ctx(js, self);
sprite *sp = js2sprite(js,argv[0]);
SDL_Texture *tex;
JS_GETATOM(js, tex, sp->image, texture, SDL_Texture);
// rect dst = renderer_worldrect_to_screen(ctx, sp->affine);
rect dst = sp->affine;
rect uv = {x:0,y:0,w:48,h:24};
SDL_RenderTexture(ctx->sdl, tex, &uv, &dst);
)
static const JSCFunctionListEntry js_renderer_ctx_funcs[] = {
MIST_FUNC_DEF(SDL_Renderer, draw_color, 1),
MIST_FUNC_DEF(SDL_Renderer, present, 0),
MIST_FUNC_DEF(SDL_Renderer, clear, 0),
MIST_FUNC_DEF(renderer, line, 1),
MIST_FUNC_DEF(renderer, point, 1),
MIST_FUNC_DEF(renderer, texture, 5),
MIST_FUNC_DEF(renderer, rects, 1),
MIST_FUNC_DEF(renderer, geometry, 2),
MIST_FUNC_DEF(renderer, sprite, 1),
MIST_FUNC_DEF(renderer, load_texture, 1),
MIST_FUNC_DEF(renderer, get_image, 1),
MIST_FUNC_DEF(renderer, scale, 1),
MIST_FUNC_DEF(renderer, logical_size,1),
MIST_FUNC_DEF(renderer, viewport,1),
MIST_FUNC_DEF(renderer, clip,1),
MIST_FUNC_DEF(renderer, vsync,1),
MIST_FUNC_DEF(renderer, coords, 1),
MIST_FUNC_DEF(renderer, camera, 2),
MIST_FUNC_DEF(renderer, screen2world, 1),
MIST_FUNC_DEF(renderer, target, 1),
MIST_FUNC_DEF(renderer, make_sprite_mesh, 2),
};
// GPU API
JSC_CCALL(gpu_claim_window,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
SDL_Window *win = js2SDL_Window(js, argv[0]);
SDL_ClaimWindowForGPUDevice(gpu,win);
)
JSC_CCALL(gpu_set_swapchain,
if (!SDL_SetGPUSwapchainParameters(global_gpu,global_window, js2SDL_GPUSwapchainComposition(js,argv[0]), js2SDL_GPUPresentMode(js,argv[1])))
return JS_ThrowReferenceError(js, "Could not set: %s\n", SDL_GetError());
)
JSC_CCALL(cmd_acquire_swapchain,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
Uint32 w,h;
SDL_GPUTexture *texture;
SDL_AcquireGPUSwapchainTexture(cmds,global_window, &texture, &w, &h);
if (!texture) return JS_UNDEFINED;
JSValue swap = JS_UNDEFINED;
JSValue *js_swapchains = ((prosperon_rt*)JS_GetContextOpaque(js))->js_swapchains;
for (int i = 0; i < arrlen(js_swapchains); i++) {
if (js2SDL_GPUTexture(js,js_swapchains[i]) == texture) {
swap = js_swapchains[i];
break;
}
}
if (JS_IsUndefined(swap)) {
swap = SDL_GPUTexture2js(js,texture);
arrput(js_swapchains,swap);
}
JS_SetPropertyStr(js,swap,"width", number2js(js,w));
JS_SetPropertyStr(js,swap,"height", number2js(js,h));
return JS_DupValue(js,swap);
)
JSC_CCALL(cmd_swapchain_pass,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
SDL_GPUColorTargetInfo info = {0};
Uint32 w, h;
SDL_AcquireGPUSwapchainTexture(cmds, global_window, &info.texture, &w,&h);
info.load_op = SDL_GPU_LOADOP_CLEAR;
info.store_op = SDL_GPU_STOREOP_STORE;
colorf c = js2color(js,argv[0]);
info.clear_color = (SDL_FColor){c.r,c.g,c.b,c.a};
SDL_GPURenderPass *pass = SDL_BeginGPURenderPass(
cmds,
&info,
1,
NULL
);
if (!pass) return JS_ThrowReferenceError(js, "Unable to create swapchain pass: %s", SDL_GetError());
JSValue jspass = SDL_GPURenderPass2js(js,pass);
JS_SetPropertyStr(js,jspass,"size", vec22js(js,(HMM_Vec2){w,h}));
return jspass;
)
JSC_CCALL(gpu_load_texture,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
SDL_Surface *surf = js2SDL_Surface(js, argv[0]);
if (!surf) return JS_ThrowReferenceError(js, "Surface was not a surface.");
int compression_level = js2number(js,argv[1]);
if (compression_level < 0) compression_level = 0;
if (compression_level > 2) compression_level = 2;
int dofree = 0;
SDL_PixelFormat sfmt = surf->format;
const SDL_PixelFormatDetails *pdetails = SDL_GetPixelFormatDetails(sfmt);
if (!pdetails) {
// If we can't get pixel format details, fall back to converting to RGBA8888
surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA8888);
dofree = 1;
sfmt = SDL_PIXELFORMAT_RGBA8888;
pdetails = SDL_GetPixelFormatDetails(sfmt);
if (!pdetails) {
// Should never happen with RGBA8888, but just in case
return JS_ThrowReferenceError(js, "Unable to get pixel format details.");
}
}
// Check if format has alpha
bool has_alpha = (pdetails->Amask != 0);
// Choose a GPU format that closely matches the surface's format
SDL_GPUTextureFormat chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
switch (sfmt) {
case SDL_PIXELFORMAT_RGBA32:
case SDL_PIXELFORMAT_RGBX8888:
case SDL_PIXELFORMAT_XRGB8888:
case SDL_PIXELFORMAT_XBGR8888:
chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
break;
case SDL_PIXELFORMAT_RGBA4444:
case SDL_PIXELFORMAT_ABGR4444:
case SDL_PIXELFORMAT_ARGB4444:
case SDL_PIXELFORMAT_BGRA4444:
// 4-4-4-4 format
chosen_format = SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM;
break;
case SDL_PIXELFORMAT_RGB565:
case SDL_PIXELFORMAT_BGR565:
// 5-6-5 format
chosen_format = SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM;
break;
case SDL_PIXELFORMAT_RGBA5551:
case SDL_PIXELFORMAT_ARGB1555:
case SDL_PIXELFORMAT_BGRA5551:
case SDL_PIXELFORMAT_ABGR1555:
// 5-5-5-1 format
chosen_format = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM;
break;
default:
surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32);
dofree = 1;
sfmt = SDL_PIXELFORMAT_RGBA32;
pdetails = SDL_GetPixelFormatDetails(sfmt);
chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
has_alpha = true;
break;
}
// If compression_level > 0, we override format with BC1/BC3
bool compress = (compression_level > 0);
int stb_mode = STB_DXT_NORMAL;
SDL_GPUTextureFormat compressed_format = chosen_format;
if (compress) {
if (has_alpha)
compressed_format = SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM; // DXT5
else
compressed_format = SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM; // DXT1
if (compression_level > 1) stb_mode = STB_DXT_HIGHQUAL;
}
SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu, &(SDL_GPUTextureCreateInfo) {
.type = SDL_GPU_TEXTURETYPE_2D,
.format = compress ? compressed_format : chosen_format,
.width = surf->w,
.height = surf->h,
.layer_count_or_depth = 1,
.num_levels = 1,
.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER
});
const void *pixel_data = surf->pixels;
size_t pixel_data_size = surf->pitch * surf->h;
unsigned char *upload_data = NULL;
size_t upload_size = pixel_data_size;
if (compress) {
// Compress with stb_dxt
int block_width = (surf->w + 3) / 4;
int block_height = (surf->h + 3) / 4;
int block_size = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM) ? 8 : 16;
int blocks_count = block_width * block_height;
unsigned char *compressed_data = (unsigned char*)malloc(blocks_count * block_size);
if (!compressed_data) {
if (dofree) SDL_DestroySurface(surf);
return JS_ThrowOutOfMemory(js);
}
const unsigned char *src_pixels = (const unsigned char *)pixel_data;
for (int by = 0; by < block_height; ++by) {
for (int bx = 0; bx < block_width; ++bx) {
unsigned char block_rgba[4*4*4];
memset(block_rgba, 0, sizeof(block_rgba));
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 4; ++x) {
int sx = bx*4 + x;
int sy = by*4 + y;
if (sx < surf->w && sy < surf->h) {
const unsigned char *pixel = src_pixels + sy * surf->pitch + sx * pdetails->bytes_per_pixel;
Uint32 pixelValue = 0;
// Copy pixel data into a Uint32 for SDL_GetRGBA-like extraction
// We'll use masks/shifts from pdetails
memcpy(&pixelValue, pixel, pdetails->bytes_per_pixel);
// Extract RGBA
unsigned char r = (pixelValue & pdetails->Rmask) >> pdetails->Rshift;
unsigned char g = (pixelValue & pdetails->Gmask) >> pdetails->Gshift;
unsigned char b = (pixelValue & pdetails->Bmask) >> pdetails->Bshift;
unsigned char a = pdetails->Amask ? ((pixelValue & pdetails->Amask) >> pdetails->Ashift) : 255;
// If bits are not fully 8-bit, scale them:
// pdetails->Rbits (etc.) give how many bits each channel uses
int Rmax = (1 << pdetails->Rbits) - 1;
int Gmax = (1 << pdetails->Gbits) - 1;
int Bmax = (1 << pdetails->Bbits) - 1;
int Amax = pdetails->Abits ? ((1 << pdetails->Abits) - 1) : 255;
if (pdetails->Rbits < 8) r = (r * 255) / Rmax;
if (pdetails->Gbits < 8) g = (g * 255) / Gmax;
if (pdetails->Bbits < 8) b = (b * 255) / Bmax;
if (pdetails->Amask && pdetails->Abits < 8) a = (a * 255) / Amax;
// Set block pixel
block_rgba[(y*4+x)*4+0] = r;
block_rgba[(y*4+x)*4+1] = g;
block_rgba[(y*4+x)*4+2] = b;
block_rgba[(y*4+x)*4+3] = a;
}
}
}
unsigned char *dest_block = compressed_data + (by * block_width + bx)*block_size;
int alpha = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM) ? 1 : 0;
stb_compress_dxt_block(dest_block, block_rgba, alpha, stb_mode);
}
}
upload_data = compressed_data;
upload_size = blocks_count * block_size;
} else {
// No compression, upload directly
upload_data = (unsigned char*)pixel_data;
upload_size = pixel_data_size;
}
SDL_GPUTransferBuffer *tex_buffer = SDL_CreateGPUTransferBuffer(
gpu,
&(SDL_GPUTransferBufferCreateInfo) {
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = upload_size
});
void *tex_ptr = SDL_MapGPUTransferBuffer(gpu, tex_buffer, false);
memcpy(tex_ptr, upload_data, upload_size);
SDL_UnmapGPUTransferBuffer(gpu, tex_buffer);
SDL_GPUCommandBuffer *uploadcmd = SDL_AcquireGPUCommandBuffer(gpu);
SDL_GPUCopyPass *copypass = SDL_BeginGPUCopyPass(uploadcmd);
SDL_UploadToGPUTexture(
copypass,
&(SDL_GPUTextureTransferInfo) {
.transfer_buffer = tex_buffer,
.offset = 0
},
&(SDL_GPUTextureRegion) {
.texture = tex,
.w = surf->w,
.h = surf->h,
.d = 1
},
false
);
SDL_EndGPUCopyPass(copypass);
SDL_SubmitGPUCommandBuffer(uploadcmd);
SDL_ReleaseGPUTransferBuffer(gpu, tex_buffer);
if (compress) {
free(upload_data);
}
ret = SDL_GPUTexture2js(js, tex);
JS_SetProperty(js, ret, width, number2js(js, surf->w));
JS_SetProperty(js, ret, height, number2js(js, surf->h));
JS_SetPropertyStr(js,ret,"dim", vec22js(js,(HMM_Vec2){surf->w,surf->h}));
if (dofree) SDL_DestroySurface(surf);
)
static JSValue js_gpu_make_pipeline(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
if (argc < 1)
return JS_ThrowTypeError(js, "gpu_pipeline requires a pipeline object");
JSValue pipe = argv[0];
if (!JS_IsObject(pipe))
return JS_ThrowTypeError(js, "gpu_pipeline argument must be an object");
SDL_GPUGraphicsPipelineCreateInfo info = {0};
// ---------------------------------------------------
// 2. Retrieve vertex buffer descriptions array
// ---------------------------------------------------
JSValue vbd_val = JS_GetPropertyStr(js, pipe, "vertex_buffer_descriptions");
Uint32 vbd_len = js_arrlen(js,vbd_val);
SDL_GPUVertexBufferDescription vbd[vbd_len];
for (Uint32 i = 0; i < vbd_len; i++) {
JSValue elem = JS_GetPropertyUint32(js, vbd_val, i);
if (JS_IsObject(elem)) {
JSValue slot_val = JS_GetPropertyStr(js, elem, "slot");
JSValue pitch_val = JS_GetPropertyStr(js, elem, "pitch");
JSValue rate_val = JS_GetPropertyStr(js, elem, "input_rate");
JSValue step_val = JS_GetPropertyStr(js, elem, "instance_step_rate");
// Slot
Uint32 slot = 0;
JS_ToUint32(js, &slot, slot_val);
JS_FreeValue(js, slot_val);
// Pitch
Uint32 pitch = 0;
JS_ToUint32(js, &pitch, pitch_val);
JS_FreeValue(js, pitch_val);
// Input Rate
const char *rate_str = JS_ToCString(js, rate_val);
SDL_GPUVertexInputRate input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
if (rate_str) {
if (!strcmp(rate_str, "vertex")) input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
else if (!strcmp(rate_str, "instance")) input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE;
JS_FreeCString(js, rate_str);
}
JS_FreeValue(js, rate_val);
// Instance Step Rate
Uint32 step_rate = 0;
JS_ToUint32(js, &step_rate, step_val);
JS_FreeValue(js, step_val);
vbd[i].slot = slot;
vbd[i].pitch = pitch;
vbd[i].input_rate = input_rate;
vbd[i].instance_step_rate = step_rate;
}
JS_FreeValue(js, elem);
}
JS_FreeValue(js, vbd_val);
// ---------------------------------------------------
// 3. Retrieve vertex attributes array
// ---------------------------------------------------
JSValue vat_val = JS_GetPropertyStr(js, pipe, "vertex_attributes");
Uint32 vat_len = js_arrlen(js,vat_val);
SDL_GPUVertexAttribute vat[vat_len];
for (Uint32 i = 0; i < vat_len; i++) {
JSValue elem = JS_GetPropertyUint32(js, vat_val, i);
if (JS_IsObject(elem)) {
JSValue loc_val = JS_GetPropertyStr(js, elem, "location");
JSValue slot_val = JS_GetPropertyStr(js, elem, "buffer_slot");
JSValue fmt_val = JS_GetPropertyStr(js, elem, "format");
JSValue off_val = JS_GetPropertyStr(js, elem, "offset");
// location
Uint32 location = 0;
JS_ToUint32(js, &location, loc_val);
JS_FreeValue(js, loc_val);
// buffer_slot
Uint32 buffer_slot = 0;
JS_ToUint32(js, &buffer_slot, slot_val);
JS_FreeValue(js, slot_val);
// format
const char *fmt_str = JS_ToCString(js, fmt_val);
SDL_GPUVertexElementFormat format = -1; // TODO: Check for error (if this is still -1)
if (fmt_str) {
// Map from string to SDL_GPUVertexElementFormat
if (!strcmp(fmt_str, "float2")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
else if (!strcmp(fmt_str, "float3")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
else if (!strcmp(fmt_str, "float4")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4;
else if (!strcmp(fmt_str, "color"))
format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4;
JS_FreeCString(js, fmt_str);
}
JS_FreeValue(js, fmt_val);
// offset
Uint32 offset = 0;
JS_ToUint32(js, &offset, off_val);
JS_FreeValue(js, off_val);
vat[i].location = location;
vat[i].buffer_slot = buffer_slot;
vat[i].format = format;
vat[i].offset = offset;
}
JS_FreeValue(js, elem);
}
JS_FreeValue(js, vat_val);
info.vertex_input_state = (SDL_GPUVertexInputState){
.vertex_buffer_descriptions = vbd,
.num_vertex_buffers = vbd_len,
.vertex_attributes = vat,
.num_vertex_attributes = vat_len
};
// Vertex Shader
JSValue vertex_val = JS_GetPropertyStr(js, pipe, "vertex");
JSValue fragment_val = JS_GetPropertyStr(js,pipe,"fragment");
JS_GETPROP(js,info.vertex_shader, vertex_val, gpu, SDL_GPUShader)
JS_GETPROP(js, info.fragment_shader, fragment_val, gpu, SDL_GPUShader)
JS_FreeValue(js, vertex_val);
JS_FreeValue(js, fragment_val);
// Primitive Type
JS_GETPROP(js, info.primitive_type, pipe, primitive, SDL_GPUPrimitiveType)
// Rasterizer State
JS_GETPROP(js, info.rasterizer_state.fill_mode, pipe, fill, SDL_GPUFillMode)
JS_GETPROP(js, info.rasterizer_state.cull_mode, pipe, cull, SDL_GPUCullMode)
JS_GETPROP(js, info.rasterizer_state.front_face, pipe, face, SDL_GPUFrontFace)
// Depth Stencil State
JSValue depth_val = JS_GetPropertyStr(js, pipe, "depth");
if (JS_IsObject(depth_val)) {
JS_GETPROP(js,info.depth_stencil_state.compare_op, depth_val, compare, SDL_GPUCompareOp)
JS_GETPROP(js,info.depth_stencil_state.enable_depth_test, depth_val, test, bool)
JS_GETPROP(js,info.depth_stencil_state.enable_depth_write, depth_val, write, bool)
JS_GETPROP(js,info.rasterizer_state.depth_bias_constant_factor, depth_val, bias, number)
JS_GETPROP(js,info.rasterizer_state.depth_bias_slope_factor, depth_val,bias_slope_scale, number)
JS_GETPROP(js,info.rasterizer_state.depth_bias_clamp, depth_val,bias_clamp_val, number)
}
JS_FreeValue(js, depth_val);
// Stencil State
JSValue stencil_val = JS_GetPropertyStr(js, pipe, "stencil");
if (JS_IsObject(stencil_val)) {
JS_GETPROP(js,info.depth_stencil_state.enable_stencil_test, stencil_val, enabled, bool)
if (info.depth_stencil_state.enable_stencil_test) {
JS_GETPROP(js, info.depth_stencil_state.front_stencil_state, stencil_val, front, SDL_GPUStencilOpState)
JS_GETPROP(js, info.depth_stencil_state.back_stencil_state, stencil_val, back, SDL_GPUStencilOpState)
JSValue compare_mask_val = JS_GetPropertyStr(js, stencil_val, "compare_mask");
uint32_t tmp;
JS_ToUint32(js, &tmp, compare_mask_val);
info.depth_stencil_state.compare_mask = tmp;
JS_FreeValue(js, compare_mask_val);
// Write Mask
JSValue write_mask_val = JS_GetPropertyStr(js, stencil_val, "write_mask");
JS_ToUint32(js, &tmp, write_mask_val);
info.depth_stencil_state.write_mask = tmp;
}
}
JS_FreeValue(js, stencil_val);
JSValue js_tar = JS_GetPropertyStr(js,pipe,"target");
SDL_GPUGraphicsPipelineTargetInfo target_info = {0};
JSValue color_tars = JS_GetPropertyStr(js,js_tar,"color_targets");
target_info.num_color_targets = js_arrlen(js,color_tars);
SDL_GPUColorTargetDescription dsc[target_info.num_color_targets];
target_info.color_target_descriptions = dsc;
for (int i = 0; i < target_info.num_color_targets; i++) {
JSValue c = JS_GetPropertyUint32(js,color_tars,i);
dsc[i] = js2SDL_GPUColorTargetDescription(js,c);
JS_FreeValue(js,c);
}
JS_FreeValue(js,color_tars);
JS_GETPROP(js,target_info.depth_stencil_format,js_tar,depth,SDL_GPUTextureFormat);
if (target_info.depth_stencil_format) target_info.has_depth_stencil_target = 1;
info.target_info = target_info;
JS_FreeValue(js, js_tar);
// Create the pipeline
SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(gpu, &info);
if (!pipeline) return JS_ThrowInternalError(js, "Failed to create GPU pipeline");
return SDL_GPUGraphicsPipeline2js(js, pipeline);
}
// Now the function to create the sampler
static JSValue js_gpu_make_sampler(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
SDL_GPUSamplerCreateInfo info = {0};
JSValue sampler = argv[0];
JS_GETPROP(js,info.min_filter,sampler,min_filter,SDL_GPUFilter)
JS_GETPROP(js,info.mag_filter,sampler,mag_filter,SDL_GPUFilter)
JS_GETPROP(js,info.mipmap_mode, sampler, mipmap, SDL_GPUSamplerMipmapMode)
JS_GETPROP(js,info.address_mode_u, sampler, u, SDL_GPUSamplerAddressMode)
JS_GETPROP(js,info.address_mode_v, sampler, v, SDL_GPUSamplerAddressMode)
JS_GETPROP(js,info.address_mode_w, sampler, w, SDL_GPUSamplerAddressMode)
JS_GETPROP(js,info.mip_lod_bias, sampler, mip_bias, number)
JS_GETPROP(js,info.max_anisotropy, sampler, max_anisotropy, number)
JS_GETPROP(js,info.compare_op,sampler,compare_op,SDL_GPUCompareOp)
JS_GETPROP(js,info.min_lod,sampler,min_lod,number)
JS_GETPROP(js,info.max_lod,sampler,max_lod,number)
JS_GETPROP(js, info.enable_anisotropy, sampler, anistropy, bool)
JS_GETPROP(js, info.enable_compare, sampler, compare, bool)
// Create the sampler
SDL_GPUSampler *sdl_sampler = SDL_CreateGPUSampler(gpu, &info);
if (!sdl_sampler) return JS_ThrowInternalError(js, "Failed to create GPU sampler");
return SDL_GPUSampler2js(js, sdl_sampler); // You need to implement SDL_GPUSampler2js similar to pipeline
}
JSC_CCALL(gpu_texture,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
SDL_GPUTextureCreateInfo info = {0};
JSValue fmt = argv[0];
JS_GETPROP(js, info.width, fmt, width, number)
JS_GETPROP(js, info.height, fmt, height, number)
JS_GETPROP(js, info.layer_count_or_depth, fmt, layers, number)
JS_GETPROP(js, info.num_levels, fmt, mip_levels, number)
JS_GETPROP(js, info.sample_count, fmt, samples, number)
JS_GETPROP(js, info.type, fmt, type, SDL_GPUTextureType)
JS_GETPROP(js, info.format, fmt, format, SDL_GPUTextureFormat)
if (JS_GETBOOL(js,fmt,"sampler"))
info.usage |= SDL_GPU_TEXTUREUSAGE_SAMPLER;
if (JS_GETBOOL(js,fmt,"color_target"))
info.usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
if (JS_GETBOOL(js,fmt,"depth_target"))
info.usage |= SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
if (JS_GETBOOL(js,fmt,"read"))
info.usage |= (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ);
if (JS_GETBOOL(js,fmt,"write"))
info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE;
if (JS_GETBOOL(js,fmt,"readwrite"))
info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE;
SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu,&info);
if (!tex) return JS_ThrowReferenceError(js, "Unable to create texture: %s", SDL_GetError());
JSValue jstex = SDL_GPUTexture2js(js,tex);
JS_SetPropertyStr(js,jstex,"width", number2js(js,info.width));
JS_SetPropertyStr(js,jstex,"height", number2js(js,info.height));
JS_SetPropertyStr(js,jstex,"dim", vec22js(js,(HMM_Vec2){info.width, info.height}));
return jstex;
)
static HMM_Vec3 base_quad[4] = {
{0.0,0.0,1.0},
{1.0,0.0,1.0},
{0.0,1.0,1.0},
{1.0,1.0,1.0}
};
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},
.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);
}
typedef struct {
text_vert vert[4];
} quad;
// Comparator inline for potential compiler inlining
static inline int sprite_compare(const sprite *a, const sprite *b) {
if (a->layer != b->layer) return a->layer - b->layer;
if (a->affine.y != b->affine.y)
return (b->affine.y - a->affine.y);
if (a->tex != b->tex)
return (a->tex < b->tex) ? -1 : 1;
return 0;
}
// Merge two sorted sub-ranges [left..mid] and [mid+1..right]
static void merge_sprites(sprite *arr, sprite *temp, size_t left, size_t mid, size_t right) {
size_t i = left, j = mid + 1, k = left;
while (i <= mid && j <= right)
if (sprite_compare(&arr[i], &arr[j]) <= 0) temp[k++] = arr[i++];
else temp[k++] = arr[j++];
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
for (size_t p = left; p <= right; p++)
arr[p] = temp[p];
}
// Recursive mergesort
static void merge_sort_recursive(sprite *arr, sprite *temp, size_t left, size_t right) {
if (left >= right) return;
size_t mid = (left + right) / 2;
merge_sort_recursive(arr, temp, left, mid);
merge_sort_recursive(arr, temp, mid + 1, right);
merge_sprites(arr, temp, left, mid, right);
}
// Public function to sort the array of sprites with mergesort
void better_sort_sprites(sprite *arr, size_t length) {
if (length < 2) return;
sprite *temp = malloc(length * sizeof(sprite));
if (!temp) return; // fallback or handle error
merge_sort_recursive(arr, temp, 0, length - 1);
free(temp);
}
int sort_sprite_backtofront(const sprite *a, const sprite *b)
{
if (a->layer != b->layer) return a->layer - b->layer;
if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1;
return 0;
}
int sort_sprite_fronttoback(const sprite *a, const sprite *b)
{
if (a->layer != b->layer) return b->layer - a->layer;
if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1;
return 0;
}
int sort_sprite_texture(const sprite *a, const sprite *b)
{
if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1;
return 0;
}
int sort_sprite(const sprite *a, const sprite *b)
{
if (a->layer != b->layer) return a->layer - b->layer;
if (a->affine.y != b->affine.y) return b->affine.y - a->affine.y;
if (a->tex != b->tex) return ((uintptr_t)a->tex < (uintptr_t)b->tex) ? -1 : 1;
return 0;
}
JSC_CCALL(gpu_sort_sprite,
JSValue a = argv[0];
JSValue b = argv[1];
int alayer, blayer;
JS_GETATOM(js,alayer,a,layer,number)
JS_GETATOM(js,blayer,b,layer,number)
if (alayer != blayer) return number2js(js,alayer - blayer);
rect ar, br;
JS_GETATOM(js,ar,a,rect,rect)
JS_GETATOM(js,br,b,rect,rect)
if (ar.y != br.y) return number2js(js,br.y-ar.y);
JSValue aimg,bimg;
aimg = JS_GetProperty(js,a,image);
bimg = JS_GetProperty(js,b,image);
JS_FreeValue(js,aimg);
JS_FreeValue(js,bimg);
if (!JS_SameValue(js,aimg,bimg)) return number2js(js,JS_VALUE_GET_PTR(aimg) < JS_VALUE_GET_PTR(bimg) ? -1 : 1);
return number2js(js,0);
)
JSC_CCALL(gpu_make_sprite_queue,
sprite *sprites = NULL;
size_t quads = 0;
int needfree = 1;
int sort = js2number(js,argv[3]);
// test for fastest
size_t size;
sprites = JS_GetArrayBuffer(js,&size,argv[0]);
if (sprites) {
quads = size/sizeof(*sprites);
needfree = 0;
for (int i = 0; i < quads; i++)
JS_DupValue(js,sprites[i].image);
} else {
quads = js_arrlen(js, argv[0]);
arrsetcap(sprites, quads);
for (int i = 0; i < quads; i++) {
JSValue sub = JS_GetPropertyUint32(js, argv[0], i);
sprite *jsp = js2sprite(js, sub);
if (jsp) {
arrput(sprites, *jsp);
JS_DupValue(js,jsp->image);
}
else {
sprite sp = {0};
JS_GETATOM(js,sp.affine, sub, rect, rect)
JS_GETATOM(js,sp.color,sub,color,color)
JS_GETATOM(js,sp.layer,sub,layer,number)
JS_GETATOM(js,sp.uv,sub,src,rect)
sp.image = JS_GetProperty(js,sub,image);
JS_GETATOM(js,sp.tex,sp.image,texture,SDL_GPUTexture)
arrput(sprites,sp);
}
JS_FreeValue(js, sub);
}
}
if (sort) qsort(sprites, quads, sizeof(sprite), sort_sprite);
// else qsort(sprites, quads, sizeof(sprite), sort_sprite_texture);
struct quad_buffers buffers = quad_buffers_new(quads*4);
for (int i = 0; i < quads; i++) {
rect pr = sprites[i].affine;
rect uv = sprites[i].uv;
HMM_Vec4 c = sprites[i].color;
int idx = i * 4;
buffers.pos[idx + 0] = (HMM_Vec2){ pr.x, pr.y };
buffers.pos[idx + 1] = (HMM_Vec2){ pr.x+pr.w, pr.y };
buffers.pos[idx + 2] = (HMM_Vec2){ pr.x, pr.y+pr.h };
buffers.pos[idx + 3] = (HMM_Vec2){ pr.x+pr.w, pr.y+pr.h };
buffers.uv[idx + 0] = (HMM_Vec2){ uv.x, uv.y+uv.h };
buffers.uv[idx + 1] = (HMM_Vec2){ uv.x+uv.w, uv.y+uv.h };
buffers.uv[idx + 2] = (HMM_Vec2){ uv.x, uv.y };
buffers.uv[idx + 3] = (HMM_Vec2){ uv.x+uv.w, uv.y };
buffers.color[idx + 0] = c;
buffers.color[idx + 1] = c;
buffers.color[idx + 2] = c;
buffers.color[idx + 3] = c;
}
JSValue mesh = quadbuffers_to_mesh(js, buffers);
ret = JS_NewArray(js);
int first_index = 0;
int count = 0;
int n = 0;
JSValue img = JS_UNDEFINED;
for (int i = 0; i < quads; i++) {
if (!JS_SameValue(js, sprites[i].image, img)) {
if (count > 0) {
JSValue q = JS_NewObject(js);
JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry"));
JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh));
JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2]));
JS_SetPropertyStr(js, q, "image", img);
JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index));
JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6));
JS_SetPropertyUint32(js, ret, n++, q);
}
first_index = i*6;
count = 1;
img = JS_DupValue(js, sprites[i].image);
} else count++;
JS_FreeValue(js,sprites[i].image);
}
if (count > 0) {
JSValue q = JS_NewObject(js);
JS_SetPropertyStr(js, q, "type", JS_NewString(js, "geometry"));
JS_SetPropertyStr(js, q, "mesh", JS_DupValue(js, mesh));
JS_SetPropertyStr(js, q, "pipeline", JS_DupValue(js, argv[2]));
JS_SetPropertyStr(js, q, "image", img);
JS_SetPropertyStr(js, q, "first_index", number2js(js, first_index));
JS_SetPropertyStr(js, q, "num_indices", number2js(js, count * 6));
JS_SetPropertyUint32(js, ret, n++, q);
}
if (needfree)
arrfree(sprites);
JS_FreeValue(js, mesh);
)
JSC_CCALL(gpu_make_sprite_mesh,
size_t quads = js_arrlen(js, argv[0]);
size_t verts = quads*4;
size_t count = quads*6;
// Prepare arrays on CPU
HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts);
HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts);
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
for (int i = 0; i < quads; i++) {
JSValue sub = JS_GetPropertyUint32(js,argv[0],i);
transform *tr;
rect src;
HMM_Vec4 color;
JS_GETATOM(js,src,sub,src,rect)
JS_GETATOM(js,color,sub,color,color)
JS_GETATOM(js,tr,sub,transform,transform)
JS_FreeValue(js,sub);
size_t base = i*4;
if (tr) {
HMM_Mat3 trmat = transform2mat3(tr);
for (int j = 0; j < 4; j++)
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
} else {
rect dst;
JS_GETATOM(js,dst,sub,rect,rect);
posdata[base+0] = (HMM_Vec2){dst.x,dst.y};
posdata[base + 1] = (HMM_Vec2){ dst.x+dst.w, dst.y };
posdata[base + 2] = (HMM_Vec2){ dst.x, dst.y+dst.h };
posdata[base + 3] = (HMM_Vec2){ dst.x+dst.w, dst.y+dst.h };
}
uvdata[base+0] = (HMM_Vec2){src.x, src.y+src.h};
uvdata[base+1] = (HMM_Vec2){src.x+src.w, src.y+src.h};
uvdata[base+2] = (HMM_Vec2){src.x, src.y};
uvdata[base+3] = (HMM_Vec2){src.x+src.w, src.y};
colordata[base+0] = color;
colordata[base+1] = color;
colordata[base+2] = color;
colordata[base+3] = color;
}
// Check old mesh
JSValue old_mesh = JS_UNDEFINED;
if (argc > 1)
old_mesh = argv[1];
// Needed sizes
size_t pos_size = sizeof(*posdata)*verts;
size_t uv_size = sizeof(*uvdata)*verts;
size_t color_size = sizeof(*colordata)*verts;
BufferCheckResult pos_chk = get_or_extend_buffer(js, old_mesh, "pos", pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0);
BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, "uv", uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0);
BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, "color", color_size, JS_TYPED_ARRAY_FLOAT32, 4, 1, 0);
int need_new_all = pos_chk.need_new || uv_chk.need_new || color_chk.need_new;
ret = JS_NewObject(js);
if (need_new_all) {
// Create all new buffers
JSValue new_pos = make_gpu_buffer(js, posdata, pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
JSValue new_uv = make_gpu_buffer(js, uvdata, uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
JSValue new_color = make_gpu_buffer(js, colordata, color_size, JS_TYPED_ARRAY_FLOAT32, 0, 1,0);
JS_SetProperty(js, ret, pos, new_pos);
JS_SetProperty(js, ret, uv, new_uv);
JS_SetProperty(js, ret, color, new_color);
// Indices
JSValue indices = make_quad_indices_buffer(js, quads);
JS_SetProperty(js, ret, indices, indices);
} else {
// Reuse the old buffers
// Just copy data into existing buffers via their pointers
memcpy(pos_chk.ptr, posdata, pos_size);
memcpy(uv_chk.ptr, uvdata, uv_size);
memcpy(color_chk.ptr, colordata, color_size);
// Duplicate old references since we're returning a new object
JS_SetProperty(js, ret, pos, JS_DupValue(js, pos_chk.val));
JS_SetProperty(js, ret, uv, JS_DupValue(js, uv_chk.val));
JS_SetProperty(js, ret, color, JS_DupValue(js, color_chk.val));
// Indices can remain the same if they were also large enough. If using a shared global index buffer:
JSValue indices = make_quad_indices_buffer(js, quads);
JS_SetProperty(js, ret, indices, indices);
}
JS_SetProperty(js, ret, vertices, number2js(js, verts));
JS_SetProperty(js, ret, count, number2js(js, count));
JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count));
// Free temporary CPU arrays
free(posdata);
free(uvdata);
free(colordata);
// Free old buffer values if they were fetched
if (!JS_IsUndefined(pos_chk.val)) JS_FreeValue(js, pos_chk.val);
if (!JS_IsUndefined(uv_chk.val)) JS_FreeValue(js, uv_chk.val);
if (!JS_IsUndefined(color_chk.val)) JS_FreeValue(js, color_chk.val);
return ret;
)
JSC_CCALL(gpu_make_quad,
size_t quads = 1;
size_t verts = quads*4;
size_t count = quads*6;
// Prepare arrays on CPU
HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts);
HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts);
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
HMM_Vec4 usecolor = (HMM_Vec4){1,1,1,1};
posdata[0] = (HMM_Vec2){0,1};
posdata[1] = (HMM_Vec2){1,1};
posdata[2] = (HMM_Vec2){0,0};
posdata[3] = (HMM_Vec2){1,0};
uvdata[0] = (HMM_Vec2){0,0};
uvdata[1] = (HMM_Vec2){1,0};
uvdata[2] = (HMM_Vec2){0,1};
uvdata[3] = (HMM_Vec2){1,1};
colordata[0] = usecolor;
colordata[1] = usecolor;
colordata[2] = usecolor;
colordata[3] = usecolor;
ret = JS_NewObject(js);
JSValue pos = make_gpu_buffer(js, posdata, sizeof(*posdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
JSValue uv = make_gpu_buffer(js, uvdata, sizeof(*uvdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
JSValue color = make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, JS_TYPED_ARRAY_FLOAT32, 0, 1,0);
JS_SetProperty(js, ret, pos, pos);
JS_SetProperty(js, ret, uv, uv);
JS_SetProperty(js, ret, color, color);
JSValue indices = make_quad_indices_buffer(js, quads);
JS_SetProperty(js, ret, indices, indices);
JS_SetProperty(js, ret, vertices, number2js(js, verts));
JS_SetProperty(js, ret, count, number2js(js, count));
JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count));
// Free temporary CPU arrays
free(posdata);
free(uvdata);
free(colordata);
return ret;
)
JSC_CCALL(gpu_driver,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
ret = JS_NewString(js, SDL_GetGPUDeviceDriver(gpu));
)
static JSValue js_gpu_make_shader(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
if (argc < 1 || !JS_IsObject(argv[0]))
return JS_ThrowTypeError(js, "make_shader expects an object with code, stage, num_samplers, num_textures, num_storage_buffers, num_uniform_buffers");
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
JSValue obj = argv[0];
SDL_GPUShaderCreateInfo info = {0};
// code
JSValue code_val = JS_GetPropertyStr(js, obj, "code");
size_t code_size;
void *code_data = JS_GetArrayBuffer(js, &code_size, code_val);
JS_FreeValue(js, code_val);
if (!code_data)
return JS_ThrowTypeError(js, "shader.code must be an ArrayBuffer");
// stage
JSValue stage_val = JS_GetPropertyStr(js, obj, "stage");
const char *stage_str = JS_ToCString(js, stage_val);
if (stage_str) {
if (!strcmp(stage_str, "vertex")) info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
else if (!strcmp(stage_str, "fragment")) info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
JS_FreeCString(js, stage_str);
} // TODO: Make this its own function
JS_FreeValue(js, stage_val);
// num_samplers
JS_GETPROP(js, info.num_samplers, obj, num_samplers, number)
JS_GETPROP(js, info.num_storage_textures, obj, num_textures, number)
JS_GETPROP(js, info.num_storage_buffers, obj, num_storage_buffers, number)
JS_GETPROP(js, info.num_uniform_buffers, obj, num_uniform_buffers, number)
JS_GETPROP(js, info.format, obj, format, SDL_GPUShaderFormat)
JSValue entry_val = JS_GetPropertyStr(js,obj,"entrypoint");
info.entrypoint = JS_ToCString(js,entry_val);
JS_FreeValue(js,entry_val);
info.code_size = code_size;
info.code = code_data;
info.props = 0; // No extension properties by default
JS_FreeCString(js,info.entrypoint);
SDL_GPUShader *shader = SDL_CreateGPUShader(gpu, &info);
if (!shader)
return JS_ThrowReferenceError(js, "Unable to create shader: %s", SDL_GetError());
return SDL_GPUShader2js(js, shader);
}
JSC_CCALL(gpu_acquire_cmd_buffer,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
SDL_GPUCommandBuffer *cb = SDL_AcquireGPUCommandBuffer(gpu);
if (!cb) return JS_ThrowReferenceError(js,"Unable to acquire command buffer: %s", SDL_GetError());
// Wrap cb in a JS object
return SDL_GPUCommandBuffer2js(js, cb);
)
/* takes argv
0: a command buffer to write to
1: a buffer or array of buffers to upload
2: an optional transfer buffer to use; if undefined a temporary one is used
*/
JSC_CCALL(gpu_upload,
JSValue js_cmd = argv[0];
JSValue js_buffers = argv[1];
JSValue js_transfer = argv[2];
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
if (!gpu)
return JS_ThrowTypeError(js, "Invalid GPU device");
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, js_cmd);
if (!cmds)
return JS_ThrowTypeError(js, "Invalid command buffer");
if (!JS_IsArray(js, js_buffers))
return JS_ThrowTypeError(js, "buffers must be an array");
size_t len = js_arrlen(js, js_buffers);
if (len == 0)
return JS_DupValue(js, js_transfer); // No data to upload, just return existing transfer buffer
struct {
SDL_GPUBuffer *gpu_buffer;
void *data;
size_t size;
} *items = malloc(sizeof(*items) * len);
if (!items)
return JS_ThrowOutOfMemory(js);
size_t total_size_needed = 0;
for (size_t i = 0; i < len; i++) {
JSValue js_buf = JS_GetPropertyUint32(js, js_buffers, i);
if (JS_IsUndefined(js_buf))
continue;
gpu_buffer_unpack(js, gpu, js_buf, &items[i].size, &items[i].data, &items[i].gpu_buffer);
total_size_needed += items[i].size;
JS_FreeValue(js, js_buf);
}
SDL_GPUTransferBuffer *transfer = js2SDL_GPUTransferBuffer(js,js_transfer);
if (transfer) {
// ensure it's large enough
size_t transfer_size = js_getnum_str(js,js_transfer, "size");
if (transfer_size < total_size_needed) {
total_size_needed *= 1.5;
transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = total_size_needed
}
);
ret = SDL_GPUTransferBuffer2js(js,transfer);
JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed));
} else
ret = JS_DupValue(js,js_transfer); // supplied transfer buffer is fine so we use it
} else {
transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = total_size_needed
}
);
ret = SDL_GPUTransferBuffer2js(js,transfer);
JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed));
}
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(cmds);
if (!copy_pass) {
free(items);
return JS_ThrowReferenceError(js, "Failed to begin copy pass");
}
void *mapped_data = SDL_MapGPUTransferBuffer(gpu, transfer, false);
if (!mapped_data) {
SDL_EndGPUCopyPass(copy_pass);
free(items);
return JS_ThrowReferenceError(js, "Failed to map transfer buffer: %s", SDL_GetError());
}
// Copy all data into the mapped transfer buffer
size_t current_offset = 0;
for (size_t i = 0; i < len; i++) {
memcpy((char*)mapped_data + current_offset, items[i].data, items[i].size);
current_offset += items[i].size;
}
SDL_UnmapGPUTransferBuffer(gpu, transfer);
// Issue uploads for each item
current_offset = 0;
for (size_t i = 0; i < len; i++) {
SDL_UploadToGPUBuffer(
copy_pass,
&(SDL_GPUTransferBufferLocation){
.transfer_buffer = transfer,
.offset = current_offset
},
&(SDL_GPUBufferRegion){
.buffer = items[i].gpu_buffer,
.offset = 0,
.size = items[i].size
},
false
);
current_offset += items[i].size;
}
SDL_EndGPUCopyPass(copy_pass);
free(items);
)
JSC_CCALL(gpu_wait_for_fences,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
int n = js_arrlen(js,argv[0]);
SDL_GPUFence *fences[n];
for (int i = 0; i < n; i++) {
JSValue a = JS_GetPropertyUint32(js,argv[0],i);
fences[i] = js2SDL_GPUFence(js,a);
JS_FreeValue(js,a);
}
int wait_all = JS_ToBool(js,argv[1]);
return JS_NewBool(js,SDL_WaitForGPUFences(gpu,wait_all,fences,n));
)
JSC_CCALL(gpu_query_fence,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
SDL_GPUFence *fence = js2SDL_GPUFence(js,argv[0]);
return JS_NewBool(js,SDL_QueryGPUFence(gpu,fence));
)
JSC_CCALL(gpu_shader_format,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
SDL_GPUShaderFormat fmt = SDL_GetGPUShaderFormats(gpu);
if (!fmt) return JS_ThrowReferenceError(js, "Shader format available invalid.");
JSValue arr = JS_NewArray(js);
int i = 0;
if (fmt & SDL_GPU_SHADERFORMAT_PRIVATE) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, ".private"));
if (fmt & SDL_GPU_SHADERFORMAT_SPIRV) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "spv"));
if (fmt & SDL_GPU_SHADERFORMAT_DXBC) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxbc"));
if (fmt & SDL_GPU_SHADERFORMAT_DXIL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxil"));
if (fmt & SDL_GPU_SHADERFORMAT_MSL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "msl"));
if (fmt & SDL_GPU_SHADERFORMAT_METALLIB) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "metallib"));
return arr;
)
JSC_CCALL(gpu_compute_pipeline,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
SDL_GPUComputePipelineCreateInfo info = {0};
JSValue pipe = argv[0];
JS_GETPROP(js, info.num_samplers, pipe, num_samplers, number)
JS_GETPROP(js,info.num_readonly_storage_textures,pipe,num_readonly_storage_textures,number)
JS_GETPROP(js,info.num_readonly_storage_buffers,pipe,num_readonly_storage_buffers,number)
JS_GETPROP(js,info.num_readwrite_storage_textures,pipe,num_readwrite_storage_textures,number)
JS_GETPROP(js,info.num_readwrite_storage_buffers,pipe,num_readwrite_storage_buffers,number)
JS_GETPROP(js,info.threadcount_x,pipe,threadcount_x,number)
JS_GETPROP(js,info.threadcount_y,pipe,threadcount_y,number)
JS_GETPROP(js,info.threadcount_z,pipe,threadcount_z,number)
JS_GETPROP(js,info.entrypoint,pipe,entrypoint,cstring)
JSValue shader = JS_GetPropertyStr(js,pipe,"shader");
info.code = JS_GetArrayBuffer(js,&info.code_size, shader);
JS_FreeValue(js,shader);
SDL_GPUComputePipeline *pipeline = SDL_CreateGPUComputePipeline(gpu, &info);
JS_FreeCString(js,info.entrypoint);
if (!pipeline) return JS_ThrowReferenceError(js,"Could not create compute pipeline: %s", SDL_GetError());
return SDL_GPUComputePipeline2js(js,pipeline);
)
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]);
// Full texture in UV coords
rect src = {
.x = 0, .y = 0,
.w = 1, .h = 1
};
// 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)
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 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);
// top-left corner (single quad)
curr_src = src;
curr_src.x = src.x;
curr_src.y = src.y + src.h - src_slice.t;
curr_src.w = src_slice.l;
curr_src.h = src_slice.t;
curr_dst = dst;
curr_dst.x = dst.x;
curr_dst.y = dst.y + dst.h - dst_slice.t;
curr_dst.w = dst_slice.l;
curr_dst.h = dst_slice.t;
add_quad(&verts, &curr_src, &curr_dst);
// bottom-right corner (single quad)
curr_src = src;
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);
// top-right corner (single quad)
curr_src = src;
curr_src.x = src.x + src.w - src_slice.r;
curr_src.y = src.y + src.h - src_slice.t;
curr_src.w = src_slice.r;
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;
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);
ret = mesh;
)
JSC_CCALL(gpu_tile,
HMM_Vec2 size;
JSValue jstex = argv[0];
JS_GETATOM(js,size.x,jstex,width,number)
JS_GETATOM(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
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;
tile_region(&verts, src_pixels, dst, size.x, size.y,tilex,tiley);
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
MIST_FUNC_DEF(gpu,compute_pipeline,1),
MIST_FUNC_DEF(gpu, set_swapchain, 2),
MIST_FUNC_DEF(gpu,sort_sprite,2),
MIST_FUNC_DEF(gpu, make_sampler,1),
MIST_FUNC_DEF(gpu, load_texture, 2),
MIST_FUNC_DEF(gpu, texture, 1),
MIST_FUNC_DEF(gpu, make_quad, 0),
MIST_FUNC_DEF(gpu, driver, 0),
MIST_FUNC_DEF(gpu, make_shader, 1),
MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0),
MIST_FUNC_DEF(gpu, upload, 3),
MIST_FUNC_DEF(gpu, wait_for_fences, 2),
MIST_FUNC_DEF(gpu, query_fence, 1),
MIST_FUNC_DEF(gpu, shader_format, 0),
};
JSC_CCALL(renderpass_bind_pipeline,
SDL_GPURenderPass *r = js2SDL_GPURenderPass(js,self);
SDL_GPUGraphicsPipeline *pipe = js2SDL_GPUGraphicsPipeline(js,argv[0]);
SDL_BindGPUGraphicsPipeline(r,pipe);
)
JSC_CCALL(renderpass_draw_indexed,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
SDL_DrawGPUIndexedPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4]));
)
JSC_CCALL(renderpass_draw,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
SDL_DrawGPUPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]));
)
JSC_CCALL(renderpass_bind_buffers,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
int first = js2number(js,argv[0]);
JSValue buffers = argv[1];
int len = js_arrlen(js,buffers);
SDL_GPUBufferBinding bindings[len];
for (int i = 0; i < len; i++) {
JSValue buffer = JS_GetPropertyUint32(js,buffers,i);
bindings[i].offset = 0;
gpu_buffer_unpack(js,global_gpu, buffer, NULL, NULL,&bindings[i].buffer);
JS_FreeValue(js,buffer);
}
SDL_BindGPUVertexBuffers(
pass,
first,
bindings,
len);
)
JSC_CCALL(renderpass_bind_index_buffer,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
SDL_GPUBufferBinding bind;
bind.offset = 0;
gpu_buffer_unpack(js,global_gpu,argv[0], NULL, NULL, &bind.buffer);
int elen;
JSValue b = argv[0];
JS_GETPROP(js, elen, b, elen, number)
SDL_BindGPUIndexBuffer(pass,&bind,elen == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT);
)
JSC_CCALL(renderpass_end,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
SDL_EndGPURenderPass(pass);
)
JSC_CCALL(renderpass_bind_samplers,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
int first_slot = js2number(js,argv[1]);
JSValue arr = argv[2];
int num = js_arrlen(js,arr);
SDL_GPUTextureSamplerBinding binds[num];
for (int i = 0; i < num; i++) {
JSValue val = JS_GetPropertyUint32(js,arr,i);
JSValue tex = JS_GetPropertyStr(js,val,"texture");
JSValue smp = JS_GetPropertyStr(js,val,"sampler");
binds[i].texture = js2SDL_GPUTexture(js,tex);
binds[i].sampler = js2SDL_GPUSampler(js,smp);
JS_FreeValue(js,tex);
JS_FreeValue(js,smp);
JS_FreeValue(js,val);
}
int vertex = JS_ToBool(js,argv[0]);
if (vertex)
SDL_BindGPUVertexSamplers(pass, first_slot, binds, num);
else
SDL_BindGPUFragmentSamplers(pass, first_slot, binds, num);
)
JSC_CCALL(renderpass_bind_storage_buffers,
)
JSC_CCALL(renderpass_bind_storage_textures,
)
JSC_CCALL(renderpass_viewport,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
rect r = js2rect(js,argv[0]);
SDL_GPUViewport vp ={0};
vp.x = r.x;
vp.y = r.y;
vp.w = r.w;
vp.h = r.h;
vp.min_depth = 0;
vp.max_depth = 1;
SDL_SetGPUViewport(pass,&vp);
)
JSC_CCALL(renderpass_scissor,
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
rect r = js2rect(js,argv[0]);
SDL_Rect rr;
rr.x = r.x;
rr.y = r.y;
rr.w = r.w;
rr.h = r.h;
SDL_SetGPUScissor(pass,&rr);
)
static const JSCFunctionListEntry js_SDL_GPURenderPass_funcs[] = {
MIST_FUNC_DEF(renderpass, bind_pipeline, 1),
MIST_FUNC_DEF(renderpass, viewport, 1),
MIST_FUNC_DEF(renderpass, scissor, 1),
MIST_FUNC_DEF(renderpass, draw, 4),
MIST_FUNC_DEF(renderpass, draw_indexed, 5),
MIST_FUNC_DEF(renderpass, end, 0),
MIST_FUNC_DEF(renderpass, bind_index_buffer, 1),
MIST_FUNC_DEF(renderpass, bind_buffers, 2),
MIST_FUNC_DEF(renderpass, bind_samplers, 3),
MIST_FUNC_DEF(renderpass, bind_storage_buffers, 2),
MIST_FUNC_DEF(renderpass, bind_storage_textures, 2),
};
JSC_CCALL(cmd_render_pass,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
if (!JS_IsObject(argv[0])) return JS_ThrowTypeError(js, "render_pass: Expected a render pass descriptor object");
JSValue passObj = argv[0];
JSValue colorTargetsVal = JS_GetPropertyStr(js, passObj, "color_targets");
if (!JS_IsArray(js, colorTargetsVal))
return JS_ThrowTypeError(js, "render_pass: colorTargets must be an array");
uint32_t colorCount = js_arrlen(js, colorTargetsVal);
SDL_GPUColorTargetInfo colortars[colorCount];
SDL_GPUDepthStencilTargetInfo depthtar;
int has_depth = 0;
// Fill colorInfos from JS array
for (uint32_t i = 0; i < colorCount; i++) {
JSValue ctargetVal = JS_GetPropertyUint32(js, colorTargetsVal, i);
colortars[i] = js2SDL_GPUColorTargetInfo(js,ctargetVal);
JS_FreeValue(js, ctargetVal);
}
// Optional depth_stencil
JSValue depthval = JS_GetPropertyStr(js, passObj, "depth_stencil");
if (!JS_IsUndefined(depthval)) {
has_depth = 1;
JS_GETPROP(js, depthtar.texture, depthval, texture, SDL_GPUTexture)
JS_GETPROP(js, depthtar.load_op, depthval, load, SDL_GPULoadOp)
JS_GETPROP(js, depthtar.store_op, depthval, store, SDL_GPUStoreOp)
JS_GETPROP(js, depthtar.stencil_load_op, depthval, stencil_load, SDL_GPULoadOp)
JS_GETPROP(js, depthtar.stencil_store_op, depthval, stencil_store, SDL_GPUStoreOp)
JS_GETPROP(js,depthtar.clear_depth, depthval, clear, number)
JS_GETPROP(js,depthtar.clear_stencil,depthval,clear_stencil,number)
}
JS_FreeValue(js, depthval);
SDL_GPURenderPass *pass = SDL_BeginGPURenderPass(
cmds,
colortars,
colorCount,
has_depth ? &depthtar : NULL
);
JS_FreeValue(js, colorTargetsVal);
if (!pass) return JS_ThrowInternalError(js, "render_pass: Failed to begin render pass");
return SDL_GPURenderPass2js(js, pass);
)
JSC_CCALL(cmd_bind_vertex_buffer,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
int slot;
JS_ToInt32(js, &slot, argv[0]);
SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[1]);
size_t offset = 0;
if (argc > 2) JS_ToIndex(js, &offset, argv[2]);
SDL_BindGPUVertexBuffers(cmds, slot, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, 1);
)
JSC_CCALL(cmd_bind_index_buffer,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[0]);
size_t offset = 0;
if (argc > 1) JS_ToIndex(js, &offset, argv[1]);
SDL_BindGPUIndexBuffer(cmds, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, SDL_GPU_INDEXELEMENTSIZE_16BIT);
)
JSC_CCALL(cmd_bind_fragment_sampler,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
int slot;
JS_ToInt32(js, &slot, argv[0]);
SDL_GPUTexture *tex = js2SDL_GPUTexture(js, argv[1]);
SDL_GPUSampler *sampler = js2SDL_GPUSampler(js, argv[2]);
SDL_BindGPUFragmentSamplers(cmds, slot, &(SDL_GPUTextureSamplerBinding){.texture = tex, .sampler = sampler}, 1);
)
JSC_CCALL(cmd_push_vertex_uniform_data,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
int slot;
JS_ToInt32(js, &slot, argv[0]);
size_t buf_size;
void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]);
SDL_PushGPUVertexUniformData(cmds, slot, data, buf_size);
)
JSC_CCALL(cmd_push_fragment_uniform_data,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
int slot;
JS_ToInt32(js, &slot, argv[0]);
size_t buf_size;
void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]);
SDL_PushGPUFragmentUniformData(cmds, slot, data, buf_size);
)
JSC_CCALL(cmd_push_compute_uniform_data,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
int slot;
JS_ToInt32(js, &slot, argv[0]);
size_t buf_size;
void *data = JS_GetArrayBuffer(js, &buf_size, argv[1]);
SDL_PushGPUComputeUniformData(cmds, slot, data, buf_size);
)
JSC_CCALL(cmd_submit,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self);
SDL_GPUFence *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(cmds);
return SDL_GPUFence2js(js,fence);
)
JSC_CCALL(cmd_hud,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self);
HMM_Vec2 size = js2vec2(js,argv[0]);
HMM_Mat4 proj = HMM_Orthographic_RH_NO(0,size.x,0,size.y,-1,1);
shader_globals data = {0};
data.world_to_projection = proj;
data.projection_to_world = HMM_InvGeneralM4(proj);
data.viewport_min_z = -1,
data.viewport_max_z = 1;
data.render_size = size;
data.view_to_projection = proj;
data.viewport_size = (HMM_Vec2){1,1};
data.viewport_offset = (HMM_Vec2){0,0};
data.time = SDL_GetTicksNS() / 1000000000.0f;
SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data));
)
JSC_CCALL(cmd_camera,
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
shader_globals data = camera_globals(js, argv[0]);
SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data));
)
JSC_SCALL(cmd_push_debug_group,
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
SDL_PushGPUDebugGroup(cmd,str);
)
JSC_CCALL(cmd_pop_debug_group,
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
SDL_PopGPUDebugGroup(cmd);
)
JSC_SCALL(cmd_debug_label,
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
SDL_InsertGPUDebugLabel(cmd, str);
)
SDL_GPUBlitRegion js2SDL_GPUBlitRegion(JSContext *js, JSValue v)
{
SDL_GPUBlitRegion info = {0};
if (JS_GetClassID(v) == js_SDL_GPUTexture_id) {
// texture path
JSValue tex = v;
info.texture = js2SDL_GPUTexture(js,tex);
info.mip_level = 0,
info.layer_or_depth_plane = 0;
info.x = 0;
info.y = 0;
JS_GETPROP(js,info.w,tex,width,number)
JS_GETPROP(js,info.h,tex,height,number)
return info;
}
JSValue tex = JS_GetPropertyStr(js, v, "texture");
info.texture = js2SDL_GPUTexture(js,tex);
JS_GETPROP(js,info.mip_level,v,mip_level, number)
JS_GETPROP(js,info.mip_level,v,layer,number)
JS_GETPROP(js,info.x,v,x,number)
JS_GETPROP(js,info.y,v,y,number)
JS_GETPROP(js,info.w,v,width,number)
JS_GETPROP(js,info.h,v,height,number)
if (!info.w) JS_GETPROP(js,info.w,tex,width,number)
if (!info.h) JS_GETPROP(js,info.h,tex,height,number)
JS_FreeValue(js,tex);
return info;
}
JSC_CCALL(cmd_blit,
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
SDL_GPUBlitInfo info = {0};
JSValue v = argv[0];
JS_GETPROP(js,info.source,v,src,SDL_GPUBlitRegion)
JS_GETPROP(js,info.destination,v,dst,SDL_GPUBlitRegion)
JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp)
JS_GETPROP(js,info.flip_mode,v,flip,SDL_FlipMode)
JS_GETPROP(js,info.filter,v,filter,SDL_GPUFilter)
JS_GETPROP(js,info.clear_color,v,color,SDL_FColor)
SDL_BlitGPUTexture(cmd,&info);
)
JSC_CCALL(cmd_cancel,
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
SDL_CancelGPUCommandBuffer(cmd);
)
JSC_CCALL(cmd_compute_pass,
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
JSValue textures = argv[0];
JSValue buffers = argv[1];
int t_n = js_arrlen(js,textures);
SDL_GPUStorageTextureReadWriteBinding t_bind[t_n];
for (int i = 0; i < t_n; i++) {
JSValue T = JS_GetPropertyUint32(js,textures,i);
JS_GETPROP(js, t_bind[i].texture, T, texture, SDL_GPUTexture)
JS_GETPROP(js,t_bind[i].mip_level,T,mip, number)
JS_GETPROP(js,t_bind[i].layer,T,layer, number)
JS_FreeValue(js,T);
}
int b_n = js_arrlen(js,buffers);
SDL_GPUStorageBufferReadWriteBinding b_bind[b_n];
for (int i = 0; i < b_n; i++) {
JSValue T = JS_GetPropertyUint32(js,buffers,i);
JS_GETPROP(js,b_bind[i].buffer, T,buffer,SDL_GPUBuffer)
}
SDL_GPUComputePass *pass = SDL_BeginGPUComputePass(cmd,t_bind,t_n,b_bind,b_n);
if (!pass) return JS_ThrowReferenceError(js, "Unable to begin compute pass: %s", SDL_GetError());
return SDL_GPUComputePass2js(js,pass);
)
static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = {
MIST_FUNC_DEF(cmd, render_pass, 1),
MIST_FUNC_DEF(cmd, compute_pass, 2),
MIST_FUNC_DEF(cmd, swapchain_pass, 1),
MIST_FUNC_DEF(cmd, acquire_swapchain,0),
MIST_FUNC_DEF(cmd, bind_vertex_buffer, 2),
MIST_FUNC_DEF(cmd, bind_index_buffer, 1),
MIST_FUNC_DEF(cmd, bind_fragment_sampler, 3),
MIST_FUNC_DEF(cmd, push_vertex_uniform_data, 2),
MIST_FUNC_DEF(cmd, push_fragment_uniform_data, 2),
MIST_FUNC_DEF(cmd, push_compute_uniform_data, 2),
MIST_FUNC_DEF(cmd, submit, 0),
MIST_FUNC_DEF(cmd, cancel, 0),
MIST_FUNC_DEF(cmd, camera, 2),
MIST_FUNC_DEF(cmd, hud, 2),
MIST_FUNC_DEF(cmd, push_debug_group, 1),
MIST_FUNC_DEF(cmd, pop_debug_group, 0),
MIST_FUNC_DEF(cmd, debug_label, 1),
MIST_FUNC_DEF(cmd, blit, 1),
};
JSC_SCALL(buffer_name,
SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js,self);
SDL_GPUDevice *gpu;
JS_GETPROP(js, gpu, self, gpu, SDL_GPUDevice)
SDL_SetGPUBufferName(gpu,buffer,str);
)
static const JSCFunctionListEntry js_SDL_GPUBuffer_funcs[] = {
MIST_FUNC_DEF(buffer, name, 1),
};
JSC_CCALL(compute_dispatch,
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
SDL_DispatchGPUCompute(pass,js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]));
)
JSC_CCALL(compute_end,
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
SDL_EndGPUComputePass(pass);
)
JSC_CCALL(compute_pipeline,
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
SDL_GPUComputePipeline *pipeline = js2SDL_GPUComputePipeline(js,argv[0]);
SDL_BindGPUComputePipeline(pass,pipeline);
)
JSC_CCALL(compute_samplers,
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
JSValue samplers = argv[0];
int n = js_arrlen(js,samplers);
SDL_GPUTextureSamplerBinding b[n];
for (int i = 0; i < n; i++) {
JSValue s = JS_GetPropertyUint32(js,samplers,i);
b[i] = js2SDL_GPUTextureSamplerBinding(js,s);
JS_FreeValue(js,s);
}
SDL_BindGPUComputeSamplers(pass,js2number(js,argv[1]),b,n);
)
JSC_CCALL(compute_storage_textures,
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
JSValue textures = argv[0];
int n = js_arrlen(js,textures);
SDL_GPUTexture *b[n];
for (int i = 0; i < n; i++) {
JSValue s = JS_GetPropertyUint32(js,textures,i);
b[i] = js2SDL_GPUTexture(js,s);
JS_FreeValue(js,s);
}
SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n);
)
JSC_CCALL(compute_storage_buffers,
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
JSValue buffers = argv[0];
int n = js_arrlen(js,buffers);
SDL_GPUBuffer *b[n];
for (int i = 0; i < n; i++) {
JSValue s = JS_GetPropertyUint32(js,buffers,i);
b[i] = js2SDL_GPUBuffer(js,s);
JS_FreeValue(js,s);
}
SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n);
)
static const JSCFunctionListEntry js_SDL_GPUComputePass_funcs[] = {
MIST_FUNC_DEF(compute, dispatch, 3),
MIST_FUNC_DEF(compute, end, 0),
MIST_FUNC_DEF(compute, pipeline, 1),
MIST_FUNC_DEF(compute, samplers, 2),
MIST_FUNC_DEF(compute, storage_buffers, 2),
MIST_FUNC_DEF(compute, storage_textures, 2),
};
JSC_CCALL(surface_blit,
SDL_Surface *dst = js2SDL_Surface(js,self);
rect dstrect = js2rect(js,argv[0]);
SDL_Surface *src = js2SDL_Surface(js,argv[1]);
rect srcrect = js2rect(js,argv[2]);
SDL_BlitSurfaceScaled(src, &srcrect, dst, &dstrect, SDL_SCALEMODE_LINEAR);
)
JSC_CCALL(surface_scale,
SDL_Surface *src = js2SDL_Surface(js,self);
HMM_Vec2 wh = js2vec2(js,argv[0]);
SDL_Surface *new = SDL_CreateSurface(wh.x,wh.y, SDL_PIXELFORMAT_RGBA32);
SDL_BlitSurfaceScaled(src, NULL, new, NULL, SDL_SCALEMODE_LINEAR);
ret = SDL_Surface2js(js,new);
)
static SDL_PixelFormatDetails pdetails = {
.format = SDL_PIXELFORMAT_RGBA8888, // Standard RGBA8888 format
.bits_per_pixel = 32, // 8 bits per channel, 4 channels = 32 bits
.bytes_per_pixel = 4, // 4 bytes per pixel
.padding = {0, 0}, // Unused padding
.Rmask = 0xFF000000, // Red mask
.Gmask = 0x00FF0000, // Green mask
.Bmask = 0x0000FF00, // Blue mask
.Amask = 0x000000FF, // Alpha mask
.Rbits = 8, // 8 bits for Red
.Gbits = 8, // 8 bits for Green
.Bbits = 8, // 8 bits for Blue
.Abits = 8, // 8 bits for Alpha
.Rshift = 24, // Red shift
.Gshift = 16, // Green shift
.Bshift = 8, // Blue shift
.Ashift = 0 // Alpha shift
};
JSC_CCALL(surface_fill,
SDL_Surface *src = js2SDL_Surface(js,self);
colorf color = js2color(js,argv[0]);
rect r = {
.x = 0,
.y = 0,
.w = src->w,
.h = src->h
};
SDL_FillSurfaceRect(src, &r, SDL_MapRGBA(&pdetails, NULL, color.r*255,color.g*255,color.b*255,color.a*255));
)
JSC_CCALL(surface_rect,
SDL_Surface *dst = js2SDL_Surface(js,self);
rect r = js2rect(js,argv[0]);
colorf color = js2color(js,argv[1]);
SDL_FillSurfaceRect(dst,&r,SDL_MapRGBA(&pdetails,NULL, color.r*255,color.g*255,color.b*255,color.a*255));
)
JSC_CCALL(surface_dup,
SDL_Surface *surf = js2SDL_Surface(js,self);
SDL_Surface *conv = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA8888);
if (!conv)
return JS_ThrowReferenceError(js, "could not blit to dup'd surface: %s", SDL_GetError());
return SDL_Surface2js(js,conv);
)
static const JSCFunctionListEntry js_SDL_Surface_funcs[] = {
MIST_FUNC_DEF(surface, blit, 3),
MIST_FUNC_DEF(surface, scale, 1),
MIST_FUNC_DEF(surface,fill,1),
MIST_FUNC_DEF(surface,rect,2),
MIST_FUNC_DEF(surface, dup, 0),
};
JSC_CCALL(camera_frame,
SDL_ClearError();
SDL_Camera *cam = js2SDL_Camera(js,self);
if (!cam) return JS_ThrowReferenceError(js,"Self was not a camera: %s", SDL_GetError());
SDL_Surface *surf = SDL_AcquireCameraFrame(cam, NULL);
if (!surf) {
const char *msg = SDL_GetError();
if (msg[0] != 0)
return JS_ThrowReferenceError(js,"Could not get camera frame: %s", SDL_GetError());
else return JS_UNDEFINED;
}
return SDL_Surface2js(js,surf);
SDL_Surface *newsurf = SDL_CreateSurface(surf->w, surf->h, surf->format);
SDL_ReleaseCameraFrame(cam,surf);
int didit = SDL_BlitSurface(surf, NULL, newsurf, NULL);
if (!didit) {
SDL_DestroySurface(newsurf);
return JS_ThrowReferenceError(js, "Could not blit: %s", SDL_GetError());
}
return SDL_Surface2js(js,newsurf);
)
JSC_CCALL(camera_release_frame,
SDL_Camera *cam = js2SDL_Camera(js,self);
SDL_Surface *surf = js2SDL_Surface(js,argv[0]);
SDL_ReleaseCameraFrame(cam,surf);
)
static const JSCFunctionListEntry js_SDL_Camera_funcs[] =
{
MIST_FUNC_DEF(camera, frame, 0),
MIST_FUNC_DEF(camera, release_frame, 1),
};
JSC_CCALL(texture_mode,
SDL_Texture *tex = js2SDL_Texture(js,self);
SDL_SetTextureScaleMode(tex,js2number(js,argv[0]));
)
static const JSCFunctionListEntry js_SDL_Texture_funcs[] = {
MIST_FUNC_DEF(texture, mode, 1),
};
JSC_SCALL(texture_name,
SDL_GPUTexture *texture = js2SDL_GPUTexture(js,self);
SDL_GPUDevice *gpu;
JS_GETPROP(js,gpu,self,gpu,SDL_GPUDevice)
SDL_SetGPUTextureName(gpu,texture,str);
)
static const JSCFunctionListEntry js_SDL_GPUTexture_funcs[] = {
MIST_FUNC_DEF(texture, name, 1),
};
JSC_CCALL(input_mouse_lock, SDL_CaptureMouse(JS_ToBool(js,argv[0])))
JSC_CCALL(input_mouse_show,
if (JS_ToBool(js,argv[0]))
SDL_ShowCursor();
else
SDL_HideCursor();
)
JSC_CCALL(input_cursor_set,
SDL_Cursor *c = js2SDL_Cursor(js,argv[0]);
if (!SDL_SetCursor(c))
return JS_ThrowReferenceError(js, "could not set cursor: %s", SDL_GetError());
)
JSC_CCALL(input_keyname,
return JS_NewString(js, SDL_GetKeyName(js2number(js,argv[0])));
)
JSC_CCALL(input_keymod,
return js_keymod(js);
)
JSC_CCALL(input_mousestate,
float x,y;
SDL_MouseButtonFlags flags = SDL_GetMouseState(&x,&y);
JSValue m = JS_NewObject(js);
JS_SetProperty(js,m,x, number2js(js,x));
JS_SetProperty(js,m,y,number2js(js,y));
if (flags & SDL_BUTTON_LMASK)
JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_MMASK)
JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_RMASK)
JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X1MASK)
JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X2MASK)
JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1));
return m;
)
static const JSCFunctionListEntry js_input_funcs[] = {
MIST_FUNC_DEF(input, mouse_show, 1),
MIST_FUNC_DEF(input, mouse_lock, 1),
MIST_FUNC_DEF(input, cursor_set, 1),
MIST_FUNC_DEF(input, keyname, 1),
MIST_FUNC_DEF(input, keymod, 0),
MIST_FUNC_DEF(input, mousestate, 0),
};
JSC_CCALL(os_guid,
SDL_GUID guid;
randombytes(guid.data, 16);
char guid_str[33];
SDL_GUIDToString(guid, guid_str, 33);
return JS_NewString(js,guid_str);
)
JSC_SCALL(os_openurl,
if (!SDL_OpenURL(str))
ret = JS_ThrowReferenceError(js, "unable to open url %s: %s\n", str, SDL_GetError());
)
JSC_SCALL(console_print,
printf("%s", str);
)
static const JSCFunctionListEntry js_console_funcs[] = {
MIST_FUNC_DEF(console,print,1),
};
JSC_CCALL(debug_stack_depth, return number2js(js,js_debugger_stack_depth(js)))
JSC_CCALL(debug_build_backtrace, return js_debugger_build_backtrace(js,NULL))
JSC_CCALL(debug_closure_vars, return js_debugger_closure_variables(js,argv[0]))
JSC_CCALL(debug_local_vars, return js_debugger_local_variables(js, js2number(js,argv[0])))
JSC_CCALL(debug_fn_info, return js_debugger_fn_info(js, argv[0]))
JSC_CCALL(debug_backtrace_fns, return js_debugger_backtrace_fns(js,NULL))
JSC_CCALL(debug_dump_obj, return js_dump_value(js, argv[0]))
static const JSCFunctionListEntry js_debug_funcs[] = {
MIST_FUNC_DEF(debug, stack_depth, 0),
MIST_FUNC_DEF(debug, build_backtrace, 0),
MIST_FUNC_DEF(debug, closure_vars, 1),
MIST_FUNC_DEF(debug, local_vars, 1),
MIST_FUNC_DEF(debug, fn_info, 1),
MIST_FUNC_DEF(debug, backtrace_fns,0),
MIST_FUNC_DEF(debug, dump_obj, 1),
};
JSC_SCALL(io_rm,
if (!PHYSFS_delete(str)) ret = JS_ThrowReferenceError(js,"could not remove %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_mkdir,
if (!PHYSFS_mkdir(str)) ret = JS_ThrowReferenceError(js,"could not make directory %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_exists, ret = JS_NewBool(js,PHYSFS_exists(str)); )
JSC_SCALL(io_stat,
PHYSFS_Stat stat;
if (!PHYSFS_stat(str, &stat))
return JS_ThrowReferenceError(js, "%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
ret = JS_NewObject(js);
JS_SetPropertyStr(js,ret,"filesize", number2js(js,stat.filesize));
JS_SetPropertyStr(js,ret,"modtime", number2js(js,stat.modtime));
JS_SetPropertyStr(js,ret,"createtime", number2js(js,stat.createtime));
JS_SetPropertyStr(js,ret,"accesstime", number2js(js,stat.accesstime));
)
JSC_SCALL(io_slurpbytes,
PHYSFS_File *f = PHYSFS_openRead(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
PHYSFS_Stat stat;
PHYSFS_stat(str,&stat);
void *data = malloc(stat.filesize);
PHYSFS_readBytes(f,data,stat.filesize);
PHYSFS_close(f);
ret = JS_NewArrayBufferCopy(js,data,stat.filesize);
END:
)
JSC_SCALL(io_slurp,
PHYSFS_File *f = PHYSFS_openRead(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
PHYSFS_Stat stat;
PHYSFS_stat(str,&stat);
void *data = malloc(stat.filesize);
PHYSFS_readBytes(f,data,stat.filesize);
PHYSFS_close(f);
ret = JS_NewStringLen(js,data, stat.filesize);
free(data);
END:
)
size_t js_physfs_write(JSContext *js, PHYSFS_File *f, JSValue val)
{
size_t len;
size_t wrote;
if (JS_IsString(val)) {
const char *data = JS_ToCStringLen(js,&len,val);
wrote = PHYSFS_writeBytes(f,data,len);
JS_FreeCString(js,data);
} else {
unsigned char *data = JS_GetArrayBuffer(js,&len,val);
wrote = PHYSFS_writeBytes(f,data,len);
}
if (wrote < len) wrote = -1;
return wrote;
}
JSC_SCALL(io_slurpwrite,
PHYSFS_File *f = PHYSFS_openWrite(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"could not write to %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
size_t wrote = js_physfs_write(js,f,argv[1]);
PHYSFS_close(f);
if (wrote == -1)
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
END:
)
JSC_SSCALL(io_mount,
if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"Unable to mount %s at %s: %s", str, str2, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_unmount,
if (!PHYSFS_unmount(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_writepath,
if (!PHYSFS_setWriteDir(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
struct globdata {
JSContext *js;
JSValue arr;
char **globs;
char *glob;
int idx;
int recurse;
};
int globfs_cb(struct globdata *data, char *dir, char *file)
{
int needfree = 0;
char *path;
if (dir[0] == 0) path = file;
else {
path = malloc(strlen(dir) + strlen(file) + 2);
path[0] = 0;
strcat(path,dir);
strcat(path,"/");
strcat(path,file);
needfree = 1;
}
char **glob = data->globs;
while (*glob != NULL) {
if (wildmatch(*glob, path, WM_WILDSTAR) == WM_MATCH)
goto END;
glob++;
}
PHYSFS_Stat stat;
PHYSFS_stat(path, &stat);
if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
PHYSFS_enumerate(path, globfs_cb, data);
goto END;
}
JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js,path));
END:
if (needfree) free(path);
return 1;
}
JSC_SSCALL(io_match,
if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH)
ret = JS_NewBool(js,1);
else
ret = JS_NewBool(js,0);
)
JSC_CCALL(io_globfs,
ret = JS_NewArray(js);
struct globdata data;
data.js = js;
data.arr = ret;
data.idx = 0;
int globs_len = js_arrlen(js,argv[0]);
const char *globs[globs_len+1];
for (int i = 0; i < globs_len; i++) {
JSValue g = JS_GetPropertyUint32(js,argv[0],i);
globs[i] = JS_ToCString(js,g);
JS_FreeValue(js,g);
}
globs[globs_len] = NULL;
data.globs = globs;
const char *path = NULL;
if (!JS_IsUndefined(argv[1])) path = JS_ToCString(js,argv[1]);
PHYSFS_enumerate(path, globfs_cb, &data);
for (int i = 0; i < globs_len; i++)
JS_FreeCString(js,globs[i]);
ret = data.arr;
JS_FreeCString(js,path);
)
static int enumerate_cb(void *udata, const char *dir, const char *fname)
{
struct globdata *data = (struct globdata*)udata;
char *path;
int needfree = 0;
if (dir[0] == 0) path = (char*)fname;
else {
size_t dlen = strlen(dir);
int ends_slash = (dlen && dir[dlen - 1] == '/');
path = malloc(dlen + strlen(fname) + (ends_slash ? 1 : 2));
if (ends_slash) sprintf(path, "%s%s", dir, fname); else sprintf(path, "%s/%s", dir, fname);
needfree = 1;
}
PHYSFS_Stat st;
if (!PHYSFS_stat(path, &st)) {
if (needfree) free(path);
return 1;
}
/* If it's a directory and we're recursing, enumerate it further. */
if (st.filetype == PHYSFS_FILETYPE_DIRECTORY && data->recurse)
PHYSFS_enumerate(path, enumerate_cb, data);
/* Add this item (file or directory) to the JS array. */
JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js, path));
if (needfree) free(path);
return 1; /* continue enumerating */
}
JSC_SCALL(io_enumerate,
/* First argument: str (directory name) */
/* Second argument: boolean => recurse or not */
ret = JS_NewArray(js);
struct globdata data;
data.js = js;
data.arr = ret;
data.idx = 0;
data.glob = NULL; /* not used here */
data.globs = NULL; /* not used here */
data.recurse = 0;
if (!JS_IsUndefined(argv[1])) /* parse second arg if provided */
data.recurse = JS_ToBool(js, argv[1]);
/* Enumerate the directory given by 'str'. */
PHYSFS_enumerate(str, enumerate_cb, &data);
/* Return the JS array we filled. */
ret = data.arr;
)
JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir()))
JSC_SSCALL(io_prefdir, return JS_NewString(js,PHYSFS_getPrefDir(str, str2)))
JSC_SCALL(io_open,
PHYSFS_File *f = PHYSFS_openWrite(str);
if (!f)
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
else
ret = PHYSFS_File2js(js,f);
)
JSC_SCALL(io_realdir,
const char *real = PHYSFS_getRealDir(str);
if (!real)
ret = JS_UNDEFINED;
else
ret = JS_NewString(js,real);
)
JSC_CCALL(io_searchpath,
ret = JS_NewArray(js);
char **paths = PHYSFS_getSearchPath();
for (int i = 0; paths[i] != NULL; i++)
JS_SetPropertyUint32(js,ret,i,JS_NewString(js,paths[i]));
)
JSC_CCALL(io_mount_core,
int mount = JS_ToBool(js,argv[0]);
if (!mount)
PHYSFS_unmount("core.zip");
else
prosperon_mount_core();
)
JSC_SCALL(io_is_directory,
PHYSFS_Stat stat;
int good = PHYSFS_stat(str, &stat);
if (!good)
ret = JS_NewBool(js, 0);
else
ret = JS_NewBool(js, stat.filetype == PHYSFS_FILETYPE_DIRECTORY);
)
static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, rm, 1),
MIST_FUNC_DEF(io, mkdir, 1),
MIST_FUNC_DEF(io,stat,1),
MIST_FUNC_DEF(io, globfs, 2),
MIST_FUNC_DEF(io, match, 2),
MIST_FUNC_DEF(io, exists, 1),
MIST_FUNC_DEF(io, mount, 2),
MIST_FUNC_DEF(io,unmount,1),
MIST_FUNC_DEF(io,slurp,1),
MIST_FUNC_DEF(io,slurpbytes,1),
MIST_FUNC_DEF(io,slurpwrite,2),
MIST_FUNC_DEF(io,writepath, 1),
MIST_FUNC_DEF(io,basedir, 0),
MIST_FUNC_DEF(io, prefdir, 2),
MIST_FUNC_DEF(io, realdir, 1),
MIST_FUNC_DEF(io, open, 1),
MIST_FUNC_DEF(io, searchpath, 0),
MIST_FUNC_DEF(io, enumerate, 2),
MIST_FUNC_DEF(io, mount_core, 1),
MIST_FUNC_DEF(io, is_directory, 1),
};
JSC_CCALL(file_close,
PHYSFS_File *f = js2PHYSFS_File(js,self);
PHYSFS_close(f);
)
JSC_CCALL(file_write,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t wrote = js_physfs_write(js,f,argv[0]);
if (wrote == -1)
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_CCALL(file_buffer,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t size = js2number(js,argv[0]);
if (!PHYSFS_setBuffer(f,size))
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_CCALL(file_tell,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t tell = PHYSFS_tell(f);
if (tell == -1)
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return number2js(js,tell);
)
JSC_CCALL(file_eof,
PHYSFS_File *f = js2PHYSFS_File(js,self);
return JS_NewBool(js, PHYSFS_eof(f));
)
static const JSCFunctionListEntry js_PHYSFS_File_funcs[] = {
MIST_FUNC_DEF(file, close, 0),
MIST_FUNC_DEF(file, write, 1),
MIST_FUNC_DEF(file, buffer, 1),
MIST_FUNC_DEF(file, tell, 0),
MIST_FUNC_DEF(file, eof, 0),
};
JSC_GETSET_APPLY(transform, pos, vec3)
JSC_GETSET_APPLY(transform, scale, vec3f)
JSC_GETSET_APPLY(transform, rotation, quat)
JSC_CCALL(transform_move,
transform *t = js2transform(js,self);
transform_move(t, js2vec3(js,argv[0]));
)
JSC_CCALL(transform_lookat,
HMM_Vec3 point = js2vec3(js,argv[0]);
transform *go = js2transform(js,self);
HMM_Mat4 m = HMM_LookAt_RH(go->pos, point, vUP);
go->rotation = HMM_M4ToQ_RH(m);
transform_apply(go);
)
JSC_CCALL(transform_rotate,
HMM_Vec3 axis = js2vec3(js,argv[0]);
transform *t = js2transform(js,self);
HMM_Quat rot = HMM_QFromAxisAngle_RH(axis, js2angle(js,argv[1]));
t->rotation = HMM_MulQ(t->rotation,rot);
transform_apply(t);
)
JSC_CCALL(transform_angle,
HMM_Vec3 axis = js2vec3(js,argv[0]);
transform *t = js2transform(js,self);
if (axis.x) return angle2js(js,HMM_Q_Roll(t->rotation));
if (axis.y) return angle2js(js,HMM_Q_Pitch(t->rotation));
if (axis.z) return angle2js(js,HMM_Q_Yaw(t->rotation));
return angle2js(js,0);
)
JSC_CCALL(transform_direction,
transform *t = js2transform(js,self);
return vec32js(js, HMM_QVRot(js2vec3(js,argv[0]), t->rotation));
)
JSC_CCALL(transform_phys2d,
transform *t = js2transform(js,self);
HMM_Vec2 v = js2vec2(js,argv[0]);
float av = js2number(js,argv[1]);
float dt = js2number(js,argv[2]);
transform_move(t, (HMM_Vec3){v.x*dt,v.y*dt,0});
HMM_Quat rot = HMM_QFromAxisAngle_RH((HMM_Vec3){0,0,1}, av*dt);
t->rotation = HMM_MulQ(t->rotation, rot);
transform_apply(t);
)
JSC_CCALL(transform_unit,
transform *t = js2transform(js,self);
t->pos = v3zero;
t->rotation = QUAT1;
t->scale = v3one;
transform_apply(t);
)
JSC_CCALL(transform_trs,
transform *t = js2transform(js,self);
t->pos = JS_IsUndefined(argv[0]) ? v3zero : js2vec3(js,argv[0]);
t->rotation = JS_IsUndefined(argv[1]) ? QUAT1 : js2quat(js,argv[1]);
t->scale = JS_IsUndefined(argv[2]) ? v3one : js2vec3(js,argv[2]);
transform_apply(t);
)
JSC_CCALL(transform_rect,
transform *t = js2transform(js,self);
rect r = js2rect(js,argv[0]);
t->pos = (HMM_Vec3){r.x,r.y,0};
t->scale = (HMM_Vec3){r.w,r.h,1};
t->rotation = QUAT1;
transform_apply(t);
)
JSC_CCALL(transform_array,
transform *t = js2transform(js,self);
HMM_Mat4 m= transform2mat(t);
ret = JS_NewArray(js);
for (int i = 0; i < 16; i++)
JS_SetPropertyUint32(js,ret,i, number2js(js,m.em[i]));
)
static JSValue js_transform_get_change_hook(JSContext *js, JSValueConst self)
{
transform *t = js2transform(js,self);
return JS_DupValue(js,t->change_hook);
}
static JSValue js_transform_set_change_hook(JSContext *js, JSValueConst self, JSValue v)
{
transform *t = js2transform(js,self);
if (!JS_IsUndefined(v) && !JS_IsFunction(js,v)) return JS_ThrowReferenceError(js, "Hook must be a function.");
JS_FreeValue(js,t->change_hook);
t->change_hook = JS_DupValue(js,v);
return JS_UNDEFINED;
}
static JSValue js_transform_get_parent(JSContext *js, JSValueConst self)
{
transform *t = js2transform(js,self);
if (t->parent) return JS_DupValue(js,t->jsparent);
return JS_UNDEFINED;
}
static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v)
{
transform *p = js2transform(js,v);
if (!JS_IsUndefined(v) && !p)
return JS_ThrowReferenceError(js,"Parent must be another transform.");
transform *t = js2transform(js,self);
if (t == p)
return JS_ThrowReferenceError(js, "A transform cannot be its own parent.");
if (t->parent) {
transform *cur_parent = t->parent;
JS_FreeValue(js,t->jsparent);
t->jsparent = JS_UNDEFINED;
for (int i = 0; i < arrlen(cur_parent->children); i++) {
if (cur_parent->children[i] == t) {
arrdelswap(cur_parent->children,i);
break;
}
}
for (int i = 0; i < arrlen(cur_parent->jschildren); i++) {
if (JS_SameValue(js,cur_parent->jschildren[i],self)) {
JS_FreeValue(js,cur_parent->jschildren[i]);
arrdelswap(cur_parent->jschildren,i);
break;
}
}
}
t->parent = p;
t->jsparent = JS_DupValue(js,v);
if (p) {
arrput(p->children, t);
JSValue child = JS_DupValue(js,self);
arrput(p->jschildren,child);
}
transform_apply(t);
return JS_UNDEFINED;
}
JSC_CCALL(transform_torect,
transform *t = js2transform(js,self);
return rect2js(js,transform2rect(t));
)
JSC_CCALL(transform_children,
transform *t = js2transform(js,self);
ret = JS_NewArray(js);
for (int i = 0; i < arrlen(t->jschildren); i++)
JS_SetPropertyUint32(js,ret,i,JS_DupValue(js,t->jschildren[i]));
)
static const JSCFunctionListEntry js_transform_funcs[] = {
CGETSET_ADD(transform, pos),
CGETSET_ADD(transform, scale),
CGETSET_ADD(transform, rotation),
CGETSET_ADD(transform, parent),
CGETSET_ADD(transform, change_hook),
MIST_FUNC_DEF(transform, trs, 3),
MIST_FUNC_DEF(transform, phys2d, 3),
MIST_FUNC_DEF(transform, move, 1),
MIST_FUNC_DEF(transform, rotate, 2),
MIST_FUNC_DEF(transform, angle, 1),
MIST_FUNC_DEF(transform, lookat, 1),
MIST_FUNC_DEF(transform, direction, 1),
MIST_FUNC_DEF(transform, unit, 0),
MIST_FUNC_DEF(transform, rect, 1),
MIST_FUNC_DEF(transform, array, 0),
MIST_FUNC_DEF(transform, torect, 0),
MIST_FUNC_DEF(transform, children, 0),
};
JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); )
JSC_CCALL(datastream_seek, ds_seek(js2datastream(js,self), js2number(js,argv[0])))
JSC_CCALL(datastream_advance, ds_advance(js2datastream(js,self), js2number(js,argv[0])))
JSC_CCALL(datastream_duration, return number2js(js,ds_length(js2datastream(js,self))))
JSC_CCALL(datastream_framerate, return number2js(js,plm_get_framerate(js2datastream(js,self)->plm)))
JSC_GETSET_CALLBACK(datastream, callback)
static const JSCFunctionListEntry js_datastream_funcs[] = {
MIST_FUNC_DEF(datastream, time, 0),
MIST_FUNC_DEF(datastream, seek, 1),
MIST_FUNC_DEF(datastream, advance, 1),
MIST_FUNC_DEF(datastream, duration, 0),
MIST_FUNC_DEF(datastream, framerate, 0),
CGETSET_ADD(datastream, callback),
};
JSC_GETSET(font, linegap, number)
JSC_GET(font, height, number)
JSC_GET(font, ascent, number)
JSC_GET(font, descent, number)
JSC_SCALL(font_text_size,
font *f = js2font(js,self);
float size = js2number(js,argv[0]);
if (!size) size = f->height;
float letterSpacing = js2number(js,argv[1]);
float wrap = js2number(js,argv[2]);
ret = vec22js(js,measure_text(str, f, size, letterSpacing, wrap));
)
static const JSCFunctionListEntry js_font_funcs[] = {
CGETSET_ADD(font, linegap),
MIST_GET(font, height),
MIST_GET(font, ascent),
MIST_GET(font, descent),
MIST_FUNC_DEF(font, text_size, 3),
};
JSC_CCALL(geometry_rect_intersection,
rect a = js2rect(js,argv[0]);
rect b = js2rect(js,argv[1]);
rect c;
SDL_GetRectIntersectionFloat(&a, &b, &c);
return rect2js(js,c);
)
JSC_CCALL(geometry_rect_intersects,
rect a = js2rect(js,argv[0]);
rect b = js2rect(js,argv[1]);
return JS_NewBool(js, SDL_HasRectIntersectionFloat(&a,&b));
)
JSC_CCALL(geometry_rect_inside,
rect inner = js2rect(js,argv[0]);
rect outer = js2rect(js,argv[1]);
return JS_NewBool(js,
inner.x >= outer.x &&
inner.x + inner.w <= outer.x + outer.w &&
inner.y >= outer.y &&
inner.y + inner.h <= outer.y + outer.h
);
)
JSC_CCALL(geometry_rect_random,
rect a = js2rect(js,argv[0]);
return vec22js(js,(HMM_Vec2){
a.x + rand_range(js,-0.5,0.5)*a.w,
a.y + rand_range(js,-0.5,0.5)*a.h
});
)
JSC_CCALL(geometry_rect_point_inside,
rect a = js2rect(js,argv[0]);
HMM_Vec2 p = js2vec2(js,argv[1]);
return JS_NewBool(js,p.x >= a.x && p.x <= a.x+a.w && p.y <= a.y+a.h && p.y >= a.y);
)
JSC_CCALL(geometry_cwh2rect,
HMM_Vec2 c = js2vec2(js,argv[0]);
HMM_Vec2 wh = js2vec2(js,argv[1]);
rect r;
r.x = c.x;
r.y = c.y;
r.w = wh.x;
r.h = wh.y;
return rect2js(js,r);
)
JSC_CCALL(geometry_rect_pos,
rect r = js2rect(js,argv[0]);
return vec22js(js,(HMM_Vec2){
.x = r.x,
.y = r.y
});
)
JSC_CCALL(geometry_rect_move,
rect r = js2rect(js,argv[0]);
HMM_Vec2 move = js2vec2(js,argv[1]);
// cblas_saxpy(2, 1.0f, move.e, 1, &r, 1);
r.x += move.x;
r.y += move.y;
return rect2js(js,r);
)
/*static inline float fmin(float a, float b)
{
if (a < b) return a;
return b;
}
static inline float fmax(float a, float b)
{
if (a > b) return a;
return b;
}
*/
JSC_CCALL(geometry_rect_expand,
rect a = js2rect(js,argv[0]);
rect b = js2rect(js,argv[1]);
rect c = {0};
c.x = fmin(a.x,b.x);
c.y = fmin(a.y,b.y);
c.w = fmax(a.x+a.w,b.x+b.w);
c.h = fmax(a.y+a.h,b.y+b.h);
return rect2js(js,c);
)
static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_intersection, 2),
MIST_FUNC_DEF(geometry, rect_intersects, 2),
MIST_FUNC_DEF(geometry, rect_expand, 2),
MIST_FUNC_DEF(geometry, rect_inside, 2),
MIST_FUNC_DEF(geometry, rect_random, 1),
MIST_FUNC_DEF(geometry, cwh2rect, 2),
MIST_FUNC_DEF(geometry, rect_point_inside, 2),
MIST_FUNC_DEF(geometry, rect_pos, 1),
MIST_FUNC_DEF(geometry, rect_move, 2),
MIST_FUNC_DEF(gpu, tile, 4),
MIST_FUNC_DEF(gpu, slice9, 3),
};
JSC_SCALL(os_env,
char *env = getenv(str);
if (env) ret = JS_NewString(js,env);
)
JSValue js_os_sys(JSContext *js, JSValue self, int argc, JSValue *argv)
{
return JS_NewString(js, SDL_GetPlatform());
}
JSC_CCALL(os_exit, exit(js2number(js,argv[0]));)
JSC_CCALL(os_gc, JS_RunGC(JS_GetRuntime(js)) )
JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0))
JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0])))
JSC_CCALL(os_gc_threshold, JS_SetGCThreshold(JS_GetRuntime(js), js2number(js,argv[0])))
JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0])))
JSC_CCALL(os_rt_info, return JS_GetRTInfo(JS_GetRuntime(js),js))
JSC_CCALL(os_dump_atoms,
return js_dump_atoms(js);
)
JSC_CCALL(os_dump_shapes,
return js_dump_shapes(js);
)
JSC_CCALL(os_calc_mem,
return number2js(js,JS_MyValueSize(JS_GetRuntime(js), argv[0]));
)
#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \
JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\
#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number)
JSC_CCALL(os_memstate,
JSMemoryUsage jsmem;
JS_FillMemoryState(JS_GetRuntime(js), &jsmem);
ret = JS_NewObject(js);
JSJMEMRET(malloc_size)
JSJMEMRET(malloc_limit)
JSJMEMRET(memory_used_size)
JSJMEMRET(gc_threshold)
JSJMEMRET(malloc_count)
JSJMEMRET(memory_used_count)
JSJMEMRET(atom_count)
JSJMEMRET(atom_size)
JSJMEMRET(str_count)
JSJMEMRET(str_size)
JSJMEMRET(obj_count)
JSJMEMRET(obj_size)
JSJMEMRET(prop_count)
JSJMEMRET(prop_size)
JSJMEMRET(shape_count)
JSJMEMRET(shape_size)
JSJMEMRET(js_func_count)
JSJMEMRET(js_func_size)
JSJMEMRET(js_func_code_size)
JSJMEMRET(js_func_pc2line_count)
JSJMEMRET(js_func_pc2line_size)
JSJMEMRET(c_func_count)
JSJMEMRET(array_count)
JSJMEMRET(fast_array_count)
JSJMEMRET(fast_array_elements)
JSJMEMRET(binary_object_count)
JSJMEMRET(binary_object_size)
)
JSC_CCALL(os_mallinfo,
ret = JS_UNDEFINED;
/*struct mallinfo jsmem = mallinfo();
ret = JS_NewObject(js);
JSJMEMRET(arena);
JSJMEMRET(ordblks);
JSJMEMRET(smblks);
JSJMEMRET(hblks);
JSJMEMRET(hblkhd);
JSJMEMRET(usmblks);
JSJMEMRET(uordblks);
JSJMEMRET(fordblks);
JSJMEMRET(keepcost);*/
)
JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
JSValue ret = JS_NewObject(js);
#ifndef _WIN32
struct rusage jsmem;
getrusage(RUSAGE_SELF, &jsmem);
JSJMEMRET(ru_maxrss);
JSJMEMRET(ru_ixrss);
JSJMEMRET(ru_idrss);
JSJMEMRET(ru_isrss);
JSJMEMRET(ru_minflt);
JSJMEMRET(ru_majflt);
JSJMEMRET(ru_nswap);
JSJMEMRET(ru_inblock);
JSJMEMRET(ru_oublock);
JSJMEMRET(ru_msgsnd);
JSJMEMRET(ru_msgrcv);
JSJMEMRET(ru_nsignals);
JSJMEMRET(ru_nvcsw);
JSJMEMRET(ru_nivcsw);
#endif
return ret;
}
JSC_CCALL(os_mem, return js_get_memory_usage(js))
JSC_CCALL(os_value_id,
return number2js(js,(intptr_t)JS_VALUE_GET_PTR(self));
)
JSC_SSCALL(os_eval,
ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_STRICT);
)
// input: (encoded image data of jpg, png, bmp, tiff)
JSC_CCALL(os_make_texture,
size_t len;
void *raw = JS_GetArrayBuffer(js, &len, argv[0]);
if (!raw) return JS_ThrowReferenceError(js, "could not load texture with array buffer");
int n, width, height;
void *data = stbi_load_from_memory(raw, len, &width, &height, &n, 0);
if (data == NULL)
return JS_ThrowReferenceError(js, "no known image type from pixel data");
int FMT = 0;
if (n == 4)
FMT = SDL_PIXELFORMAT_RGBA32;
else if (n == 3)
FMT = SDL_PIXELFORMAT_RGB24;
else {
free(data);
return JS_ThrowReferenceError(js, "unknown pixel format. got %d channels", n);
}
SDL_Surface *surf = SDL_CreateSurfaceFrom(width,height,FMT, data, width*n);
if (!surf) {
free(data);
return JS_ThrowReferenceError(js, "Error creating surface from data: %s",SDL_GetError());
}
ret = SDL_Surface2js(js,surf);
)
// input: (gif image data)
JSC_CCALL(os_make_gif,
size_t rawlen;
void *raw = JS_GetArrayBuffer(js, &rawlen, argv[0]);
if (!raw) return JS_ThrowReferenceError(js, "could not load gif from supplied array buffer");
int n;
int frames;
int *delays;
int width;
int height;
void *pixels = stbi_load_gif_from_memory(raw, rawlen, &delays, &width, &height, &frames, &n, 4);
JSValue gif = JS_NewObject(js);
ret = gif;
if (frames == 1) {
// still image, so return just that
JS_SetPropertyStr(js, gif, "surface", SDL_Surface2js(js,SDL_CreateSurfaceFrom(width,height,SDL_PIXELFORMAT_RGBA32, pixels, width*4)));
return gif;
}
JSValue delay_arr = JS_NewArray(js);
for (int i = 0; i < frames; i++) {
JSValue frame = JS_NewObject(js);
JS_SetPropertyStr(js, frame, "time", number2js(js,(float)delays[i]/1000.0));
void *frame_pixels = malloc(width*height*4);
if (!frame_pixels) {
JS_FreeValue(js,gif);
ret = JS_ThrowOutOfMemory(js);
goto CLEANUP;
}
memcpy(frame_pixels, (unsigned char*)pixels+(width*height*4*i), width*height*4);
SDL_Surface *framesurf = SDL_CreateSurfaceFrom(width,height,SDL_PIXELFORMAT_RGBA32,frame_pixels, width*4);
if (!framesurf) {
ret = JS_ThrowReferenceError(js, "failed to create SDL_Surface: %s", SDL_GetError());
goto CLEANUP;
}
JS_SetPropertyStr(js, frame, "surface", SDL_Surface2js(js,framesurf));
JS_SetPropertyUint32(js, delay_arr, i, frame);
}
JS_SetPropertyStr(js, gif, "frames", delay_arr);
CLEANUP:
free(delays);
free(pixels);
)
JSValue aseframe2js(JSContext *js, ase_frame_t aframe)
{
JSValue frame = JS_NewObject(js);
void *frame_pixels = malloc(aframe.ase->w*aframe.ase->h*4);
memcpy(frame_pixels, aframe.pixels, aframe.ase->w*aframe.ase->h*4);
SDL_Surface *surf = SDL_CreateSurfaceFrom(aframe.ase->w, aframe.ase->h, SDL_PIXELFORMAT_RGBA32, frame_pixels, aframe.ase->w*4);
JS_SetPropertyStr(js, frame, "surface", SDL_Surface2js(js,surf));
JS_SetPropertyStr(js, frame, "rect", rect2js(js,(rect){.x=0,.y=0,.w=1,.h=1}));
JS_SetPropertyStr(js, frame, "time", number2js(js,(float)aframe.duration_milliseconds/1000.0));
return frame;
}
// input: (aseprite data)
JSC_CCALL(os_make_aseprite,
size_t rawlen;
void *raw = JS_GetArrayBuffer(js,&rawlen,argv[0]);
ase_t *ase = cute_aseprite_load_from_memory(raw, rawlen, NULL);
if (!ase)
return JS_ThrowReferenceError(js, "could not load aseprite from supplied array buffer: %s", aseprite_GetError());
if (ase->tag_count == 0) {
// we're dealing with a single frame image, or single animation
if (ase->frame_count == 1) {
JSValue obj = aseframe2js(js,ase->frames[0]);
cute_aseprite_free(ase);
return obj;
}
}
JSValue obj = JS_NewObject(js);
for (int t = 0; t < ase->tag_count; t++) {
ase_tag_t tag = ase->tags[t];
JSValue anim = JS_NewObject(js);
JS_SetPropertyStr(js, anim, "repeat", number2js(js,tag.repeat));
switch(tag.loop_animation_direction) {
case ASE_ANIMATION_DIRECTION_FORWARDS:
JS_SetPropertyStr(js, anim, "loop", JS_NewString(js,"forward"));
break;
case ASE_ANIMATION_DIRECTION_BACKWORDS:
JS_SetPropertyStr(js, anim, "loop", JS_NewString(js,"backward"));
break;
case ASE_ANIMATION_DIRECTION_PINGPONG:
JS_SetPropertyStr(js, anim, "loop", JS_NewString(js,"pingpong"));
break;
}
int _frame = 0;
JSValue frames = JS_NewArray(js);
for (int f = tag.from_frame; f <= tag.to_frame; f++) {
JSValue frame = aseframe2js(js,ase->frames[f]);
JS_SetPropertyUint32(js, frames, _frame, frame);
_frame++;
}
JS_SetPropertyStr(js, anim, "frames", frames);
JS_SetPropertyStr(js, obj, tag.name, anim);
}
ret = obj;
cute_aseprite_free(ase);
)
JSC_CCALL(os_make_surface,
HMM_Vec2 wh = js2vec2(js,argv[0]);
SDL_Surface *surface = SDL_CreateSurface(wh.x, wh.y, SDL_PIXELFORMAT_RGBA32);
ret = SDL_Surface2js(js, surface);
)
JSC_CCALL(os_make_cursor,
if (SDL_GetCurrentThreadID() != main_thread)
return JS_ThrowInternalError(js, "This can only be called from the root actor.");
SDL_Surface *s = js2SDL_Surface(js,argv[0]);
HMM_Vec2 hot = js2vec2(js,argv[1]);
SDL_Cursor *c = SDL_CreateColorCursor(s, hot.x, hot.y);
if (!c) return JS_ThrowReferenceError(js,"couldn't make cursor: %s", SDL_GetError());
return SDL_Cursor2js(js,c);
)
JSC_CCALL(os_make_font,
size_t len;
void *data = JS_GetArrayBuffer(js,&len,argv[0]);
if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data");
font *f = MakeFont(data, len, js2number(js,argv[1]));
if (!f) return JS_ThrowReferenceError(js, "could not create font");
ret = font2js(js,f);
JS_SetPropertyStr(js, ret, "surface", SDL_Surface2js(js,f->surface));
)
JSC_CCALL(os_make_transform,
transform *t = make_transform();
ret = transform2js(js,t);
// t->self = JS_DupValue(js,ret);
t->self = ret;
)
JSC_CCALL(os_make_sprite, return sprite2js(js,make_sprite()))
JSC_SCALL(os_system, ret = number2js(js,system(str)); )
JSValue make_color_buffer(JSContext *js, colorf c, int verts)
{
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
for (int i = 0; i < verts; i++)
colordata[i] = c;
return make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, JS_TYPED_ARRAY_FLOAT32, 4, 0, 0);
}
JSC_CCALL(os_make_line_prim,
JSValue prim = JS_NewObject(js);
HMM_Vec2 *v = js2cpvec2arr(js,argv[0]);
parsl_context *par_ctx = parsl_create_context((parsl_config){
.thickness = js2number(js,argv[1]),
.flags= PARSL_FLAG_ANNOTATIONS,
.u_mode = js2number(js,argv[2])
});
uint16_t spine_lens[] = {arrlen(v)};
parsl_mesh *m = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){
.num_vertices = arrlen(v),
.num_spines = 1,
.vertices = v,
.spine_lengths = spine_lens,
.closed = JS_ToBool(js,argv[3])
});
JS_SetPropertyStr(js, prim, "pos", make_gpu_buffer(js,m->positions,sizeof(*m->positions)*m->num_vertices, JS_TYPED_ARRAY_FLOAT32, 2,1,0));
JS_SetPropertyStr(js, prim, "indices", make_gpu_buffer(js,m->triangle_indices,sizeof(*m->triangle_indices)*m->num_triangles*3, JS_TYPED_ARRAY_UINT32, 1,1,1));
float uv[m->num_vertices*2];
for (int i = 0; i < m->num_vertices; i++) {
uv[i*2] = m->annotations[i].u_along_curve;
uv[i*2+1] = m->annotations[i].v_across_curve;
}
JS_SetPropertyStr(js, prim, "uv", make_gpu_buffer(js, uv, sizeof(uv), JS_TYPED_ARRAY_FLOAT32,2,1,0));
JS_SetPropertyStr(js,prim,"vertices", number2js(js,m->num_vertices));
JS_SetPropertyStr(js,prim,"color",make_color_buffer(js,js2color(js,argv[4]), m->num_vertices));
JS_SetPropertyStr(js,prim,"num_indices", number2js(js,m->num_triangles*3));
JS_SetPropertyStr(js,prim,"first_index", number2js(js,0));
parsl_destroy_context(par_ctx);
return prim;
)
static void render_frame(plm_t *mpeg, plm_frame_t *frame, datastream *ds) {
if (JS_IsUndefined(ds->callback)) return;
uint8_t *rgb = malloc(frame->height*frame->width*4);
memset(rgb,255,frame->height*frame->width*4);
plm_frame_to_rgba(frame, rgb, frame->width*4);
SDL_Surface *surf = SDL_CreateSurfaceFrom(frame->width,frame->height, SDL_PIXELFORMAT_RGBA32, rgb, frame->width*4);
JSValue s[1];
s[0] = SDL_Surface2js(ds->js,surf);
JSValue cb = JS_DupValue(ds->js,ds->callback);
JSValue ret = JS_Call(ds->js, cb, JS_UNDEFINED, 1, s);
JS_FreeValue(ds->js,cb);
free(rgb);
uncaught_exception(ds->js,ret);
}
JSC_CCALL(os_make_video,
size_t len;
void *data = JS_GetArrayBuffer(js,&len,argv[0]);
datastream *ds = ds_openvideo(data, len);
if (!ds) return JS_ThrowReferenceError(js, "Video file was not valid.");
ds->js = js;
ds->callback = JS_UNDEFINED;
plm_set_video_decode_callback(ds->plm, render_frame, ds);
return datastream2js(js,ds);
)
JSC_CCALL(os_rectpack,
int width = js2number(js,argv[0]);
int height = js2number(js,argv[1]);
int num = js_arrlen(js,argv[2]);
stbrp_context ctx[1];
stbrp_rect rects[num];
for (int i = 0; i < num; i++) {
HMM_Vec2 wh = js2vec2(js,js_getpropertyuint32(js, argv[2], i));
rects[i].w = wh.x;
rects[i].h = wh.y;
rects[i].id = i;
}
stbrp_node nodes[width];
stbrp_init_target(ctx, width, height, nodes, width);
int packed = stbrp_pack_rects(ctx, rects, num);
if (!packed) {
return JS_UNDEFINED;
}
ret = JS_NewArray(js);
for (int i = 0; i < num; i++) {
HMM_Vec2 pos;
pos.x = rects[i].x;
pos.y = rects[i].y;
JS_SetPropertyUint32(js, ret, i, vec22js(js,pos));
}
)
JSC_SCALL(os_kill,
int sig = 0;
if (!strcmp(str, "SIGABRT")) sig = SIGABRT;
else if (!strcmp(str, "SIGFPE")) sig = SIGFPE;
else if (!strcmp(str, "SIGILL")) sig = SIGILL;
else if (!strcmp(str, "SIGINT")) sig = SIGINT;
else if (!strcmp(str, "SIGSEGV")) sig = SIGSEGV;
else if (!strcmp(str, "SIGTERM")) sig = SIGTERM;
if (!sig) return JS_ThrowReferenceError(js, "string %s is not a valid signal", str);
raise(sig);
)
JSC_CCALL(os_sleep,
double time = js2number(js,argv[0]);
time *= 1000000000.;
SDL_DelayNS(time);
)
JSC_CCALL(os_battery_pct,
int pct;
SDL_GetPowerInfo(NULL, &pct);
return number2js(js,pct);
)
JSC_CCALL(os_battery_voltage,
)
JSC_CCALL(os_battery_seconds,
int seconds;
SDL_GetPowerInfo(&seconds, NULL);
return number2js(js,seconds);
)
JSC_CCALL(os_insertion_sort,
JSValue arr = argv[0];
JSValue cmp = argv[1];
int len = js_arrlen(js, arr);
for (int i = 1; i < len; i++) {
JSValue key = JS_GetPropertyUint32(js, arr, i);
int j = i - 1;
while (j >= 0) {
JSValue arr_j = JS_GetPropertyUint32(js, arr, j);
JSValue ret = JS_Call(js, cmp, JS_UNDEFINED, 2, (JSValue[]){ arr_j, key });
if (JS_IsException(ret)) {
JS_FreeValue(js,arr_j);
JS_FreeValue(js,key);
return ret;
}
double c = js2number(js, ret);
JS_FreeValue(js, ret);
if (c > 0) {
JS_SetPropertyUint32(js, arr, j + 1, arr_j);
j--;
} else {
JS_FreeValue(js, arr_j);
break;
}
}
JS_SetPropertyUint32(js, arr, j + 1, key);
}
ret = JS_DupValue(js,arr);
)
JSC_CCALL(os_power_state,
SDL_PowerState state = SDL_GetPowerInfo(NULL, NULL);
switch(state) {
case SDL_POWERSTATE_ERROR: return JS_ThrowTypeError(js, "Error determining power status");
case SDL_POWERSTATE_UNKNOWN: return JS_UNDEFINED;
case SDL_POWERSTATE_ON_BATTERY: return JS_NewString(js, "on battery");
case SDL_POWERSTATE_NO_BATTERY: return JS_NewString(js, "no battery");
case SDL_POWERSTATE_CHARGING: return JS_NewString(js, "charging");
case SDL_POWERSTATE_CHARGED: return JS_NewString(js, "charged");
}
return JS_UNDEFINED;
)
JSC_CCALL(os_cull_sprites,
ret = JS_NewArray(js);
int n = 0;
JSValue sprites = argv[0];
shader_globals info = camera_globals(js,argv[1]);
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_arrlen(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);
}
)
JSC_CCALL(os_make_rtree,
struct rtree *tree = rtree_new();
if (!tree) return JS_ThrowOutOfMemory(js);
return rtree2js(js,tree);
)
JSC_CCALL(os_rects_to_sprites,
ret = JS_NewArray(js);
JSValue image = argv[0];
JSValue jstex = JS_GetProperty(js,image,texture);
double w, h = 0;
JS_GETATOM(js,w,jstex,width,number)
JS_GETATOM(js,h,jstex,height,number)
SDL_GPUTexture *tex = js2SDL_GPUTexture(js,jstex);
JS_FreeValue(js,jstex);
JSValue rects = argv[1];
rect uv;
JS_GETATOM(js,uv,image,rect,rect)
ret = JS_NewArray(js);
int n = js_arrlen(js,rects);
for (int i = 0; i < n; i++) {
JSValue sub = JS_GetPropertyUint32(js,rects,i);
sprite *s = make_sprite();
s->affine = js2rect(js,sub);
s->affine.w = w;
s->affine.h = h;
s->image = JS_DupValue(js,image);
s->tex = tex;
s->uv = uv;
JS_SetPropertyUint32(js,ret,i,sprite2js(js,s));
JS_FreeValue(js,sub);
}
)
JSC_CCALL(os_on,
prosperon_rt *rt = JS_GetContextOpaque(js);
JS_FreeValue(js, rt->on_exception);
rt->on_exception = JS_DupValue(js,argv[1]);
)
JSC_CCALL(os_clean_transforms,
clean_all(js);
)
JSC_CCALL(os_totalmem, return number2js(js, SDL_GetSystemRAM()))
JSC_CCALL(os_platform, return JS_NewString(js,SDL_GetPlatform()))
JSC_CCALL(os_hostname,
char buf[256];
if (gethostname(buf,sizeof(buf)) == 0) return JS_NewString(js,buf);
return JS_NewString(js,"");
)
JSValue js_os_freemem(JSContext *js, JSValue self, int argc, JSValue *argv) {
#ifdef _WIN32
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
return JS_NewInt64(js,(int64_t)statex.ullAvailPhys);
#elif defined(__linux__)
struct sysinfo info;
if (sysinfo(&info) == 0)
return JS_NewInt64(js,(int64_t)info.freeram * info.mem_unit);
return JS_NewInt64(js,0);
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
// A very rough fallback using the same sysconf approach
// (macOS or *BSD typically need specialized APIs to get free mem accurately)
// This is often only "unused" pages, ignoring caches, etc.
long pages = sysconf(_SC_AVPHYS_PAGES);
long page_size = sysconf(_SC_PAGE_SIZE);
if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
return JS_NewInt64(js,(int64_t)pages * (int64_t)page_size);
#else
// Fallback: unknown
return JS_NewInt64(js,0);
#endif
}
JSValue js_os_arch(JSContext *js, JSValue self, int argc, JSValue *argv) {
#if defined(__x86_64__) || defined(_M_X64)
return JS_NewString(js,"x64");
#elif defined(__aarch64__) || defined(_M_ARM64)
return JS_NewString(js,"arm64");
#elif defined(__arm__) || defined(_M_ARM)
return JS_NewString(js,"arm");
#elif defined(__i386__) || defined(_M_IX86)
return JS_NewString(js,"ia32");
#elif defined(__loongarch__) || defined(__loongarch32) || defined(__loongarch64)
return JS_NewString(js,"loong64");
#elif defined(__mips__) || defined(__mips) || defined(_M_MIPS)
// You might want to distinguish mips vs mipsel
return JS_NewString(js,"mips");
#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_M_PPC)
// You might want to distinguish ppc vs ppc64, big-endian vs little-endian
return JS_NewString(js,"ppc64");
#elif defined(__riscv) && __riscv_xlen == 64
return JS_NewString(js,"riscv64");
#elif defined(__s390x__)
return JS_NewString(js,"s390x");
#else
return JS_NewString(js,"unknown");
#endif
}
JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *argv) {
#ifdef _WIN32
typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
HMODULE h = GetModuleHandleA("ntdll.dll");
if (h) {
RtlGetVersionPtr fx = (RtlGetVersionPtr)GetProcAddress(h, "RtlGetVersion");
if (fx) {
RTL_OSVERSIONINFOW ver;
memset(&ver, 0, sizeof(ver));
ver.dwOSVersionInfoSize = sizeof(ver);
if (!fx(&ver)) {
char buf[128];
sprintf(buf, "%u.%u.%u",
(unsigned)ver.dwMajorVersion,
(unsigned)ver.dwMinorVersion,
(unsigned)ver.dwBuildNumber
);
return JS_NewString(js, buf);
}
}
}
OSVERSIONINFOW wver;
memset(&wver, 0, sizeof(wver));
wver.dwOSVersionInfoSize = sizeof(wver);
if (GetVersionExW(&wver)) {
char buf[128];
sprintf(buf, "%u.%u.%u",
(unsigned)wver.dwMajorVersion,
(unsigned)wver.dwMinorVersion,
(unsigned)wver.dwBuildNumber
);
return JS_NewString(js, buf);
}
return JS_NewString(js, "Windows_Unknown");
#else
struct utsname info;
if (!uname(&info)) return JS_NewString(js, info.release);
return JS_NewString(js, "");
#endif
return JS_UNDEFINED;
}
JSC_CCALL(os_createprocess,
int ac = JS_ArrayLength(js,argv[0]);
const char *args[ac+1];
for (int i = 0; i < ac; i++) {
JSValue astr = JS_GetPropertyUint32(js,argv[0],i);
args[i] = JS_ToCString(js,astr);
JS_FreeValue(js,astr);
}
args[ac] = NULL;
SDL_Process *actor = SDL_CreateProcess(args, 0);
for (int i = 0; i < ac; i++)
JS_FreeCString(js,args[i]);
if (!actor)
return JS_ThrowReferenceError(js, "Unable to create process: %s\n", SDL_GetError());
)
JSC_CCALL(os_createactor,
int margc = JS_ArrayLength(js, argv[0]);
char **margv = malloc(margc*sizeof(char*));
for (int i = 0; i < margc; i++) {
JSValue val = JS_GetPropertyUint32(js, argv[0], i);
char *cstr = JS_ToCString(js,val);
margv[i] = strdup(cstr);
JS_FreeCString(js,cstr);
JS_FreeValue(js,val);
}
create_actor(margc, margv);
)
#include "qjs_wota.h"
JSC_CCALL(os_mailbox_push,
if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message");
if (!JS_IsObject(argv[1])) return JS_ThrowInternalError(js, "Object to push must be an object.");
const char *id = JS_ToCString(js, argv[0]);
int exist = actor_exists(id);
JS_FreeCString(js,id);
if (!exist)
return JS_ThrowInternalError(js, "No mailbox found for given ID");
void *data = value2wota(js, argv[1], JS_UNDEFINED);
char *err = send_message(id, data);
if (err) {
free(data);
return JS_ThrowInternalError(js, "Could not send message: %s", err);
}
)
JSC_CCALL(os_register_actor,
prosperon_rt *rt = JS_GetContextOpaque(js);
char *id = JS_ToCString(js, argv[0]);
char *err = register_actor(id, rt, JS_ToBool(js, argv[2]));
if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err);
rt->message_handle = JS_DupValue(js, argv[1]);
rt->context = js;
JS_FreeCString(js, id);
)
JSC_CCALL(os_mailbox_exist,
const char *id = JS_ToCString(js, argv[0]);
int exist = actor_exists(id);
JS_FreeCString(js, id);
return JS_NewBool(js, exist);
)
JSC_CCALL(os_unneeded,
prosperon_rt *actor = JS_GetContextOpaque(js);
SDL_LockMutex(actor->msg_mutex);
JS_FreeValue(js, actor->unneeded);
if (!JS_IsFunction(js, argv[0])) {
actor->unneeded = JS_UNDEFINED;
goto END;
}
actor->unneeded = JS_DupValue(js, argv[0]);
JS_ToFloat64(js, &actor->unneeded_secs, argv[1]);
END:
if (actor->ar) {
SDL_RemoveTimer(actor->ar);
actor->ar = 0;
}
set_actor_state(actor);
SDL_UnlockMutex(actor->msg_mutex);
)
JSValue js_os_get_trace(JSContext *js, JSValue self)
{
return JS_NewBool(js, trace);
}
JSValue js_os_set_trace(JSContext *js, JSValue self, JSValue value)
{
trace = JS_ToBool(js, value);
return JS_UNDEFINED;
}
JSC_CCALL(os_destroy,
prosperon_rt *rt = JS_GetContextOpaque(js);
rt->need_stop = 1;
)
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_transform, 0),
MIST_FUNC_DEF(os, clean_transforms, 0),
MIST_FUNC_DEF(os, platform, 0),
MIST_FUNC_DEF(os, arch, 0),
MIST_FUNC_DEF(os, totalmem, 0),
MIST_FUNC_DEF(os, freemem, 0),
MIST_FUNC_DEF(os, hostname, 0),
MIST_FUNC_DEF(os, version, 0),
JS_CGETSET_DEF("trace", js_os_get_trace, js_os_set_trace),
MIST_FUNC_DEF(os, kill, 1),
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, now, 0),
MIST_FUNC_DEF(os, openurl, 1),
MIST_FUNC_DEF(os, sleep, 1),
MIST_FUNC_DEF(os, battery_pct, 0),
MIST_FUNC_DEF(os, battery_voltage, 0),
MIST_FUNC_DEF(os, battery_seconds, 0),
MIST_FUNC_DEF(os, power_state, 0),
MIST_FUNC_DEF(os, on, 2),
MIST_FUNC_DEF(os, rusage, 0),
MIST_FUNC_DEF(os, mallinfo, 0),
// dangerous ones that need disabled for shipping
MIST_FUNC_DEF(os, env, 1),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, createprocess, 0),
MIST_FUNC_DEF(os, createactor, 1),
MIST_FUNC_DEF(os, mailbox_push, 2),
MIST_FUNC_DEF(os, mailbox_exist, 1),
MIST_FUNC_DEF(actor, delay, 2),
MIST_FUNC_DEF(actor, removetimer, 1),
MIST_FUNC_DEF(os, register_actor, 2),
MIST_FUNC_DEF(os, unneeded, 2),
MIST_FUNC_DEF(os, destroy, 0),
MIST_FUNC_DEF(os, ioactor, 0),
};
JSC_CCALL(js_dump_class, return js_get_object_class_distribution(js))
JSC_CCALL(js_dump_type_overheads, return js_get_object_type_overheads(js))
JSC_CCALL(js_dump_objects, return js_dump_objects(js))
void cycle_hook_call(JSContext *js, JSValue v)
{
prosperon_rt *rt = JS_GetContextOpaque(js);
JS_FreeValue(js,JS_Call(js, rt->cycle_fn,JS_UNDEFINED,1,&v));
}
JSC_CCALL(js_cycle_hook,
if (JS_IsUndefined(argv[0]))
js_debug_sethook(js,NULL,JS_HOOK_CYCLE);
else {
prosperon_rt *rt = JS_GetContextOpaque(js);
JS_FreeValue(js,rt->cycle_fn);
rt->cycle_fn = JS_DupValue(js,argv[0]);
js_debug_sethook(js,cycle_hook_call, JS_HOOK_CYCLE);
}
)
JSC_CCALL(js_stack_info, return js_dump_stack_info(js))
static const JSCFunctionListEntry js_js_funcs[] = {
MIST_FUNC_DEF(js, cycle_hook,1),
MIST_FUNC_DEF(os, dump_shapes, 0),
MIST_FUNC_DEF(os, dump_atoms,0),
MIST_FUNC_DEF(js, dump_class, 0),
MIST_FUNC_DEF(js, dump_objects, 0),
MIST_FUNC_DEF(js, dump_type_overheads, 0),
MIST_FUNC_DEF(js, stack_info, 0),
MIST_FUNC_DEF(os, calc_mem, 1),
MIST_FUNC_DEF(os, mem, 1),
MIST_FUNC_DEF(os, mem_limit, 1),
MIST_FUNC_DEF(os, gc_threshold, 1),
MIST_FUNC_DEF(os, max_stacksize, 1),
MIST_FUNC_DEF(os, memstate, 0),
MIST_FUNC_DEF(os, gc, 0),
MIST_FUNC_DEF(os, eval, 2),
MIST_FUNC_DEF(os, rt_info, 0),
};
static const JSCFunctionListEntry js_util_funcs[] = {
MIST_FUNC_DEF(os, guid, 0),
MIST_FUNC_DEF(os, insertion_sort, 2),
};
static const JSCFunctionListEntry js_graphics_funcs[] = {
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),
MIST_FUNC_DEF(gpu, make_sprite_queue, 4),
MIST_FUNC_DEF(os, make_text_buffer, 6),
MIST_FUNC_DEF(os, rectpack, 3),
MIST_FUNC_DEF(os, make_rtree, 0),
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, rects_to_sprites,2),
MIST_FUNC_DEF(os, make_surface, 1),
MIST_FUNC_DEF(os, make_cursor, 1),
MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_sprite, 0),
MIST_FUNC_DEF(os, make_line_prim, 5),
};
static const JSCFunctionListEntry js_video_funcs[] = {
MIST_FUNC_DEF(os, make_video, 1),
};
JSC_SCALL(os_use_embed,
prosperon_rt *rt = JS_GetContextOpaque(js);
ModuleEntry *module_registry = rt->module_registry;
for (int i = 0; i < arrlen(module_registry); i++) {
if (strcmp(str,module_registry[i].name) == 0) {
ret = module_registry[i].fn(js);
break;
}
}
if (JS_IsUndefined(ret))
ret = JS_ThrowReferenceError(js,"Library %s could not be found embedded", str);
)
JSC_SCALL(os_use_dyn,
SDL_SharedObject *ptr = SDL_LoadObject(str);
if (!ptr)
return JS_ThrowReferenceError(js, "Shared library %s could not be loaded", SDL_GetError());
JSValue (*js_use)(JSContext*);
js_use = (JSValue (*)(JSContext*))SDL_LoadFunction(ptr, "use");
if (!js_use)
ret = JS_ThrowReferenceError(js, "Shared library %s has no use function", str);
else
ret = js_use(js);
SDL_UnloadObject(ptr);
)
JSC_CCALL(rtree_add,
rtree *tree = js2rtree(js,self);
JSValue v = argv[0];
rect r;
JS_GETATOM(js,r,v,rect,rect)
NUMTYPE min[3];
NUMTYPE max[3];
min[0] = r.x;
min[1] = r.y;
min[2] = 0;
max[0] = r.x+r.w;
max[1] = r.y+r.h;
max[2] = 0;
JSValue *ins = malloc(sizeof(*ins));
*ins = JS_DupValue(js,v);
if (!rtree_insert(tree, min, max, ins)) {
JS_FreeValue(js,*ins);
return JS_ThrowOutOfMemory(js);
}
)
int rtree_cmp(const JSValue *a, const JSValue *b, JSContext *js)
{
int same = JS_SameValue(js, *a, *b);
if (same)
JS_FreeValue(js,*a);
return !same;
}
JSC_CCALL(rtree_delete,
rtree *tree = js2rtree(js,self);
JSValue v = argv[0];
rect r;
JS_GETATOM(js,r,v,rect,rect)
NUMTYPE min[3];
NUMTYPE max[3];
min[0] = r.x;
min[1] = r.y;
min[2] = 0;
max[0] = r.x+r.w;
max[1] = r.y+r.h;
max[2] = 0;
if (!rtree_delete_with_comparator(tree, min, max, &v, rtree_cmp, js))
return JS_ThrowOutOfMemory(js);
)
struct rtree_iter_data {
JSContext *js;
JSValue arr;
int n;
};
bool rtree_iter(const NUMTYPE *min, const NUMTYPE *max, const JSValue *data, struct rtree_iter_data *ctx)
{
JS_SetPropertyUint32(ctx->js,ctx->arr,ctx->n, JS_DupValue(ctx->js,*data));
ctx->n++;
return 1;
}
JSC_CCALL(rtree_query,
rtree *tree = js2rtree(js,self);
rect r = js2rect(js,argv[0]);
NUMTYPE min[3];
NUMTYPE max[3];
min[0] = r.x;
min[1] = r.y;
min[2] = 0;
max[0] = r.x+r.w;
max[1] = r.y+r.h;
max[2] = 0;
struct rtree_iter_data data = {0};
data.js = js;
data.arr = JS_NewArray(js);
data.n = 0;
rtree_search(tree, min, max, rtree_iter, &data);
ret = data.arr;
)
struct rtree_each
{
JSValue fn;
JSContext *js;
};
int rtree_foreach(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, struct rtree_each *each)
{
JSValue ret = JS_Call(each->js, each->fn, JS_UNDEFINED, 0, NULL);
uncaught_exception(each->js, ret);
return 1;
}
JSC_CCALL(rtree_forEach,
rtree *tree = js2rtree(js,self);
struct rtree_each each;
each.fn = JS_DupValue(js,argv[0]);
each.js = js;
rtree_scan(tree, rtree_foreach, &each);
JS_FreeValue(js,each.fn);
)
typedef struct {
JSContext *js;
JSValue v;
int has;
} rtree_has;
int rtree_hasfn(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, rtree_has *has)
{
if (JS_SameValue(has->js, has->v, *value)) {
has->has = 1;
return 0;
}
return 1;
}
JSC_CCALL(rtree_has,
rtree *tree = js2rtree(js,self);
rtree_has has;
has.js = js;
has.v = JS_DupValue(js,argv[0]);
has.has = 0;
rtree_scan(tree, rtree_hasfn, &has);
JS_FreeValue(js,argv[0]);
return JS_NewBool(js,has.has);
)
JSValue js_rtree_get_size(JSContext *js, JSValue self, int magic)
{
rtree *tree = js2rtree(js,self);
return number2js(js,rtree_count(tree));
}
int rtree_valuefn(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, struct rtree_iter_data *data)
{
JS_SetPropertyUint32(data->js, data->arr, data->n, JS_DupValue(data->js, *value));
data->n++;
return 1;
}
JSC_CCALL(rtree_values,
rtree *tree = js2rtree(js,self);
struct rtree_iter_data data = {0};
data.js = js;
data.arr = JS_NewArray(js);
data.n = 0;
rtree_scan(tree, rtree_valuefn, &data);
ret = data.arr;
)
static const JSCFunctionListEntry js_rtree_funcs[] = {
MIST_FUNC_DEF(rtree, add, 1),
MIST_FUNC_DEF(rtree, delete, 1),
MIST_FUNC_DEF(rtree, query, 1),
JS_CGETSET_DEF("size", js_rtree_get_size,NULL),
MIST_FUNC_DEF(rtree, forEach, 1),
MIST_FUNC_DEF(rtree, has, 1),
MIST_FUNC_DEF(rtree,values,0),
};
JSC_GETSET(sprite, layer, number)
JSC_GETSET(sprite, color, color)
JSC_CCALL(sprite_set_affine,
sprite *sp = js2sprite(js,self);
transform *t = js2transform(js,argv[0]);
if (t)
sp->affine = transform2rect(t);
)
JSC_CCALL(sprite_set_rect,
sprite *sp = js2sprite(js,self);
sp->affine = js2rect(js,argv[0]);
)
JSC_CCALL(sprite_set_image,
sprite *sp = js2sprite(js,self);
if (!JS_IsUndefined(sp->image))
JS_FreeValue(js,sp->image);
sp->image = JS_DupValue(js,argv[0]);
if (JS_IsUndefined(sp->image)) return JS_UNDEFINED;
JSValue img = sp->image;
JS_GETATOM(js, sp->uv, img, rect, rect)
JS_GETATOM(js, sp->tex, img, texture, SDL_GPUTexture);
)
static const JSCFunctionListEntry js_sprite_funcs[] = {
MIST_FUNC_DEF(sprite, set_affine, 1),
MIST_FUNC_DEF(sprite, set_rect, 1),
MIST_FUNC_DEF(sprite, set_image, 1),
CGETSET_ADD(sprite, layer),
CGETSET_ADD(sprite, color),
};
#define JSSTATIC(NAME, PARENT) \
js_##NAME = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, js_##NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
JS_SetPrototype(js, js_##NAME, PARENT); \
JSValue js_layout_use(JSContext *js);
JSValue js_miniz_use(JSContext *js);
#ifdef TRACY_ENABLE
JSValue js_tracy_use(JSContext *js);
#endif
MISTUSE(io)
MISTUSE(os)
MISTUSE(input)
MISTUSE(math)
MISTUSE(spline)
MISTUSE(geometry)
MISTUSE(js)
MISTUSE(graphics)
MISTUSE(util)
MISTUSE(video)
MISTUSE(camera)
MISTUSE(debug)
#include "qjs_crypto.h"
#include "qjs_time.h"
#include "qjs_blob.h"
JSValue js_imgui_use(JSContext *js);
#define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use}
void ffi_load(JSContext *js)
{
prosperon_rt *rt = JS_GetContextOpaque(js);
m_seedRand(&rt->mrand, time(NULL));
arrput(rt->module_registry, MISTLINE(io));
arrput(rt->module_registry, MISTLINE(os));
arrput(rt->module_registry, MISTLINE(input));
arrput(rt->module_registry, MISTLINE(time));
arrput(rt->module_registry, MISTLINE(math));
arrput(rt->module_registry, MISTLINE(spline));
arrput(rt->module_registry, MISTLINE(geometry));
arrput(rt->module_registry, MISTLINE(graphics));
arrput(rt->module_registry, MISTLINE(js));
arrput(rt->module_registry, MISTLINE(util));
arrput(rt->module_registry, MISTLINE(video));
arrput(rt->module_registry, MISTLINE(soloud));
arrput(rt->module_registry, MISTLINE(layout));
arrput(rt->module_registry, MISTLINE(miniz));
arrput(rt->module_registry, MISTLINE(imgui));
arrput(rt->module_registry, MISTLINE(camera));
arrput(rt->module_registry, MISTLINE(debug));
arrput(rt->module_registry, MISTLINE(dmon));
arrput(rt->module_registry, MISTLINE(nota));
arrput(rt->module_registry, MISTLINE(enet));
arrput(rt->module_registry, MISTLINE(qr));
arrput(rt->module_registry, MISTLINE(wota));
arrput(rt->module_registry, MISTLINE(crypto));
arrput(rt->module_registry, MISTLINE(blob));
#ifdef TRACY_ENABLE
arrput(rt->module_registry, MISTLINE(tracy));
#endif
JSValue globalThis = JS_GetGlobalObject(js);
JSValue prosp = JS_NewObject(js);
JS_SetPropertyStr(js,globalThis,"prosperon", prosp);
JSValue c_types = JS_NewObject(js);
JS_SetPropertyStr(js,prosp, "c_types", c_types);
QJSCLASSPREP_FUNCS(rtree)
QJSCLASSPREP_FUNCS(SDL_Window)
QJSCLASSPREP_FUNCS(SDL_Surface)
QJSCLASSPREP_FUNCS(SDL_Texture)
QJSCLASSPREP_NO_FUNCS(SDL_Cursor)
QJSCLASSPREP_FUNCS(SDL_Camera)
QJSCLASSPREP_FUNCS(renderer_ctx)
QJSCLASSPREP_FUNCS(SDL_GPUDevice)
QJSCLASSPREP_FUNCS(SDL_GPUTexture)
QJSCLASSPREP_FUNCS(SDL_GPUCommandBuffer)
QJSCLASSPREP_FUNCS(SDL_GPURenderPass)
QJSCLASSPREP_FUNCS(SDL_GPUComputePass)
QJSCLASSPREP_NO_FUNCS(SDL_GPUCopyPass)
QJSCLASSPREP_NO_FUNCS(SDL_GPUFence)
QJSCLASSPREP_NO_FUNCS(SDL_GPUTransferBuffer)
QJSCLASSPREP_NO_FUNCS(SDL_GPUShader)
QJSCLASSPREP_NO_FUNCS(SDL_GPUSampler)
QJSCLASSPREP_NO_FUNCS(SDL_GPUGraphicsPipeline)
QJSCLASSPREP_NO_FUNCS(SDL_GPUComputePipeline)
QJSCLASSPREP_FUNCS(sprite)
QJSCLASSPREP_FUNCS(SDL_GPUBuffer)
QJSCLASSPREP_FUNCS(PHYSFS_File)
QJSCLASSPREP_FUNCS(transform);
QJSCLASSPREP_FUNCS(font);
QJSCLASSPREP_FUNCS(datastream);
JS_SetPropertyStr(js, globalThis, "use_dyn", JS_NewCFunction(js,js_os_use_dyn,"use_dyn", 1));
JS_SetPropertyStr(js, globalThis, "use_embed", JS_NewCFunction(js,js_os_use_embed,"use_embed", 1));
QJSGLOBALCLASS(os);
QJSGLOBALCLASS(console);
JSValue jsobject = JS_GetPropertyStr(js,globalThis, "Object");
JS_SetPropertyStr(js, jsobject, "id", JS_NewCFunction(js, js_os_value_id, "id", 1));
JS_FreeValue(js,jsobject);
JSValue jsarray = JS_GetPropertyStr(js,globalThis, "Array");
JSValue array_proto = JS_GetPropertyStr(js,jsarray, "prototype");
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, countof(js_array_funcs));
JS_FreeValue(js,jsarray);
JS_FreeValue(js,array_proto);
JSValue jsnumber = JS_GetPropertyStr(js,globalThis, "Number");
JSValue number_proto = JS_GetPropertyStr(js,jsnumber, "prototype");
JS_SetPropertyFunctionList(js, number_proto, js_number_funcs, countof(js_number_funcs));
JS_FreeValue(js,jsnumber);
JS_FreeValue(js,number_proto);
JSValue args = JS_NewArray(js);
for (int i = 0; i < rt->cmd.argc; i++)
JS_SetPropertyUint32(js,args, i, JS_NewString(js,rt->cmd.argv[i]));
JS_SetPropertyStr(js,prosp,"argv", args);
JS_SetPropertyStr(js,prosp, "version", JS_NewString(js,PROSPERON_VERSION));
JS_SetPropertyStr(js,prosp,"revision",JS_NewString(js,PROSPERON_COMMIT));
JS_SetPropertyStr(js,prosp,"engine_start", JS_NewCFunction(js,js_os_engine_start, "engine_start", 1));
JS_FreeValue(js,globalThis);
}