Files
Juliet/JulietApp/main.cpp
Patedam bfd042abbf Added:
- Depth buffer
- Debug display basics
- Basic vector + matrix maths
Made partially with gemini + antigravity
2026-01-11 22:07:38 -05:00

421 lines
15 KiB
C++

#include "main.h"
#include <Core/Application/ApplicationManager.h>
#include <Core/Common/EnumUtils.h>
#include <Core/HAL/Display/Display.h>
#include <Core/HAL/Event/SystemEvent.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/JulietInit.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Graphics/Camera.h>
#include <Graphics/DebugDisplay.h>
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsConfig.h>
#include <Graphics/GraphicsPipeline.h>
#include <Graphics/RenderPass.h>
#include <Core/Common/String.h>
#include <Core/Memory/Utils.h>
#include <cstdlib>
// TODO : Replace with message box from framework + call main and not winmain + subsystem
// TODO : Think how to do the draw pipeline.
// Ex: Expose a Draw method ?
// Store a graphics context ?
// For now. Put everything in Update down below.
// Should split update from draw, update should have a != timestep than graphics (60fps or more)
// TODO : Remove main.h. Useless
// May be remove that Application class, useless too.
using namespace Juliet;
namespace
{
using GameInit_t = void (*)(void);
using GameShutdown_t = void (*)(void);
using GameUpdate_t = void (*)(float deltaTime);
struct GameFunctionTable
{
GameInit_t Init = nullptr;
GameShutdown_t Shutdown = nullptr;
GameUpdate_t Update = nullptr;
} Game;
const char* GameFunctionTable[] = { "GameInit", "GameShutdown", "GameUpdate" };
} // namespace
struct Vertex
{
float Position[2];
float Color[4];
};
void JulietApplication::Init()
{
Log(LogLevel::Message, LogCategory::Tool, "Initializing Juliet Application...");
Log(LogLevel::Message, LogCategory::Tool, "%s", CStr(GetBasePath()));
GraphicsConfig config;
#if JULIET_DEBUG
config.EnableDebug = true;
#endif
GraphicsDevice = CreateGraphicsDevice(config);
MainWindow = CreatePlatformWindow("Juliet Editor", 1280, 720);
Running = MainWindow != nullptr && GraphicsDevice != nullptr;
if (Running)
{
AttachToWindow(GraphicsDevice, MainWindow);
{
// Create graphics pipeline
String entryPoint = WrapString("main");
ShaderCreateInfo shaderCI = {};
shaderCI.EntryPoint = entryPoint;
// TODO: Assets management that handles path to assets or something.
String shaderPath = WrapString("../../Assets/compiled/Triangle.vert.dxil");
shaderCI.Stage = ShaderStage::Vertex;
Shader* vertexShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
shaderPath = WrapString("../../Assets/compiled/SolidColor.frag.dxil");
shaderCI.Stage = ShaderStage::Fragment;
Shader* fragmentShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
ColorTargetDescription colorTargetDescription = {};
colorTargetDescription.Format = GetSwapChainTextureFormat(GraphicsDevice, MainWindow);
GraphicsPipelineCreateInfo pipelineCI = {};
pipelineCI.VertexShader = vertexShader;
pipelineCI.FragmentShader = fragmentShader;
pipelineCI.PrimitiveType = PrimitiveType::TriangleList;
pipelineCI.TargetInfo = { .ColorTargetDescriptions = &colorTargetDescription,
.NumColorTargets = 1,
.DepthStencilFormat = TextureFormat::D32_FLOAT,
.HasDepthStencilTarget = true };
pipelineCI.RasterizerState.FillMode = FillMode::Solid;
pipelineCI.DepthStencilState.EnableDepthTest = true;
pipelineCI.DepthStencilState.EnableDepthWrite = true;
pipelineCI.DepthStencilState.CompareOperation = CompareOperation::Less;
GraphicsPipeline = CreateGraphicsPipeline(GraphicsDevice, pipelineCI);
if (GraphicsPipeline == nullptr)
{
LogError(LogCategory::Game, "Failed to create graphics pipeline!");
Running = false;
}
// Create Depth Buffer
TextureCreateInfo depthCI = {};
depthCI.Type = TextureType::Texture_2D;
depthCI.Width = 1280;
depthCI.Height = 720;
depthCI.Format = TextureFormat::D32_FLOAT;
depthCI.Flags = TextureUsageFlag::DepthStencilTarget;
depthCI.LayerCount = 1;
depthCI.MipLevelCount = 1;
depthCI.SampleCount = TextureSampleCount::One;
DepthBuffer = CreateTexture(GraphicsDevice, depthCI);
if (DepthBuffer == nullptr)
{
LogError(LogCategory::Game, "Failed to create depth buffer!");
Running = false;
}
// Create Buffers
BufferCreateInfo bufferCI = {};
bufferCI.Size = 256;
bufferCI.Usage = BufferUsage::StructuredBuffer; // Changed to StructuredBuffer As Requested
ConstantBuffer = CreateGraphicsBuffer(GraphicsDevice, bufferCI);
TransferBufferCreateInfo transferCI = {};
transferCI.Size = 256;
transferCI.Usage = TransferBufferUsage::Upload;
TransferBuffer = CreateGraphicsTransferBuffer(GraphicsDevice, transferCI);
if (vertexShader)
{
DestroyShader(GraphicsDevice, vertexShader);
}
if (fragmentShader)
{
DestroyShader(GraphicsDevice, fragmentShader);
}
if (Running == false)
{
return;
}
}
GameCode.Functions = reinterpret_cast<void**>(&Game);
GameCode.FunctionCount = ArraySize(GameFunctionTable);
GameCode.FunctionNames = GameFunctionTable;
InitHotReloadCode(GameCode, ConstString("Game.dll"), ConstString("Game_Temp.dll"), ConstString("lock.tmp"));
if ((Running = GameCode.IsValid))
{
Game.Init();
}
// Initialize DebugDisplay
DebugDisplay_Initialize(GraphicsDevice);
}
}
void JulietApplication::Shutdown()
{
Log(LogLevel::Message, LogCategory::Tool, "Shutting down Juliet Application...");
if (GameCode.IsValid)
{
Game.Shutdown();
ShutdownHotReloadCode(GameCode);
}
// Shutdown DebugDisplay before graphics device
if (GraphicsDevice)
{
DebugDisplay_Shutdown(GraphicsDevice);
}
if (GraphicsPipeline)
{
DestroyGraphicsPipeline(GraphicsDevice, GraphicsPipeline);
}
if (ConstantBuffer)
{
DestroyGraphicsBuffer(GraphicsDevice, ConstantBuffer);
}
if (TransferBuffer)
{
DestroyGraphicsTransferBuffer(GraphicsDevice, TransferBuffer);
}
if (DepthBuffer)
{
DestroyTexture(GraphicsDevice, DepthBuffer);
}
if (MainWindow && GraphicsDevice)
{
DetachFromWindow(GraphicsDevice, MainWindow);
}
if (MainWindow)
{
DestroyPlatformWindow(MainWindow);
}
if (GraphicsDevice)
{
DestroyGraphicsDevice(GraphicsDevice);
}
Log(LogLevel::Message, LogCategory::Tool, "Juliet App shutdown Completed");
}
void JulietApplication::Update()
{
bool reloadShaders = false;
static bool reloadShadersDebounce = false;
SystemEvent evt;
while (GetEvent(evt))
{
if (evt.Type == EventType::Window_Close_Request)
{
if (evt.Data.Window.AssociatedWindowID == GetWindowID(MainWindow))
{
Running = false;
}
}
if (evt.Type == EventType::Key_Down)
{
}
// Shader hot reload using keyboard.
// TODO: Add Input debounce in the library. (Just pressed vs pressed)
if (!reloadShadersDebounce && ((GetKeyModState() & KeyMod::Alt) != KeyMod::None) && IsKeyDown(ScanCode::R))
{
reloadShaders = true;
reloadShadersDebounce = true;
}
if (reloadShadersDebounce && !IsKeyDown(ScanCode::R))
{
reloadShadersDebounce = false;
}
}
Game.Update(0.0f);
if (ShouldReloadCode(GameCode))
{
ReloadCode(GameCode);
}
if (reloadShaders)
{
// We need to wait for the gpu to be idle to recreate our graphics pipelines
WaitUntilGPUIsIdle(GraphicsDevice);
#if ALLOW_SHADER_HOT_RELOAD
String entryPoint = WrapString("main");
ShaderCreateInfo shaderCI = {};
shaderCI.EntryPoint = entryPoint;
String shaderPath = WrapString("../../Assets/compiled/Triangle.vert.dxil");
shaderCI.Stage = ShaderStage::Vertex;
Shader* vertexShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
shaderPath = WrapString("../../Assets/compiled/SolidColor.frag.dxil");
shaderCI.Stage = ShaderStage::Fragment;
Shader* fragmentShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
UpdateGraphicsPipelineShaders(GraphicsDevice, GraphicsPipeline, vertexShader, fragmentShader);
if (vertexShader)
{
DestroyShader(GraphicsDevice, vertexShader);
}
if (fragmentShader)
{
DestroyShader(GraphicsDevice, fragmentShader);
}
#endif
}
// Draw here for now
// 1) Acquire a Command Buffer
CommandList* cmdList = AcquireCommandList(GraphicsDevice, QueueType::Graphics);
if (cmdList == nullptr)
{
Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire command list.");
Running = false;
return;
}
Texture* swapChainTexture = nullptr;
if (!WaitAndAcquireSwapChainTexture(cmdList, MainWindow, &swapChainTexture))
{
Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire swapchain texture.");
Running = false;
return;
}
if (swapChainTexture)
{
ColorTargetInfo colorTargetInfo = {};
colorTargetInfo.TargetTexture = swapChainTexture;
colorTargetInfo.ClearColor = { .R = .0f, .G = .0f, .B = .0f, .A = 1.f };
colorTargetInfo.LoadOperation = LoadOperation::Clear;
colorTargetInfo.StoreOperation = StoreOperation::Store;
if (ConstantBuffer && TransferBuffer)
{
void* ptr = MapGraphicsTransferBuffer(GraphicsDevice, TransferBuffer);
if (ptr)
{
Vertex* vertices = static_cast<Vertex*>(ptr);
// Triangle 1
vertices[0] = { { -0.5f, -0.5f }, { 1.0f, 0.0f, 0.0f, 1.0f } }; // Red
vertices[1] = { { 0.0f, 0.5f }, { 0.0f, 1.0f, 0.0f, 1.0f } }; // Green
vertices[2] = { { 0.5f, -0.5f }, { 0.0f, 0.0f, 1.0f, 1.0f } }; // Blue
// Triangle 2
vertices[3] = { { -0.5f, 0.5f }, { 1.0f, 1.0f, 0.0f, 1.0f } }; // Yellow
vertices[4] = { { 0.0f, 0.8f }, { 0.0f, 1.0f, 1.0f, 1.0f } }; // Cyan
vertices[5] = { { 0.5f, 0.5f }, { 1.0f, 0.0f, 1.0f, 1.0f } }; // Magenta
UnmapGraphicsTransferBuffer(GraphicsDevice, TransferBuffer);
}
CopyBuffer(cmdList, ConstantBuffer, TransferBuffer, 256);
TransitionBufferToReadable(cmdList, ConstantBuffer);
}
// Test lines and sphere - GIANT SCALE
DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, false); // X-Axis (Red)
DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 10.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, true); // Y-Axis (Green)
DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 10.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, true); // Z-Axis (Blue) - Up
DebugDisplay_DrawSphere({ 0.0f, 0.0f, 0.0f }, 5.0f, { 1.0f, 1.0f, 0.0f, 1.0f }, true); // Yellow sphere
// Prepare debug data (outside render pass)
DebugDisplay_Prepare(cmdList);
DepthStencilTargetInfo depthTargetInfo = {};
depthTargetInfo.TargetTexture = DepthBuffer;
depthTargetInfo.ClearDepth = 1.0f;
depthTargetInfo.LoadOperation = LoadOperation::Clear;
depthTargetInfo.StoreOperation = StoreOperation::Store;
RenderPass* renderPass = BeginRenderPass(cmdList, colorTargetInfo, &depthTargetInfo);
BindGraphicsPipeline(renderPass, GraphicsPipeline);
// Pass descriptor index via Push Constants AFTER finding the pipeline (RootSignature)
uint32 descriptorIndex = GetDescriptorIndex(GraphicsDevice, ConstantBuffer);
SetPushConstants(cmdList, ShaderStage::Vertex, 0, 1, &descriptorIndex);
DrawPrimitives(renderPass, 6, 1, 0, 0);
// Debug Display - render shapes (inside render pass)
static float orbitTime = 0.0f;
orbitTime += 0.016f; // Rough approximation for 60fps
float radius = 30.0f;
Camera debugCamera = {};
debugCamera.Position = { cosf(orbitTime) * radius, sinf(orbitTime) * radius, 10.0f }; // Rotate in XY plane
debugCamera.Target = { 0.0f, 0.0f, 0.0f };
debugCamera.Up = { 0.0f, 0.0f, 1.0f }; // Z-Up
debugCamera.FOV = 1.047f; // 60 degrees
debugCamera.AspectRatio = 1200.0f / 800.0f;
debugCamera.NearPlane = 0.1f;
debugCamera.FarPlane = 1000.0f;
DebugDisplay_Flush(cmdList, renderPass, debugCamera);
EndRenderPass(renderPass);
}
// Submit Commands
SubmitCommandLists(cmdList);
}
bool JulietApplication::IsRunning()
{
return Running;
}
namespace
{
JulietApplication EditorApplication;
}
JulietApplication& GetEditorApplication()
{
return EditorApplication;
}
int main(int /*argc*/, char** /*argv*/)
{
// Allow only one instance to be launched.
// Need to create Mutex code in the lib because i dont want to include windows.h in this file anymore
// CreateMutex(0, false, L"Local\\Juliet.App");
// if (GetLastError() == ERROR_ALREADY_EXISTS)
// {
// MessageBox(nullptr, L"An instance of Juliet is already running.", L"Juliet", MB_OK | MB_ICONEXCLAMATION);
// return EXIT_FAILURE;
// }
StartApplication(EditorApplication, JulietInit_Flags::Display);
// Pause here to not close the console window immediatly on stop
system("PAUSE");
return EXIT_SUCCESS;
}