diff --git a/CMakeLists.txt b/CMakeLists.txt index 3391b43..11dbe05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ set(MAIN_HEADERS src/define.h src/signals/signal.h src/signals/signalconnection.h + src/signals/rubyDispatcher.h ) set(MAIN_SOURCE @@ -127,6 +128,7 @@ set(MAIN_SOURCE src/modloader.cpp src/sunshine.cpp src/lightmap.cpp + src/signals/rubyDispatcher.cpp ) if(WIN32) @@ -265,6 +267,7 @@ set(BINDING_HEADERS binding-mri/viewportelement-binding.h binding-mri/flashable-binding.h binding-mri/keybindings-binding.h + binding-mri/signalconnection-binding.h ) set(BINDING_SOURCE binding-mri/binding-mri.cpp @@ -294,6 +297,7 @@ set(BINDING_SOURCE binding-mri/modloader-binding.cpp binding-mri/keybindings-binding.cpp binding-mri/lightmap-binding.cpp + binding-mri/signalconnection-binding.cpp ) source_group("Binding Source" FILES ${BINDING_SOURCE} ${BINDING_HEADERS}) diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp index 2cc93aa..6ceeaa0 100644 --- a/binding-mri/binding-mri.cpp +++ b/binding-mri/binding-mri.cpp @@ -121,6 +121,7 @@ void tilemapBindingInit(); void windowVXBindingInit(); void tilemapVXBindingInit(); void TimeBindingInit(); +void SignalConnectionBindingInit(); void inputBindingInit(); void audioBindingInit(); @@ -165,6 +166,7 @@ static void mriBindingInit(){ windowBindingInit(); tilemapBindingInit(); TimeBindingInit(); + SignalConnectionBindingInit(); inputBindingInit(); audioBindingInit(); graphicsBindingInit(); diff --git a/binding-mri/graphics-binding.cpp b/binding-mri/graphics-binding.cpp index f52af92..176a0ea 100644 --- a/binding-mri/graphics-binding.cpp +++ b/binding-mri/graphics-binding.cpp @@ -26,6 +26,12 @@ #include "binding-types.h" #include "exception.h" #include "config.h" +#include "debugwriter.h" +#include "signals/rubydispatcher.h" + +#include "signalconnection-binding.h" + +#include RB_METHOD(graphicsUpdate){ RB_UNUSED_PARAM; @@ -208,19 +214,35 @@ DEF_GRA_PROP_B(Frameskip) _rb_define_module_function(module, prop_name_s "=", graphics##Set##PropName); \ } +static VALUE graphicsWindowMoved(VALUE self){ + RUBY_CONNECTION + conn->connection = shState->windowSignals.moved.Connect([conn](int x, int y){ + shState->rubyDispatcher().invoke([conn, x, y]{ + rb_funcall(conn->proc, rb_intern("call"), 2, INT2NUM(x), INT2NUM(y)); + }); + }); + return TypedData_Wrap_Struct(rb_cRubyConnection, &rubyConnection_type, conn); +} + +static VALUE graphicsWindowResized(VALUE self){ + RUBY_CONNECTION + conn->connection = shState->windowSignals.resized.Connect([conn](int w, int h){ + shState->rubyDispatcher().invoke([conn, w, h]{ + rb_funcall(conn->proc, rb_intern("call"), 2, INT2NUM(w), INT2NUM(h)); + }); + }); + return TypedData_Wrap_Struct(rb_cRubyConnection, &rubyConnection_type, conn); +} + void graphicsBindingInit(){ VALUE module = rb_define_module("Graphics"); - _rb_define_module_function(module, "update", graphicsUpdate); - _rb_define_module_function(module, "freeze", graphicsFreeze); - _rb_define_module_function(module, "transition", graphicsTransition); - _rb_define_module_function(module, "frame_reset", graphicsFrameReset); + // Signals - _rb_define_module_function(module, "__reset__", graphicsReset); - - INIT_GRA_PROP_BIND( FrameRate, "frame_rate" ); - INIT_GRA_PROP_BIND( FrameCount, "frame_count" ); + rb_define_module_function(module, "window_moved", RUBY_METHOD_FUNC(graphicsWindowMoved), 0); + rb_define_module_function(module, "window_resized", RUBY_METHOD_FUNC(graphicsWindowResized), 0); + // Functions _rb_define_module_function(module, "x", graphicsPosX); _rb_define_module_function(module, "y", graphicsPosY); _rb_define_module_function(module, "width", graphicsWidth); @@ -231,7 +253,16 @@ void graphicsBindingInit(){ _rb_define_module_function(module, "snap_to_bitmap", graphicsSnapToBitmap); _rb_define_module_function(module, "resize_screen", graphicsResizeScreen); _rb_define_module_function(module, "move_screen", graphicsMoveScreen); + _rb_define_module_function(module, "update", graphicsUpdate); + _rb_define_module_function(module, "freeze", graphicsFreeze); + _rb_define_module_function(module, "transition", graphicsTransition); + _rb_define_module_function(module, "frame_reset", graphicsFrameReset); + _rb_define_module_function(module, "__reset__", graphicsReset); + + // Variables + INIT_GRA_PROP_BIND( FrameRate, "frame_rate" ); + INIT_GRA_PROP_BIND( FrameCount, "frame_count" ); INIT_GRA_PROP_BIND( Brightness, "brightness" ); INIT_GRA_PROP_BIND( Fullscreen, "fullscreen" ); INIT_GRA_PROP_BIND( ShowCursor, "show_cursor" ); diff --git a/binding-mri/signalconnection-binding.cpp b/binding-mri/signalconnection-binding.cpp new file mode 100644 index 0000000..de2dd58 --- /dev/null +++ b/binding-mri/signalconnection-binding.cpp @@ -0,0 +1,59 @@ +#include "etc.h" +#include "binding-util.h" +#include "binding-types.h" +#include "debugwriter.h" +#include "sharedstate.h" + +#include "signalconnection-binding.h" + +void RubyConnection::Disconnect(){ + RubyConnection::connection.Disconnect(); +} +bool RubyConnection::Connected(){ + return RubyConnection::connection.Connected(); +} + +VALUE rb_cRubyConnection = Qnil; + +void rubyConnection_free(void* ptr) +{ + RubyConnection* conn = static_cast(ptr); + conn->Disconnect(); + delete conn; +} + +void rubyConnection_mark(void *ptr) +{ + RubyConnection *conn = static_cast(ptr); + + if (!NIL_P(conn->proc)) + rb_gc_mark(conn->proc); +} + +const rb_data_type_t rubyConnection_type = { "RubyConnection", {rubyConnection_mark, rubyConnection_free, 0}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; + +static VALUE rubyConnection_alloc(VALUE klass) +{ + RubyConnection* ptr = ALLOC(RubyConnection); + return TypedData_Wrap_Struct(klass, &rubyConnection_type, ptr); +} + +static VALUE rubyConnectionDisconnect(VALUE self){ + RubyConnection* conn; + TypedData_Get_Struct(self, RubyConnection, &rubyConnection_type, conn); + conn->Disconnect(); + return Qnil; +} + +static VALUE rubyConnectionConnected(VALUE self){ + RubyConnection* conn; + TypedData_Get_Struct(self, RubyConnection, &rubyConnection_type, conn); + return rb_bool_new(conn->Connected()); +} + +void SignalConnectionBindingInit(){ + rb_cRubyConnection = rb_define_class("RubyConnection", rb_cObject); + rb_define_alloc_func(rb_cRubyConnection, rubyConnection_alloc); + rb_define_method(rb_cRubyConnection, "disconnect", RUBY_METHOD_FUNC(rubyConnectionDisconnect), 0); + rb_define_method(rb_cRubyConnection, "connected", RUBY_METHOD_FUNC(rubyConnectionConnected), 0); +} \ No newline at end of file diff --git a/binding-mri/signalconnection-binding.h b/binding-mri/signalconnection-binding.h new file mode 100644 index 0000000..6ccfd66 --- /dev/null +++ b/binding-mri/signalconnection-binding.h @@ -0,0 +1,26 @@ +#pragma once + +#include "signals/signalconnection.h" + +#include + +extern void rubyConnection_free(void* ptr); +extern void rubyConnection_mark(void* ptr); +extern const rb_data_type_t rubyConnection_type; +extern VALUE rb_cRubyConnection; + +struct RubyConnection +{ + VALUE proc; + SignalConnection connection; + + void Disconnect(); + bool Connected(); +}; + +#define RUBY_CONNECTION \ + if (!rb_block_given_p()) \ + rb_raise(rb_eArgError, "unable to connect signal, block required"); \ + VALUE proc = rb_block_proc(); \ + RubyConnection* conn = new RubyConnection(); \ + conn->proc = proc; diff --git a/scripts/DynamicLight.rb b/scripts/DynamicLight.rb index 6de65ff..bc6560c 100644 --- a/scripts/DynamicLight.rb +++ b/scripts/DynamicLight.rb @@ -2,7 +2,6 @@ class DynamicLight def initialize(viewport) @debug_sprite = Sprite.new(viewport) @debug_sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height) - @debug_sprite.bitmap.fill_rect(0, 0, Graphics.width, Graphics.height, Color.new(255, 255, 255)) @debug_sprite.visible = false @light_sprite = LightMap.new(viewport) #@light_sprite.wallmap = Bitmap.new($game_map.width, $game_map.height) @@ -17,6 +16,13 @@ class DynamicLight # end #end + @update_connection = Graphics.window_resized do |w, h| + @debug_sprite.bitmap.rect.width = w; + @debug_sprite.bitmap.rect.height = h; + #@debug_sprite.bitmap.dispose + #@debug_sprite.bitmap = Bitmap.new(w, h) + end + if $light == nil return end @@ -34,6 +40,7 @@ class DynamicLight end def dispose + @update_connection.disconnect @light_sprite.dispose end diff --git a/scripts/Spriteset_Map.rb b/scripts/Spriteset_Map.rb index 9d19a14..6d3813c 100644 --- a/scripts/Spriteset_Map.rb +++ b/scripts/Spriteset_Map.rb @@ -19,6 +19,26 @@ class Spriteset_Map @viewport_lights = Viewport.new(0, 0, Graphics.width, Graphics.height) @viewport_flash = Viewport.new(0, 0, Graphics.width, Graphics.height) + @update_connection = Graphics.window_resized do |w, h| + @viewport.rect.width = w + @viewport.rect.height = h + + @viewport_bg.rect.width = w + @viewport_bg.rect.height = h + + @viewport_pics.rect.width = w + @viewport_pics.rect.height = h + + @viewport_particles.rect.width = w + @viewport_particles.rect.height = h + + @viewport_lights.rect.width = w + @viewport_lights.rect.height = h + + @viewport_flash.rect.width = w + @viewport_flash.rect.height = h + end + @viewport_bg.z = -500 @viewport_lights.z = 200 @viewport_pics.z = 500 @@ -89,6 +109,7 @@ class Spriteset_Map # * Dispose #-------------------------------------------------------------------------- def dispose + @update_connection.disconnect # Dispose of tilemap @tilemap.tileset.dispose if @tilemap.tileset for i in 0..6 diff --git a/scripts/Window_Message.rb b/scripts/Window_Message.rb index 8747ad2..bfb8d9e 100644 --- a/scripts/Window_Message.rb +++ b/scripts/Window_Message.rb @@ -18,6 +18,18 @@ class Window_Message < Window_Selectable self.z = 9999 self.back_opacity = 210 + @update_connection = Graphics.window_resized do |w, h| + self.x = Graphics.width / 2 - 304 + case $game_system.message_position + when 0 # up + self.y = 16 + when 1 # middle + self.y = Graphics.height / 2 - height / 2 + when 2 # down + self.y = Graphics.height - height - 16 + end + end + # Animation flags @fade_in = false @fade_out = false diff --git a/src/eventthread.cpp b/src/eventthread.cpp index dd8cd90..da1ec12 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -212,9 +212,11 @@ void EventThread::process(RGSSThreadData &rtData){ //SDL_GL_GetDrawableSize(win, &winW, &winH); windowSizeMsg.post(Vec2i(winW, winH)); + if (shState != nullptr) + shState->windowSignals.resized.Emit(event.window.data1, event.window.data2); resetInputStates(); break; - +// random meow case SDL_EVENT_WINDOW_MOUSE_ENTER : cursorInWindow = true; mouseState.inWindow = true; diff --git a/src/graphics.cpp b/src/graphics.cpp index 40074d1..3d9df5f 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -39,6 +39,7 @@ #include "debugwriter.h" #include "oneshot.h" #include "define.h" +#include "signals/rubydispatcher.h" #include #include #include @@ -616,6 +617,9 @@ Graphics::~Graphics(){ } void Graphics::update(bool limitFps){ + // TODO: move this to ruby thread update, idk where it is + shState->rubyDispatcher().process(); + p->checkShutDownReset(); p->checkSyncLock(); @@ -902,6 +906,7 @@ void Graphics::resizeScreen(int width, int height){ FloatRect screenRect(0, 0, width, height); p->screenQuad.setTexPosRect(screenRect, screenRect); + glState.scissorBox.set(IntRect(0, 0, width, height)); shState->eThread().requestWindowResize(width, height); } diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 6e7a50d..e18ce21 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -9,7 +9,6 @@ #include "gl-util.h" #include "quad.h" -#include "transform.h" #include "shader.h" #include "glstate.h" #include "quadarray.h" @@ -33,8 +32,9 @@ struct LightMapPrivate{ std::vector dynamicLightSources; std::vector gpuBuffer; + SignalConnection bitmapUpdateConnection; + Quad quad; - Transform trans; Rect *srcRect; SignalConnection srcRectCon; @@ -67,6 +67,15 @@ struct LightMapPrivate{ updateSrcRectCon(); prepareCon = shState->graphicsSignals.prepareDraw.Connect(*this, &LightMapPrivate::prepare); + bitmapUpdateConnection = shState->windowSignals.resized.Connect([&](int w, int h){ + shState->rubyDispatcher().invoke([&, w, h]{ + bitmap = new Bitmap(w, h); + bitmap->ensureNonMega(); + *srcRect = bitmap->rect(); + onSrcRectChange(); + quad.setPosRect(srcRect->toFloatRect()); + }); + }); bitmap->ensureNonMega(); //bitmap->fillRect(0, 0, shState->graphics().width(), shState->graphics().height(), Vec4(0, 0, 0, 1)); @@ -79,6 +88,7 @@ struct LightMapPrivate{ ~LightMapPrivate(){ srcRectCon.Disconnect(); prepareCon.Disconnect(); + bitmapUpdateConnection.Disconnect(); } void onSrcRectChange(){ @@ -112,17 +122,8 @@ struct LightMapPrivate{ return; /* Compare sprite bounding box against the scene */ - - /* If sprite is zoomed/rotated, just opt out for now - * for simplicity's sake */ - const Vec2 &scale = trans.getScale(); - if (scale.x != 1 || scale.y != 1 || trans.getRotation() != 0){ - isVisible = true; - return; - } - IntRect self; - self.setPos(trans.getPositionI() - (trans.getOriginI() + sceneOrig)); + self.setPos(Vec2i(0, 0)); self.w = bitmap->width(); self.h = bitmap->height(); @@ -249,10 +250,6 @@ void LightMap::draw(){ } void LightMap::onGeometryChange(const Scene::Geometry &geo){ - /* Offset at which the sprite will be drawn - * relative to screen origin */ - p->trans.setGlobalOffset(geo.offset()); - p->sceneRect.setSize(geo.rect.size()); p->sceneOrig = geo.orig; } diff --git a/src/scene.h b/src/scene.h index c8e3659..2530c61 100644 --- a/src/scene.h +++ b/src/scene.h @@ -69,6 +69,8 @@ protected: IntruList elements; Geometry geometry; + SignalConnection resizeConnection; + friend class SceneElement; friend class Window; friend class WindowVX; diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index c229692..7c96227 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -56,6 +56,9 @@ struct SharedStatePrivate{ SDL_Window *sdlWindow; Scene *screen; + RubyDispatcher rubyDispatcher; + RenderDispatcher renderDispatcher; + FileSystem fileSystem; EventThread &eThread; @@ -96,6 +99,8 @@ struct SharedStatePrivate{ SharedStatePrivate(RGSSThreadData *threadData) : bindingData(0), sdlWindow(threadData->window), + rubyDispatcher(), + renderDispatcher(), fileSystem(threadData->config.allowSymlinks), eThread(*threadData->ethread), rtData(*threadData), @@ -194,6 +199,8 @@ void SharedState::setScreen(Scene &screen){ GSATT(void*, bindingData) GSATT(SDL_Window*, sdlWindow) GSATT(Scene*, screen) +GSATT(RubyDispatcher&, rubyDispatcher) +GSATT(RenderDispatcher&, renderDispatcher) GSATT(FileSystem&, fileSystem) GSATT(EventThread&, eThread) GSATT(RGSSThreadData&, rtData) diff --git a/src/sharedstate.h b/src/sharedstate.h index 0194e08..e4f7a7f 100644 --- a/src/sharedstate.h +++ b/src/sharedstate.h @@ -23,6 +23,8 @@ #define SHAREDSTATE_H #include "signals/signal.h" +#include "signals/rubydispatcher.h" +#include "signals/renderdispatcher.h" #define shState SharedState::instance #define glState shState->_glState() @@ -69,6 +71,10 @@ struct SharedState{ WindowSignals windowSignals; GraphicsSignals graphicsSignals; + // dispatchers, they redirect signals from another thread to their own + RubyDispatcher &rubyDispatcher() const; + RenderDispatcher &renderDispatcher() const; + // other shit idk void *bindingData() const; void setBindingData(void *data); diff --git a/src/signals/renderdispatcher.cpp b/src/signals/renderdispatcher.cpp new file mode 100644 index 0000000..634a26b --- /dev/null +++ b/src/signals/renderdispatcher.cpp @@ -0,0 +1,21 @@ +#include "renderdispatcher.h" + +void RenderDispatcher::invoke(std::function fn){ + std::lock_guard lock(mutex); + queue.push(std::move(fn)); +} + +void RenderDispatcher::process(){ + std::queue> local; + + { + std::lock_guard lock(mutex); + std::swap(local, queue); + } + + while (!local.empty()) + { + local.front()(); + local.pop(); + } +} \ No newline at end of file diff --git a/src/signals/renderdispatcher.h b/src/signals/renderdispatcher.h new file mode 100644 index 0000000..462f856 --- /dev/null +++ b/src/signals/renderdispatcher.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +class RenderDispatcher{ +public: + RenderDispatcher() {}; + + void invoke(std::function fn); + void process(); + +private: + std::mutex mutex; + std::queue> queue; +}; \ No newline at end of file diff --git a/src/signals/rubydispatcher.cpp b/src/signals/rubydispatcher.cpp new file mode 100644 index 0000000..6afba3c --- /dev/null +++ b/src/signals/rubydispatcher.cpp @@ -0,0 +1,21 @@ +#include "rubydispatcher.h" + +void RubyDispatcher::invoke(std::function fn){ + std::lock_guard lock(mutex); + queue.push(std::move(fn)); +} + +void RubyDispatcher::process(){ + std::queue> local; + + { + std::lock_guard lock(mutex); + std::swap(local, queue); + } + + while (!local.empty()) + { + local.front()(); + local.pop(); + } +} \ No newline at end of file diff --git a/src/signals/rubydispatcher.h b/src/signals/rubydispatcher.h new file mode 100644 index 0000000..b09bc48 --- /dev/null +++ b/src/signals/rubydispatcher.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +class RubyDispatcher{ +public: + RubyDispatcher() {}; + + void invoke(std::function fn); + void process(); + +private: + std::mutex mutex; + std::queue> queue; +}; \ No newline at end of file diff --git a/src/tilemap.cpp b/src/tilemap.cpp index e2a6856..2618bb6 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -40,6 +40,7 @@ #include "tileatlas.h" #include "tilemap-common.h" #include "sunshine.h" +#include "signals/rubydispatcher.h" #include @@ -48,6 +49,7 @@ #include #include +#include extern const StaticRect autotileRects[]; @@ -250,6 +252,7 @@ struct TilemapPrivate { } atlas; int viewpW, viewpH; + SignalConnection viewpUpdateConnection; size_t zlayersMax; /* Map viewport position */ @@ -328,8 +331,8 @@ struct TilemapPrivate { mapViewportDirty(false), zOrderDirty(false), tilemapReady(false), - viewpW(shState->graphics().width() / 32 + 1), - viewpH(shState->graphics().height() / 32 + 2), + viewpW(shState->graphics().width() / 31 + 2), + viewpH(shState->graphics().height() / 31 + 2), zlayersMax(viewpH + 5) { zlayerVert.resize(zlayersMax); @@ -360,6 +363,34 @@ struct TilemapPrivate { elem.zlayers[i] = new ZLayer(this, viewport); prepareCon = shState->graphicsSignals.prepareDraw.Connect(*this, &TilemapPrivate::prepare); + viewpUpdateConnection = shState->windowSignals.resized.Connect([&](int w, int h){ + shState->rubyDispatcher().invoke([&, w, h]{ + if (this->viewport->isDisposed()){ + Debug() << "Warning: resize updating of disposed tilemap, disconnecting"; + this->viewpUpdateConnection.Disconnect(); + return; + } + + int oldZLayersCount = zlayersMax; + viewpW = w / 31 + 2; + viewpH = h / 31 + 2; + zlayersMax = viewpH + 5; + zlayerVert.resize(zlayersMax); + zlayerBases.resize(zlayersMax + 1); + if (zlayersMax > oldZLayersCount){ + elem.zlayers.resize(zlayersMax); + for (size_t i = oldZLayersCount; i < zlayersMax; ++i) + if (elem.zlayers[i] == nullptr) + elem.zlayers[i] = new ZLayer(this, this->viewport); + } + else{ + for (size_t i = zlayersMax; i < oldZLayersCount; ++i) + if (elem.zlayers[i] != nullptr) + delete elem.zlayers[i]; + elem.zlayers.resize(zlayersMax); + } + }); + }); updateFlashMapViewport(); } @@ -377,6 +408,7 @@ struct TilemapPrivate { VBO::del(tiles.vbo); /* Disconnect signal handlers */ + viewpUpdateConnection.Disconnect(); tilesetCon.Disconnect(); for (int i = 0; i < autotileCount; ++i){ autotilesCon[i].Disconnect(); diff --git a/src/viewport.h b/src/viewport.h index fc046df..4365574 100644 --- a/src/viewport.h +++ b/src/viewport.h @@ -53,7 +53,6 @@ private: void composite(); void draw(); void onGeometryChange(const Geometry &); - bool isEffectiveViewport(Rect *&, Color *&, Tone *&) const; void releaseResources(); const char *klassName() const { return "viewport"; }