A per-cell humidity/cloud/rain cycle advanced on the live clock (PlanetWeather.cpp, raylib-free): evaporate over warm sunlit seas -> advect humidity & cloud along the prevailing wind (upwind differencing) -> condense into cloud (saturation vs temperature + windward orographic lift) -> rain out thick cloud -> dissipate. Bounded exp-rate forms keep it stable at any timestep, so it runs cleanly from hours/sec up to a month/sec. initWeather() spins the fields up from the moisture climatology; fully deterministic (no RNG). Render: a translucent cloud shell over the 3D globe (white -> dark slate where it rains, alpha = cover) plus a matching drawWeather2D layer on the 2D map (shared drawMapTris rasterizer), toggled with K (default on). stepSim runs stepWeather each live frame at the sim-hours added to liveTime (held when paused). Cell-info shows cloud/humidity/raining. Saved as v10 (humidity/cloud/rain, flag-gated; pre-v10 saves spin weather up live). New weather* config knobs. Reseed/regen now also drops out of Live World. test_weather.cpp: fields in range, clouds form + rain falls, oceans moister than land, determinism, v10 round-trip; the other four suites still pass; GUI build clean. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
150 lines
6.9 KiB
C++
150 lines
6.9 KiB
C++
#pragma once
|
|
#include "raylib.h"
|
|
#include "Planet.hpp"
|
|
#include "Colors.hpp" // ColorMode
|
|
#include "Overlays.hpp" // PlateLabel
|
|
#include "Map2D.hpp" // Map2D
|
|
#include <vector>
|
|
#include <string>
|
|
#include <memory>
|
|
|
|
// The interactive viewer: owns all window/sim/view state and runs the frame
|
|
// loop. The old free-standing main() lived as one giant function with capturing
|
|
// lambdas; those lambdas are now methods and their captured locals are members,
|
|
// so the body splits cleanly across Viewer.cpp (setup + sim orchestration),
|
|
// ViewerInput.cpp (input/picking/keys) and ViewerRender.cpp (drawing).
|
|
struct Viewer {
|
|
// ---- Files / save format ------------------------------------------------
|
|
static constexpr uint32_t SAVE_VERSION = 10; // v10: +weather; v9: +moons; v8: +Live World clock; v7: +biota; v6: self-describing config; v4: +biome; v3: +phase3
|
|
const char* CONFIG_PATH = "planet.cfg";
|
|
const char* SAVE_PATH = "planet.save";
|
|
std::string configPath = "planet.cfg"; // initial config (--config overrides)
|
|
|
|
// ---- Window / layout (set in init) --------------------------------------
|
|
int screenW = 1920, screenH = 1080;
|
|
int leftW = 0, rightX = 0, rightW = 0, rightH = 0;
|
|
int view3DW = 0, view3DH = 0;
|
|
RenderTexture2D rt3d{};
|
|
Rectangle mapRect{}, hoverRect{}, panelRect{}, gridRect{};
|
|
Rectangle liveInfoRect{}; // free space right of the (left-aligned) 2D map: moon/tide phase
|
|
Rectangle pauseBtn{}, p3ContinueBtn{}, p3StartBtn{};
|
|
float pbCx = 0.0f, pbCy = 0.0f;
|
|
|
|
const float visBase = 2.0f;
|
|
const float elevExagg = 0.00000004f;
|
|
const float borderR = visBase + 0.004f;
|
|
const float driftR = visBase + 0.006f;
|
|
const float riverR = visBase + 0.005f;
|
|
const float gratR = visBase + 0.003f;
|
|
const int subRes = 16;
|
|
|
|
// ---- Sim / world --------------------------------------------------------
|
|
Planet planet;
|
|
PlanetConfig cfg;
|
|
|
|
Camera3D cam{};
|
|
float camYaw = 0.4f, camPitch = 0.3f, camDist = 6.0f;
|
|
|
|
ColorMode mode = ColorMode::Elevation;
|
|
std::vector<Color> vcolors;
|
|
|
|
std::vector<Vector3> borders, ridgeBorders; bool showBorders = true;
|
|
std::vector<Vector3> driftArrows; bool showDrift = true;
|
|
std::vector<PlateLabel> plateLabels;
|
|
std::vector<Vector3> rivers, bigRivers; bool showRivers = true;
|
|
std::vector<std::vector<Vector2>> graticule; bool showGrat = false;
|
|
Map2D map2D;
|
|
|
|
// Phase-1 forming model.
|
|
bool paused = false, settled = false;
|
|
long long stepCount = 0;
|
|
double maxChange = 0.0;
|
|
int settleRun = 0;
|
|
const double settleThresh = 2.0; // m/tick at or below which it's "settled"
|
|
const int settleNeed = 3; // consecutive settled ticks before pausing
|
|
const double formRate = 55.0; // forming ticks per second (watchable)
|
|
double formAccum = 0.0;
|
|
double minE = 0.0, maxE = 0.0;
|
|
|
|
// Phase 2 drift.
|
|
double elapsedMy = 0.0, dtMy = 0.0, driftRate = 4.0, driftAccum = 0.0;
|
|
|
|
// Phase 3 hydrology.
|
|
bool phase3 = false, phase3Prompt = false;
|
|
double phase3PromptAt = 0.0;
|
|
|
|
// Live World: a slow real-time clock (hours -> weeks/months) over the finished planet.
|
|
// Geological drift freezes while it runs; a moving day/night terminator, a live seasonal
|
|
// temperature cycle and a moving snow line animate. liveRate is sim hours per real second.
|
|
bool liveWorld = false, dayNightOn = true;
|
|
double liveTime = 0.0; // hours since the live clock started
|
|
double liveRate = 1.0; // sim hours advanced per real second (ramps hour->month)
|
|
std::vector<float> illum; // per-cell day/night brightness (1 = day, floor = night)
|
|
std::vector<Color> shadedColors;// vcolors + snow/ice tint + day/night dim (live overlay)
|
|
Vector3 sunDir{0.0f, 0.0f, 1.0f}; // model-space sub-solar direction (for the 3D sun marker)
|
|
std::vector<Vector3> moonDirs; // model-space sub-lunar directions (one per moon, for render)
|
|
std::vector<Vector3> moonNormals;// model-space orbit-plane normals (one per moon, for the ring)
|
|
std::vector<Vector3> coast; // coastline segments (land/ocean boundary, rebuilt with terrain)
|
|
std::vector<int> coastOcean; // ocean cell per coast segment (to sample tide)
|
|
std::vector<Color> coastCols; // per-segment tide colour (filled each frame when showTides)
|
|
bool showTides = false; // colour the coastline by the live tide level (key T)
|
|
std::vector<Vector3> currentSegs; std::vector<Color> currentCols; // ocean-current arrows
|
|
bool showCurrents = false; // ocean current arrows, warm/cold (key O)
|
|
bool showClouds = true; // Live World cloud/rain cover overlay (key K)
|
|
|
|
// Selection + subgrid (phase 4/5 preview).
|
|
int selectedCell = -1;
|
|
double selectedThresh = 0.06;
|
|
std::vector<std::shared_ptr<SubGrid>> subgrids;
|
|
|
|
// Transient on-screen status line.
|
|
std::string statusMsg; double statusUntil = 0.0;
|
|
|
|
// Input state.
|
|
float dragDist = 0.0f;
|
|
double mapLon = 0.0; // 2D map longitude pan (radians)
|
|
bool pressInMap = false; // a drag that started on the map pans it
|
|
|
|
// Per-frame picking state (set by handleInput, read by render).
|
|
Vector2 mp{};
|
|
bool onPause = false;
|
|
int hovered = -1;
|
|
bool hasHoverSub = false;
|
|
SubCell hoverSub;
|
|
int hoveredSubIdx = -1;
|
|
|
|
// ---- Lifecycle ----------------------------------------------------------
|
|
bool init(int argc, char** argv); // window, layout, config, first world
|
|
void run(); // the frame loop (until window closes)
|
|
|
|
// ---- Sim orchestration (Viewer.cpp) -------------------------------------
|
|
void rebuildSub();
|
|
void selectCell(int idx);
|
|
void recolor();
|
|
void refreshView();
|
|
void rebuildLiveOverlay(); // Live World: fill illum + shadedColors from sim fields
|
|
// Colors the 3D globe + 2D map actually draw: the live overlay when in Live World, else the
|
|
// plain per-cell colours.
|
|
const std::vector<Color>& displayColors() const { return liveWorld ? shadedColors : vcolors; }
|
|
void regenWorld(); // after generate(): geometry changed
|
|
void regen(); // generate(cfg) + regenWorld()
|
|
void stepOnce(); // one tick + settle bookkeeping
|
|
void pauseAction();
|
|
void setStatus(const std::string& m);
|
|
void saveGame(const char* path);
|
|
void loadGame(const char* path);
|
|
void stepSim(); // advance forming / drift+hydrology this frame
|
|
|
|
// ---- Input (ViewerInput.cpp) --------------------------------------------
|
|
void handleInput();
|
|
|
|
// ---- Render (ViewerRender.cpp) ------------------------------------------
|
|
void renderFrame();
|
|
void renderGlobe3D();
|
|
void renderMap2D();
|
|
void renderLiveInfo(); // Live World: moon phases + (coastal) tidal phase, beside the 2D map
|
|
void renderPanels();
|
|
void renderHUD();
|
|
void renderPrompt();
|
|
};
|