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-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
7120 lines
211 KiB
C
7120 lines
211 KiB
C
#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);
|
|
}
|
|
|
|
void SDL_Renderer_free(JSRuntime *rt, SDL_Renderer *r)
|
|
{
|
|
SDL_DestroyRenderer(r);
|
|
}
|
|
|
|
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(SDL_Renderer,)
|
|
|
|
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};
|
|
HMM_Vec2 size;
|
|
transform *transform;
|
|
double fov = 0;
|
|
double aspect = 0;
|
|
int ortho;
|
|
double near_z = 0;
|
|
double far_z = 0;
|
|
|
|
JS_GETPROP(js, size, camera, size, vec2)
|
|
JS_GETPROP(js, transform, camera, transform, transform)
|
|
JS_GETPROP(js, fov, camera, fov, number)
|
|
JS_GETPROP(js, aspect, camera, aspect, 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)
|
|
|
|
HMM_Mat4 proj;
|
|
HMM_Mat4 view;
|
|
|
|
if (ortho) {
|
|
proj = HMM_Orthographic_RH_NO(
|
|
-size.x*0.5, 0.5*size.x,
|
|
-size.y*0.5, 0.5*size.y,
|
|
-1.0f, 1.0f
|
|
);
|
|
view = HMM_Translate((HMM_Vec3){ -transform->pos.x, -transform->pos.y, 0.0f });
|
|
}
|
|
else {
|
|
proj = HMM_Perspective_RH_NO(fov, aspect,near_z,far_z);
|
|
|
|
HMM_Mat4 camera_transform = HMM_Translate(transform->pos);
|
|
camera_transform = HMM_MulM4(camera_transform, HMM_QToM4(transform->rotation));
|
|
// camera_transform = HMM_MulM4(camera_transform, HMM_Scale(transform->scale)); // don't bother w/ scale
|
|
view = HMM_InvGeneralM4(camera_transform);
|
|
}
|
|
|
|
// 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);
|
|
SDL_PropertiesID props = SDL_CreateProperties();
|
|
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0);
|
|
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, win);
|
|
SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, str);
|
|
SDL_Renderer *r = SDL_CreateRendererWithProperties(props);
|
|
SDL_DestroyProperties(props);
|
|
if (!r) return JS_ThrowReferenceError(js, "Error creating renderer: %s",SDL_GetError());
|
|
SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_BLEND);
|
|
return SDL_Renderer2js(js,r);
|
|
)
|
|
|
|
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),
|
|
};
|
|
|
|
JSC_CCALL(SDL_Renderer_clear,
|
|
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
|
|
SDL_RenderClear(renderer);
|
|
)
|
|
|
|
JSC_CCALL(SDL_Renderer_present,
|
|
SDL_Renderer *ren = js2SDL_Renderer(js,self);
|
|
SDL_RenderPresent(ren);
|
|
)
|
|
|
|
JSC_CCALL(SDL_Renderer_draw_color,
|
|
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
|
|
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 = js2SDL_Renderer(js,self);
|
|
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 = js2SDL_Renderer(js,self);
|
|
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,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
|
|
if (JS_IsArray(js,argv[0])) {
|
|
int len = js_arrlen(js,argv[0]);
|
|
HMM_Vec2 points[len];
|
|
assert(sizeof(HMM_Vec2) == sizeof(SDL_FPoint));
|
|
for (int i = 0; i < len; i++) {
|
|
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
|
points[i] = js2vec2(js,val);
|
|
JS_FreeValue(js,val);
|
|
}
|
|
SDL_RenderLines(r,points,len);
|
|
}
|
|
)
|
|
|
|
JSC_CCALL(renderer_point,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
|
|
if (JS_IsArray(js,argv[0])) {
|
|
int len = js_arrlen(js,argv[0]);
|
|
HMM_Vec2 points[len];
|
|
assert(sizeof(HMM_Vec2) ==sizeof(SDL_FPoint));
|
|
for (int i = 0; i < len; i++) {
|
|
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
|
points[i] = js2vec2(js,val);
|
|
JS_FreeValue(js,val);
|
|
}
|
|
SDL_RenderPoints(r, points, len);
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
HMM_Vec2 point = transform_point(r, js2vec2(js,argv[0]), &cam_mat);
|
|
SDL_RenderPoint(r,point.x,point.y);
|
|
)
|
|
|
|
// Function to translate a list of 2D points
|
|
void Translate2DPoints(HMM_Vec2 *points, int count, HMM_Vec3 position, HMM_Quat rotation, HMM_Vec3 scale) {
|
|
// Precompute the 2D rotation matrix from the quaternion
|
|
float xx = rotation.x * rotation.x;
|
|
float yy = rotation.y * rotation.y;
|
|
float zz = rotation.z * rotation.z;
|
|
float xy = rotation.x * rotation.y;
|
|
float zw = rotation.z * rotation.w;
|
|
|
|
// Extract 2D affine rotation and scaling
|
|
float m00 = (1.0f - 2.0f * (yy + zz)) * scale.x; // Row 1, Column 1
|
|
float m01 = (2.0f * (xy + zw)) * scale.y; // Row 1, Column 2
|
|
float m10 = (2.0f * (xy - zw)) * scale.x; // Row 2, Column 1
|
|
float m11 = (1.0f - 2.0f * (xx + zz)) * scale.y; // Row 2, Column 2
|
|
|
|
// Translation components (ignore the z position)
|
|
float tx = position.x;
|
|
float ty = position.y;
|
|
|
|
// Transform each point
|
|
for (int i = 0; i < count; ++i) {
|
|
HMM_Vec2 p = points[i];
|
|
points[i].x = m00 * p.x + m01 * p.y + tx;
|
|
points[i].y = m10 * p.x + m11 * p.y + ty;
|
|
}
|
|
}
|
|
|
|
// Should take a single struct with pos, color, uv, and indices arrays
|
|
JSC_CCALL(renderer_geometry,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
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 = js2SDL_Renderer(js,self);
|
|
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 = js2SDL_Renderer(js,self);
|
|
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 = js2SDL_Renderer(js,self);
|
|
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 = js2SDL_Renderer(js,self);
|
|
HMM_Vec2 v = js2vec2(js,argv[0]);
|
|
SDL_SetRenderScale(r, v.x, v.y);
|
|
)
|
|
|
|
JSC_CCALL(renderer_vsync,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
SDL_SetRenderVSync(r,js2number(js,argv[0]));
|
|
)
|
|
|
|
// This returns the coordinates inside the
|
|
JSC_CCALL(renderer_coords,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
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,
|
|
int centered = JS_ToBool(js,argv[1]);
|
|
SDL_Renderer *ren = js2SDL_Renderer(js,self);
|
|
SDL_Rect vp;
|
|
SDL_GetRenderViewport(ren, &vp);
|
|
HMM_Mat3 proj;
|
|
proj.Columns[0] = (HMM_Vec3){1,0,0};
|
|
proj.Columns[1] = (HMM_Vec3){0,-1,0};
|
|
if (centered)
|
|
proj.Columns[2] = (HMM_Vec3){vp.w/2.0,vp.h/2.0,1};
|
|
else
|
|
proj.Columns[2] = (HMM_Vec3){0,vp.h,1};
|
|
|
|
transform *tra = js2transform(js,argv[0]);
|
|
HMM_Mat3 view;
|
|
view.Columns[0] = (HMM_Vec3){1,0,0};
|
|
view.Columns[1] = (HMM_Vec3){0,1,0};
|
|
view.Columns[2] = (HMM_Vec3){-tra->pos.x, -tra->pos.y,1};
|
|
cam_mat = HMM_MulM3(proj,view);
|
|
)
|
|
|
|
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 = js2SDL_Renderer(js,self);
|
|
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));
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_Renderer_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, geometry, 2),
|
|
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(SDL_Renderer)
|
|
|
|
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);
|
|
}
|