Named camera bookmarks, Auto Engineer / WaypointQueue pins, and extracted track labels. Stock MapWindow stays collapsed while S3 owns the camera. Radio Runner is not in this drop.
207 lines
11 KiB
C++
207 lines
11 KiB
C++
#pragma once
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h>
|
|
#include <d3d11.h>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <mutex>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include "input_queue.h"
|
|
|
|
struct PopoutWindow {
|
|
HWND hwnd = nullptr;
|
|
HWND contentHwnd = nullptr; // D3D11 swapchain target
|
|
IDXGISwapChain* swapChain = nullptr;
|
|
ID3D11RenderTargetView* rtv = nullptr;
|
|
|
|
// Message-pump thread that owns hwnd. Kept open for the window's lifetime so
|
|
// DestroyPopoutWindow can join it before freeing this struct (the pump thread
|
|
// touches `this` while processing WM_DESTROY).
|
|
HANDLE pumpThread = nullptr;
|
|
|
|
// Swapchain dimensions — content area only (no status bar gap any more;
|
|
// the ImGui toolbar is an overlay rendered into the same swapchain).
|
|
std::atomic<int> width {800};
|
|
std::atomic<int> height {600};
|
|
|
|
// Texture + UV rect written by RRPOPOUT_SetFrameTexture on the main thread,
|
|
// read by Renderer_Present on the render thread.
|
|
std::atomic<void*> pendingTexture {nullptr};
|
|
std::atomic<float> srcU0 {0.f}, srcV0 {0.f};
|
|
std::atomic<float> srcU1 {1.f}, srcV1 {1.f};
|
|
|
|
std::atomic<bool> resizing {false};
|
|
std::atomic<bool> destroyRequested {false};
|
|
std::atomic<bool> alive {true};
|
|
|
|
InputQueue inputQueue;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// ImGui input state
|
|
// Written by the Win32 message pump thread; consumed by the render thread.
|
|
// -----------------------------------------------------------------------
|
|
std::atomic<float> imMouseX {-1.f};
|
|
std::atomic<float> imMouseY {-1.f};
|
|
std::atomic<bool> imLButton {false};
|
|
std::atomic<bool> imRButton {false};
|
|
// Wheel accumulator in raw WHEEL_DELTA units (120 per notch).
|
|
// fetch_add on message pump; exchange(0) on render thread.
|
|
std::atomic<int> imWheelRaw {0};
|
|
// Written by render thread after each ImGui frame.
|
|
// When true, PollInputEvents suppresses mouse events so they don't reach Unity.
|
|
std::atomic<bool> imWantMouse {false};
|
|
|
|
// When true, Present blits the frame texture only: no map toolbar, compass,
|
|
// radio rail, or view presets. Used by Car Cards (and any future non-map window).
|
|
std::atomic<bool> imPlainContent {false};
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Status bar label (UTF-8), shown in the ImGui toolbar.
|
|
// Written by RRPOPOUT_SetStatusText (main thread), read by render thread.
|
|
// -----------------------------------------------------------------------
|
|
char imStatusText[256];
|
|
std::mutex imStatusMutex;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Dynamic menu lists (UTF-8 labels), set by RRPOPOUT_SetLocoList /
|
|
// RRPOPOUT_SetLocationList from the C# side every few seconds.
|
|
// Read by the render thread inside the ImGui settings menu.
|
|
// -----------------------------------------------------------------------
|
|
struct ImMenuItem { char label[128]; };
|
|
std::vector<ImMenuItem> imLocoList;
|
|
std::mutex imLocoMutex;
|
|
std::vector<ImMenuItem> imLocationList;
|
|
std::mutex imLocationMutex;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Map rotation + ME flag (C# → render thread via exports)
|
|
// -----------------------------------------------------------------------
|
|
std::atomic<float> imMapRotationDeg {0.f};
|
|
std::atomic<float> imMapZoom {500.f};
|
|
std::atomic<bool> imMapSyncPlayer {false};
|
|
std::atomic<bool> imMapEnhancerInstalled {false};
|
|
std::atomic<bool> imMEHasTurntable {false}; // true if ShowTurntableMarkers exists (v1.6.0 or community)
|
|
std::atomic<bool> imMECommunity {false}; // true = community build (has crossing/spawn/switch-reset)
|
|
std::atomic<bool> imFollowPlayer {false};
|
|
std::atomic<bool> imAlwaysOnTop {false};
|
|
std::atomic<bool> imPanDisablesFollow {true};
|
|
std::atomic<bool> imRightClickRecenter {true};
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Icon culling + EOTD settings (C# → render thread via RRPOPOUT_SetIconCullingState)
|
|
// -----------------------------------------------------------------------
|
|
std::atomic<bool> imIconCullingEnabled {true};
|
|
std::atomic<float> imCullThreshold {0.40f};
|
|
std::atomic<bool> imHideMuLocos {true};
|
|
std::atomic<float> imLocoBoostedScale {1.5f};
|
|
std::atomic<bool> imEotdEnabled {true};
|
|
std::atomic<bool> imEotdOnlyWhenCulled {true};
|
|
std::atomic<float> imEotdSizeScale {8.0f};
|
|
|
|
// -----------------------------------------------------------------------
|
|
// MapEnhancer settings state (C# → render thread via RRPOPOUT_SetMEState)
|
|
// Bit layout of imMEFlags matches MEBoolBit enum in shared_types.h.
|
|
// Scale atomics are live-updated during slider drag so the slider doesn't
|
|
// snap back between frames, then the final value is pushed as UICmd::SetMEFloat.
|
|
// -----------------------------------------------------------------------
|
|
std::atomic<uint32_t> imMEFlags {0};
|
|
std::atomic<float> imMEFlareScale {0.6f};
|
|
std::atomic<float> imMEJunctionSc {0.6f};
|
|
std::atomic<float> imMETrackThick {1.25f};
|
|
std::atomic<float> imMECrossingSc {0.3f};
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Track name labels (industry track names + viewport UVs, pushed from C# each frame).
|
|
// C# projects TrackSpan centroids through the map camera, culls off-screen entries,
|
|
// and pushes (name, centroid_uv, anchors). Native runs per-frame collision avoidance
|
|
// (AABB push in pixel space) then draws leader lines to each anchor before the text.
|
|
// -----------------------------------------------------------------------
|
|
struct ImTrackLabel {
|
|
char name[64];
|
|
float u, v; // centroid UV — starting position for the layout solver
|
|
float angle; // track direction in screen space (degrees; 0=right, +ve=CW)
|
|
float scale; // font size multiplier (1.0 = track label, 1.5 = industry label)
|
|
int anchorStart; // index into imAnchorUs/Vs
|
|
int anchorCount; // 1 for single span; >1 for merged same-name cluster
|
|
};
|
|
std::vector<ImTrackLabel> imTrackLabels;
|
|
std::vector<float> imAnchorUs; // flat anchor UV arrays, indexed by anchorStart
|
|
std::vector<float> imAnchorVs;
|
|
std::mutex imTrackLabelMutex;
|
|
std::atomic<bool> imTrackLabelsEnabled {true};
|
|
std::atomic<float> imTrackLabelFontSize {14.f};
|
|
std::atomic<float> imTrackLabelLineThickness {1.5f};
|
|
std::atomic<float> imTrackLabelZoomLimit {3000.f};
|
|
std::atomic<bool> imTrackLeaderLinesEnabled {true};
|
|
std::atomic<bool> imTrackCollisionEnabled {true};
|
|
std::atomic<bool> imTrackParallelEnabled {false};
|
|
std::atomic<bool> imTrackMergeEnabled {true};
|
|
std::atomic<float> imTrackLabelMergeZoom {150.f};
|
|
std::atomic<float> imTrackIndustryZoom {200.f};
|
|
std::atomic<bool> imTrackUtilityRepair {true};
|
|
std::atomic<bool> imTrackUtilityDiesel {true};
|
|
std::atomic<bool> imTrackUtilityLoader {true};
|
|
std::atomic<bool> imTrackUtilityInterchange {true};
|
|
std::atomic<float> imTrackUtilityZoom {200.f};
|
|
std::atomic<float> imTrackAllLabelsZoom {8000.f};
|
|
std::atomic<bool> imTrackLabelAvoidTrack {false};
|
|
std::atomic<float> imTrackLabelFontSizeMin {8.f};
|
|
|
|
// Named camera-view presets (C# owns the data; native draws the left rail).
|
|
std::vector<ImMenuItem> imPresetList;
|
|
std::mutex imPresetMutex;
|
|
std::atomic<int> imPresetEditIndex {-1};
|
|
std::atomic<bool> imPresetPreviewing {false};
|
|
std::atomic<int> imPresetPendingDelete{-1};
|
|
char imPresetRenameBuf[128] {};
|
|
|
|
// AE waypoint pins (gear-menu toggles).
|
|
std::atomic<bool> imWaypointsEnabled {true};
|
|
std::atomic<bool> imWaypointsSelectedOnly {false};
|
|
|
|
// Radio-control rail (top-right). C# owns pin ids; native draws the list.
|
|
std::vector<ImMenuItem> imRadioList;
|
|
std::vector<uint32_t> imRadioColors;
|
|
std::mutex imRadioMutex;
|
|
std::atomic<bool> imRadioOn {false};
|
|
std::atomic<int> imRadioSelected {-1};
|
|
std::atomic<int> imRadioTool {0};
|
|
std::atomic<bool> imRadioWq {false};
|
|
std::atomic<uint64_t> imRadioAeBits {0};
|
|
std::atomic<bool> imRadioForward {true};
|
|
std::atomic<float> imRadioSpeed {15.f};
|
|
std::atomic<int> imRadioEditIndex {-1};
|
|
char imRadioRenameBuf[128] {};
|
|
|
|
// Waypoint-mode ghost arrow (Unity viewport UV, v=0 at bottom) + near-cursor popup.
|
|
std::atomic<bool> imRadioGhostOn {false};
|
|
std::atomic<float> imRadioGhostU {0.f};
|
|
std::atomic<float> imRadioGhostV {0.f};
|
|
std::atomic<float> imRadioGhostAngle {0.f}; // screen deg, 0=right, +CW, y-down
|
|
std::atomic<uint32_t> imRadioGhostColor {0xFFFFFFFFu};
|
|
std::atomic<int> imRadioWpStage {0}; // 0 off, 1 choose order, 2 enter count
|
|
std::atomic<float> imRadioWpU {0.f};
|
|
std::atomic<float> imRadioWpV {0.f};
|
|
std::atomic<int> imRadioWpFlags {0}; // bit0 = has couple target
|
|
std::atomic<int> imRadioWpCount {1};
|
|
|
|
// Keyboard for ImGui InputText (preset rename). Pump thread writes, render thread reads.
|
|
std::mutex imKeyMutex;
|
|
char imCharsUtf8[512] {};
|
|
std::atomic<uint32_t> imKeyDown {0};
|
|
std::atomic<uint32_t> imKeyMods {0};
|
|
uint32_t imPrevKeyDown = 0;
|
|
uint32_t imPrevKeyMods = 0;
|
|
|
|
// Loaded geometry from the save file — consumed by MessagePumpThread before CreateWindowExW.
|
|
std::atomic<int> lastWinX {-1}, lastWinY {-1};
|
|
std::atomic<int> lastWinW {900}, lastWinH {700};
|
|
|
|
PopoutWindow() {
|
|
// Status starts empty (filled live with zoom/coords). Avoids a default label
|
|
// that would be redundant with the window title bar and would render any
|
|
// non-ASCII glyph as '?' in ImGui's default font.
|
|
imStatusText[0] = '\0';
|
|
}
|
|
};
|