7 Commits
main ... main

Author SHA1 Message Date
f96a846202 small tweeks 2025-02-01 14:39:44 -05:00
442b397290 added Pop_OS! libvulkan-dev dependancy and info about obstacles 2025-02-01 12:57:55 -05:00
9f8224462b Added impervious obstacles 2025-02-01 12:54:28 -05:00
166bf96928 added framerate output. 2025-02-01 11:49:51 -05:00
cf1518a61b Updated to use JGL 2025-02-01 11:41:24 -05:00
583315193d Updated to use JGL 2025-02-01 11:40:21 -05:00
29c32f6d9a Updated to use JGL 2025-02-01 11:39:24 -05:00
4 changed files with 166 additions and 146 deletions

View File

@@ -28,20 +28,39 @@ include(cmake/CPM.cmake)
# You specify the release name to target against. See the link below for releases:
# https://git.redacted.cc/Redacted/ReWindow/releases
CPMAddPackage(
NAME mcolor
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-5.zip
)
CPMAddPackage(
NAME ReWindow
URL URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-31.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
)
CPMAddPackage(
NAME JGL
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-47.zip
)
add_executable(ReWalkerApp main.cpp)
target_include_directories(ReWalkerApp PUBLIC ${ReWindow_SOURCE_DIR}/include ${J3ML_SOURCE_DIR}/include)
target_include_directories(ReWalkerApp PUBLIC
${ReWindow_SOURCE_DIR}/include
${J3ML_SOURCE_DIR}/include
${mcolor_SOURCE_DIR}/include
${JGL_SOURCE_DIR}/include
)
target_link_libraries(ReWalkerApp PUBLIC J3ML ReWindowLibrary)
target_link_libraries(ReWalkerApp PUBLIC J3ML ReWindow mcolor JGL)
# simple window test
add_executable(TestWindowApp test.cpp)
target_include_directories(TestWindowApp PUBLIC ${ReWindow_SOURCE_DIR}/include ${J3ML_SOURCE_DIR}/include)
target_include_directories(TestWindowApp PUBLIC
${ReWindow_SOURCE_DIR}/include
${J3ML_SOURCE_DIR}/include
${mcolor_SOURCE_DIR}/include
)
target_link_libraries(TestWindowApp PUBLIC J3ML ReWindowLibrary)
target_link_libraries(TestWindowApp PUBLIC J3ML ReWindow)

View File

@@ -5,6 +5,8 @@ This is a simple random walker that has a little twist that if the user clicks t
The user can user the number 1, 2, 3, 4 and 5 keys to change the force at which the walker is blown away from the mouse pointer. 1 is lowest force and 5 is highest.
There are now 4 red impervious obstacles to bump into.
test.cpp builds to TestWindowApp as a test to help me understand how ReWindow works.
main.cpp builds to ReWalkerApp which is the main random walker program.
@@ -17,7 +19,7 @@ Install dependencies
```bash
Rocky/Fedora/RHEL: dnf install cmake make gcc-g++ libX11 libX11-devel mesa-libGL-devel
Pop_OS!/Ubuntu/Debian: apt-get install cmake make gcc g++ libx11-6 libx11-dev libgl-dev libxrandr-dev
Pop_OS!/Ubuntu/Debian: apt-get install cmake make gcc g++ libx11-6 libx11-dev libgl-dev libxrandr-dev libvulkan-dev
```
Clone the repository

259
main.cpp
View File

