diff --git a/board/README.md b/board/README.md index b23e2b5..0d8c7a8 100644 --- a/board/README.md +++ b/board/README.md @@ -5,8 +5,8 @@ The LumenLab was built in two phases. The first phase (labeled as "Version 1") w The LumenLab PCB was designed using KiCad 9.0.5 # Schematic -![LumenLab schematic](./images/lumenlab-pcb.svg) -

Figure 1. LumenLab schematic and electronic layout

+![LumenLab schematic](./images/lumenlab-pcb.jpg) +

Figure 1. LumenLab schematic and electronic layout

# Bill of Materials Still to be finalized, however you can [view the tentative list of components here](./bom/ibom.html). diff --git a/board/enclosure/README.md b/board/enclosure/README.md index dc96ebc..2f6f926 100644 --- a/board/enclosure/README.md +++ b/board/enclosure/README.md @@ -20,16 +20,16 @@ My previous design served me well for my needs, but it isn't an ideal solution f ![Front edge angle of the LumenLab enclosure v3, with a controller saddle](../images/lumenlab-platform-enclosure-with-controller-front.png) -*Front edge angle of the LumenLab enclosure v3, with a controller saddle* +

Front edge angle of the LumenLab enclosure v3, with a controller saddle

![Rear edge angle of the LumenLab enclosure v3, with a controller saddle](../images/lumenlab-platform-enclosure-with-controller-rear.png) -*Rear edge angle of the LumenLab enclosure v3, with a controller saddle* +

Rear edge angle of the LumenLab enclosure v3, with a controller saddle

![Front edge angle of the LumenLab enclosure v3, no controller saddle](../images/lumenlab-platform-enclosure-no-controller-front.png) -*Front edge angle of the LumenLab enclosure v3, no controller saddle* +

Front edge angle of the LumenLab enclosure v3, no controller saddle

![Rear edge angle of the LumenLab enclosure v3, no controller saddle](../images/lumenlab-platform-enclosure-no-controller-rear.png) -*Rear edge angle of the LumenLab enclosure v3, no controller saddle* +

Rear edge angle of the LumenLab enclosure v3, no controller saddle

