#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 #include #include #include #include #include #include #include "render.h" #include "model.h" #include "HandmadeMath.h" #include "par/par_streamlines.h" #include "par/par_shapes.h" #include #include "cute_aseprite.h" #include "cgltf.h" #include "cell.h" #include "qjs_blob.h" #include "qjs_dmon.h" #include "qjs_nota.h" #include "qjs_wota.h" #include "qjs_enet.h" #include "qjs_soloud.h" #include "qjs_qr.h" #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 void gui_input(SDL_Event *e); #ifdef _WIN32 #include #else #include #include #ifdef __linux__ #include #endif #endif #include "wildmatch.h" #include "freelist.h" #include "sprite.h" #include #include #include #include #include #include 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 //#else //#include #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); }\ #define JS_SETATOM(JS, TARGET, ATOM, VALUE, TYPE) JS_SetProperty(JS, TARGET, #ATOM, TYPE##2js(JS, VALUE)); int JS_GETBOOL(JSContext *js, JSValue v, const char *prop) { JSValue __v = JS_GetPropertyStr(js,v,prop); int r = JS_ToBool(js,__v); JS_FreeValue(js,__v); return r; } JSValue js_getproperty(JSContext *js, JSValue v, const char *prop) { JSValue ret = JS_GetPropertyStr(js, v, prop); JS_FreeValue(js,ret); return ret; } void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr) { free(ptr); } JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index) { JSValue tstack[3]; tstack[1] = JS_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); 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), 0, 2,0,0); JSValue jsuv = make_gpu_buffer(js, uv, sizeof(HMM_Vec2)*arrlen(buffer), 0, 2,0,0); JSValue jscolor = make_gpu_buffer(js, color, sizeof(HMM_Vec4)*arrlen(buffer), 0, 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_SetPropertyStr(js, ret, "pos", jspos); JS_SetPropertyStr(js, ret, "uv", jsuv); JS_SetPropertyStr(js, ret, "color", jscolor); JS_SetPropertyStr(js, ret, "indices", jsidx); JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts)); JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count)); return ret; } #ifndef _WIN32 #include #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; 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_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_SETATOM(js, obj, x, rect.x, number); JS_SETATOM(js, obj, y, rect.y, number); JS_SETATOM(js, obj, width, rect.w, number); JS_SETATOM(js, obj, height, rect.h, number); return obj; } JSValue ints2js(JSContext *js,int *ints) { JSValue arr = JS_NewArray(js); for (int i = 0; i < arrlen(ints); i++) JS_SetPropertyUint32(js, arr,i, number2js(js,ints[i])); return arr; } int vec_between(HMM_Vec2 p, HMM_Vec2 a, HMM_Vec2 b) { HMM_Vec2 n; n.x = b.x - a.x; n.y = b.y - a.y; n = HMM_NormV2(n); return HMM_DotV2(n, HMM_SubV2(p,a)) > 0 && HMM_DotV2(HMM_MulV2F(n, -1), HMM_SubV2(p,b)) > 0; } /* Determines between which two points in 'segs' point 'p' falls. 0 indicates 'p' comes before the first point. arrlen(segs) indicates it comes after the last point. */ int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) { float shortest = slop < 0 ? INFINITY : slop; int best = -1; for (int i = 0; i < arrlen(segs) - 1; i++) { float a = (segs[i + 1].y - segs[i].y) / (segs[i + 1].x - segs[i].x); float c = segs[i].y - (a * segs[i].x); float b = -1; float dist = fabsf(a * p.x + b * p.y + c) / sqrt(pow(a, 2) + 1); if (dist > shortest) continue; int between = vec_between(p, segs[i], segs[i + 1]); if (between) { shortest = dist; best = i + 1; } else { if (i == 0 && HMM_DistV2(p,segs[0]) < slop) { shortest = dist; best = i; } else if (i == arrlen(segs) - 2 && HMM_DistV2(p,arrlast(segs)) < slop) { shortest = dist; best = arrlen(segs); } } } if (best == 1) { HMM_Vec2 n; n.x = segs[1].x - segs[0].x; n.y = segs[1].y - segs[0].y; n = HMM_NormV2(n); if (HMM_DotV2(n, HMM_SubV2(p,segs[0])) < 0 ){ if (HMM_DistV2(p, segs[0]) >= slop) best = -1; else best = 0; } } if (best == arrlen(segs) - 1) { HMM_Vec2 n; n.x = segs[best - 1].x - segs[best].x; n.y = segs[best - 1].y - segs[best - 1].y; n = HMM_NormV2(n); if (HMM_DotV2(n, HMM_SubV2(p, segs[best])) < 0) { if (HMM_DistV2(p, segs[best]) >= slop) best = -1; else best = arrlen(segs); } } return best; } 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); ) 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_SCALL(font_text_size, font *f = js2font(js,self); float size = js2number(js,argv[0]); if (!size) size = f->height; float letterSpacing = js2number(js,argv[1]); float wrap = js2number(js,argv[2]); ret = vec22js(js,measure_text(str, f, size, letterSpacing, wrap)); ) static const JSCFunctionListEntry js_font_funcs[] = { CGETSET_ADD(font, linegap), MIST_GET(font, height), MIST_GET(font, ascent), MIST_GET(font, descent), MIST_FUNC_DEF(font, text_size, 3), }; // 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); JSValue gif = JS_NewObject(js); ret = gif; if (frames == 1) { // still image, so return 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)); JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, pixels, width*height*4)); JS_SetPropertyStr(js, gif, "surface", surfData); 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)); // Create surface data object instead of SDL_Surface 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)); JS_SetPropertyStr(js, frame, "surface", surfData); 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); // 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; } } 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, 6), 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), }; 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})); arrput(rt->module_registry, MISTLINE(qr)); 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)); arrput(rt->module_registry, MISTLINE(dmon)); 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)); #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)); JS_SetPropertyStr(js, hidden_fn, "enet", js_enet_use(js)); // 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); }