7627 lines
236 KiB
C
7627 lines
236 KiB
C
#include "jsffi.h"
|
|
#include "script.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 "yugine.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 "timer.h"
|
|
#include <signal.h>
|
|
#include "cute_aseprite.h"
|
|
#include "cgltf.h"
|
|
#include "physfs.h"
|
|
|
|
#include "freelist.h"
|
|
|
|
#include "sprite.h"
|
|
|
|
#include "quadtree.h"
|
|
#include "Quadtree.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>
|
|
|
|
#ifdef __APPLE__
|
|
#include <Accelerate/Accelerate.h>
|
|
//#else
|
|
//#include <cblas.h>
|
|
#endif
|
|
|
|
#define RT_DEPTH SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT
|
|
|
|
static JSAtom width_atom;
|
|
static JSAtom height_atom;
|
|
static JSAtom x_atom;
|
|
static JSAtom y_atom;
|
|
static JSAtom anchor_x_atom;
|
|
static JSAtom anchor_y_atom;
|
|
static JSAtom pos_atom;
|
|
static JSAtom uv_atom;
|
|
static JSAtom color_atom;
|
|
static JSAtom indices_atom;
|
|
static JSAtom vertices_atom;
|
|
static JSAtom dst_atom;
|
|
static JSAtom src_atom;
|
|
static JSAtom count_atom;
|
|
static JSAtom num_indices_atom;
|
|
static JSAtom transform_atom;
|
|
static JSAtom image_atom;
|
|
static JSAtom layer_atom;
|
|
static JSAtom parent_atom;
|
|
|
|
// GPU ATOMS
|
|
static JSAtom cw_atom;
|
|
static JSAtom ccw_atom;
|
|
static JSAtom zero_atom;
|
|
static JSAtom one_atom;
|
|
static JSAtom add_atom;
|
|
static JSAtom sub_atom;
|
|
static JSAtom rev_sub_atom;
|
|
static JSAtom min_atom;
|
|
static JSAtom max_atom;
|
|
static JSAtom none_atom;
|
|
static JSAtom norm_atom;
|
|
static JSAtom front_atom;
|
|
static JSAtom back_atom;
|
|
static JSAtom never_atom;
|
|
static JSAtom less_atom;
|
|
static JSAtom equal_atom;
|
|
static JSAtom less_equal_atom;
|
|
static JSAtom greater_atom;
|
|
static JSAtom not_equal_atom;
|
|
static JSAtom greater_equal_atom;
|
|
static JSAtom always_atom;
|
|
static JSAtom keep_atom;
|
|
static JSAtom zero_stencil_atom;
|
|
static JSAtom replace_atom;
|
|
static JSAtom incr_clamp_atom;
|
|
static JSAtom decr_clamp_atom;
|
|
static JSAtom invert_atom;
|
|
static JSAtom incr_wrap_atom;
|
|
static JSAtom decr_wrap_atom;
|
|
static JSAtom point_atom;
|
|
static JSAtom line_atom;
|
|
static JSAtom linestrip_atom;
|
|
static JSAtom triangle_atom;
|
|
static JSAtom trianglestrip_atom;
|
|
static JSAtom src_color_atom;
|
|
static JSAtom one_minus_src_color_atom;
|
|
static JSAtom dst_color_atom;
|
|
static JSAtom one_minus_dst_color_atom;
|
|
static JSAtom src_alpha_atom;
|
|
static JSAtom one_minus_src_alpha_atom;
|
|
static JSAtom dst_alpha_atom;
|
|
static JSAtom one_minus_dst_alpha_atom;
|
|
static JSAtom constant_color_atom;
|
|
static JSAtom one_minus_constant_color_atom;
|
|
static JSAtom src_alpha_saturate_atom;
|
|
static JSAtom none_cull_atom;
|
|
static JSAtom front_cull_atom;
|
|
static JSAtom back_cull_atom;
|
|
|
|
// For sampler filtering:
|
|
static JSAtom nearest_atom;
|
|
static JSAtom linear_atom;
|
|
|
|
// For sampler mipmap modes:
|
|
static JSAtom mipmap_nearest_atom;
|
|
static JSAtom mipmap_linear_atom;
|
|
|
|
// For sampler address modes:
|
|
static JSAtom repeat_atom;
|
|
static JSAtom mirror_atom;
|
|
static JSAtom clamp_edge_atom;
|
|
static JSAtom clamp_border_atom;
|
|
|
|
static JSAtom vertex_atom;
|
|
static JSAtom index_atom;
|
|
static JSAtom indirect_atom;
|
|
static JSAtom rect_atom;
|
|
|
|
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;
|
|
}
|
|
|
|
double js_getnum(JSContext *js, JSValue v, JSAtom prop)
|
|
{
|
|
JSValue val = JS_GetProperty(js,v,prop);
|
|
double ret = js2number(js,val);
|
|
JS_FreeValue(js,val);
|
|
return ret;
|
|
}
|
|
|
|
#define JS_PULLPROPSTR(JS,VALUE,STR, TYPE) { \
|
|
JSValue __v = JS_GetPropertyStr(JS,VALUE,#STR); \
|
|
STR = js2##TYPE(JS, __v); \
|
|
JS_FreeValue(JS, __v); }
|
|
|
|
#define JS_GETPROPSTR(JS, VALUE, TARGET, STR, TYPE) {\
|
|
JSValue STR##__v = JS_GetPropertyStr(JS,VALUE,#STR); \
|
|
TARGET.STR = js2##TYPE(JS, STR##__v); \
|
|
JS_FreeValue(JS,STR##__v); }\
|
|
|
|
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
|
|
JSValue VALUE##__##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
|
TARGET = js2##TYPE(JS, VALUE##__##PROP##__v); \
|
|
JS_FreeValue(JS,VALUE##__##PROP##__v); }\
|
|
|
|
#define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\
|
|
JSValue VALUE##__##PROP##__v = JS_GetProperty(JS,VALUE,ATOM); \
|
|
TARGET = js2##TYPE(JS, VALUE##__##PROP##__v); \
|
|
JS_FreeValue(JS,VALUE##__##PROP##__v); }\
|
|
|
|
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_getpropertystr(JSContext *js, JSValue v, const char *str)
|
|
{
|
|
JSValue ret = JS_GetPropertyStr(js, v, str);
|
|
JS_FreeValue(js,ret);
|
|
return ret;
|
|
}
|
|
|
|
JSValue js_getproperty(JSContext *js, JSValue v, JSAtom atom)
|
|
{
|
|
JSValue ret = JS_GetProperty(js, v, atom);
|
|
JS_FreeValue(js,ret);
|
|
return ret;
|
|
}
|
|
|
|
SDL_Window *global_window;
|
|
|
|
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,
|
|
JSAtom 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
|
|
|
|
#include "stb_perlin.h"
|
|
|
|
#if (defined(_WIN32) || defined(__WIN32__))
|
|
#include <direct.h>
|
|
#define mkdir(x,y) _mkdir(x)
|
|
#endif
|
|
|
|
struct lrtb {
|
|
float l;
|
|
float r;
|
|
float t;
|
|
float b;
|
|
};
|
|
|
|
static JSContext *global_js;
|
|
|
|
static SDL_GPUDevice *global_gpu;
|
|
|
|
SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v)
|
|
{
|
|
}
|
|
|
|
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_ST2048
|
|
};
|
|
|
|
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_GETPROPSTR(js,v,ret,l,number)
|
|
JS_GETPROPSTR(js,v,ret,b,number)
|
|
JS_GETPROPSTR(js,v,ret,t,number)
|
|
JS_GETPROPSTR(js,v,ret,r,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;
|
|
}
|
|
|
|
void skin_free(JSRuntime *rt,skin *sk) {
|
|
arrfree(sk->invbind);
|
|
free(sk);
|
|
}
|
|
|
|
#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);
|
|
TracyCFreeN(s,"texture memory");
|
|
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)
|
|
{
|
|
}
|
|
|
|
void SDL_Thread_free(JSRuntime *rt, SDL_Thread *t)
|
|
{
|
|
}
|
|
|
|
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) \
|
|
|
|
QJSCLASS(transform)
|
|
QJSCLASS(font)
|
|
//QJSCLASS(warp_gravity)
|
|
//QJSCLASS(warp_damp)
|
|
QJSCLASS(datastream)
|
|
QJSCLASS(timer)
|
|
QJSCLASS(skin)
|
|
|
|
QJSCLASS(jssprite)
|
|
|
|
QJSCLASS(SDL_Window)
|
|
QJSCLASS(SDL_Renderer)
|
|
QJSCLASS(SDL_Camera)
|
|
|
|
void SDL_Texture_free(JSRuntime *rt, SDL_Texture *t){
|
|
TracyCFreeN(t, "vram");
|
|
SDL_DestroyTexture(t);
|
|
}
|
|
|
|
QJSCLASS(SDL_Texture,
|
|
TracyCAllocN(n, n->w*n->h*4, "vram");
|
|
JS_SetProperty(js, j, width_atom, number2js(js,n->w));
|
|
JS_SetProperty(js,j,height_atom,number2js(js,n->h));
|
|
)
|
|
|
|
QJSCLASS(SDL_Surface,
|
|
TracyCAllocN(n, n->pitch*n->h, "texture memory");
|
|
JS_SetProperty(js, j, width_atom, number2js(js,n->w));
|
|
JS_SetProperty(js,j,height_atom,number2js(js,n->h));
|
|
)
|
|
|
|
QJSCLASS(SDL_GPUDevice)
|
|
QJSCLASS(SDL_Thread)
|
|
|
|
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)
|
|
|
|
void qtree_free(JSRuntime *rt, qtree *tree)
|
|
{
|
|
qtree_destroy(*tree);
|
|
}
|
|
|
|
QJSCLASS(qtree)
|
|
|
|
void Quadtree_free(JSRuntime *rt, Quadtree *tree)
|
|
{
|
|
qt_destroy(tree);
|
|
}
|
|
|
|
QJSCLASS(Quadtree)
|
|
|
|
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;
|
|
}
|
|
static inline int js_transform_dirty_chain(JSContext *js, JSValue v)
|
|
{
|
|
transform *t = js2transform(js, v);
|
|
if (!t) return 0; // no transform => assume not dirty
|
|
|
|
if (t->dirty) return 1;
|
|
|
|
JSValue parentVal = JS_GetProperty(js, v, parent_atom);
|
|
if (JS_IsObject(parentVal)) {
|
|
int r = js_transform_dirty_chain(js, parentVal);
|
|
JS_FreeValue(js, parentVal);
|
|
return r;
|
|
}
|
|
|
|
JS_FreeValue(js, parentVal);
|
|
return 0;
|
|
}
|
|
|
|
static inline HMM_Mat3 js2transform_mat3(JSContext *js, JSValue v)
|
|
{
|
|
transform *T = js2transform(js,v);
|
|
if (!js_transform_dirty_chain(js,v)) return T->gcache3;
|
|
if (!T) return HMM_M3D(1);
|
|
transform *P;
|
|
JS_GETATOM(js,P,v,parent_atom,transform);
|
|
if (P) {
|
|
HMM_Mat3 pm = transform2mat3(P);
|
|
HMM_Mat3 tm = transform2mat3(T);
|
|
T->gcache3 = HMM_MulM3(pm,tm);
|
|
|
|
} else
|
|
T->gcache3 = transform2mat3(T);
|
|
|
|
return T->gcache3;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
int js_print_exception(JSContext *js, JSValue v)
|
|
{
|
|
if (!js) return 0;
|
|
#ifndef NDEBUG
|
|
if (!JS_IsException(v))
|
|
return 0;
|
|
|
|
JSValue ex = JS_GetException(js);
|
|
|
|
const char *name = JS_ToCString(js, js_getpropertystr(js,ex, "name"));
|
|
const char *msg = JS_ToCString(js, js_getpropertystr(js,ex, "message"));
|
|
const char *stack = JS_ToCString(js, js_getpropertystr(js,ex, "stack"));
|
|
printf("%s :: %s\n%s\n", name, msg, stack);
|
|
|
|
JS_FreeCString(js, name);
|
|
JS_FreeCString(js, msg);
|
|
JS_FreeCString(js, stack);
|
|
JS_FreeValue(js,ex);
|
|
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
rect js2rect(JSContext *js,JSValue v) {
|
|
if (JS_IsUndefined(v)) return (rect){0,0,1,1};
|
|
rect rect;
|
|
rect.w = js_getnum(js,v,width_atom);
|
|
rect.h = js_getnum(js,v,height_atom);
|
|
JSValue xv = JS_GetProperty(js,v,x_atom);
|
|
rect.x = js2number(js,xv);
|
|
JS_FreeValue(js,xv);
|
|
JSValue yv = JS_GetProperty(js,v,y_atom);
|
|
rect.y = js2number(js,yv);
|
|
JS_FreeValue(js,yv);
|
|
float anchor_x = js_getnum(js,v, anchor_x_atom);
|
|
float anchor_y = js_getnum(js,v, anchor_y_atom);
|
|
|
|
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_SetProperty(js, obj, x_atom, number2js(js,rect.x));
|
|
JS_SetProperty(js, obj, y_atom, number2js(js,rect.y));
|
|
JS_SetProperty(js, obj, width_atom, number2js(js,rect.w));
|
|
JS_SetProperty(js, obj, height_atom, number2js(js,rect.h));
|
|
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;
|
|
}
|
|
|
|
/*JSC_GETSET(warp_gravity, strength, number)
|
|
JSC_GETSET(warp_gravity, decay, number)
|
|
JSC_GETSET(warp_gravity, spherical, bool)
|
|
JSC_GETSET(warp_gravity, mask, bitmask)
|
|
JSC_GETSET(warp_gravity, planar_force, vec3)
|
|
|
|
static const JSCFunctionListEntry js_warp_gravity_funcs [] = {
|
|
CGETSET_ADD(warp_gravity, strength),
|
|
CGETSET_ADD(warp_gravity, decay),
|
|
CGETSET_ADD(warp_gravity, spherical),
|
|
CGETSET_ADD(warp_gravity, mask),
|
|
CGETSET_ADD(warp_gravity, planar_force),
|
|
};
|
|
|
|
JSC_GETSET(warp_damp, damp, vec3)
|
|
|
|
static const JSCFunctionListEntry js_warp_damp_funcs [] = {
|
|
CGETSET_ADD(warp_damp, damp)
|
|
};
|
|
*/
|
|
|
|
HMM_Mat4 transform2view(transform *t)
|
|
{
|
|
HMM_Vec3 look = HMM_AddV3(t->pos, transform_direction(t, vFWD));
|
|
HMM_Mat4 ret = HMM_LookAt_RH(t->pos, look, vUP);
|
|
ret = HMM_MulM4(ret, HMM_Scale(t->scale));
|
|
return ret;
|
|
}
|
|
|
|
JSC_CCALL(render_set_projection_ortho,
|
|
lrtb extents = js2lrtb(js, argv[0]);
|
|
float nearme = js2number(js,argv[1]);
|
|
float farme = js2number(js,argv[2]);
|
|
globalview.p = HMM_Orthographic_RH_ZO(
|
|
extents.l,
|
|
extents.r,
|
|
extents.b,
|
|
extents.t,
|
|
nearme,
|
|
farme
|
|
);
|
|
globalview.vp = HMM_MulM4(globalview.p, globalview.v);
|
|
)
|
|
|
|
JSC_CCALL(render_set_projection_perspective,
|
|
float fov = js2number(js,argv[0]);
|
|
float aspect = js2number(js,argv[1]);
|
|
float nearme = js2number(js,argv[2]);
|
|
float farme = js2number(js,argv[3]);
|
|
globalview.p = HMM_Perspective_RH_NO(fov, aspect, nearme, farme);
|
|
globalview.vp = HMM_MulM4(globalview.p, globalview.v);
|
|
)
|
|
|
|
JSC_CCALL(render_set_view,
|
|
globalview.v = transform2view(js2transform(js,argv[0]));
|
|
globalview.vp = HMM_MulM4(globalview.p, globalview.v);
|
|
)
|
|
|
|
JSC_SCALL(render_text_size,
|
|
font *f = js2font(js,argv[1]);
|
|
float size = js2number(js,argv[2]);
|
|
if (!size) size = f->height;
|
|
float letterSpacing = js2number(js,argv[3]);
|
|
float wrap = js2number(js,argv[4]);
|
|
ret = vec22js(js,measure_text(str, f, size, letterSpacing, wrap));
|
|
)
|
|
|
|
JSC_CCALL(render_draw_color,
|
|
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
|
|
colorf rgba = js2color(js,argv[0]);
|
|
SDL_SetRenderDrawColorFloat(renderer, rgba.r, rgba.g, rgba.b, rgba.a);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_render_funcs[] = {
|
|
MIST_FUNC_DEF(render, text_size, 5),
|
|
MIST_FUNC_DEF(render, set_projection_ortho, 3),
|
|
MIST_FUNC_DEF(render, set_projection_perspective, 4),
|
|
MIST_FUNC_DEF(render, set_view, 1),
|
|
MIST_FUNC_DEF(render, draw_color, 1),
|
|
};
|
|
|
|
static JSValue idx_buffer = JS_UNDEFINED;
|
|
static int idx_count = 0;
|
|
|
|
JSValue make_quad_indices_buffer(JSContext *js, int quads)
|
|
{
|
|
int count = quads*6;
|
|
if (!JS_IsUndefined(idx_buffer) && idx_count >= count)
|
|
return JS_DupValue(js,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(idx_buffer))
|
|
JS_FreeValue(js,idx_buffer);
|
|
|
|
idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1);
|
|
idx_count = count;
|
|
return JS_DupValue(js,idx_buffer);
|
|
}
|
|
|
|
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_atom, jspos);
|
|
JS_SetProperty(js, ret, uv_atom, jsuv);
|
|
JS_SetProperty(js, ret, color_atom, jscolor);
|
|
JS_SetProperty(js, ret, indices_atom, jsidx);
|
|
JS_SetProperty(js, ret, vertices_atom, number2js(js, verts));
|
|
JS_SetProperty(js,ret,num_indices_atom, 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);
|
|
arrfree(points);
|
|
if (!samples) return JS_UNDEFINED;
|
|
JSValue arr = vecarr2js(js,samples, arrlen(samples));
|
|
arrfree(samples);
|
|
return arr;
|
|
)
|
|
|
|
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);
|
|
arrfree(points);
|
|
if (!samples) return JS_UNDEFINED;
|
|
JSValue arr = vecarr2js(js,samples, arrlen(samples));
|
|
arrfree(samples);
|
|
return arr;
|
|
)
|
|
|
|
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;
|
|
double aspect;
|
|
int ortho;
|
|
double near;
|
|
double far;
|
|
|
|
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,camera,near,number)
|
|
JS_GETPROP(js,far,camera,far,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,far);
|
|
|
|
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;
|
|
data.viewport_max_z = far;
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
JSValue js_vector_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);
|
|
// JSValue ret = number2js(js, cblas_sdot(alen, a, 1, b,1));
|
|
free(a);
|
|
free(b);
|
|
return JS_UNDEFINED;
|
|
};
|
|
|
|
JSC_CCALL(vector_project, return vec22js(js,HMM_ProjV2(js2vec2(js,argv[0]), js2vec2(js,argv[1]))))
|
|
|
|
JSC_CCALL(vector_midpoint,
|
|
HMM_Vec2 a = js2vec2(js,argv[0]);
|
|
HMM_Vec2 b = js2vec2(js,argv[1]);
|
|
// HMM_Vec2 c = HMM_AddV2(a,b);
|
|
// c = HMM_Div2VF(c, 2);
|
|
return vec22js(js,(HMM_Vec2){(a.x+b.x)/2, (a.y+b.y)/2});
|
|
)
|
|
|
|
JSC_CCALL(vector_distance,
|
|
HMM_Vec2 a = js2vec2(js,argv[0]);
|
|
HMM_Vec2 b = js2vec2(js,argv[1]);
|
|
return number2js(js,HMM_DistV2(a,b));
|
|
)
|
|
|
|
JSC_CCALL(vector_angle,
|
|
HMM_Vec2 a = js2vec2(js,argv[0]);
|
|
return angle2js(js,atan2(a.y,a.x));
|
|
)
|
|
|
|
/* 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(vector_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(vector_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(vector_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 = HMM_SubV2(vec,pivot);
|
|
float r = HMM_LenV2(vec);
|
|
angle += atan2(vec.y,vec.x);
|
|
vec.x = r*cos(angle);
|
|
vec.y = r*sin(angle);
|
|
return vec22js(js,HMM_AddV2(vec,pivot));
|
|
)
|
|
*/
|
|
JSC_CCALL(vector_add,
|
|
HMM_Vec4 a = js2vec4(js,argv[0]);
|
|
HMM_Vec4 b = js2vec4(js,argv[1]);
|
|
HMM_Vec4 c = HMM_AddV4(a,b);
|
|
return vec42js(js,c);
|
|
)
|
|
|
|
JSC_CCALL(vector_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));
|
|
|
|
return newarr;
|
|
)
|
|
|
|
JSC_CCALL(vector_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 angle2js(js,0);
|
|
)
|
|
|
|
JSC_CCALL(vector_lerp,
|
|
double s = js2number(js,argv[0]);
|
|
double f = js2number(js,argv[1]);
|
|
double t = js2number(js,argv[2]);
|
|
|
|
return number2js(js,(f-s)*t+s);
|
|
)
|
|
|
|
int gcd(int a, int b) {
|
|
if (b == 0)
|
|
return a;
|
|
return gcd(b, a % b);
|
|
}
|
|
|
|
JSC_CCALL(vector_gcd,
|
|
return number2js(js,gcd(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(vector_lcm,
|
|
double a = js2number(js,argv[0]);
|
|
double b = js2number(js,argv[1]);
|
|
return number2js(js,(a*b)/gcd(a,b));
|
|
)
|
|
|
|
JSC_CCALL(vector_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_SSCALL(vector_trimchr,
|
|
int len = js2number(js,js_getpropertystr(js,argv[0], "length"));
|
|
const char *start = str;
|
|
|
|
while (*start == *str2)
|
|
start++;
|
|
|
|
const char *end = str + len-1;
|
|
while(*end == *str2)
|
|
end--;
|
|
|
|
ret = JS_NewStringLen(js, start, end-start+1);
|
|
)
|
|
|
|
JSC_CCALL(vector_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(vector_length,
|
|
return number2js(js,arr_vec_length(js,argv[0]));
|
|
)
|
|
|
|
double r2()
|
|
{
|
|
return (double)rand() / (double)RAND_MAX ;
|
|
}
|
|
|
|
double rand_range(double min, double max) {
|
|
return r2() * (max-min) + min;
|
|
}
|
|
|
|
JSC_CCALL(vector_variate,
|
|
double n = js2number(js,argv[0]);
|
|
double pct = js2number(js,argv[1]);
|
|
|
|
return number2js(js,n + (rand_range(-pct,pct)*n));
|
|
)
|
|
|
|
JSC_CCALL(vector_random_range, return number2js(js,rand_range(js2number(js,argv[0]), js2number(js,argv[1]))))
|
|
|
|
JSC_CCALL(vector_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(vector_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(vector_fastsum,
|
|
float sum = 0.0;
|
|
size_t len;
|
|
float *a = get_typed_buffer(js, argv[0], &len);
|
|
// sum = cblas_sasum(len, a,1);
|
|
ret = number2js(js,sum);
|
|
)
|
|
|
|
JSC_CCALL(vector_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(vector_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]);
|
|
)
|
|
|
|
int fibonacci(int n) {
|
|
if (n <= 1) return n;
|
|
return fibonacci(n-1) + fibonacci(n-2);
|
|
}
|
|
JSC_CCALL(vector_fib,
|
|
int n = js2number(js,argv[0]);
|
|
int fib = fibonacci(n);
|
|
|
|
printf("ANSWER IS %d\n", fib);
|
|
)
|
|
|
|
JSC_CCALL(vector_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);
|
|
int stepsize = length/(steps+1);
|
|
|
|
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(vector_float32add,
|
|
size_t len;
|
|
float *vec_a = get_typed_buffer(js,self, &len);
|
|
float *vec_b = get_typed_buffer(js,argv[0], &len);
|
|
// cblas_saxpy(len,1.0f,vec_b,1,vec_a,1);
|
|
JSValue tstack[3];
|
|
tstack[0] = JS_NewArrayBufferCopy(js,vec_a,sizeof(float)*4);
|
|
tstack[1] = JS_UNDEFINED;
|
|
tstack[2] = JS_UNDEFINED;
|
|
ret = JS_NewTypedArray(js, 3, tstack, JS_TYPED_ARRAY_FLOAT32);
|
|
JS_FreeValue(js,tstack[0]);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_vector_funcs[] = {
|
|
MIST_FUNC_DEF(vector, dot,2),
|
|
MIST_FUNC_DEF(vector, project,2),
|
|
MIST_FUNC_DEF(vector, inflate, 2),
|
|
MIST_FUNC_DEF(vector, rotate, 3),
|
|
MIST_FUNC_DEF(vector, add, 2),
|
|
MIST_FUNC_DEF(vector, midpoint, 2),
|
|
MIST_FUNC_DEF(vector, distance, 2),
|
|
MIST_FUNC_DEF(vector, angle, 1),
|
|
MIST_FUNC_DEF(vector, norm, 1),
|
|
MIST_FUNC_DEF(vector, angle_between, 2),
|
|
MIST_FUNC_DEF(vector, lerp, 3),
|
|
MIST_FUNC_DEF(vector, gcd, 2),
|
|
MIST_FUNC_DEF(vector, lcm, 2),
|
|
MIST_FUNC_DEF(vector, clamp, 3),
|
|
MIST_FUNC_DEF(vector, trimchr, 2),
|
|
MIST_FUNC_DEF(vector, angledist, 2),
|
|
MIST_FUNC_DEF(vector, variate, 2),
|
|
MIST_FUNC_DEF(vector, random_range, 2),
|
|
MIST_FUNC_DEF(vector, mean, 1),
|
|
MIST_FUNC_DEF(vector, sum, 1),
|
|
MIST_FUNC_DEF(vector, fastsum, 1),
|
|
MIST_FUNC_DEF(vector, sigma, 1),
|
|
MIST_FUNC_DEF(vector, median, 1),
|
|
MIST_FUNC_DEF(vector, length, 1),
|
|
MIST_FUNC_DEF(vector, fib, 1),
|
|
MIST_FUNC_DEF(vector, from_to, 5),
|
|
MIST_FUNC_DEF(vector, float32add, 2),
|
|
};
|
|
|
|
#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; \
|
|
) \
|
|
|
|
/*JSC_CCALL(array_add,
|
|
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) + n));
|
|
return arr;
|
|
}
|
|
|
|
JSValue arr = JS_NewArray(js);
|
|
size_t len_a, len_b;
|
|
float *a = js2floats(js,self, &len_a);
|
|
float *b = js2floats(js,argv[0], &len_b);
|
|
cblas_saxpy(len_a, 1.0, a, 1, b, 1);
|
|
for (int i = 0; i < len; i++)
|
|
JS_SetPropertyUint32(js, arr, i, number2js(js,b[i]));
|
|
|
|
ret = arr;
|
|
free(a);
|
|
free(b);
|
|
return arr;
|
|
) */
|
|
|
|
/*JSC_CCALL(array_add,
|
|
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) + n));
|
|
|
|
return arr;
|
|
}
|
|
float *vec_a = js2floatarray(js,self);
|
|
float *vec_b = js2floatarray(js,argv[0]);
|
|
cblas_saxpy(len,1.0f,vec_b,1,vec_a,1);
|
|
JSValue tstack[3];
|
|
tstack[0] = JS_NewArrayBuffer(js,vec_a,sizeof(float)*2,free_gpu_buffer, NULL, 0);
|
|
tstack[1] = JS_UNDEFINED;
|
|
tstack[2] = JS_UNDEFINED;
|
|
ret = JS_NewTypedArray(js, 3, tstack, JS_TYPED_ARRAY_FLOAT32);
|
|
JS_FreeValue(js,tstack[0]);
|
|
free(vec_b);
|
|
)
|
|
*/
|
|
|
|
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;
|
|
)
|
|
|
|
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)
|
|
};
|
|
|
|
#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_SCALL(game_engine_start,
|
|
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)
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0)
|
|
return JS_ThrowReferenceError(js, "Couldn't initialize SDL: %s\n", SDL_GetError());
|
|
|
|
SDL_Window *new = SDL_CreateWindow("prosperon", js2number(js, js_getproperty(js,argv[0], width_atom)), js2number(js,js_getproperty(js,argv[0], height_atom)), SDL_WINDOW_RESIZABLE);
|
|
|
|
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);
|
|
)
|
|
|
|
typedef struct {
|
|
SDL_EventType key;
|
|
JSAtom value;
|
|
} SDL_EventTypePair;
|
|
|
|
struct {SDL_EventType key; JSAtom value; } *event_hash = NULL;
|
|
|
|
void fill_event_atoms(JSContext *js)
|
|
{
|
|
if (event_hash != NULL) return;
|
|
|
|
// Application events
|
|
hmput(event_hash, SDL_EVENT_QUIT, JS_NewAtom(js, "quit"));
|
|
hmput(event_hash, SDL_EVENT_TERMINATING, JS_NewAtom(js, "terminating"));
|
|
hmput(event_hash, SDL_EVENT_LOW_MEMORY, JS_NewAtom(js, "low_memory"));
|
|
hmput(event_hash, SDL_EVENT_WILL_ENTER_BACKGROUND, JS_NewAtom(js, "will_enter_background"));
|
|
hmput(event_hash, SDL_EVENT_DID_ENTER_BACKGROUND, JS_NewAtom(js, "did_enter_background"));
|
|
hmput(event_hash, SDL_EVENT_WILL_ENTER_FOREGROUND, JS_NewAtom(js, "will_enter_foreground"));
|
|
hmput(event_hash, SDL_EVENT_DID_ENTER_FOREGROUND, JS_NewAtom(js, "did_enter_foreground"));
|
|
hmput(event_hash, SDL_EVENT_LOCALE_CHANGED, JS_NewAtom(js, "locale_changed"));
|
|
hmput(event_hash, SDL_EVENT_SYSTEM_THEME_CHANGED, JS_NewAtom(js, "system_theme_changed"));
|
|
|
|
// Display events
|
|
hmput(event_hash, SDL_EVENT_DISPLAY_ORIENTATION, JS_NewAtom(js, "display_orientation"));
|
|
hmput(event_hash, SDL_EVENT_DISPLAY_ADDED, JS_NewAtom(js, "display_added"));
|
|
hmput(event_hash, SDL_EVENT_DISPLAY_REMOVED, JS_NewAtom(js, "display_removed"));
|
|
hmput(event_hash, SDL_EVENT_DISPLAY_MOVED, JS_NewAtom(js, "display_moved"));
|
|
hmput(event_hash, SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED, JS_NewAtom(js, "display_desktop_mode_changed"));
|
|
hmput(event_hash, SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, JS_NewAtom(js, "display_current_mode_changed"));
|
|
hmput(event_hash, SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, JS_NewAtom(js, "display_content_scale_changed"));
|
|
|
|
// Window events
|
|
hmput(event_hash, SDL_EVENT_WINDOW_SHOWN, JS_NewAtom(js, "window_shown"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_HIDDEN, JS_NewAtom(js, "window_hidden"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_EXPOSED, JS_NewAtom(js, "window_exposed"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_MOVED, JS_NewAtom(js, "window_moved"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_RESIZED, JS_NewAtom(js, "window_resized"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, JS_NewAtom(js, "window_pixel_size_changed"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_METAL_VIEW_RESIZED, JS_NewAtom(js, "window_metal_view_resized"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_MINIMIZED, JS_NewAtom(js, "window_minimized"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_MAXIMIZED, JS_NewAtom(js, "window_maximized"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_RESTORED, JS_NewAtom(js, "window_restored"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_MOUSE_ENTER, JS_NewAtom(js, "window_mouse_enter"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_MOUSE_LEAVE, JS_NewAtom(js, "window_mouse_leave"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_FOCUS_GAINED, JS_NewAtom(js, "window_focus_gained"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_FOCUS_LOST, JS_NewAtom(js, "window_focus_lost"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_CLOSE_REQUESTED, JS_NewAtom(js, "window_close_requested"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_HIT_TEST, JS_NewAtom(js, "window_hit_test"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_ICCPROF_CHANGED, JS_NewAtom(js, "window_iccprof_changed"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_DISPLAY_CHANGED, JS_NewAtom(js, "window_display_changed"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED, JS_NewAtom(js, "window_display_scale_changed"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, JS_NewAtom(js, "window_safe_area_changed"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_OCCLUDED, JS_NewAtom(js, "window_occluded"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, JS_NewAtom(js, "window_enter_fullscreen"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, JS_NewAtom(js, "window_leave_fullscreen"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_DESTROYED, JS_NewAtom(js, "window_destroyed"));
|
|
hmput(event_hash, SDL_EVENT_WINDOW_HDR_STATE_CHANGED, JS_NewAtom(js, "window_hdr_state_changed"));
|
|
|
|
// Keyboard events
|
|
hmput(event_hash, SDL_EVENT_KEY_DOWN, JS_NewAtom(js, "key_down"));
|
|
hmput(event_hash, SDL_EVENT_KEY_UP, JS_NewAtom(js, "key_up"));
|
|
hmput(event_hash, SDL_EVENT_TEXT_EDITING, JS_NewAtom(js, "text_editing"));
|
|
hmput(event_hash, SDL_EVENT_TEXT_INPUT, JS_NewAtom(js, "text_input"));
|
|
hmput(event_hash, SDL_EVENT_KEYMAP_CHANGED, JS_NewAtom(js, "keymap_changed"));
|
|
hmput(event_hash, SDL_EVENT_KEYBOARD_ADDED, JS_NewAtom(js, "keyboard_added"));
|
|
hmput(event_hash, SDL_EVENT_KEYBOARD_REMOVED, JS_NewAtom(js, "keyboard_removed"));
|
|
hmput(event_hash, SDL_EVENT_TEXT_EDITING_CANDIDATES, JS_NewAtom(js, "text_editing_candidates"));
|
|
|
|
// Mouse events
|
|
hmput(event_hash, SDL_EVENT_MOUSE_MOTION, JS_NewAtom(js, "mouse_motion"));
|
|
hmput(event_hash, SDL_EVENT_MOUSE_BUTTON_DOWN, JS_NewAtom(js, "mouse_button_down"));
|
|
hmput(event_hash, SDL_EVENT_MOUSE_BUTTON_UP, JS_NewAtom(js, "mouse_button_up"));
|
|
hmput(event_hash, SDL_EVENT_MOUSE_WHEEL, JS_NewAtom(js, "mouse_wheel"));
|
|
hmput(event_hash, SDL_EVENT_MOUSE_ADDED, JS_NewAtom(js, "mouse_added"));
|
|
hmput(event_hash, SDL_EVENT_MOUSE_REMOVED, JS_NewAtom(js, "mouse_removed"));
|
|
|
|
// Joystick events
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_AXIS_MOTION, JS_NewAtom(js, "joystick_axis_motion"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_BALL_MOTION, JS_NewAtom(js, "joystick_ball_motion"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_HAT_MOTION, JS_NewAtom(js, "joystick_hat_motion"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_BUTTON_DOWN, JS_NewAtom(js, "joystick_button_down"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_BUTTON_UP, JS_NewAtom(js, "joystick_button_up"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_ADDED, JS_NewAtom(js, "joystick_added"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_REMOVED, JS_NewAtom(js, "joystick_removed"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_BATTERY_UPDATED, JS_NewAtom(js, "joystick_battery_updated"));
|
|
hmput(event_hash, SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, JS_NewAtom(js, "joystick_update_complete"));
|
|
|
|
// Gamepad events
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_AXIS_MOTION, JS_NewAtom(js, "gamepad_axis_motion"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_BUTTON_DOWN, JS_NewAtom(js, "gamepad_button_down"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_BUTTON_UP, JS_NewAtom(js, "gamepad_button_up"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_ADDED, JS_NewAtom(js, "gamepad_added"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_REMOVED, JS_NewAtom(js, "gamepad_removed"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_REMAPPED, JS_NewAtom(js, "gamepad_remapped"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, JS_NewAtom(js, "gamepad_touchpad_down"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, JS_NewAtom(js, "gamepad_touchpad_motion"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_TOUCHPAD_UP, JS_NewAtom(js, "gamepad_touchpad_up"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_SENSOR_UPDATE, JS_NewAtom(js, "gamepad_sensor_update"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, JS_NewAtom(js, "gamepad_update_complete"));
|
|
hmput(event_hash, SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED, JS_NewAtom(js, "gamepad_steam_handle_updated"));
|
|
|
|
// Touch events
|
|
hmput(event_hash, SDL_EVENT_FINGER_DOWN, JS_NewAtom(js, "finger_down"));
|
|
hmput(event_hash, SDL_EVENT_FINGER_UP, JS_NewAtom(js, "finger_up"));
|
|
hmput(event_hash, SDL_EVENT_FINGER_MOTION, JS_NewAtom(js, "finger_motion"));
|
|
|
|
// Clipboard events
|
|
hmput(event_hash, SDL_EVENT_CLIPBOARD_UPDATE, JS_NewAtom(js, "clipboard_update"));
|
|
|
|
// Drag and drop events
|
|
hmput(event_hash, SDL_EVENT_DROP_FILE, JS_NewAtom(js, "drop_file"));
|
|
hmput(event_hash, SDL_EVENT_DROP_TEXT, JS_NewAtom(js, "drop_text"));
|
|
hmput(event_hash, SDL_EVENT_DROP_BEGIN, JS_NewAtom(js, "drop_begin"));
|
|
hmput(event_hash, SDL_EVENT_DROP_COMPLETE, JS_NewAtom(js, "drop_complete"));
|
|
hmput(event_hash, SDL_EVENT_DROP_POSITION, JS_NewAtom(js, "drop_position"));
|
|
|
|
// Audio device events
|
|
hmput(event_hash, SDL_EVENT_AUDIO_DEVICE_ADDED, JS_NewAtom(js, "audio_device_added"));
|
|
hmput(event_hash, SDL_EVENT_AUDIO_DEVICE_REMOVED, JS_NewAtom(js, "audio_device_removed"));
|
|
hmput(event_hash, SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED, JS_NewAtom(js, "audio_device_format_changed"));
|
|
|
|
// Sensor events
|
|
hmput(event_hash, SDL_EVENT_SENSOR_UPDATE, JS_NewAtom(js, "sensor_update"));
|
|
|
|
// Pen events
|
|
hmput(event_hash, SDL_EVENT_PEN_PROXIMITY_IN, JS_NewAtom(js, "pen_proximity_in"));
|
|
hmput(event_hash, SDL_EVENT_PEN_PROXIMITY_OUT, JS_NewAtom(js, "pen_proximity_out"));
|
|
hmput(event_hash, SDL_EVENT_PEN_DOWN, JS_NewAtom(js, "pen_down"));
|
|
hmput(event_hash, SDL_EVENT_PEN_UP, JS_NewAtom(js, "pen_up"));
|
|
hmput(event_hash, SDL_EVENT_PEN_BUTTON_DOWN, JS_NewAtom(js, "pen_button_down"));
|
|
hmput(event_hash, SDL_EVENT_PEN_BUTTON_UP, JS_NewAtom(js, "pen_button_up"));
|
|
hmput(event_hash, SDL_EVENT_PEN_MOTION, JS_NewAtom(js, "pen_motion"));
|
|
hmput(event_hash, SDL_EVENT_PEN_AXIS, JS_NewAtom(js, "pen_axis"));
|
|
|
|
// Camera events
|
|
hmput(event_hash, SDL_EVENT_CAMERA_DEVICE_ADDED, JS_NewAtom(js, "camera_device_added"));
|
|
hmput(event_hash, SDL_EVENT_CAMERA_DEVICE_REMOVED, JS_NewAtom(js, "camera_device_removed"));
|
|
hmput(event_hash, SDL_EVENT_CAMERA_DEVICE_APPROVED, JS_NewAtom(js, "camera_device_approved"));
|
|
hmput(event_hash, SDL_EVENT_CAMERA_DEVICE_DENIED, JS_NewAtom(js, "camera_device_denied"));
|
|
|
|
// Render events
|
|
hmput(event_hash, SDL_EVENT_RENDER_TARGETS_RESET, JS_NewAtom(js, "render_targets_reset"));
|
|
hmput(event_hash, SDL_EVENT_RENDER_DEVICE_RESET, JS_NewAtom(js, "render_device_reset"));
|
|
hmput(event_hash, SDL_EVENT_RENDER_DEVICE_LOST, JS_NewAtom(js, "render_device_lost"));
|
|
}
|
|
|
|
static JSAtom mouse2atom(JSContext *js, int mouse)
|
|
{
|
|
switch(mouse) {
|
|
case SDL_BUTTON_LEFT: return JS_NewAtom(js,"left");
|
|
case SDL_BUTTON_MIDDLE: return JS_NewAtom(js,"middle");
|
|
case SDL_BUTTON_RIGHT: return JS_NewAtom(js,"right");
|
|
case SDL_BUTTON_X1: return JS_NewAtom(js,"x1");
|
|
case SDL_BUTTON_X2: return JS_NewAtom(js,"x2");
|
|
}
|
|
return JS_NewAtom(js,"left");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static JSValue event2js(JSContext *js, SDL_Event event)
|
|
{
|
|
JSValue e = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, e, "type", JS_AtomToString(js,hmget(event_hash, event.type)));
|
|
JS_SetPropertyStr(js,e,"timestamp", number2js(js,event.common.timestamp));
|
|
|
|
switch(event.type) {
|
|
case SDL_EVENT_AUDIO_DEVICE_ADDED:
|
|
case SDL_EVENT_AUDIO_DEVICE_REMOVED:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.adevice.which));
|
|
JS_SetPropertyStr(js,e,"recording", JS_NewBool(js,event.adevice.recording));
|
|
break;
|
|
case SDL_EVENT_DISPLAY_ORIENTATION:
|
|
case SDL_EVENT_DISPLAY_ADDED:
|
|
case SDL_EVENT_DISPLAY_REMOVED:
|
|
case SDL_EVENT_DISPLAY_MOVED:
|
|
case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED:
|
|
case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED:
|
|
case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.display.displayID));
|
|
JS_SetPropertyStr(js,e,"data1", number2js(js,event.display.data1));
|
|
JS_SetPropertyStr(js,e,"data2", number2js(js,event.display.data2));
|
|
break;
|
|
case SDL_EVENT_MOUSE_MOTION:
|
|
JS_SetPropertyStr(js,e,"window", number2js(js,event.motion.windowID));
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.motion.which));
|
|
JS_SetPropertyStr(js, e, "state", number2js(js,event.motion.state));
|
|
JS_SetPropertyStr(js,e, "pos", vec22js(js,(HMM_Vec2){event.motion.x,event.motion.y}));
|
|
JS_SetPropertyStr(js,e,"d_pos", vec22js(js,(HMM_Vec2){event.motion.xrel, event.motion.yrel}));
|
|
break;
|
|
case SDL_EVENT_MOUSE_WHEEL:
|
|
JS_SetPropertyStr(js,e,"window", number2js(js,event.wheel.windowID));
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.wheel.which));
|
|
JS_SetPropertyStr(js,e,"scroll", vec22js(js,(HMM_Vec2){event.wheel.x,event.wheel.y}));
|
|
JS_SetPropertyStr(js,e, "mouse", vec22js(js,(HMM_Vec2){event.wheel.mouse_x,event.wheel.mouse_y}));
|
|
break;
|
|
case SDL_EVENT_MOUSE_BUTTON_UP:
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
JS_SetPropertyStr(js,e,"window", number2js(js,event.button.windowID));
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.button.which));
|
|
JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.button.down));
|
|
JS_SetPropertyStr(js,e,"button", JS_AtomToString(js,mouse2atom(js,event.button.button)));
|
|
JS_SetPropertyStr(js,e,"clicks", number2js(js,event.button.clicks));
|
|
JS_SetPropertyStr(js,e,"mouse", vec22js(js,(HMM_Vec2){event.button.x,event.button.y}));
|
|
break;
|
|
case SDL_EVENT_SENSOR_UPDATE:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.sensor.which));
|
|
JS_SetPropertyStr(js,e, "sensor_timestamp", number2js(js,event.sensor.sensor_timestamp));
|
|
break;
|
|
case SDL_EVENT_KEY_DOWN:
|
|
case SDL_EVENT_KEY_UP:
|
|
JS_SetPropertyStr(js,e,"window", number2js(js,event.key.windowID));
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.key.which));
|
|
JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.key.down));
|
|
JS_SetPropertyStr(js,e,"repeat", JS_NewBool(js,event.key.repeat));
|
|
JS_SetPropertyStr(js,e,"key", number2js(js,event.key.key));
|
|
JS_SetPropertyStr(js,e,"scancode", number2js(js,event.key.scancode));
|
|
JS_SetPropertyStr(js,e,"mod", js_keymod(js));
|
|
break;
|
|
case SDL_EVENT_FINGER_MOTION:
|
|
case SDL_EVENT_FINGER_DOWN:
|
|
case SDL_EVENT_FINGER_UP:
|
|
JS_SetPropertyStr(js,e,"touch", number2js(js,event.tfinger.touchID));
|
|
JS_SetPropertyStr(js,e,"finger", number2js(js,event.tfinger.fingerID));
|
|
JS_SetPropertyStr(js,e,"pos", vec22js(js, (HMM_Vec2){event.tfinger.x, event.tfinger.y}));
|
|
JS_SetPropertyStr(js,e,"d_pos", vec22js(js,(HMM_Vec2){event.tfinger.x, event.tfinger.dy}));
|
|
JS_SetPropertyStr(js,e,"pressure", number2js(js,event.tfinger.pressure));
|
|
JS_SetPropertyStr(js,e,"window", number2js(js,event.key.windowID));
|
|
break;
|
|
case SDL_EVENT_DROP_BEGIN:
|
|
case SDL_EVENT_DROP_FILE:
|
|
case SDL_EVENT_DROP_TEXT:
|
|
case SDL_EVENT_DROP_COMPLETE:
|
|
case SDL_EVENT_DROP_POSITION:
|
|
JS_SetPropertyStr(js,e,"window", number2js(js,event.drop.windowID));
|
|
JS_SetPropertyStr(js,e,"pos", vec22js(js, (HMM_Vec2){event.drop.x,event.drop.y}));
|
|
JS_SetPropertyStr(js,e,"data", JS_NewString(js,event.drop.data));
|
|
JS_SetPropertyStr(js,e,"source",JS_NewString(js,event.drop.source));
|
|
break;
|
|
case SDL_EVENT_TEXT_INPUT:
|
|
JS_SetPropertyStr(js,e,"window", number2js(js,event.text.windowID));
|
|
JS_SetPropertyStr(js,e,"text", JS_NewString(js,event.text.text));
|
|
JS_SetPropertyStr(js,e,"mod", js_keymod(js));
|
|
break;
|
|
case SDL_EVENT_CAMERA_DEVICE_APPROVED:
|
|
case SDL_EVENT_CAMERA_DEVICE_REMOVED:
|
|
case SDL_EVENT_CAMERA_DEVICE_ADDED:
|
|
case SDL_EVENT_CAMERA_DEVICE_DENIED:
|
|
JS_SetPropertyStr(js, e, "which", number2js(js,event.cdevice.which));
|
|
break;
|
|
case SDL_EVENT_CLIPBOARD_UPDATE:
|
|
JS_SetPropertyStr(js, e, "owner", JS_NewBool(js,event.clipboard.owner));
|
|
break;
|
|
case SDL_EVENT_WINDOW_SHOWN:
|
|
case SDL_EVENT_WINDOW_HIDDEN:
|
|
case SDL_EVENT_WINDOW_EXPOSED:
|
|
case SDL_EVENT_WINDOW_MOVED:
|
|
case SDL_EVENT_WINDOW_RESIZED:
|
|
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
|
case SDL_EVENT_WINDOW_METAL_VIEW_RESIZED:
|
|
case SDL_EVENT_WINDOW_MINIMIZED:
|
|
case SDL_EVENT_WINDOW_MAXIMIZED:
|
|
case SDL_EVENT_WINDOW_RESTORED:
|
|
case SDL_EVENT_WINDOW_MOUSE_ENTER:
|
|
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
|
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
|
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
|
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
|
case SDL_EVENT_WINDOW_HIT_TEST:
|
|
case SDL_EVENT_WINDOW_ICCPROF_CHANGED:
|
|
case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
|
|
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
|
|
case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED:
|
|
case SDL_EVENT_WINDOW_OCCLUDED:
|
|
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
|
|
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
|
|
case SDL_EVENT_WINDOW_DESTROYED:
|
|
case SDL_EVENT_WINDOW_HDR_STATE_CHANGED:
|
|
/* rest of SDL_EVENT_WINDOW_ here */
|
|
JS_SetPropertyStr(js,e,"which", number2js(js, event.window.windowID));
|
|
JS_SetPropertyStr(js,e,"data1", number2js(js, event.window.data1));
|
|
JS_SetPropertyStr(js,e,"data2", number2js(js, event.window.data2));
|
|
break;
|
|
|
|
case SDL_EVENT_JOYSTICK_ADDED:
|
|
case SDL_EVENT_JOYSTICK_REMOVED:
|
|
case SDL_EVENT_JOYSTICK_UPDATE_COMPLETE:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.jdevice.which));
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_AXIS_MOTION:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.jaxis.which));
|
|
JS_SetPropertyStr(js,e,"axis", number2js(js,event.jaxis.axis));
|
|
JS_SetPropertyStr(js,e,"value", number2js(js,event.jaxis.value));
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_BALL_MOTION:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.jball.which));
|
|
JS_SetPropertyStr(js,e,"ball",number2js(js,event.jball.ball));
|
|
JS_SetPropertyStr(js,e, "rel", vec22js(js,(HMM_Vec2){event.jball.xrel,event.jball.yrel}));
|
|
break;
|
|
case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
|
|
case SDL_EVENT_JOYSTICK_BUTTON_UP:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.jbutton.which));
|
|
JS_SetPropertyStr(js,e,"button", number2js(js,event.jbutton.button));
|
|
JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.jbutton.down));
|
|
break;
|
|
|
|
case SDL_EVENT_GAMEPAD_ADDED:
|
|
case SDL_EVENT_GAMEPAD_REMOVED:
|
|
case SDL_EVENT_GAMEPAD_REMAPPED:
|
|
case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE:
|
|
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.gdevice.which));
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.gaxis.which));
|
|
JS_SetPropertyStr(js,e,"axis", number2js(js,event.gaxis.axis));
|
|
JS_SetPropertyStr(js,e,"value", number2js(js,event.gaxis.value));
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.gbutton.which));
|
|
JS_SetPropertyStr(js,e,"button", number2js(js,event.gbutton.button));
|
|
JS_SetPropertyStr(js,e,"down", JS_NewBool(js,event.gbutton.down));
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
|
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
|
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.gtouchpad.which));
|
|
JS_SetPropertyStr(js,e,"touchpad", number2js(js,event.gtouchpad.touchpad));
|
|
JS_SetPropertyStr(js,e,"finger", number2js(js,event.gtouchpad.finger));
|
|
JS_SetPropertyStr(js,e,"pos", vec22js(js,(HMM_Vec2){event.gtouchpad.x,event.gtouchpad.y}));
|
|
JS_SetPropertyStr(js,e,"pressure", number2js(js,event.gtouchpad.pressure));
|
|
break;
|
|
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
|
JS_SetPropertyStr(js,e,"which", number2js(js,event.gsensor.which));
|
|
JS_SetPropertyStr(js,e,"sensor", number2js(js,event.gsensor.sensor));
|
|
JS_SetPropertyStr(js,e,"sensor_timestamp", number2js(js,event.gsensor.sensor_timestamp));
|
|
break;
|
|
case SDL_EVENT_USER:
|
|
JS_SetPropertyStr(js,e,"cb", JS_DupValue(js,*(JSValue*)event.user.data1));
|
|
JS_FreeValue(js,*(JSValue*)event.user.data1);
|
|
free(event.user.data1);
|
|
event.user.data1 = NULL;
|
|
break;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void gui_input(SDL_Event *e);
|
|
// Polls and handles all input events
|
|
JSC_CCALL(game_engine_input,
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
#ifndef NEDITOR
|
|
gui_input(&event);
|
|
#endif
|
|
JSValue e = event2js(js,event);
|
|
JSValue ret = JS_Call(js,argv[0], JS_UNDEFINED, 1, &e);
|
|
js_print_exception(js,ret);
|
|
JS_FreeValue(js,ret);
|
|
}
|
|
)
|
|
|
|
JSC_CCALL(game_cameras,
|
|
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(game_open_camera,
|
|
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);
|
|
)
|
|
|
|
#include "wildmatch.h"
|
|
JSC_SSCALL(game_glob,
|
|
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(game_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(game_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_game_funcs[] = {
|
|
MIST_FUNC_DEF(game, engine_start, 1),
|
|
MIST_FUNC_DEF(game, engine_input,1),
|
|
MIST_FUNC_DEF(game, cameras, 0),
|
|
MIST_FUNC_DEF(game, open_camera, 1),
|
|
MIST_FUNC_DEF(game, camera_name,1),
|
|
MIST_FUNC_DEF(game, camera_position,1),
|
|
MIST_FUNC_DEF(game, glob, 2),
|
|
};
|
|
|
|
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_SCALL(SDL_Window_make_gpu,
|
|
SDL_Window *win = js2SDL_Window(js,self);
|
|
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("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);
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
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]));
|
|
|
|
}
|
|
|
|
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);
|
|
)
|
|
|
|
const char *tn = "present thread";
|
|
static int present_thread(SDL_Renderer *r)
|
|
{
|
|
TracyCZone(present,1)
|
|
SDL_RenderPresent(r);
|
|
TracyCZoneEnd(present)
|
|
return 0;
|
|
}
|
|
|
|
JSC_CCALL(SDL_Renderer_present,
|
|
// SDL_Thread *thread;
|
|
SDL_Renderer *ren = js2SDL_Renderer(js,self);
|
|
// thread = SDL_CreateThread(present_thread, "present", ren);
|
|
SDL_RenderPresent(ren);
|
|
// return SDL_Thread2js(js,thread);
|
|
)
|
|
|
|
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);
|
|
)
|
|
|
|
JSC_CCALL(SDL_Renderer_rect,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
if (!JS_IsUndefined(argv[1])) {
|
|
colorf color = js2color(js,argv[1]);
|
|
SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a);
|
|
}
|
|
|
|
if (JS_IsArray(js,argv[0])) {
|
|
int len = js_arrlen(js,argv[0]);
|
|
rect rects[len];
|
|
for (int i = 0; i < len; i++) {
|
|
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
|
rects[i] = transform_rect(r,js2rect(js,val), &cam_mat);
|
|
JS_FreeValue(js,val);
|
|
}
|
|
SDL_RenderRects(r,rects,len);
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
rect rect = js2rect(js,argv[0]);
|
|
|
|
rect = transform_rect(r,rect, &cam_mat);
|
|
|
|
SDL_RenderRect(r, &rect);
|
|
)
|
|
|
|
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_atom, number2js(js,tex->w));
|
|
JS_SetProperty(js,ret,height_atom, number2js(js,tex->h));
|
|
)
|
|
|
|
JSC_CCALL(SDL_Renderer_fillrect,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
if (!JS_IsUndefined(argv[1])) {
|
|
colorf color = js2color(js,argv[1]);
|
|
SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a);
|
|
}
|
|
|
|
if (JS_IsArray(js,argv[0])) {
|
|
int len = js_arrlen(js,argv[0]);
|
|
rect rects[len];
|
|
for (int i = 0; i < len; i++) {
|
|
JSValue val = JS_GetPropertyUint32(js,argv[0],i);
|
|
rects[i] = js2rect(js,val);
|
|
JS_FreeValue(js,val);
|
|
}
|
|
if (!SDL_RenderFillRects(r,rects,len))
|
|
return JS_ThrowReferenceError("Could not render rectangle: %s", SDL_GetError());
|
|
}
|
|
rect rect = transform_rect(r,js2rect(js,argv[0]),&cam_mat);
|
|
|
|
if (!SDL_RenderFillRect(r, &rect))
|
|
return JS_ThrowReferenceError("Could not render rectangle: %s", SDL_GetError());
|
|
)
|
|
|
|
JSC_CCALL(renderer_texture,
|
|
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
|
|
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
|
|
rect dst = transform_rect(renderer,js2rect(js,argv[1]), &cam_mat);
|
|
|
|
if (!JS_IsUndefined(argv[3])) {
|
|
colorf color = js2color(js,argv[3]);
|
|
SDL_SetTextureColorModFloat(tex, color.r, color.g, color.b);
|
|
SDL_SetTextureAlphaModFloat(tex,color.a);
|
|
}
|
|
if (JS_IsUndefined(argv[2]))
|
|
SDL_RenderTexture(renderer,tex,NULL,&dst);
|
|
else {
|
|
|
|
rect src = js2rect(js,argv[2]);
|
|
|
|
SDL_RenderTextureRotated(renderer, tex, &src, &dst, 0, NULL, SDL_FLIP_NONE);
|
|
}
|
|
)
|
|
|
|
JSC_CCALL(renderer_tile,
|
|
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
|
|
if (!renderer) return JS_ThrowTypeError(js,"self was not a renderer");
|
|
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
|
|
if (!tex) return JS_ThrowTypeError(js,"first argument was not a texture");
|
|
rect dst = js2rect(js,argv[1]);
|
|
if (!dst.w) dst.w = tex->w;
|
|
if (!dst.h) dst.h = tex->h;
|
|
float scale = js2number(js,argv[3]);
|
|
if (!scale) scale = 1;
|
|
if (JS_IsUndefined(argv[2]))
|
|
SDL_RenderTextureTiled(renderer,tex,NULL,scale, &dst);
|
|
else {
|
|
rect src = js2rect(js,argv[2]);
|
|
SDL_RenderTextureTiled(renderer,tex,&src,scale, &dst);
|
|
}
|
|
)
|
|
|
|
JSC_CCALL(renderer_slice9,
|
|
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
|
|
SDL_Texture *tex = js2SDL_Texture(js,argv[0]);
|
|
lrtb bounds = js2lrtb(js,argv[2]);
|
|
rect src, dst;
|
|
src = transform_rect(renderer,js2rect(js,argv[3]),&cam_mat);
|
|
dst = transform_rect(renderer,js2rect(js,argv[1]), &cam_mat);
|
|
|
|
SDL_RenderTexture9Grid(renderer, tex,
|
|
JS_IsUndefined(argv[3]) ? NULL : &src,
|
|
bounds.l, bounds.r, bounds.t, bounds.b, 0.0,
|
|
JS_IsUndefined(argv[1]) ? NULL : &dst);
|
|
)
|
|
|
|
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_SCALL(renderer_fasttext,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
if (!JS_IsUndefined(argv[2])) {
|
|
colorf color = js2color(js,argv[2]);
|
|
SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a);
|
|
}
|
|
HMM_Vec2 pos = js2vec2(js,argv[1]);
|
|
pos.y += 8;
|
|
HMM_Vec2 tpos = HMM_MulM3V3(cam_mat, (HMM_Vec3){pos.x,pos.y,1}).xy;
|
|
SDL_RenderDebugText(r, tpos.x, tpos.y, str);
|
|
)
|
|
|
|
JSC_CCALL(renderer_line,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
if (!JS_IsUndefined(argv[1])) {
|
|
colorf color = js2color(js,argv[1]);
|
|
SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a);
|
|
}
|
|
|
|
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_IsUndefined(argv[1])) {
|
|
colorf color = js2color(js,argv[1]);
|
|
SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a);
|
|
}
|
|
|
|
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_get_viewport,
|
|
SDL_Renderer *r = js2SDL_Renderer(js,self);
|
|
SDL_Rect vp;
|
|
SDL_GetRenderViewport(r, &vp);
|
|
rect re;
|
|
re.x = vp.x;
|
|
re.y = vp.y;
|
|
re.h = vp.h;
|
|
re.w = vp.w;
|
|
return rect2js(js,re);
|
|
)
|
|
|
|
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);
|
|
}
|
|
)
|
|
|
|
static void generate_normals(float* positions, size_t vertex_count, uint16_t* indices, size_t index_count, int16_t* normal_data) {
|
|
// Initialize normals to zero
|
|
float* temp_normals = malloc(sizeof(float)*3*vertex_count);
|
|
for (size_t i = 0; i < vertex_count*3; i++) {
|
|
temp_normals[i] = 0.0f;
|
|
}
|
|
|
|
// Compute face normals and accumulate
|
|
for (size_t i = 0; i < index_count; i += 3) {
|
|
uint16_t i0 = indices[i+0];
|
|
uint16_t i1 = indices[i+1];
|
|
uint16_t i2 = indices[i+2];
|
|
|
|
float* p0 = &positions[i0*3];
|
|
float* p1 = &positions[i1*3];
|
|
float* p2 = &positions[i2*3];
|
|
|
|
float ux = p1[0] - p0[0];
|
|
float uy = p1[1] - p0[1];
|
|
float uz = p1[2] - p0[2];
|
|
|
|
float vx = p2[0] - p0[0];
|
|
float vy = p2[1] - p0[1];
|
|
float vz = p2[2] - p0[2];
|
|
|
|
// Cross product (u x v)
|
|
float nx = uy*vz - uz*vy;
|
|
float ny = uz*vx - ux*vz;
|
|
float nz = ux*vy - uy*vx;
|
|
|
|
// Accumulate to each vertex normal
|
|
temp_normals[i0*3+0] += nx; temp_normals[i0*3+1] += ny; temp_normals[i0*3+2] += nz;
|
|
temp_normals[i1*3+0] += nx; temp_normals[i1*3+1] += ny; temp_normals[i1*3+2] += nz;
|
|
temp_normals[i2*3+0] += nx; temp_normals[i2*3+1] += ny; temp_normals[i2*3+2] += nz;
|
|
}
|
|
|
|
// Normalize and pack
|
|
for (size_t i = 0; i < vertex_count; i++) {
|
|
float x = temp_normals[i*3+0];
|
|
float y = temp_normals[i*3+1];
|
|
float z = temp_normals[i*3+2];
|
|
float length = sqrtf(x*x + y*y + z*z);
|
|
if (length > 1e-6f) {
|
|
x /= length; y /= length; z /= length;
|
|
} else {
|
|
x = 0; y = 0; z = 1; // arbitrary default
|
|
}
|
|
|
|
normal_data[i*3+0] = (int16_t)(x * 32767.0f);
|
|
normal_data[i*3+1] = (int16_t)(y * 32767.0f);
|
|
normal_data[i*3+2] = (int16_t)(z * 32767.0f);
|
|
}
|
|
|
|
free(temp_normals);
|
|
}
|
|
|
|
// 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];
|
|
JSValue old = argv[1];
|
|
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_atom);
|
|
transform *tr = js2transform(js,jstransform);
|
|
|
|
JSValue jssrc = JS_GetProperty(js,sub,src_atom);
|
|
JSValue jscolor = JS_GetProperty(js,sub,color_atom);
|
|
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;
|
|
|
|
|
|
HMM_Mat3 trmat = tr->gcache3;
|
|
|
|
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}
|
|
};
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
|
|
|
// 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_atom, make_gpu_buffer(js, posdata, sizeof(*posdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
|
|
JS_SetProperty(js, ret, uv_atom, make_gpu_buffer(js, uvdata, sizeof(*uvdata) * verts, JS_TYPED_ARRAY_FLOAT32, 2, 0,0));
|
|
JS_SetProperty(js, ret, color_atom, make_gpu_buffer(js, colordata, sizeof(*colordata) * verts, JS_TYPED_ARRAY_FLOAT32, 4, 0,0));
|
|
JS_SetProperty(js, ret, indices_atom, make_quad_indices_buffer(js, quads));
|
|
JS_SetProperty(js, ret, vertices_atom, number2js(js, verts));
|
|
JS_SetProperty(js, ret, count_atom, 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(SDL_Renderer, rect, 2),
|
|
MIST_FUNC_DEF(SDL_Renderer, fillrect, 2),
|
|
MIST_FUNC_DEF(renderer, line, 2),
|
|
MIST_FUNC_DEF(renderer, point, 2),
|
|
MIST_FUNC_DEF(renderer, load_texture, 1),
|
|
MIST_FUNC_DEF(renderer, texture, 4),
|
|
MIST_FUNC_DEF(renderer, slice9, 4),
|
|
MIST_FUNC_DEF(renderer, tile, 4),
|
|
MIST_FUNC_DEF(renderer, get_image, 1),
|
|
MIST_FUNC_DEF(renderer, fasttext, 2),
|
|
MIST_FUNC_DEF(renderer, geometry, 2),
|
|
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, get_viewport,0),
|
|
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("Could not set: %s\n", SDL_GetError());
|
|
)
|
|
|
|
static JSValue *js_swapchains;
|
|
|
|
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;
|
|
|
|
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_atom, number2js(js, surf->w));
|
|
JS_SetProperty(js, ret, height_atom, number2js(js, 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;
|
|
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");
|
|
tmp;
|
|
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("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));
|
|
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 HMM_Vec4 base_quad_4[4] = {
|
|
{ 0.0,0.0, 1.0f, 1.0f },
|
|
{ 1,0,0.0, 1.0f, 1.0f },
|
|
{ 0.0,1.0, 1.0f, 1.0f },
|
|
{ 1.0,1.0, 1.0f, 1.0f }
|
|
};
|
|
|
|
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;
|
|
|
|
typedef struct {
|
|
quad quad;
|
|
JSValue image;
|
|
int layer;
|
|
JSContext *js;
|
|
JSValue sprite;
|
|
} sprite;
|
|
|
|
int sort_sprite(const sprite *a, const sprite *b)
|
|
{
|
|
if (a->layer != b->layer) return a->layer - b->layer;
|
|
|
|
if (a->quad.vert[0].pos.y != b->quad.vert[0].pos.y) return b->quad.vert[0].pos.y - a->quad.vert[0].pos.y;
|
|
|
|
if (!JS_SameValue(a->js, a->image, b->image)) return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -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_atom,number)
|
|
JS_GETATOM(js,blayer,b,layer_atom,number)
|
|
if (alayer != blayer) return number2js(js,alayer - blayer);
|
|
|
|
transform *atr, *btr;
|
|
JS_GETATOM(js,atr,a,transform_atom,transform)
|
|
JS_GETATOM(js,btr,b,transform_atom,transform);
|
|
HMM_Mat3 am3 = transform2mat3_global(atr);
|
|
HMM_Mat3 bm3 = transform2mat3_global(btr);
|
|
if (am3.Columns[2].y != bm3.Columns[2].y) return number2js(js,bm3.Columns[2].y - am3.Columns[2].y);
|
|
|
|
JSValue aimg,bimg;
|
|
aimg = JS_GetProperty(js,a,image_atom);
|
|
bimg = JS_GetProperty(js,b,image_atom);
|
|
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);
|
|
)
|
|
|
|
inline int sprite_in_view(HMM_Mat4 sprite, HMM_Mat4 camera)
|
|
{
|
|
HMM_Mat4 camsprite = HMM_MulM4(camera, sprite);
|
|
int outside = 0;
|
|
for (int j = 0; j < 4; j++) {
|
|
HMM_Vec4 clip = HMM_MulM4V4_P(&camsprite, &base_quad_4[j]);
|
|
|
|
if (clip.w <= 0.0) {
|
|
outside++;
|
|
continue;
|
|
}
|
|
|
|
float nx = clip.x/clip.w;
|
|
float ny = clip.y/clip.w;
|
|
float nz = clip.z/clip.w;
|
|
|
|
if (nx < -1 || nx > 1) outside++;
|
|
else if (ny < -1 || ny > 1) outside++;
|
|
else if (nz < -1 || nz > 1) outside++;
|
|
}
|
|
|
|
return outside != 4;
|
|
}
|
|
|
|
JSC_CCALL(gpu_make_sprite_queue,
|
|
size_t quads = js_arrlen(js, argv[0]);
|
|
|
|
sprite *sprites = NULL;
|
|
arrsetcap(sprites, quads);
|
|
|
|
for (int i = 0; i < quads; i++) {
|
|
JSValue sub = JS_GetPropertyUint32(js, argv[0], i);
|
|
|
|
rect src;
|
|
HMM_Vec4 color;
|
|
rect pr;
|
|
|
|
JS_GETATOM(js,pr,sub,rect_atom,rect);
|
|
JS_GETATOM(js, src, sub, src_atom, rect)
|
|
JS_GETATOM(js, color, sub, color_atom, color)
|
|
|
|
// Profile: build quad
|
|
quad sprite_quad;
|
|
sprite_quad.vert[0].pos = (HMM_Vec2){pr.x, pr.y};
|
|
sprite_quad.vert[1].pos = (HMM_Vec2){pr.x+pr.w, pr.y};
|
|
sprite_quad.vert[2].pos = (HMM_Vec2){pr.x, pr.y+pr.h};
|
|
sprite_quad.vert[3].pos = (HMM_Vec2){pr.x+pr.w, pr.y+pr.h};
|
|
|
|
sprite_quad.vert[0].uv = (HMM_Vec2){ src.x, src.y + src.h };
|
|
sprite_quad.vert[1].uv = (HMM_Vec2){ src.x+src.w, src.y + src.h };
|
|
sprite_quad.vert[2].uv = (HMM_Vec2){ src.x, src.y };
|
|
sprite_quad.vert[3].uv = (HMM_Vec2){ src.x+src.w, src.y };
|
|
|
|
sprite_quad.vert[0].color = color;
|
|
sprite_quad.vert[1].color = color;
|
|
sprite_quad.vert[2].color = color;
|
|
sprite_quad.vert[3].color = color;
|
|
|
|
sprite sp;
|
|
sp.quad = sprite_quad;
|
|
sp.image = JS_GetProperty(js, sub, image_atom);
|
|
sp.js = js;
|
|
JS_GETATOM(js, sp.layer, sub, layer_atom, number)
|
|
arrput(sprites, sp);
|
|
|
|
JS_FreeValue(js, sub);
|
|
}
|
|
|
|
qsort(sprites, arrlen(sprites), sizeof(sprite), sort_sprite);
|
|
|
|
text_vert *buffer = NULL;
|
|
arrsetcap(buffer, arrlen(sprites)*4);
|
|
for (int i = 0; i < arrlen(sprites); i++)
|
|
for (int j = 0; j < 4; j++)
|
|
arrput(buffer, sprites[i].quad.vert[j]);
|
|
|
|
JSValue mesh = quads_to_mesh(js, buffer);
|
|
|
|
arrfree(buffer);
|
|
|
|
ret = JS_NewArray(js);
|
|
int first_index = 0;
|
|
int count = 0;
|
|
int n = 0;
|
|
JSValue img = JS_UNDEFINED;
|
|
|
|
for (int i = 0; i < arrlen(sprites); 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", JS_DupValue(js, 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;
|
|
JS_FreeValue(js, img);
|
|
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", JS_DupValue(js, 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);
|
|
}
|
|
|
|
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_atom,rect)
|
|
JS_GETATOM(js,color,sub,color_atom,color)
|
|
JS_GETATOM(js,tr,sub,transform_atom,transform)
|
|
JS_FreeValue(js,sub);
|
|
|
|
size_t base = i*4;
|
|
HMM_Mat3 trmat = tr->gcache3;
|
|
// HMM_Mat3 trmat = transform2mat3(tr);
|
|
for (int j = 0; j < 4; j++)
|
|
posdata[base+j] = HMM_MulM3V3(trmat, base_quad[j]).xy;
|
|
|
|
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_atom, pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0);
|
|
BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, uv_atom, uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0);
|
|
BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, color_atom, 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_atom, new_pos);
|
|
JS_SetProperty(js, ret, uv_atom, new_uv);
|
|
JS_SetProperty(js, ret, color_atom, new_color);
|
|
|
|
// Indices
|
|
JSValue indices = make_quad_indices_buffer(js, quads);
|
|
JS_SetProperty(js, ret, indices_atom, 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_atom, JS_DupValue(js, pos_chk.val));
|
|
JS_SetProperty(js, ret, uv_atom, JS_DupValue(js, uv_chk.val));
|
|
JS_SetProperty(js, ret, color_atom, 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_atom, indices);
|
|
}
|
|
|
|
JS_SetProperty(js, ret, vertices_atom, number2js(js, verts));
|
|
JS_SetProperty(js, ret, count_atom, 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_atom, pos);
|
|
JS_SetProperty(js, ret, uv_atom, uv);
|
|
JS_SetProperty(js, ret, color_atom, color);
|
|
JSValue indices = make_quad_indices_buffer(js, quads);
|
|
JS_SetProperty(js, ret, indices_atom, indices);
|
|
|
|
JS_SetProperty(js, ret, vertices_atom, number2js(js, verts));
|
|
JS_SetProperty(js, ret, count_atom, 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];
|
|
|
|
JSValue jsfile = JS_GetPropertyStr(js,obj,"file");
|
|
const char *file = JS_ToCString(js,jsfile);
|
|
JS_FreeValue(js,jsfile);
|
|
|
|
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);
|
|
SDL_GPUShaderStage stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
|
if (stage_str) {
|
|
if (!strcmp(stage_str, "vertex")) stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
|
else if (!strcmp(stage_str, "fragment")) stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
|
JS_FreeCString(js, stage_str);
|
|
}
|
|
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) {
|
|
printf("New transfer buffer needed of size %d, old was %d\n", total_size_needed, transfer_size);
|
|
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(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_GETPROP(js,size.x,jstex,width,number)
|
|
JS_GETPROP(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_sprite_mesh, 2),
|
|
MIST_FUNC_DEF(gpu, make_sprite_queue, 3),
|
|
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),
|
|
MIST_FUNC_DEF(gpu, slice9, 3),
|
|
MIST_FUNC_DEF(gpu, tile, 4),
|
|
};
|
|
|
|
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(copypass_end,
|
|
SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self);
|
|
SDL_EndGPUCopyPass(pass);
|
|
)
|
|
|
|
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_GETPROPSTR(js, depthval, depthtar, 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,
|
|
Uint64 start = SDL_GetTicksNS();
|
|
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(thread_wait,
|
|
SDL_Thread *th = js2SDL_Thread(js,self);
|
|
SDL_WaitThread(th, NULL);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_Thread_funcs[] = {
|
|
MIST_FUNC_DEF(thread,wait,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),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_SDL_Cursor_funcs[] = {};
|
|
|
|
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]);
|
|
printf("setting cursor to %p\n", c);
|
|
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);
|
|
)
|
|
|
|
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),
|
|
};
|
|
|
|
JSC_CCALL(prosperon_guid,
|
|
char guid[33];
|
|
for (int i = 0; i < 4; i++) {
|
|
int r = rand();
|
|
for (int j = 0; j < 8; j++) {
|
|
guid[i*8+j] = "0123456789abcdef"[r%16];
|
|
r /= 16;
|
|
}
|
|
}
|
|
|
|
guid[32] = 0;
|
|
return JS_NewString(js,guid);
|
|
)
|
|
|
|
JSC_SCALL(prosperon_openurl,
|
|
if (!SDL_OpenURL(str))
|
|
ret = JS_ThrowReferenceError(js, "unable to open url %s: %s\n", str, SDL_GetError());
|
|
)
|
|
|
|
JSC_CCALL(prosperon_push_event,
|
|
SDL_UserEvent e;
|
|
SDL_zero(e);
|
|
e.type = SDL_EVENT_USER;
|
|
e.timestamp = SDL_GetTicksNS();
|
|
e.code = 0;
|
|
JSValue fn = JS_DupValue(js,argv[0]);
|
|
e.data1 = malloc(sizeof(JSValue));
|
|
*(JSValue*)e.data1 = fn;
|
|
SDL_PushEvent(&e);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_prosperon_funcs[] = {
|
|
MIST_FUNC_DEF(prosperon, guid, 0),
|
|
MIST_FUNC_DEF(prosperon, openurl, 1),
|
|
MIST_FUNC_DEF(prosperon, push_event, 1),
|
|
};
|
|
|
|
JSC_CCALL(time_now,
|
|
struct timeval ct;
|
|
gettimeofday(&ct, NULL);
|
|
return number2js(js,(double)ct.tv_sec+(double)(ct.tv_usec/1000000.0));
|
|
)
|
|
|
|
JSValue js_time_computer_dst(JSContext *js, JSValue self) {
|
|
time_t t = time(NULL);
|
|
return JS_NewBool(js,localtime(&t)->tm_isdst);
|
|
}
|
|
|
|
JSValue js_time_computer_zone(JSContext *js, JSValue self) {
|
|
time_t t = time(NULL);
|
|
time_t local_t = mktime(localtime(&t));
|
|
double diff = difftime(t, local_t);
|
|
return number2js(js,diff/3600);
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_time_funcs[] = {
|
|
MIST_FUNC_DEF(time, now, 0),
|
|
MIST_FUNC_DEF(time, computer_dst, 0),
|
|
MIST_FUNC_DEF(time, computer_zone, 0)
|
|
};
|
|
|
|
JSC_SCALL(console_log,
|
|
printf("%s\n", str);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_console_funcs[] = {
|
|
MIST_FUNC_DEF(console,log,1),
|
|
};
|
|
|
|
static JSValue instr_v = JS_UNDEFINED;
|
|
int iiihandle(JSRuntime *rt, void *data)
|
|
{
|
|
script_call_sym(instr_v, 0, NULL);
|
|
return 0;
|
|
}
|
|
|
|
JSC_CCALL(profile_gather,
|
|
instr_v = JS_DupValue(js, argv[1]);
|
|
JS_SetInterruptHandler(JS_GetRuntime(js), iiihandle, NULL);
|
|
)
|
|
|
|
JSC_CCALL(profile_gather_rate,
|
|
JS_SetInterruptRate(js2number(js,argv[0]));
|
|
)
|
|
|
|
JSC_CCALL(profile_gather_stop,
|
|
JS_SetInterruptHandler(JS_GetRuntime(js),NULL,NULL);
|
|
)
|
|
|
|
JSC_CCALL(profile_best_t,
|
|
char* result[50];
|
|
double seconds = js2number(js,argv[0]);
|
|
if (seconds < 1e-6)
|
|
snprintf(result, 50, "%.2f ns", seconds * 1e9);
|
|
else if (seconds < 1e-3)
|
|
snprintf(result, 50, "%.2f µs", seconds * 1e6);
|
|
else if (seconds < 1)
|
|
snprintf(result, 50, "%.2f ms", seconds * 1e3);
|
|
else
|
|
snprintf(result, 50, "%.2f s", seconds);
|
|
|
|
return JS_NewString(js,result);
|
|
)
|
|
|
|
JSC_CCALL(profile_now, return number2js(js, SDL_GetTicksNS()/1000000000.f))
|
|
|
|
static const JSCFunctionListEntry js_profile_funcs[] = {
|
|
MIST_FUNC_DEF(profile,now,0),
|
|
MIST_FUNC_DEF(profile,best_t, 1),
|
|
MIST_FUNC_DEF(profile,gather,2),
|
|
MIST_FUNC_DEF(profile,gather_rate,1),
|
|
MIST_FUNC_DEF(profile,gather_stop,0),
|
|
};
|
|
|
|
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,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
)
|
|
JSC_SCALL(io_mkdir,
|
|
if (!PHYSFS_mkdir(str)) ret = JS_ThrowReferenceError(js,"%s", 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_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);
|
|
if (JS_ToBool(js,argv[1]))
|
|
ret = JS_NewStringLen(js,data, stat.filesize);
|
|
else
|
|
ret = JS_NewArrayBufferCopy(js,data,stat.filesize);
|
|
|
|
END:
|
|
)
|
|
|
|
JSC_SCALL(io_slurpwrite,
|
|
PHYSFS_File *f = PHYSFS_openWrite(str);
|
|
if (!f) {
|
|
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
goto END;
|
|
}
|
|
size_t len;
|
|
unsigned char *data;
|
|
if (JS_IsString(argv[1]))
|
|
data = JS_ToCStringLen(js,&len,argv[1]);
|
|
else
|
|
data = JS_GetArrayBuffer(js,&len, argv[1]);
|
|
|
|
size_t wrote = PHYSFS_writeBytes(f,data, len);
|
|
PHYSFS_close(f);
|
|
if (wrote == -1 || wrote < len)
|
|
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
|
|
END:
|
|
)
|
|
|
|
JSC_SCALL(io_mount,
|
|
if (!PHYSFS_mount(str,NULL,0)) ret = JS_ThrowReferenceError(js,"%s", 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()));
|
|
)
|
|
|
|
JSC_SSCALL(io_gamemode,
|
|
const char *prefdir = PHYSFS_getPrefDir(str, str2);
|
|
printf("%s\n", prefdir);
|
|
PHYSFS_setWriteDir(prefdir);
|
|
)
|
|
|
|
struct globdata {
|
|
JSContext *js;
|
|
JSValue arr;
|
|
char **globs;
|
|
int idx;
|
|
};
|
|
|
|
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_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]);
|
|
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;
|
|
|
|
PHYSFS_enumerate("", globfs_cb, &data);
|
|
|
|
return data.arr;
|
|
)
|
|
|
|
JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir()))
|
|
|
|
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, 1),
|
|
MIST_FUNC_DEF(io, exists, 1),
|
|
MIST_FUNC_DEF(io, mount, 1),
|
|
MIST_FUNC_DEF(io,unmount,1),
|
|
MIST_FUNC_DEF(io,slurp,2),
|
|
MIST_FUNC_DEF(io,slurpwrite,2),
|
|
MIST_FUNC_DEF(io,writepath, 1),
|
|
MIST_FUNC_DEF(io,basedir, 0),
|
|
MIST_FUNC_DEF(io, gamemode, 2),
|
|
};
|
|
|
|
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);
|
|
go->dirty = true;
|
|
)
|
|
|
|
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);
|
|
t->parent = NULL;
|
|
)
|
|
|
|
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_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 *t = js2transform(js,self);
|
|
if (t->parent)
|
|
JS_FreeValue(js,t->jsparent);
|
|
|
|
transform *p = js2transform(js,v);
|
|
t->parent = p;
|
|
if (p)
|
|
t->jsparent = JS_DupValue(js,v);
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
JSC_CCALL(transform_clean,
|
|
transform2mat3_global(js2transform(js,self));
|
|
)
|
|
|
|
JSC_CCALL(transform_dirty,
|
|
return JS_NewBool(js,transform_dirty_chain(js2transform(js,self)));
|
|
)
|
|
|
|
JSC_CCALL(transform_torect,
|
|
transform *t = js2transform(js,self);
|
|
return rect2js(js,transform2rect(t));
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_transform_funcs[] = {
|
|
CGETSET_ADD(transform, pos),
|
|
CGETSET_ADD(transform, scale),
|
|
CGETSET_ADD(transform, rotation),
|
|
CGETSET_ADD(transform, parent),
|
|
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, clean, 0),
|
|
MIST_FUNC_DEF(transform, dirty, 0),
|
|
MIST_FUNC_DEF(transform, torect, 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_CALLBACK(timer, fn)
|
|
JSC_GETSET(timer, remain, number)
|
|
|
|
static const JSCFunctionListEntry js_timer_funcs[] = {
|
|
CGETSET_ADD(timer, remain),
|
|
CGETSET_ADD(timer, fn),
|
|
};
|
|
|
|
JSC_GETSET(font, linegap, number)
|
|
JSC_GET(font, height, number)
|
|
JSC_GET(font, ascent, number)
|
|
JSC_GET(font, descent, number)
|
|
|
|
static const JSCFunctionListEntry js_font_funcs[] = {
|
|
CGETSET_ADD(font, linegap),
|
|
MIST_GET(font, height),
|
|
MIST_GET(font, ascent),
|
|
MIST_GET(font, descent)
|
|
};
|
|
|
|
const char *STRTEST = "TEST STRING";
|
|
|
|
JSC_CCALL(performance_barecall,)
|
|
JSC_CCALL(performance_unpack_array,
|
|
void *v = js2cpvec2arr(js,argv[0]);
|
|
arrfree(v);
|
|
)
|
|
JSC_CCALL(performance_pack_num, return number2js(js,1.0))
|
|
JSC_CCALL(performance_pack_string, return JS_NewStringLen(js, STRTEST, sizeof(*STRTEST)))
|
|
JSC_CCALL(performance_unpack_string, JS_ToCString(js, argv[0]))
|
|
JSC_CCALL(performance_call_fn_n,
|
|
for (int i = 0; i < js2number(js,argv[1]); i++)
|
|
script_call_sym(argv[0],0,NULL);
|
|
script_call_sym(argv[2],0,NULL);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_performance_funcs[] = {
|
|
MIST_FUNC_DEF(performance, barecall,0),
|
|
MIST_FUNC_DEF(performance, unpack_array, 1),
|
|
MIST_FUNC_DEF(performance, pack_num, 0),
|
|
MIST_FUNC_DEF(performance, pack_string, 0),
|
|
MIST_FUNC_DEF(performance, unpack_string, 1),
|
|
MIST_FUNC_DEF(performance, call_fn_n, 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(-0.5,0.5)*a.w,
|
|
a.y + rand_range(-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),
|
|
};
|
|
|
|
JSValue js_os_cwd(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
char cwd[PATH_MAX];
|
|
#ifndef __EMSCRIPTEN__
|
|
getcwd(cwd, sizeof(cwd));
|
|
#else
|
|
cwd[0] = '.';
|
|
cwd[1] = 0;
|
|
#endif
|
|
return JS_NewString(js,cwd);
|
|
}
|
|
|
|
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,
|
|
return JS_RunGC(JS_GetRuntime(js), js)
|
|
)
|
|
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))
|
|
|
|
static JSValue tmp2js(JSContext *js,FILE *tmp)
|
|
{
|
|
size_t size = ftell(tmp);
|
|
rewind(tmp);
|
|
char *buffer = calloc(size+1, sizeof(char));
|
|
fread(buffer, sizeof(char),size, tmp);
|
|
JSValue ret = JS_NewString(js,buffer);
|
|
free(buffer);
|
|
return ret;
|
|
}
|
|
|
|
JSC_CCALL(os_dump_atoms,
|
|
FILE *tmp = tmpfile();
|
|
quickjs_set_dumpout(tmp);
|
|
JS_PrintAtoms(JS_GetRuntime(js));
|
|
ret = tmp2js(js,tmp);
|
|
)
|
|
|
|
JSC_CCALL(os_dump_shapes,
|
|
FILE *tmp = tmpfile();
|
|
quickjs_set_dumpout(tmp);
|
|
JS_PrintShapes(JS_GetRuntime(js));
|
|
size_t size = ftell(tmp);
|
|
rewind(tmp);
|
|
char buffer[size];
|
|
fgets(buffer, sizeof(char)*size, tmp);
|
|
fclose(tmp);
|
|
ret = JS_NewString(js,buffer);
|
|
)
|
|
|
|
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)
|
|
)
|
|
|
|
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);*/
|
|
)
|
|
|
|
JSC_CCALL(os_rusage,
|
|
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
|
|
)
|
|
|
|
JSC_CCALL(os_mem,
|
|
JSMemoryUsage jsmem;
|
|
JS_ComputeMemoryUsage(JS_GetRuntime(js), &jsmem);
|
|
ret = JS_NewObject(js);
|
|
JSJMEMRET(malloc_size)
|
|
JSJMEMRET(malloc_limit)
|
|
JSJMEMRET(memory_used_size)
|
|
JSJMEMRET(memory_used_count)
|
|
JSJMEMRET(atom_count)
|
|
JSJMEMRET(atom_size)
|
|
JSJMEMRET(str_count)
|
|
JSJMEMRET(str_size)
|
|
JSJMEMRET(obj_count)
|
|
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_dump_mem,
|
|
FILE *tmp = tmpfile();
|
|
JSMemoryUsage mem = {0};
|
|
JS_DumpMemoryUsage(tmp, &mem, JS_GetRuntime(js));
|
|
ret = tmp2js(js,tmp);
|
|
)
|
|
|
|
JSC_CCALL(os_value_id,
|
|
return number2js(js,(intptr_t)JS_VALUE_GET_PTR(argv[0]));
|
|
)
|
|
|
|
static double gc_t = 0;
|
|
static double gc_mem = 0;
|
|
static double gc_startmem = 0;
|
|
void script_report_gc_time(double t, double startmem, double mem)
|
|
{
|
|
gc_t = t;
|
|
gc_mem = mem;
|
|
gc_startmem = startmem;
|
|
}
|
|
|
|
static FILE *cycles;
|
|
|
|
JSC_CCALL(os_check_cycles,
|
|
if (ftell(cycles) == 0) return JS_UNDEFINED;
|
|
cycles = tmpfile();
|
|
quickjs_set_cycleout(cycles);
|
|
ret = tmp2js(js,cycles);
|
|
)
|
|
|
|
JSC_CCALL(os_check_gc,
|
|
if (gc_t == 0) return JS_UNDEFINED;
|
|
|
|
JSValue gc = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, gc, "time", number2js(js,gc_t));
|
|
JS_SetPropertyStr(js, gc, "mem", number2js(js,gc_mem));
|
|
JS_SetPropertyStr(js, gc, "startmem", number2js(js,gc_startmem));
|
|
gc_t = 0;
|
|
gc_mem = 0;
|
|
gc_startmem = 0;
|
|
return gc;
|
|
)
|
|
|
|
JSC_SSCALL(os_eval, ret = script_eval(str, str2))
|
|
JSC_CCALL(os_make_timer, return timer2js(js,timer_make(js,argv[0])))
|
|
JSC_CCALL(os_update_timers, timer_update(js, js2number(js,argv[0])))
|
|
|
|
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);
|
|
)
|
|
|
|
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;
|
|
}
|
|
|
|
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());
|
|
|
|
int w = ase->w;
|
|
int h = ase->h;
|
|
|
|
int pixels = w*h;
|
|
|
|
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,
|
|
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("couldn't make cursor: %s", SDL_GetError());
|
|
printf("made cursor %p\n", c);
|
|
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, return transform2js(js,make_transform()))
|
|
|
|
JSC_SCALL(os_system, return number2js(js,system(str)); )
|
|
|
|
JSC_SCALL(os_model_buffer,
|
|
/*
|
|
int mesh_idx = 0;
|
|
|
|
// Configure cgltf
|
|
cgltf_options options;
|
|
memset(&options, 0, sizeof(options));
|
|
|
|
// Parse the file
|
|
cgltf_data* data = NULL;
|
|
cgltf_result result = cgltf_parse_file(&options, str, &data);
|
|
if (result != cgltf_result_success) {
|
|
JS_ThrowInternalError(js, "Failed to load glTF model: parse error");
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// Load any external buffers (bin files, images)
|
|
result = cgltf_load_buffers(&options, data, str);
|
|
if (result != cgltf_result_success) {
|
|
cgltf_free(data);
|
|
JS_ThrowInternalError(js, "Failed to load glTF model: buffer load error");
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// We only check for mesh_idx vs. the count of cgltf_data->meshes
|
|
// (though note that glTF organizes data in scenes, nodes, etc.,
|
|
// so you might want to handle multiple nodes or node->mesh references)
|
|
if (mesh_idx < 0 || mesh_idx >= (int)data->meshes_count) {
|
|
cgltf_free(data);
|
|
JS_ThrowInternalError(js, "Invalid mesh index");
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
JSValue materials[data->materials_count];
|
|
for (int i = 0; i < (int)data->materials_count; i++) {
|
|
JSValue mat = JS_NewObject(js);
|
|
materials[i] = mat;
|
|
cgltf_material *cgmat = &data->materials[i];
|
|
|
|
// Grab the base color texture if it exists
|
|
cgltf_texture_view *bc_view = &cgmat->pbr_metallic_roughness.base_color_texture;
|
|
if (bc_view->texture && bc_view->texture->image) {
|
|
cgltf_image *img = bc_view->texture->image;
|
|
// If the image is an external URI
|
|
if (img->uri) {
|
|
// For glTF 2.0, this often points to a .png/.jpg
|
|
JS_SetPropertyStr(js, mat, "diffuse", JS_NewString(js, img->uri));
|
|
}
|
|
// If it's an embedded buffer view (e.g., "data:" or bufferView-based image)
|
|
else if (img->buffer_view) {
|
|
size_t size = img->buffer_view->size;
|
|
uint8_t *ptr = (uint8_t*)img->buffer_view->buffer->data + img->buffer_view->offset;
|
|
JS_SetPropertyStr(js, mat, "diffuse", JS_NewArrayBufferCopy(js, ptr, size));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create an array to hold all the "model" objects
|
|
JSValue ret_arr = JS_NewArray(js);
|
|
|
|
for (int m = 0; m < (int)data->meshes_count; m++) {
|
|
cgltf_mesh *cgmesh = &data->meshes[m];
|
|
// Go through each primitive in this mesh
|
|
for (int p = 0; p < (int)cgmesh->primitives_count; p++) {
|
|
cgltf_primitive *prim = &cgmesh->primitives[p];
|
|
if (!prim->attributes) {
|
|
// No attributes => no geometry
|
|
continue;
|
|
}
|
|
|
|
// We'll collect attribute data in arrays, just like with Assimp
|
|
float *posdata = NULL, *normdata = NULL, *uvdata = NULL, *colordata = NULL;
|
|
Uint16 *indicesdata = NULL;
|
|
size_t num_verts = 0;
|
|
size_t index_count = 0;
|
|
|
|
// Helper function to find an accessor by "POSITION", "NORMAL", "TEXCOORD_0", etc.
|
|
// We'll parse it below in parseAttributeFloat.
|
|
cgltf_accessor* findAccessor(cgltf_primitive* prim, const char* semantic) {
|
|
for (int a = 0; a < (int)prim->attributes_count; a++) {
|
|
if (prim->attributes[a].name && strcmp(prim->attributes[a].name, semantic) == 0)
|
|
return prim->attributes[a].data;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// parseAttributeFloat:
|
|
// read floats from a cgltf_accessor into outBuffer
|
|
// The 'stride' is how many floats we read per element (e.g. 3 for POSITION)
|
|
// Returns the number of elements read
|
|
size_t parseAttributeFloat(JSContext* js, cgltf_accessor* acc, float** outBuffer, int stride, float defaultVal) {
|
|
if (!acc) {
|
|
// If the attribute doesn't exist, fill default
|
|
size_t count = 0;
|
|
*outBuffer = NULL;
|
|
return count;
|
|
}
|
|
size_t count = acc->count;
|
|
size_t total_floats = count * stride;
|
|
*outBuffer = malloc(total_floats * sizeof(float));
|
|
if (!(*outBuffer)) return 0;
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
float tmp[4] = { defaultVal, defaultVal, defaultVal, defaultVal };
|
|
cgltf_accessor_read_float(acc, i, tmp, 4);
|
|
// copy only 'stride' components
|
|
for (int c = 0; c < stride; c++)
|
|
(*outBuffer)[i*stride + c] = tmp[c];
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// 1) POSITION
|
|
cgltf_accessor *accPos = findAccessor(prim, "POSITION");
|
|
num_verts = parseAttributeFloat(js, accPos, &posdata, 3, 0.0f);
|
|
|
|
// 2) NORMAL
|
|
cgltf_accessor *accNorm = findAccessor(prim, "NORMAL");
|
|
// If missing normals, default them to (0,0,1)
|
|
parseAttributeFloat(js, accNorm, &normdata, 3, 0.0f);
|
|
if (!normdata && num_verts > 0) {
|
|
normdata = malloc(num_verts*3*sizeof(float));
|
|
for (size_t i = 0; i < num_verts; i++) {
|
|
normdata[i*3+0] = 0.0f;
|
|
normdata[i*3+1] = 0.0f;
|
|
normdata[i*3+2] = 1.0f;
|
|
}
|
|
}
|
|
|
|
// 3) TEXCOORD_0
|
|
cgltf_accessor *accUV = findAccessor(prim, "TEXCOORD_0");
|
|
parseAttributeFloat(js, accUV, &uvdata, 2, 0.0f);
|
|
if (!uvdata && num_verts > 0) {
|
|
uvdata = malloc(num_verts*2*sizeof(float));
|
|
for (size_t i = 0; i < num_verts; i++) {
|
|
uvdata[i*2+0] = 0.0f;
|
|
uvdata[i*2+1] = 0.0f;
|
|
}
|
|
}
|
|
|
|
// 4) COLOR_0
|
|
cgltf_accessor *accColor = findAccessor(prim, "COLOR_0");
|
|
parseAttributeFloat(js, accColor, &colordata, 4, 1.0f);
|
|
if (!colordata && num_verts > 0) {
|
|
colordata = malloc(num_verts*4*sizeof(float));
|
|
for (size_t i = 0; i < num_verts; i++) {
|
|
colordata[i*4+0] = 1.0f;
|
|
colordata[i*4+1] = 1.0f;
|
|
colordata[i*4+2] = 1.0f;
|
|
colordata[i*4+3] = 1.0f;
|
|
}
|
|
}
|
|
|
|
// 5) Indices (if present)
|
|
if (prim->indices) {
|
|
cgltf_accessor *idxAcc = prim->indices;
|
|
index_count = idxAcc->count;
|
|
indicesdata = malloc(index_count*sizeof(Uint16));
|
|
if (indicesdata) {
|
|
for (size_t i = 0; i < index_count; i++) {
|
|
// cgltf_accessor_read_index can read the index as uint32_t
|
|
uint32_t val = 0;
|
|
val = cgltf_accessor_read_index(idxAcc, i);
|
|
// NOTE: if val > 65535, you'll need 32-bit index buffers
|
|
indicesdata[i] = (Uint16)val;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build a JS object for the mesh data
|
|
JSValue js_mesh = JS_NewObject(js);
|
|
|
|
// Positions
|
|
if (posdata && num_verts > 0)
|
|
JS_SetProperty(js, js_mesh, pos_atom,
|
|
make_gpu_buffer(js, posdata, sizeof(float)*3*num_verts, JS_TYPED_ARRAY_FLOAT32, 3, 1, 0));
|
|
|
|
// UV
|
|
if (uvdata && num_verts > 0)
|
|
JS_SetProperty(js, js_mesh, uv_atom,
|
|
make_gpu_buffer(js, uvdata, sizeof(float)*2*num_verts, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0));
|
|
|
|
// Color
|
|
if (colordata && num_verts > 0)
|
|
JS_SetProperty(js, js_mesh, color_atom,
|
|
make_gpu_buffer(js, colordata, sizeof(float)*4*num_verts, JS_TYPED_ARRAY_FLOAT32, 4, 1, 0));
|
|
|
|
// Normal
|
|
if (normdata && num_verts > 0)
|
|
JS_SetProperty(js, js_mesh, norm_atom,
|
|
make_gpu_buffer(js, normdata, sizeof(float)*3*num_verts, JS_TYPED_ARRAY_FLOAT32, 3, 1, 0));
|
|
|
|
// Indices
|
|
if (indicesdata && index_count > 0)
|
|
JS_SetProperty(js, js_mesh, indices_atom,
|
|
make_gpu_buffer(js, indicesdata, sizeof(Uint16)*index_count, JS_TYPED_ARRAY_UINT16, 0, 1, 1));
|
|
|
|
// Metadata
|
|
JS_SetProperty(js, js_mesh, vertices_atom, number2js(js, (double)num_verts));
|
|
JS_SetProperty(js, js_mesh, count_atom, number2js(js, (double)index_count));
|
|
|
|
// Build final "model" object with mesh + material
|
|
JSValue model_obj = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, model_obj, "mesh", js_mesh);
|
|
|
|
// Figure out the index of the material in data->materials. If prim->material is non-null,
|
|
// its index is `prim->material - data->materials`.
|
|
int mat_index = -1;
|
|
if (prim->material) mat_index = (int)(prim->material - data->materials);
|
|
if (mat_index >= 0 && mat_index < (int)data->materials_count)
|
|
JS_SetPropertyStr(js, model_obj, "material", JS_DupValue(js, materials[mat_index]));
|
|
else
|
|
JS_SetPropertyStr(js, model_obj, "material", JS_NewObject(js));
|
|
|
|
// Place this "model" object into our return array
|
|
uint32_t idx_in_array = js_arrlen(js,ret_arr);
|
|
JS_SetPropertyUint32(js, ret_arr, idx_in_array, model_obj);
|
|
|
|
// Cleanup (per-primitive)
|
|
if (posdata) free(posdata);
|
|
if (normdata) free(normdata);
|
|
if (uvdata) free(uvdata);
|
|
if (colordata) free(colordata);
|
|
if (indicesdata) free(indicesdata);
|
|
}
|
|
}
|
|
|
|
cgltf_free(data);
|
|
|
|
ret = ret_arr;
|
|
*/
|
|
)
|
|
|
|
JSC_SCALL(os_gltf_buffer,
|
|
int buffer_idx = js2number(js,argv[1]);
|
|
int type = js2number(js,argv[2]);
|
|
cgltf_options options = {0};
|
|
cgltf_data *data = NULL;
|
|
cgltf_result result = cgltf_parse_file(&options, str, &data);
|
|
result = cgltf_load_buffers(&options, data, str);
|
|
|
|
SDL_GPUBuffer *b = SDL_CreateGPUBuffer(NULL,NULL);
|
|
// *b = accessor2buffer(&data->accessors[buffer_idx], type);
|
|
cgltf_free(data);
|
|
|
|
// ret = sg_buffer2js(js,b);
|
|
)
|
|
|
|
JSC_SCALL(os_gltf_skin,
|
|
cgltf_options options = {0};
|
|
cgltf_data *data = NULL;
|
|
cgltf_parse_file(&options,str,&data);
|
|
cgltf_load_buffers(&options,data,str);
|
|
|
|
if (data->skins_count <= 0) {
|
|
ret = (JS_UNDEFINED);
|
|
goto CLEANUP;
|
|
}
|
|
|
|
ret = skin2js(js,make_gltf_skin(data->skins+0, data));
|
|
|
|
CLEANUP:
|
|
cgltf_free(data);
|
|
)
|
|
|
|
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;
|
|
)
|
|
|
|
JSValue parmesh2js(JSContext *js,par_shapes_mesh *m)
|
|
{
|
|
return JS_UNDEFINED;
|
|
/* JSValue obj = JS_NewObject(js);
|
|
sg_buffer *pos = malloc(sizeof(*pos));
|
|
*pos = float_buffer(m->points, 3*m->npoints);
|
|
JS_SetPropertyStr(js, obj, "pos", sg_buffer2js(js,pos));
|
|
|
|
if (m->tcoords) {
|
|
sg_buffer *uv = malloc(sizeof(*uv));
|
|
*uv = texcoord_floats(m->tcoords, 2*m->npoints);
|
|
JS_SetPropertyStr(js, obj, "uv", sg_buffer2js(js,uv));
|
|
}
|
|
|
|
if (m->normals) {
|
|
sg_buffer *norm = malloc(sizeof(*norm));
|
|
*norm = normal_floats(m->normals, 3*m->npoints);
|
|
JS_SetPropertyStr(js, obj, "norm", sg_buffer2js(js,norm));
|
|
}
|
|
|
|
sg_buffer *index = malloc(sizeof(*index));
|
|
*index = sg_make_buffer(&(sg_buffer_desc){
|
|
.data = {
|
|
.ptr = m->triangles,
|
|
.size = sizeof(*m->triangles)*3*m->ntriangles
|
|
},
|
|
.type = SG_BUFFERTYPE_INDEXBUFFER
|
|
});
|
|
JS_SetPropertyStr(js, obj, "index", sg_buffer2js(js,index));
|
|
|
|
JS_SetPropertyStr(js, obj, "count", number2js(js,3*m->ntriangles));
|
|
|
|
par_shapes_free_mesh(m);
|
|
return obj;
|
|
*/
|
|
}
|
|
|
|
JSC_CCALL(os_make_cylinder,
|
|
return parmesh2js(js,par_shapes_create_cylinder(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_cone,
|
|
return parmesh2js(js,par_shapes_create_cone(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_disk,
|
|
return parmesh2js(js,par_shapes_create_parametric_disk(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_torus,
|
|
return parmesh2js(js,par_shapes_create_torus(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_sphere,
|
|
return parmesh2js(js,par_shapes_create_parametric_sphere(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_klein_bottle,
|
|
return parmesh2js(js,par_shapes_create_klein_bottle(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_trefoil_knot,
|
|
return parmesh2js(js,par_shapes_create_trefoil_knot(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_hemisphere,
|
|
return parmesh2js(js,par_shapes_create_hemisphere(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(os_make_plane,
|
|
return parmesh2js(js,par_shapes_create_plane(js2number(js,argv[0]), js2number(js,argv[1])));
|
|
)
|
|
|
|
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,ret);
|
|
JS_FreeValue(ds->js,cb);
|
|
free(rgb);
|
|
}
|
|
|
|
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_skin_calculate,
|
|
skin *sk = js2skin(js,argv[0]);
|
|
skin_calculate(sk);
|
|
)
|
|
|
|
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_CCALL(os_perlin,
|
|
HMM_Vec3 coord = js2vec3(js,argv[0]);
|
|
HMM_Vec3 wrap = js2vec3(js,argv[2]);
|
|
return number2js(js,stb_perlin_noise3_seed(coord.x, coord.y, coord.z, wrap.x, wrap.y, wrap.z, js2number(js,argv[1])));
|
|
)
|
|
|
|
JSC_CCALL(os_ridge,
|
|
HMM_Vec3 c = js2vec3(js,argv[0]);
|
|
return number2js(js,stb_perlin_ridge_noise3(c.x, c.y, c.z, js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4])));
|
|
)
|
|
|
|
JSC_CCALL(os_fbm,
|
|
HMM_Vec3 c = js2vec3(js,argv[0]);
|
|
return number2js(js,stb_perlin_fbm_noise3(c.x, c.y, c.z, js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])));
|
|
)
|
|
|
|
JSC_CCALL(os_turbulence,
|
|
HMM_Vec3 c = js2vec3(js,argv[0]);
|
|
return number2js(js,stb_perlin_turbulence_noise3(c.x, c.y, c.z, js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])));
|
|
)
|
|
|
|
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);
|
|
return JS_UNDEFINED;
|
|
)
|
|
int detectImageInWebcam(SDL_Surface *a, SDL_Surface *b);
|
|
/*JSC_CCALL(os_match_img,
|
|
SDL_Surface *img1 = js2SDL_Surface(js,argv[0]);
|
|
SDL_Surface *img2 = js2SDL_Surface(js,argv[1]);
|
|
int n = detectImageInWebcam(img1,img2);
|
|
return number2js(js,n);
|
|
)
|
|
*/
|
|
JSC_CCALL(os_sleep,
|
|
double time = js2number(js,argv[0]);
|
|
time *= 1000000000.;
|
|
SDL_DelayNS(time);
|
|
)
|
|
|
|
JSC_CCALL(os_battery_pct,
|
|
int pct;
|
|
SDL_PowerState state = SDL_GetPowerInfo(NULL, &pct);
|
|
return number2js(js,pct);
|
|
)
|
|
|
|
JSC_CCALL(os_battery_voltage,
|
|
|
|
)
|
|
|
|
JSC_CCALL(os_battery_seconds,
|
|
int seconds;
|
|
SDL_PowerState state = 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 });
|
|
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_atom,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);
|
|
}
|
|
)
|
|
|
|
struct qtree_sprite {
|
|
rect rect;
|
|
JSValue value;
|
|
};
|
|
|
|
int js_qtree_cmp(struct qtree_sprite *v, aabb *range)
|
|
{
|
|
rect ab = {
|
|
.x = range->center.x-range->dims.w,
|
|
.y = range->center.y-range->dims.h,
|
|
.w = range->dims.w*2.0,
|
|
.h = range->dims.h*2.0
|
|
};
|
|
// printf("INTERSECT? %d\n", SDL_HasRectIntersectionFloat(&vrect,&ab));
|
|
// printf("ab: %g,%g,%g,%g\n", ab.x,ab.y,ab.w,ab.h);
|
|
// printf("vrect:%g,%g,%g,%g\n", vrect.x,vrect.y,vrect.w,vrect.h);
|
|
return SDL_HasRectIntersectionFloat(&v->rect, &ab);
|
|
}
|
|
|
|
int js_qtree_rm(struct qtree_sprite *val, struct qtree_sprite *cmp)
|
|
{
|
|
int same = JS_SameValue(global_js, val->value, cmp->value);
|
|
if (same)
|
|
JS_FreeValue(global_js, val->value);
|
|
return same;
|
|
}
|
|
|
|
JSC_CCALL(os_make_quadtree,
|
|
rect area = js2rect(js,argv[0]);
|
|
qtree tree = qtree_new(area.x,area.y,area.w,area.h, js_qtree_cmp, js_qtree_rm);
|
|
return qtree2js(js,tree);
|
|
)
|
|
|
|
struct { int key; JSValue value; } *qthash = NULL;
|
|
|
|
JSC_CCALL(os_make_qtree,
|
|
hmdefault(qthash, JS_UNDEFINED);
|
|
rect area = js2rect(js,argv[0]);
|
|
Quadtree *tree = malloc(sizeof(*tree));
|
|
qt_create(tree, area.x, area.y, area.x+area.w, area.y+area.h, 4, 8);
|
|
return Quadtree2js(js,tree);
|
|
)
|
|
|
|
JSC_CCALL(os_make_rtree,
|
|
struct rtree *tree = rtree_new();
|
|
return rtree2js(js,tree);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_os_funcs[] = {
|
|
MIST_FUNC_DEF(os, turbulence, 4),
|
|
MIST_FUNC_DEF(os, model_buffer, 1),
|
|
MIST_FUNC_DEF(os, fbm, 4),
|
|
MIST_FUNC_DEF(os, ridge, 5),
|
|
MIST_FUNC_DEF(os, perlin, 3),
|
|
MIST_FUNC_DEF(os, rectpack, 3),
|
|
MIST_FUNC_DEF(os, cwd, 0),
|
|
MIST_FUNC_DEF(os, rusage, 0),
|
|
MIST_FUNC_DEF(os, mallinfo, 0),
|
|
MIST_FUNC_DEF(os, env, 1),
|
|
MIST_FUNC_DEF(os, sys, 0),
|
|
MIST_FUNC_DEF(os, system, 1),
|
|
MIST_FUNC_DEF(os, exit, 1),
|
|
MIST_FUNC_DEF(os, gc, 0),
|
|
MIST_FUNC_DEF(os, eval, 2),
|
|
MIST_FUNC_DEF(os, make_quadtree, 1),
|
|
MIST_FUNC_DEF(os, make_qtree, 1),
|
|
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, make_surface, 1),
|
|
MIST_FUNC_DEF(os, make_cursor, 1),
|
|
MIST_FUNC_DEF(os, make_font, 2),
|
|
MIST_FUNC_DEF(os, make_transform, 0),
|
|
MIST_FUNC_DEF(os, make_line_prim, 5),
|
|
MIST_FUNC_DEF(os, make_cylinder, 2),
|
|
MIST_FUNC_DEF(os, make_cone, 2),
|
|
MIST_FUNC_DEF(os, make_disk, 2),
|
|
MIST_FUNC_DEF(os, make_torus, 3),
|
|
MIST_FUNC_DEF(os, make_sphere, 2),
|
|
MIST_FUNC_DEF(os, make_klein_bottle, 2),
|
|
MIST_FUNC_DEF(os, make_trefoil_knot, 3),
|
|
MIST_FUNC_DEF(os, make_hemisphere, 2),
|
|
MIST_FUNC_DEF(os, make_plane, 2),
|
|
MIST_FUNC_DEF(os, make_video, 1),
|
|
MIST_FUNC_DEF(os, make_timer, 1),
|
|
MIST_FUNC_DEF(os, make_text_buffer, 6),
|
|
MIST_FUNC_DEF(os, update_timers, 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, rt_info, 0),
|
|
MIST_FUNC_DEF(os, dump_mem, 0),
|
|
MIST_FUNC_DEF(os, dump_shapes, 0),
|
|
MIST_FUNC_DEF(os, dump_atoms,0),
|
|
MIST_FUNC_DEF(os, calc_mem, 1),
|
|
MIST_FUNC_DEF(os, check_gc, 0),
|
|
MIST_FUNC_DEF(os, check_cycles, 0),
|
|
MIST_FUNC_DEF(os, memstate, 0),
|
|
MIST_FUNC_DEF(os, value_id, 1),
|
|
MIST_FUNC_DEF(os, gltf_buffer, 3),
|
|
MIST_FUNC_DEF(os, gltf_skin, 1),
|
|
MIST_FUNC_DEF(os, skin_calculate, 1),
|
|
MIST_FUNC_DEF(os, kill, 1),
|
|
// MIST_FUNC_DEF(os, match_img, 2),
|
|
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, insertion_sort, 2),
|
|
MIST_FUNC_DEF(os, cull_sprites, 2),
|
|
};
|
|
|
|
JSC_CCALL(qtree_insert,
|
|
qtree tree = js2qtree(js,self);
|
|
JSValue v = argv[0];
|
|
struct qtree_sprite *item = malloc(sizeof(*item));
|
|
item->value = JS_DupValue(js,v);
|
|
JS_GETATOM(js,item->rect,v,rect_atom,rect)
|
|
qtree_insert(tree, item);
|
|
)
|
|
|
|
JSC_CCALL(qtree_remove,
|
|
struct qtree_sprite tmp;
|
|
tmp.value = argv[0];
|
|
qtree tree = js2qtree(js,self);
|
|
qtree_remove(tree, &tmp);
|
|
)
|
|
|
|
JSC_CCALL(qtree_query,
|
|
qtree tree = js2qtree(js,self);
|
|
rect area = js2rect(js,argv[0]);
|
|
uint32_t n;
|
|
struct qtree_sprite **items = qtree_findInArea(tree, area.x, area.y, area.w, area.h, &n);
|
|
|
|
ret = JS_NewArray(js);
|
|
for (int i = 0; i < n; i++)
|
|
JS_SetPropertyUint32(js,ret,i, JS_DupValue(js,items[i]->value));
|
|
|
|
free(items);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_qtree_funcs[] = {
|
|
MIST_FUNC_DEF(qtree, insert, 1),
|
|
MIST_FUNC_DEF(qtree, remove, 1),
|
|
MIST_FUNC_DEF(qtree, query, 2),
|
|
};
|
|
|
|
|
|
|
|
JSC_CCALL(Quadtree_insert,
|
|
Quadtree *tree = js2Quadtree(js,self);
|
|
JSValue v = argv[0];
|
|
rect r;
|
|
JS_GETATOM(js,r,v,rect_atom,rect)
|
|
int id = (int)JS_VALUE_GET_PTR(v);
|
|
int e = qt_insert(tree, id, r.x,r.y,r.x+r.w,r.y+r.h);
|
|
hmput(qthash, e, JS_DupValue(js,v));
|
|
)
|
|
|
|
JSC_CCALL(Quadtree_remove,
|
|
return JS_UNDEFINED;
|
|
Quadtree *tree = js2Quadtree(js,self);
|
|
int e = js2number(js,argv[0]);
|
|
qt_remove(tree, e);
|
|
// free jsvalue
|
|
)
|
|
|
|
JSC_CCALL(Quadtree_query,
|
|
Quadtree *tree = js2Quadtree(js,self);
|
|
rect area = js2rect(js,argv[0]);
|
|
IntList out;
|
|
il_create(&out,1);
|
|
qt_query(tree,&out,area.x,area.y,area.x+area.w,area.y+area.h,-1);
|
|
|
|
ret = JS_NewArray(js);
|
|
|
|
int n = 0;
|
|
for (int i = 0; i < il_size(&out); i++) {
|
|
int id = il_get(&out, i, 0);
|
|
JSValue v = hmget(qthash, id);
|
|
if (!JS_IsUndefined(v))
|
|
JS_SetPropertyUint32(js,ret,n++,JS_DupValue(js,v));
|
|
}
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_Quadtree_funcs[] = {
|
|
MIST_FUNC_DEF(Quadtree, insert, 1),
|
|
MIST_FUNC_DEF(Quadtree, remove, 1),
|
|
MIST_FUNC_DEF(Quadtree, query, 1),
|
|
};
|
|
|
|
JSC_CCALL(rtree_insert,
|
|
rtree *tree = js2rtree(js,self);
|
|
JSValue v = argv[0];
|
|
rect r;
|
|
JS_GETATOM(js,r,v,rect_atom,rect)
|
|
double min[2];
|
|
double max[2];
|
|
min[0] = r.x;
|
|
min[1] = r.y;
|
|
max[0] = r.x+r.w;
|
|
max[1] = r.y+r.h;
|
|
JSValue *ins = malloc(sizeof(*ins));
|
|
*ins = JS_DupValue(js,v);
|
|
rtree_insert(tree, min, max, ins);
|
|
)
|
|
|
|
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_remove,
|
|
rtree *tree = js2rtree(js,self);
|
|
JSValue v = argv[0];
|
|
rect r;
|
|
JS_GETATOM(js,r,v,rect_atom,rect)
|
|
double min[2];
|
|
double max[2];
|
|
min[0] = r.x;
|
|
min[1] = r.y;
|
|
max[0] = r.x+r.w;
|
|
max[1] = r.y+r.h;
|
|
|
|
int deleted = rtree_delete_with_comparator(tree, min, max, &v, rtree_cmp, js);
|
|
)
|
|
|
|
struct rtree_iter_data {
|
|
JSContext *js;
|
|
JSValue arr;
|
|
int n;
|
|
};
|
|
|
|
bool rtree_iter(const double *min, const double *max, const JSValue *data, struct rtree_iter_data *ctx)
|
|
{
|
|
JS_SetPropertyUint32(ctx->js,ctx->arr,ctx->n, JS_DupValue(ctx->js,*data));
|
|
ctx->n++;
|
|
}
|
|
|
|
JSC_CCALL(rtree_query,
|
|
rtree *tree = js2rtree(js,self);
|
|
rect r = js2rect(js,argv[0]);
|
|
double min[2];
|
|
double max[2];
|
|
min[0] = r.x;
|
|
min[1] = r.y;
|
|
max[0] = r.x+r.w;
|
|
max[1] = r.y+r.h;
|
|
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;
|
|
)
|
|
|
|
JSC_CCALL(rtree_count,
|
|
rtree *tree = js2rtree(js,self);
|
|
return number2js(js, rtree_count(tree));
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_rtree_funcs[] = {
|
|
MIST_FUNC_DEF(rtree, insert, 1),
|
|
MIST_FUNC_DEF(rtree, remove, 1),
|
|
MIST_FUNC_DEF(rtree, query, 1),
|
|
MIST_FUNC_DEF(rtree, count, 0),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_jssprite_funcs[] = {
|
|
};
|
|
|
|
#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);
|
|
JSValue js_soloud_use(JSContext *js);
|
|
JSValue js_chipmunk2d_use(JSContext *js);
|
|
|
|
#ifdef TRACY_ENABLE
|
|
JSValue js_tracy_use(JSContext *js);
|
|
#endif
|
|
|
|
#ifndef NEDITOR
|
|
JSValue js_imgui(JSContext *js);
|
|
JSValue js_dmon_use(JSContext *js);
|
|
#endif
|
|
|
|
static void signal_handler(int sig) {
|
|
const char *str = NULL;
|
|
switch(sig) {
|
|
case SIGABRT:
|
|
str = "SIGABRT";
|
|
break;
|
|
case SIGFPE:
|
|
str = "SIGFPE";
|
|
break;
|
|
case SIGILL:
|
|
str = "SIGILL";
|
|
break;
|
|
case SIGINT:
|
|
str = "SIGINT";
|
|
break;
|
|
case SIGSEGV:
|
|
str = "SIGSEGV";
|
|
break;
|
|
case SIGTERM:
|
|
str = "SIGTERM";
|
|
break;
|
|
}
|
|
if (!str) return;
|
|
script_evalf("prosperon.%s?.();", str);
|
|
}
|
|
|
|
static void exit_handler()
|
|
{
|
|
script_evalf("prosperon.exit?.();");
|
|
script_stop();
|
|
}
|
|
|
|
void ffi_load(JSContext *js) {
|
|
cycles = tmpfile();
|
|
quickjs_set_cycleout(cycles);
|
|
|
|
JSValue globalThis = JS_GetGlobalObject(js);
|
|
|
|
QJSCLASSPREP_FUNCS(qtree)
|
|
QJSCLASSPREP_FUNCS(Quadtree)
|
|
QJSCLASSPREP_FUNCS(rtree)
|
|
QJSCLASSPREP_FUNCS(SDL_Window)
|
|
QJSCLASSPREP_FUNCS(SDL_Surface)
|
|
QJSCLASSPREP_FUNCS(SDL_Thread)
|
|
QJSCLASSPREP_FUNCS(SDL_Texture)
|
|
QJSCLASSPREP_FUNCS(SDL_Renderer)
|
|
QJSCLASSPREP_FUNCS(SDL_Camera)
|
|
QJSCLASSPREP_FUNCS(SDL_Cursor)
|
|
QJSCLASSPREP_FUNCS(SDL_GPUDevice)
|
|
QJSCLASSPREP_FUNCS(SDL_GPUTexture)
|
|
QJSCLASSPREP_FUNCS(SDL_GPUCommandBuffer)
|
|
QJSCLASSPREP_FUNCS(SDL_GPURenderPass)
|
|
QJSCLASSPREP_FUNCS(jssprite)
|
|
// QJSCLASSPREP_FUNCS(SDL_GPUGraphicsPipeline)
|
|
// QJSCLASSPREP_FUNCS(SDL_GPUSampler)
|
|
// QJSCLASSPREP_FUNCS(SDL_GPUShader)
|
|
QJSCLASSPREP_FUNCS(SDL_GPUBuffer)
|
|
// QJSCLASSPREP_FUNCS(SDL_GPUTransferBuffer)
|
|
|
|
QJSGLOBALCLASS(os);
|
|
|
|
QJSCLASSPREP_FUNCS(transform);
|
|
// QJSCLASSPREP_FUNCS(warp_gravity);
|
|
// QJSCLASSPREP_FUNCS(warp_damp);
|
|
QJSCLASSPREP_FUNCS(font);
|
|
QJSCLASSPREP_FUNCS(datastream);
|
|
QJSCLASSPREP_FUNCS(timer);
|
|
|
|
QJSGLOBALCLASS(input);
|
|
QJSGLOBALCLASS(io);
|
|
QJSGLOBALCLASS(prosperon);
|
|
QJSGLOBALCLASS(time);
|
|
QJSGLOBALCLASS(console);
|
|
QJSGLOBALCLASS(profile);
|
|
QJSGLOBALCLASS(debug);
|
|
QJSGLOBALCLASS(game);
|
|
QJSGLOBALCLASS(render);
|
|
QJSGLOBALCLASS(vector);
|
|
QJSGLOBALCLASS(spline);
|
|
QJSGLOBALCLASS(performance);
|
|
QJSGLOBALCLASS(geometry);
|
|
|
|
JS_SetPropertyStr(js, prosperon, "version", JS_NewString(js,"ver"));
|
|
JS_SetPropertyStr(js, prosperon, "revision", JS_NewString(js,"com"));
|
|
JS_SetPropertyStr(js, prosperon, "date", JS_NewString(js,"date"));
|
|
|
|
JSValue array_proto = js_getpropertystr(js,globalThis, "Array");
|
|
array_proto = js_getpropertystr(js,array_proto, "prototype");
|
|
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, countof(js_array_funcs));
|
|
|
|
JS_SetPropertyStr(js, globalThis, "layout", js_layout_use(js));
|
|
JS_SetPropertyStr(js, globalThis, "miniz", js_miniz_use(js));
|
|
JS_SetPropertyStr(js, globalThis, "soloud", js_soloud_use(js));
|
|
JS_SetPropertyStr(js, globalThis, "chipmunk2d", js_chipmunk2d_use(js));
|
|
|
|
#ifdef TRACY_ENABLE
|
|
JS_SetPropertyStr(js, globalThis, "tracy", js_tracy_use(js));
|
|
#endif
|
|
|
|
signal(SIGINT, signal_handler);
|
|
signal(SIGTERM, signal_handler);
|
|
signal(SIGSEGV, signal_handler);
|
|
signal(SIGABRT, signal_handler);
|
|
atexit(exit_handler);
|
|
|
|
#ifndef NEDITOR
|
|
JS_SetPropertyStr(js, globalThis, "dmon", js_dmon_use(js));
|
|
JS_SetPropertyStr(js, globalThis, "imgui", js_imgui(js));
|
|
#endif
|
|
|
|
x_atom = JS_NewAtom(js,"x");
|
|
y_atom = JS_NewAtom(js,"y");
|
|
width_atom = JS_NewAtom(js,"width");
|
|
height_atom = JS_NewAtom(js,"height");
|
|
anchor_x_atom = JS_NewAtom(js, "anchor_x");
|
|
anchor_y_atom = JS_NewAtom(js,"anchor_y");
|
|
src_atom = JS_NewAtom(js,"src");
|
|
pos_atom = JS_NewAtom(js, "pos");
|
|
uv_atom = JS_NewAtom(js, "uv");
|
|
color_atom = JS_NewAtom(js, "color");
|
|
indices_atom = JS_NewAtom(js, "indices");
|
|
vertices_atom = JS_NewAtom(js, "vertices");
|
|
dst_atom = JS_NewAtom(js, "dst");
|
|
count_atom = JS_NewAtom(js, "count");
|
|
transform_atom = JS_NewAtom(js,"transform");
|
|
image_atom = JS_NewAtom(js,"image");
|
|
layer_atom = JS_NewAtom(js,"layer");
|
|
|
|
cw_atom = JS_NewAtom(js,"cw");
|
|
ccw_atom = JS_NewAtom(js,"ccw");
|
|
zero_atom = JS_NewAtom(js, "zero");
|
|
one_atom = JS_NewAtom(js, "one");
|
|
add_atom = JS_NewAtom(js, "add");
|
|
sub_atom = JS_NewAtom(js, "sub");
|
|
rev_sub_atom = JS_NewAtom(js, "rev_sub");
|
|
min_atom = JS_NewAtom(js, "min");
|
|
max_atom = JS_NewAtom(js, "max");
|
|
none_atom = JS_NewAtom(js, "none");
|
|
front_atom = JS_NewAtom(js, "front");
|
|
back_atom = JS_NewAtom(js, "back");
|
|
never_atom = JS_NewAtom(js, "never");
|
|
less_atom = JS_NewAtom(js, "less");
|
|
equal_atom = JS_NewAtom(js, "equal");
|
|
less_equal_atom = JS_NewAtom(js, "less_equal");
|
|
greater_atom = JS_NewAtom(js, "greater");
|
|
not_equal_atom = JS_NewAtom(js, "not_equal");
|
|
greater_equal_atom = JS_NewAtom(js, "greater_equal");
|
|
always_atom = JS_NewAtom(js, "always");
|
|
keep_atom = JS_NewAtom(js, "keep");
|
|
zero_stencil_atom = JS_NewAtom(js, "zero");
|
|
replace_atom = JS_NewAtom(js, "replace");
|
|
incr_clamp_atom = JS_NewAtom(js, "incr_clamp");
|
|
decr_clamp_atom = JS_NewAtom(js, "decr_clamp");
|
|
invert_atom = JS_NewAtom(js, "invert");
|
|
incr_wrap_atom = JS_NewAtom(js, "incr_wrap");
|
|
decr_wrap_atom = JS_NewAtom(js, "decr_wrap");
|
|
point_atom = JS_NewAtom(js, "point");
|
|
line_atom = JS_NewAtom(js, "line");
|
|
linestrip_atom = JS_NewAtom(js, "linestrip");
|
|
triangle_atom = JS_NewAtom(js, "triangle");
|
|
trianglestrip_atom = JS_NewAtom(js, "trianglestrip");
|
|
src_color_atom = JS_NewAtom(js, "src_color");
|
|
one_minus_src_color_atom = JS_NewAtom(js, "one_minus_src_color");
|
|
dst_color_atom = JS_NewAtom(js, "dst_color");
|
|
one_minus_dst_color_atom = JS_NewAtom(js, "one_minus_dst_color");
|
|
src_alpha_atom = JS_NewAtom(js, "src_alpha");
|
|
one_minus_src_alpha_atom = JS_NewAtom(js, "one_minus_src_alpha");
|
|
dst_alpha_atom = JS_NewAtom(js, "dst_alpha");
|
|
one_minus_dst_alpha_atom = JS_NewAtom(js, "one_minus_dst_alpha");
|
|
constant_color_atom = JS_NewAtom(js, "constant_color");
|
|
one_minus_constant_color_atom = JS_NewAtom(js, "one_minus_constant_color");
|
|
src_alpha_saturate_atom = JS_NewAtom(js, "src_alpha_saturate");
|
|
none_cull_atom = JS_NewAtom(js, "none");
|
|
front_cull_atom = JS_NewAtom(js, "front");
|
|
back_cull_atom = JS_NewAtom(js, "back");
|
|
norm_atom = JS_NewAtom(js,"norm");
|
|
|
|
nearest_atom = JS_NewAtom(js, "nearest");
|
|
linear_atom = JS_NewAtom(js, "linear");
|
|
|
|
mipmap_nearest_atom = JS_NewAtom(js, "nearest"); // For mipmap mode "nearest"
|
|
mipmap_linear_atom = JS_NewAtom(js, "linear"); // For mipmap mode "linear"
|
|
|
|
repeat_atom = JS_NewAtom(js, "repeat");
|
|
mirror_atom = JS_NewAtom(js, "mirror");
|
|
clamp_edge_atom = JS_NewAtom(js, "clamp_edge");
|
|
clamp_border_atom = JS_NewAtom(js, "clamp_border");
|
|
vertex_atom = JS_NewAtom(js, "vertex");
|
|
index_atom = JS_NewAtom(js, "index");
|
|
indirect_atom = JS_NewAtom(js, "indirect");
|
|
num_indices_atom = JS_NewAtom(js,"num_indices");
|
|
parent_atom = JS_NewAtom(js,"parent");
|
|
rect_atom = JS_NewAtom(js,"rect");
|
|
|
|
fill_event_atoms(js);
|
|
|
|
global_js = js;
|
|
|
|
JSValue *vals = NULL;
|
|
|
|
JS_FreeValue(js,globalThis);
|
|
}
|