Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@
# Set CMake policies for modern behavior
cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY support

# Set consistent runtime library for all targets
# Set consistent runtime library for all targets using modern CMake (CMP0091)
if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
# Force specific runtime library settings to avoid conflicts
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MDd")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MD")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
# Note: With CMP0091 NEW, CMAKE_MSVC_RUNTIME_LIBRARY handles /MD and /MDd
# automatically. Do NOT manually add /MD or /MDd flags as they conflict.
endif()

# ---------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion Spark Engine/Source/Core/SparkEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Timer;
* Windows application instance handle used throughout the engine
* for Win32 API calls and window management.
*/
extern HINSTANCE hInst;
extern HINSTANCE g_hInst;

/**
* @brief Global graphics engine instance
Expand Down
7 changes: 4 additions & 3 deletions Spark Engine/Source/Game/Console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ bool Console::HandleKeyDown(WPARAM key)
return true;

case VK_RETURN:
ASSERT_MSG(!inputLine.empty(), "Executing empty command");
ExecuteCommand(inputLine);
inputLine.clear();
if (!inputLine.empty()) {
ExecuteCommand(inputLine);
inputLine.clear();
}
return true;

case VK_ESCAPE:
Expand Down
49 changes: 20 additions & 29 deletions Spark Engine/Source/Game/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,12 @@ void Game::Shutdown()
--------------------------------------------------------------*/
void Game::Update(float dt)
{
// **FIXED: Removed all per-frame logging to prevent console spam**

if (m_isPaused) {
return;
}

dt *= m_timeScale;

HandleInput(dt);
UpdateCamera(dt);
UpdateGameObjects(dt);
Expand Down Expand Up @@ -216,12 +216,14 @@ void Game::Render()
}