diff --git a/board/images/lumenlab-pcb.jpg b/board/images/lumenlab-pcb.jpg new file mode 100644 index 0000000..0413815 Binary files /dev/null and b/board/images/lumenlab-pcb.jpg differ diff --git a/board/images/lumenlab-pcb.svg b/board/images/lumenlab-pcb.svg index 5525b4f..ebd5fe7 100644 --- a/board/images/lumenlab-pcb.svg +++ b/board/images/lumenlab-pcb.svg @@ -7,7 +7,7 @@ xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="297.0022mm" height="210.0072mm" viewBox="0.0000 0.0000 297.0022 210.0072"> -SVG Image created as lumenlab-pcb.svg date 2025/10/13 21:04:53 +SVG Image created as lumenlab-pcb.svg date 2026/07/03 03:06:23 Image generated by Eeschema-SVG 4.7K -4.7K -33 +33 + + + + + - - - - - - - - - - - - + + + + + + + + + 4.7K -4.7K -33 +33 + + + + + + - - - - - - - - - - - - + + + + + + + + -10K -10K - - - - - - - - - - - - - - - -4.7K +4.7K + - - - - - - - - - - - - - - -C23 -C23 - - - - - - - - - - - - - - - - - - - - - - - - - - - - -C22 +C22 + - - - - - - - - - -0.1uF -0.1uF - - - - - - - - - - - - - - - - - - - - - - - - - -47nF +47nF + - - - - - - - - - - - - - - - - -C22 -C22 - - - - - - - - - - - - - - - - - - - - - - -C23 +C23 + - - - - - - - - - - - - -0.1uF -0.1uF - - - - - - - - - - - - - - - - - - - - - - - - - -47nF +47nF + - - - - - - - - - - - - - - -10K -10K - - - - - - - - - - - - - - - -4.7K +4.7K + - - - - - - - - - - - - -LED_DIN -LED_DIN - - - - - - -ESP32_EN +ESP32_EN + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -LED_DIN -LED_DIN - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ESP32_GPIO22 -ESP32_GPIO22 -ESP32_GPIO0 +ESP32_GPIO0 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +ESP32_GPIO22 +ESP32_GPIO22 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -ESP32_GPIO25 -ESP32_GPIO25 - -LED_DIN +LED_DIN + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + +LED_DIN +LED_DIN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +ESP32_GPIO25 +ESP32_GPIO25 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -ESP32_GPIO21 -ESP32_GPIO21 -ESP32_GPIO15 +ESP32_GPIO15 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + -ESP32_GPIO34 -ESP32_GPIO34 -ESP32_GPIO13 +ESP32_GPIO13 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + -ESP32_GPIO15 -ESP32_GPIO15 -ESP32_GPIO14 +ESP32_GPIO14 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ESP32_GPIO13 -ESP32_GPIO13 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ESP32_GPIO14 -ESP32_GPIO14 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ESP32_GPIO22 -ESP32_GPIO22 - - - - - -ESP32_GPIO21 +ESP32_GPIO21 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ESP32_GPIO12 -ESP32_GPIO12 -ESP32_GPIO34 +ESP32_GPIO34 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + -ESP32_GPIO0 -ESP32_GPIO0 -ESP32_GPIO22 +ESP32_GPIO22 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - +ESP32_GPIO12 +ESP32_GPIO12 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ESP32_EN -ESP32_EN - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ESP32_GPIO0 ESP32_GPIO0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -57610,8 +57442,14 @@ L153.6700 99.0600 - + + namespace Engine { - class Layer + enum class Layer : uint8_t { - public: - virtual void nextEvent() = 0; + Background, + GameLayer_1, + GameLayer_2, + GameLayer_3, + GameLayer_4, + Effects }; } \ No newline at end of file diff --git a/include/engine/render-model.h b/include/engine/render-model.h new file mode 100644 index 0000000..5792579 --- /dev/null +++ b/include/engine/render-model.h @@ -0,0 +1,17 @@ +#pragma once + +#include "lights/pixel.h" +#include "engine/layer.h" + +#include + +namespace Engine +{ + class RenderModel + { + public: + virtual CustomDataStructures::Span getRendering() const = 0; + virtual float getPositionPrecise() const = 0; + virtual Layer getLayer() const = 0; + }; +} \ No newline at end of file diff --git a/include/engine/renderer.h b/include/engine/renderer.h new file mode 100644 index 0000000..baf21b5 --- /dev/null +++ b/include/engine/renderer.h @@ -0,0 +1,24 @@ +#pragma once + +#include "lights/led-strip.h" +#include "lights/pixel.h" +#include "engine/render-model.h" +#include "logger.h" +#include + +namespace Engine +{ + class Renderer + { + public: + Renderer() = default; + + Lights::LedStrip leds; + + void drawPixel(const RenderModel &renderModel, const uint16_t normalizedPosition, const uint16_t offset); + void renderEntity(const RenderModel &model); + + private: + void blend(const RenderModel &renderModel, const uint16_t normalizedPosition, uint16_t offset); + }; +} \ No newline at end of file diff --git a/include/games/chain-reaction/driver.h b/include/games/chain-reaction/game-main.h similarity index 61% rename from include/games/chain-reaction/driver.h rename to include/games/chain-reaction/game-main.h index 82436ed..53bd14c 100644 --- a/include/games/chain-reaction/driver.h +++ b/include/games/chain-reaction/game-main.h @@ -1,17 +1,17 @@ #pragma once -#include "engine/layer.h" +#include "engine/application-runtime.h" #include "engine/timer.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "games/chain-reaction/ion-cannon.h" namespace Games::ChainReaction { - class Driver : public Engine::Layer, private Engine::Timer + class GameMain : public Engine::ApplicationRuntime, private Engine::Timer { public: - Driver(SystemCore::ContextManager *ctx); - ~Driver() { state.reset(); } + GameMain(SystemCore::ContextManager *ctx); + ~GameMain() { state.reset(); } void nextEvent() override; void reset(); void renderIons(); diff --git a/include/games/chain-reaction/ion-cannon.h b/include/games/chain-reaction/ion-cannon.h index 8aad5ff..8b30586 100644 --- a/include/games/chain-reaction/ion-cannon.h +++ b/include/games/chain-reaction/ion-cannon.h @@ -1,7 +1,7 @@ #pragma once #include "games/chain-reaction/ion.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" namespace Games::ChainReaction { diff --git a/include/games/chain-reaction/player.h b/include/games/chain-reaction/player.h index aa96df3..685f8bd 100644 --- a/include/games/chain-reaction/player.h +++ b/include/games/chain-reaction/player.h @@ -1,6 +1,6 @@ #pragma once -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "player/player.h" namespace Games::ChainReaction diff --git a/include/games/demo/driver.h b/include/games/demo/driver.h deleted file mode 100644 index b71f0a3..0000000 --- a/include/games/demo/driver.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "core/context-manager.h" -#include "engine/layer.h" -#include "games/demo/player.h" - -namespace Games::Demo -{ - class Driver : public Engine::Layer - { - public: - Driver(SystemCore::ContextManager *ctx) : Engine::Layer{}, contextManager{ctx}, player1{Player{ctx, width}}, player2{Player{ctx, width}} - { - contextManager->stateManager.getDemoGameState().reset(); - } - - void nextEvent() override; - uint16_t getCurrentScore() { return contextManager->stateManager.getDemoGameState().currentScore; } - uint16_t getHighScore() { return contextManager->stateManager.getDemoGameState().highScore; } - void incrementCurrentScore(); - void incrementHighScore(); - - private: - SystemCore::ContextManager *contextManager; - Player player1; - Player player2; - static constexpr float speed = 4.0f; - static constexpr uint16_t width = 7; - }; -} \ No newline at end of file diff --git a/include/games/demo/game-main.h b/include/games/demo/game-main.h new file mode 100644 index 0000000..4c0fd5f --- /dev/null +++ b/include/games/demo/game-main.h @@ -0,0 +1,36 @@ +#pragma once + +#include "platform/context-manager.h" +#include "engine/application-runtime.h" +#include "games/demo/player.h" + +namespace Games::Demo +{ + class GameMain : public Engine::ApplicationRuntime + { + public: + GameMain(SystemCore::ContextManager *ctx) : Engine::ApplicationRuntime{}, + state{ctx->stateManager.getDemoGameState()}, + contextManager{ctx}, + player1{Player{ctx, ColorPalette::Red}}, + player2{Player{ctx, ColorPalette::Blue}} + { + state.reset(); + } + + void nextEvent() override; + void incrementCurrentScore(); + void incrementHighScore(); + + private: + SystemCore::ContextManager *contextManager; + GameState &state; + Player player1; + Player player2; + static constexpr float speed = 4.0f; + + void readInput(); + void updatePosition(); + void render(); + }; +} \ No newline at end of file diff --git a/include/games/demo/player.h b/include/games/demo/player.h index cd1b79f..de47024 100644 --- a/include/games/demo/player.h +++ b/include/games/demo/player.h @@ -1,15 +1,72 @@ #pragma once -#include "core/context-manager.h" +#include "platform/context-manager.h" +#include "engine/render-model.h" #include "player/player.h" +#include "lights/pixels.h" +#include namespace Games::Demo { - class Player : public ::Player::BasePlayer + + enum class ColorPalette + { + Red, + Blue + }; + + class Player : public ::Player::BasePlayer, public Engine::RenderModel { public: - Player(SystemCore::ContextManager *ctx, const uint16_t width) : ::Player::BasePlayer{ctx, width} {}; - void updatePlayer1LedBuffer(); - void updatePlayer2LedBuffer(); + Player(SystemCore::ContextManager *ctx, ColorPalette profile) : ::Player::BasePlayer{ctx, width} + { + setColorPalette(profile); + }; + void setColorPalette(ColorPalette profile) + { + if (profile == ColorPalette::Red) + { + pixels = { + {0xff4d4d, 0x40}, + {0xff4d4d, 0x80}, + {0xff4d4d, 0xa0}, + {0xff4d4d, 0xe8}, + {0xff8888, 0xe8}, + {0xfff0f0, 0xff}, + {0xff8888, 0xe8}, + {0xff4d4d, 0xe8}, + {0xff4d4d, 0xa0}, + {0xff4d4d, 0x80}, + {0xff4d4d, 0x40}}; + layer = Engine::Layer::GameLayer_2; + positionPrecise = 50.0f; + } + else + { + pixels = { + {0x4da6ff, 0x40}, + {0x4da6ff, 0x80}, + {0x4da6ff, 0xa0}, + {0x4da6ff, 0xe8}, + {0x8ec8ff, 0xe8}, + {0xf0f0ff, 0xff}, + {0x8ec8ff, 0xe8}, + {0x4da6ff, 0xe8}, + {0x4da6ff, 0xa0}, + {0x4da6ff, 0x80}, + {0x4da6ff, 0x40}}; + layer = Engine::Layer::GameLayer_1; + } + } + + Engine::Layer layer; + + CustomDataStructures::Span getRendering() const override { return pixels.getRendering(); } + float getPositionPrecise() const override { return ::Player::BasePlayer::positionPrecise; } + Engine::Layer getLayer() const override { return layer; } + + private: + static constexpr uint16_t width = 11; + Lights::Pixels pixels; }; } \ No newline at end of file diff --git a/include/games/demo/state.h b/include/games/demo/state.h index 4c87d8d..b07bbcd 100644 --- a/include/games/demo/state.h +++ b/include/games/demo/state.h @@ -4,11 +4,18 @@ namespace Games::Demo { + enum class Actions + { + Active + }; + class GameState { public: GameState() : highScore{0}, currentScore{0} {} void reset() { highScore = currentScore = 0; } + + Actions current = Actions::Active; uint16_t highScore; uint16_t currentScore; }; diff --git a/include/games/phase-evasion/flare-manager.h b/include/games/phase-evasion/flare-manager.h index 4320333..7f44138 100644 --- a/include/games/phase-evasion/flare-manager.h +++ b/include/games/phase-evasion/flare-manager.h @@ -1,6 +1,6 @@ #pragma once -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "games/phase-evasion/flare.h" namespace Games::PhaseEvasion diff --git a/include/games/phase-evasion/flare.h b/include/games/phase-evasion/flare.h index 453c7e1..dfaf86a 100644 --- a/include/games/phase-evasion/flare.h +++ b/include/games/phase-evasion/flare.h @@ -5,7 +5,7 @@ #include "common.h" #include "engine/timer.h" -#include "core/configuration.h" +#include "platform/configuration.h" #include "lights/color.h" namespace Games::PhaseEvasion diff --git a/include/games/phase-evasion/driver.h b/include/games/phase-evasion/game-main.h similarity index 82% rename from include/games/phase-evasion/driver.h rename to include/games/phase-evasion/game-main.h index 4643945..ca06f63 100644 --- a/include/games/phase-evasion/driver.h +++ b/include/games/phase-evasion/game-main.h @@ -1,8 +1,8 @@ #pragma once -#include "engine/layer.h" +#include "engine/application-runtime.h" #include "engine/timer.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "games/phase-evasion/player.h" #include "games/phase-evasion/flare.h" #include "games/phase-evasion/flare-manager.h" @@ -10,11 +10,11 @@ namespace Games::PhaseEvasion { - class Driver : public Engine::Layer, private Engine::Timer + class GameMain : public Engine::ApplicationRuntime, private Engine::Timer { public: - Driver(SystemCore::ContextManager *ctx); - ~Driver() { state.reset(); } + GameMain(SystemCore::ContextManager *ctx); + ~GameMain() { state.reset(); } void nextEvent() override; static constexpr uint16_t playerOffset = 25; static constexpr uint16_t playerWidth = 5; diff --git a/include/games/phase-evasion/gem.h b/include/games/phase-evasion/gem.h index 96359e4..846554e 100644 --- a/include/games/phase-evasion/gem.h +++ b/include/games/phase-evasion/gem.h @@ -2,7 +2,7 @@ #include -#include "core/configuration.h" +#include "platform/configuration.h" namespace Games::PhaseEvasion { diff --git a/include/games/phase-evasion/player.h b/include/games/phase-evasion/player.h index 5551044..de55f74 100644 --- a/include/games/phase-evasion/player.h +++ b/include/games/phase-evasion/player.h @@ -1,6 +1,6 @@ #pragma once -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "player/player.h" namespace Games::PhaseEvasion @@ -11,6 +11,7 @@ namespace Games::PhaseEvasion Player(SystemCore::ContextManager *ctx, uint16_t width) : ::Player::BasePlayer{ctx, width} {}; void checkColorChangeRequest(); Lights::Color getColor() { return currentColor; } + float getPositionPrecise() const override { return ::Player::BasePlayer::positionPrecise; } private: Lights::Color currentColor; diff --git a/include/games/recall/driver.h b/include/games/recall/game-main.h similarity index 85% rename from include/games/recall/driver.h rename to include/games/recall/game-main.h index 89206af..af8b976 100644 --- a/include/games/recall/driver.h +++ b/include/games/recall/game-main.h @@ -1,7 +1,7 @@ #pragma once #include -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "engine/timer.h" #include "lights/color.h" #include "lights/color-code.h" @@ -9,11 +9,11 @@ namespace Games::Recall { - class Driver : public Engine::Layer, private Engine::Timer + class GameMain : public Engine::ApplicationRuntime, private Engine::Timer { public: - Driver(SystemCore::ContextManager *ctx); - ~Driver() { state.reset(); } + GameMain(SystemCore::ContextManager *ctx); + ~GameMain() { state.reset(); } void nextEvent() override; private: diff --git a/include/games/reflex/driver.h b/include/games/reflex/game-main.h similarity index 62% rename from include/games/reflex/driver.h rename to include/games/reflex/game-main.h index d2c560e..2aae057 100644 --- a/include/games/reflex/driver.h +++ b/include/games/reflex/game-main.h @@ -1,17 +1,17 @@ #pragma once -#include "engine/layer.h" +#include "engine/application-runtime.h" #include "engine/timer.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "games/reflex/signal.h" namespace Games::Reflex { - class Driver : public Engine::Layer, private Engine::Timer + class GameMain : public Engine::ApplicationRuntime, private Engine::Timer { public: - Driver(SystemCore::ContextManager *ctx); - ~Driver() { state.reset(); } + GameMain(SystemCore::ContextManager *ctx); + ~GameMain() { state.reset(); } static constexpr uint16_t signalWidth = 7; static constexpr float signalSpeed = 1.0f; void nextEvent() override; diff --git a/include/games/reflex/signal.h b/include/games/reflex/signal.h index 4f23312..8fa0f79 100644 --- a/include/games/reflex/signal.h +++ b/include/games/reflex/signal.h @@ -1,6 +1,6 @@ #pragma once -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "player/player.h" namespace Games::Reflex @@ -12,6 +12,7 @@ namespace Games::Reflex ~Signal() = default; void advance() { setPosition(positionPrecise += speed); } + float getPositionPrecise() const override { return ::Player::BasePlayer::positionPrecise; } private: float speed; diff --git a/include/lights/led-buffer.h b/include/lights/led-buffer.h index ec7b2ef..e305979 100644 --- a/include/lights/led-buffer.h +++ b/include/lights/led-buffer.h @@ -2,7 +2,7 @@ #include -#include "core/configuration.h" +#include "platform/configuration.h" #include "lights/color.h" namespace Lights @@ -10,7 +10,7 @@ namespace Lights class LedBuffer { public: - LedBuffer() : leds{new Color[maxLEDs]} {} + LedBuffer() : leds{new Color[SystemCore::Configuration::maxLeds]} {} ~LedBuffer() { delete[] leds; } LedBuffer(LedBuffer &&other) = delete; LedBuffer &operator=(LedBuffer &&other) = delete; @@ -27,6 +27,5 @@ namespace Lights private: Color *leds; - static constexpr uint16_t maxLEDs = 600; }; } \ No newline at end of file diff --git a/include/lights/led-luminance.h b/include/lights/led-luminance.h index 6bb7221..57cdef7 100644 --- a/include/lights/led-luminance.h +++ b/include/lights/led-luminance.h @@ -1,6 +1,6 @@ #pragma once -#include "core/configuration.h" +#include "platform/configuration.h" namespace Lights { diff --git a/include/lights/led-strip.h b/include/lights/led-strip.h index 4289f18..16fc1ce 100644 --- a/include/lights/led-strip.h +++ b/include/lights/led-strip.h @@ -1,9 +1,10 @@ #pragma once -#include "core/configuration.h" +#include "platform/configuration.h" #include "lights/color.h" #include "lights/led-buffer.h" #include "lights/led-luminance.h" +#include "engine/layer.h" namespace Lights { @@ -11,13 +12,19 @@ namespace Lights { public: LedStrip(); + LedBuffer buffer; - Color *getRawColors(); + Color &operator[](uint16_t index) { return buffer[index]; } + const Color &operator[](uint16_t index) const { return buffer[index]; } + + Color *getRawColors(); static const uint16_t size() { return SystemCore::Configuration::numLeds(); } void reset(); void adjustLuminance(); + Engine::Layer layer[SystemCore::Configuration::maxLeds]; + private: LedLuminance luminance; }; diff --git a/include/lights/pixel.h b/include/lights/pixel.h new file mode 100644 index 0000000..c277e30 --- /dev/null +++ b/include/lights/pixel.h @@ -0,0 +1,13 @@ +#pragma once + +#include "lights/color.h" +#include "engine/layer.h" + +namespace Lights +{ + struct Pixel + { + Color color; + uint8_t alpha = 0xff; + }; +} \ No newline at end of file diff --git a/include/lights/pixels.h b/include/lights/pixels.h new file mode 100644 index 0000000..53d3a15 --- /dev/null +++ b/include/lights/pixels.h @@ -0,0 +1,42 @@ +#pragma once + +#include "lights/color.h" +#include "lights/pixel.h" +#include + +namespace Lights +{ + template + class Pixels + { + public: + Pixels() = default; + Pixels(std::initializer_list list) + { + uint16_t index = 0; + for (const auto &pixel : list) + { + if (index >= N) + break; + pixels[index++] = pixel; + } + } + + CustomDataStructures::Span getRendering() const { return CustomDataStructures::Span(pixels.data(), N); } + + Pixel &operator[](uint16_t index) { return pixels[index]; } + const Pixel &operator[](uint16_t index) const { return pixels[index]; } + + auto begin() { return pixels.begin(); } + auto end() { return pixels.end(); } + + auto begin() const { return pixels.begin(); } + auto end() const { return pixels.end(); } + + auto cbegin() const { return pixels.cbegin(); } + auto cend() const { return pixels.cend(); } + + private: + std::array pixels; + }; +} \ No newline at end of file diff --git a/include/core/configuration.h b/include/platform/configuration.h similarity index 84% rename from include/core/configuration.h rename to include/platform/configuration.h index e6e6718..0138730 100644 --- a/include/core/configuration.h +++ b/include/platform/configuration.h @@ -16,6 +16,8 @@ namespace SystemCore static const std::array &recallBoundaries() { return _recallBoundaries; } static constexpr uint8_t ledDimmerGpio = 34; // set from PCB design, should not be configurable + static constexpr uint16_t maxLeds = 600; // Set limit, large values may + // severely impact refresh rate static void load(::Preferences &memory); diff --git a/include/core/context-manager.h b/include/platform/context-manager.h similarity index 82% rename from include/core/context-manager.h rename to include/platform/context-manager.h index 7315315..469851c 100644 --- a/include/core/context-manager.h +++ b/include/platform/context-manager.h @@ -2,14 +2,14 @@ #include -#include "engine/layer.h" +#include "engine/renderer.h" +#include "engine/application-runtime.h" #include "engine/state-manager.h" #ifdef USE_PS3 #include "player/ps3-controller.h" #else #include "player/ps4-controller.h" #endif -#include "lights/led-strip.h" #include "display/display.h" #include "display/menu-navigation.h" @@ -23,16 +23,16 @@ namespace SystemCore ContextManager(ContextManager &&other) = delete; ContextManager(const ContextManager &other) = delete; - Engine::Layer *application = nullptr; + Engine::ApplicationRuntime *application = nullptr; Engine::StateManager stateManager; + Engine::Renderer renderer; #ifdef USE_PS3 Player::Ps3Controller controller; #else Player::Ps4Controller controller; #endif - Lights::LedStrip leds; - Display::OledDisplay display; Preferences memory; + Display::OledDisplay display; Display::MenuTileNavigation menuNav; void initializeSystemMemory(); @@ -41,6 +41,6 @@ namespace SystemCore void navigateSceneMenu(); void checkExitRequest(); void checkDisplayVisibilityChange(); - void transitionLayer(); + void changeApplication(); }; } \ No newline at end of file diff --git a/include/player/player.h b/include/player/player.h index 35bf3b6..f5f6d47 100644 --- a/include/player/player.h +++ b/include/player/player.h @@ -1,6 +1,6 @@ #pragma once -#include "core/context-manager.h" +#include "platform/context-manager.h" namespace Player { @@ -14,6 +14,7 @@ namespace Player void move(const int distance, const float speed, const bool shouldWrap = true); uint16_t getPosition() { return static_cast(positionPrecise); } + virtual float getPositionPrecise() const = 0; void setPosition(float position) { positionPrecise = position; } void setPosition(uint16_t position) { positionPrecise = static_cast(position); } diff --git a/include/player/rumble.h b/include/player/rumble.h index 0eb11b8..f0066cd 100644 --- a/include/player/rumble.h +++ b/include/player/rumble.h @@ -34,19 +34,19 @@ namespace Player }; static const RumbleStep singleQuickSteps[] = { - {200, 100}}; + {255, 75}}; static const RumbleStep doubleQuickSteps[] = { - {255, 100}, - {0, 100}, - {200, 100}}; + {180, 60}, + {0, 45}, + {255, 75}}; static const RumbleStep deathSteps[] = { + {255, 75}, + {0, 75}, {255, 100}, - {0, 100}, - {255, 400}, - {0, 100}, - {255, 100}, - {0, 100}, - {255, 400}}; + {0, 75}, + {255, 85}, + {0, 75}, + {255, 100}}; static const RumblePattern singleQuickRumbleSeq{singleQuickSteps}; static const RumblePattern doubleQuickRumbleSeq{doubleQuickSteps}; static const RumblePattern deathRumbleSeq{deathSteps}; diff --git a/include/scenes/canvas/driver.h b/include/scenes/canvas/driver.h index a3e2fa1..6a6827c 100644 --- a/include/scenes/canvas/driver.h +++ b/include/scenes/canvas/driver.h @@ -1,12 +1,12 @@ #pragma once -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "engine/timer.h" #include "lights/color-hsl.h" namespace Scenes::Canvas { - class Driver : public Engine::Layer, private Engine::Timer + class Driver : public Engine::ApplicationRuntime, private Engine::Timer { public: Driver(SystemCore::ContextManager *ctx); diff --git a/lib/custom-data-structures/span.h b/lib/custom-data-structures/span.h new file mode 100644 index 0000000..73c9932 --- /dev/null +++ b/lib/custom-data-structures/span.h @@ -0,0 +1,22 @@ +#pragma once + +namespace CustomDataStructures +{ + template + class Span + { + public: + constexpr Span(const T *data, uint16_t size) : data_(data), size_(size) {} + + constexpr const T &operator[](uint16_t i) const { return data_[i]; } + + constexpr const T *begin() const { return data_; } + constexpr const T *end() const { return data_ + size_; } + + constexpr uint16_t size() const { return size_; } + + private: + const T *data_; + uint16_t size_; + }; +} diff --git a/src/display/display.cpp b/src/display/display.cpp index 4ca3ef7..442eef0 100644 --- a/src/display/display.cpp +++ b/src/display/display.cpp @@ -2,7 +2,7 @@ #include #include "display/display.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" namespace Display { diff --git a/src/display/menu-navigation.cpp b/src/display/menu-navigation.cpp index 1188f4c..e602877 100644 --- a/src/display/menu-navigation.cpp +++ b/src/display/menu-navigation.cpp @@ -1,6 +1,6 @@ #include "display/menu-navigation.h" #include "engine/state-manager.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" namespace Display { @@ -23,9 +23,9 @@ namespace Display float blend = std::exp(-(x * x) / (4 * sigma * sigma)); // computed gaussian curve if (modeIdx == static_cast(modeSelected)) - contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale * blend; + contextManager->renderer.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeGreen} * inactiveSelectionDimmingScale * blend; else - contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; + contextManager->renderer.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; displayIndex--; } @@ -52,11 +52,11 @@ namespace Display float blend = std::exp(-(x * x) / (4 * sigma * sigma)); if (currentState == Engine::SystemState::MenuGames && modeIdx == gameSelected) - contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue} * blend; + contextManager->renderer.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeBlue} * blend; else if (currentState == Engine::SystemState::MenuScenes && modeIdx == sceneSelected) - contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow} * blend; + contextManager->renderer.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::ThemeYellow} * blend; else - contextManager->leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; + contextManager->renderer.leds.buffer[displayIndex] = Lights::Color{Lights::ColorCode::MenuUnselected} * inactiveSelectionDimmingScale * blend; displayIndex++; } diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a9198a6..778d61c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -26,7 +26,7 @@ namespace Engine { while (contextManager.stateManager.isRunning()) { - contextManager.leds.reset(); + contextManager.renderer.leds.reset(); contextManager.controller.poll(); contextManager.checkExitRequest(); contextManager.checkDisplayVisibilityChange(); @@ -75,7 +75,7 @@ namespace Engine // If debugging, ensure serial connection is stable before setting up components #if defined(VIRTUALIZATION) || defined(DEBUG) Serial.begin(SystemCore::Configuration::serialBaud()); - contextManager.leds.reset(); + contextManager.renderer.leds.reset(); renderLedStrip(); log("Connecting to computer using a serial connection for debugging."); while (!Serial) @@ -154,7 +154,7 @@ namespace Engine for (uint16_t i = 0; i < SystemCore::Configuration::numLeds(); ++i) { float phase = std::cos((2 * M_PI * i / SystemCore::Configuration::numLeds()) + (2 * M_PI * disconnectedLedPhaseShift / SystemCore::Configuration::numLeds())) * 127 + 128; - contextManager.leds.buffer[i] = {static_cast(std::floor(phase)), 0, 0}; + contextManager.renderer.leds.buffer[i] = {static_cast(std::floor(phase)), 0, 0}; } disconnectedLedPhaseShift += 0.5; @@ -164,14 +164,14 @@ namespace Engine void GameEngine::renderLedStrip() { - contextManager.leds.adjustLuminance(); + contextManager.renderer.leds.adjustLuminance(); #ifdef RELEASE FastLED.show(); #endif #ifdef VIRTUALIZATION Serial.write(0xAA); // sync bytes Serial.write(0x55); - Serial.write(reinterpret_cast(contextManager.leds.getRawColors()), SystemCore::Configuration::numLeds() * sizeof(Lights::Color)); + Serial.write(reinterpret_cast(contextManager.renderer.leds.getRawColors()), SystemCore::Configuration::numLeds() * sizeof(Lights::Color)); #endif } diff --git a/src/engine/renderer.cpp b/src/engine/renderer.cpp new file mode 100644 index 0000000..a068b8c --- /dev/null +++ b/src/engine/renderer.cpp @@ -0,0 +1,50 @@ +#include +#include "engine/renderer.h" +#include "logger.h" + +namespace Engine +{ + void Renderer::drawPixel(const RenderModel &renderModel, const uint16_t normalizedPosition, uint16_t offset) + { + if (renderModel.getLayer() < leds.layer[normalizedPosition + offset]) + return; + + const Lights::Pixel pixel = renderModel.getRendering()[offset]; + + if (pixel.alpha == 0) + return; + + if (pixel.alpha == 255) + { + leds.buffer[normalizedPosition + offset] = pixel.color; + leds.layer[normalizedPosition + offset] = renderModel.getLayer(); + return; + } + + blend(renderModel, normalizedPosition, offset); + } + + void Renderer::renderEntity(const RenderModel &renderModel) + { + CustomDataStructures::Span model = renderModel.getRendering(); + for (uint16_t offset = 0; offset < model.size(); ++offset) + { + const uint16_t roundedPosition = static_cast(renderModel.getPositionPrecise()); + const uint16_t normalizedPosition = std::clamp(roundedPosition, static_cast(0), static_cast(SystemCore::Configuration::numLeds() - 1)); + drawPixel(renderModel, normalizedPosition, offset); + } + } + + void Renderer::blend(const RenderModel &renderModel, const uint16_t normalizedPosition, uint16_t offset) + { + Lights::Color &destLed = leds.buffer[normalizedPosition + offset]; + const auto &pixel = renderModel.getRendering()[offset]; + + const uint16_t alpha = pixel.alpha; + const uint16_t inverseAlpha = 255 - alpha; + + destLed.r = (destLed.r * inverseAlpha + pixel.color.r * alpha) / 255; + destLed.g = (destLed.g * inverseAlpha + pixel.color.g * alpha) / 255; + destLed.b = (destLed.b * inverseAlpha + pixel.color.b * alpha) / 255; + } +} \ No newline at end of file diff --git a/src/engine/state-manager.cpp b/src/engine/state-manager.cpp index 8ebff64..e151d63 100644 --- a/src/engine/state-manager.cpp +++ b/src/engine/state-manager.cpp @@ -1,5 +1,5 @@ #include "engine/state-manager.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" namespace Engine { diff --git a/src/games/chain-reaction/driver.cpp b/src/games/chain-reaction/game-main.cpp similarity index 73% rename from src/games/chain-reaction/driver.cpp rename to src/games/chain-reaction/game-main.cpp index e57504c..7eb8cb6 100644 --- a/src/games/chain-reaction/driver.cpp +++ b/src/games/chain-reaction/game-main.cpp @@ -1,17 +1,17 @@ -#include "games/chain-reaction/driver.h" +#include "games/chain-reaction/game-main.h" #include "logger.h" namespace Games::ChainReaction { - Driver::Driver(SystemCore::ContextManager *ctx) : contextManager{ctx}, - state{ctx->stateManager.getChainReactionGameState()}, - cannon{ctx} + GameMain::GameMain(SystemCore::ContextManager *ctx) : contextManager{ctx}, + state{ctx->stateManager.getChainReactionGameState()}, + cannon{ctx} { reset(); wait(500); } - void Driver::nextEvent() + void GameMain::nextEvent() { switch (state.current) { @@ -35,7 +35,7 @@ namespace Games::ChainReaction } } - void Driver::advanceActiveIon() + void GameMain::advanceActiveIon() { for (auto &ion : cannon) { @@ -46,7 +46,7 @@ namespace Games::ChainReaction } } - void Driver::renderIons() + void GameMain::renderIons() { logf("# of Ions: %u", cannon.size()); int ct = 1; @@ -61,7 +61,7 @@ namespace Games::ChainReaction for (uint16_t j = segmentHead; j < segmentTail; ++j) { - contextManager->leds.buffer[j] = ion.getColors().at(i); + contextManager->renderer.leds.buffer[j] = ion.getColors().at(i); } logf("Ion %u - Color: %u", ct, ion.getColors().at(i)); } @@ -69,14 +69,14 @@ namespace Games::ChainReaction } } - void Driver::reset() + void GameMain::reset() { state.reset(); state.current = Actions::Startup; cannon.reset(); } - void Driver::handleDispatch() + void GameMain::handleDispatch() { cannon.dispatch(); state.current = Actions::ActiveDrop; diff --git a/src/games/chain-reaction/ion.cpp b/src/games/chain-reaction/ion.cpp index 6bdbcda..784cee3 100644 --- a/src/games/chain-reaction/ion.cpp +++ b/src/games/chain-reaction/ion.cpp @@ -1,6 +1,6 @@ #include "games/chain-reaction/ion.h" #include "lights/color.h" -#include "core/configuration.h" +#include "platform/configuration.h" #include "common.h" namespace Games::ChainReaction diff --git a/src/games/demo/driver.cpp b/src/games/demo/game-main.cpp similarity index 50% rename from src/games/demo/driver.cpp rename to src/games/demo/game-main.cpp index 0832919..469904c 100644 --- a/src/games/demo/driver.cpp +++ b/src/games/demo/game-main.cpp @@ -1,35 +1,55 @@ -#include "games/demo/driver.h" +#include "games/demo/game-main.h" #include "player/controller.h" #include "logger.h" namespace Games::Demo { - void Driver::nextEvent() + void GameMain::nextEvent() + { + switch (state.current) + { + case Actions::Active: + readInput(); + updatePosition(); + render(); + break; + } + } + + void GameMain::readInput() { if (contextManager->controller.wasPressed(::Player::ControllerButton::Cross)) { incrementCurrentScore(); - contextManager->stateManager.displayShouldUpdate = true; contextManager->controller.rumble(::Player::RumbleOptions::SingleQuickPulse); - logf("Current score: %d", getCurrentScore()); + logf("Current score: %d", state.currentScore); } + } + + void GameMain::updatePosition() + { auto leftInput = contextManager->controller.leftAnalog(); auto rightInput = contextManager->controller.rightAnalog(); player1.move(leftInput.x, speed); - player1.updatePlayer1LedBuffer(); player2.move(rightInput.x, speed); - player2.updatePlayer2LedBuffer(); } - void Driver::incrementCurrentScore() + void GameMain::render() + { + Engine::Renderer &renderer = contextManager->renderer; + renderer.renderEntity(player1); + renderer.renderEntity(player2); + } + + void GameMain::incrementCurrentScore() { - ++contextManager->stateManager.getDemoGameState().currentScore; + ++state.currentScore; contextManager->stateManager.displayShouldUpdate = true; } - void Driver::incrementHighScore() + void GameMain::incrementHighScore() { - ++contextManager->stateManager.getDemoGameState().highScore; + ++state.highScore; contextManager->stateManager.displayShouldUpdate = true; } } \ No newline at end of file diff --git a/src/games/demo/player.cpp b/src/games/demo/player.cpp deleted file mode 100644 index 72894b0..0000000 --- a/src/games/demo/player.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -#include "games/demo/player.h" - -namespace Games::Demo -{ - void Player::updatePlayer1LedBuffer() - { - float center = (width + 1) / 2.0f; - for (uint16_t i = 0; i <= width; ++i) - { - uint16_t index = (getPosition() + i) % SystemCore::Configuration::numLeds(); - float intensity = 1.0f - std::abs(i - center) / center; - if (intensity < 0) - intensity = 0; - - contextManager->leds.buffer[index].r = static_cast(245.0f * intensity); - contextManager->leds.buffer[index].g = static_cast(215.0f * intensity); - contextManager->leds.buffer[index].b = static_cast(128.0f * intensity); - } - } - - void Player::updatePlayer2LedBuffer() - { - - float center = (width + 1) / 2.0f; - for (uint16_t i = 0; i <= width; ++i) - { - uint16_t index = (getPosition() + i) % SystemCore::Configuration::numLeds(); - float intensity = 1.0f - std::abs(i - center) / center; - if (intensity < 0) - intensity = 0; - - contextManager->leds.buffer[index].r = static_cast(103.0f * intensity); - contextManager->leds.buffer[index].g = static_cast(162.0f * intensity); - contextManager->leds.buffer[index].b = static_cast(235.0f * intensity); - } - } -} \ No newline at end of file diff --git a/src/games/phase-evasion/driver.cpp b/src/games/phase-evasion/game-main.cpp similarity index 82% rename from src/games/phase-evasion/driver.cpp rename to src/games/phase-evasion/game-main.cpp index 96f604b..d5eda7e 100644 --- a/src/games/phase-evasion/driver.cpp +++ b/src/games/phase-evasion/game-main.cpp @@ -1,14 +1,14 @@ #include -#include "games/phase-evasion/driver.h" +#include "games/phase-evasion/game-main.h" #include "player/controller.h" #include "logger.h" namespace Games::PhaseEvasion { - Driver::Driver(SystemCore::ContextManager *ctx) : contextManager{ctx}, - state{contextManager->stateManager.getPhaseEvasionGameState()}, - player{ctx, playerWidth}, - flareManager{ctx} + GameMain::GameMain(SystemCore::ContextManager *ctx) : contextManager{ctx}, + state{contextManager->stateManager.getPhaseEvasionGameState()}, + player{ctx, playerWidth}, + flareManager{ctx} { state.reset(); state.current = Actions::Startup; @@ -18,7 +18,7 @@ namespace Games::PhaseEvasion windDownTimer.wait(windDownLength); } - void Driver::nextEvent() + void GameMain::nextEvent() { switch (state.current) { @@ -53,7 +53,7 @@ namespace Games::PhaseEvasion } } - void Driver::getUpdates() + void GameMain::getUpdates() { player.checkColorChangeRequest(); flareManager.updatePositions(); @@ -61,16 +61,16 @@ namespace Games::PhaseEvasion player.move(leftAnalog.x, 1.25, false); } - void Driver::renderPlayer() + void GameMain::renderPlayer() { for (uint16_t i = 0; i < player.width; ++i) { uint16_t index = (player.getPosition() + i) % SystemCore::Configuration::numLeds(); - contextManager->leds.buffer[index] = player.getColor(); + contextManager->renderer.leds.buffer[index] = player.getColor(); } } - void Driver::renderFlare() + void GameMain::renderFlare() { for (const auto &flare : flareManager) { @@ -91,12 +91,12 @@ namespace Games::PhaseEvasion uint16_t distance = i - flareHead; double attenuation = std::clamp(1.0 - 0.08 * static_cast(distance), 0.0, 1.0); - contextManager->leds.buffer[i] = flare.getColor() * attenuation; + contextManager->renderer.leds.buffer[i] = flare.getColor() * attenuation; } } } - void Driver::renderGem() + void GameMain::renderGem() { if (!gem.isActive() || !gem.isReady()) return; @@ -106,11 +106,11 @@ namespace Games::PhaseEvasion for (uint16_t i = gemLeft; i < gemRight; ++i) { - contextManager->leds.buffer[i] = gem.getColor(); + contextManager->renderer.leds.buffer[i] = gem.getColor(); } } - void Driver::checkCollision() + void GameMain::checkCollision() { for (auto &flare : flareManager) { @@ -135,7 +135,7 @@ namespace Games::PhaseEvasion } } - void Driver::checkGemCapture() + void GameMain::checkGemCapture() { if (!gem.isActive() && gem.isReady()) { @@ -171,7 +171,7 @@ namespace Games::PhaseEvasion } } - void Driver::assessDifficulty() + void GameMain::assessDifficulty() { if (isReady()) { @@ -199,17 +199,17 @@ namespace Games::PhaseEvasion } } - void Driver::checkIfHighScore() + void GameMain::checkIfHighScore() { GameState &state = contextManager->stateManager.getPhaseEvasionGameState(); state.checkHighScore(); } - void Driver::muzzleFlash() + void GameMain::muzzleFlash() { for (uint16_t i = 0; i < SystemCore::Configuration::numLeds(); ++i) { - contextManager->leds.buffer[i] = Lights::Color::White; + contextManager->renderer.leds.buffer[i] = Lights::Color::White; } if (isReady()) @@ -218,16 +218,16 @@ namespace Games::PhaseEvasion } } - void Driver::gameOver() + void GameMain::gameOver() { for (uint16_t i = 0; i < SystemCore::Configuration::numLeds(); ++i) { float offset = std::cos((2.0f * M_PI * i / SystemCore::Configuration::numLeds()) - (2.0f * M_PI * static_cast(gameOverPhaseShift) / SystemCore::Configuration::numLeds())); float phase = offset * 127.5 + 127.5; - contextManager->leds.buffer[i] = {static_cast(std::floor(phase) * 0.95), - static_cast(std::floor(phase) * 0.15), - static_cast(std::floor(phase) * 0.25)}; + contextManager->renderer.leds.buffer[i] = {static_cast(std::floor(phase) * 0.95), + static_cast(std::floor(phase) * 0.15), + static_cast(std::floor(phase) * 0.25)}; } gameOverPhaseShift += std::cos(gameOverPhaseOffset) / 16.0f; @@ -244,7 +244,7 @@ namespace Games::PhaseEvasion } } - void Driver::reset() + void GameMain::reset() { interval = 2500; gap = 1600; diff --git a/src/games/phase-evasion/state.cpp b/src/games/phase-evasion/state.cpp index 71ba490..1c31280 100644 --- a/src/games/phase-evasion/state.cpp +++ b/src/games/phase-evasion/state.cpp @@ -1,5 +1,5 @@ #include "games/phase-evasion/state.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "logger.h" namespace Games::PhaseEvasion diff --git a/src/games/recall/driver.cpp b/src/games/recall/game-main.cpp similarity index 85% rename from src/games/recall/driver.cpp rename to src/games/recall/game-main.cpp index 314b1ca..41b4b20 100644 --- a/src/games/recall/driver.cpp +++ b/src/games/recall/game-main.cpp @@ -1,13 +1,13 @@ #include "esp_system.h" #include "player/controller-properties.h" -#include "games/recall/driver.h" +#include "games/recall/game-main.h" #include "common.h" #include "logger.h" namespace Games::Recall { - Driver::Driver(SystemCore::ContextManager *ctx) : contextManager{ctx}, - state{contextManager->stateManager.getRecallGameState()} + GameMain::GameMain(SystemCore::ContextManager *ctx) : contextManager{ctx}, + state{contextManager->stateManager.getRecallGameState()} { setupGameColors(); state.reset(); @@ -16,14 +16,14 @@ namespace Games::Recall wait(gameplaySpeedIlluminated); } - void Driver::setupGameColors() + void GameMain::setupGameColors() { for (uint16_t i = 0; i < maxRound; ++i) { // for testing // gameplayColors[i] = static_cast(i % arraySize(Driver::availableGameplayButtons)); - uint16_t colorIndex = static_cast(esp_random()) % arraySize(Driver::availableGameplayButtons); + uint16_t colorIndex = static_cast(esp_random()) % arraySize(GameMain::availableGameplayButtons); gameplayColors[i] = static_cast(colorIndex); } auto button = gameplayColors[0]; @@ -32,7 +32,7 @@ namespace Games::Recall logf(" Color=%u (%u - %u - %u)", button, color.r, color.g, color.b); } - void Driver::nextEvent() + void GameMain::nextEvent() { handleUserSpeedChange(); switch (state.current) @@ -64,7 +64,7 @@ namespace Games::Recall } } - void Driver::handleUserSpeedChange() + void GameMain::handleUserSpeedChange() { if (contextManager->controller.wasPressed(Player::ControllerButton::Up) || contextManager->controller.leftAnalog().y < -64) { @@ -80,7 +80,7 @@ namespace Games::Recall } } - void Driver::displayComputerPlayback() + void GameMain::displayComputerPlayback() { if (isReady()) { @@ -104,11 +104,11 @@ namespace Games::Recall double x = static_cast(i); double scope = std::exp(-0.5 * std::pow((x - mu) / delta, 2.0)); auto color = Lights::colorPalette[static_cast(gameplayColors[sequenceIndex])]; - contextManager->leds.buffer[i] = color * scope; + contextManager->renderer.leds.buffer[i] = color * scope; } } - void Driver::pauseComputerPlayback() + void GameMain::pauseComputerPlayback() { if (sequenceIndex >= state.round) { @@ -129,7 +129,7 @@ namespace Games::Recall } } - void Driver::evaluateUserRecall() + void GameMain::evaluateUserRecall() { if (sequenceIndex > state.round && isReady()) { @@ -146,9 +146,9 @@ namespace Games::Recall evaluateUserButton(gameplayColors[sequenceIndex]); } - void Driver::evaluateUserButton(Player::ControllerButton expectedButton) + void GameMain::evaluateUserButton(Player::ControllerButton expectedButton) { - for (auto button : Driver::availableGameplayButtons) + for (auto button : GameMain::availableGameplayButtons) { if (contextManager->controller.wasPressedAndReleased(button)) { @@ -168,12 +168,12 @@ namespace Games::Recall } } - void Driver::illuminateOnSelection() + void GameMain::illuminateOnSelection() { static uint32_t lastLightTime = 0; static int pressedButtonIndex = -1; bool buttonPressed = false; - for (uint16_t i = 0; i < arraySize(Driver::availableGameplayButtons); ++i) + for (uint16_t i = 0; i < arraySize(GameMain::availableGameplayButtons); ++i) { auto btn = static_cast(i); if (contextManager->controller.isDown(btn)) @@ -188,16 +188,16 @@ namespace Games::Recall bool keepLit = ((millis() - lastLightTime) < gameplaySpeedPaused); if (buttonPressed || keepLit) { - if (pressedButtonIndex >= 0 && pressedButtonIndex < static_cast(arraySize(Driver::availableGameplayButtons))) + if (pressedButtonIndex >= 0 && pressedButtonIndex < static_cast(arraySize(GameMain::availableGameplayButtons))) { auto boundaries = directionBoundaries(static_cast(pressedButtonIndex)); for (uint16_t i = boundaries.first; i <= boundaries.second; ++i) - contextManager->leds.buffer[i] = Lights::colorPalette[pressedButtonIndex]; + contextManager->renderer.leds.buffer[i] = Lights::colorPalette[pressedButtonIndex]; } } } - std::pair Driver::directionBoundaries(Player::ControllerButton button) + std::pair GameMain::directionBoundaries(Player::ControllerButton button) { const auto &boundary = SystemCore::Configuration::recallBoundaries(); @@ -216,7 +216,7 @@ namespace Games::Recall } } - void Driver::prepareComputerPlayback() + void GameMain::prepareComputerPlayback() { if (isReady()) { @@ -237,12 +237,12 @@ namespace Games::Recall for (uint16_t i = boundaries.first; i <= boundaries.second; ++i) { auto &color = Lights::colorPalette[static_cast(gameplayColors[sequenceIndex - 1])]; - contextManager->leds.buffer[i] = color * successFadeawayAnimation; + contextManager->renderer.leds.buffer[i] = color * successFadeawayAnimation; } successFadeawayAnimation = std::clamp(successFadeawayAnimation - 0.08, 0.0, 1.0); } - void Driver::gameOver() + void GameMain::gameOver() { if (contextManager->controller.wasPressed(Player::ControllerButton::Start)) { @@ -257,7 +257,7 @@ namespace Games::Recall for (uint16_t i = 0; i < SystemCore::Configuration::numLeds(); ++i) { float phase = std::cos((2.0f * M_PI * i / SystemCore::Configuration::numLeds()) + (2.0f * M_PI * gameOverLedPhaseShift / SystemCore::Configuration::numLeds())) * 127 + 128; - contextManager->leds.buffer[i] = {static_cast(std::floor(phase)), 0, 0}; + contextManager->renderer.leds.buffer[i] = {static_cast(std::floor(phase)), 0, 0}; } gameOverLedPhaseShift += 0.5f; diff --git a/src/games/recall/state.cpp b/src/games/recall/state.cpp index 57f9177..ad79c6f 100644 --- a/src/games/recall/state.cpp +++ b/src/games/recall/state.cpp @@ -1,5 +1,5 @@ #include "games/recall/state.h" -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "logger.h" namespace Games::Recall diff --git a/src/games/reflex/driver.cpp b/src/games/reflex/game-main.cpp similarity index 63% rename from src/games/reflex/driver.cpp rename to src/games/reflex/game-main.cpp index 2f29e42..d7cc052 100644 --- a/src/games/reflex/driver.cpp +++ b/src/games/reflex/game-main.cpp @@ -1,17 +1,17 @@ -#include "games/reflex/driver.h" +#include "games/reflex/game-main.h" #include "games/reflex/signal.h" #include "logger.h" namespace Games::Reflex { - Driver::Driver(SystemCore::ContextManager *ctx) : contextManager{ctx}, - state{ctx->stateManager.getReflexGameState()}, - signal{ctx, signalWidth, signalSpeed} + GameMain::GameMain(SystemCore::ContextManager *ctx) : contextManager{ctx}, + state{ctx->stateManager.getReflexGameState()}, + signal{ctx, signalWidth, signalSpeed} { reset(); } - void Driver::nextEvent() + void GameMain::nextEvent() { switch (state.current) { @@ -40,17 +40,17 @@ namespace Games::Reflex } } - void Driver::reset() + void GameMain::reset() { contextManager->stateManager.displayShouldUpdate = true; } - void Driver::renderSignal() + void GameMain::renderSignal() { for (uint16_t i = 0; i < signal.width; ++i) { uint16_t index = (signal.getPosition() + i) % SystemCore::Configuration::numLeds(); - contextManager->leds.buffer[index] = Lights::ColorCode::MenuUnselected; + contextManager->renderer.leds.buffer[index] = Lights::ColorCode::MenuUnselected; } } } \ No newline at end of file diff --git a/src/lights/led-strip.cpp b/src/lights/led-strip.cpp index 0aff085..7d1f46f 100644 --- a/src/lights/led-strip.cpp +++ b/src/lights/led-strip.cpp @@ -6,6 +6,7 @@ namespace Lights { #ifdef RELEASE FastLED.addLeds(static_cast(buffer), size()); + // TODO: Test if this now works on bare metal with buffer istead of static_casting it to Color *. // FastLED.setCorrection(0xFFB0F0); // FastLED.setTemperature(0xFFE0A0); reset(); @@ -21,7 +22,8 @@ namespace Lights { for (uint16_t i = 0; i < size(); i++) { - buffer[i] = 0; + buffer[i] = Color{}; + layer[i] = Engine::Layer::Background; } } diff --git a/src/core/configuration.cpp b/src/platform/configuration.cpp similarity index 96% rename from src/core/configuration.cpp rename to src/platform/configuration.cpp index 8cd15d9..ae0fb98 100644 --- a/src/core/configuration.cpp +++ b/src/platform/configuration.cpp @@ -1,4 +1,4 @@ -#include "core/configuration.h" +#include "platform/configuration.h" #include #ifndef LUMENLAB_VERSION diff --git a/src/core/context-manager.cpp b/src/platform/context-manager.cpp similarity index 89% rename from src/core/context-manager.cpp rename to src/platform/context-manager.cpp index 8c57826..bc1d744 100644 --- a/src/core/context-manager.cpp +++ b/src/platform/context-manager.cpp @@ -1,10 +1,10 @@ -#include "core/context-manager.h" +#include "platform/context-manager.h" #include "player/controller-properties.h" -#include "games/recall/driver.h" -#include "games/phase-evasion/driver.h" -#include "games/chain-reaction/driver.h" -#include "games/reflex/driver.h" -#include "games/demo/driver.h" +#include "games/recall/game-main.h" +#include "games/phase-evasion/game-main.h" +#include "games/chain-reaction/game-main.h" +#include "games/reflex/game-main.h" +#include "games/demo/game-main.h" #include "scenes/canvas/driver.h" #include "logger.h" @@ -34,6 +34,7 @@ namespace SystemCore stateManager.getPhaseEvasionGameState().loadHighScore(); stateManager.getRecallGameState().loadHighScore(); // TODO: Add loadHighScore for Chain Reaction + // TODO: additional. refactor this so that this automatically registers on startup. } void ContextManager::checkExitRequest() @@ -124,7 +125,7 @@ namespace SystemCore stateManager.setNext(Engine::SystemState::GameDemo); break; } - transitionLayer(); + changeApplication(); } if (controller.wasPressed(Player::ControllerButton::Circle)) @@ -157,7 +158,7 @@ namespace SystemCore stateManager.setNext(Engine::SystemState::SceneCanvas); break; } - transitionLayer(); + changeApplication(); } if (controller.wasPressed(Player::ControllerButton::Circle)) @@ -166,9 +167,11 @@ namespace SystemCore log("Transitioning to Main Menu."); } menuNav.displayMenuNavigation(); + + /////////////// Move all this business logic to the MenuNav class. } - void ContextManager::transitionLayer() + void ContextManager::changeApplication() { if (application) { @@ -178,23 +181,23 @@ namespace SystemCore switch (stateManager.current()) { case Engine::SystemState::GameRecall: - application = new Games::Recall::Driver{this}; + application = new Games::Recall::GameMain{this}; logf("Transitioning to Recall (Game)"); break; case Engine::SystemState::GamePhaseEvasion: - application = new Games::PhaseEvasion::Driver{this}; + application = new Games::PhaseEvasion::GameMain{this}; logf("Transitioning to Phase Evasion (Game)"); break; case Engine::SystemState::GameChainReaction: - application = new Games::ChainReaction::Driver{this}; + application = new Games::ChainReaction::GameMain{this}; logf("Transitioning to Chain Reaction (Game)"); break; case Engine::SystemState::GameReflex: - application = new Games::Reflex::Driver{this}; + application = new Games::Reflex::GameMain{this}; logf("Transitioning to Reflex (Game)"); break; case Engine::SystemState::GameDemo: - application = new Games::Demo::Driver{this}; + application = new Games::Demo::GameMain{this}; logf("Transitioning to Demo (Game)"); break; case Engine::SystemState::SceneCanvas: diff --git a/src/player/player.cpp b/src/player/player.cpp index 3b1aa3f..74d9420 100644 --- a/src/player/player.cpp +++ b/src/player/player.cpp @@ -10,7 +10,7 @@ namespace Player float step = analogToSpeed(distance, speed); positionPrecise += step; - const float adjustedLedCount = static_cast(SystemCore::Configuration::numLeds() - width); + const float adjustedLedCount = static_cast(SystemCore::Configuration::numLeds() - width + 1); if (shouldWrap) { positionPrecise = std::fmod(positionPrecise, adjustedLedCount); diff --git a/src/scenes/canvas/driver.cpp b/src/scenes/canvas/driver.cpp index f5ffc47..961d198 100644 --- a/src/scenes/canvas/driver.cpp +++ b/src/scenes/canvas/driver.cpp @@ -21,7 +21,7 @@ namespace Scenes::Canvas for (uint16_t i; i < SystemCore::Configuration::numLeds(); ++i) { - contextManager->leds.buffer[i] = contextManager->stateManager.getCanvasSceneState().currentColor; + contextManager->renderer.leds.buffer[i] = contextManager->stateManager.getCanvasSceneState().currentColor; } }