diff --git a/src/PowderToyRenderer.cpp b/src/PowderToyRenderer.cpp index 5527f4856..fb88865d0 100644 --- a/src/PowderToyRenderer.cpp +++ b/src/PowderToyRenderer.cpp @@ -52,8 +52,10 @@ int main(int argc, char *argv[]) sim->Load(gameSave.get(), true, { 0, 0 }); //Render save - ren->decorations_enable = true; - ren->blackDecorations = true; + RendererSettings rendererSettings; + rendererSettings.decorations_enable = true; + rendererSettings.blackDecorations = true; + ren->ApplySettings(rendererSettings); int frame = 15; while(frame) diff --git a/src/client/ThumbnailRendererTask.cpp b/src/client/ThumbnailRendererTask.cpp index 61c4d64f2..bf03faa3c 100644 --- a/src/client/ThumbnailRendererTask.cpp +++ b/src/client/ThumbnailRendererTask.cpp @@ -29,7 +29,7 @@ ThumbnailRendererTask::~ThumbnailRendererTask() bool ThumbnailRendererTask::doWork() { - thumbnail = SaveRenderer::Ref().Render(save.get(), decorations, fire); + thumbnail = SaveRenderer::Ref().Render(save.get(), decorations, fire, RendererSettings{}); if (thumbnail) { thumbnail->ResizeToFit(size, true); diff --git a/src/graphics/Renderer.h b/src/graphics/Renderer.h index e861b9d3b..e438e88db 100644 --- a/src/graphics/Renderer.h +++ b/src/graphics/Renderer.h @@ -2,10 +2,9 @@ #include "Icons.h" #include "RasterDrawMethods.h" #include "gui/game/RenderPreset.h" -#include "gui/interface/Point.h" +#include "RendererSettings.h" #include "common/tpt-rand.h" #include "RendererFrame.h" -#include "FindingElement.h" #include #include #include @@ -13,13 +12,13 @@ #include struct RenderPreset; -struct RenderableSimulation; class Renderer; +struct RenderableSimulation; struct Particle; struct GraphicsFuncContext { - const Renderer *ren; + const RendererSettings *ren; const RenderableSimulation *sim; RNG rng; const Particle *pipeSubcallCpart; @@ -28,7 +27,7 @@ struct GraphicsFuncContext int HeatToColour(float temp); -class Renderer: public RasterDrawMethods +class Renderer : private RendererSettings, public RasterDrawMethods { RendererFrame video; std::array persistentVideo; @@ -41,39 +40,24 @@ class Renderer: public RasterDrawMethods friend struct RasterDrawMethods; - float fireIntensity = 1; + RNG rng; + unsigned char fire_r[YCELLS][XCELLS]; + unsigned char fire_g[YCELLS][XCELLS]; + unsigned char fire_b[YCELLS][XCELLS]; + unsigned int fire_alpha[CELL*3][CELL*3]; public: - RNG rng; - const RenderableSimulation *sim = nullptr; - const RendererFrame &GetVideo() const { return video; } - uint32_t renderMode = 0; - uint32_t colorMode = 0; - uint32_t displayMode = 0; + const RenderableSimulation *sim = nullptr; + + void ApplySettings(const RendererSettings &newSettings); + static const std::vector renderModePresets; - // - unsigned char fire_r[YCELLS][XCELLS]; - unsigned char fire_g[YCELLS][XCELLS]; - unsigned char fire_b[YCELLS][XCELLS]; - unsigned int fire_alpha[CELL*3][CELL*3]; - // - bool gravityZonesEnabled; - bool gravityFieldEnabled; - int decorations_enable; - bool blackDecorations; - bool debugLines; - std::optional findingElement; - int foundElements; - //Mouse position for debug information - ui::Point mousePos; - - //Renderers void RenderSimulation(); void DrawBlob(Vec2 pos, RGB colour); @@ -81,10 +65,6 @@ public: void DrawSigns(); void render_gravlensing(const RendererFrame &source); void render_fire(); - float GetFireIntensity() const - { - return fireIntensity; - } void prepare_alpha(int size, float intensity); void render_parts(); void draw_grav_zones(); @@ -95,22 +75,6 @@ public: void ClearAccumulation(); void clearScreen(); - void draw_icon(int x, int y, Icon icon); - - //... - //Display mode modifiers - void SetRenderMode(uint32_t newRenderMode); - uint32_t GetRenderMode(); - void SetDisplayMode(uint32_t newDisplayMode); - uint32_t GetDisplayMode(); - void SetColorMode(uint32_t newColorMode); - uint32_t GetColorMode(); - - void ResetModes(); - - int GetGridSize() { return gridSize; } - void SetGridSize(int value) { gridSize = value; } - static std::unique_ptr WallIcon(int wallID, Vec2 size); Renderer(); @@ -140,7 +104,4 @@ public: RENDERER_TABLE(firwTable) #undef RENDERER_TABLE static void PopulateTables(); - -private: - int gridSize; }; diff --git a/src/graphics/RendererBasic.cpp b/src/graphics/RendererBasic.cpp index 606ae9869..879ec3b33 100644 --- a/src/graphics/RendererBasic.cpp +++ b/src/graphics/RendererBasic.cpp @@ -180,15 +180,7 @@ void Renderer::PopulateTables() } } -Renderer::Renderer(): - gravityZonesEnabled(false), - gravityFieldEnabled(false), - decorations_enable(1), - blackDecorations(false), - debugLines(false), - foundElements(0), - mousePos(0, 0), - gridSize(0) +Renderer::Renderer() { PopulateTables(); @@ -197,9 +189,8 @@ Renderer::Renderer(): memset(fire_b, 0, sizeof(fire_b)); //Set defauly display modes - ResetModes(); - prepare_alpha(CELL, 1.0f); + ClearAccumulation(); } void Renderer::ClearAccumulation() @@ -210,51 +201,17 @@ void Renderer::ClearAccumulation() std::fill(persistentVideo.begin(), persistentVideo.end(), 0); } -void Renderer::SetRenderMode(uint32_t newRenderMode) +void Renderer::ApplySettings(const RendererSettings &newSettings) { - int oldRenderMode = renderMode; - renderMode = newRenderMode; - if (!(renderMode & FIREMODE) && (oldRenderMode & FIREMODE)) + if (!(newSettings.renderMode & FIREMODE) && (renderMode & FIREMODE)) { ClearAccumulation(); } -} - -uint32_t Renderer::GetRenderMode() -{ - return renderMode; -} - -void Renderer::SetDisplayMode(uint32_t newDisplayMode) -{ - int oldDisplayMode = displayMode; - displayMode = newDisplayMode; - if (!(displayMode & DISPLAY_PERS) && (oldDisplayMode & DISPLAY_PERS)) + if (!(newSettings.displayMode & DISPLAY_PERS) && (displayMode & DISPLAY_PERS)) { ClearAccumulation(); } -} - -uint32_t Renderer::GetDisplayMode() -{ - return displayMode; -} - -void Renderer::SetColorMode(uint32_t newColorMode) -{ - colorMode = newColorMode; -} - -uint32_t Renderer::GetColorMode() -{ - return colorMode; -} - -void Renderer::ResetModes() -{ - SetRenderMode(RENDER_BASC | RENDER_FIRE | RENDER_SPRK | RENDER_EFFE); - SetDisplayMode(0); - SetColorMode(COLOUR_DEFAULT); + static_cast(*this) = newSettings; } template struct RasterDrawMethods; diff --git a/src/graphics/RendererSettings.h b/src/graphics/RendererSettings.h new file mode 100644 index 000000000..25845b8f8 --- /dev/null +++ b/src/graphics/RendererSettings.h @@ -0,0 +1,23 @@ +#pragma once +#include "gui/interface/Point.h" +#include "simulation/ElementGraphics.h" +#include "FindingElement.h" +#include +#include + +struct RendererSettings +{ + uint32_t renderMode = RENDER_BASC | RENDER_FIRE | RENDER_SPRK | RENDER_EFFE; + uint32_t displayMode = 0; + uint32_t colorMode = COLOUR_DEFAULT; + std::optional findingElement; + bool gravityZonesEnabled = false; + bool gravityFieldEnabled = false; + int decorations_enable = 1; + bool blackDecorations = false; + bool debugLines = false; + int foundElements = 0; + ui::Point mousePos = { 0, 0 }; + int gridSize = 0; + float fireIntensity = 1; +}; diff --git a/src/gui/game/GameController.cpp b/src/gui/game/GameController.cpp index 22d1d4b1e..2b4e91349 100644 --- a/src/gui/game/GameController.cpp +++ b/src/gui/game/GameController.cpp @@ -271,9 +271,9 @@ void GameController::Install() void GameController::AdjustGridSize(int direction) { if(direction > 0) - gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+1)%10); + gameModel->GetRendererSettings().gridSize = (gameModel->GetRendererSettings().gridSize+1)%10; else - gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+9)%10); + gameModel->GetRendererSettings().gridSize = (gameModel->GetRendererSettings().gridSize+9)%10; } void GameController::InvertAirSim() @@ -851,19 +851,19 @@ void GameController::ToggleNewtonianGravity() void GameController::LoadRenderPreset(int presetNum) { - Renderer * renderer = gameModel->GetRenderer(); + auto &settings = gameModel->GetRendererSettings(); RenderPreset preset = Renderer::renderModePresets[presetNum]; gameModel->SetInfoTip(preset.Name); - renderer->SetRenderMode(preset.renderMode); - renderer->SetDisplayMode(preset.displayMode); - renderer->SetColorMode(preset.colorMode); + settings.renderMode = preset.renderMode; + settings.displayMode = preset.displayMode; + settings.colorMode = preset.colorMode; } void GameController::Update() { auto &sd = SimulationData::CRef(); ui::Point pos = gameView->GetMousePosition(); - gameModel->GetRenderer()->mousePos = PointTranslate(pos); + gameModel->GetRendererSettings().mousePos = PointTranslate(pos); if (pos.X < XRES && pos.Y < YRES) gameView->SetSample(gameModel->GetSimulation()->GetSample(PointTranslate(pos).X, PointTranslate(pos).Y)); else @@ -1106,7 +1106,7 @@ void GameController::SetActiveTool(int toolSelection, Tool * tool) if (gameModel->GetActiveMenu() == SC_DECO && toolSelection == 2) toolSelection = 0; gameModel->SetActiveTool(toolSelection, tool); - gameModel->GetRenderer()->gravityZonesEnabled = false; + gameModel->GetRendererSettings().gravityZonesEnabled = false; if (toolSelection == 3) gameModel->GetSimulation()->replaceModeSelected = tool->ToolID; gameModel->SetLastTool(tool); @@ -1115,7 +1115,7 @@ void GameController::SetActiveTool(int toolSelection, Tool * tool) auto *activeTool = gameModel->GetActiveTool(i); if (activeTool && activeTool->Identifier == "DEFAULT_WL_GRVTY") { - gameModel->GetRenderer()->gravityZonesEnabled = true; + gameModel->GetRendererSettings().gravityZonesEnabled = true; } } if (tool->Identifier == "DEFAULT_UI_PROPERTY") @@ -1404,7 +1404,7 @@ void GameController::HideConsole() void GameController::OpenRenderOptions() { - renderOptions = new RenderController(gameModel->GetSimulation(), gameModel->GetRenderer(), NULL); + renderOptions = new RenderController(gameModel->GetSimulation(), gameModel->GetRenderer(), &gameModel->GetRendererSettings(), NULL); ui::Engine::Ref().ShowWindow(renderOptions->GetView()); } @@ -1727,3 +1727,8 @@ void GameController::AfterSimDraw() { commandInterface->HandleEvent(AfterSimDrawEvent{}); } + +bool GameController::HaveSimGraphicsEventHandlers() +{ + return commandInterface->HaveSimGraphicsEventHandlers(); +} diff --git a/src/gui/game/GameController.h b/src/gui/game/GameController.h index b96305628..94b33652b 100644 --- a/src/gui/game/GameController.h +++ b/src/gui/game/GameController.h @@ -2,6 +2,7 @@ #include "lua/CommandInterfacePtr.h" #include "client/ClientListener.h" #include "client/StartupInfo.h" +#include "common/ExplicitSingleton.h" #include "gui/interface/Point.h" #include "gui/interface/Colour.h" #include "gui/SavePreviewType.h" @@ -37,7 +38,7 @@ class GameSave; class LoginController; class TagsController; class ConsoleController; -class GameController: public ClientListener +class GameController : public ClientListener, public ExplicitSingleton { CommandInterfacePtr commandInterface; @@ -204,4 +205,5 @@ public: void BeforeSimDraw(); void AfterSimDraw(); + bool HaveSimGraphicsEventHandlers(); }; diff --git a/src/gui/game/GameModel.cpp b/src/gui/game/GameModel.cpp index 49800137b..4ef2ab21a 100644 --- a/src/gui/game/GameModel.cpp +++ b/src/gui/game/GameModel.cpp @@ -91,15 +91,15 @@ GameModel::GameModel(): setFunc(*pref); }; handleOldModes("Renderer.RenderMode", "Renderer.RenderModes", RENDER_FIRE | RENDER_EFFE | RENDER_BASC, [this](uint32_t renderMode) { - ren->SetRenderMode(renderMode); + rendererSettings.renderMode = renderMode; }); handleOldModes("Renderer.DisplayMode", "Renderer.DisplayModes", 0, [this](uint32_t displayMode) { - ren->SetDisplayMode(displayMode); + rendererSettings.displayMode = displayMode; }); - ren->SetColorMode(prefs.Get("Renderer.ColourMode", UINT32_C(0))); + rendererSettings.colorMode = prefs.Get("Renderer.ColourMode", UINT32_C(0)); - ren->gravityFieldEnabled = prefs.Get("Renderer.GravityField", false); - ren->decorations_enable = prefs.Get("Renderer.Decorations", true); + rendererSettings.gravityFieldEnabled = prefs.Get("Renderer.GravityField", false); + rendererSettings.decorations_enable = prefs.Get("Renderer.Decorations", true); //Load config into simulation edgeMode = prefs.Get("Simulation.EdgeMode", NUM_EDGEMODES, EDGE_VOID); @@ -170,12 +170,12 @@ GameModel::~GameModel() { //Save to config: Prefs::DeferWrite dw(prefs); - prefs.Set("Renderer.ColourMode", ren->GetColorMode()); - prefs.Set("Renderer.DisplayMode", ren->GetDisplayMode()); - prefs.Set("Renderer.RenderMode", ren->GetRenderMode()); - prefs.Set("Renderer.GravityField", (bool)ren->gravityFieldEnabled); - prefs.Set("Renderer.Decorations", (bool)ren->decorations_enable); - prefs.Set("Renderer.DebugMode", ren->debugLines); //These two should always be equivalent, even though they are different things + prefs.Set("Renderer.ColourMode", rendererSettings.colorMode); + prefs.Set("Renderer.DisplayMode", rendererSettings.displayMode); + prefs.Set("Renderer.RenderMode", rendererSettings.renderMode); + prefs.Set("Renderer.GravityField", (bool)rendererSettings.gravityFieldEnabled); + prefs.Set("Renderer.Decorations", (bool)rendererSettings.decorations_enable); + prefs.Set("Renderer.DebugMode", rendererSettings.debugLines); //These two should always be equivalent, even though they are different things prefs.Set("Simulation.NewtonianGravity", bool(sim->grav)); prefs.Set("Simulation.AmbientHeat", sim->aheat_enable); prefs.Set("Simulation.PrettyPowder", sim->pretty_powder); @@ -1260,9 +1260,9 @@ bool GameModel::GetPaused() void GameModel::SetDecoration(bool decorationState) { - if (ren->decorations_enable != (decorationState?1:0)) + if (rendererSettings.decorations_enable != (decorationState?1:0)) { - ren->decorations_enable = decorationState?1:0; + rendererSettings.decorations_enable = decorationState?1:0; notifyDecorationChanged(); UpdateQuickOptions(); if (decorationState) @@ -1274,7 +1274,7 @@ void GameModel::SetDecoration(bool decorationState) bool GameModel::GetDecoration() { - return ren->decorations_enable?true:false; + return rendererSettings.decorations_enable?true:false; } void GameModel::SetAHeatEnable(bool aHeat) @@ -1318,7 +1318,7 @@ bool GameModel::GetNewtonianGrvity() void GameModel::ShowGravityGrid(bool showGrid) { - ren->gravityFieldEnabled = showGrid; + rendererSettings.gravityFieldEnabled = showGrid; if (showGrid) SetInfoTip("Gravity Grid: On"); else @@ -1327,7 +1327,7 @@ void GameModel::ShowGravityGrid(bool showGrid) bool GameModel::GetGravityGrid() { - return ren->gravityFieldEnabled; + return rendererSettings.gravityFieldEnabled; } void GameModel::FrameStep(int frames) diff --git a/src/gui/game/GameModel.h b/src/gui/game/GameModel.h index 2f2dcb0fb..444db9386 100644 --- a/src/gui/game/GameModel.h +++ b/src/gui/game/GameModel.h @@ -2,6 +2,7 @@ #include "gui/interface/Colour.h" #include "client/User.h" #include "gui/interface/Point.h" +#include "graphics/RendererSettings.h" #include #include #include @@ -66,6 +67,7 @@ private: Simulation * sim; Renderer * ren; + RendererSettings rendererSettings; std::vector menuList; std::vector quickOptions; int activeMenu; @@ -231,6 +233,10 @@ public: void SetUser(User user); Simulation * GetSimulation(); Renderer * GetRenderer(); + RendererSettings &GetRendererSettings() + { + return rendererSettings; + } void SetZoomEnabled(bool enabled); bool GetZoomEnabled(); void SetZoomSize(int size); diff --git a/src/gui/game/GameView.cpp b/src/gui/game/GameView.cpp index 375d40798..bf9535211 100644 --- a/src/gui/game/GameView.cpp +++ b/src/gui/game/GameView.cpp @@ -203,7 +203,6 @@ GameView::GameView(): recordingFolder(0), currentPoint(ui::Point(0, 0)), lastPoint(ui::Point(0, 0)), - ren(NULL), activeBrush(NULL), saveSimulationButtonEnabled(false), saveReuploadAllowed(true), @@ -332,6 +331,7 @@ GameView::GameView(): GameView::~GameView() { + StopRendererThread(); if(!colourPicker->GetParentWindow()) delete colourPicker; @@ -470,8 +470,7 @@ bool GameView::GetBrushEnable() void GameView::SetDebugHUD(bool mode) { showDebug = mode; - if (ren) - ren->debugLines = showDebug; + rendererSettings->debugLines = showDebug; } bool GameView::GetDebugHUD() @@ -518,9 +517,10 @@ void GameView::NotifyActiveToolsChanged(GameModel * sender) decoBrush = sender->GetActiveTool(0)->Identifier.BeginsWith("DEFAULT_DECOR_"); - if (sender->GetRenderer()->findingElement) + auto &settings = sender->GetRendererSettings(); + if (settings.findingElement) { - ren->findingElement = FindingElementCandidate(); + settings.findingElement = FindingElementCandidate(); } } @@ -734,6 +734,7 @@ void GameView::NotifyColourSelectorColourChanged(GameModel * sender) void GameView::NotifyRendererChanged(GameModel * sender) { ren = sender->GetRenderer(); + rendererSettings = &sender->GetRendererSettings(); } void GameView::NotifySimulationChanged(GameModel * sender) @@ -1443,13 +1444,13 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, if (ctrl) { auto findingElementCandidate = FindingElementCandidate(); - if (ren->findingElement == findingElementCandidate) + if (rendererSettings->findingElement == findingElementCandidate) { - ren->findingElement = std::nullopt; + rendererSettings->findingElement = std::nullopt; } else { - ren->findingElement = findingElementCandidate; + rendererSettings->findingElement = findingElementCandidate; } } else @@ -1982,7 +1983,7 @@ void GameView::NotifyTransformedPlaceSaveChanged(GameModel *sender) { if (sender->GetTransformedPlaceSave()) { - placeSaveThumb = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRenderer()); + placeSaveThumb = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRendererSettings()); selectMode = PlaceSave; selectPoint2 = mousePosition; } @@ -2126,166 +2127,189 @@ void GameView::SetSaveButtonTooltips() saveSimulationButton->SetToolTips("Re-upload the current simulation", "Upload a new simulation. Hold Ctrl to save offline."); } +void GameView::RenderSimulation(const RenderableSimulation &sim, bool handleEvents) +{ + ren->sim = ∼ + ren->clearScreen(); + ren->draw_air(); + if (handleEvents) + { + c->BeforeSimDraw(); + } + { + // we may write graphicscache here + auto &sd = SimulationData::Ref(); + std::unique_lock lk(sd.elementGraphicsMx); + ren->RenderSimulation(); + } + if (handleEvents) + { + c->AfterSimDraw(); + } + ren->sim = nullptr; +} + void GameView::OnDraw() { Graphics * g = GetGraphics(); - if (ren) + + auto wantRendererThread = !c->HaveSimGraphicsEventHandlers(); + if (wantRendererThread) { - // we're the main thread, we may write graphicscache - auto &sd = SimulationData::Ref(); - std::unique_lock lk(sd.elementGraphicsMx); - ren->sim = sim; - ren->clearScreen(); - ren->draw_air(); - c->BeforeSimDraw(); - ren->RenderSimulation(); - ren->sim = nullptr; - - c->AfterSimDraw(); - + StartRendererThread(); + WaitForRendererThread(); rendererFrame = ren->GetVideo(); - std::copy_n(rendererFrame.data(), rendererFrame.Size().X * rendererFrame.Size().Y, g->Data()); + DispatchRendererThread(); + } + else + { + PauseRendererThread(); + ren->ApplySettings(*rendererSettings); + RenderSimulation(*sim, true); + rendererFrame = ren->GetVideo(); + } - if (showBrush && selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && (isMouseDown || (currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES))) + std::copy_n(rendererFrame.data(), rendererFrame.Size().X * rendererFrame.Size().Y, g->Data()); + + if (showBrush && selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && (isMouseDown || (currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES))) + { + ui::Point finalCurrentMouse = windTool ? c->PointTranslateNoClamp(currentMouse) : c->PointTranslate(currentMouse); + ui::Point initialDrawPoint = drawPoint1; + + if (wallBrush) { - ui::Point finalCurrentMouse = windTool ? c->PointTranslateNoClamp(currentMouse) : c->PointTranslate(currentMouse); - ui::Point initialDrawPoint = drawPoint1; - - if (wallBrush) - { - finalCurrentMouse = c->NormaliseBlockCoord(finalCurrentMouse); - initialDrawPoint = c->NormaliseBlockCoord(initialDrawPoint); - } - - if (drawMode == DrawRect && isMouseDown) - { - if (drawSnap) - { - finalCurrentMouse = rectSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse); - } - if (wallBrush) - { - if (finalCurrentMouse.X > initialDrawPoint.X) - finalCurrentMouse.X += CELL-1; - else - initialDrawPoint.X += CELL-1; - - if (finalCurrentMouse.Y > initialDrawPoint.Y) - finalCurrentMouse.Y += CELL-1; - else - initialDrawPoint.Y += CELL-1; - } - activeBrush->RenderRect(g, c->PointTranslate(initialDrawPoint), finalCurrentMouse); - } - else if (drawMode == DrawLine && isMouseDown) - { - if (drawSnap) - { - finalCurrentMouse = lineSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse); - } - activeBrush->RenderLine(g, c->PointTranslate(initialDrawPoint), finalCurrentMouse); - } - else if (drawMode == DrawFill)// || altBehaviour) - { - if (!decoBrush) - activeBrush->RenderFill(g, finalCurrentMouse); - } - if (drawMode == DrawPoints || drawMode==DrawLine || (drawMode == DrawRect && !isMouseDown)) - { - if (wallBrush) - { - ui::Point finalBrushRadius = c->NormaliseBlockCoord(activeBrush->GetRadius()); - auto topLeft = finalCurrentMouse - finalBrushRadius; - auto bottomRight = finalCurrentMouse + finalBrushRadius + Vec2{ CELL - 1, CELL - 1 }; - g->XorLine({ topLeft.X, topLeft.Y }, { bottomRight.X, topLeft.Y }); - g->XorLine({ topLeft.X, bottomRight.Y }, { bottomRight.X, bottomRight.Y }); - g->XorLine({ topLeft.X, topLeft.Y + 1 }, { topLeft.X, bottomRight.Y - 1 }); // offset by 1 so the corners don't get xor'd twice - g->XorLine({ bottomRight.X, topLeft.Y + 1 }, { bottomRight.X, bottomRight.Y - 1 }); // offset by 1 so the corners don't get xor'd twice - } - else - { - activeBrush->RenderPoint(g, finalCurrentMouse); - } - } + finalCurrentMouse = c->NormaliseBlockCoord(finalCurrentMouse); + initialDrawPoint = c->NormaliseBlockCoord(initialDrawPoint); } - if(selectMode!=SelectNone) + if (drawMode == DrawRect && isMouseDown) { - if(selectMode==PlaceSave) + if (drawSnap) { - if(placeSaveThumb && selectPoint2.X!=-1) - { - auto rect = RectSized(PlaceSavePos() * CELL, placeSaveThumb->Size()); - g->BlendImage(placeSaveThumb->Data(), 0x80, rect); - g->XorDottedRect(rect); - } + finalCurrentMouse = rectSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + if (wallBrush) + { + if (finalCurrentMouse.X > initialDrawPoint.X) + finalCurrentMouse.X += CELL-1; + else + initialDrawPoint.X += CELL-1; + + if (finalCurrentMouse.Y > initialDrawPoint.Y) + finalCurrentMouse.Y += CELL-1; + else + initialDrawPoint.Y += CELL-1; + } + activeBrush->RenderRect(g, c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + else if (drawMode == DrawLine && isMouseDown) + { + if (drawSnap) + { + finalCurrentMouse = lineSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + activeBrush->RenderLine(g, c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + else if (drawMode == DrawFill)// || altBehaviour) + { + if (!decoBrush) + activeBrush->RenderFill(g, finalCurrentMouse); + } + if (drawMode == DrawPoints || drawMode==DrawLine || (drawMode == DrawRect && !isMouseDown)) + { + if (wallBrush) + { + ui::Point finalBrushRadius = c->NormaliseBlockCoord(activeBrush->GetRadius()); + auto topLeft = finalCurrentMouse - finalBrushRadius; + auto bottomRight = finalCurrentMouse + finalBrushRadius + Vec2{ CELL - 1, CELL - 1 }; + g->XorLine({ topLeft.X, topLeft.Y }, { bottomRight.X, topLeft.Y }); + g->XorLine({ topLeft.X, bottomRight.Y }, { bottomRight.X, bottomRight.Y }); + g->XorLine({ topLeft.X, topLeft.Y + 1 }, { topLeft.X, bottomRight.Y - 1 }); // offset by 1 so the corners don't get xor'd twice + g->XorLine({ bottomRight.X, topLeft.Y + 1 }, { bottomRight.X, bottomRight.Y - 1 }); // offset by 1 so the corners don't get xor'd twice } else { - if(selectPoint1.X==-1) - { - g->BlendFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ XRES, YRES }), 0x000000_rgb .WithAlpha(100)); - } - else - { - int x2 = (selectPoint1.X>selectPoint2.X)?selectPoint1.X:selectPoint2.X; - int y2 = (selectPoint1.Y>selectPoint2.Y)?selectPoint1.Y:selectPoint2.Y; - int x1 = (selectPoint2.XXRES-1) - x2 = XRES-1; - if(y2>YRES-1) - y2 = YRES-1; - - g->BlendFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ XRES, y1 }), 0x000000_rgb .WithAlpha(100)); - g->BlendFilledRect(RectSized(Vec2{ 0, y2+1 }, Vec2{ XRES, YRES-y2-1 }), 0x000000_rgb .WithAlpha(100)); - - g->BlendFilledRect(RectSized(Vec2{ 0, y1 }, Vec2{ x1, (y2-y1)+1 }), 0x000000_rgb .WithAlpha(100)); - g->BlendFilledRect(RectSized(Vec2{ x2+1, y1 }, Vec2{ XRES-x2-1, (y2-y1)+1 }), 0x000000_rgb .WithAlpha(100)); - - g->XorDottedRect(RectBetween(Vec2{ x1, y1 }, Vec2{ x2, y2 })); - } + activeBrush->RenderPoint(g, finalCurrentMouse); } } + } - g->RenderZoom(); - - if (doScreenshot) + if(selectMode!=SelectNone) + { + if(selectMode==PlaceSave) { - doScreenshot = false; - TakeScreenshot(0, 0); - } - - if(recording) - { - std::vector data = VideoBuffer(rendererFrame).ToPPM(); - - ByteString filename = ByteString::Build("recordings", PATH_SEP_CHAR, recordingFolder, PATH_SEP_CHAR, "frame_", Format::Width(recordingIndex++, 6), ".ppm"); - - Platform::WriteFile(data, filename); - } - - if (logEntries.size()) - { - int startX = 20; - int startY = YRES-20; - std::deque >::iterator iter; - for(iter = logEntries.begin(); iter != logEntries.end(); iter++) + if(placeSaveThumb && selectPoint2.X!=-1) { - String message = (*iter).first; - int alpha = std::min((*iter).second, 255); - if (alpha <= 0) //erase this and everything older - { - logEntries.erase(iter, logEntries.end()); - break; - } - startY -= 14; - g->BlendFilledRect(RectSized(Vec2{ startX-3, startY-3 }, Vec2{ Graphics::TextSize(message).X + 5, 14 }), 0x000000_rgb .WithAlpha(std::min(100, alpha))); - g->BlendText({ startX, startY }, message, 0xFFFFFF_rgb .WithAlpha(alpha)); - (*iter).second -= 3; + auto rect = RectSized(PlaceSavePos() * CELL, placeSaveThumb->Size()); + g->BlendImage(placeSaveThumb->Data(), 0x80, rect); + g->XorDottedRect(rect); } } + else + { + if(selectPoint1.X==-1) + { + g->BlendFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ XRES, YRES }), 0x000000_rgb .WithAlpha(100)); + } + else + { + int x2 = (selectPoint1.X>selectPoint2.X)?selectPoint1.X:selectPoint2.X; + int y2 = (selectPoint1.Y>selectPoint2.Y)?selectPoint1.Y:selectPoint2.Y; + int x1 = (selectPoint2.XXRES-1) + x2 = XRES-1; + if(y2>YRES-1) + y2 = YRES-1; + + g->BlendFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ XRES, y1 }), 0x000000_rgb .WithAlpha(100)); + g->BlendFilledRect(RectSized(Vec2{ 0, y2+1 }, Vec2{ XRES, YRES-y2-1 }), 0x000000_rgb .WithAlpha(100)); + + g->BlendFilledRect(RectSized(Vec2{ 0, y1 }, Vec2{ x1, (y2-y1)+1 }), 0x000000_rgb .WithAlpha(100)); + g->BlendFilledRect(RectSized(Vec2{ x2+1, y1 }, Vec2{ XRES-x2-1, (y2-y1)+1 }), 0x000000_rgb .WithAlpha(100)); + + g->XorDottedRect(RectBetween(Vec2{ x1, y1 }, Vec2{ x2, y2 })); + } + } + } + + g->RenderZoom(); + + if (doScreenshot) + { + doScreenshot = false; + TakeScreenshot(0, 0); + } + + if(recording) + { + std::vector data = VideoBuffer(rendererFrame).ToPPM(); + + ByteString filename = ByteString::Build("recordings", PATH_SEP_CHAR, recordingFolder, PATH_SEP_CHAR, "frame_", Format::Width(recordingIndex++, 6), ".ppm"); + + Platform::WriteFile(data, filename); + } + + if (logEntries.size()) + { + int startX = 20; + int startY = YRES-20; + std::deque >::iterator iter; + for(iter = logEntries.begin(); iter != logEntries.end(); iter++) + { + String message = (*iter).first; + int alpha = std::min((*iter).second, 255); + if (alpha <= 0) //erase this and everything older + { + logEntries.erase(iter, logEntries.end()); + break; + } + startY -= 14; + g->BlendFilledRect(RectSized(Vec2{ startX-3, startY-3 }, Vec2{ Graphics::TextSize(message).X + 5, 14 }), 0x000000_rgb .WithAlpha(std::min(100, alpha))); + g->BlendText({ startX, startY }, message, 0xFFFFFF_rgb .WithAlpha(alpha)); + (*iter).second -= 3; + } } if (recording) @@ -2487,8 +2511,8 @@ void GameView::OnDraw() if (showDebug) { - if (ren->findingElement) - fpsInfo << " Parts: " << ren->foundElements << "/" << sample.NumParts; + if (rendererSettings->findingElement) + fpsInfo << " Parts: " << rendererSettings->foundElements << "/" << sample.NumParts; else fpsInfo << " Parts: " << sample.NumParts; } @@ -2496,10 +2520,17 @@ void GameView::OnDraw() fpsInfo << " [REPLACE MODE]"; if (c->GetReplaceModeFlags()&SPECIFIC_DELETE) fpsInfo << " [SPECIFIC DELETE]"; - if (ren && ren->GetGridSize()) - fpsInfo << " [GRID: " << ren->GetGridSize() << "]"; - if (ren && ren->findingElement) + if (rendererSettings->gridSize) + fpsInfo << " [GRID: " << rendererSettings->gridSize << "]"; + if (rendererSettings->findingElement) fpsInfo << " [FIND]"; + if (showDebug) + { + if (wantRendererThread) + { + fpsInfo << " [SRT]"; + } + } int textWidth = Graphics::TextSize(fpsInfo.Build()).X - 1; int alpha = 255-introText*5; @@ -2584,3 +2615,106 @@ pixel GameView::GetPixelUnderMouse() const } return rendererFrame[point]; } + +void GameView::RendererThread() +{ + while (true) + { + { + std::unique_lock lk(rendererThreadMx); + rendererThreadOwnsRenderer = false; + rendererThreadCv.notify_one(); + rendererThreadCv.wait(lk, [this]() { + return rendererThreadState == rendererThreadStopping || rendererThreadOwnsRenderer; + }); + if (rendererThreadState == rendererThreadStopping) + { + break; + } + } + RenderSimulation(*rendererThreadSim, false); + } +} + +void GameView::StartRendererThread() +{ + bool start = false; + bool notify = false; + { + std::lock_guard lk(rendererThreadMx); + if (rendererThreadState == rendererThreadAbsent) + { + rendererThreadSim = std::make_unique(); + rendererThreadState = rendererThreadRunning; + start = true; + } + else if (rendererThreadState == rendererThreadPaused) + { + rendererThreadState = rendererThreadRunning; + notify = true; + } + } + if (start) + { + rendererThread = std::thread([this]() { + RendererThread(); + }); + notify = true; + } + if (notify) + { + DispatchRendererThread(); + } +} + +void GameView::StopRendererThread() +{ + bool join = false; + { + std::lock_guard lk(rendererThreadMx); + if (rendererThreadState != rendererThreadAbsent) + { + rendererThreadState = rendererThreadStopping; + join = true; + } + } + if (join) + { + rendererThreadCv.notify_one(); + rendererThread.join(); + } +} + +void GameView::PauseRendererThread() +{ + std::unique_lock lk(rendererThreadMx); + if (rendererThreadState == rendererThreadRunning) + { + rendererThreadState = rendererThreadPaused; + rendererThreadCv.notify_one(); + rendererThreadCv.wait(lk, [this]() { + return !rendererThreadOwnsRenderer; + }); + } +} + +void GameView::DispatchRendererThread() +{ + ren->ApplySettings(*rendererSettings); + *rendererThreadSim = *sim; + rendererThreadSim->useLuaCallbacks = false; + rendererThreadOwnsRenderer = true; + { + std::lock_guard lk(rendererThreadMx); + rendererThreadOwnsRenderer = true; + } + rendererThreadCv.notify_one(); +} + +void GameView::WaitForRendererThread() +{ + std::unique_lock lk(rendererThreadMx); + rendererThreadCv.wait(lk, [this]() { + return !rendererThreadOwnsRenderer; + }); +} diff --git a/src/gui/game/GameView.h b/src/gui/game/GameView.h index 3e55ec1d4..3fe832a54 100644 --- a/src/gui/game/GameView.h +++ b/src/gui/game/GameView.h @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include enum DrawMode { @@ -29,9 +32,11 @@ namespace ui class SplitButton; class Simulation; +struct RenderableSimulation; class MenuButton; class Renderer; +struct RendererSettings; class VideoBuffer; class ToolButton; class GameController; @@ -82,7 +87,8 @@ private: ui::Point currentPoint, lastPoint; GameController * c; - Renderer * ren; + Renderer *ren = nullptr; + RendererSettings *rendererSettings = nullptr; Simulation *sim = nullptr; Brush const *activeBrush; //UI Elements @@ -147,10 +153,28 @@ private: Vec2 PlaceSavePos() const; std::optional FindingElementCandidate() const; + enum RendererThreadState + { + rendererThreadAbsent, + rendererThreadRunning, + rendererThreadPaused, + rendererThreadStopping, + }; + RendererThreadState rendererThreadState = rendererThreadAbsent; + std::thread rendererThread; + std::mutex rendererThreadMx; + std::condition_variable rendererThreadCv; + bool rendererThreadOwnsRenderer = false; + void StartRendererThread(); + void StopRendererThread(); + void RendererThread(); + void WaitForRendererThread(); + void DispatchRendererThread(); + std::unique_ptr rendererThreadSim; public: GameView(); - virtual ~GameView(); + ~GameView(); //Breaks MVC, but any other way is going to be more of a mess. ui::Point GetMousePosition(); @@ -238,4 +262,12 @@ public: pixel GetPixelUnderMouse() const; RendererFrame rendererFrame; + // Call this before accessing Renderer "out of turn", e.g. from RenderView. This *does not* + // include OptionsModel or Lua setting functions because they only access the RendererSettings + // in GameModel, or Lua drawing functions because they only access Renderer in eventTraitSimGraphics + // and *SimDraw events, and the renderer thread gets paused anyway if there are handlers + // installed for such events. + void PauseRendererThread(); + + void RenderSimulation(const RenderableSimulation &sim, bool handleEvents); }; diff --git a/src/gui/preview/PreviewView.cpp b/src/gui/preview/PreviewView.cpp index 938feaad1..d29bc5634 100644 --- a/src/gui/preview/PreviewView.cpp +++ b/src/gui/preview/PreviewView.cpp @@ -575,7 +575,7 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender) if(save->GetGameSave()) { missingElements = save->GetGameSave()->missingElements; - savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true); + savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true, RendererSettings{}); if (savePreview) savePreview->ResizeToFit(RES / 2, true); missingElementsButton->Visible = missingElements; diff --git a/src/gui/render/RenderController.cpp b/src/gui/render/RenderController.cpp index 07de224d9..9c371a275 100644 --- a/src/gui/render/RenderController.cpp +++ b/src/gui/render/RenderController.cpp @@ -5,7 +5,7 @@ #include "Controller.h" -RenderController::RenderController(Simulation *sim, Renderer * ren, std::function onDone_): +RenderController::RenderController(Simulation *sim, Renderer * ren, RendererSettings *rendererSettings, std::function onDone_): HasExited(false) { renderView = new RenderView(); @@ -14,7 +14,7 @@ RenderController::RenderController(Simulation *sim, Renderer * ren, std::functio renderView->AttachController(this); renderModel->AddObserver(renderView); - renderModel->SetRenderer(ren); + renderModel->SetRenderer(ren, rendererSettings); renderModel->SetSimulation(sim); onDone = onDone_; } diff --git a/src/gui/render/RenderController.h b/src/gui/render/RenderController.h index b027d9125..82be481b3 100644 --- a/src/gui/render/RenderController.h +++ b/src/gui/render/RenderController.h @@ -5,6 +5,7 @@ class RenderView; class RenderModel; class Renderer; +struct RendererSettings; class Simulation; class RenderController { @@ -13,7 +14,7 @@ class RenderController std::function onDone; public: bool HasExited; - RenderController(Simulation *sim, Renderer * ren, std::function onDone = nullptr); + RenderController(Simulation *sim, Renderer * ren, RendererSettings *rendererSettings, std::function onDone = nullptr); void Exit(); RenderView * GetView() { return renderView; } virtual ~RenderController(); diff --git a/src/gui/render/RenderModel.cpp b/src/gui/render/RenderModel.cpp index c15fc1611..1f4ef5064 100644 --- a/src/gui/render/RenderModel.cpp +++ b/src/gui/render/RenderModel.cpp @@ -1,17 +1,10 @@ #include "RenderModel.h" - #include "RenderView.h" - #include "gui/game/RenderPreset.h" - +#include "gui/game/GameController.h" +#include "gui/game/GameView.h" #include "graphics/Renderer.h" -RenderModel::RenderModel(): - renderer(NULL) -{ - -} - void RenderModel::AddObserver(RenderView * observer) { observers.push_back(observer); @@ -23,44 +16,35 @@ void RenderModel::AddObserver(RenderView * observer) void RenderModel::SetRenderMode(uint32_t newRenderMode) { - if (renderer) - { - renderer->SetRenderMode(newRenderMode); - } + rendererSettings->renderMode = newRenderMode; notifyRenderChanged(); } uint32_t RenderModel::GetRenderMode() { - return renderer ? renderer->GetRenderMode() : 0; + return rendererSettings->renderMode; } void RenderModel::SetDisplayMode(uint32_t newDisplayMode) { - if (renderer) - { - renderer->SetDisplayMode(newDisplayMode); - } + rendererSettings->displayMode = newDisplayMode; notifyDisplayChanged(); } uint32_t RenderModel::GetDisplayMode() { - return renderer ? renderer->GetDisplayMode() : 0; + return rendererSettings->displayMode; } void RenderModel::SetColorMode(uint32_t newColorMode) { - if (renderer) - { - renderer->SetColorMode(newColorMode); - } + rendererSettings->colorMode = newColorMode; notifyColourChanged(); } uint32_t RenderModel::GetColorMode() { - return renderer ? renderer->GetColorMode() : 0; + return rendererSettings->colorMode; } void RenderModel::LoadRenderPreset(int presetNum) @@ -71,9 +55,10 @@ void RenderModel::LoadRenderPreset(int presetNum) SetColorMode(preset.colorMode); } -void RenderModel::SetRenderer(Renderer * ren) +void RenderModel::SetRenderer(Renderer * ren, RendererSettings *newRendererSettings) { renderer = ren; + rendererSettings = newRendererSettings; notifyRendererChanged(); notifyRenderChanged(); notifyDisplayChanged(); @@ -94,6 +79,11 @@ Renderer * RenderModel::GetRenderer() return renderer; } +RendererSettings *RenderModel::GetRendererSettings() +{ + return rendererSettings; +} + Simulation *RenderModel::GetSimulation() { return sim; diff --git a/src/gui/render/RenderModel.h b/src/gui/render/RenderModel.h index 448d3db37..8f33d9bb8 100644 --- a/src/gui/render/RenderModel.h +++ b/src/gui/render/RenderModel.h @@ -4,11 +4,13 @@ class RenderView; class Renderer; +struct RendererSettings; class Simulation; class RenderModel { std::vector observers; - Renderer * renderer; + Renderer * renderer = nullptr; + RendererSettings *rendererSettings = nullptr; Simulation *sim = nullptr; void notifyRendererChanged(); void notifySimulationChanged(); @@ -16,11 +18,11 @@ class RenderModel void notifyDisplayChanged(); void notifyColourChanged(); public: - RenderModel(); Renderer * GetRenderer(); + RendererSettings *GetRendererSettings(); Simulation *GetSimulation(); void AddObserver(RenderView * observer); - void SetRenderer(Renderer * ren); + void SetRenderer(Renderer * ren, RendererSettings *newRendererSettings); void SetSimulation(Simulation *newSim); void SetRenderMode(uint32_t newRenderMode); uint32_t GetRenderMode(); diff --git a/src/gui/render/RenderView.cpp b/src/gui/render/RenderView.cpp index f900e2b47..4e8a0f989 100644 --- a/src/gui/render/RenderView.cpp +++ b/src/gui/render/RenderView.cpp @@ -1,18 +1,16 @@ #include "RenderView.h" - #include "simulation/ElementGraphics.h" #include "simulation/SimulationData.h" #include "simulation/Simulation.h" - #include "graphics/Graphics.h" #include "graphics/Renderer.h" #include "graphics/VideoBuffer.h" - #include "RenderController.h" #include "RenderModel.h" - #include "gui/interface/Checkbox.h" #include "gui/interface/Button.h" +#include "gui/game/GameController.h" +#include "gui/game/GameView.h" class ModeCheckbox : public ui::Checkbox { @@ -144,6 +142,7 @@ void RenderView::OnTryExit(ExitMethod method) void RenderView::NotifyRendererChanged(RenderModel * sender) { ren = sender->GetRenderer(); + rendererSettings = sender->GetRendererSettings(); } void RenderView::NotifySimulationChanged(RenderModel * sender) @@ -183,20 +182,14 @@ void RenderView::OnDraw() { Graphics * g = GetGraphics(); g->DrawFilledRect(WINDOW.OriginRect(), 0x000000_rgb); - if(ren) + auto *view = GameController::Ref().GetView(); + view->PauseRendererThread(); + ren->ApplySettings(*rendererSettings); + view->RenderSimulation(*sim, true); + for (auto y = 0; y < YRES; ++y) { - // we're the main thread, we may write graphicscache - auto &sd = SimulationData::Ref(); - std::unique_lock lk(sd.elementGraphicsMx); - ren->sim = sim; - ren->clearScreen(); - ren->RenderSimulation(); - ren->sim = nullptr; - for (auto y = 0; y < YRES; ++y) - { - auto &video = ren->GetVideo(); - std::copy_n(video.data() + video.Size().X * y, video.Size().X, g->Data() + g->Size().X * y); - } + auto &video = ren->GetVideo(); + std::copy_n(video.data() + video.Size().X * y, video.Size().X, g->Data() + g->Size().X * y); } g->DrawLine({ 0, YRES }, { XRES-1, YRES }, 0xC8C8C8_rgb); g->DrawLine({ line1, YRES }, { line1, WINDOWH }, 0xC8C8C8_rgb); diff --git a/src/gui/render/RenderView.h b/src/gui/render/RenderView.h index 2de166324..d6ac49768 100644 --- a/src/gui/render/RenderView.h +++ b/src/gui/render/RenderView.h @@ -5,12 +5,14 @@ class ModeCheckbox; class Renderer; +struct RendererSettings; class Simulation; class RenderController; class RenderModel; class RenderView: public ui::Window { RenderController * c; Renderer * ren; + RendererSettings *rendererSettings = nullptr; Simulation *sim = nullptr; std::vector renderModes; std::vector displayModes; diff --git a/src/lua/CommandInterface.h b/src/lua/CommandInterface.h index 5cca385c0..0afa3ad50 100644 --- a/src/lua/CommandInterface.h +++ b/src/lua/CommandInterface.h @@ -34,6 +34,7 @@ public: void Init(); bool HandleEvent(const GameControllerEvent &event); + bool HaveSimGraphicsEventHandlers(); int Command(String command); String FormatCommand(String command); diff --git a/src/lua/LuaGraphics.cpp b/src/lua/LuaGraphics.cpp index ba0ae5d56..f6c746824 100644 --- a/src/lua/LuaGraphics.cpp +++ b/src/lua/LuaGraphics.cpp @@ -11,16 +11,6 @@ static int32_t int32Truncate(double n) return int32_t(n); } -static std::variant currentGraphics() -{ - auto *lsi = GetLSI(); - if (lsi->eventTraits & eventTraitSimGraphics) - { - return lsi->ren; - } - return lsi->g; -} - static int textSize(lua_State *L) { auto text = tpt_lua_optString(L, 1, ""); @@ -51,7 +41,7 @@ static int drawText(lua_State *L) std::visit([x, y, r, g, b, a, &text](auto p) { p->BlendText({ x, y }, text, RGBA(r, g, b, a)); - }, currentGraphics()); + }, GetLSI()->GetGraphics()); return 0; } @@ -73,7 +63,7 @@ static int drawPixel(lua_State *L) else if (a > 255) a = 255; std::visit([x, y, r, g, b, a](auto p) { p->BlendPixel({ x, y }, RGBA(r, g, b, a)); - }, currentGraphics()); + }, GetLSI()->GetGraphics()); return 0; } @@ -106,7 +96,7 @@ static int drawLine(lua_State *L) { p->BlendLine({ x1, y1 }, { x2, y2 }, RGBA(r, g, b, a)); } - }, currentGraphics()); + }, GetLSI()->GetGraphics()); return 0; } @@ -139,7 +129,7 @@ static int drawRect(lua_State *L) { p->BlendRect(RectSized(Vec2{ x, y }, Vec2{ width, height }), RGBA(r, g, b, a)); } - }, currentGraphics()); + }, GetLSI()->GetGraphics()); return 0; } @@ -172,7 +162,7 @@ static int fillRect(lua_State *L) { p->BlendFilledRect(RectSized(Vec2{ x, y }, Vec2{ width, height }), RGBA(r, g, b, a)); } - }, currentGraphics()); + }, GetLSI()->GetGraphics()); return 0; } @@ -198,7 +188,7 @@ static int drawCircle(lua_State *L) std::visit([x, y, rx, ry, r, g, b, a](auto p) { p->BlendEllipse({ x, y }, { abs(rx), abs(ry) }, RGBA(r, g, b, a)); - }, currentGraphics()); + }, GetLSI()->GetGraphics()); return 0; } @@ -224,7 +214,7 @@ static int fillCircle(lua_State *L) std::visit([x, y, rx, ry, r, g, b, a](auto p) { p->BlendFilledEllipse({ x, y }, { abs(rx), abs(ry) }, RGBA(r, g, b, a)); - }, currentGraphics()); + }, GetLSI()->GetGraphics()); return 0; } diff --git a/src/lua/LuaRenderer.cpp b/src/lua/LuaRenderer.cpp index 5674c81d6..791abac57 100644 --- a/src/lua/LuaRenderer.cpp +++ b/src/lua/LuaRenderer.cpp @@ -10,10 +10,10 @@ static int renderMode(lua_State *L) auto *lsi = GetLSI(); if (lua_gettop(L)) { - lsi->ren->SetRenderMode(luaL_checkinteger(L, 1)); + lsi->gameModel->GetRendererSettings().renderMode = luaL_checkinteger(L, 1); return 0; } - lua_pushinteger(L, lsi->ren->GetRenderMode()); + lua_pushinteger(L, lsi->gameModel->GetRendererSettings().renderMode); return 1; } @@ -63,11 +63,10 @@ static int fireSize(lua_State *L) auto *lsi = GetLSI(); if (lua_gettop(L) < 1) { - lua_pushnumber(L, lsi->gameModel->GetRenderer()->GetFireIntensity()); + lua_pushnumber(L, lsi->gameModel->GetRendererSettings().fireIntensity); return 1; } - float fireintensity = float(luaL_checknumber(L, 1)); - lsi->gameModel->GetRenderer()->prepare_alpha(CELL, fireintensity); + lsi->gameModel->GetRendererSettings().fireIntensity = float(luaL_checknumber(L, 1)); return 0; } @@ -76,10 +75,10 @@ static int displayMode(lua_State *L) auto *lsi = GetLSI(); if (lua_gettop(L)) { - lsi->ren->SetDisplayMode(luaL_checkinteger(L, 1)); + lsi->gameModel->GetRendererSettings().displayMode = luaL_checkinteger(L, 1); return 0; } - lua_pushinteger(L, lsi->ren->GetDisplayMode()); + lua_pushinteger(L, lsi->gameModel->GetRendererSettings().displayMode); return 1; } @@ -89,10 +88,10 @@ static int colorMode(lua_State *L) auto *lsi = GetLSI(); if (lua_gettop(L)) { - lsi->ren->SetColorMode(luaL_checkinteger(L, 1)); + lsi->gameModel->GetRendererSettings().colorMode = luaL_checkinteger(L, 1); return 0; } - lua_pushinteger(L, lsi->ren->GetColorMode()); + lua_pushinteger(L, lsi->gameModel->GetRendererSettings().colorMode); return 1; } @@ -117,11 +116,11 @@ static int grid(lua_State *L) int acount = lua_gettop(L); if (acount == 0) { - lua_pushnumber(L, lsi->ren->GetGridSize()); + lua_pushnumber(L, lsi->gameModel->GetRendererSettings().gridSize); return 1; } int grid = luaL_optint(L, 1, -1); - lsi->ren->SetGridSize(grid); + lsi->gameModel->GetRendererSettings().gridSize = grid; return 0; } diff --git a/src/lua/LuaScriptInterface.cpp b/src/lua/LuaScriptInterface.cpp index 6f37a8d58..37ae26776 100644 --- a/src/lua/LuaScriptInterface.cpp +++ b/src/lua/LuaScriptInterface.cpp @@ -116,12 +116,12 @@ String LuaGetError() LuaScriptInterface::LuaScriptInterface(GameController *newGameController, GameModel *newGameModel) : CommandInterface(newGameController, newGameModel), + ren(newGameModel->GetRenderer()), gameModel(newGameModel), gameController(newGameController), window(gameController->GetView()), sim(gameModel->GetSimulation()), g(ui::Engine::Ref().g), - ren(gameModel->GetRenderer()), customElements(PT_NUM), gameControllerEventHandlers(std::variant_size_v) { @@ -417,6 +417,42 @@ bool CommandInterface::HandleEvent(const GameControllerEvent &event) return cont; } +template +std::enable_if_t, bool> HaveSimGraphicsEventHandlersHelper(lua_State *L, std::vector &gameControllerEventHandlers) +{ + if (std::variant_alternative_t::traits & eventTraitSimGraphics) + { + gameControllerEventHandlers[Index].Push(L); + auto have = lua_objlen(L, -1) > 0; + lua_pop(L, 1); + if (have) + { + return true; + } + } + return HaveSimGraphicsEventHandlersHelper(L, gameControllerEventHandlers); +} + +template +std::enable_if_t, bool> HaveSimGraphicsEventHandlersHelper(lua_State *L, std::vector &gameControllerEventHandlers) +{ + return false; +} + +bool CommandInterface::HaveSimGraphicsEventHandlers() +{ + auto &sd = SimulationData::CRef(); + auto *lsi = static_cast(this); + for (int i = 0; i < int(lsi->customElements.size()); ++i) + { + if (lsi->customElements[i].graphics && !sd.graphicscache[i].isready && lsi->sim->elementCount[i]) + { + return true; + } + } + return HaveSimGraphicsEventHandlersHelper<0>(lsi->L, lsi->gameControllerEventHandlers); +} + void CommandInterface::OnTick() { auto *lsi = static_cast(this); @@ -804,3 +840,4 @@ void CommandInterfaceDeleter::operator ()(CommandInterface *ptr) const { delete static_cast(ptr); } + diff --git a/src/lua/LuaScriptInterface.h b/src/lua/LuaScriptInterface.h index 6b0ee07d1..e6f58fdc0 100644 --- a/src/lua/LuaScriptInterface.h +++ b/src/lua/LuaScriptInterface.h @@ -62,6 +62,8 @@ class LuaScriptInterface : public CommandInterface { LuaStatePtr luaState; + Renderer *ren; + public: lua_State *L{}; @@ -70,7 +72,18 @@ public: ui::Window *window; Simulation *sim; Graphics *g; - Renderer *ren; + + std::variant GetGraphics() + { + if (eventTraits & eventTraitSimGraphics) + { + // This is ok without calling gameModel->view->PauseRendererThread() because + // the renderer thread gets paused anyway if there are handlers + // installed for eventTraitSimGraphics and *SimDraw events. + return ren; + } + return g; + } std::vector customElements; // must come after luaState diff --git a/src/lua/PlainCommandInterface.cpp b/src/lua/PlainCommandInterface.cpp index e8ac71cd3..707af8765 100644 --- a/src/lua/PlainCommandInterface.cpp +++ b/src/lua/PlainCommandInterface.cpp @@ -23,6 +23,11 @@ bool CommandInterface::HandleEvent(const GameControllerEvent &event) return true; } +bool CommandInterface::HaveSimGraphicsEventHandlers() +{ + return false; +} + int CommandInterface::Command(String command) { return PlainCommand(command); diff --git a/src/simulation/SaveRenderer.cpp b/src/simulation/SaveRenderer.cpp index fad7d3379..444dd4b11 100644 --- a/src/simulation/SaveRenderer.cpp +++ b/src/simulation/SaveRenderer.cpp @@ -13,32 +13,24 @@ SaveRenderer::SaveRenderer() sim = std::make_unique(); ren = std::make_unique(); ren->sim = sim.get(); - ren->decorations_enable = true; - ren->blackDecorations = true; } SaveRenderer::~SaveRenderer() = default; -std::unique_ptr SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource) +std::unique_ptr SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, RendererSettings rendererSettings) { // this function usually runs on a thread different from where element info in SimulationData may be written, so we acquire a read-only lock on it auto &sd = SimulationData::CRef(); std::shared_lock lk(sd.elementGraphicsMx); std::lock_guard gx(renderMutex); - ren->ResetModes(); - if (renderModeSource) - { - ren->SetRenderMode(renderModeSource->GetRenderMode()); - ren->SetDisplayMode(renderModeSource->GetDisplayMode()); - ren->SetColorMode(renderModeSource->GetColorMode()); - } + rendererSettings.decorations_enable = true; + rendererSettings.blackDecorations = !decorations; + ren->ApplySettings(rendererSettings); sim->clear_sim(); sim->Load(save, true, { 0, 0 }); - ren->decorations_enable = true; - ren->blackDecorations = !decorations; ren->ClearAccumulation(); ren->clearScreen(); diff --git a/src/simulation/SaveRenderer.h b/src/simulation/SaveRenderer.h index a5fffca98..9e058f168 100644 --- a/src/simulation/SaveRenderer.h +++ b/src/simulation/SaveRenderer.h @@ -4,6 +4,7 @@ #include #include #include "common/ExplicitSingleton.h" +#include "graphics/RendererSettings.h" #include "common/String.h" class GameSave; @@ -20,5 +21,5 @@ class SaveRenderer: public ExplicitSingleton public: SaveRenderer(); ~SaveRenderer(); - std::unique_ptr Render(const GameSave *save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr); + std::unique_ptr Render(const GameSave *save, bool decorations, bool fire, RendererSettings rendererSettings); };