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
-
-
Figure 1. LumenLab schematic and electronic layout
+
+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*
+Front edge angle of the LumenLab enclosure v3, with a controller saddle

-*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*
+Front edge angle of the LumenLab enclosure v3, no controller saddle

-*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;
}
}