From ac9fd39cf4c2e9ee4c5097d92ba207392039c3a5 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 5 Dec 2024 12:00:12 -0600 Subject: [PATCH] use colorf --- meson.build | 10 ++- scripts/tween.js | 1 - source/HandmadeMath.h | 2 +- source/cv.cpp | 190 ++++++++++++++++++++---------------------- source/cv.hpp | 2 +- source/font.c | 6 +- source/font.h | 2 +- source/jsffi.c | 92 ++++++++++++-------- source/render.h | 2 + 9 files changed, 161 insertions(+), 146 deletions(-) diff --git a/meson.build b/meson.build index fda19c18..1a4bc0ed 100644 --- a/meson.build +++ b/meson.build @@ -24,12 +24,12 @@ if host_machine.system() == 'darwin' endif cc = meson.get_compiler('c') - -# adding cblas + deps += dependency('sdl3') if host_machine.system() == 'darwin' deps += dependency('appleframeworks', modules: 'accelerate') else deps += dependency('cblas') + endif if host_machine.system() == 'linux' @@ -71,10 +71,12 @@ deps += dependency('qjs-layout',static:true) deps += dependency('qjs-nota',static:true) deps += dependency('qjs-miniz',static:true) deps += dependency('qjs-soloud',static:true) -deps += dependency('sdl3') + deps += dependency('physfs',static:true) -deps += dependency('opencv') + +deps += dependency('opencv4') +#deps += cc.find_library('opencv') deps += dependency('threads') diff --git a/scripts/tween.js b/scripts/tween.js index bfb84309..c1e86581 100644 --- a/scripts/tween.js +++ b/scripts/tween.js @@ -124,7 +124,6 @@ var tween = function (from, to, time, fn) { } }; var stop = Register.update.register(update); - console.log(update) return stop; }; diff --git a/source/HandmadeMath.h b/source/HandmadeMath.h index b700162f..e1029828 100644 --- a/source/HandmadeMath.h +++ b/source/HandmadeMath.h @@ -382,7 +382,7 @@ typedef union HMM_Vec4 { HMM_Vec2 cp; HMM_Vec2 wh; }; - + HMM_Quat quat; struct {float x, y, z, w; }; struct {float r, g, b, a; }; diff --git a/source/cv.cpp b/source/cv.cpp index 0cd3accd..aa360828 100644 --- a/source/cv.cpp +++ b/source/cv.cpp @@ -2,26 +2,27 @@ #include #include #include +#include cv::Mat SDL_SurfaceToMat(SDL_Surface* surface) { if (!surface) { throw std::invalid_argument("SDL_Surface pointer is null."); } // Convert the surface to a known pixel format (e.g., RGBA32) - // SDL_Surface* convertedSurface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); - // if (!convertedSurface) { -// throw std::runtime_error("Failed to convert SDL_Surface to RGBA32 format."); -// } + SDL_Surface* convertedSurface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); + if (!convertedSurface) { + throw std::runtime_error("Failed to convert SDL_Surface to RGBA32 format."); + } // Create a cv::Mat with the same size and type - cv::Mat mat(surface->h, surface->w, CV_8UC4, surface->pixels, surface->pitch); + cv::Mat mat(convertedSurface->h, convertedSurface->w, CV_8UC4, convertedSurface->pixels, convertedSurface->pitch); // Convert RGBA to BGR cv::Mat matBGR; cv::cvtColor(mat, matBGR, cv::COLOR_RGBA2BGR); // Free the converted surface - // SDL_DestroySurface(convertedSurface); + SDL_DestroySurface(convertedSurface); return matBGR; } @@ -51,101 +52,88 @@ cv::Mat SDL_SurfaceToMat_YUY2(SDL_Surface* surface) { // Function to perform feature matching -extern "C" {bool detectImageInWebcam(SDL_Surface* webcamSurface, SDL_Surface* targetSurface, double matchThreshold = 10.0) { - // Convert SDL_Surface to cv::Mat - cv::Mat webcamMat = SDL_SurfaceToMat(webcamSurface); - cv::Mat targetMat = SDL_SurfaceToMat(targetSurface); -// cv::imshow("Webcam Image", webcamMat); // uncomment when testing image appearance -//cv::waitKey(0); - - - // Initialize ORB detector -cv::Ptr orb = cv::ORB::create(); - /* 1500, // nfeatures - 1.2f, // scaleFactor - 8, // nlevels - 31, // edgeThreshold - 0, // firstLevel - 2, // WTA_K - cv::ORB::HARRIS_SCORE, // scoreType - 31, // patchSize - 20 // fastThreshold -);*/ - - - // Detect keypoints and compute descriptors - std::vector keypointsWebcam, keypointsTarget; - cv::Mat descriptorsWebcam, descriptorsTarget; - - orb->detectAndCompute(webcamMat, cv::noArray(), keypointsWebcam, descriptorsWebcam); - orb->detectAndCompute(targetMat, cv::noArray(), keypointsTarget, descriptorsTarget); - - if (descriptorsWebcam.empty() || descriptorsTarget.empty()) { - fprintf(stderr, "No descriptors found. On webcam? %d. On input image? %d.\n", !descriptorsWebcam.empty(), !descriptorsTarget.empty()); - return false; - } - - // Match descriptors using Brute-Force matcher with Hamming distance - /* cv::BFMatcher matcher(cv::NORM_HAMMING, true); // crossCheck=true - std::vector matches; - matcher.match(descriptorsTarget, descriptorsWebcam, matches); -*/ -// Initialize BFMatcher without crossCheck -cv::BFMatcher matcher(cv::NORM_HAMMING); - -// Perform k-NN matching with k=2 -std::vector> matches; -matcher.knnMatch(descriptorsTarget, descriptorsWebcam, matches, 2); - - if (matches.empty()) { - return false; - } -/* - // Filter good matches based on distance - double max_dist = 0; double min_dist = 100; - - // Find min and max distances - for (const auto& match : matches) { - double dist = match.distance; - if (dist < min_dist) min_dist = dist; - if (dist > max_dist) max_dist = dist; - } - - // Define a threshold to identify good matches - std::vector goodMatches; - for (const auto& match : matches) { - if (match.distance <= std::max(2 * min_dist, 30.0)) { - goodMatches.push_back(match); +extern "C" { + // Modified function to return a pointer to Rectangle instead of a bool + SDL_FRect* detectImageInWebcam(SDL_Surface* webcamSurface, SDL_Surface* targetSurface, double matchThreshold = 10.0) { + // Convert SDL_Surface to cv::Mat + cv::Mat webcamMat = SDL_SurfaceToMat(webcamSurface); + cv::Mat targetMat = SDL_SurfaceToMat(targetSurface); + + // Initialize ORB detector + cv::Ptr orb = cv::ORB::create(); + + // Detect keypoints and compute descriptors + std::vector keypointsWebcam, keypointsTarget; + cv::Mat descriptorsWebcam, descriptorsTarget; + + orb->detectAndCompute(webcamMat, cv::noArray(), keypointsWebcam, descriptorsWebcam); + orb->detectAndCompute(targetMat, cv::noArray(), keypointsTarget, descriptorsTarget); + + // Check if descriptors are found + if (descriptorsWebcam.empty() || descriptorsTarget.empty()) { + fprintf(stderr, "No descriptors found. On webcam? %d. On input image? %d.\n", + !descriptorsWebcam.empty(), !descriptorsTarget.empty()); + return NULL; + } + + // Initialize BFMatcher without crossCheck + cv::BFMatcher matcher(cv::NORM_HAMMING); + + // Perform k-NN matching with k=2 + std::vector> matches; + matcher.knnMatch(descriptorsTarget, descriptorsWebcam, matches, 2); + + // Check if any matches are found + if (matches.empty()) { + return NULL; + } + + // Apply Lowe's ratio test to filter good matches + const float ratioThresh = 0.75f; + std::vector goodMatches; + for (size_t i = 0; i < matches.size(); i++) { + if (matches[i].size() < 2) + continue; // Not enough matches + const cv::DMatch& bestMatch = matches[i][0]; + const cv::DMatch& betterMatch = matches[i][1]; + + float ratio = bestMatch.distance / betterMatch.distance; + if (ratio < ratioThresh) { + goodMatches.push_back(bestMatch); + } + } + + // Determine if enough good matches are found + if (static_cast(goodMatches.size()) >= matchThreshold) { + // Collect the locations of the matched keypoints in the webcam image + std::vector pointsWebcam; + pointsWebcam.reserve(goodMatches.size()); + for (const auto& match : goodMatches) { + pointsWebcam.emplace_back(keypointsWebcam[match.trainIdx].pt); + } + + // Compute the bounding rectangle that encompasses all matched points + cv::Rect boundingRect = cv::boundingRect(pointsWebcam); + + // Allocate memory for the Rectangle struct + SDL_FRect* rect = (SDL_FRect*)malloc(sizeof(*rect)); + if (!rect) { + // Allocation failed + fprintf(stderr, "Memory allocation for Rectangle failed.\n"); + return NULL; + } + + // Populate the Rectangle struct with bounding rectangle data + + rect->x = boundingRect.x; + rect->y = boundingRect.y; + rect->w = boundingRect.width; + rect->h = boundingRect.height; + + return rect; + } else { + // Not enough matches; return NULL + return NULL; } } -*/ -// Apply Lowe's ratio test -const float ratioThresh = 0.75f; -std::vector goodMatches; -for (size_t i = 0; i < matches.size(); i++) { - if (matches[i].size() < 2) - continue; // Not enough matches - const cv::DMatch& bestMatch = matches[i][0]; - const cv::DMatch& betterMatch = matches[i][1]; - - float ratio = bestMatch.distance / betterMatch.distance; - if (ratio < ratioThresh) { - goodMatches.push_back(bestMatch); - } -} - - // Debug: Print number of good matches -// std::cout << "Good Matches: " << goodMatches.size() << std::endl; - - // Optionally, visualize matches - /* - cv::Mat imgMatches; - cv::drawMatches(targetMat, keypointsTarget, webcamMat, keypointsWebcam, goodMatches, imgMatches); - cv::imshow("Good Matches", imgMatches); - cv::waitKey(30); - */ - - // Determine if enough good matches are found - return (goodMatches.size() >= matchThreshold); -} } diff --git a/source/cv.hpp b/source/cv.hpp index 49addd1b..52a5c7ae 100644 --- a/source/cv.hpp +++ b/source/cv.hpp @@ -6,7 +6,7 @@ #ifdef __cplusplus extern "C" { #endif - bool detectImageInWebcam(SDL_Surface *webcam, SDL_Surface *img, double threshold); + SDL_FRect *detectImageInWebcam(SDL_Surface *webcam, SDL_Surface *img, double threshold); #ifdef __cplusplus } diff --git a/source/font.c b/source/font.c index e12dcc71..37047e10 100644 --- a/source/font.c +++ b/source/font.c @@ -152,7 +152,7 @@ void sdrawCharacter(struct text_vert **buffer, stbtt_packedchar c, HMM_Vec2 curs arrput(*buffer, vert); } -void draw_char_verts(struct text_vert **buffer, struct character c, HMM_Vec2 cursor, float scale, struct rgba color) +void draw_char_verts(struct text_vert **buffer, struct character c, HMM_Vec2 cursor, float scale, colorf color) { // packedchar has // Adds four verts: bottom left, bottom right, top left, top right @@ -161,7 +161,7 @@ void draw_char_verts(struct text_vert **buffer, struct character c, HMM_Vec2 cur bl.pos.y = cursor.Y + c.quad.y; bl.uv.x = c.uv.x; bl.uv.y = c.uv.y; - rgba2floats(bl.color.e, color); + bl.color = color; arrput(*buffer, bl); @@ -282,7 +282,7 @@ HMM_Vec2 measure_text(const char *text, font *f, float size, float letterSpacing return dim; } /* pos given in screen coordinates */ -struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap) { +struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, float scale, colorf color, float wrap) { int wrapAtWord = 1; text_vert *buffer = NULL; int len = strlen(text); diff --git a/source/font.h b/source/font.h index 95c1a2d9..f87f295c 100644 --- a/source/font.h +++ b/source/font.h @@ -51,7 +51,7 @@ typedef struct Character glyph; void font_free(JSRuntime *rt,font *f); struct sFont *MakeFont(void *data, size_t len, int height); -struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap); +struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, float scale, colorf color, float wrap); HMM_Vec2 measure_text(const char *text, font *f, float scale, float letterSpacing, float wrap); // Flushes all letters from renderText calls into the provided buffer diff --git a/source/jsffi.c b/source/jsffi.c index 8a2f99eb..c5f988d7 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -339,15 +339,17 @@ double js2angle(JSContext *js,JSValue v) { return n * HMM_TurnToRad; } -struct rgba js2color(JSContext *js,JSValue v) { +typedef HMM_Vec4 colorf; + +colorf js2color(JSContext *js,JSValue v) { JSValue c[4]; for (int i = 0; i < 4; i++) c[i] = JS_GetPropertyUint32(js,v,i); float a = JS_IsUndefined(c[3]) ? 1.0 : js2number(js,c[3]); - struct rgba color = { - .r = js2number(js,c[0])*RGBA_MAX, - .g = js2number(js,c[1])*RGBA_MAX, - .b = js2number(js,c[2])*RGBA_MAX, - .a = a*RGBA_MAX, + colorf color = { + .r = js2number(js,c[0]), + .g = js2number(js,c[1]), + .b = js2number(js,c[2]), + .a = a, }; for (int i = 0; i < 4; i++) JS_FreeValue(js,c[i]); @@ -355,13 +357,13 @@ struct rgba js2color(JSContext *js,JSValue v) { return color; } -JSValue color2js(JSContext *js,struct rgba color) +JSValue color2js(JSContext *js, colorf color) { JSValue arr = JS_NewArray(js); - JS_SetPropertyUint32(js, arr,0,number2js(js,(double)color.r/255)); - JS_SetPropertyUint32(js, arr,1,number2js(js,(double)color.g/255)); - JS_SetPropertyUint32(js, arr,2,number2js(js,(double)color.b/255)); - JS_SetPropertyUint32(js, arr,3,number2js(js,(double)color.a/255)); + 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; } @@ -700,8 +702,8 @@ JSC_SCALL(render_text_rect, JSC_CCALL(render_draw_color, SDL_Renderer *renderer = js2SDL_Renderer(js,self); - struct rgba rgba = js2color(js,argv[0]); - SDL_SetRenderDrawColor(renderer, rgba.r, rgba.g, rgba.b, rgba.a); + colorf rgba = js2color(js,argv[0]); + SDL_SetRenderDrawColorFloat(renderer, rgba.r, rgba.g, rgba.b, rgba.a); ) static const JSCFunctionListEntry js_render_funcs[] = { @@ -745,7 +747,7 @@ JSC_CCALL(os_make_text_buffer, float size = js2number(js,argv[2]); font *f = js2font(js,argv[5]); if (!size) size = f->height; - struct rgba c = js2color(js,argv[3]); + 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); @@ -1455,6 +1457,22 @@ JSC_SSCALL(game_glob, return JS_NewBool(js, 0); ) +JSC_CCALL(game_camera_name, + const char *name = SDL_GetCameraName(js2number(js,argv[0])); + if (!name) return JS_ThrowReferenceError(js, "Could not get camera name from id %d.", js2number(js,argv[0])); + + return JS_NewString(js, name); +) + +JSC_CCALL(game_camera_position, + SDL_CameraPosition pos = SDL_GetCameraPosition(js2number(js,argv[0])); + switch(pos) { + case SDL_CAMERA_POSITION_UNKNOWN: return JS_NewString(js,"unknown"); + case SDL_CAMERA_POSITION_FRONT_FACING: return JS_NewString(js,"front"); + case SDL_CAMERA_POSITION_BACK_FACING: return JS_NewString(js,"back"); + } +) + static const JSCFunctionListEntry js_game_funcs[] = { MIST_FUNC_DEF(game, engine_start, 1), MIST_FUNC_DEF(game, engine_input,1), @@ -1462,6 +1480,8 @@ static const JSCFunctionListEntry js_game_funcs[] = { MIST_FUNC_DEF(game, renderers, 0), MIST_FUNC_DEF(game, cameras, 0), MIST_FUNC_DEF(game, open_camera, 1), + MIST_FUNC_DEF(game, camera_name,1), + MIST_FUNC_DEF(game, camera_position,1), MIST_FUNC_DEF(game, glob, 2), }; @@ -1505,15 +1525,15 @@ JSC_CCALL(SDL_Renderer_present, JSC_CCALL(SDL_Renderer_draw_color, SDL_Renderer *renderer = js2SDL_Renderer(js,self); - struct rgba color = js2color(js,argv[0]); - SDL_SetRenderDrawColor(renderer, color.r,color.g,color.b,color.a); + colorf color = js2color(js,argv[0]); + SDL_SetRenderDrawColorFloat(renderer, color.r,color.g,color.b,color.a); ) JSC_CCALL(SDL_Renderer_rect, SDL_Renderer *r = js2SDL_Renderer(js,self); if (!JS_IsUndefined(argv[1])) { - struct rgba color = js2color(js,argv[1]); - SDL_SetRenderDrawColor(r, color.r, color.g, color.b, color.a); + colorf color = js2color(js,argv[1]); + SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a); } if (JS_IsArray(js,argv[0])) { @@ -1553,8 +1573,8 @@ JSC_CCALL(renderer_load_texture, JSC_CCALL(SDL_Renderer_fillrect, SDL_Renderer *r = js2SDL_Renderer(js,self); if (!JS_IsUndefined(argv[1])) { - struct rgba color = js2color(js,argv[1]); - SDL_SetRenderDrawColor(r, color.r, color.g, color.b, color.a); + colorf color = js2color(js,argv[1]); + SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a); } if (JS_IsArray(js,argv[0])) { @@ -1585,8 +1605,9 @@ JSC_CCALL(renderer_texture, dst.y -= dst.h; if (!JS_IsUndefined(argv[3])) { - struct rgba color = js2color(js,argv[3]); - SDL_SetTextureColorMod(tex, color.r, color.g, color.b); + colorf color = js2color(js,argv[3]); + SDL_SetTextureColorModFloat(tex, color.r, color.g, color.b); + SDL_SetTextureAlphaModFloat(tex,color.a); } if (JS_IsUndefined(argv[2])) SDL_RenderTexture(renderer,tex,NULL,&dst); @@ -1639,8 +1660,8 @@ JSC_CCALL(renderer_get_image, JSC_SCALL(renderer_fasttext, SDL_Renderer *r = js2SDL_Renderer(js,self); if (!JS_IsUndefined(argv[2])) { - struct rgba color = js2color(js,argv[2]); - SDL_SetRenderDrawColor(r, color.r, color.g, color.b, color.a); + colorf color = js2color(js,argv[2]); + SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a); } HMM_Vec2 pos = js2vec2(js,argv[1]); pos.y *= -1; @@ -1651,8 +1672,8 @@ JSC_SCALL(renderer_fasttext, JSC_CCALL(renderer_line, SDL_Renderer *r = js2SDL_Renderer(js,self); if (!JS_IsUndefined(argv[1])) { - struct rgba color = js2color(js,argv[1]); - SDL_SetRenderDrawColor(r, color.r, color.g, color.b, color.a); + colorf color = js2color(js,argv[1]); + SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a); } if (JS_IsArray(js,argv[0])) { @@ -1671,8 +1692,8 @@ JSC_CCALL(renderer_line, JSC_CCALL(renderer_point, SDL_Renderer *r = js2SDL_Renderer(js,self); if (!JS_IsUndefined(argv[1])) { - struct rgba color = js2color(js,argv[1]); - SDL_SetRenderDrawColor(r, color.r, color.g, color.b, color.a); + colorf color = js2color(js,argv[1]); + SDL_SetRenderDrawColorFloat(r, color.r, color.g, color.b, color.a); } if (JS_IsArray(js,argv[0])) { @@ -1867,21 +1888,21 @@ static SDL_PixelFormatDetails pdetails = { JSC_CCALL(surface_fill, SDL_Surface *src = js2SDL_Surface(js,self); - struct rgba color = js2color(js,argv[0]); + colorf color = js2color(js,argv[0]); rect r = { .x = 0, .y = 0, .w = src->w, .h = src->h }; - SDL_FillSurfaceRect(src, &r, SDL_MapRGBA(&pdetails, NULL, color.r,color.g,color.b,color.a)); + //SDL_FillSurfaceRect(src, &r, SDL_MapRGBA(&pdetails, NULL, color.r,color.g,color.b,color.a)); ) JSC_CCALL(surface_rect, SDL_Surface *dst = js2SDL_Surface(js,self); rect r = js2rect(js,argv[0]); - struct rgba color = js2color(js,argv[1]); - SDL_FillSurfaceRect(dst,&r,SDL_MapRGBA(&pdetails,NULL,color.r,color.g,color.b,color.a)); + colorf color = js2color(js,argv[1]); + //SDL_FillSurfaceRect(dst,&r,SDL_MapRGBA(&pdetails,NULL,color.r,color.g,color.b,color.a)); ) JSC_CCALL(surface_dup, @@ -3117,12 +3138,15 @@ JSC_CCALL(os_make_sprite_mesh, JS_SetProperty(js, ret, count_atom, number2js(js, count)); ) -bool detectImageInWebcam(SDL_Surface *a, SDL_Surface *b, double t); +SDL_FRect *detectImageInWebcam(SDL_Surface *a, SDL_Surface *b, double t); JSC_CCALL(os_match_img, SDL_Surface *img1 = js2SDL_Surface(js,argv[0]); SDL_Surface *img2 = js2SDL_Surface(js,argv[1]); double threshold = js2number(js,argv[2]); - return JS_NewBool(js, detectImageInWebcam(img1,img2,threshold)); + SDL_FRect *r = detectImageInWebcam(img1,img2,threshold); + if (!r) return JS_UNDEFINED; + ret = rect2js(js, *r); + free(r); ) static const JSCFunctionListEntry js_os_funcs[] = { diff --git a/source/render.h b/source/render.h index 23c38e16..7e73e0b2 100644 --- a/source/render.h +++ b/source/render.h @@ -44,6 +44,8 @@ struct rgba { }; typedef struct rgba rgba; +typedef HMM_Vec4 rgbaf; +typedef HMM_Vec4 colorf; static inline rgba vec2rgba(HMM_Vec4 v) { return (rgba){v.e[0]*255,v.e[1]*255,v.e[2]*255,v.e[3]*255};