1745 lines
52 KiB
C
1745 lines
52 KiB
C
#include "jsffi.h"
|
|
#include "font.h"
|
|
#include "datastream.h"
|
|
#include "qjs_sdl.h"
|
|
#include "qjs_sdl_input.h"
|
|
#include "qjs_io.h"
|
|
#include "qjs_fd.h"
|
|
#include "transform.h"
|
|
#include "stb_ds.h"
|
|
#include "stb_image.h"
|
|
#include "stb_rect_pack.h"
|
|
#define STB_DXT_IMPLEMENTATION
|
|
#include "stb_dxt.h"
|
|
#include "stb_image_write.h"
|
|
#include "string.h"
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include "render.h"
|
|
#include "model.h"
|
|
#include "HandmadeMath.h"
|
|
#include "par/par_streamlines.h"
|
|
#include "par/par_shapes.h"
|
|
#include <stdint.h>
|
|
#include "cute_aseprite.h"
|
|
#include "cgltf.h"
|
|
#include "cell.h"
|
|
|
|
#include "qjs_blob.h"
|
|
#include "qjs_dmon.h"
|
|
#include "qjs_enet.h"
|
|
#include "qjs_nota.h"
|
|
#include "qjs_wota.h"
|
|
#include "qjs_soloud.h"
|
|
#ifdef HAVE_QRENCODE
|
|
#include "qjs_qr.h"
|
|
#endif
|
|
#include "qjs_sdl.h"
|
|
#include "qjs_sdl_video.h"
|
|
#include "qjs_math.h"
|
|
#include "qjs_geometry.h"
|
|
#include "qjs_transform.h"
|
|
#include "qjs_sprite.h"
|
|
#include "qjs_io.h"
|
|
#include "qjs_sdl_gpu.h"
|
|
#include "qjs_os.h"
|
|
#include "qjs_actor.h"
|
|
#include "qjs_rtree.h"
|
|
#include "qjs_spline.h"
|
|
#include "qjs_js.h"
|
|
#include "qjs_debug.h"
|
|
#include "qjs_sdl_surface.h"
|
|
#include "qjs_sdl.h"
|
|
#include "qjs_kim.h"
|
|
#include "qjs_utf8.h"
|
|
#include "qjs_fit.h"
|
|
#include "qjs_text.h"
|
|
#ifndef NSTEAM
|
|
#include "qjs_steam.h"
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
|
|
void gui_input(SDL_Event *e);
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/utsname.h>
|
|
#ifdef __linux__
|
|
#include <sys/sysinfo.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include "wildmatch.h"
|
|
|
|
#include "freelist.h"
|
|
|
|
#include "sprite.h"
|
|
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_gpu.h>
|
|
#include <SDL3/SDL_error.h>
|
|
#include <SDL3/SDL_properties.h>
|
|
#include <SDL3/SDL_loadso.h>
|
|
#include <SDL3/SDL_cpuinfo.h>
|
|
|
|
int randombytes(void *buf, size_t n);
|
|
|
|
int trace = 0;
|
|
|
|
// External transform function declarations
|
|
extern JSClassID js_transform_id;
|
|
JSValue transform2js(JSContext *js, transform *t);
|
|
transform *js2transform(JSContext *js, JSValue v);
|
|
|
|
#ifdef __APPLE__
|
|
#include <Accelerate/Accelerate.h>
|
|
//#else
|
|
//#include <cblas.h>
|
|
#endif
|
|
|
|
// Random number generation constants for MT19937-64
|
|
#define NN STATE_VECTOR_LENGTH
|
|
#define MM STATE_VECTOR_M
|
|
#define MATRIX_A 0xB5026F5AA96619E9ULL
|
|
#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */
|
|
#define LM 0x7FFFFFFFULL /* Least significant 31 bits */
|
|
|
|
// Random number generation functions
|
|
void m_seedRand(MTRand* rand, uint64_t seed) {
|
|
rand->mt[0] = seed;
|
|
for(rand->index = 1; rand->index < NN; rand->index++) {
|
|
rand->mt[rand->index] = (6364136223846793005ULL * (rand->mt[rand->index-1] ^ (rand->mt[rand->index-1] >> 62)) + rand->index);
|
|
}
|
|
}
|
|
|
|
int64_t genRandLong(MTRand* rand) {
|
|
int i;
|
|
uint64_t x;
|
|
static uint64_t mag01[2] = {0ULL, MATRIX_A};
|
|
|
|
if (rand->index >= NN) { /* generate NN words at one time */
|
|
/* if init_genrand64() has not been called, */
|
|
/* a default initial seed is used */
|
|
if (rand->index == NN+1)
|
|
m_seedRand(rand, 5489ULL);
|
|
|
|
for (i = 0; i < NN-MM; i++) {
|
|
x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM);
|
|
rand->mt[i] = rand->mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
|
|
}
|
|
for (; i < NN-1; i++) {
|
|
x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM);
|
|
rand->mt[i] = rand->mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
|
|
}
|
|
x = (rand->mt[NN-1] & UM) | (rand->mt[0] & LM);
|
|
rand->mt[NN-1] = rand->mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
|
|
|
|
rand->index = 0;
|
|
}
|
|
|
|
x = rand->mt[rand->index++];
|
|
|
|
x ^= (x >> 29) & 0x5555555555555555ULL;
|
|
x ^= (x << 17) & 0x71D67FFFEDA60000ULL;
|
|
x ^= (x << 37) & 0xFFF7EEE000000000ULL;
|
|
x ^= (x >> 43);
|
|
|
|
return (int64_t)(x & 0x000FFFFFFFFFFFFFULL); /* return 52-bit value safe for JS */
|
|
}
|
|
|
|
double genRand(MTRand* rand) {
|
|
/* generates a random number on [0,1)-real-interval */
|
|
return (genRandLong(rand) >> 11) * (1.0/9007199254740992.0);
|
|
}
|
|
|
|
double rand_range(JSContext *js, double min, double max)
|
|
{
|
|
MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand;
|
|
return genRand(mrand) * (max-min)+min;
|
|
}
|
|
|
|
typedef struct texture_vertex {
|
|
float x, y, z;
|
|
float u, v;
|
|
uint8_t r, g, b,a;
|
|
} texture_vertex;
|
|
|
|
#define JS_GETNUM(JS,VAL,I,TO,TYPE) { \
|
|
JSValue val = JS_GetPropertyUint32(JS,VAL,I); \
|
|
TO = js2##TYPE(JS, val); \
|
|
JS_FreeValue(JS, val); } \
|
|
|
|
int js2bool(JSContext *js, JSValue v) { return JS_ToBool(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;
|
|
}
|
|
|
|
double js_getnum_str(JSContext *js, JSValue v, const char *str)
|
|
{
|
|
JSValue val = JS_GetPropertyStr(js,v,str);
|
|
double ret = js2number(js,val);
|
|
JS_FreeValue(js,val);
|
|
return ret;
|
|
}
|
|
|
|
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
|
|
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
|
TARGET = js2##TYPE(JS, __##PROP##__v); \
|
|
JS_FreeValue(JS,__##PROP##__v); }\
|
|
|
|
#define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\
|
|
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#ATOM); \
|
|
TARGET = js2##TYPE(JS, __##PROP##__v); \
|
|
JS_FreeValue(JS,__##PROP##__v); }\
|
|
|
|
int JS_GETBOOL(JSContext *js, JSValue v, const char *prop)
|
|
{
|
|
JSValue __v = JS_GetPropertyStr(js,v,prop);
|
|
int r = JS_ToBool(js,__v);
|
|
JS_FreeValue(js,__v);
|
|
return r;
|
|
}
|
|
|
|
JSValue js_getproperty(JSContext *js, JSValue v, const char *prop)
|
|
{
|
|
JSValue ret = JS_GetPropertyStr(js, v, prop);
|
|
JS_FreeValue(js,ret);
|
|
return ret;
|
|
}
|
|
|
|
void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index)
|
|
{
|
|
JSValue tstack[3];
|
|
tstack[1] = JS_NULL;
|
|
tstack[2] = JS_NULL;
|
|
// TODO: always copying; implement "takeover"
|
|
tstack[0] = js_new_blob_stoned_copy(js,data,size);//, make_gpu_buffer, NULL, 1);
|
|
// 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;
|
|
return JS_NULL;
|
|
}
|
|
|
|
void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size)
|
|
{
|
|
return NULL;
|
|
/* size_t o, len, bytes, msize;
|
|
JSValue buf = JS_GetTypedArrayBuffer(js, argv, &o, &len, &bytes);
|
|
void *data = js_get_blob_data(js, &msize, buf);
|
|
JS_FreeValue(js,buf);
|
|
if (stride) *stride = js_getnum_str(js, argv, "stride");
|
|
if (size) *size = msize;
|
|
return data;*/
|
|
}
|
|
|
|
JSValue make_quad_indices_buffer(JSContext *js, int quads)
|
|
{
|
|
cell_rt *rt = JS_GetContextOpaque(js);
|
|
int count = quads*6;
|
|
if (!JS_IsNull(rt->idx_buffer) && rt->idx_count >= count)
|
|
return JS_DupValue(js,rt->idx_buffer);
|
|
|
|
int verts = quads*4;
|
|
uint16_t *indices = malloc(sizeof(*indices)*count);
|
|
for (int i = 0, v = 0; v < verts; i +=6, v += 4) {
|
|
indices[i] = v;
|
|
indices[i+1] = v+2;
|
|
indices[i+2] = v+1;
|
|
indices[i+3] = v+2;
|
|
indices[i+4] = v+3;
|
|
indices[i+5] = v+1;
|
|
}
|
|
|
|
if (!JS_IsNull(rt->idx_buffer))
|
|
JS_FreeValue(js,rt->idx_buffer);
|
|
|
|
// rt->idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1);
|
|
rt->idx_count = count;
|
|
return JS_DupValue(js,rt->idx_buffer);
|
|
}
|
|
|
|
JSValue quads_to_mesh(JSContext *js, text_vert *buffer)
|
|
{
|
|
size_t verts = arrlen(buffer);
|
|
|
|
JSValue ret = JS_NewObject(js);
|
|
|
|
// Allocate flat arrays for xy, uv, and color data
|
|
size_t xy_size = verts * 2 * sizeof(float);
|
|
size_t uv_size = verts * 2 * sizeof(float);
|
|
size_t color_size = verts * sizeof(SDL_FColor);
|
|
|
|
float *xy_data = malloc(xy_size);
|
|
float *uv_data = malloc(uv_size);
|
|
SDL_FColor *color_data = malloc(color_size);
|
|
|
|
// Convert vertex data to flat arrays
|
|
for (int i = 0; i < verts; i++) {
|
|
xy_data[i*2] = buffer[i].pos.x;
|
|
xy_data[i*2+1] = buffer[i].pos.y;
|
|
|
|
uv_data[i*2] = buffer[i].uv.x;
|
|
uv_data[i*2+1] = buffer[i].uv.y;
|
|
|
|
color_data[i].r = buffer[i].color.x;
|
|
color_data[i].g = buffer[i].color.y;
|
|
color_data[i].b = buffer[i].color.z;
|
|
color_data[i].a = buffer[i].color.w;
|
|
}
|
|
|
|
size_t quads = verts/4;
|
|
size_t count = quads*6;
|
|
|
|
// Create indices
|
|
uint16_t *indices = malloc(sizeof(uint16_t)*count);
|
|
for (int i = 0, v = 0; i < count; 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;
|
|
}
|
|
|
|
// Create blobs for geometry data
|
|
JS_SetPropertyStr(js, ret, "xy", js_new_blob_stoned_copy(js, xy_data, xy_size));
|
|
JS_SetPropertyStr(js, ret, "xy_stride", JS_NewInt32(js, 2 * sizeof(float)));
|
|
JS_SetPropertyStr(js, ret, "uv", js_new_blob_stoned_copy(js, uv_data, uv_size));
|
|
JS_SetPropertyStr(js, ret, "uv_stride", JS_NewInt32(js, 2 * sizeof(float)));
|
|
JS_SetPropertyStr(js, ret, "color", js_new_blob_stoned_copy(js, color_data, color_size));
|
|
JS_SetPropertyStr(js, ret, "color_stride", JS_NewInt32(js, sizeof(SDL_FColor)));
|
|
JS_SetPropertyStr(js, ret, "indices", js_new_blob_stoned_copy(js, indices, sizeof(uint16_t)*count));
|
|
JS_SetPropertyStr(js, ret, "num_vertices", JS_NewInt32(js, verts));
|
|
JS_SetPropertyStr(js, ret, "num_indices", JS_NewInt32(js, count));
|
|
JS_SetPropertyStr(js, ret, "size_indices", JS_NewInt32(js, 2)); // uint16_t size
|
|
|
|
free(xy_data);
|
|
free(uv_data);
|
|
free(color_data);
|
|
free(indices);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
typedef struct lrtb lrtb;
|
|
|
|
lrtb js2lrtb(JSContext *js, JSValue v)
|
|
{
|
|
lrtb ret = {0};
|
|
JS_GETATOM(js,ret.l,v,l,number)
|
|
JS_GETATOM(js,ret.r,v,r,number)
|
|
JS_GETATOM(js,ret.b,v,b,number)
|
|
JS_GETATOM(js,ret.t,v,t,number)
|
|
return ret;
|
|
}
|
|
|
|
JSValue vec22js(JSContext *js,HMM_Vec2 v)
|
|
{
|
|
JSValue array = JS_NewArray(js);
|
|
JS_SetPropertyUint32(js, array,0,number2js(js,v.x));
|
|
JS_SetPropertyUint32(js, array,1,number2js(js,v.y));
|
|
return array;
|
|
}
|
|
|
|
char *js2strdup(JSContext *js, JSValue v) {
|
|
const char *str = JS_ToCString(js, v);
|
|
char *ret = strdup(str);
|
|
JS_FreeCString(js, str);
|
|
return ret;
|
|
}
|
|
|
|
#include "qjs_macros.h"
|
|
|
|
void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c)
|
|
{
|
|
|
|
}
|
|
|
|
QJSCLASS(font,)
|
|
QJSCLASS(datastream,)
|
|
|
|
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_IsNull(v)) return (colorf){1,1,1,1};
|
|
|
|
colorf color = {1,1,1,1}; // Default to white
|
|
|
|
if (JS_IsArray(js, v)) {
|
|
// Handle array format: [r, g, b, a]
|
|
JSValue c[4];
|
|
for (int i = 0; i < 4; i++) c[i] = JS_GetPropertyUint32(js,v,i);
|
|
|
|
color.r = js2number(js,c[0]);
|
|
color.g = js2number(js,c[1]);
|
|
color.b = js2number(js,c[2]);
|
|
color.a = JS_IsNull(c[3]) ? 1.0 : js2number(js,c[3]);
|
|
|
|
for (int i = 0; i < 4; i++) JS_FreeValue(js,c[i]);
|
|
} else if (JS_IsObject(v)) {
|
|
// Handle object format: {r, g, b, a}
|
|
JSValue r_val = JS_GetPropertyStr(js, v, "r");
|
|
JSValue g_val = JS_GetPropertyStr(js, v, "g");
|
|
JSValue b_val = JS_GetPropertyStr(js, v, "b");
|
|
JSValue a_val = JS_GetPropertyStr(js, v, "a");
|
|
|
|
color.r = JS_IsNull(r_val) ? 1.0 : js2number(js, r_val);
|
|
color.g = JS_IsNull(g_val) ? 1.0 : js2number(js, g_val);
|
|
color.b = JS_IsNull(b_val) ? 1.0 : js2number(js, b_val);
|
|
color.a = JS_IsNull(a_val) ? 1.0 : js2number(js, a_val);
|
|
|
|
JS_FreeValue(js, r_val);
|
|
JS_FreeValue(js, g_val);
|
|
JS_FreeValue(js, b_val);
|
|
JS_FreeValue(js, a_val);
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
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;
|
|
|
|
// Check if it's an array
|
|
if (JS_IsArray(js, v)) {
|
|
v2.X = js_getnum_uint32(js,v,0);
|
|
v2.Y = js_getnum_uint32(js,v,1);
|
|
} else {
|
|
// Try to get x,y properties from object
|
|
JSValue x_val = JS_GetPropertyStr(js, v, "x");
|
|
JSValue y_val = JS_GetPropertyStr(js, v, "y");
|
|
|
|
v2.X = js2number(js, x_val);
|
|
v2.Y = js2number(js, y_val);
|
|
|
|
JS_FreeValue(js, x_val);
|
|
JS_FreeValue(js, y_val);
|
|
}
|
|
|
|
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_ArrayLength(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_ArrayLength(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_ArrayLength(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_ArrayLength(js,v);
|
|
arrsetlen(arr,n);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
arr[i] = js2vec2(js,js_getpropertyuint32(js, v, i));
|
|
|
|
return arr;
|
|
}
|
|
|
|
JSValue vecarr2js(JSContext *js,HMM_Vec2 *points, int n) {
|
|
JSValue array = JS_NewArray(js);
|
|
for (int i = 0; i < n; i++)
|
|
JS_SetPropertyUint32(js, array,i,vec22js(js,points[i]));
|
|
|
|
return array;
|
|
}
|
|
|
|
rect js2rect(JSContext *js,JSValue v) {
|
|
if (JS_IsNull(v)) return (rect){0,0,1,1};
|
|
rect rect;
|
|
JS_GETATOM(js,rect.x,v,x,number)
|
|
JS_GETATOM(js,rect.y,v,y,number)
|
|
JS_GETATOM(js,rect.w,v,width,number)
|
|
JS_GETATOM(js,rect.h,v,height,number)
|
|
float anchor_x, anchor_y;
|
|
JS_GETATOM(js, anchor_x, v, anchor_x, number)
|
|
JS_GETATOM(js, anchor_y, v, anchor_y, number)
|
|
|
|
rect.y -= anchor_y*rect.h;
|
|
rect.x -= anchor_x*rect.w;
|
|
|
|
return rect;
|
|
}
|
|
|
|
irect js2irect(JSContext *js, JSValue v)
|
|
{
|
|
if (JS_IsNull(v)) return (irect){0,0,1,1};
|
|
irect rect;
|
|
JS_GETATOM(js,rect.x,v,x,number)
|
|
JS_GETATOM(js,rect.y,v,y,number)
|
|
JS_GETATOM(js,rect.w,v,width,number)
|
|
JS_GETATOM(js,rect.h,v,height,number)
|
|
float anchor_x, anchor_y;
|
|
JS_GETATOM(js, anchor_x, v, anchor_x, number)
|
|
JS_GETATOM(js, anchor_y, v, anchor_y, number)
|
|
|
|
rect.y -= anchor_y*rect.h;
|
|
rect.x -= anchor_x*rect.w;
|
|
|
|
return rect;
|
|
}
|
|
|
|
rect transform_rect(SDL_Renderer *ren, rect in, HMM_Mat3 *t)
|
|
{
|
|
HMM_Vec3 bottom_left = (HMM_Vec3){in.x,in.y,1.0};
|
|
HMM_Vec3 transformed_bl = HMM_MulM3V3(*t, bottom_left);
|
|
in.x = transformed_bl.x;
|
|
in.y = transformed_bl.y;
|
|
in.y = in.y - in.h; // should be done for any platform that draws rectangles from top left
|
|
return in;
|
|
}
|
|
|
|
HMM_Vec2 transform_point(SDL_Renderer *ren, HMM_Vec2 in, HMM_Mat3 *t)
|
|
{
|
|
rect logical;
|
|
SDL_GetRenderLogicalPresentationRect(ren, &logical);
|
|
in.y *= -1;
|
|
in.y += logical.h;
|
|
in.x -= t->Columns[2].x;
|
|
in.y -= t->Columns[2].y;
|
|
return in;
|
|
}
|
|
|
|
JSValue rect2js(JSContext *js,rect rect) {
|
|
JSValue obj = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, obj, "x", number2js(js, rect.x));
|
|
JS_SetPropertyStr(js, obj, "y", number2js(js, rect.y));
|
|
JS_SetPropertyStr(js, obj, "width", number2js(js, rect.w));
|
|
JS_SetPropertyStr(js, obj, "height", 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_CCALL(os_make_text_buffer,
|
|
const char *s = JS_ToCString(js, argv[0]);
|
|
rect rectpos = js2rect(js,argv[1]);
|
|
font *f = js2font(js,argv[4]);
|
|
colorf c = js2color(js,argv[2]);
|
|
int wrap = js2number(js,argv[3]);
|
|
HMM_Vec2 startpos = {.x = rectpos.x, .y = rectpos.y };
|
|
text_vert *buffer = renderText(s, startpos, f, c, wrap);
|
|
ret = quads_to_mesh(js,buffer);
|
|
JS_FreeCString(js, s);
|
|
arrfree(buffer);
|
|
)
|
|
|
|
JSValue js_util_camera_globals(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
JSValue camera = argv[0];
|
|
if(JS_IsNull(camera)) return JS_NULL;
|
|
|
|
HMM_Vec2 size; HMM_Vec3 pos; HMM_Quat rotation;
|
|
double fov = 0; int ortho; double near_z = 0; double far_z = 0;
|
|
HMM_Vec2 anchor;
|
|
|
|
JS_GETPROP(js, size, camera, size, vec2)
|
|
JS_GETPROP(js, fov, camera, fov, number)
|
|
JS_GETPROP(js, ortho, camera, ortho, bool)
|
|
JS_GETPROP(js, near_z, camera, near_z, number)
|
|
JS_GETPROP(js, far_z, camera, far_z, number)
|
|
JS_GETPROP(js, anchor, camera, anchor, vec2)
|
|
JS_GETPROP(js, pos, camera, pos, vec3)
|
|
JS_GETPROP(js, rotation, camera, rotation, quat)
|
|
|
|
rotation.w = 1;
|
|
|
|
HMM_Mat4 proj, view;
|
|
|
|
if(ortho) {
|
|
float left = -anchor.x * size.x;
|
|
float bottom = -anchor.y * size.y;
|
|
float right = left + size.x;
|
|
float top = bottom + size.y;
|
|
proj = HMM_Orthographic_RH_NO(left, right, bottom, top, -1.0f, 1.0f);
|
|
} else {
|
|
proj = HMM_Perspective_RH_NO(fov, size.x/size.y, near_z, far_z);
|
|
proj.Columns[1] = HMM_MulV4F(proj.Columns[1], -1.0f);
|
|
}
|
|
|
|
view = HMM_MulM4(
|
|
HMM_InvTranslate(HMM_Translate(pos)),
|
|
HMM_InvRotate (HMM_QToM4(rotation))
|
|
);
|
|
|
|
JSValue data = JS_NewObject(js);
|
|
|
|
HMM_Mat4 world_to_projection = HMM_MulM4(proj, view);
|
|
HMM_Mat4 projection_to_world = HMM_InvGeneralM4(world_to_projection);
|
|
HMM_Vec3 camera_dir_world = HMM_NormV3(
|
|
HMM_QVRot((HMM_Vec3){0,0,-1}, rotation)
|
|
);
|
|
|
|
JS_SetPropertyStr(js, data, "world_to_projection",
|
|
js_new_blob_stoned_copy(js, world_to_projection.em,
|
|
sizeof(float)*16));
|
|
JS_SetPropertyStr(js, data, "projection_to_world",
|
|
js_new_blob_stoned_copy(js, projection_to_world.em,
|
|
sizeof(float)*16));
|
|
JS_SetPropertyStr(js, data, "world_to_view",
|
|
js_new_blob_stoned_copy(js, view.em, sizeof(float)*16));
|
|
JS_SetPropertyStr(js, data, "view_to_projection",
|
|
js_new_blob_stoned_copy(js, proj.em, sizeof(float)*16));
|
|
|
|
JS_SetPropertyStr(js, data, "camera_pos_world", vec32js(js, pos));
|
|
JS_SetPropertyStr(js, data, "camera_dir_world", vec32js(js, camera_dir_world));
|
|
JS_SetPropertyStr(js, data, "render_size", vec22js(js, size));
|
|
JS_SetPropertyStr(js, data, "viewport_size", vec22js(js, (HMM_Vec2){0.5,0.5}));
|
|
JS_SetPropertyStr(js, data, "viewport_offset", vec22js(js, (HMM_Vec2){0,0}));
|
|
JS_SetPropertyStr(js, data, "viewport_min_z", number2js(js, near_z));
|
|
JS_SetPropertyStr(js, data, "viewport_max_z", number2js(js, far_z));
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
static JSValue floats2array(JSContext *js, float *vals, size_t len) {
|
|
JSValue arr = JS_NewArray(js);
|
|
for (size_t i = 0; i < len; i++) {
|
|
JS_SetPropertyUint32(js, arr, i, number2js(js, vals[i]));
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
#define JS_HMM_FN(OP, HMM, SIGN) \
|
|
JSC_CCALL(array_##OP, \
|
|
int len = JS_ArrayLength(js,self); \
|
|
if (!JS_IsArray(js, argv[0])) { \
|
|
double n = js2number(js,argv[0]); \
|
|
JSValue arr = JS_NewArray(js); \
|
|
for (int i = 0; i < len; i++) \
|
|
JS_SetPropertyUint32(js, arr, i, number2js(js,js_getnum_uint32(js, self,i) SIGN n)); \
|
|
return arr; \
|
|
} \
|
|
switch(len) { \
|
|
case 2: \
|
|
return vec22js(js,HMM_##HMM##V2(js2vec2(js,self), js2vec2(js,argv[0]))); \
|
|
case 3: \
|
|
return vec32js(js, HMM_##HMM##V3(js2vec3(js,self), js2vec3(js,argv[0]))); \
|
|
case 4: \
|
|
return vec42js(js,HMM_##HMM##V4(js2vec4(js,self), js2vec4(js,argv[0]))); \
|
|
} \
|
|
\
|
|
JSValue arr = JS_NewArray(js); \
|
|
for (int i = 0; i < len; i++) { \
|
|
double a = js_getnum_uint32(js, self,i); \
|
|
double b = js_getnum_uint32(js, argv[0],i); \
|
|
JS_SetPropertyUint32(js, arr, i, number2js(js,a SIGN b)); \
|
|
} \
|
|
return arr; \
|
|
) \
|
|
|
|
JS_HMM_FN(add, Add, +)
|
|
JS_HMM_FN(sub, Sub, -)
|
|
JS_HMM_FN(div, Div, /)
|
|
JS_HMM_FN(scale, Mul, *)
|
|
|
|
JSC_CCALL(array_lerp,
|
|
double t = js2number(js,argv[1]);
|
|
int len = JS_ArrayLength(js,self);
|
|
JSValue arr = JS_NewArray(js);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
double from = js_getnum_uint32(js, self, i);
|
|
double to = js_getnum_uint32(js, argv[0], i);
|
|
JS_SetPropertyUint32(js, arr, i, number2js(js,(to - from) * t + from));
|
|
}
|
|
return arr;
|
|
)
|
|
|
|
JSValue js_array_get_x(JSContext *js, JSValue self) { return JS_GetPropertyUint32(js,self,0); }
|
|
JSValue js_array_set_x(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,0,val); return JS_NULL; }
|
|
|
|
JSValue js_array_get_y(JSContext *js, JSValue self) { return JS_GetPropertyUint32(js,self,1); }
|
|
JSValue js_array_set_y(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,1,val); return JS_NULL; }
|
|
|
|
JSValue js_array_get_xy(JSContext *js, JSValue self)
|
|
{
|
|
JSValue arr = JS_NewArray(js);
|
|
JS_SetPropertyUint32(js,arr,0,JS_GetPropertyUint32(js,self,0));
|
|
JS_SetPropertyUint32(js,arr,1,JS_GetPropertyUint32(js,self,1));
|
|
return arr;
|
|
}
|
|
|
|
JSValue js_array_set_xy(JSContext *js, JSValue self, JSValue v)
|
|
{
|
|
JS_SetPropertyUint32(js,self,0,JS_GetPropertyUint32(js,v,0));
|
|
JS_SetPropertyUint32(js,self,1,JS_GetPropertyUint32(js,v,1));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Single-value accessors
|
|
|
|
JSValue js_array_get_r(JSContext *js, JSValue self)
|
|
{ return JS_GetPropertyUint32(js, self, 0); }
|
|
|
|
JSValue js_array_set_r(JSContext *js, JSValue self, JSValue val)
|
|
{ JS_SetPropertyUint32(js, self, 0, val); return JS_NULL; }
|
|
|
|
JSValue js_array_get_g(JSContext *js, JSValue self)
|
|
{ return JS_GetPropertyUint32(js, self, 1); }
|
|
|
|
JSValue js_array_set_g(JSContext *js, JSValue self, JSValue val)
|
|
{ JS_SetPropertyUint32(js, self, 1, val); return JS_NULL; }
|
|
|
|
JSValue js_array_get_b(JSContext *js, JSValue self)
|
|
{ return JS_GetPropertyUint32(js, self, 2); }
|
|
|
|
JSValue js_array_set_b(JSContext *js, JSValue self, JSValue val)
|
|
{ JS_SetPropertyUint32(js, self, 2, val); return JS_NULL; }
|
|
|
|
JSValue js_array_get_a(JSContext *js, JSValue self)
|
|
{ return JS_GetPropertyUint32(js, self, 3); }
|
|
|
|
JSValue js_array_set_a(JSContext *js, JSValue self, JSValue val)
|
|
{ JS_SetPropertyUint32(js, self, 3, val); return JS_NULL; }
|
|
|
|
// Multi-value accessors
|
|
|
|
JSValue js_array_get_rgb(JSContext *js, JSValue self)
|
|
{
|
|
JSValue arr = JS_NewArray(js);
|
|
JS_SetPropertyUint32(js, arr, 0, JS_GetPropertyUint32(js, self, 0));
|
|
JS_SetPropertyUint32(js, arr, 1, JS_GetPropertyUint32(js, self, 1));
|
|
JS_SetPropertyUint32(js, arr, 2, JS_GetPropertyUint32(js, self, 2));
|
|
return arr;
|
|
}
|
|
|
|
JSValue js_array_set_rgb(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
JS_SetPropertyUint32(js, self, 0, JS_GetPropertyUint32(js, val, 0));
|
|
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
|
|
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_array_get_rgba(JSContext *js, JSValue self)
|
|
{
|
|
JSValue arr = JS_NewArray(js);
|
|
JS_SetPropertyUint32(js, arr, 0, JS_GetPropertyUint32(js, self, 0));
|
|
JS_SetPropertyUint32(js, arr, 1, JS_GetPropertyUint32(js, self, 1));
|
|
JS_SetPropertyUint32(js, arr, 2, JS_GetPropertyUint32(js, self, 2));
|
|
JS_SetPropertyUint32(js, arr, 3, JS_GetPropertyUint32(js, self, 3));
|
|
return arr;
|
|
}
|
|
|
|
JSValue js_array_set_rgba(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
JS_SetPropertyUint32(js, self, 0, JS_GetPropertyUint32(js, val, 0));
|
|
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
|
|
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
|
|
JS_SetPropertyUint32(js, self, 3, JS_GetPropertyUint32(js, val, 3));
|
|
return JS_NULL;
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_array_funcs[] = {
|
|
PROTO_FUNC_DEF(array, add, 1),
|
|
PROTO_FUNC_DEF(array, sub, 1),
|
|
PROTO_FUNC_DEF(array, div,1),
|
|
PROTO_FUNC_DEF(array, scale, 1),
|
|
PROTO_FUNC_DEF(array, lerp, 2),
|
|
JS_CGETSET_DEF("x", js_array_get_x,js_array_set_x),
|
|
JS_CGETSET_DEF("y", js_array_get_y, js_array_set_y),
|
|
JS_CGETSET_DEF("xy", js_array_get_xy, js_array_set_xy),
|
|
JS_CGETSET_DEF("r", js_array_get_r, js_array_set_r),
|
|
JS_CGETSET_DEF("g", js_array_get_g, js_array_set_g),
|
|
JS_CGETSET_DEF("b", js_array_get_b, js_array_set_b),
|
|
JS_CGETSET_DEF("a", js_array_get_a, js_array_set_a),
|
|
JS_CGETSET_DEF("rgb", js_array_get_rgb, js_array_set_rgb),
|
|
JS_CGETSET_DEF("rgba", js_array_get_rgba, js_array_set_rgba),
|
|
};
|
|
|
|
JSC_CCALL(number_lerp,
|
|
double a = js2number(js,self);
|
|
double b = js2number(js,argv[0]);
|
|
double t = js2number(js,argv[1]);
|
|
return number2js(js, (b-a)*t+a);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_number_funcs[] = {
|
|
PROTO_FUNC_DEF(number, lerp, 2),
|
|
};
|
|
|
|
static uint32_t rng_state = 123456789;
|
|
static uint32_t xorshift32(){
|
|
uint32_t x = rng_state;
|
|
x ^= x << 13;
|
|
x ^= x >> 17;
|
|
x ^= x << 5;
|
|
return rng_state = x;
|
|
}
|
|
|
|
JSC_CCALL(os_guid,
|
|
uint8_t data[16];
|
|
for(int i = 0; i < 4; i++){
|
|
uint32_t v = xorshift32();
|
|
memcpy(&data[i*4], &v, 4);
|
|
}
|
|
|
|
static const char hex[] = "0123456789abcdef";
|
|
char buf[32];
|
|
for(int i = 0; i < 16; i++){
|
|
uint8_t b = data[i];
|
|
buf[i*2 ] = hex[b >> 4];
|
|
buf[i*2 + 1] = hex[b & 0x0f];
|
|
}
|
|
|
|
return JS_NewStringLen(js, buf, 32);
|
|
)
|
|
|
|
JSC_SCALL(console_print, printf("%s", str); )
|
|
|
|
static const JSCFunctionListEntry js_console_funcs[] = {
|
|
MIST_FUNC_DEF(console,print,1),
|
|
};
|
|
|
|
JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); )
|
|
JSC_CCALL(datastream_seek, ds_seek(js2datastream(js,self), js2number(js,argv[0])))
|
|
JSC_CCALL(datastream_advance, ds_advance(js2datastream(js,self), js2number(js,argv[0])))
|
|
JSC_CCALL(datastream_duration, return number2js(js,ds_length(js2datastream(js,self))))
|
|
JSC_CCALL(datastream_framerate, return number2js(js,plm_get_framerate(js2datastream(js,self)->plm)))
|
|
|
|
JSC_GETSET_CALLBACK(datastream, callback)
|
|
|
|
static const JSCFunctionListEntry js_datastream_funcs[] = {
|
|
MIST_FUNC_DEF(datastream, time, 0),
|
|
MIST_FUNC_DEF(datastream, seek, 1),
|
|
MIST_FUNC_DEF(datastream, advance, 1),
|
|
MIST_FUNC_DEF(datastream, duration, 0),
|
|
MIST_FUNC_DEF(datastream, framerate, 0),
|
|
CGETSET_ADD(datastream, callback),
|
|
};
|
|
|
|
JSC_GETSET(font, linegap, number)
|
|
JSC_GET(font, height, number)
|
|
JSC_GET(font, ascent, number)
|
|
JSC_GET(font, descent, number)
|
|
|
|
JSC_CCALL(graphics_font_text_size,
|
|
font *f = js2font(js,argv[0]);
|
|
const char *str = JS_ToCString(js, argv[1]);
|
|
float letterSpacing = js2number(js,argv[2]);
|
|
float wrap = js2number(js,argv[3]);
|
|
ret = vec22js(js,measure_text(str, f, letterSpacing, wrap));
|
|
JS_FreeCString(js, str);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_font_funcs[] = {
|
|
CGETSET_ADD(font, linegap),
|
|
MIST_GET(font, height),
|
|
MIST_GET(font, ascent),
|
|
MIST_GET(font, descent),
|
|
};
|
|
|
|
// input: (encoded image data of jpg, png, bmp, tiff)
|
|
JSC_CCALL(os_make_texture,
|
|
size_t len;
|
|
void *raw = js_get_blob_data(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, 4);
|
|
|
|
if (!data)
|
|
return JS_ThrowReferenceError(js, "no known image type from pixel data: %s", stbi_failure_reason());
|
|
|
|
if (width <= 0 || height <= 0) {
|
|
free(data);
|
|
return JS_ThrowReferenceError(js, "decoded image has invalid size: %dx%d", width, height);
|
|
}
|
|
|
|
int pitch = width*4;
|
|
size_t pixels_size = pitch * height;
|
|
|
|
// Create JS object with surface data
|
|
JSValue obj = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, obj, "width", JS_NewInt32(js, width));
|
|
JS_SetPropertyStr(js, obj, "height", JS_NewInt32(js, height));
|
|
JS_SetPropertyStr(js, obj, "format", JS_NewString(js, "rgba32"));
|
|
JS_SetPropertyStr(js, obj, "pitch", JS_NewInt32(js, pitch));
|
|
JS_SetPropertyStr(js, obj, "pixels", js_new_blob_stoned_copy(js, data, pixels_size));
|
|
|
|
free(data);
|
|
ret = obj;
|
|
)
|
|
|
|
|
|
// input: (gif image data)
|
|
JSC_CCALL(os_make_gif,
|
|
size_t rawlen;
|
|
void *raw = js_get_blob_data(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);
|
|
|
|
if (!pixels) {
|
|
return JS_ThrowReferenceError(js, "Failed to decode GIF: %s", stbi_failure_reason());
|
|
}
|
|
|
|
// Always return an array of surfaces, even for single frame
|
|
JSValue surface_array = JS_NewArray(js);
|
|
ret = surface_array;
|
|
|
|
for (int i = 0; i < frames; i++) {
|
|
// Create surface data object
|
|
JSValue surfData = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, width));
|
|
JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, height));
|
|
JS_SetPropertyStr(js, surfData, "format", JS_NewString(js, "rgba32"));
|
|
JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, width*4));
|
|
|
|
void *frame_pixels = (unsigned char*)pixels+(width*height*4*i);
|
|
JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, frame_pixels, width*height*4));
|
|
|
|
// Add time property for animation frames
|
|
if (frames > 1 && delays) {
|
|
JS_SetPropertyStr(js, surfData, "time", number2js(js,(float)delays[i]/1000.0));
|
|
}
|
|
|
|
JS_SetPropertyUint32(js, surface_array, i, surfData);
|
|
}
|
|
|
|
CLEANUP:
|
|
if (delays) free(delays);
|
|
if (pixels) free(pixels);
|
|
)
|
|
|
|
JSValue aseframe2js(JSContext *js, ase_frame_t aframe)
|
|
{
|
|
JSValue frame = JS_NewObject(js);
|
|
|
|
// Create surface data object instead of SDL_Surface
|
|
JSValue surfData = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, aframe.ase->w));
|
|
JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, aframe.ase->h));
|
|
JS_SetPropertyStr(js, surfData, "format", JS_NewString(js, "rgba32"));
|
|
JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, aframe.ase->w*4));
|
|
JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, aframe.pixels, aframe.ase->w*aframe.ase->h*4));
|
|
|
|
JS_SetPropertyStr(js, frame, "surface", surfData);
|
|
JS_SetPropertyStr(js, frame, "time", number2js(js,(float)aframe.duration_milliseconds/1000.0));
|
|
return frame;
|
|
}
|
|
|
|
// input: (aseprite data)
|
|
JSC_CCALL(os_make_aseprite,
|
|
size_t rawlen;
|
|
void *raw = js_get_blob_data(js,&rawlen,argv[0]);
|
|
|
|
ase_t *ase = cute_aseprite_load_from_memory(raw, rawlen, NULL);
|
|
|
|
if (!ase)
|
|
return JS_ThrowReferenceError(js, "could not load aseprite from supplied array buffer: %s", aseprite_GetError());
|
|
|
|
if (ase->tag_count == 0) {
|
|
// we're dealing with a single frame image, or single animation
|
|
if (ase->frame_count == 1) {
|
|
JSValue obj = aseframe2js(js,ase->frames[0]);
|
|
cute_aseprite_free(ase);
|
|
return obj;
|
|
} else {
|
|
// Multiple frames but no tags - create a simple animation
|
|
JSValue obj = JS_NewObject(js);
|
|
JSValue frames = JS_NewArray(js);
|
|
for (int f = 0; f < ase->frame_count; f++) {
|
|
JSValue frame = aseframe2js(js,ase->frames[f]);
|
|
JS_SetPropertyUint32(js, frames, f, frame);
|
|
}
|
|
JS_SetPropertyStr(js, obj, "frames", frames);
|
|
JS_SetPropertyStr(js, obj, "loop", JS_NewBool(js, true));
|
|
ret = obj;
|
|
cute_aseprite_free(ase);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
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_font,
|
|
size_t len;
|
|
void *data = js_get_blob_data(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);
|
|
|
|
// Create surface data object for the font's atlas
|
|
if (f->surface) {
|
|
JSValue surfData = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, f->surface->w));
|
|
JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, f->surface->h));
|
|
JS_SetPropertyStr(js, surfData, "format", pixelformat2js(js, f->surface->format));
|
|
JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, f->surface->pitch));
|
|
|
|
// Lock surface if needed
|
|
int locked = 0;
|
|
if (SDL_MUSTLOCK(f->surface)) {
|
|
if (SDL_LockSurface(f->surface) < 0)
|
|
return JS_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
|
|
locked = 1;
|
|
}
|
|
|
|
size_t byte_size = f->surface->pitch * f->surface->h;
|
|
JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, f->surface->pixels, byte_size));
|
|
|
|
if (locked)
|
|
SDL_UnlockSurface(f->surface);
|
|
|
|
JS_SetPropertyStr(js, ret, "surface", surfData);
|
|
}
|
|
)
|
|
|
|
JSC_SCALL(os_system, ret = number2js(js,system(str)); )
|
|
|
|
JSC_CCALL(os_rand,
|
|
MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand;
|
|
return number2js(js, genRand(mrand));
|
|
)
|
|
|
|
JSC_CCALL(os_randi,
|
|
MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand;
|
|
return JS_NewInt64(js, genRandLong(mrand));
|
|
)
|
|
|
|
JSC_CCALL(os_srand,
|
|
MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand;
|
|
m_seedRand(mrand, js2number(js,argv[0]));
|
|
)
|
|
|
|
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, 0, 4, 0, 0);
|
|
}
|
|
|
|
JSC_CCALL(os_make_line_prim,
|
|
return JS_NULL;
|
|
/*
|
|
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, 0, 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), 0,2,1,0));
|
|
JS_SetPropertyStr(js,prim,"vertices", number2js(js,m->num_vertices));
|
|
JS_SetPropertyStr(js,prim,"color",make_color_buffer(js,js2color(js,argv[4]), m->num_vertices));
|
|
JS_SetPropertyStr(js,prim,"num_indices", number2js(js,m->num_triangles*3));
|
|
JS_SetPropertyStr(js,prim,"first_index", number2js(js,0));
|
|
|
|
parsl_destroy_context(par_ctx);
|
|
|
|
return prim;
|
|
*/
|
|
)
|
|
|
|
static void render_frame(plm_t *mpeg, plm_frame_t *frame, datastream *ds) {
|
|
if (JS_IsNull(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);
|
|
|
|
// Create surface data object instead of SDL_Surface
|
|
JSValue surfData = JS_NewObject(ds->js);
|
|
JS_SetPropertyStr(ds->js, surfData, "width", JS_NewInt32(ds->js, frame->width));
|
|
JS_SetPropertyStr(ds->js, surfData, "height", JS_NewInt32(ds->js, frame->height));
|
|
JS_SetPropertyStr(ds->js, surfData, "format", JS_NewString(ds->js, "rgba32"));
|
|
JS_SetPropertyStr(ds->js, surfData, "pitch", JS_NewInt32(ds->js, frame->width*4));
|
|
JS_SetPropertyStr(ds->js, surfData, "pixels", js_new_blob_stoned_copy(ds->js, rgb, frame->height*frame->width*4));
|
|
|
|
JSValue s[1];
|
|
s[0] = surfData;
|
|
JSValue cb = JS_DupValue(ds->js,ds->callback);
|
|
JSValue ret = JS_Call(ds->js, cb, JS_NULL, 1, s);
|
|
JS_FreeValue(ds->js,cb);
|
|
free(rgb);
|
|
uncaught_exception(ds->js,ret);
|
|
}
|
|
|
|
JSC_CCALL(os_make_video,
|
|
size_t len;
|
|
void *data = js_get_blob_data(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_NULL;
|
|
plm_set_video_decode_callback(ds->plm, render_frame, ds);
|
|
return datastream2js(js,ds);
|
|
)
|
|
|
|
JSC_CCALL(os_rectpack,
|
|
int width = js2number(js,argv[0]);
|
|
int height = js2number(js,argv[1]);
|
|
int num = JS_ArrayLength(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_NULL;
|
|
}
|
|
|
|
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_insertion_sort,
|
|
JSValue arr = argv[0];
|
|
JSValue cmp = argv[1];
|
|
int len = JS_ArrayLength(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_NULL, 2, (JSValue[]){ arr_j, key });
|
|
if (JS_IsException(ret)) {
|
|
JS_FreeValue(js,arr_j);
|
|
JS_FreeValue(js,key);
|
|
return ret;
|
|
}
|
|
double c = js2number(js, ret);
|
|
JS_FreeValue(js, ret);
|
|
|
|
if (c > 0) {
|
|
JS_SetPropertyUint32(js, arr, j + 1, arr_j);
|
|
j--;
|
|
} else {
|
|
JS_FreeValue(js, arr_j);
|
|
break;
|
|
}
|
|
}
|
|
|
|
JS_SetPropertyUint32(js, arr, j + 1, key);
|
|
}
|
|
|
|
ret = JS_DupValue(js,arr);
|
|
)
|
|
|
|
JSC_CCALL(os_power_state,
|
|
SDL_PowerState state = SDL_GetPowerInfo(NULL, NULL);
|
|
switch(state) {
|
|
case SDL_POWERSTATE_ERROR: return JS_ThrowTypeError(js, "Error determining power status");
|
|
case SDL_POWERSTATE_UNKNOWN: return JS_NULL;
|
|
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_NULL;
|
|
)
|
|
|
|
JSC_CCALL(os_cull_sprites,
|
|
ret = JS_NewArray(js);
|
|
int n = 0;
|
|
|
|
JSValue sprites = argv[0];
|
|
shader_globals info = {0}; // TODO: get this as a JS object
|
|
rect camera_rect = {0};
|
|
camera_rect.x = info.camera_pos_world.x - info.render_size.x/2.0;
|
|
camera_rect.y = info.camera_pos_world.y - info.render_size.y/2.0;
|
|
camera_rect.w = info.render_size.x;
|
|
camera_rect.h = info.render_size.y;
|
|
|
|
int len = JS_ArrayLength(js,sprites);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
JSValue sub = JS_GetPropertyUint32(js,sprites,i);
|
|
transform *t;
|
|
JS_GETATOM(js,t,sub,transform,transform)
|
|
|
|
rect sprite = transform2rect(t);
|
|
if (SDL_HasRectIntersectionFloat(&sprite, &camera_rect)) {
|
|
JS_SetPropertyUint32(js,ret,n,JS_DupValue(js,sub));
|
|
n++;
|
|
}
|
|
JS_FreeValue(js,sub);
|
|
}
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_util_funcs[] = {
|
|
MIST_FUNC_DEF(os, guid, 0),
|
|
MIST_FUNC_DEF(os, insertion_sort, 2),
|
|
MIST_FUNC_DEF(util, camera_globals, 1),
|
|
};
|
|
|
|
JSC_CCALL(graphics_hsl_to_rgb,
|
|
float h, s, l;
|
|
JS_ToFloat64(js, &h, argv[0]);
|
|
JS_ToFloat64(js, &s, argv[1]);
|
|
JS_ToFloat64(js, &l, argv[2]);
|
|
float c = (1 - abs(2 * l - 1)) * s;
|
|
float x = c * (1 - abs(fmod((h/60),2) - 1));
|
|
float m = l - c / 2;
|
|
float r = 0, g = 0, b = 0;
|
|
|
|
if (h < 60) { r = c; g = x; }
|
|
else if (h < 120) { r = x; g = c; }
|
|
else if (h < 180) { g = c; b = x; }
|
|
else if (h < 240) { g = x; b = c; }
|
|
else if (h < 300) { r = x; b = c; }
|
|
else { r = c; b = x; }
|
|
|
|
return color2js(js, (colorf){r+m, g+m, b+m, 1});
|
|
)
|
|
|
|
JSC_CCALL(graphics_save_png,
|
|
const char *file = JS_ToCString(js, argv[0]);
|
|
int w, h, comp, pitch;
|
|
JS_ToInt32(js, &w, argv[1]);
|
|
JS_ToInt32(js, &h, argv[2]);
|
|
JS_ToInt32(js, &pitch, argv[4]);
|
|
size_t size;
|
|
void *data = js_get_blob_data(js, &size, argv[3]);
|
|
|
|
if (!stbi_write_png(file, w, h, 4, data, pitch))
|
|
return JS_ThrowInternalError(js, "Could not write png");
|
|
)
|
|
|
|
JSC_CCALL(graphics_save_jpg,
|
|
const char *file = JS_ToCString(js, argv[0]);
|
|
int w, h, comp, pitch, quality;
|
|
JS_ToInt32(js, &w, argv[1]);
|
|
JS_ToInt32(js, &h, argv[2]);
|
|
JS_ToInt32(js, &pitch, argv[4]);
|
|
JS_ToInt32(js, &quality, argv[5]);
|
|
if (!quality) quality = 80;
|
|
size_t size;
|
|
void *data = js_get_blob_data(js, &size, argv[3]);
|
|
|
|
if (!stbi_write_jpg(file, w, h, 4, data, quality))
|
|
return JS_ThrowInternalError(js, "Could not write png");
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_graphics_funcs[] = {
|
|
MIST_FUNC_DEF(os, make_text_buffer, 5),
|
|
MIST_FUNC_DEF(os, rectpack, 3),
|
|
MIST_FUNC_DEF(os, make_texture, 1),
|
|
MIST_FUNC_DEF(os, make_gif, 1),
|
|
MIST_FUNC_DEF(os, make_aseprite, 1),
|
|
MIST_FUNC_DEF(os, cull_sprites, 2),
|
|
MIST_FUNC_DEF(os, make_font, 2),
|
|
MIST_FUNC_DEF(os, make_line_prim, 5),
|
|
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),
|
|
MIST_FUNC_DEF(graphics, save_png, 4),
|
|
MIST_FUNC_DEF(graphics, save_jpg, 4),
|
|
MIST_FUNC_DEF(graphics, font_text_size, 4),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_video_funcs[] = {
|
|
MIST_FUNC_DEF(os, make_video, 1),
|
|
};
|
|
|
|
JSC_SCALL(os_use_embed,
|
|
cell_rt *rt = JS_GetContextOpaque(js);
|
|
ModuleEntry *module_registry = rt->module_registry;
|
|
for (int i = 0; i < arrlen(module_registry); i++) {
|
|
if (strcmp(str,module_registry[i].name) == 0) {
|
|
ret = module_registry[i].fn(js);
|
|
break;
|
|
}
|
|
}
|
|
)
|
|
|
|
JSC_SCALL(os_use_dyn,
|
|
SDL_SharedObject *ptr = SDL_LoadObject(str);
|
|
if (!ptr)
|
|
return JS_ThrowReferenceError(js, "Shared library %s could not be loaded", SDL_GetError());
|
|
|
|
JSValue (*js_use)(JSContext*);
|
|
js_use = (JSValue (*)(JSContext*))SDL_LoadFunction(ptr, "use");
|
|
|
|
if (!js_use)
|
|
ret = JS_ThrowReferenceError(js, "Shared library %s has no use function", str);
|
|
else
|
|
ret = js_use(js);
|
|
|
|
SDL_UnloadObject(ptr);
|
|
)
|
|
|
|
#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_miniz_use(JSContext *js);
|
|
JSValue js_num_use(JSContext *js);
|
|
|
|
JSValue js_graphics_use(JSContext *js) {
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_graphics_funcs,countof(js_graphics_funcs));
|
|
return mod;
|
|
}
|
|
|
|
JSValue js_util_use(JSContext *js) {
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_util_funcs,countof(js_util_funcs));
|
|
return mod;
|
|
}
|
|
|
|
JSValue js_video_use(JSContext *js) {
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_video_funcs,countof(js_video_funcs));
|
|
return mod;
|
|
}
|
|
|
|
JSValue js_console_use(JSContext *js) {
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_console_funcs,countof(js_console_funcs));
|
|
return mod;
|
|
}
|
|
|
|
JSC_CCALL(os_value_id,
|
|
JS_SetPropertyStr(js, argv[0], "id", number2js(js, (uintptr_t)JS_VALUE_GET_PTR(argv[0])));
|
|
return argv[0];
|
|
)
|
|
|
|
#include "qjs_crypto.h"
|
|
#include "qjs_time.h"
|
|
#include "qjs_http.h"
|
|
#include "qjs_wota.h"
|
|
#include "qjs_socket.h"
|
|
#include "qjs_nota.h"
|
|
#include "qjs_layout.h"
|
|
|
|
JSValue js_imgui_use(JSContext *js);
|
|
#define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use}
|
|
|
|
void ffi_load(JSContext *js)
|
|
{
|
|
cell_rt *rt = JS_GetContextOpaque(js);
|
|
|
|
JS_FreeValue(js, js_blob_use(js)); // juice blob
|
|
|
|
uint64_t rr;
|
|
randombytes(&rr,4);
|
|
m_seedRand(&rt->mrand, rr);
|
|
|
|
// cell modules
|
|
arrput(rt->module_registry, MISTLINE(time));
|
|
arrput(rt->module_registry, ((ModuleEntry){"math", js_math_use}));
|
|
arrput(rt->module_registry, MISTLINE(blob));
|
|
|
|
// extra
|
|
arrput(rt->module_registry, ((ModuleEntry){"io", js_io_use}));
|
|
arrput(rt->module_registry, ((ModuleEntry){"fd", js_fd_use}));
|
|
arrput(rt->module_registry, MISTLINE(socket));
|
|
arrput(rt->module_registry, ((ModuleEntry){"os", js_os_use}));
|
|
#ifdef HAVE_QRENCODE
|
|
arrput(rt->module_registry, MISTLINE(qr));
|
|
#endif
|
|
arrput(rt->module_registry, MISTLINE(http));
|
|
arrput(rt->module_registry, MISTLINE(crypto));
|
|
arrput(rt->module_registry, MISTLINE(miniz));
|
|
// arrput(rt->module_registry, MISTLINE(num));
|
|
arrput(rt->module_registry, MISTLINE(kim));
|
|
arrput(rt->module_registry, MISTLINE(utf8));
|
|
arrput(rt->module_registry, MISTLINE(fit));
|
|
arrput(rt->module_registry, MISTLINE(text));
|
|
arrput(rt->module_registry, MISTLINE(wota));
|
|
arrput(rt->module_registry, MISTLINE(nota));
|
|
|
|
// power user
|
|
arrput(rt->module_registry, MISTLINE(js));
|
|
arrput(rt->module_registry, MISTLINE(debug));
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
arrput(rt->module_registry, MISTLINE(dmon));
|
|
#endif
|
|
|
|
arrput(rt->module_registry, MISTLINE(util));
|
|
|
|
// prosperon
|
|
arrput(rt->module_registry, ((ModuleEntry){"sdl_audio", js_sdl_audio_use}));
|
|
arrput(rt->module_registry, ((ModuleEntry){"sdl_video", js_sdl_video_use}));
|
|
arrput(rt->module_registry, ((ModuleEntry){"input", js_input_use}));
|
|
arrput(rt->module_registry, ((ModuleEntry){"surface", js_sdl_surface_use}));
|
|
arrput(rt->module_registry, MISTLINE(spline));
|
|
arrput(rt->module_registry, ((ModuleEntry){"geometry", js_geometry_use}));
|
|
arrput(rt->module_registry, MISTLINE(graphics));
|
|
arrput(rt->module_registry, MISTLINE(video));
|
|
arrput(rt->module_registry, MISTLINE(soloud));
|
|
arrput(rt->module_registry, MISTLINE(layout));
|
|
|
|
// arrput(rt->module_registry, MISTLINE(imgui));
|
|
arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use}));
|
|
|
|
arrput(rt->module_registry, MISTLINE(rtree));
|
|
arrput(rt->module_registry, MISTLINE(sprite));
|
|
arrput(rt->module_registry, MISTLINE(transform));
|
|
|
|
arrput(rt->module_registry, MISTLINE(imgui));
|
|
|
|
#ifndef NSTEAM
|
|
arrput(rt->module_registry, MISTLINE(steam));
|
|
#endif
|
|
|
|
JSValue globalThis = JS_GetGlobalObject(js);
|
|
|
|
JSValue prosp = JS_NewObject(js);
|
|
JS_SetPropertyStr(js,globalThis,"prosperon", prosp);
|
|
|
|
JSValue jsarray = JS_GetPropertyStr(js,globalThis, "Array");
|
|
JSValue array_proto = JS_GetPropertyStr(js,jsarray, "prototype");
|
|
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, countof(js_array_funcs));
|
|
JS_FreeValue(js,jsarray);
|
|
JS_FreeValue(js,array_proto);
|
|
|
|
JSValue jsnumber = JS_GetPropertyStr(js,globalThis, "Number");
|
|
JSValue number_proto = JS_GetPropertyStr(js,jsnumber, "prototype");
|
|
JS_SetPropertyFunctionList(js, number_proto, js_number_funcs, countof(js_number_funcs));
|
|
JS_FreeValue(js,jsnumber);
|
|
JS_FreeValue(js,number_proto);
|
|
|
|
JSValue hidden_fn = JS_NewObject(js);
|
|
// add engine.js-only functions to hidden_fn. It should grab them and then remove so nothing else can use them.
|
|
|
|
// Add modules that should only be accessible to engine.js
|
|
JS_SetPropertyStr(js, hidden_fn, "actor", js_actor_use(js));
|
|
JS_SetPropertyStr(js, hidden_fn, "wota", js_wota_use(js));
|
|
JS_SetPropertyStr(js, hidden_fn, "console", js_console_use(js));
|
|
JS_SetPropertyStr(js, hidden_fn, "nota", js_nota_use(js));
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
JS_SetPropertyStr(js, hidden_fn, "enet", js_enet_use(js));
|
|
#endif
|
|
|
|
// Add functions that should only be accessible to engine.js
|
|
JS_SetPropertyStr(js, hidden_fn, "use_dyn", JS_NewCFunction(js, js_os_use_dyn, "use_dyn", 1));
|
|
JS_SetPropertyStr(js, hidden_fn, "use_embed", JS_NewCFunction(js, js_os_use_embed, "use_embed", 1));
|
|
JS_SetPropertyStr(js, hidden_fn, "rand", JS_NewCFunction(js, js_os_rand, "rand", 0));
|
|
JS_SetPropertyStr(js, hidden_fn, "randi", JS_NewCFunction(js, js_os_randi, "randi", 0));
|
|
JS_SetPropertyStr(js, hidden_fn, "srand", JS_NewCFunction(js, js_os_srand, "srand", 1));
|
|
|
|
const char actorsym_script[] = "var sym = Symbol(`actordata`); sym;";
|
|
|
|
JSValue actorsym = JS_Eval(js, actorsym_script, sizeof(actorsym_script)-1, "internal", 0);
|
|
|
|
JS_SetPropertyStr(js, hidden_fn, "actorsym", actorsym);
|
|
|
|
rt->actor_sym = JS_ValueToAtom(js, actorsym);
|
|
|
|
if (rt->init_wota) {
|
|
JS_SetPropertyStr(js, hidden_fn, "init", wota2value(js, rt->init_wota));
|
|
// init wota can now be freed
|
|
free(rt->init_wota);
|
|
rt->init_wota = NULL;
|
|
}
|
|
|
|
JS_SetPropertyStr(js, prosp, "hidden", hidden_fn);
|
|
|
|
JS_FreeValue(js,globalThis);
|
|
}
|