// **CRITICAL: This is the ONLY place BeginFrame/EndFrame should be called**
bool frameStarted = false;
try {
m_graphics->BeginFrame();

frameStarted = true;

// Collect all renderable objects for unified rendering
std::vector<GameObject*> renderableObjects;

// Add game objects
for (auto& obj : m_gameObjects) {
if (obj && obj->IsActive() && obj->IsVisible()) {
Expand All @@ -241,51 +243,40 @@ void Game::Render()
// **UNIFIED RENDERING: Use the complete modern graphics pipeline**
XMMATRIX view = m_camera->GetViewMatrix();
XMMATRIX proj = m_camera->GetProjectionMatrix();

// Call the unified RenderScene method
m_graphics->RenderScene(view, proj, renderableObjects);

// **ENHANCED: Render player weapons in first-person view**
if (m_player) {
m_player->Render(view, proj);
}

if (m_projectilePool) {
m_projectilePool->Render(view, proj);
}

// **SOLUTION: Single console rendering location - this is the ONLY place console renders**
if (g_console.IsVisible()) {
g_console.Render(m_graphics->GetContext());
}

// **CRITICAL: Always EndFrame, even if errors occurred above**
m_graphics->EndFrame();


} catch (const std::exception& e) {
// Ensure we always call EndFrame even if rendering fails
try {
m_graphics->EndFrame();
} catch (...) {
// If EndFrame also fails, log it but don't throw again
LOG_TO_CONSOLE_IMMEDIATE(L"Critical: EndFrame failed during error recovery", L"ERROR");
}

// Log the original error
std::string errorMsg = "Rendering error: " + std::string(e.what());
std::wstring wErrorMsg(errorMsg.begin(), errorMsg.end());
LOG_TO_CONSOLE_IMMEDIATE(wErrorMsg, L"ERROR");

} catch (...) {
// Ensure we always call EndFrame even if rendering fails
LOG_TO_CONSOLE_IMMEDIATE(L"Unknown rendering error occurred", L"ERROR");
}

// Always call EndFrame exactly once if BeginFrame succeeded
if (frameStarted) {
try {
m_graphics->EndFrame();
} catch (...) {
// If EndFrame also fails, log it but don't throw again
LOG_TO_CONSOLE_IMMEDIATE(L"Critical: EndFrame failed during error recovery", L"ERROR");
}

LOG_TO_CONSOLE_IMMEDIATE(L"Unknown rendering error occurred", L"ERROR");
}
}

Expand Down Expand Up @@ -617,9 +608,9 @@ bool Game::DeleteObject(size_t index)
{
LOG_TO_CONSOLE_IMMEDIATE(L"Deleting object via console integration", L"INFO");

if (index >= m_gameObjects.size()) {
std::wstring errorMsg = L"Invalid object index: " + std::to_wstring(index) +
L" (max: " + std::to_wstring(m_gameObjects.size() - 1) + L")";
if (m_gameObjects.empty() || index >= m_gameObjects.size()) {
std::wstring errorMsg = L"Invalid object index: " + std::to_wstring(index) +
L" (total objects: " + std::to_wstring(m_gameObjects.size()) + L")";
LOG_TO_CONSOLE_IMMEDIATE(errorMsg, L"ERROR");
return false;
}
Expand Down
11 changes: 6 additions & 5 deletions Spark Engine/Source/Game/ModelObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ void ModelObject::Render(const DirectX::XMMATRIX& view, const DirectX::XMMATRIX&

ASSERT(m_context != nullptr);

// ? ENHANCED: Calculate world transformation matrix
DirectX::XMMATRIX world = DirectX::XMMatrixIdentity();

// Apply position (access m_position from GameObject base class)
// Build full world matrix with scale, rotation, and translation
DirectX::XMFLOAT3 pos = GetPosition();
world = DirectX::XMMatrixTranslation(pos.x, pos.y, pos.z);
DirectX::XMFLOAT3 rot = GetRotation();
DirectX::XMFLOAT3 scl = GetScale();
DirectX::XMMATRIX world = DirectX::XMMatrixScaling(scl.x, scl.y, scl.z) *
DirectX::XMMatrixRotationRollPitchYaw(rot.x, rot.y, rot.z) *
DirectX::XMMatrixTranslation(pos.x, pos.y, pos.z);

// ? ENHANCED: Get graphics engine reference (you may need to adjust this based on how you access it)
// For now, we'll try to get it from a global or pass it down from the render system
Expand Down
38 changes: 20 additions & 18 deletions Spark Engine/Source/Game/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ HRESULT Player::Initialize(ID3D11Device* device,

if (!m_projectilePool)
{
m_projectilePool = new ProjectilePool(50);
m_ownedProjectilePool = std::make_unique<ProjectilePool>(50);
m_projectilePool = m_ownedProjectilePool.get();
ASSERT_NOT_NULL(m_projectilePool);
hr = m_projectilePool->Initialize(device, context);
LOG_TO_CONSOLE_IMMEDIATE(L"Player projectile pool initialized. HR=0x" + std::to_wstring(hr), L"INFO");
Expand Down Expand Up @@ -339,16 +340,17 @@ void Player::OnHit(GameObject* target)
{
LOG_TO_CONSOLE(L"Player::OnHit called.", L"OPERATION");
ASSERT_NOT_NULL(target);
TakeDamage(m_currentWeapon.Damage);
LOG_TO_CONSOLE(L"Player hit target.", L"INFO");
// Player was hit by target - take a default collision damage, not own weapon damage
TakeDamage(10.0f);
LOG_TO_CONSOLE(L"Player hit by target.", L"INFO");
}

void Player::OnHitWorld(const XMFLOAT3& hitPoint, const XMFLOAT3& normal)
{
LOG_TO_CONSOLE(L"Player::OnHitWorld called.", L"OPERATION");
ASSERT_MSG(std::isfinite(hitPoint.x) && std::isfinite(hitPoint.y) && std::isfinite(hitPoint.z),
"Invalid hitPoint");
TakeDamage(m_currentWeapon.Damage);
// World collision - no damage from hitting world geometry
LOG_TO_CONSOLE(L"Player hit world.", L"INFO");
}

Expand Down Expand Up @@ -521,7 +523,7 @@ XMFLOAT3 Player::CalculateFireDirection()

void Player::Console_SetHealth(float health)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
m_health = std::max(0.0f, std::min(m_maxHealth, health));
if (m_health <= 0.0f) {
SetActive(false);
Expand All @@ -534,15 +536,15 @@ void Player::Console_SetHealth(float health)

void Player::Console_SetArmor(float armor)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
m_armor = std::max(0.0f, std::min(m_maxArmor, armor));
NotifyStateChange();
LOG_TO_CONSOLE_IMMEDIATE(L"Player armor set to " + std::to_wstring(m_armor) + L" via console", L"SUCCESS");
}

void Player::Console_SetMaxHealth(float maxHealth)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
if (maxHealth > 0.0f && maxHealth <= 9999.0f) {
m_maxHealth = maxHealth;
// Ensure current health doesn't exceed new maximum
Expand All @@ -558,7 +560,7 @@ void Player::Console_SetMaxHealth(float maxHealth)

void Player::Console_SetSpeed(float speed)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
if (speed > 0.0f && speed <= 100.0f) {
m_speed = speed;
NotifyStateChange();
Expand All @@ -570,7 +572,7 @@ void Player::Console_SetSpeed(float speed)

void Player::Console_SetJumpHeight(float height)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
if (height > 0.0f && height <= 50.0f) {
m_jumpHeight = height;
NotifyStateChange();
Expand All @@ -582,7 +584,7 @@ void Player::Console_SetJumpHeight(float height)

void Player::Console_SetPosition(float x, float y, float z)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
DirectX::XMFLOAT3 newPos = { x, y, z };
SetPosition(newPos);

Expand All @@ -601,31 +603,31 @@ void Player::Console_SetPosition(float x, float y, float z)

void Player::Console_SetGodMode(bool enabled)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
m_godModeEnabled = enabled;
NotifyStateChange();
LOG_TO_CONSOLE_IMMEDIATE(L"Player god mode " + std::wstring(enabled ? L"enabled" : L"disabled") + L" via console", L"SUCCESS");
}

void Player::Console_SetNoclip(bool enabled)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
m_noclipEnabled = enabled;
NotifyStateChange();
LOG_TO_CONSOLE_IMMEDIATE(L"Player noclip " + std::wstring(enabled ? L"enabled" : L"disabled") + L" via console", L"SUCCESS");
}

void Player::Console_SetInfiniteAmmo(bool enabled)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
m_infiniteAmmoEnabled = enabled;
NotifyStateChange();
LOG_TO_CONSOLE_IMMEDIATE(L"Player infinite ammo " + std::wstring(enabled ? L"enabled" : L"disabled") + L" via console", L"SUCCESS");
}

void Player::Console_GiveAmmo(int amount)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
if (amount > 0 && amount <= 9999) {
m_currentAmmo = std::min(m_currentWeapon.MagazineSize, m_currentAmmo + amount);
NotifyStateChange();
Expand All @@ -638,7 +640,7 @@ void Player::Console_GiveAmmo(int amount)

void Player::Console_ChangeWeapon(WeaponType weaponType)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
ChangeWeapon(weaponType);
NotifyStateChange();
LOG_TO_CONSOLE_IMMEDIATE(L"Player weapon changed via console", L"SUCCESS");
Expand All @@ -651,14 +653,14 @@ Player::PlayerState Player::Console_GetState() const

void Player::Console_RegisterStateCallback(std::function<void(const PlayerState&)> callback)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
m_stateCallback = callback;
LOG_TO_CONSOLE_IMMEDIATE(L"Player state callback registered", L"INFO");
}

void Player::Console_ApplyPhysicsSettings(float gravity, float friction)
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
if (gravity >= -100.0f && gravity <= 100.0f) {
m_gravityForce = gravity;
}
Expand All @@ -679,7 +681,7 @@ void Player::NotifyStateChange()

Player::PlayerState Player::GetStateThreadSafe() const
{
std::lock_guard<std::mutex> lock(m_stateMutex);
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
PlayerState state;
state.health = m_health;
state.maxHealth = m_maxHealth;
Expand Down
3 changes: 2 additions & 1 deletion Spark Engine/Source/Game/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,12 +432,13 @@ class Player : public GameObject

// Console callback system
std::function<void(const PlayerState&)> m_stateCallback;
mutable std::mutex m_stateMutex; ///< Thread safety for state access
mutable std::recursive_mutex m_stateMutex; ///< Thread safety for state access

// External references (not owned)
SparkEngineCamera* m_camera{ nullptr }; ///< Reference to camera system
InputManager* m_input{ nullptr }; ///< Reference to input manager
ProjectilePool* m_projectilePool{ nullptr }; ///< Reference to projectile pool
std::unique_ptr<ProjectilePool> m_ownedProjectilePool; ///< Owned pool if created by Player

// Collision & animation
BoundingSphere m_collisionSphere; ///< Collision bounds for player
Expand Down
5 changes: 5 additions & 0 deletions Spark Engine/Source/Game/Terrain.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ struct TerrainVertex {

class Terrain {
public:
~Terrain() {
if (m_vb) { m_vb->Release(); m_vb = nullptr; }
if (m_ib) { m_ib->Release(); m_ib = nullptr; }
}

// Load heightmap from 8-bit BMP; build vertex/index buffers
HRESULT Initialize(ID3D11Device* device,
ID3D11DeviceContext* ctx,
Expand Down
13 changes: 10 additions & 3 deletions Spark Engine/Source/Graphics/GraphicsEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ using Microsoft::WRL::ComPtr;
GraphicsEngine::GraphicsEngine()
: m_windowWidth(1280)
, m_windowHeight(720)
, m_width(1280)
, m_height(720)
, m_fullscreen(false)
, m_hwnd(nullptr)
, m_textureMemoryUsage(0)
, m_bufferMemoryUsage(0)
, m_frameInProgress(false)
Expand Down Expand Up @@ -1105,15 +1109,16 @@ void GraphicsEngine::ApplyGraphicsState()

void GraphicsEngine::UpdateMetrics()
{
static int frameCount = 0;
frameCount++;

auto now = std::chrono::high_resolution_clock::now();
static auto lastUpdate = now;
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastUpdate);

if (elapsed.count() >= 1000) {
std::lock_guard<std::mutex> lock(m_metricsMutex);

static int frameCount = 0;
frameCount++;
m_statistics.fps = static_cast<uint32_t>(frameCount * 1000.0f / elapsed.count());
frameCount = 0;
lastUpdate = now;
Expand Down Expand Up @@ -1593,7 +1598,9 @@ void GraphicsEngine::UpdateFrameConstants(const XMMATRIX& view, const XMMATRIX&
frameConstants.Time = 0.0f; // You could track actual time here
frameConstants.DeltaTime = 0.016f; // Approximate 60 FPS
frameConstants.ScreenResolution = XMFLOAT2(static_cast<float>(m_windowWidth), static_cast<float>(m_windowHeight));
frameConstants.InvScreenResolution = XMFLOAT2(1.0f / m_windowWidth, 1.0f / m_windowHeight);
frameConstants.InvScreenResolution = XMFLOAT2(
(m_windowWidth > 0) ? (1.0f / m_windowWidth) : 0.0f,
(m_windowHeight > 0) ? (1.0f / m_windowHeight) : 0.0f);

// Set up basic directional lighting
frameConstants.DirectionalLightDir = XMFLOAT3(0.3f, -0.7f, 0.6f); // Pointing down and slightly forward
Expand Down
Loading