415 lines
12 KiB
C++
415 lines
12 KiB
C++
#include "main.h"
|
|
|
|
#include <Core/Application/ApplicationManager.h>
|
|
#include <Core/Common/EnumUtils.h>
|
|
#include <Core/Common/String.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 <Core/Main.h>
|
|
#include <Core/Math/Matrix.h>
|
|
#include <Core/Memory/MemoryArena.h>
|
|
#include <Core/Memory/Utils.h>
|
|
#include <cstdlib>
|
|
#include <Engine/Debug/MemoryDebugger.h>
|
|
#include <Graphics/Camera.h>
|
|
#include <Graphics/DebugDisplay.h>
|
|
#include <Graphics/Graphics.h>
|
|
#include <Graphics/GraphicsConfig.h>
|
|
#include <Graphics/GraphicsPipeline.h>
|
|
#include <Graphics/Mesh.h>
|
|
#include <Graphics/MeshRenderer.h>
|
|
#include <Graphics/RenderPass.h>
|
|
#include <Graphics/VertexData.h>
|
|
#include <Juliet.h>
|
|
|
|
#ifdef JULIET_ENABLE_IMGUI
|
|
#include <imgui.h>
|
|
#endif
|
|
|
|
static bool ShowMemoryDebugger = false;
|
|
|
|
// 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;
|
|
|
|
extern "C" {
|
|
__declspec(dllexport) extern const unsigned int D3D12SDKVersion = 615;
|
|
}
|
|
extern "C" {
|
|
__declspec(dllexport) extern const char* D3D12SDKPath = ".\\";
|
|
}
|
|
|
|
namespace
|
|
{
|
|
using GameInit_t = void (*)(GameInitParams*);
|
|
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" };
|
|
|
|
Arena* PlatformArena = nullptr;
|
|
} // namespace
|
|
|
|
void JulietApplication::Init()
|
|
{
|
|
Log(LogLevel::Message, LogCategory::Tool, "Initializing Juliet Application...");
|
|
|
|
Log(LogLevel::Message, LogCategory::Tool, "%s", CStr(GetBasePath()));
|
|
|
|
PlatformArena = ArenaAllocate({ .AllowRealloc = true } JULIET_DEBUG_PARAM("Platform Arena"));
|
|
|
|
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);
|
|
{
|
|
Running = InitializeMeshRenderer(PlatformArena, GraphicsDevice, MainWindow);
|
|
|
|
// 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;
|
|
}
|
|
|
|
constexpr int kGridSize = 10;
|
|
constexpr float kSpacing = 2.5f;
|
|
constexpr float kOffset = (kGridSize - 1) * kSpacing * 0.5f;
|
|
|
|
for (int row = 0; row < kGridSize; ++row)
|
|
{
|
|
for (int col = 0; col < kGridSize; ++col)
|
|
{
|
|
MeshID cube = AddCube();
|
|
float x = static_cast<float>(col) * kSpacing - kOffset;
|
|
float y = static_cast<float>(row) * kSpacing - kOffset;
|
|
|
|
float seed = static_cast<float>(row * kGridSize + col);
|
|
Matrix rotation = MatrixRotation(seed * 0.73f, seed * 1.17f, seed * 0.53f);
|
|
SetMeshTransform(cube, MatrixTranslation(x, y, 0.0f) * rotation);
|
|
}
|
|
}
|
|
|
|
CommandList* loadCmd = AcquireCommandList(GraphicsDevice);
|
|
LoadMeshesOnGPU(loadCmd);
|
|
SubmitCommandLists(loadCmd);
|
|
|
|
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))
|
|
{
|
|
GameInitParams params;
|
|
params.GameArena = GameArena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "Game Arena"));
|
|
params.ScratchArena = GameScratchArena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "Scratch Arena"));
|
|
Game.Init(¶ms);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JulietApplication::Shutdown()
|
|
{
|
|
Log(LogLevel::Message, LogCategory::Tool, "Shutting down Juliet Application...");
|
|
|
|
if (GameCode.IsValid)
|
|
{
|
|
Game.Shutdown();
|
|
ShutdownHotReloadCode(GameCode);
|
|
}
|
|
|
|
if (GraphicsPipeline)
|
|
{
|
|
DestroyGraphicsPipeline(GraphicsDevice, GraphicsPipeline);
|
|
}
|
|
|
|
if (DepthBuffer)
|
|
{
|
|
DestroyTexture(GraphicsDevice, DepthBuffer);
|
|
}
|
|
|
|
ShutdownMeshRenderer();
|
|
|
|
if (MainWindow && GraphicsDevice)
|
|
{
|
|
DetachFromWindow(GraphicsDevice, MainWindow);
|
|
}
|
|
|
|
if (MainWindow)
|
|
{
|
|
DestroyPlatformWindow(MainWindow);
|
|
}
|
|
|
|
if (GraphicsDevice)
|
|
{
|
|
DestroyGraphicsDevice(GraphicsDevice);
|
|
}
|
|
|
|
ArenaRelease(PlatformArena);
|
|
|
|
Log(LogLevel::Message, LogCategory::Tool, "Juliet App shutdown Completed");
|
|
}
|
|
|
|
void JulietApplication::Update()
|
|
{
|
|
static LARGE_INTEGER frequency = {};
|
|
static LARGE_INTEGER lastTime = {};
|
|
if (frequency.QuadPart == 0)
|
|
{
|
|
QueryPerformanceFrequency(&frequency);
|
|
QueryPerformanceCounter(&lastTime);
|
|
}
|
|
|
|
LARGE_INTEGER currentTime;
|
|
QueryPerformanceCounter(¤tTime);
|
|
float deltaTime = static_cast<float>(currentTime.QuadPart - lastTime.QuadPart) / static_cast<float>(frequency.QuadPart);
|
|
lastTime = currentTime;
|
|
|
|
CameraTime += deltaTime;
|
|
|
|
static float fpsTimer = 0.0f;
|
|
static int fpsFrames = 0;
|
|
fpsTimer += deltaTime;
|
|
fpsFrames++;
|
|
if (fpsTimer >= 0.5f)
|
|
{
|
|
float fps = static_cast<float>(fpsFrames) / fpsTimer;
|
|
float ms = (fpsTimer / static_cast<float>(fpsFrames)) * 1000.0f;
|
|
char title[64];
|
|
snprintf(title, sizeof(title), "Juliet | %.1f FPS | %.2f ms", static_cast<double>(fps), static_cast<double>(ms));
|
|
SetWindowTitle(MainWindow, WrapString(title));
|
|
fpsTimer = 0.0f;
|
|
fpsFrames = 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Shader hot reload using keyboard.
|
|
if (!reloadShadersDebounce && ((GetKeyModState() & KeyMod::Alt) != KeyMod::None) && IsKeyDown(ScanCode::R))
|
|
{
|
|
reloadShaders = true;
|
|
reloadShadersDebounce = true;
|
|
}
|
|
|
|
if (reloadShadersDebounce && !IsKeyDown(ScanCode::R))
|
|
{
|
|
reloadShadersDebounce = false;
|
|
}
|
|
}
|
|
|
|
#ifdef JULIET_ENABLE_IMGUI
|
|
// ImGui::ShowDemoWindow();
|
|
#endif
|
|
|
|
DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, false);
|
|
DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, true);
|
|
DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, true);
|
|
DebugDisplay_DrawSphere({ 0.0f, 0.0f, 0.0f }, 0.5f, { 1.0f, 1.0f, 0.0f, 1.0f }, true);
|
|
|
|
Game.Update(0.0f);
|
|
|
|
if (ShouldReloadCode(GameCode))
|
|
{
|
|
ReloadCode(GameCode);
|
|
}
|
|
|
|
if (reloadShaders)
|
|
{
|
|
WaitUntilGPUIsIdle(GraphicsDevice);
|
|
|
|
#if ALLOW_SHADER_HOT_RELOAD
|
|
ReloadMeshRendererShaders();
|
|
#endif
|
|
}
|
|
|
|
// Memory debugger toggle
|
|
static bool toggleDebounce = false;
|
|
if (IsKeyDown(Juliet::ScanCode::Home))
|
|
{
|
|
if (!toggleDebounce)
|
|
{
|
|
ShowMemoryDebugger = !ShowMemoryDebugger;
|
|
toggleDebounce = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
toggleDebounce = false;
|
|
}
|
|
|
|
// Auto-close logic
|
|
if (AutoCloseFrameCount > 0)
|
|
{
|
|
AutoCloseFrameCount--;
|
|
if (AutoCloseFrameCount == 0)
|
|
{
|
|
Log(LogLevel::Message, LogCategory::Tool, "Auto-closing application as requested.");
|
|
Running = false;
|
|
}
|
|
}
|
|
|
|
if (ShowMemoryDebugger)
|
|
{
|
|
#if JULIET_DEBUG
|
|
Debug::DebugDrawMemoryArena();
|
|
#endif
|
|
}
|
|
|
|
ArenaClear(GameScratchArena);
|
|
}
|
|
|
|
void JulietApplication::OnPreRender(CommandList* /*cmd*/) {}
|
|
|
|
void JulietApplication::OnRender(RenderPass* pass, CommandList* cmd)
|
|
{
|
|
PushData pushData = {};
|
|
pushData.ViewProjection = Camera_GetViewProjectionMatrix(GetDebugCamera());
|
|
|
|
RenderMeshes(pass, cmd, pushData);
|
|
}
|
|
|
|
ColorTargetInfo JulietApplication::GetColorTargetInfo(Texture* swapchainTexture)
|
|
{
|
|
ColorTargetInfo info = {};
|
|
info.TargetTexture = swapchainTexture;
|
|
info.ClearColor = { .R = 0.0f, .G = 0.0f, .B = 0.0f, .A = 1.0f };
|
|
info.LoadOperation = LoadOperation::Clear;
|
|
info.StoreOperation = StoreOperation::Store;
|
|
return info;
|
|
}
|
|
|
|
DepthStencilTargetInfo* JulietApplication::GetDepthTargetInfo()
|
|
{
|
|
static DepthStencilTargetInfo info = {};
|
|
info.TargetTexture = DepthBuffer;
|
|
info.ClearDepth = 1.0f;
|
|
info.LoadOperation = LoadOperation::Clear;
|
|
info.StoreOperation = StoreOperation::Store;
|
|
return &info;
|
|
}
|
|
|
|
Camera JulietApplication::GetDebugCamera()
|
|
{
|
|
float time = CameraTime;
|
|
|
|
float orbitSpeed = 0.5f;
|
|
float currentOrbitTime = time * orbitSpeed;
|
|
|
|
// --- Adjusted for 1-Meter Scale ---
|
|
float baseRadius = 25.0f; // Increased to see 10x10 cube grid
|
|
|
|
float radius = baseRadius;
|
|
//* Uncomment for active zoom
|
|
float zoomAmplitude = 15.0f;
|
|
float zoomSpeed = 0.5f;
|
|
radius = baseRadius + (sinf(time * zoomSpeed) * zoomAmplitude);
|
|
//*/
|
|
float zHeight = radius * 0.5f; // Keep a nice downward viewing angle
|
|
|
|
Camera cam = {};
|
|
cam.Position = { cosf(currentOrbitTime) * radius, sinf(currentOrbitTime) * radius, zHeight };
|
|
cam.Target = { 0.0f, 0.0f, 0.0f };
|
|
cam.Up = { 0.0f, 0.0f, 1.0f };
|
|
cam.FOV = 1.047f;
|
|
cam.AspectRatio = 1200.0f / 800.0f;
|
|
cam.NearPlane = 0.1f;
|
|
cam.FarPlane = 1000.0f;
|
|
|
|
return cam;
|
|
}
|
|
|
|
bool JulietApplication::IsRunning()
|
|
{
|
|
return Running;
|
|
}
|
|
namespace
|
|
{
|
|
JulietApplication EditorApplication;
|
|
}
|
|
|
|
JulietApplication& GetEditorApplication()
|
|
{
|
|
return EditorApplication;
|
|
}
|
|
|
|
int JulietMain(int argc, wchar_t** argv)
|
|
{
|
|
if (argc > 1)
|
|
{
|
|
for (int i = 1; i < argc; ++i)
|
|
{
|
|
if (wcscmp(argv[i], L"-autoclose") == 0 && (i + 1 < argc))
|
|
{
|
|
int frames = _wtoi(argv[i + 1]);
|
|
EditorApplication.SetAutoCloseFrameCount(frames);
|
|
}
|
|
}
|
|
}
|
|
|
|
StartApplication(EditorApplication, JulietInit_Flags::Display);
|
|
|
|
// Pause here to not close the console window immediatly on stop
|
|
// Only pause if not in auto-close mode
|
|
if (EditorApplication.GetAutoCloseFrameCount() == -1 && !Debug::IsDebuggerPresent())
|
|
{
|
|
system("PAUSE");
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|