@@ -1,77 +1,107 @@
// ReWalker
// A random walker that moves around the screen with a fading trail and mouse interaction.
// Now with real-time adjustable push force using number keys 1-5!
// Written using Redacted ReWindow https://git.redacted.cc/Redacted/ReWindow
// A random walker that moves around the screen with a fading trail, mouse interaction, and a status bar.
// There are now obsticalse you cant walk through
// Updated to use the latest ReWindow API and JGL rendering.
// Written by Rich
// With help from (and thanks to) from Maxine, Josh and Bill
// With help from (and thanks to) from Maxine, Josh, and Bill.
#include <rewindow/types/window.h>
#include <rewindow/logger/logger.h>
#include <GL/gl.h>
#include <ReWindow/types/Window.h>
#include <ReWindow/Logger.h>
#include <JGL/JGL.h>
#include <Color4.hpp>
#include <memory>
// Define window size and dot size (dot size should be odd for symmetry)
// Define window size and dot size
#define WIDTH 800
#define HEIGHT 600
#define DOT_SIZE 5
#define OBSTACLE_SIZE 50
#define STATUS_BAR_HEIGHT 30 // Height of the status bar at the bottom
// Define fading speed (higher = faster fading, range: 0.001 - 0.2)
#define FADE_SPEED 0.05f
constexpr unsigned int TRAIL_LENGTH = 20;
// Enable debug mode (set to 0 to disable debug output)
#define DEBUG 0
// Enable debug mode
#define DEBUG 1
/**
* @brief Represents the walker that moves randomly on the screen.
* @brief Represents an obstacle that the walker cannot pass through.
*/
struct Obstacle {
Vector2 position; // Top-left corner of the obstacle
Obstacle(float x, float y) : position(x, y) {}
/**
* @brief Checks if a given position is inside the obstacle.
*/
bool isColliding(const Vector2& pos) const {
return pos.x >= position.x && pos.x <= position.x + OBSTACLE_SIZE &&
pos.y >= position.y && pos.y <= position.y + OBSTACLE_SIZE;
}
};
/**
* @brief Represents the walker that moves randomly.
*/
struct Walker {
float x, y; // Current position of the walker
Vector2 position;
Vector2 lastValidPosition;
Walker() : position(WIDTH / 2.0f, (HEIGHT - STATUS_BAR_HEIGHT) / 2.0f),
lastValidPosition(position) {}
/**
* @brief Constructor initializes the walker at the center of the screen.
* @brief Moves the walker in a random direction.
*/
Walker() : x(WIDTH / 2.0f), y(HEIGHT / 2.0f) {}
/**
* @brief Moves the walker one step in a random direction.
*/
void step() {
void step(const std::vector<Obstacle>& obstacles) {
float angle = static_cast<float>(rand()) / RAND_MAX * 360.0f;
angle = angle * M_PI / 180.0f; // Convert degrees to radians
angle = Math::Radians(angle);
float dx = std::cos(angle);
float dy = std::sin(angle);
// Update position and clamp within screen boundaries
x = std::clamp(x + dx, 0.0f, static_cast<float>(WIDTH - 1));
y = std::clamp(y + dy, 0.0f, static_cast<float>(HEIGHT - 1));
// Store last valid position
lastValidPosition = position;
#if DEBUG
std::cout << "Walker Position: (" << x << ", " << y << ")\n";
#endif
// Update position
position.x = std::clamp(position.x + dx, 0.0f, static_cast<float>(WIDTH - 1));
position.y = std::clamp(position.y + dy, 0.0f, static_cast<float>(HEIGHT - STATUS_BAR_HEIGHT - 1));
// Check for collisions
for (const auto& obstacle : obstacles) {
if (obstacle.isColliding(position)) {
position = lastValidPosition; // Revert movement if collision occurs
break;
}
}
}
/**
* @brief Pushes the walker away from a given (mouse) position.
* @param mouseX X coordinate of the mouse.
* @param mouseY Y coordinate of the mouse.
* @param force The force applied to push the walker away.
* @brief Pushes the walker away from the mouse pointer.
*/
void pushAway(float mouseX, float mouseY, float force) {
float adjustedMouseY = HEIGHT - mouseY; // Adjust for coordinate system
float deltaX = x - mouseX;
float deltaY = y - adjustedMouseY;
void pushAway(float mouseX, float mouseY, float force, const std::vector<Obstacle>& obstacles) {
float deltaX = position.x - mouseX;
float deltaY = position.y - mouseY;
float distance = std::sqrt(deltaX * deltaX + deltaY * deltaY);
// Only push if the mouse is close enough
if (distance > 0.0f) {
float unitX = deltaX / distance;
float unitY = deltaY / distance;
// Apply dynamic push force
x = std::clamp(x + unitX * force, 0.0f, static_cast<float>(WIDTH - 1));
y = std::clamp(y + unitY * force, 0.0f, static_cast<float>(HEIGHT - 1));
lastValidPosition = position;
position.x = std::clamp(position.x + unitX * force, 0.0f, static_cast<float>(WIDTH - 1));
position.y = std::clamp(position.y + unitY * force, 0.0f, static_cast<float>(HEIGHT - STATUS_BAR_HEIGHT - 1));
// Prevent moving into obstacles
for (const auto& obstacle : obstacles) {
if (obstacle.isColliding(position)) {
position = lastValidPosition;
break;
}
}
}
}
@@ -79,96 +109,78 @@ struct Walker {
* @brief Resets the walker to the center of the screen.
*/
void resetPosition() {
x = WIDTH / 2.0f;
y = HEIGHT / 2.0f;
position = Vector2(WIDTH / 2.0f, (HEIGHT - STATUS_BAR_HEIGHT) / 2.0f);
}
};
/**
* @brief Custom ReWindow window class that manages the random walker simulation.
* @brief Custom ReWindow window class that manages the walker simulation.
*/
class RandomWalkerWindow : public ReWindow::RWindow {
class RandomWalkerWindow : public ReWindow::OpenGLWindow {
public:
/**
* @brief Constructor initializes the window, walker, and trail buffer.
* @param title Window title
* @param width Window width
* @param height Window height
*/
RandomWalkerWindow(const std::string& title, int width, int height)
: ReWindow::RWindow(title, width, height),
: ReWindow::OpenGLWindow(title, width, height, 2, 1),
walker(std::make_unique<Walker>()),
trailBuffer(WIDTH * HEIGHT, 1.0f), // Initialize trail buffer to white (1.0)
pushForce(3.0f) // Default push force
{}
/**
* @brief Called once when the window is opened.
* Configures OpenGL to use a 2D orthographic projection.
*/
void OnOpen() override {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, WIDTH, HEIGHT, 0, -1, 1); // Map OpenGL coordinates to screen pixels
glMatrixMode(GL_MODELVIEW);
pushForce(3.0f)
{
// Define four obstacles
obstacles.emplace_back(200, 150);
obstacles.emplace_back(500, 150);
obstacles.emplace_back(200, 400);
obstacles.emplace_back(500, 400);
}
/**
* @brief Called every frame to update and render the walker with a fading trail.
*/
void OnRefresh(float elapsed) override {
// Clear the OpenGL buffer (not the trail buffer)
glClear(GL_COLOR_BUFFER_BIT);
void OnOpen() override {
if (!JGL::Init({WIDTH, HEIGHT}, 0, 0))
exit(0);
// Handle user inputs
this->render_target = new RenderTarget({WIDTH, HEIGHT}, Colors::White);
}
void OnRefresh(float elapsed) override {
handleMouseInput();
handleKeyboardInput();
// Update trail (fade effect)
fadeTrail();
// Move walker
walker->step(obstacles);
// Move the walker
walker->step();
JGL::Update({WIDTH, HEIGHT});
J2D::Begin(render_target, true);
fadeTrail();
drawTrail();
drawObstacles();
plotWalker();
J2D::End();
// Update the trail buffer with the walkers current position
plotWalker();
J2D::Begin(nullptr, true);
J2D::DrawRenderTarget(render_target, {0, 0});
J2D::End();
// Render the updated trail
drawTrail();
// Swap buffers to display the frame
GLSwapBuffers();
SwapBuffers();
}
private:
std::unique_ptr<Walker> walker;
std::vector<float> trailBuffer; // Stores grayscale intensity of pixels (1.0 = white, 0.0 = black)
float pushForce; // Variable push force that can be adjusted in real time
std::vector<Obstacle> obstacles;
std::vector<std::pair<Vector2, float>> trailBuffer{};
RenderTarget* render_target = nullptr;
float pushForce;
/**
* @brief Handles mouse input to push the walker away.
*/
void handleMouseInput() {
auto mousePos = GetMouseCoordinates();
if (IsMouseButtonDown(MouseButtons::Left)) {
walker->pushAway(mousePos.x, mousePos.y, pushForce);
walker->pushAway(mousePos.x, mousePos.y, pushForce, obstacles);
}
}
/**
* @brief Handles keyboard input to reset the walker and adjust push force.
*/
void handleKeyboardInput() {
if (IsKeyDown(Keys::Space)) {
walker->resetPosition();
}
if (IsKeyDown(Keys::Escape)) {
Close();
}
// Adjust push force using number keys 1-5
if (IsKeyDown(Keys::One)) pushForce = 1.0f;
if (IsKeyDown(Keys::Two)) pushForce = 2.0f;
if (IsKeyDown(Keys::Three)) pushForce = 3.0f;
@@ -176,48 +188,37 @@ private:
if (IsKeyDown(Keys::Five)) pushForce = 5.0f;
}
/**
* @brief Applies the fade effect to the trail buffer.
*/
void fadeTrail() {
for (auto& pixel : trailBuffer) {
pixel = std::min(pixel + FADE_SPEED, 1.0f); // Ensure value does not exceed 1.0 (white)
}
}
/**
* @brief Plots the walkers current position as a black dot in the trail buffer.
*/
void plotWalker() {
int centerX = static_cast<int>(walker->x);
int centerY = static_cast<int>(walker->y);
int halfSize = DOT_SIZE / 2;
for (int dy = -halfSize; dy <= halfSize; ++dy) {
for (int dx = -halfSize; dx <= halfSize; ++dx) {
int px = centerX + dx;
int py = centerY + dy;
if (px >= 0 && px < WIDTH && py >= 0 && py < HEIGHT) {
trailBuffer[py * WIDTH + px] = 0.0f;
}
}
}
for (auto& trailPoint : trailBuffer)
trailPoint.second = std::max(trailPoint.second - FADE_SPEED, 0.0f);
trailBuffer.emplace_back(walker->position, 1.0f);
if (trailBuffer.size() > TRAIL_LENGTH)
trailBuffer.erase(trailBuffer.begin());
}
void drawTrail() {
std::vector<uint8_t> grayscale(WIDTH * HEIGHT);
for (size_t i = 0; i < trailBuffer.size(); ++i) {
grayscale[i] = static_cast<uint8_t>(trailBuffer[i] * 255.0f);
for (const auto& trailPoint : trailBuffer)
J2D::DrawPoint({0, 0, 0, (uint8_t)(trailPoint.second * 255.0f)}, trailPoint.first, DOT_SIZE);
}
void plotWalker() {
J2D::DrawPoint(Colors::Black, walker->position, DOT_SIZE);
}
/**
* @brief Draws the four red obstacles.
*/
void drawObstacles() {
for (const auto& obstacle : obstacles) {
J2D::FillRect(Colors::Red, obstacle.position, {OBSTACLE_SIZE, OBSTACLE_SIZE});
}
glDrawPixels(WIDTH, HEIGHT, GL_LUMINANCE, GL_UNSIGNED_BYTE, grayscale.data());
}
};
int main() {
auto window = std::make_unique<RandomWalkerWindow>("Random Walker with ReWindow", WIDTH, HEIGHT);
window->SetRenderer(RenderingAPI::OPENGL);
window->Open();
auto window = std::make_unique<RandomWalkerWindow>("Random Walker with Obstacles", WIDTH, HEIGHT);
if (!window->Open())
return -1;
while (!window->IsClosing()) {
window->ManagedRefresh();

View File

@@ -1,32 +1,30 @@
// simple test program to open a window with
// a blue background.
#include <rewindow/types/window.h>
#include <rewindow/logger/logger.h>
#include <ReWindow/types/Window.h>
#include <ReWindow/Logger.h>
#include <GL/gl.h>
#define WIDTH 800
#define HEIGHT 600
class RandomWalkerWindow : public ReWindow::RWindow {
class RandomWalkerWindow : public ReWindow::OpenGLWindow {
public:
RandomWalkerWindow(const std::string& title, int width, int height)
: ReWindow::RWindow(title, width, height) {}
: ReWindow::OpenGLWindow(title, width, height, 2, 1) {}
void OnRefresh(float elapsed) override {
glClearColor(0.0f, 0.0f, 1.0f, 1.0f); // Clear with blue
glClear(GL_COLOR_BUFFER_BIT);
GLSwapBuffers();
//glClearColor(0.0f, 0.0f, 1.0f, 1.0f); // Clear with blue
//glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers();
}
};
int main() {
std::unique_ptr<RandomWalkerWindow> window =
std::make_unique<RandomWalkerWindow>("Random Walker with ReWindow", WIDTH, HEIGHT);
window->SetRenderer(RenderingAPI::OPENGL);
std::unique_ptr<RandomWalkerWindow> window =
std::make_unique<RandomWalkerWindow>("Random Walker with ReWindow", WIDTH, HEIGHT);
window->Open();
while (!window->IsClosing()) {