Initial commit
This commit is contained in:
commit
e36facce47
20 changed files with 1090 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.cache/
|
||||||
|
build/
|
||||||
|
build_pi/
|
||||||
56
CMakeLists.txt
Normal file
56
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
cmake_minimum_required(VERSION 3.24...3.30)
|
||||||
|
project(BarStatusScreen)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
# Generate compile_commands.json
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
set(RAYLIB_VERSION 5.0)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
raylib
|
||||||
|
DOWNLOAD_EXTRACT_TIMESTAMP OFF
|
||||||
|
URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz
|
||||||
|
FIND_PACKAGE_ARGS
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(raylib)
|
||||||
|
|
||||||
|
# Our Project
|
||||||
|
add_executable(${PROJECT_NAME})
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PROJECT_NAME})
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $<TARGET_FILE_DIR:${PROJECT_NAME}>)
|
||||||
|
|
||||||
|
if ("${PLATFORM}" STREQUAL "Web")
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME} PRE_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/resources $<TARGET_FILE_DIR:${PROJECT_NAME}>/../resources
|
||||||
|
)
|
||||||
|
#DEPENDS ${PROJECT_NAME}
|
||||||
|
else()
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/resources $<TARGET_FILE_DIR:${PROJECT_NAME}>/resources
|
||||||
|
)
|
||||||
|
#DEPENDS ${PROJECT_NAME}
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#set(raylib_VERBOSE 1)
|
||||||
|
target_link_libraries(${PROJECT_NAME} raylib)
|
||||||
|
|
||||||
|
# Web Configurations
|
||||||
|
if ("${PLATFORM}" STREQUAL "Web")
|
||||||
|
# Tell Emscripten to build an example.html file.
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html")
|
||||||
|
target_link_options(${PROJECT_NAME} PUBLIC -sUSE_GLFW=3 PUBLIC --preload-file resources)
|
||||||
|
endif()
|
||||||
55
build_pi.sh
Executable file
55
build_pi.sh
Executable file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$(uname -m)" == "aarch64" ]; then
|
||||||
|
apk add -U cmake make g++ mesa-dev
|
||||||
|
mkdir -p /build/build_pi
|
||||||
|
cd /build/build_pi
|
||||||
|
cmake .. \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DPLATFORM=DRM \
|
||||||
|
-DCUSTOMIZE_BUILD=ON \
|
||||||
|
-DSUPPORT_CAMERA_SYSTEM=ON \
|
||||||
|
-DSUPPORT_CLIPBOARD_IMAGE=OFF \
|
||||||
|
-DSUPPORT_COMPRESSION_API=OFF \
|
||||||
|
-DSUPPORT_DEFAULT_FONT=ON \
|
||||||
|
-DSUPPORT_FILEFORMAT_DDS=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_FNT=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_GIF=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_GLTF=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_HDR=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_IQM=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_M3D=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_MOD=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_MP3=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_MTL=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_OBJ=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_OGG=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_PNG=ON \
|
||||||
|
-DSUPPORT_FILEFORMAT_QOA=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_QOI=ON \
|
||||||
|
-DSUPPORT_FILEFORMAT_TTF=ON \
|
||||||
|
-DSUPPORT_FILEFORMAT_VOX=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_WAV=OFF \
|
||||||
|
-DSUPPORT_FILEFORMAT_XM=OFF \
|
||||||
|
-DSUPPORT_GESTURES_SYSTEM=OFF \
|
||||||
|
-DSUPPORT_GIF_RECORDING=OFF \
|
||||||
|
-DSUPPORT_IMAGE_EXPORT=OFF \
|
||||||
|
-DSUPPORT_IMAGE_GENERATION=OFF \
|
||||||
|
-DSUPPORT_IMAGE_MANIPULATION=OFF \
|
||||||
|
-DSUPPORT_MESH_GENERATION=ON \
|
||||||
|
-DSUPPORT_MODULE_RAUDIO=OFF \
|
||||||
|
-DSUPPORT_MODULE_RMODELS=ON \
|
||||||
|
-DSUPPORT_MODULE_RSHAPES=ON \
|
||||||
|
-DSUPPORT_MODULE_RTEXT=ON \
|
||||||
|
-DSUPPORT_MODULE_RTEXTURES=ON \
|
||||||
|
-DSUPPORT_MOUSE_GESTURES=OFF \
|
||||||
|
-DSUPPORT_QUADS_DRAW_MODE=ON \
|
||||||
|
-DSUPPORT_SCREEN_CAPTURE=OFF \
|
||||||
|
-DSUPPORT_SSH_KEYBOARD_RPI=OFF \
|
||||||
|
-DSUPPORT_STANDARD_FILEIO=ON \
|
||||||
|
-DSUPPORT_TEXT_MANIPULATION=ON \
|
||||||
|
-DSUPPORT_WINMM_HIGHRES_TIMER=ON
|
||||||
|
make -j12
|
||||||
|
else
|
||||||
|
podman run --rm --arch=aarch64 --pull always -v `pwd`:/build ghcr.io/linuxcontainers/alpine /bin/sh /build/build_pi.sh
|
||||||
|
fi
|
||||||
205
src/Application.cpp
Normal file
205
src/Application.cpp
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
#include "Application.hpp"
|
||||||
|
|
||||||
|
#include "Widget.hpp"
|
||||||
|
#include "widgets/DummyWidget.hpp"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace BSS;
|
||||||
|
|
||||||
|
Application::Application()
|
||||||
|
: m_shuffleInterval(30)
|
||||||
|
, m_activeWidgetCounter{}
|
||||||
|
, m_halfWidth{}
|
||||||
|
, m_halfHeight{}
|
||||||
|
, m_widgetShuffleTime{}
|
||||||
|
, m_rand(std::random_device()())
|
||||||
|
{
|
||||||
|
std::cout << "Starting up Bar Status Screen..." << std::endl;
|
||||||
|
|
||||||
|
for (auto& factory : BSS::Widget::WidgetFactories)
|
||||||
|
{
|
||||||
|
auto widget = factory();
|
||||||
|
if (!widget->IsAvailable())
|
||||||
|
{
|
||||||
|
std::cout << " " << (widget->IsVanity() ? "(Vanity) " : "") << "Widget " << widget->GetName() << " is not available, skipping." << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << " Registering " << widget->GetName() << (widget->IsVanity() ? " vanity" : "") << " widget" << std::endl;
|
||||||
|
m_widgets.emplace_back(std::move(widget));
|
||||||
|
}
|
||||||
|
BSS::Widget::WidgetFactories.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
m_activeWidgets[i] = std::make_shared<Widgets::DummyWidget>();
|
||||||
|
|
||||||
|
#if PLATFORM_DRM
|
||||||
|
int screenWidth = 1280;
|
||||||
|
int screenHeight = 720;
|
||||||
|
#else
|
||||||
|
int screenWidth = GetMonitorWidth(0);
|
||||||
|
int screenHeight = GetMonitorHeight(0);
|
||||||
|
#endif
|
||||||
|
// SetTargetFPS(GetMonitorRefreshRate(0));
|
||||||
|
|
||||||
|
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE);
|
||||||
|
InitWindow(screenWidth, screenHeight, "Bar Status Screen");
|
||||||
|
SetWindowState(FLAG_WINDOW_MAXIMIZED | FLAG_FULLSCREEN_MODE);
|
||||||
|
SetTargetFPS(30);
|
||||||
|
|
||||||
|
m_halfWidth = GetScreenWidth() / 2.f;
|
||||||
|
m_halfHeight = GetScreenHeight() / 2.f;
|
||||||
|
|
||||||
|
std::cout << " Adding Logo widget." << std::endl;
|
||||||
|
AddWidget("Logo");
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::~Application()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::Run()
|
||||||
|
{
|
||||||
|
auto lastFrame = GetTime();
|
||||||
|
while (!WindowShouldClose())
|
||||||
|
{
|
||||||
|
const auto cur = GetTime();
|
||||||
|
const float dt = cur - lastFrame;
|
||||||
|
lastFrame = cur;
|
||||||
|
|
||||||
|
Update(dt);
|
||||||
|
Draw();
|
||||||
|
}
|
||||||
|
CloseWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::AddWidget(const std::string& name)
|
||||||
|
{
|
||||||
|
std::cout << "Adding named widget " << name << std::endl;
|
||||||
|
auto it = std::find_if(std::begin(m_widgets), std::end(m_widgets), [&](auto& widget) { return widget->GetName() == name; });
|
||||||
|
if (it == std::end(m_widgets))
|
||||||
|
std::cout << " Unable to find widged named " << name << ", ignoring." << std::endl;
|
||||||
|
else
|
||||||
|
AddWidget(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::AddDummyWidget()
|
||||||
|
{
|
||||||
|
AddWidget(std::make_shared<Widgets::DummyWidget>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::Update(float dt)
|
||||||
|
{
|
||||||
|
m_halfWidth = GetScreenWidth() / 2.f;
|
||||||
|
m_halfHeight = GetScreenHeight() / 2.f;
|
||||||
|
|
||||||
|
for (auto& widget : m_widgets)
|
||||||
|
if (widget->IsActive())
|
||||||
|
widget->Update(dt);
|
||||||
|
|
||||||
|
m_widgetShuffleTime += dt;
|
||||||
|
if (m_widgetShuffleTime > m_shuffleInterval || IsKeyPressed(KEY_ENTER))
|
||||||
|
{
|
||||||
|
AddRandomWidget();
|
||||||
|
m_widgetShuffleTime = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::Draw()
|
||||||
|
{
|
||||||
|
BeginDrawing();
|
||||||
|
|
||||||
|
ClearBackground(BLACK);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (auto& widget : m_activeWidgets)
|
||||||
|
{
|
||||||
|
uint8_t x = i % 2;
|
||||||
|
uint8_t y = i++ / 2;
|
||||||
|
|
||||||
|
Rectangle rect {
|
||||||
|
x * m_halfWidth, y * m_halfHeight,
|
||||||
|
m_halfWidth, m_halfHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
bool scissor = widget->ShouldScissor();
|
||||||
|
if (scissor)
|
||||||
|
BeginScissorMode(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
|
||||||
|
|
||||||
|
widget->Draw(rect.x, rect.y, rect.width, rect.height);
|
||||||
|
|
||||||
|
if (scissor)
|
||||||
|
EndScissorMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawFPS(0, 0);
|
||||||
|
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::AddRandomWidget()
|
||||||
|
{
|
||||||
|
std::shuffle(m_widgets.begin(), m_widgets.end(), m_rand);
|
||||||
|
|
||||||
|
bool allowVanity = std::uniform_real_distribution<float>()(m_rand) < 0.25;
|
||||||
|
auto it = std::end(m_widgets);
|
||||||
|
|
||||||
|
std::cout << "Adding random widget" << (allowVanity ? " (vanity allowed)" : "") << std::endl;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Find first valid widget in list of loaded widgets
|
||||||
|
it = std::find_if(std::begin(m_widgets), std::end(m_widgets), [&](auto& potential) {
|
||||||
|
if (potential->IsVanity() && !allowVanity)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Skip if widget is already displayed
|
||||||
|
if (std::find_if(std::begin(m_activeWidgets), std::end(m_activeWidgets), [&](auto& loaded) { return loaded == potential; }) != std::end(m_activeWidgets))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == std::end(m_widgets) && !allowVanity)
|
||||||
|
{
|
||||||
|
std::cout << " Failed to find regular widget, retrying with vanity allowed." << std::endl;
|
||||||
|
allowVanity = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
} while(true);
|
||||||
|
|
||||||
|
if (it != std::end(m_widgets))
|
||||||
|
AddWidget(*it);
|
||||||
|
else
|
||||||
|
std::cout << " No valid widgets to add, ignoring." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::AddWidget(const std::shared_ptr<Widget>& widget)
|
||||||
|
{
|
||||||
|
std::cout << "Adding " << widget->GetName() << " widget" << std::endl;
|
||||||
|
|
||||||
|
if (!widget->IsActive())
|
||||||
|
widget->Activate(m_halfWidth, m_halfHeight);
|
||||||
|
|
||||||
|
auto active = m_activeWidgets[m_activeWidgetCounter];
|
||||||
|
m_activeWidgets[m_activeWidgetCounter] = widget;
|
||||||
|
m_activeWidgetCounter = (m_activeWidgetCounter + 1) % m_activeWidgets.size();
|
||||||
|
|
||||||
|
std::cout << " Replacing " << active->GetName() << " widget" << std::endl;
|
||||||
|
|
||||||
|
if (std::find(std::begin(m_activeWidgets), std::end(m_activeWidgets), active) == std::end(m_activeWidgets))
|
||||||
|
{
|
||||||
|
std::cout << " Deactivating " << active->GetName() << " widget" << std::endl;
|
||||||
|
|
||||||
|
active->Deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/Application.hpp
Normal file
43
src/Application.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Widget.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace BSS
|
||||||
|
{
|
||||||
|
|
||||||
|
class Application
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Application();
|
||||||
|
Application(const Application&) = delete;
|
||||||
|
~Application();
|
||||||
|
|
||||||
|
void SetShuffleInterval(float interval) { m_shuffleInterval = interval; }
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
void AddWidget(const std::string& name);
|
||||||
|
void AddRandomWidget();
|
||||||
|
void AddDummyWidget();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Update(float dt);
|
||||||
|
void Draw();
|
||||||
|
|
||||||
|
void AddWidget(const std::shared_ptr<Widget>& widget);
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Widget>> m_widgets;
|
||||||
|
std::array<std::shared_ptr<Widget>, 4> m_activeWidgets;
|
||||||
|
size_t m_activeWidgetCounter;
|
||||||
|
|
||||||
|
float m_shuffleInterval;
|
||||||
|
std::mt19937 m_rand;
|
||||||
|
|
||||||
|
float m_halfWidth, m_halfHeight, m_widgetShuffleTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
4
src/CMakeLists.txt
Normal file
4
src/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS *.cpp)
|
||||||
|
file(GLOB_RECURSE HEADER_FILES CONFIGURE_DEPENDS *.hpp)
|
||||||
|
|
||||||
|
target_sources(${PROJECT_NAME} PRIVATE ${SOURCE_FILES} ${HEADER_FILES})
|
||||||
16
src/Util/RaylibHelpers.hpp
Normal file
16
src/Util/RaylibHelpers.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "rlgl.h"
|
||||||
|
|
||||||
|
namespace BSS::Util
|
||||||
|
{
|
||||||
|
|
||||||
|
class RLMatrix
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RLMatrix() { rlPushMatrix(); }
|
||||||
|
~RLMatrix() { rlPopMatrix(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
89
src/Util/Text2D.hpp
Normal file
89
src/Util/Text2D.hpp
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace BSS::Util
|
||||||
|
{
|
||||||
|
|
||||||
|
inline void DrawTextCenter(Font font, const std::string& text, Vector2 position, float fontSize, float fontSpacing, Color textColor)
|
||||||
|
{
|
||||||
|
if (text.find('\n') != std::string::npos)
|
||||||
|
{
|
||||||
|
const int splits = std::count(std::begin(text), std::end(text), '\n');
|
||||||
|
const float total = splits * fontSize;
|
||||||
|
|
||||||
|
int at = 0;
|
||||||
|
int part = 0;
|
||||||
|
do {
|
||||||
|
const int next = text.find('\n', at);
|
||||||
|
const float perc = part++ / (float)splits;
|
||||||
|
|
||||||
|
DrawTextCenter(font, text.substr(at, next), { position.x, position.y - total * 0.5f + total * perc }, fontSize, fontSpacing, textColor);
|
||||||
|
at = next;
|
||||||
|
if (at != std::string::npos)
|
||||||
|
at++;
|
||||||
|
} while (at != std::string::npos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size = MeasureTextEx(font, text.c_str(), fontSize, fontSpacing);
|
||||||
|
|
||||||
|
DrawTextEx(font, text.c_str(), { position.x - size.x / 2.f, position.y - size.y / 2.f }, fontSize, fontSpacing, textColor);
|
||||||
|
}
|
||||||
|
inline void DrawTextCenterOutline(Font font, const std::string& text, Vector2 position, float fontSize, float fontSpacing, float outline, Color textColor, Color outlineColor)
|
||||||
|
{
|
||||||
|
if (text.find('\n') != std::string::npos)
|
||||||
|
{
|
||||||
|
const int splits = std::count(std::begin(text), std::end(text), '\n');
|
||||||
|
const float total = splits * fontSize;
|
||||||
|
|
||||||
|
int at = 0;
|
||||||
|
int part = 0;
|
||||||
|
do {
|
||||||
|
const int next = text.find('\n', at);
|
||||||
|
const float perc = part++ / (float)splits;
|
||||||
|
|
||||||
|
DrawTextCenterOutline(font, text.substr(at, next), { position.x, position.y - total * 0.5f + total * perc }, fontSize, fontSpacing, outline, textColor, outlineColor);
|
||||||
|
at = next;
|
||||||
|
if (at != std::string::npos)
|
||||||
|
at++;
|
||||||
|
} while (at != std::string::npos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float scale = fontSize / font.baseSize;
|
||||||
|
const char* str = text.c_str();
|
||||||
|
const auto size = MeasureTextEx(font, str, fontSize, fontSpacing);
|
||||||
|
Vector2 pos = { position.x - size.x * 0.5f, position.y - size.y * 0.5f };
|
||||||
|
|
||||||
|
for (size_t i = 0; i < text.length();)
|
||||||
|
{
|
||||||
|
int codepointByteCount = 0;
|
||||||
|
const int codepoint = GetCodepoint(str + i, &codepointByteCount);
|
||||||
|
const int glyphIdx = GetGlyphIndex(font, codepoint);
|
||||||
|
|
||||||
|
if (codepoint == 0x3f)
|
||||||
|
codepointByteCount = 1;
|
||||||
|
if (codepoint != ' ' && codepoint != '\t' && codepoint != '\n')
|
||||||
|
{
|
||||||
|
DrawTextCodepoint(font, codepoint, { pos.x - outline, pos.y - outline }, fontSize, outlineColor);
|
||||||
|
DrawTextCodepoint(font, codepoint, { pos.x - outline, pos.y + outline }, fontSize, outlineColor);
|
||||||
|
DrawTextCodepoint(font, codepoint, { pos.x + outline, pos.y - outline }, fontSize, outlineColor);
|
||||||
|
DrawTextCodepoint(font, codepoint, { pos.x + outline, pos.y + outline }, fontSize, outlineColor);
|
||||||
|
|
||||||
|
DrawTextCodepoint(font, codepoint, pos, fontSize, textColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font.glyphs[glyphIdx].advanceX == 0.f)
|
||||||
|
pos.x += (font.recs[glyphIdx].width + fontSpacing) * scale;
|
||||||
|
else
|
||||||
|
pos.x += (font.glyphs[glyphIdx].advanceX + fontSpacing) * scale;
|
||||||
|
|
||||||
|
i += codepointByteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
161
src/Util/Text3D.cpp
Normal file
161
src/Util/Text3D.cpp
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
#include "Text3D.hpp"
|
||||||
|
|
||||||
|
#include "rlgl.h"
|
||||||
|
|
||||||
|
void BSS::Util::DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint)
|
||||||
|
{
|
||||||
|
// Character index position in sprite font
|
||||||
|
// NOTE: In case a codepoint is not available in the font, index returned points to '?'
|
||||||
|
int index = GetGlyphIndex(font, codepoint);
|
||||||
|
float scale = fontSize/(float)font.baseSize;
|
||||||
|
|
||||||
|
// Character destination rectangle on screen
|
||||||
|
// NOTE: We consider charsPadding on drawing
|
||||||
|
position.x += (float)(font.glyphs[index].offsetX - font.glyphPadding)/(float)font.baseSize*scale;
|
||||||
|
position.z += (float)(font.glyphs[index].offsetY - font.glyphPadding)/(float)font.baseSize*scale;
|
||||||
|
|
||||||
|
// Character source rectangle from font texture atlas
|
||||||
|
// NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
|
||||||
|
Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding,
|
||||||
|
font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding };
|
||||||
|
|
||||||
|
float width = (float)(font.recs[index].width + 2.0f*font.glyphPadding)/(float)font.baseSize*scale;
|
||||||
|
float height = (float)(font.recs[index].height + 2.0f*font.glyphPadding)/(float)font.baseSize*scale;
|
||||||
|
|
||||||
|
if (font.texture.id > 0)
|
||||||
|
{
|
||||||
|
const float x = 0.0f;
|
||||||
|
const float y = 0.0f;
|
||||||
|
const float z = 0.0f;
|
||||||
|
|
||||||
|
// normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f)
|
||||||
|
const float tx = srcRec.x/font.texture.width;
|
||||||
|
const float ty = srcRec.y/font.texture.height;
|
||||||
|
const float tw = (srcRec.x+srcRec.width)/font.texture.width;
|
||||||
|
const float th = (srcRec.y+srcRec.height)/font.texture.height;
|
||||||
|
|
||||||
|
rlCheckRenderBatchLimit(4 + 4*backface);
|
||||||
|
rlSetTexture(font.texture.id);
|
||||||
|
|
||||||
|
rlPushMatrix();
|
||||||
|
rlTranslatef(position.x, position.y, position.z);
|
||||||
|
|
||||||
|
rlBegin(RL_QUADS);
|
||||||
|
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
|
||||||
|
|
||||||
|
// Front Face
|
||||||
|
rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
|
||||||
|
rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Left Of The Texture and Quad
|
||||||
|
rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad
|
||||||
|
rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad
|
||||||
|
rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad
|
||||||
|
|
||||||
|
if (backface)
|
||||||
|
{
|
||||||
|
// Back Face
|
||||||
|
rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down
|
||||||
|
rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Right Of The Texture and Quad
|
||||||
|
rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Left Of The Texture and Quad
|
||||||
|
rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad
|
||||||
|
rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad
|
||||||
|
}
|
||||||
|
rlEnd();
|
||||||
|
rlPopMatrix();
|
||||||
|
|
||||||
|
rlSetTexture(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSS::Util::DrawText3D(Font font, const std::string& text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint)
|
||||||
|
{
|
||||||
|
float textOffsetY = 0.0f; // Offset between lines (on line break '\n')
|
||||||
|
float textOffsetX = 0.0f; // Offset X to next character to draw
|
||||||
|
|
||||||
|
float scale = fontSize/(float)font.baseSize;
|
||||||
|
|
||||||
|
for (int i = 0; i < text.size();)
|
||||||
|
{
|
||||||
|
// Get next codepoint from byte string and glyph index in font
|
||||||
|
int codepointByteCount = 0;
|
||||||
|
int codepoint = GetCodepoint(&text[i], &codepointByteCount);
|
||||||
|
int index = GetGlyphIndex(font, codepoint);
|
||||||
|
|
||||||
|
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||||
|
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
|
||||||
|
if (codepoint == 0x3f) codepointByteCount = 1;
|
||||||
|
|
||||||
|
if (codepoint == '\n')
|
||||||
|
{
|
||||||
|
// NOTE: Fixed line spacing of 1.5 line-height
|
||||||
|
// TODO: Support custom line spacing defined by user
|
||||||
|
textOffsetY += scale + lineSpacing/(float)font.baseSize*scale;
|
||||||
|
textOffsetX = 0.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((codepoint != ' ') && (codepoint != '\t'))
|
||||||
|
{
|
||||||
|
DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font.glyphs[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale;
|
||||||
|
else textOffsetX += (float)(font.glyphs[index].advanceX + fontSpacing)/(float)font.baseSize*scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += codepointByteCount; // Move text bytes counter to next codepoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 BSS::Util::MeasureText3D(Font font, const std::string& text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint)
|
||||||
|
{
|
||||||
|
int tempLen = 0; // Used to count longer text line num chars
|
||||||
|
int lenCounter = 0;
|
||||||
|
|
||||||
|
float tempTextWidth = 0.0f; // Used to count longer text line width
|
||||||
|
|
||||||
|
float scale = fontSize/(float)font.baseSize;
|
||||||
|
float textHeight = scale;
|
||||||
|
float textWidth = 0.0f;
|
||||||
|
|
||||||
|
int letter = 0; // Current character
|
||||||
|
int index = 0; // Index position in sprite font
|
||||||
|
|
||||||
|
for (int i = 0; i < text.size(); i++)
|
||||||
|
{
|
||||||
|
lenCounter++;
|
||||||
|
|
||||||
|
int next = 0;
|
||||||
|
letter = GetCodepoint(&text[i], &next);
|
||||||
|
index = GetGlyphIndex(font, letter);
|
||||||
|
|
||||||
|
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||||
|
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
|
||||||
|
if (letter == 0x3f)
|
||||||
|
next = 1;
|
||||||
|
i += next - 1;
|
||||||
|
|
||||||
|
if (letter != '\n')
|
||||||
|
{
|
||||||
|
if (font.glyphs[index].advanceX != 0) textWidth += (font.glyphs[index].advanceX+fontSpacing)/(float)font.baseSize*scale;
|
||||||
|
else textWidth += (font.recs[index].width + font.glyphs[index].offsetX)/(float)font.baseSize*scale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||||
|
lenCounter = 0;
|
||||||
|
textWidth = 0.0f;
|
||||||
|
textHeight += scale + lineSpacing/(float)font.baseSize*scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempLen < lenCounter) tempLen = lenCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||||
|
|
||||||
|
Vector3 vec = { 0 };
|
||||||
|
vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure
|
||||||
|
vec.y = 0.25f;
|
||||||
|
vec.z = textHeight;
|
||||||
|
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
14
src/Util/Text3D.hpp
Normal file
14
src/Util/Text3D.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace BSS::Util
|
||||||
|
{
|
||||||
|
|
||||||
|
extern void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint);
|
||||||
|
extern void DrawText3D(Font font, const std::string& text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint);
|
||||||
|
extern Vector3 MeasureText3D(Font font, const std::string& text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint);
|
||||||
|
|
||||||
|
}
|
||||||
54
src/Widget.hpp
Normal file
54
src/Widget.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <cstdarg>
|
||||||
|
|
||||||
|
namespace BSS
|
||||||
|
{
|
||||||
|
|
||||||
|
class Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Widget() : m_activated(false) {}
|
||||||
|
virtual ~Widget() { if (IsActive()) Deactivate(); }
|
||||||
|
|
||||||
|
static std::list<std::function<std::shared_ptr<Widget>()>> WidgetFactories;
|
||||||
|
|
||||||
|
virtual std::string GetName() const = 0;
|
||||||
|
|
||||||
|
virtual void Init() {}
|
||||||
|
virtual void Deinit() {}
|
||||||
|
virtual bool IsAvailable() { return true; }
|
||||||
|
|
||||||
|
virtual void Activate(float w, float h) { m_activated = true; LogLine("Activated"); }
|
||||||
|
virtual void Deactivate() { m_activated = false; }
|
||||||
|
virtual bool IsActive() { return m_activated; }
|
||||||
|
|
||||||
|
virtual bool ShouldScissor() const { return true; }
|
||||||
|
virtual bool IsVanity() const { return false; }
|
||||||
|
|
||||||
|
virtual void Update(float dt) = 0;
|
||||||
|
virtual void Draw(float x, float y, float width, float height) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void LogLine(const char* format, ...) {
|
||||||
|
printf("[%s] ", GetName().c_str());
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vprintf(format, args);
|
||||||
|
va_end(args);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_activated;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IMPLEMENT_WIDGET(WIDGET) namespace { int dummy = (::BSS::Widget::WidgetFactories.push_back([]() { return std::make_shared<WIDGET>(); }), 0); }; \
|
||||||
|
|
||||||
|
}
|
||||||
49
src/main.cpp
Normal file
49
src/main.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "Application.hpp"
|
||||||
|
#include "Widget.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
std::list<std::function<std::shared_ptr<BSS::Widget>()>> BSS::Widget::WidgetFactories{};
|
||||||
|
std::unique_ptr<BSS::Application> app;
|
||||||
|
|
||||||
|
void sighup_handler(int sig)
|
||||||
|
{
|
||||||
|
static const std::filesystem::path widgetPath = "/tmp/bss-widget";
|
||||||
|
std::cout << "Received sighup, adding widget." << std::endl;
|
||||||
|
|
||||||
|
if (std::filesystem::exists(widgetPath))
|
||||||
|
{
|
||||||
|
auto file = std::ifstream(widgetPath);
|
||||||
|
std::string widgetName;
|
||||||
|
file >> widgetName;
|
||||||
|
std::filesystem::remove(widgetPath);
|
||||||
|
|
||||||
|
app->AddWidget(widgetName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
app->AddRandomWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
app = std::make_unique<BSS::Application>();
|
||||||
|
|
||||||
|
std::signal(SIGHUP, sighup_handler);
|
||||||
|
|
||||||
|
//for (int i = 0; i < 3; ++i)
|
||||||
|
// app->AddRandomWidget();
|
||||||
|
|
||||||
|
//app.AddWidget("Clock");
|
||||||
|
//app.AddWidget("Logo");
|
||||||
|
//app.AddWidget("Matrix");
|
||||||
|
|
||||||
|
//app.SetShuffleInterval(5);
|
||||||
|
app->Run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
0
src/resources/LysatorLogo.vox
Normal file
0
src/resources/LysatorLogo.vox
Normal file
21
src/widgets/ClockWidget.cpp
Normal file
21
src/widgets/ClockWidget.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include "ClockWidget.hpp"
|
||||||
|
|
||||||
|
#include "../Util/Text2D.hpp"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
using namespace BSS::Widgets;
|
||||||
|
|
||||||
|
IMPLEMENT_WIDGET(ClockWidget);
|
||||||
|
|
||||||
|
void ClockWidget::Draw(float x, float y, float width, float height)
|
||||||
|
{
|
||||||
|
static char timeString[std::size("yyyy-mm-dd hh:mm:ss")];
|
||||||
|
|
||||||
|
const std::time_t time = std::time({});
|
||||||
|
std::strftime(std::data(timeString), std::size(timeString), "%F\n%T", std::gmtime(&time));
|
||||||
|
|
||||||
|
Util::DrawTextCenterOutline(GetFontDefault(), timeString, { x + width * 0.5f, y + height * 0.5f }, height / 8.f, 1.f, height / 200.f, BLACK, GRAY);
|
||||||
|
}
|
||||||
20
src/widgets/ClockWidget.hpp
Normal file
20
src/widgets/ClockWidget.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Widget.hpp"
|
||||||
|
|
||||||
|
namespace BSS::Widgets
|
||||||
|
{
|
||||||
|
|
||||||
|
// A widget displaying the time
|
||||||
|
class ClockWidget : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string GetName() const override { return "Clock"; }
|
||||||
|
|
||||||
|
void Update(float dt) override {}
|
||||||
|
void Draw(float x, float y, float width, float height) override;
|
||||||
|
|
||||||
|
bool IsVanity() const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
18
src/widgets/DummyWidget.hpp
Normal file
18
src/widgets/DummyWidget.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Widget.hpp"
|
||||||
|
|
||||||
|
namespace BSS::Widgets
|
||||||
|
{
|
||||||
|
|
||||||
|
// A "This space left intentionally blank" widget
|
||||||
|
class DummyWidget : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline std::string GetName() const override { return "Dummy"; }
|
||||||
|
|
||||||
|
inline void Update(float dt) override {}
|
||||||
|
inline void Draw(float x, float y, float width, float height) override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
111
src/widgets/LogoWidget.cpp
Normal file
111
src/widgets/LogoWidget.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
#include "LogoWidget.hpp"
|
||||||
|
|
||||||
|
#include "../Util/Text2D.hpp"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "rlgl.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using namespace BSS::Widgets;
|
||||||
|
|
||||||
|
IMPLEMENT_WIDGET(LogoWidget);
|
||||||
|
|
||||||
|
// Morris Maroon / PDP11
|
||||||
|
const Color Color_CtrlC_Blue{ 99, 15, 30, 255 };
|
||||||
|
// Brite Copenhagen Blue / PDP15
|
||||||
|
const Color Color_CtrlC_Red{ 39, 128, 196, 255 };
|
||||||
|
// Pure yellow
|
||||||
|
const Color Color_Lysator{ 255, 255, 0, 255 };
|
||||||
|
|
||||||
|
LogoWidget::LogoWidget()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogoWidget::Activate(float w, float h)
|
||||||
|
{
|
||||||
|
Widget::Activate(w, h);
|
||||||
|
|
||||||
|
m_font = GetFontDefault();
|
||||||
|
|
||||||
|
PreRender(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogoWidget::Deactivate()
|
||||||
|
{
|
||||||
|
Widget::Deactivate();
|
||||||
|
|
||||||
|
UnloadFont(m_font);
|
||||||
|
UnloadRenderTexture(m_texture);
|
||||||
|
m_texture.texture.width = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogoWidget::Draw(float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
if (m_texture.texture.width != (int)w || m_texture.texture.height != (int)h)
|
||||||
|
{
|
||||||
|
EndScissorMode();
|
||||||
|
PreRender(w, h);
|
||||||
|
BeginScissorMode(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lysator
|
||||||
|
DrawTextureRec(m_texture.texture, { (float)m_texture.texture.width * 0.25f, 0, (float)m_texture.texture.width, (float)-m_texture.texture.height }, { x, y - h * 0.05f }, WHITE);
|
||||||
|
|
||||||
|
// Ctrl-C
|
||||||
|
{
|
||||||
|
const auto mid = Vector2{ x + w * 0.75f, y + h * 0.45f };
|
||||||
|
const float base = h / 5.f;
|
||||||
|
|
||||||
|
Util::DrawTextCenterOutline(m_font, "C", mid, base * 2, 1.f, h / 250.f, Color_CtrlC_Red, WHITE);
|
||||||
|
Util::DrawTextCenterOutline(m_font, "Ctrl", { mid.x - base * 0.25f, mid.y }, base * 0.75f, 1.f, h / 250.f, Color_CtrlC_Blue, WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Util::DrawTextCenterOutline(m_font, "Bar\nMonitoring", { x + w * 0.5f, y + h * 0.5f }, cos(GetTime() * 0.5f) * 5.f + h / 4.f, 1.f, h / 200.f, BLACK, GRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogoWidget::PreRender(float w, float h)
|
||||||
|
{
|
||||||
|
if (m_texture.texture.width == (int)w && m_texture.texture.height == (int)h)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LogLine("Rendering %fx%fpx texture of Lysator logo", w, h);
|
||||||
|
|
||||||
|
auto torusMesh = GenMeshTorus(0.15f, 0.5f, 16, 16);
|
||||||
|
auto torusModel = LoadModelFromMesh(torusMesh);
|
||||||
|
|
||||||
|
if (IsRenderTextureReady(m_texture))
|
||||||
|
UnloadRenderTexture(m_texture);
|
||||||
|
|
||||||
|
m_texture = LoadRenderTexture(w, h);
|
||||||
|
|
||||||
|
BeginTextureMode(m_texture);
|
||||||
|
ClearBackground(BLACK);
|
||||||
|
|
||||||
|
Camera camera = { 0 };
|
||||||
|
camera.position = (Vector3){ 0.0f, 0.0f, 12.0f }; // Camera position
|
||||||
|
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
|
||||||
|
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
|
||||||
|
camera.fovy = 45.0f; // Camera field-of-view Y
|
||||||
|
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
|
||||||
|
BeginMode3D(camera);
|
||||||
|
|
||||||
|
// Lysator
|
||||||
|
{
|
||||||
|
// X
|
||||||
|
DrawCube({ 0, 0, 0 }, 8.f, 0.3f, 0.3f, Color_Lysator);
|
||||||
|
// Y
|
||||||
|
DrawCube({ 0, 0, 0 }, 0.3f, 8.f, 0.3f, Color_Lysator);
|
||||||
|
// Z
|
||||||
|
DrawCube({ 0, 0, 0 }, 0.3f, 0.3f, 8.f, Color_Lysator);
|
||||||
|
|
||||||
|
DrawModelEx( torusModel, { 0, 0, 0 }, { 0, -0.75f, 1.f }, -90.f, { 8.f, 8.f, 8.f }, Color_Lysator);
|
||||||
|
//DrawModelWiresEx(torusModel, { 0, 0, 0 }, { 0, -0.75f, 1.f }, -90.f, { 8.f, 8.f, 8.f }, BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndMode3D();
|
||||||
|
|
||||||
|
EndTextureMode();
|
||||||
|
|
||||||
|
UnloadModel(torusModel);
|
||||||
|
}
|
||||||
34
src/widgets/LogoWidget.hpp
Normal file
34
src/widgets/LogoWidget.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Widget.hpp"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
namespace BSS::Widgets
|
||||||
|
{
|
||||||
|
|
||||||
|
// A widget displaying the status screen logo
|
||||||
|
class LogoWidget : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LogoWidget();
|
||||||
|
|
||||||
|
std::string GetName() const override { return "Logo"; }
|
||||||
|
|
||||||
|
void Activate(float w, float h) override;
|
||||||
|
void Deactivate() override;
|
||||||
|
|
||||||
|
void Update(float dt) override {}
|
||||||
|
void Draw(float x, float y, float width, float height) override;
|
||||||
|
|
||||||
|
bool IsVanity() const override { return true; }
|
||||||
|
// bool ShouldScissor() const override { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void PreRender(float w, float h);
|
||||||
|
|
||||||
|
RenderTexture2D m_texture;
|
||||||
|
Font m_font;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
100
src/widgets/MatrixWidget.cpp
Normal file
100
src/widgets/MatrixWidget.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
#include "MatrixWidget.hpp"
|
||||||
|
|
||||||
|
#include "../Util/Text2D.hpp"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numbers>
|
||||||
|
#include <random>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace BSS::Widgets;
|
||||||
|
|
||||||
|
IMPLEMENT_WIDGET(MatrixWidget);
|
||||||
|
|
||||||
|
void MatrixWidget::Activate(float w, float h)
|
||||||
|
{
|
||||||
|
Widget::Activate(w, h);
|
||||||
|
|
||||||
|
m_rand = std::mt19937(std::random_device()());
|
||||||
|
|
||||||
|
float x = 0;
|
||||||
|
for (size_t i = 0; i < m_strands.size(); ++i)
|
||||||
|
{
|
||||||
|
using namespace std::numbers;
|
||||||
|
x = std::fmod(x + (float)phi, 0.95f) + 0.025f,
|
||||||
|
|
||||||
|
m_strands[i] = {
|
||||||
|
(int)m_rand(),
|
||||||
|
x,
|
||||||
|
0,
|
||||||
|
std::uniform_real_distribution<float>(0, 0.5f)(m_rand),
|
||||||
|
std::uniform_int_distribution<size_t>(1, 25)(m_rand),
|
||||||
|
std::uniform_int_distribution<size_t>(3, 15)(m_rand)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixWidget::Update(float dt)
|
||||||
|
{
|
||||||
|
const float step = 0.1f;
|
||||||
|
m_accum += dt;
|
||||||
|
|
||||||
|
while (m_accum > step)
|
||||||
|
{
|
||||||
|
m_accum -= step;
|
||||||
|
|
||||||
|
for (auto& strand : m_strands)
|
||||||
|
{
|
||||||
|
strand.accum += step;
|
||||||
|
if (strand.accum >= strand.speed)
|
||||||
|
{
|
||||||
|
strand.accum = 0;
|
||||||
|
strand.length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strand.length > 25)
|
||||||
|
{
|
||||||
|
using namespace std::numbers;
|
||||||
|
strand.seed = m_rand();
|
||||||
|
strand.x = std::fmod(strand.x + (float)phi, 0.95f) + 0.025f;
|
||||||
|
strand.speed = std::uniform_real_distribution<float>(0, 0.5f)(m_rand);
|
||||||
|
strand.length = 1;
|
||||||
|
strand.renderLen = std::uniform_int_distribution<size_t>(3, 15)(m_rand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MatrixWidget::Draw(float x, float y, float width, float height)
|
||||||
|
{
|
||||||
|
const static std::string chars = "ABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789";
|
||||||
|
const static Font font = GetFontDefault();
|
||||||
|
|
||||||
|
std::uniform_int_distribution<size_t> charDist(0, std::size(chars) - 1);
|
||||||
|
for (const auto& strand : m_strands)
|
||||||
|
{
|
||||||
|
std::mt19937 twister(strand.seed);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < strand.length; ++i)
|
||||||
|
{
|
||||||
|
const auto chr = chars[charDist(twister)];
|
||||||
|
const size_t dist = strand.length - i;
|
||||||
|
const auto maxDist = strand.renderLen;
|
||||||
|
|
||||||
|
if (dist > maxDist)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const float visibility = ((maxDist + 1) - dist) / (float)(maxDist + 1);
|
||||||
|
|
||||||
|
auto mainCol = DARKGREEN;
|
||||||
|
auto outCol = GREEN;
|
||||||
|
|
||||||
|
mainCol.a = 255 * visibility;
|
||||||
|
outCol.a = 255 * visibility;
|
||||||
|
|
||||||
|
Util::DrawTextCenterOutline(font, std::string(&chr, 1), { x + width * strand.x, y + (height / 10.f) * i }, height / 20.f, 1.f, height / 500.f, mainCol, outCol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/widgets/MatrixWidget.hpp
Normal file
37
src/widgets/MatrixWidget.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../Widget.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace BSS::Widgets
|
||||||
|
{
|
||||||
|
|
||||||
|
// A widget displaying Matrix-style falling letters
|
||||||
|
class MatrixWidget : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string GetName() const override { return "Matrix"; }
|
||||||
|
|
||||||
|
void Activate(float w, float h) override;
|
||||||
|
|
||||||
|
void Update(float dt) override;
|
||||||
|
void Draw(float x, float y, float width, float height) override;
|
||||||
|
|
||||||
|
bool IsVanity() const override { return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct TextStrand
|
||||||
|
{
|
||||||
|
int seed;
|
||||||
|
float x, accum, speed;
|
||||||
|
size_t length, renderLen;
|
||||||
|
};
|
||||||
|
|
||||||
|
float m_accum;
|
||||||
|
std::mt19937 m_rand;
|
||||||
|
std::array<TextStrand, 20> m_strands;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue