i've added so much and idek anymore
This commit is contained in:
parent
c120871293
commit
51ff181659
19 changed files with 1573 additions and 10 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -42,6 +42,7 @@ Debug/
|
|||
Release/
|
||||
x64/
|
||||
x86/
|
||||
.cache/
|
||||
|
||||
# IDE and Editor files
|
||||
.vscode/
|
||||
|
|
|
@ -7,13 +7,26 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||
# Find SDL2
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
# GLM - direct path approach for macOS/Homebrew
|
||||
set(GLM_INCLUDE_DIR "/opt/homebrew/include")
|
||||
if(EXISTS "${GLM_INCLUDE_DIR}/glm/glm.hpp")
|
||||
message(STATUS "Found GLM at: ${GLM_INCLUDE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR "GLM not found at ${GLM_INCLUDE_DIR}/glm/glm.hpp")
|
||||
endif()
|
||||
|
||||
# Collect all source files
|
||||
set(SOURCES
|
||||
src/main.cpp
|
||||
src/core/Application.cpp
|
||||
src/core/InputHandler.cpp
|
||||
src/window/Window.cpp
|
||||
src/renderer/Renderer.cpp
|
||||
src/events/EventManager.cpp
|
||||
src/particles/Particle.cpp
|
||||
src/particles/ParticleManager.cpp
|
||||
src/particles/ParticleSystem.cpp
|
||||
src/particles/ParticleFactory.cpp
|
||||
)
|
||||
|
||||
# Add executable
|
||||
|
@ -22,8 +35,17 @@ add_executable(${PROJECT_NAME} ${SOURCES})
|
|||
# Link SDL2
|
||||
target_link_libraries(${PROJECT_NAME} SDL2::SDL2)
|
||||
|
||||
# GLM is header-only, no linking needed
|
||||
# target_link_libraries(${PROJECT_NAME} ${GLM_LIBRARIES})
|
||||
|
||||
# Include directories for SDL2
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
# Include directories for GLM - set globally
|
||||
include_directories(${GLM_INCLUDE_DIR})
|
||||
|
||||
# Alternative approach: also set via CMAKE_CXX_FLAGS as fallback
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${GLM_INCLUDE_DIR}")
|
||||
|
||||
# Set include directories for our source files
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
#include "Application.h"
|
||||
#include "Config.h"
|
||||
#include "../utils/Utils.h"
|
||||
#include "../core/InputHandler.h"
|
||||
#include "../particles/ParticleFactory.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
Application::Application(const std::string& title, int width, int height)
|
||||
: initialized_(false) {
|
||||
: initialized_(false)
|
||||
, lastFrameTime_(0)
|
||||
, showBoundaries_(Config::SHOW_BOUNDARIES)
|
||||
, useMaterialRendering_(false) {
|
||||
|
||||
// Initialize random seed for particle offset
|
||||
std::srand(static_cast<unsigned int>(std::time(nullptr)));
|
||||
|
||||
if (!initialize()) {
|
||||
Utils::logError("Failed to initialize application");
|
||||
|
@ -25,9 +37,22 @@ Application::Application(const std::string& title, int width, int height)
|
|||
return;
|
||||
}
|
||||
|
||||
// Create particle manager
|
||||
particleManager_ = std::make_unique<ParticleManager>();
|
||||
particleManager_->initialize(width, height);
|
||||
|
||||
// Create event manager
|
||||
eventManager_ = std::make_unique<EventManager>();
|
||||
|
||||
// Create input handler
|
||||
inputHandler_ = std::make_unique<InputHandler>();
|
||||
|
||||
// Create particle factory
|
||||
particleFactory_ = std::make_unique<ParticleFactory>();
|
||||
|
||||
// Connect particle manager to event manager
|
||||
eventManager_->setParticleManager(particleManager_.get());
|
||||
|
||||
// Register event callbacks
|
||||
eventManager_->registerCallback(EventType::Quit,
|
||||
[this](const EventData& event) { onQuit(event); });
|
||||
|
@ -35,6 +60,23 @@ Application::Application(const std::string& title, int width, int height)
|
|||
[this](const EventData& event) { onKeyDown(event); });
|
||||
eventManager_->registerCallback(EventType::KeyUp,
|
||||
[this](const EventData& event) { onKeyUp(event); });
|
||||
eventManager_->registerCallback(EventType::MouseButtonDown,
|
||||
[this](const EventData& event) { onMouseButtonDown(event); });
|
||||
eventManager_->registerCallback(EventType::MouseButtonUp,
|
||||
[this](const EventData& event) { onMouseButtonUp(event); });
|
||||
eventManager_->registerCallback(EventType::MouseMove,
|
||||
[this](const EventData& event) { onMouseMove(event); });
|
||||
|
||||
// Set up input handler callbacks
|
||||
inputHandler_->setOnParticleCreateCallback(
|
||||
[this](const glm::vec2& start, const glm::vec2& end) {
|
||||
createParticleAt(static_cast<int>(start.x), static_cast<int>(start.y));
|
||||
});
|
||||
|
||||
inputHandler_->setOnParticlePathCallback(
|
||||
[this](const glm::vec2& start, const glm::vec2& end) {
|
||||
createParticlesAlongPath(start, end);
|
||||
});
|
||||
|
||||
initialized_ = true;
|
||||
}
|
||||
|
@ -65,9 +107,14 @@ int Application::run() {
|
|||
}
|
||||
|
||||
Utils::log("Starting application...");
|
||||
Utils::log("Press 'B' to toggle boundaries on/off");
|
||||
Utils::log("Press 'M' to toggle material rendering");
|
||||
Utils::log("Press 'ESC' to quit");
|
||||
|
||||
// Main application loop
|
||||
while (isRunning()) {
|
||||
Uint32 frameStart = SDL_GetTicks();
|
||||
|
||||
// Handle events
|
||||
eventManager_->pollEvents();
|
||||
|
||||
|
@ -76,6 +123,14 @@ int Application::run() {
|
|||
|
||||
// Render frame
|
||||
render();
|
||||
|
||||
// Performance optimization: frame rate limiting
|
||||
Uint32 frameTime = SDL_GetTicks() - frameStart;
|
||||
if (frameTime < TARGET_FRAME_TIME) {
|
||||
SDL_Delay(TARGET_FRAME_TIME - frameTime);
|
||||
}
|
||||
|
||||
lastFrameTime_ = frameTime;
|
||||
}
|
||||
|
||||
Utils::log("Application finished");
|
||||
|
@ -83,8 +138,15 @@ int Application::run() {
|
|||
}
|
||||
|
||||
void Application::update() {
|
||||
// Update game logic here
|
||||
// This is where you would update game objects, physics, etc.
|
||||
// Update particle systems
|
||||
if (particleManager_) {
|
||||
particleManager_->update(1.0f / 60.0f); // Assuming 60 FPS
|
||||
}
|
||||
|
||||
// Update input handler
|
||||
if (inputHandler_) {
|
||||
inputHandler_->update();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::render() {
|
||||
|
@ -94,8 +156,36 @@ void Application::render() {
|
|||
renderer_->setDrawColor(0, 0, 0, 255);
|
||||
renderer_->clear();
|
||||
|
||||
// Render game objects here
|
||||
// This is where you would draw sprites, shapes, etc.
|
||||
// Render particles
|
||||
if (particleManager_) {
|
||||
auto defaultSystem = particleManager_->getDefaultParticleSystem();
|
||||
if (defaultSystem) {
|
||||
if (useMaterialRendering_) {
|
||||
renderer_->renderParticlesWithMaterials(defaultSystem->getAllParticles(), window_->getWidth(), window_->getHeight());
|
||||
} else {
|
||||
renderer_->renderParticles(defaultSystem->getAllParticles(), window_->getWidth(), window_->getHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render boundaries (floor and walls)
|
||||
renderer_->drawBoundaries(window_->getWidth(), window_->getHeight(), showBoundaries_);
|
||||
|
||||
// Draw boundary status text (simple visual indicator)
|
||||
if (showBoundaries_) {
|
||||
// Draw a small indicator in the top-left corner
|
||||
renderer_->setDrawColor(0, 255, 0, 255); // Green for enabled
|
||||
SDL_Rect indicatorRect = {10, 10, 20, 20};
|
||||
renderer_->fillRect(indicatorRect);
|
||||
}
|
||||
|
||||
// Draw material rendering status indicator
|
||||
if (useMaterialRendering_) {
|
||||
// Draw a small indicator in the top-right corner
|
||||
renderer_->setDrawColor(0, 255, 255, 255); // Cyan for material rendering
|
||||
SDL_Rect indicatorRect = {window_->getWidth() - 30, 10, 20, 20};
|
||||
renderer_->fillRect(indicatorRect);
|
||||
}
|
||||
|
||||
// Present the frame
|
||||
renderer_->present();
|
||||
|
@ -112,6 +202,16 @@ void Application::onKeyDown(const EventData& event) {
|
|||
case SDLK_ESCAPE:
|
||||
stop();
|
||||
break;
|
||||
case SDLK_b:
|
||||
// Toggle boundaries
|
||||
showBoundaries_ = !showBoundaries_;
|
||||
Utils::log("Boundaries " + std::string(showBoundaries_ ? "enabled" : "disabled"));
|
||||
break;
|
||||
case SDLK_m:
|
||||
// Toggle material rendering
|
||||
useMaterialRendering_ = !useMaterialRendering_;
|
||||
Utils::log("Material rendering " + std::string(useMaterialRendering_ ? "enabled" : "disabled"));
|
||||
break;
|
||||
// Add more key handling here
|
||||
}
|
||||
}
|
||||
|
@ -119,3 +219,63 @@ void Application::onKeyDown(const EventData& event) {
|
|||
void Application::onKeyUp(const EventData& event) {
|
||||
// Handle key up events here
|
||||
}
|
||||
|
||||
void Application::onMouseButtonDown(const EventData& event) {
|
||||
const SDL_MouseButtonEvent& mouseEvent = event.sdlEvent.button;
|
||||
|
||||
Utils::log("Mouse button down: " + std::to_string((int)mouseEvent.button) + " at (" + std::to_string(mouseEvent.x) + ", " + std::to_string(mouseEvent.y) + ")");
|
||||
|
||||
if (mouseEvent.button == SDL_BUTTON_LEFT && inputHandler_) {
|
||||
inputHandler_->startDrawing(glm::vec2(mouseEvent.x, mouseEvent.y));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::onMouseButtonUp(const EventData& event) {
|
||||
const SDL_MouseButtonEvent& mouseEvent = event.sdlEvent.button;
|
||||
|
||||
if (mouseEvent.button == SDL_BUTTON_LEFT && inputHandler_) {
|
||||
inputHandler_->stopDrawing();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::onMouseMove(const EventData& event) {
|
||||
const SDL_MouseMotionEvent& mouseEvent = event.sdlEvent.motion;
|
||||
|
||||
if (inputHandler_) {
|
||||
inputHandler_->updateMousePosition(glm::vec2(mouseEvent.x, mouseEvent.y));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::createParticleAt(int x, int y) {
|
||||
if (!particleManager_ || !particleFactory_) {
|
||||
Utils::logError("ParticleManager or ParticleFactory is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
Particle particle = particleFactory_->createSandParticle(glm::vec2(x, y));
|
||||
particleManager_->addParticleToDefault(particle);
|
||||
|
||||
// Performance optimization: only log every 10th particle to reduce console spam
|
||||
static int particleCount = 0;
|
||||
if (++particleCount % 10 == 0) {
|
||||
Utils::log("Added particle to default system at (" + std::to_string(x) + ", " + std::to_string(y) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
void Application::createParticlesAlongPath(const glm::vec2& start, const glm::vec2& end) {
|
||||
if (!particleManager_ || !particleFactory_) return;
|
||||
|
||||
std::vector<Particle> particles = particleFactory_->createParticlesAlongPath(start, end);
|
||||
|
||||
// Performance optimization: only process particles if there are any
|
||||
if (!particles.empty()) {
|
||||
for (const auto& particle : particles) {
|
||||
particleManager_->addParticleToDefault(particle);
|
||||
}
|
||||
|
||||
// Performance optimization: only log if we created a significant number of particles
|
||||
if (particles.size() > 5) {
|
||||
Utils::log("Created " + std::to_string(particles.size()) + " particles along path");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include "../window/Window.h"
|
||||
#include "../renderer/Renderer.h"
|
||||
#include "../events/EventManager.h"
|
||||
#include "../particles/ParticleManager.h"
|
||||
#include "../core/InputHandler.h"
|
||||
#include "../particles/ParticleFactory.h"
|
||||
|
||||
class Application {
|
||||
public:
|
||||
|
@ -31,9 +34,18 @@ private:
|
|||
WindowPtr window_;
|
||||
RendererPtr renderer_;
|
||||
EventManagerPtr eventManager_;
|
||||
std::unique_ptr<ParticleManager> particleManager_;
|
||||
std::unique_ptr<InputHandler> inputHandler_;
|
||||
std::unique_ptr<ParticleFactory> particleFactory_;
|
||||
|
||||
// Application state
|
||||
bool initialized_;
|
||||
bool showBoundaries_;
|
||||
bool useMaterialRendering_;
|
||||
|
||||
// Performance optimization: frame rate limiting
|
||||
static constexpr Uint32 TARGET_FRAME_TIME = 16; // ~60 FPS (1000ms / 60fps ≈ 16.67ms)
|
||||
Uint32 lastFrameTime_;
|
||||
|
||||
// Private methods
|
||||
bool initialize();
|
||||
|
@ -41,8 +53,15 @@ private:
|
|||
void update();
|
||||
void render();
|
||||
|
||||
// Particle management
|
||||
void createParticleAt(int x, int y);
|
||||
void createParticlesAlongPath(const glm::vec2& start, const glm::vec2& end);
|
||||
|
||||
// Event callbacks
|
||||
void onQuit(const EventData& event);
|
||||
void onKeyDown(const EventData& event);
|
||||
void onKeyUp(const EventData& event);
|
||||
void onMouseButtonDown(const EventData& event);
|
||||
void onMouseButtonUp(const EventData& event);
|
||||
void onMouseMove(const EventData& event);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Config {
|
||||
// Window settings
|
||||
constexpr int DEFAULT_WINDOW_WIDTH = 800;
|
||||
|
@ -12,6 +10,7 @@ namespace Config {
|
|||
constexpr bool VSYNC_ENABLED = true;
|
||||
constexpr int TARGET_FPS = 60;
|
||||
constexpr float TARGET_FRAME_TIME = 1.0f / TARGET_FPS;
|
||||
constexpr bool SHOW_BOUNDARIES = true;
|
||||
|
||||
// Application settings
|
||||
constexpr bool DEBUG_MODE = true;
|
||||
|
|
94
src/core/InputHandler.cpp
Normal file
94
src/core/InputHandler.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#include "InputHandler.h"
|
||||
#include "../utils/Utils.h"
|
||||
|
||||
InputHandler::InputHandler()
|
||||
: state_(InputState::IDLE)
|
||||
, lastMousePos_(0.0f, 0.0f)
|
||||
, currentMousePos_(0.0f, 0.0f)
|
||||
, lastParticleTime_(0) {
|
||||
}
|
||||
|
||||
void InputHandler::update() {
|
||||
// Input state updates can go here if needed
|
||||
}
|
||||
|
||||
void InputHandler::startDrawing(const glm::vec2& position) {
|
||||
state_ = InputState::DRAWING;
|
||||
lastMousePos_ = position;
|
||||
currentMousePos_ = position;
|
||||
lastParticleTime_ = SDL_GetTicks();
|
||||
|
||||
// Only log once when starting to draw (not on every mouse move)
|
||||
Utils::log("Started drawing particles at (" + std::to_string(position.x) + ", " + std::to_string(position.y) + ")");
|
||||
|
||||
// Trigger particle creation callback
|
||||
if (onParticleCreate_) {
|
||||
onParticleCreate_(position, position);
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler::stopDrawing() {
|
||||
state_ = InputState::IDLE;
|
||||
Utils::log("Stopped drawing particles");
|
||||
}
|
||||
|
||||
void InputHandler::updateMousePosition(const glm::vec2& position) {
|
||||
if (state_ == InputState::DRAWING) {
|
||||
// Performance optimization: only create particles if conditions are met
|
||||
if (shouldCreateParticle(position)) {
|
||||
// Trigger particle path callback for continuous drawing
|
||||
if (onParticlePath_) {
|
||||
onParticlePath_(lastMousePos_, position);
|
||||
}
|
||||
|
||||
lastMousePos_ = currentMousePos_;
|
||||
lastParticleTime_ = SDL_GetTicks();
|
||||
}
|
||||
|
||||
currentMousePos_ = position;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputHandler::shouldCreateParticle(const glm::vec2& newPos) const {
|
||||
// Check distance threshold
|
||||
float distance = glm::length(newPos - lastMousePos_);
|
||||
if (distance < MIN_PARTICLE_DISTANCE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check rate limiting
|
||||
Uint32 currentTime = SDL_GetTicks();
|
||||
if (currentTime - lastParticleTime_ < PARTICLE_CREATION_INTERVAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputHandler::setOnParticleCreateCallback(MouseCallback callback) {
|
||||
onParticleCreate_ = callback;
|
||||
}
|
||||
|
||||
void InputHandler::setOnParticlePathCallback(MouseCallback callback) {
|
||||
onParticlePath_ = callback;
|
||||
}
|
||||
|
||||
void InputHandler::handleMouseButtonDown(const SDL_MouseButtonEvent& event) {
|
||||
if (event.button == SDL_BUTTON_LEFT) {
|
||||
glm::vec2 position(event.x, event.y);
|
||||
startDrawing(position);
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler::handleMouseButtonUp(const SDL_MouseButtonEvent& event) {
|
||||
if (event.button == SDL_BUTTON_LEFT) {
|
||||
stopDrawing();
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler::handleMouseMove(const SDL_MouseMotionEvent& event) {
|
||||
if (state_ == InputState::DRAWING) {
|
||||
glm::vec2 position(event.x, event.y);
|
||||
updateMousePosition(position);
|
||||
}
|
||||
}
|
63
src/core/InputHandler.h
Normal file
63
src/core/InputHandler.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <functional>
|
||||
|
||||
enum class InputState {
|
||||
IDLE,
|
||||
DRAWING
|
||||
};
|
||||
|
||||
class InputHandler {
|
||||
public:
|
||||
InputHandler();
|
||||
~InputHandler() = default;
|
||||
|
||||
// Disable copying
|
||||
InputHandler(const InputHandler&) = delete;
|
||||
InputHandler& operator=(const InputHandler&) = delete;
|
||||
|
||||
// Input state management
|
||||
void update();
|
||||
InputState getState() const { return state_; }
|
||||
bool isDrawing() const { return state_ == InputState::DRAWING; }
|
||||
|
||||
// Mouse position tracking
|
||||
glm::vec2 getLastMousePos() const { return lastMousePos_; }
|
||||
glm::vec2 getCurrentMousePos() const { return currentMousePos_; }
|
||||
|
||||
// Drawing state management
|
||||
void startDrawing(const glm::vec2& position);
|
||||
void stopDrawing();
|
||||
void updateMousePosition(const glm::vec2& position);
|
||||
|
||||
// Callback registration
|
||||
using MouseCallback = std::function<void(const glm::vec2&, const glm::vec2&)>;
|
||||
void setOnParticleCreateCallback(MouseCallback callback);
|
||||
void setOnParticlePathCallback(MouseCallback callback);
|
||||
|
||||
private:
|
||||
InputState state_;
|
||||
glm::vec2 lastMousePos_;
|
||||
glm::vec2 currentMousePos_;
|
||||
|
||||
// Performance optimization: minimum distance threshold for particle creation
|
||||
static constexpr float MIN_PARTICLE_DISTANCE = 8.0f; // Only create particles if mouse moved 8+ pixels
|
||||
|
||||
// Performance optimization: rate limiting for particle creation
|
||||
Uint32 lastParticleTime_;
|
||||
static constexpr Uint32 PARTICLE_CREATION_INTERVAL = 16; // ~60 particles per second max
|
||||
|
||||
// Callbacks
|
||||
MouseCallback onParticleCreate_;
|
||||
MouseCallback onParticlePath_;
|
||||
|
||||
// Helper methods
|
||||
void handleMouseButtonDown(const SDL_MouseButtonEvent& event);
|
||||
void handleMouseButtonUp(const SDL_MouseButtonEvent& event);
|
||||
void handleMouseMove(const SDL_MouseMotionEvent& event);
|
||||
|
||||
// Performance helpers
|
||||
bool shouldCreateParticle(const glm::vec2& newPos) const;
|
||||
};
|
|
@ -1,7 +1,13 @@
|
|||
#include "EventManager.h"
|
||||
#include <iostream>
|
||||
#include "../particles/ParticleManager.h"
|
||||
#include "../particles/Particle.h"
|
||||
#include "../utils/Utils.h"
|
||||
|
||||
EventManager::EventManager() : running_(true) {
|
||||
EventManager::EventManager() : running_(true), particleManager_(nullptr) {
|
||||
}
|
||||
|
||||
void EventManager::setParticleManager(ParticleManager* manager) {
|
||||
particleManager_ = manager;
|
||||
}
|
||||
|
||||
bool EventManager::pollEvents() {
|
||||
|
@ -91,3 +97,32 @@ void EventManager::handleWindowEvent(const SDL_Event& event) {
|
|||
it->second(data);
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::onMouseButtonDown(const EventData& event) {
|
||||
Utils::log("Mouse button down");
|
||||
|
||||
if (particleManager_ && event.sdlEvent.type == SDL_MOUSEBUTTONDOWN) {
|
||||
const SDL_MouseButtonEvent& mouseEvent = event.sdlEvent.button;
|
||||
int x = mouseEvent.x;
|
||||
int y = mouseEvent.y;
|
||||
|
||||
// Create a sand particle at mouse position
|
||||
Particle particle(Particle::MaterialType::SAND, glm::vec2(x, y));
|
||||
particle.velocity = glm::vec2(0.0f, 0.0f);
|
||||
particle.size = 2.0f;
|
||||
particle.active = true;
|
||||
|
||||
// Add particle to the default system
|
||||
particleManager_->addParticleToDefault(particle);
|
||||
|
||||
Utils::log("Created sand particle at (" + std::to_string(x) + ", " + std::to_string(y) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::onMouseButtonUp(const EventData& event) {
|
||||
Utils::log("Mouse button up");
|
||||
}
|
||||
|
||||
void EventManager::onMouseMove(const EventData& event) {
|
||||
Utils::log("Mouse move");
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
class ParticleManager;
|
||||
|
||||
enum class EventType {
|
||||
Quit,
|
||||
KeyDown,
|
||||
|
@ -33,6 +35,9 @@ public:
|
|||
using EventCallback = std::function<void(const EventData&)>;
|
||||
void registerCallback(EventType type, EventCallback callback);
|
||||
void unregisterCallback(EventType type);
|
||||
|
||||
// Particle management
|
||||
void setParticleManager(ParticleManager* manager);
|
||||
|
||||
// Event processing
|
||||
void processEvent(const SDL_Event& event);
|
||||
|
@ -40,9 +45,12 @@ public:
|
|||
private:
|
||||
bool running_;
|
||||
std::unordered_map<EventType, EventCallback> callbacks_;
|
||||
|
||||
ParticleManager* particleManager_;
|
||||
void handleQuitEvent();
|
||||
void handleKeyEvent(const SDL_Event& event);
|
||||
void handleMouseEvent(const SDL_Event& event);
|
||||
void handleWindowEvent(const SDL_Event& event);
|
||||
void onMouseButtonDown(const EventData& event);
|
||||
void onMouseButtonUp(const EventData& event);
|
||||
void onMouseMove(const EventData& event);
|
||||
};
|
||||
|
|
71
src/particles/Particle.cpp
Normal file
71
src/particles/Particle.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "Particle.h"
|
||||
|
||||
// Particle class implementation - define particle methods here
|
||||
|
||||
Particle::Particle()
|
||||
: position(0.0f, 0.0f)
|
||||
, velocity(0.0f, 0.0f)
|
||||
, acceleration(0.0f, 0.0f)
|
||||
, mass(1.0f)
|
||||
, material(MaterialType::SAND)
|
||||
, hardness(1.0f)
|
||||
, cohesion(0.0f)
|
||||
, friction(0.5f)
|
||||
, size(1.0f)
|
||||
, density(1.0f)
|
||||
, temperature(20.0f)
|
||||
, viscosity(0.0f)
|
||||
, state(State::FALLING)
|
||||
, active(true)
|
||||
, gravity(9.8f)
|
||||
, canFall(true)
|
||||
, canFlow(false) {}
|
||||
|
||||
Particle::Particle(MaterialType mat, const glm::vec2& pos)
|
||||
: Particle() {
|
||||
material = mat;
|
||||
position = pos;
|
||||
setMaterial(mat);
|
||||
}
|
||||
|
||||
void Particle::setMaterial(MaterialType mat) {
|
||||
material = mat;
|
||||
|
||||
switch (mat) {
|
||||
case MaterialType::SAND:
|
||||
density = 1.6f;
|
||||
canFall = true;
|
||||
canFlow = false;
|
||||
break;
|
||||
case MaterialType::WATER:
|
||||
density = 1.0f;
|
||||
canFall = true;
|
||||
canFlow = true;
|
||||
break;
|
||||
case MaterialType::ROCK:
|
||||
density = 2.7f;
|
||||
canFall = false;
|
||||
canFlow = false;
|
||||
break;
|
||||
case MaterialType::ICE:
|
||||
density = 0.9f;
|
||||
canFall = true;
|
||||
canFlow = false;
|
||||
break;
|
||||
case MaterialType::METAL:
|
||||
density = 7.8f;
|
||||
canFall = false;
|
||||
canFlow = false;
|
||||
break;
|
||||
case MaterialType::EMPTY:
|
||||
density = 0.0f;
|
||||
canFall = false;
|
||||
canFlow = false;
|
||||
break;
|
||||
default:
|
||||
density = 1.0f;
|
||||
canFall = true;
|
||||
canFlow = false;
|
||||
break;
|
||||
}
|
||||
}
|
60
src/particles/Particle.h
Normal file
60
src/particles/Particle.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
// Particle class header - define individual particle properties and behavior here
|
||||
|
||||
struct Particle {
|
||||
// Physical properties
|
||||
glm::vec2 position;
|
||||
glm::vec2 velocity;
|
||||
glm::vec2 acceleration;
|
||||
float mass;
|
||||
|
||||
// Material properties
|
||||
enum class MaterialType {
|
||||
SAND,
|
||||
WATER,
|
||||
ROCK,
|
||||
ICE,
|
||||
METAL,
|
||||
EMPTY,
|
||||
};
|
||||
|
||||
// Particle state
|
||||
enum class State {
|
||||
FALLING,
|
||||
SETTLED,
|
||||
FLOWING,
|
||||
GAS,
|
||||
};
|
||||
|
||||
// Material properties
|
||||
MaterialType material;
|
||||
float hardness;
|
||||
float cohesion;
|
||||
float friction;
|
||||
|
||||
// Particle properties
|
||||
float size;
|
||||
float density;
|
||||
float temperature;
|
||||
float viscosity;
|
||||
|
||||
// Particle state
|
||||
State state;
|
||||
bool active;
|
||||
|
||||
// Particle behavior
|
||||
float gravity;
|
||||
bool canFall;
|
||||
bool canFlow;
|
||||
|
||||
// Particle constructor
|
||||
Particle();
|
||||
Particle(MaterialType mat, const glm::vec2& pos = {0.0f, 0.0f});
|
||||
|
||||
// Particle methods
|
||||
void setMaterial(MaterialType mat);
|
||||
};
|
80
src/particles/ParticleFactory.cpp
Normal file
80
src/particles/ParticleFactory.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
#include "ParticleFactory.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
ParticleFactory::ParticleFactory()
|
||||
: particleSize_(2.0f)
|
||||
, offsetRange_(3.0f) { // Reduced from 5.0f for better performance
|
||||
}
|
||||
|
||||
Particle ParticleFactory::createParticle(Particle::MaterialType material, const glm::vec2& position) {
|
||||
Particle particle(material, position);
|
||||
particle.velocity = glm::vec2(0.0f, 0.0f);
|
||||
particle.size = particleSize_;
|
||||
particle.active = true;
|
||||
|
||||
// Add small random offset for natural placement
|
||||
glm::vec2 offset = generateRandomOffset();
|
||||
particle.position += offset;
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
Particle ParticleFactory::createSandParticle(const glm::vec2& position) {
|
||||
return createParticle(Particle::MaterialType::SAND, position);
|
||||
}
|
||||
|
||||
std::vector<Particle> ParticleFactory::createParticlesAlongPath(const glm::vec2& start, const glm::vec2& end, float spacing) {
|
||||
std::vector<Particle> particles;
|
||||
|
||||
// Performance optimization: don't create particles for very short paths
|
||||
float distance = glm::length(end - start);
|
||||
if (distance < MIN_PATH_DISTANCE) {
|
||||
return particles; // Return empty vector for very short paths
|
||||
}
|
||||
|
||||
// Create particles at regular intervals along the path
|
||||
int numParticles = static_cast<int>(distance / spacing);
|
||||
|
||||
// Performance optimization: limit maximum particles per path
|
||||
if (numParticles > 0 && numParticles <= 20) { // Cap at 20 particles per path
|
||||
glm::vec2 direction = glm::normalize(end - start);
|
||||
float step = distance / numParticles;
|
||||
|
||||
for (int i = 1; i <= numParticles; ++i) {
|
||||
glm::vec2 pos = start + direction * (step * i);
|
||||
particles.push_back(createSandParticle(pos));
|
||||
}
|
||||
}
|
||||
|
||||
return particles;
|
||||
}
|
||||
|
||||
std::vector<Particle> ParticleFactory::createParticlesInArea(const glm::vec2& center, float radius, int count) {
|
||||
std::vector<Particle> particles;
|
||||
|
||||
// Performance optimization: limit maximum particles per area
|
||||
count = std::min(count, 50); // Cap at 50 particles per area
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
float angle = randomFloat(0.0f, 2.0f * M_PI);
|
||||
float distance = randomFloat(0.0f, radius);
|
||||
|
||||
glm::vec2 offset(cos(angle) * distance, sin(angle) * distance);
|
||||
glm::vec2 position = center + offset;
|
||||
|
||||
particles.push_back(createSandParticle(position));
|
||||
}
|
||||
|
||||
return particles;
|
||||
}
|
||||
|
||||
glm::vec2 ParticleFactory::generateRandomOffset() {
|
||||
float offsetX = (randomFloat(0.0f, 1.0f) - 0.5f) * offsetRange_;
|
||||
float offsetY = (randomFloat(0.0f, 1.0f) - 0.5f) * offsetRange_;
|
||||
return glm::vec2(offsetX, offsetY);
|
||||
}
|
||||
|
||||
float ParticleFactory::randomFloat(float min, float max) {
|
||||
return min + (max - min) * (static_cast<float>(rand()) / RAND_MAX);
|
||||
}
|
41
src/particles/ParticleFactory.h
Normal file
41
src/particles/ParticleFactory.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "Particle.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
class ParticleFactory {
|
||||
public:
|
||||
ParticleFactory();
|
||||
~ParticleFactory() = default;
|
||||
|
||||
// Disable copying
|
||||
ParticleFactory(const ParticleFactory&) = delete;
|
||||
ParticleFactory& operator=(const ParticleFactory&) = delete;
|
||||
|
||||
// Core particle creation methods
|
||||
Particle createParticle(Particle::MaterialType material, const glm::vec2& position);
|
||||
Particle createSandParticle(const glm::vec2& position);
|
||||
|
||||
// Batch particle creation
|
||||
std::vector<Particle> createParticlesAlongPath(const glm::vec2& start, const glm::vec2& end, float spacing = 8.0f);
|
||||
std::vector<Particle> createParticlesInArea(const glm::vec2& center, float radius, int count);
|
||||
|
||||
// Configuration
|
||||
void setParticleSize(float size) { particleSize_ = size; }
|
||||
void setOffsetRange(float range) { offsetRange_ = range; }
|
||||
float getParticleSize() const { return particleSize_; }
|
||||
float getOffsetRange() const { return offsetRange_; }
|
||||
|
||||
private:
|
||||
float particleSize_;
|
||||
float offsetRange_;
|
||||
|
||||
// Performance optimization: minimum distance threshold
|
||||
static constexpr float MIN_PATH_DISTANCE = 4.0f; // Don't create particles for very short paths
|
||||
|
||||
// Helper methods
|
||||
glm::vec2 generateRandomOffset();
|
||||
float randomFloat(float min, float max);
|
||||
};
|
366
src/particles/ParticleManager.cpp
Normal file
366
src/particles/ParticleManager.cpp
Normal file
|
@ -0,0 +1,366 @@
|
|||
#include "ParticleManager.h"
|
||||
#include "core/Config.h"
|
||||
#include "../utils/Utils.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
// ParticleManager class implementation - define manager methods here
|
||||
|
||||
ParticleManager::ParticleManager()
|
||||
: initialized_(false)
|
||||
, globalPaused_(false)
|
||||
, globalSimulationSpeed_(1.0f)
|
||||
, defaultWidth_(800)
|
||||
, defaultHeight_(600) {
|
||||
}
|
||||
|
||||
void ParticleManager::initialize(int defaultWidth, int defaultHeight) {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
defaultWidth_ = defaultWidth;
|
||||
defaultHeight_ = defaultHeight;
|
||||
|
||||
// Create default particle system
|
||||
defaultSystem_ = createParticleSystem(defaultWidth_, defaultHeight_, "default");
|
||||
|
||||
initialized_ = true;
|
||||
logSystemOperation("initialized");
|
||||
}
|
||||
|
||||
void ParticleManager::update(float deltaTime) {
|
||||
if (!initialized_ || globalPaused_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update all particle systems
|
||||
for (auto& [name, system] : systems_) {
|
||||
if (system && !globalPaused_) {
|
||||
updateSystem(name, deltaTime * globalSimulationSpeed_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::cleanup() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear all systems
|
||||
clearAllSystems();
|
||||
|
||||
// Reset state
|
||||
initialized_ = false;
|
||||
globalPaused_ = false;
|
||||
globalSimulationSpeed_ = 1.0f;
|
||||
|
||||
logSystemOperation("cleaned up");
|
||||
}
|
||||
|
||||
std::shared_ptr<ParticleSystem> ParticleManager::createParticleSystem(int width, int height, const std::string& name) {
|
||||
std::string systemName = name.empty() ? generateUniqueSystemName("system") : name;
|
||||
|
||||
if (!validateSystemName(systemName)) {
|
||||
std::cerr << "Invalid system name: " << systemName << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (systems_.find(systemName) != systems_.end()) {
|
||||
std::cerr << "System with name '" << systemName << "' already exists" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto system = std::make_shared<ParticleSystem>(width, height);
|
||||
systems_[systemName] = system;
|
||||
|
||||
logSystemOperation("created", systemName);
|
||||
return system;
|
||||
}
|
||||
|
||||
void ParticleManager::destroyParticleSystem(const std::string& name) {
|
||||
if (name == "default") {
|
||||
std::cerr << "Cannot destroy default system" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = systems_.find(name);
|
||||
if (it != systems_.end()) {
|
||||
logSystemOperation("destroyed", name);
|
||||
systems_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ParticleSystem> ParticleManager::getParticleSystem(const std::string& name) {
|
||||
auto it = systems_.find(name);
|
||||
return (it != systems_.end()) ? it->second : nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ParticleSystem> ParticleManager::getDefaultParticleSystem() {
|
||||
return defaultSystem_;
|
||||
}
|
||||
|
||||
void ParticleManager::addParticleToSystem(const std::string& systemName, const Particle& particle) {
|
||||
auto system = getParticleSystem(systemName);
|
||||
if (system) {
|
||||
system->addParticle(particle);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::addParticleToDefault(const Particle& particle) {
|
||||
if (defaultSystem_) {
|
||||
defaultSystem_->addParticle(particle);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::clearAllSystems() {
|
||||
for (auto& [name, system] : systems_) {
|
||||
if (system) {
|
||||
// Clear particles but keep the system structure
|
||||
// This would need a clear method in ParticleSystem
|
||||
}
|
||||
}
|
||||
logSystemOperation("cleared all systems");
|
||||
}
|
||||
|
||||
void ParticleManager::resetAllSystems() {
|
||||
// Destroy all non-default systems
|
||||
std::vector<std::string> systemNames;
|
||||
for (const auto& [name, _] : systems_) {
|
||||
if (name != "default") {
|
||||
systemNames.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& name : systemNames) {
|
||||
destroyParticleSystem(name);
|
||||
}
|
||||
|
||||
// Reset default system
|
||||
if (defaultSystem_) {
|
||||
// This would need a reset method in ParticleSystem
|
||||
}
|
||||
|
||||
logSystemOperation("reset all systems");
|
||||
}
|
||||
|
||||
void ParticleManager::placeMaterialInSystem(const std::string& systemName, int x, int y, Particle::MaterialType material) {
|
||||
auto system = getParticleSystem(systemName);
|
||||
if (system) {
|
||||
system->placeMaterial(x, y, material);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::placeMaterialInDefault(int x, int y, Particle::MaterialType material) {
|
||||
if (defaultSystem_) {
|
||||
defaultSystem_->placeMaterial(x, y, material);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::floodFill(const std::string& systemName, int startX, int startY, Particle::MaterialType material) {
|
||||
auto system = getParticleSystem(systemName);
|
||||
if (!system) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Simple flood fill implementation
|
||||
if (!system->isValidPosition(startX, startY) || !system->isEmpty(startX, startY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This would need a flood fill implementation in ParticleSystem
|
||||
// For now, just place material at the starting position
|
||||
system->placeMaterial(startX, startY, material);
|
||||
}
|
||||
|
||||
void ParticleManager::pauseSimulation(const std::string& systemName) {
|
||||
if (systemName.empty()) {
|
||||
globalPaused_ = true;
|
||||
logSystemOperation("paused globally");
|
||||
} else {
|
||||
// Individual system pause would need pause state in ParticleSystem
|
||||
logSystemOperation("paused", systemName);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::resumeSimulation(const std::string& systemName) {
|
||||
if (systemName.empty()) {
|
||||
globalPaused_ = false;
|
||||
logSystemOperation("resumed globally");
|
||||
} else {
|
||||
// Individual system resume would need pause state in ParticleSystem
|
||||
logSystemOperation("resumed", systemName);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::setSimulationSpeed(float speed, const std::string& systemName) {
|
||||
if (systemName.empty()) {
|
||||
globalSimulationSpeed_ = std::max(0.0f, speed);
|
||||
logSystemOperation("set global speed to " + std::to_string(globalSimulationSpeed_));
|
||||
} else {
|
||||
// Individual system speed would need speed state in ParticleSystem
|
||||
logSystemOperation("set speed for " + systemName + " to " + std::to_string(speed));
|
||||
}
|
||||
}
|
||||
|
||||
bool ParticleManager::isSimulationPaused(const std::string& systemName) const {
|
||||
if (systemName.empty()) {
|
||||
return globalPaused_;
|
||||
}
|
||||
// Individual system pause state would need pause state in ParticleSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
int ParticleManager::getTotalParticleCount() const {
|
||||
int total = 0;
|
||||
for (const auto& [name, system] : systems_) {
|
||||
if (system) {
|
||||
total += system->getParticleCount();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
int ParticleManager::getSystemCount() const {
|
||||
return static_cast<int>(systems_.size());
|
||||
}
|
||||
|
||||
std::vector<std::string> ParticleManager::getSystemNames() const {
|
||||
std::vector<std::string> names;
|
||||
names.reserve(systems_.size());
|
||||
for (const auto& [name, _] : systems_) {
|
||||
names.push_back(name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
void ParticleManager::printSystemStats() const {
|
||||
Utils::log("\n=== Particle Manager Statistics ===");
|
||||
Utils::log("Total Systems: " + std::to_string(getSystemCount()));
|
||||
Utils::log("Total Particles: " + std::to_string(getTotalParticleCount()));
|
||||
Utils::log("Global Paused: " + std::string(globalPaused_ ? "Yes" : "No"));
|
||||
Utils::log("Global Speed: " + std::to_string(globalSimulationSpeed_) + "x");
|
||||
|
||||
for (const auto& [name, system] : systems_) {
|
||||
if (system) {
|
||||
Utils::log("\nSystem: " + name);
|
||||
Utils::log(" Dimensions: " + std::to_string(system->getWidth()) + "x" + std::to_string(system->getHeight()));
|
||||
Utils::log(" Particles: " + std::to_string(system->getParticleCount()));
|
||||
}
|
||||
}
|
||||
Utils::log("================================");
|
||||
}
|
||||
|
||||
bool ParticleManager::saveSystemToFile(const std::string& systemName, const std::string& filename) {
|
||||
auto system = getParticleSystem(systemName);
|
||||
if (!system) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This would need serialization methods in ParticleSystem
|
||||
// For now, just log the operation
|
||||
logSystemOperation("save requested to " + filename, systemName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParticleManager::loadSystemFromFile(const std::string& systemName, const std::string& filename) {
|
||||
auto system = getParticleSystem(systemName);
|
||||
if (!system) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This would need deserialization methods in ParticleSystem
|
||||
// For now, just log the operation
|
||||
logSystemOperation("load requested from " + filename, systemName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParticleManager::exportSystemsAsImage(const std::string& systemName, const std::string& filename) {
|
||||
auto system = getParticleSystem(systemName);
|
||||
if (!system) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This would need image export methods in ParticleSystem
|
||||
// For now, just log the operation
|
||||
logSystemOperation("export requested to " + filename, systemName);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
void ParticleManager::updateSystem(const std::string& name, float deltaTime) {
|
||||
auto system = getParticleSystem(name);
|
||||
if (system) {
|
||||
system->update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
bool ParticleManager::validateSystemName(const std::string& name) const {
|
||||
return !name.empty() && name.length() <= 64 &&
|
||||
name.find_first_of("\\/:*?\"<>|") == std::string::npos;
|
||||
}
|
||||
|
||||
std::string ParticleManager::generateUniqueSystemName(const std::string& baseName) const {
|
||||
static int counter = 0;
|
||||
std::string name;
|
||||
do {
|
||||
name = "system_" + std::to_string(counter++);
|
||||
} while (systems_.find(name) != systems_.end());
|
||||
return name;
|
||||
}
|
||||
|
||||
void ParticleManager::logSystemOperation(const std::string& operation, const std::string& systemName) {
|
||||
if (Config::LOGGING_ENABLED) {
|
||||
std::string message = "[ParticleManager] " + operation;
|
||||
if (!systemName.empty()) {
|
||||
message += " (" + systemName + ")";
|
||||
}
|
||||
Utils::log(message);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::applyMaterialPhysics(Particle& particle, Particle::MaterialType material) {
|
||||
// Apply material-specific physics
|
||||
switch (particle.material) {
|
||||
case Particle::MaterialType::WATER:
|
||||
// Water flows more easily
|
||||
particle.viscosity = 0.1f;
|
||||
break;
|
||||
case Particle::MaterialType::SAND:
|
||||
// Sand has more friction
|
||||
particle.friction = 0.8f;
|
||||
break;
|
||||
case Particle::MaterialType::ICE:
|
||||
// Ice melts over time
|
||||
if (particle.temperature > 0.0f) {
|
||||
particle.temperature -= 0.1f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::handleParticleCollision(Particle& p1, Particle& p2) {
|
||||
// Simple collision response
|
||||
if (p1.material == Particle::MaterialType::WATER &&
|
||||
p2.material == Particle::MaterialType::SAND) {
|
||||
// Water can displace sand
|
||||
if (p1.density < p2.density) {
|
||||
// Swap positions
|
||||
std::swap(p1.position, p2.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleManager::updateParticleState(Particle& particle) {
|
||||
// Update particle state based on conditions
|
||||
if (particle.velocity.length() < 0.1f && particle.canFall) {
|
||||
particle.state = Particle::State::SETTLED;
|
||||
} else if (particle.velocity.length() > 0.5f && particle.canFlow) {
|
||||
particle.state = Particle::State::FLOWING;
|
||||
} else if (particle.temperature > 100.0f) {
|
||||
particle.state = Particle::State::GAS;
|
||||
}
|
||||
}
|
83
src/particles/ParticleManager.h
Normal file
83
src/particles/ParticleManager.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include "Particle.h"
|
||||
#include "ParticleSystem.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
// ParticleManager class header - high-level particle management and coordination
|
||||
class ParticleManager {
|
||||
public:
|
||||
ParticleManager();
|
||||
~ParticleManager() = default;
|
||||
|
||||
// Disable copying
|
||||
ParticleManager(const ParticleManager&) = delete;
|
||||
ParticleManager& operator=(const ParticleManager&) = delete;
|
||||
|
||||
// Core management methods
|
||||
void initialize(int width, int height);
|
||||
void update(float deltaTime);
|
||||
void cleanup();
|
||||
|
||||
// Particle system management
|
||||
std::shared_ptr<ParticleSystem> createParticleSystem(int width, int height, const std::string& name);
|
||||
void destroyParticleSystem(const std::string& name);
|
||||
std::shared_ptr<ParticleSystem> getParticleSystem(const std::string& name);
|
||||
std::shared_ptr<ParticleSystem> getDefaultParticleSystem();
|
||||
|
||||
// Global particle operations
|
||||
void addParticleToSystem(const std::string& systemName, const Particle& particle);
|
||||
void addParticleToDefault(const Particle& particle);
|
||||
void clearAllSystems();
|
||||
void resetAllSystems();
|
||||
|
||||
// Material management across systems
|
||||
void placeMaterialInSystem(const std::string& systemName, int x, int y, Particle::MaterialType material);
|
||||
void placeMaterialInDefault(int x, int y, Particle::MaterialType material);
|
||||
void floodFill(const std::string& systemName, int startX, int startY, Particle::MaterialType material);
|
||||
|
||||
// Simulation control
|
||||
void pauseSimulation(const std::string& systemName);
|
||||
void resumeSimulation(const std::string& systemName);
|
||||
void setSimulationSpeed(float speed, const std::string& systemName);
|
||||
bool isSimulationPaused(const std::string& systemName) const;
|
||||
|
||||
// Statistics and monitoring
|
||||
int getTotalParticleCount() const;
|
||||
int getSystemCount() const;
|
||||
std::vector<std::string> getSystemNames() const;
|
||||
void printSystemStats() const;
|
||||
|
||||
// File I/O
|
||||
bool saveSystemToFile(const std::string& systemName, const std::string& filename);
|
||||
bool loadSystemFromFile(const std::string& systemName, const std::string& filename);
|
||||
bool exportSystemsAsImage(const std::string& systemName, const std::string& filename);
|
||||
|
||||
private:
|
||||
// Core data structures
|
||||
std::unordered_map<std::string, std::shared_ptr<ParticleSystem>> systems_;
|
||||
std::shared_ptr<ParticleSystem> defaultSystem_;
|
||||
|
||||
// System state
|
||||
bool initialized_;
|
||||
bool globalPaused_;
|
||||
float globalSimulationSpeed_;
|
||||
|
||||
// Default system settings
|
||||
int defaultWidth_;
|
||||
int defaultHeight_;
|
||||
|
||||
// Helper methods
|
||||
void updateSystem(const std::string& systemName, float deltaTime);
|
||||
bool validateSystemName(const std::string& name) const;
|
||||
std::string generateUniqueSystemName(const std::string& baseName) const;
|
||||
void logSystemOperation(const std::string& operation, const std::string& systemName = "");
|
||||
|
||||
// Material interaction helpers
|
||||
void applyMaterialPhysics(Particle& particle, Particle::MaterialType material);
|
||||
void handleParticleCollision(Particle& p1, Particle& p2);
|
||||
void updateParticleState(Particle& particle);
|
||||
};
|
276
src/particles/ParticleSystem.cpp
Normal file
276
src/particles/ParticleSystem.cpp
Normal file
|
@ -0,0 +1,276 @@
|
|||
#include "ParticleSystem.h"
|
||||
#include "../utils/Utils.h"
|
||||
#include <algorithm>
|
||||
|
||||
// ParticleSystem class implementation - define system methods here
|
||||
|
||||
ParticleSystem::ParticleSystem(int width, int height)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
, particleCount_(0) {
|
||||
initializeGrid();
|
||||
}
|
||||
|
||||
void ParticleSystem::initializeGrid() {
|
||||
grid_.resize(height_);
|
||||
for (auto& row : grid_) {
|
||||
row.resize(width_);
|
||||
// Initialize each cell to nullptr
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSystem::update(float deltaTime) {
|
||||
// Update all active particles
|
||||
for (auto& particle : particles_) {
|
||||
if (particle && particle->active) {
|
||||
updateParticlePosition(*particle, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up inactive particles
|
||||
particles_.erase(
|
||||
std::remove_if(particles_.begin(), particles_.end(),
|
||||
[](const std::unique_ptr<Particle>& p) { return !p->active; }),
|
||||
particles_.end()
|
||||
);
|
||||
}
|
||||
|
||||
void ParticleSystem::step() {
|
||||
// Single physics step - apply gravity and friction
|
||||
for (auto& particle : particles_) {
|
||||
if (particle && particle->active && particle->canFall) {
|
||||
// Check if particle would cross boundary before attempting movement
|
||||
if (!wouldCrossBoundary(*particle, {0, 1})) {
|
||||
// Try to move particle down
|
||||
if (!tryMoveParticle(*particle, {0, 1})) {
|
||||
// If particle can't move down, try diagonal moves
|
||||
if (particle->canFlow) {
|
||||
if (!wouldCrossBoundary(*particle, {-1, 1}) && !tryMoveParticle(*particle, {-1, 1})) {
|
||||
if (!wouldCrossBoundary(*particle, {1, 1})) {
|
||||
tryMoveParticle(*particle, {1, 1});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSystem::addParticle(const Particle& particle) {
|
||||
int x = static_cast<int>(particle.position.x);
|
||||
int y = static_cast<int>(particle.position.y);
|
||||
|
||||
// Ensure particle is within boundaries
|
||||
if (x < 0 || x >= width_ || y < 0 || y >= height_) {
|
||||
Utils::log("Cannot add particle outside boundaries at (" + std::to_string(x) + ", " + std::to_string(y) + ")");
|
||||
return; // Particle can't be placed outside boundaries
|
||||
}
|
||||
|
||||
if (!isEmpty(x, y)) {
|
||||
return; // Position already occupied
|
||||
}
|
||||
|
||||
auto newParticle = std::make_unique<Particle>(particle);
|
||||
particles_.push_back(std::move(newParticle));
|
||||
grid_[y][x] = std::move(newParticle);
|
||||
particleCount_++;
|
||||
}
|
||||
|
||||
void ParticleSystem::removeParticle(int x, int y) {
|
||||
// Ensure position is within boundaries
|
||||
if (x < 0 || x >= width_ || y < 0 || y >= height_) {
|
||||
Utils::log("Cannot remove particle outside boundaries at (" + std::to_string(x) + ", " + std::to_string(y) + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if (grid_[y][x]) {
|
||||
grid_[y][x]->active = false;
|
||||
grid_[y][x].reset();
|
||||
particleCount_--;
|
||||
}
|
||||
}
|
||||
|
||||
Particle* ParticleSystem::getParticle(int x, int y) {
|
||||
// Ensure position is within boundaries
|
||||
if (x < 0 || x >= width_ || y < 0 || y >= height_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return grid_[y][x].get();
|
||||
}
|
||||
|
||||
bool ParticleSystem::isEmpty(int x, int y) const {
|
||||
// Ensure position is within boundaries
|
||||
if (x < 0 || x >= width_ || y < 0 || y >= height_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return grid_[y][x] == nullptr;
|
||||
}
|
||||
|
||||
void ParticleSystem::placeMaterial(int x, int y, Particle::MaterialType material) {
|
||||
// Ensure position is within boundaries
|
||||
if (x < 0 || x >= width_ || y < 0 || y >= height_) {
|
||||
Utils::log("Cannot place material outside boundaries at (" + std::to_string(x) + ", " + std::to_string(y) + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isEmpty(x, y)) {
|
||||
return; // Position already occupied
|
||||
}
|
||||
|
||||
Particle newParticle(material, {static_cast<float>(x), static_cast<float>(y)});
|
||||
addParticle(newParticle);
|
||||
}
|
||||
|
||||
bool ParticleSystem::isValidPosition(int x, int y) const {
|
||||
// Check if position is within boundaries
|
||||
return x >= 0 && x < width_ && y >= 0 && y < height_;
|
||||
}
|
||||
|
||||
void ParticleSystem::updateParticlePosition(Particle& particle, float deltaTime) {
|
||||
// Apply gravity
|
||||
particle.acceleration.y += particle.gravity;
|
||||
|
||||
// Update velocity
|
||||
particle.velocity += particle.acceleration * deltaTime;
|
||||
|
||||
// Limit terminal velocity
|
||||
if (particle.velocity.y > 10.0f) {
|
||||
particle.velocity.y = 10.0f;
|
||||
}
|
||||
|
||||
// Update position
|
||||
particle.position += particle.velocity * deltaTime;
|
||||
|
||||
// Apply boundary constraints
|
||||
applyBoundaryConstraints(particle);
|
||||
|
||||
// Additional boundary collision detection
|
||||
if (particle.position.x < 0 || particle.position.x >= width_ ||
|
||||
particle.position.y < 0 || particle.position.y >= height_) {
|
||||
// Particle somehow got outside boundaries, clamp it back
|
||||
clampParticleToBoundaries(particle);
|
||||
|
||||
// Log boundary violation for debugging
|
||||
Utils::log("Particle boundary violation corrected at (" + std::to_string(particle.position.x) + ", " + std::to_string(particle.position.y) + ")");
|
||||
}
|
||||
|
||||
// Reset acceleration
|
||||
particle.acceleration = {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
bool ParticleSystem::tryMoveParticle(Particle& particle, const glm::vec2& direction) {
|
||||
int newX = static_cast<int>(particle.position.x + direction.x);
|
||||
int newY = static_cast<int>(particle.position.y + direction.y);
|
||||
|
||||
// Check boundary constraints
|
||||
if (newX < 0 || newX >= width_ || newY < 0 || newY >= height_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isEmpty(newX, newY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current grid position
|
||||
int currentX = static_cast<int>(particle.position.x);
|
||||
int currentY = static_cast<int>(particle.position.y);
|
||||
|
||||
if (!isValidPosition(currentX, currentY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move particle in grid
|
||||
grid_[newY][newX] = std::move(grid_[currentY][currentX]);
|
||||
grid_[currentY][currentX].reset();
|
||||
|
||||
// Update particle position
|
||||
particle.position = {static_cast<float>(newX), static_cast<float>(newY)};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ParticleSystem::applyBoundaryConstraints(Particle& particle) {
|
||||
// Use the dedicated boundary collision handler
|
||||
handleBoundaryCollision(particle);
|
||||
}
|
||||
|
||||
bool ParticleSystem::isAtBoundary(const Particle& particle) const {
|
||||
return particle.position.x <= 0 ||
|
||||
particle.position.x >= width_ - 1 ||
|
||||
particle.position.y <= 0 ||
|
||||
particle.position.y >= height_ - 1;
|
||||
}
|
||||
|
||||
bool ParticleSystem::wouldCrossBoundary(const Particle& particle, const glm::vec2& direction) const {
|
||||
glm::vec2 newPosition = particle.position + direction;
|
||||
return newPosition.x < 0 ||
|
||||
newPosition.x >= width_ ||
|
||||
newPosition.y < 0 ||
|
||||
newPosition.y >= height_;
|
||||
}
|
||||
|
||||
void ParticleSystem::handleBoundaryCollision(Particle& particle) {
|
||||
// Handle collision with left wall
|
||||
if (particle.position.x < 0) {
|
||||
particle.position.x = 0;
|
||||
particle.velocity.x = 0;
|
||||
particle.acceleration.x = 0;
|
||||
}
|
||||
|
||||
// Handle collision with right wall
|
||||
if (particle.position.x >= width_) {
|
||||
particle.position.x = static_cast<float>(width_ - 1);
|
||||
particle.velocity.x = 0;
|
||||
particle.acceleration.x = 0;
|
||||
}
|
||||
|
||||
// Handle collision with floor
|
||||
if (particle.position.y >= height_) {
|
||||
particle.position.y = static_cast<float>(height_ - 1);
|
||||
particle.velocity.y = 0;
|
||||
particle.acceleration.y = 0;
|
||||
particle.state = Particle::State::SETTLED;
|
||||
}
|
||||
|
||||
// Handle collision with ceiling
|
||||
if (particle.position.y < 0) {
|
||||
particle.position.y = 0;
|
||||
particle.velocity.y = 0;
|
||||
particle.acceleration.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ParticleSystem::isMovingTowardsBoundary(const Particle& particle) const {
|
||||
// Check if particle is moving towards any boundary
|
||||
if (particle.velocity.x > 0 && particle.position.x >= width_ - 1) return true; // Moving right towards right wall
|
||||
if (particle.velocity.x < 0 && particle.position.x <= 0) return true; // Moving left towards left wall
|
||||
if (particle.velocity.y > 0 && particle.position.y >= height_ - 1) return true; // Moving down towards floor
|
||||
if (particle.velocity.y < 0 && particle.position.y <= 0) return true; // Moving up towards ceiling
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ParticleSystem::clampParticleToBoundaries(Particle& particle) {
|
||||
// Clamp particle position to boundaries
|
||||
particle.position.x = std::max(0.0f, std::min(particle.position.x, static_cast<float>(width_ - 1)));
|
||||
particle.position.y = std::max(0.0f, std::min(particle.position.y, static_cast<float>(height_ - 1)));
|
||||
|
||||
// Stop velocity if particle is at boundary
|
||||
if (particle.position.x <= 0 || particle.position.x >= width_ - 1) {
|
||||
particle.velocity.x = 0;
|
||||
particle.acceleration.x = 0;
|
||||
}
|
||||
|
||||
if (particle.position.y <= 0 || particle.position.y >= height_ - 1) {
|
||||
particle.velocity.y = 0;
|
||||
particle.acceleration.y = 0;
|
||||
|
||||
// If particle is at floor, mark it as settled
|
||||
if (particle.position.y >= height_ - 1) {
|
||||
particle.state = Particle::State::SETTLED;
|
||||
}
|
||||
}
|
||||
}
|
55
src/particles/ParticleSystem.h
Normal file
55
src/particles/ParticleSystem.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include "Particle.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// ParticleSystem class header - manages collections of particles
|
||||
|
||||
class ParticleSystem {
|
||||
public:
|
||||
ParticleSystem(int width, int height);
|
||||
~ParticleSystem() = default;
|
||||
|
||||
// Disable copying
|
||||
ParticleSystem(const ParticleSystem&) = delete;
|
||||
ParticleSystem& operator=(const ParticleSystem&) = delete;
|
||||
|
||||
// Core simulation methods
|
||||
void update(float deltaTime);
|
||||
void step();
|
||||
|
||||
// Particle management methods
|
||||
void addParticle(const Particle& particle);
|
||||
void removeParticle(int x, int y);
|
||||
bool isEmpty(int x, int y) const;
|
||||
|
||||
// Material placement methods
|
||||
void placeMaterial(int x, int y, Particle::MaterialType material);
|
||||
bool isValidPosition(int x, int y) const;
|
||||
|
||||
// Getter methods
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
int getParticleCount() const { return particleCount_; }
|
||||
Particle* getParticle(int x, int y);
|
||||
const std::vector<std::unique_ptr<Particle>>& getAllParticles() const { return particles_; }
|
||||
|
||||
private:
|
||||
int width_;
|
||||
int height_;
|
||||
std::vector<std::vector<std::unique_ptr<Particle>>> grid_;
|
||||
std::vector<std::unique_ptr<Particle>> particles_;
|
||||
int particleCount_;
|
||||
|
||||
// Helper methods
|
||||
void initializeGrid();
|
||||
void updateParticlePosition(Particle& particle, float deltaTime);
|
||||
bool tryMoveParticle(Particle& particle, const glm::vec2& direction);
|
||||
void applyBoundaryConstraints(Particle& particle);
|
||||
bool isAtBoundary(const Particle& particle) const;
|
||||
bool wouldCrossBoundary(const Particle& particle, const glm::vec2& direction) const;
|
||||
void handleBoundaryCollision(Particle& particle);
|
||||
bool isMovingTowardsBoundary(const Particle& particle) const;
|
||||
void clampParticleToBoundaries(Particle& particle);
|
||||
};
|
|
@ -1,4 +1,6 @@
|
|||
#include "Renderer.h"
|
||||
#include "../particles/Particle.h"
|
||||
#include "../utils/Utils.h"
|
||||
#include <iostream>
|
||||
|
||||
Renderer::Renderer(SDL_Window* window)
|
||||
|
@ -71,3 +73,122 @@ void Renderer::fillRect(const SDL_Rect& rect) {
|
|||
SDL_RenderFillRect(renderer_, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::renderParticles(const std::vector<std::unique_ptr<Particle>>& particles, int screenWidth, int screenHeight) {
|
||||
if (!renderer_) return;
|
||||
|
||||
// Set default sand color
|
||||
setDrawColor(194, 178, 128, 255);
|
||||
|
||||
int activeParticles = 0;
|
||||
for (const auto& particle : particles) {
|
||||
if (particle && particle->active) {
|
||||
// Check if particle is at boundary and change color accordingly
|
||||
if (particle->position.x <= 0 || particle->position.x >= screenWidth - 1 ||
|
||||
particle->position.y <= 0 || particle->position.y >= screenHeight - 1) {
|
||||
// Boundary particles get a different color (red)
|
||||
setDrawColor(255, 0, 0, 255);
|
||||
} else {
|
||||
// Regular sand color
|
||||
setDrawColor(194, 178, 128, 255);
|
||||
}
|
||||
|
||||
// Draw each particle as a small filled rectangle
|
||||
SDL_Rect rect = {
|
||||
static_cast<int>(particle->position.x - particle->size / 2),
|
||||
static_cast<int>(particle->position.y - particle->size / 2),
|
||||
static_cast<int>(particle->size),
|
||||
static_cast<int>(particle->size)
|
||||
};
|
||||
fillRect(rect);
|
||||
activeParticles++;
|
||||
}
|
||||
}
|
||||
|
||||
// Performance optimization: reduce logging frequency to every 120 frames (~2 seconds at 60 FPS)
|
||||
static int frameCount = 0;
|
||||
if (++frameCount % 120 == 0) {
|
||||
Utils::log("Rendering " + std::to_string(activeParticles) + " active particles");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::renderParticlesWithMaterials(const std::vector<std::unique_ptr<Particle>>& particles, int screenWidth, int screenHeight) {
|
||||
if (!renderer_) return;
|
||||
|
||||
int activeParticles = 0;
|
||||
for (const auto& particle : particles) {
|
||||
if (particle && particle->active) {
|
||||
// Set color based on material type
|
||||
switch (particle->material) {
|
||||
case Particle::MaterialType::SAND:
|
||||
setDrawColor(194, 178, 128, 255); // Sand color
|
||||
break;
|
||||
case Particle::MaterialType::WATER:
|
||||
setDrawColor(0, 100, 255, 255); // Blue
|
||||
break;
|
||||
case Particle::MaterialType::ROCK:
|
||||
setDrawColor(128, 128, 128, 255); // Gray
|
||||
break;
|
||||
case Particle::MaterialType::ICE:
|
||||
setDrawColor(200, 220, 255, 255); // Light blue
|
||||
break;
|
||||
case Particle::MaterialType::METAL:
|
||||
setDrawColor(192, 192, 192, 255); // Silver
|
||||
break;
|
||||
default:
|
||||
setDrawColor(194, 178, 128, 255); // Default sand color
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if particle is at boundary and override color
|
||||
if (particle->position.x <= 0 || particle->position.x >= screenWidth - 1 ||
|
||||
particle->position.y <= 0 || particle->position.y >= screenHeight - 1) {
|
||||
// Boundary particles get a different color (red)
|
||||
setDrawColor(255, 0, 0, 255);
|
||||
}
|
||||
|
||||
// Draw each particle as a small filled rectangle
|
||||
SDL_Rect rect = {
|
||||
static_cast<int>(particle->position.x - particle->size / 2),
|
||||
static_cast<int>(particle->position.y - particle->size / 2),
|
||||
static_cast<int>(particle->size),
|
||||
static_cast<int>(particle->size)
|
||||
};
|
||||
fillRect(rect);
|
||||
activeParticles++;
|
||||
}
|
||||
}
|
||||
|
||||
// Performance optimization: reduce logging frequency to every 120 frames (~2 seconds at 60 FPS)
|
||||
static int frameCount = 0;
|
||||
if (++frameCount % 120 == 0) {
|
||||
Utils::log("Rendering " + std::to_string(activeParticles) + " active particles with materials");
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::setParticleColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
||||
setDrawColor(r, g, b, a);
|
||||
}
|
||||
|
||||
void Renderer::drawBoundaries(int screenWidth, int screenHeight, bool showBoundaries) {
|
||||
if (!renderer_ || !showBoundaries) return;
|
||||
|
||||
// Set boundary color (dark gray)
|
||||
setDrawColor(64, 64, 64, 255);
|
||||
|
||||
// Draw floor (bottom boundary)
|
||||
SDL_Rect floorRect = {0, screenHeight - 2, screenWidth, 2};
|
||||
fillRect(floorRect);
|
||||
|
||||
// Draw left wall
|
||||
SDL_Rect leftWallRect = {0, 0, 2, screenHeight};
|
||||
fillRect(leftWallRect);
|
||||
|
||||
// Draw right wall
|
||||
SDL_Rect rightWallRect = {screenWidth - 2, 0, 2, screenHeight};
|
||||
fillRect(rightWallRect);
|
||||
|
||||
// Draw ceiling (optional - top boundary)
|
||||
SDL_Rect ceilingRect = {0, 0, screenWidth, 2};
|
||||
fillRect(ceilingRect);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "../core/Common.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <vector>
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
|
@ -30,6 +31,14 @@ public:
|
|||
void drawRect(const SDL_Rect& rect);
|
||||
void fillRect(const SDL_Rect& rect);
|
||||
|
||||
// Particle rendering
|
||||
void renderParticles(const std::vector<std::unique_ptr<class Particle>>& particles, int screenWidth, int screenHeight);
|
||||
void renderParticlesWithMaterials(const std::vector<std::unique_ptr<class Particle>>& particles, int screenWidth, int screenHeight);
|
||||
void setParticleColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a);
|
||||
|
||||
// Boundary rendering
|
||||
void drawBoundaries(int screenWidth, int screenHeight, bool showBoundaries = true);
|
||||
|
||||
private:
|
||||
SDL_Renderer* renderer_;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue