From 66ce48776a5ad0991a7ea39518176f353503d0f3 Mon Sep 17 00:00:00 2001 From: Bobby Lucero Date: Sun, 21 Apr 2024 23:33:39 -0400 Subject: [PATCH] Testing out python bindings, needs refactor A disgusting little race condition has appeared, running will result in a black screen 70% of the time >:( TODO: lambda functions referencing non static graphics methods instead of making graphics methods static state manager (everything is hodge podged into graphics and pycron objects) sort math functions into relevant places (trig, random etc) --- CMakeLists.txt | 1 + python/main.py | 17 +++++++++ src/Graphics/Graphics.cpp | 73 ++++++++++++++++++++++++++++++++------- src/Graphics/Graphics.h | 21 +++++++++-- src/Pycron.cpp | 70 +++++++++++++++++++++++++++++++++---- src/Pycron.h | 5 +++ 6 files changed, 165 insertions(+), 22 deletions(-) create mode 100644 python/main.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f4d93d..99ed260 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,5 +47,6 @@ if(EMSCRIPTEN) endif() file(COPY ${CMAKE_SOURCE_DIR}/resources DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${CMAKE_SOURCE_DIR}/python DESTINATION ${CMAKE_BINARY_DIR}) diff --git a/python/main.py b/python/main.py new file mode 100644 index 0000000..e1e557f --- /dev/null +++ b/python/main.py @@ -0,0 +1,17 @@ + +i = 0 +def update(): + global i + i += 1 + clear(43) + x = sin(i * 0.071234) * 50 + 100 + y = sin(i * 0.0236) * 20 + 80 + circle(x + sin(i * 0.05) * 30, y + cos(i * 0.05) * 30, 5, 15) + circle(x + sin(i * 0.05) * 30, y + cos(i * 0.05) * 30, 3, 11) + circle(x + -sin(i * 0.05) * 30, y + -cos(i * 0.05) * 30, 5, 17) + circle(x + -sin(i * 0.05) * 30, y + -cos(i * 0.05) * 30, 3, 19) + + + +clear(44) +circle(20,30, 5, 17) diff --git a/src/Graphics/Graphics.cpp b/src/Graphics/Graphics.cpp index 3a8c87f..80aff05 100644 --- a/src/Graphics/Graphics.cpp +++ b/src/Graphics/Graphics.cpp @@ -6,6 +6,9 @@ #include "Graphics.h" #include "../Utilities.h" +std::vector Graphics::palette; + + Graphics::Graphics(int screenWidth, int screenHeight, int startupScale) : screenWidth(screenWidth), screenHeight(screenHeight){ startupScreenWidth = screenWidth * startupScale; startupScreenHeight = screenHeight * startupScale; @@ -16,11 +19,19 @@ Graphics::Graphics(int screenWidth, int screenHeight, int startupScale) : screen InitWindow(startupScreenWidth, startupScreenHeight, "test"); SetTargetFPS(60); virtualScreen = LoadRenderTexture(screenWidth, screenHeight); + virtualScreenLocalBounds = {0.0f, 0.0f, (float)virtualScreen.texture.width, -(float)virtualScreen.texture.height }; + updateFunction = nullptr; calculateScreenPositionInWindow(); + + std::cout << origin.x << " : " << origin.y << std::endl; + std::cout << virtualScreenLocalBounds.width << " : " << virtualScreenLocalBounds.height << std::endl; + std::cout << virtualScreenWindowBounds.width << " : " << virtualScreenWindowBounds.height << std::endl; + + } -void Graphics::draw() { +void Graphics::draw(pkpy::VM* vm) { windowShouldClose = WindowShouldClose(); @@ -32,18 +43,12 @@ void Graphics::draw() { BeginTextureMode(virtualScreen); ////////// - ClearBackground(palette[1]); - DrawText(("Hello World " + std::to_string(GetFPS()) + " FPS").c_str(), 5, 5, 5, RAYWHITE); - - DrawRectangle(3, 19, 33, 33, BLACK); - - for (int i = 0; i < 8; ++i) { - for (int j = 0; j < 8; ++j) { - DrawRectangle(4 + i * 4, 20 + j * 4, 3, 3, palette[i + j * 8]); - } + try{ + if(updateFunction != nullptr) + vm->call(updateFunction); + } catch(pkpy::Exception e){ + std::cout << e.summary() << std::endl; } - - DrawCircleLines(mouseX(), mouseY(),3, palette[18]); ////////// EndTextureMode(); @@ -52,7 +57,7 @@ void Graphics::draw() { void Graphics::renderVirtualScreen() { BeginDrawing(); - ClearBackground(palette[0]); + //ClearBackground(palette[16]); DrawTexturePro(virtualScreen.texture, virtualScreenLocalBounds, virtualScreenWindowBounds, origin, 0.0f, WHITE); EndDrawing(); } @@ -114,6 +119,48 @@ void Graphics::toggleFullScreen() { calculateScreenPositionInWindow(); } +void Graphics::bindMethods(pkpy::VM *vm) { + vm->bind(vm->builtins, "clear(color: int)", reinterpret_cast(Clear)); + vm->bind(vm->builtins, "pixel(x: int, y: int, color: int)", reinterpret_cast(Pixel)); + vm->bind(vm->builtins, "circle(x: int, y: int, radius: float, color: int)", reinterpret_cast(Circle)); +} + +void Graphics::Clear(pkpy::VM* vm, pkpy::ArgsView args) { + int paletteIndex = pkpy::py_cast(vm, args[0]); + if(paletteIndex < 0 || paletteIndex >= palette.size()) paletteIndex = 0; + ClearBackground(palette[paletteIndex]); +} + +void Graphics::Pixel(pkpy::VM* vm, pkpy::ArgsView args) { + int x = pkpy::py_cast(vm, args[0]); + int y = pkpy::py_cast(vm, args[1]); + int paletteIndex = pkpy::py_cast(vm, args[2]); + DrawPixel(x, y, palette[paletteIndex]); +} + +void Graphics::Circle(pkpy::VM* vm, pkpy::ArgsView args) { + float x = pkpy::py_cast(vm, args[0]); + float y = pkpy::py_cast(vm, args[1]); + float radius = pkpy::py_cast(vm, args[2]); + int paletteIndex = pkpy::py_cast(vm, args[3]); + DrawCircle(x, y, radius, palette[paletteIndex]); +} + +void Graphics::searchForDrawFunc(pkpy::VM* vm) { + updateFunction = vm->eval("update"); + if(updateFunction == nullptr){ + std::cout << "Can't find update function" << std::endl; + } +} + +void Graphics::beginDraw() { + BeginTextureMode(virtualScreen); +} + +void Graphics::endDraw() { + EndTextureMode(); +} + diff --git a/src/Graphics/Graphics.h b/src/Graphics/Graphics.h index ece4ffe..16b3bdd 100644 --- a/src/Graphics/Graphics.h +++ b/src/Graphics/Graphics.h @@ -1,5 +1,6 @@ #pragma once #include "raylib.h" +#include "pocketpy/vm.h" #include #include @@ -18,7 +19,6 @@ private: Rectangle virtualScreenLocalBounds; // virtual screen bounds RenderTexture2D virtualScreen; // actual pixel screen - std::vector palette; private: void renderVirtualScreen(); @@ -31,15 +31,32 @@ public: bool windowShouldClose; + static std::vector palette; + + pkpy::PyObject* updateFunction; + + public: Graphics(int screenWidth, int screenHeight, int startupScale); - void draw(); + void draw(pkpy::VM* vm); + + void beginDraw(); + void endDraw(); void loadPalette(std::string path); int mouseX(); int mouseY(); void toggleFullScreen(); + void bindMethods(pkpy::VM* vm); + + void searchForDrawFunc(pkpy::VM* vm); + + + static void Clear(pkpy::VM* vm, pkpy::ArgsView args); + static void Pixel(pkpy::VM* vm, pkpy::ArgsView args); + static void Circle(pkpy::VM* vm, pkpy::ArgsView args); + }; diff --git a/src/Pycron.cpp b/src/Pycron.cpp index dfe1086..0f440d2 100644 --- a/src/Pycron.cpp +++ b/src/Pycron.cpp @@ -1,10 +1,27 @@ // // Created by Bobby Lucero on 4/20/24. // +#include +#include +#include +#include #include "Pycron.h" #include "Utilities.h" #include "Graphics/Graphics.h" + +std::string loadFileToString(const std::string& filename) { + std::ifstream file(filename); // Open the file + std::stringstream buffer; // String stream to hold file content + if (file.is_open()) { // Check if file is open + buffer << file.rdbuf(); // Read the entire file into the buffer + file.close(); // Close the file + } else { + std::cerr << "Unable to open file: " << filename << std::endl; + } + return buffer.str(); // Return the content string +} + Pycron::Pycron() { SetTraceLogLevel(LOG_ERROR); @@ -13,14 +30,21 @@ Pycron::Pycron() { vm = new pkpy::VM(); + bindMethods(vm); + + graphics->bindMethods(vm); + + std::string python = loadFileToString("../python/main.py"); + + graphics->beginDraw(); try { - pkpy::CodeObject_ code = vm->compile("return 'test'", "main.py", pkpy::EXEC_MODE, false); - pkpy::PyObject* obj = vm->_exec(code, vm->_main); - - auto& str = pkpy::py_cast(vm, obj); + pkpy::CodeObject_ code = vm->compile(python, "main.py", pkpy::EXEC_MODE, false); + vm->_exec(code, vm->_main); + graphics->searchForDrawFunc(vm); }catch (pkpy::Exception e) { - + std::cout << e.summary() << std::endl; } + graphics->endDraw(); } @@ -37,6 +61,38 @@ void Pycron::StartGameLoop() { if (IsKeyPressed(KEY_F)) { graphics->toggleFullScreen(); } - graphics->draw(); + graphics->draw(vm); } -} \ No newline at end of file +} + +void Pycron::bindMethods(pkpy::VM *vm) { + vm->bind(vm->builtins, "rnd(min: int, max: int) -> int", reinterpret_cast(getRandomNumber)); + vm->bind(vm->builtins, "sin(num: float) -> float", reinterpret_cast(getSin)); + vm->bind(vm->builtins, "cos(num: float) -> float", reinterpret_cast(getCos)); +} + + +pkpy::PyObject* Pycron::getRandomNumber(pkpy::VM* vm, pkpy::ArgsView args) { + int min = pkpy::py_cast(vm, args[0]); + int max = pkpy::py_cast(vm, args[1]); + // Seed the random number generator with a random device + std::random_device rd; + std::mt19937 gen(rd()); + + // Define a uniform distribution for the range [min, max] + std::uniform_int_distribution distribution(min, max); + + // Generate a random number within the specified range + return pkpy::py_var(vm, distribution(gen)); +} + +pkpy::PyObject* Pycron::getSin(pkpy::VM* vm, pkpy::ArgsView args) { + auto num = pkpy::py_cast(vm, args[0]); + return pkpy::py_var(vm, sin(num)); +} + +pkpy::PyObject* Pycron::getCos(pkpy::VM* vm, pkpy::ArgsView args) { + auto num = pkpy::py_cast(vm, args[0]); + return pkpy::py_var(vm, cos(num)); +} + diff --git a/src/Pycron.h b/src/Pycron.h index be367bd..4247132 100644 --- a/src/Pycron.h +++ b/src/Pycron.h @@ -17,6 +17,11 @@ public: ~Pycron(); void StartGameLoop(); + void bindMethods(pkpy::VM* vm); + + static pkpy::PyObject* getRandomNumber(pkpy::VM* vm, pkpy::ArgsView args); + static pkpy::PyObject* getSin(pkpy::VM* vm, pkpy::ArgsView args); + static pkpy::PyObject* getCos(pkpy::VM* vm, pkpy::ArgsView args); };