#include "WickedEngine.h" #include "voxel/VoxelRenderer.h" #include "wiHelper.h" #include #include #pragma comment(lib, "dbghelp.lib") // ── BVLE Voxels - Prototype Application ───────────────────────── // Wicked Engine based voxel engine prototype for performance validation. // ── Crash handler: writes stack trace + minidump on unhandled exception ── static LONG WINAPI CrashHandler(EXCEPTION_POINTERS* ep) { std::ofstream crash("bvle_crash.log", std::ios::trunc); crash << "=== BVLE CRASH REPORT ===" << std::endl; DWORD code = ep->ExceptionRecord->ExceptionCode; PVOID addr = ep->ExceptionRecord->ExceptionAddress; crash << "Exception code: 0x" << std::hex << code << std::endl; crash << "Crash address: 0x" << addr << std::endl; if (code == EXCEPTION_ACCESS_VIOLATION && ep->ExceptionRecord->NumberParameters >= 2) { ULONG_PTR type = ep->ExceptionRecord->ExceptionInformation[0]; ULONG_PTR target = ep->ExceptionRecord->ExceptionInformation[1]; crash << (type == 0 ? "Reading" : "Writing") << " address: 0x" << std::hex << target << std::endl; } HANDLE process = GetCurrentProcess(); HANDLE thread = GetCurrentThread(); SymInitialize(process, NULL, TRUE); CONTEXT* ctx = ep->ContextRecord; STACKFRAME64 frame = {}; frame.AddrPC.Offset = ctx->Rip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = ctx->Rbp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = ctx->Rsp; frame.AddrStack.Mode = AddrModeFlat; crash << "\nStack trace:" << std::endl; for (int i = 0; i < 32; i++) { if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, process, thread, &frame, ctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) break; char symbolBuf[sizeof(SYMBOL_INFO) + 256]; SYMBOL_INFO* symbol = (SYMBOL_INFO*)symbolBuf; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = 255; DWORD64 disp64 = 0; crash << " [" << i << "] 0x" << std::hex << frame.AddrPC.Offset; if (SymFromAddr(process, frame.AddrPC.Offset, &disp64, symbol)) crash << " " << symbol->Name << " +0x" << disp64; IMAGEHLP_LINE64 line = {}; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); DWORD disp32 = 0; if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &disp32, &line)) crash << " (" << line.FileName << ":" << std::dec << line.LineNumber << ")"; crash << std::endl; } HANDLE dumpFile = CreateFileA("bvle_crash.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (dumpFile != INVALID_HANDLE_VALUE) { MINIDUMP_EXCEPTION_INFORMATION mei; mei.ThreadId = GetCurrentThreadId(); mei.ExceptionPointers = ep; mei.ClientPointers = FALSE; MiniDumpWriteDump(process, GetCurrentProcessId(), dumpFile, MiniDumpWithDataSegs, &mei, NULL, NULL); CloseHandle(dumpFile); } crash.close(); SymCleanup(process); return EXCEPTION_EXECUTE_HANDLER; } static wi::Application application; static voxel::VoxelRenderPath renderPath; int APIENTRY wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { SetUnhandledExceptionFilter(CrashHandler); // Win32 window setup static auto WndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT { switch (message) { case WM_SIZE: case WM_DPICHANGED: if (application.is_window_active) application.SetWindow(hWnd); break; case WM_CHAR: switch (wParam) { case VK_BACK: wi::gui::TextInputField::DeleteFromInput(); break; case VK_RETURN: break; default: wi::gui::TextInputField::AddInput((const wchar_t)wParam); break; } break; case WM_INPUT: wi::input::rawinput::ParseMessage((void*)lParam); break; case WM_KILLFOCUS: application.is_window_active = false; break; case WM_SETFOCUS: application.is_window_active = true; break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }; SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); // Parse arguments early so we can adjust window creation wi::arguments::Parse(lpCmdLine); bool isScreenshot = wi::arguments::HasArgument("screenshot"); WNDCLASSEXW wcex = {}; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszClassName = L"BVLEVoxels"; RegisterClassExW(&wcex); // Screenshot mode: small minimized window to avoid interrupting user HWND hWnd = CreateWindowW( wcex.lpszClassName, isScreenshot ? L"BVLE Screenshot" : L"BVLE Voxels - Prototype", WS_OVERLAPPEDWINDOW, isScreenshot ? 0 : CW_USEDEFAULT, isScreenshot ? 0 : 0, isScreenshot ? 640 : 1920, isScreenshot ? 480 : 1080, nullptr, nullptr, hInstance, nullptr ); // SW_SHOWNOACTIVATE: visible but doesn't steal focus (minimized windows don't render) ShowWindow(hWnd, isScreenshot ? SW_SHOWNOACTIVATE : SW_SHOWMAXIMIZED); // Initialize Wicked Engine application.SetWindow(hWnd); // Redirect Wicked Engine log to file wi::backlog::SetLogFile("bvle_backlog.txt"); // Info display (disabled in screenshot mode) application.infoDisplay.active = !isScreenshot; application.infoDisplay.watermark = false; application.infoDisplay.resolution = !isScreenshot; application.infoDisplay.fpsinfo = !isScreenshot; application.infoDisplay.heap_allocation_counter = !isScreenshot; // Check for "debug" argument to enable face-color debug mode if (wi::arguments::HasArgument("debug")) { renderPath.debugMode = true; } // Check for "debugsmooth" argument to enable smooth blending debug scene if (wi::arguments::HasArgument("debugsmooth")) { renderPath.debugSmooth = true; } // Screenshot mode: auto-position camera, wait for AO convergence, capture, quit if (wi::arguments::HasArgument("screenshot")) { renderPath.screenshotMode = true; } // Activate our custom voxel render path application.ActivatePath(&renderPath); // Main loop MSG msg = { 0 }; static int screenshotFrameCounter = 0; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { application.Run(); // Screenshot mode: cycle through multiple camera views if (renderPath.screenshotMode) { struct CamView { float x, y, z, pitch, yaw; const char* name; }; static const CamView views[] = { { 223.f, 36.5f, 261.f, -0.20f, 0.7f, "closeup" }, // close-up: slightly above grass, looking across { 222.5f, 36.2f, 261.f, -0.10f, 0.5f,"blade" }, // eye-level with grass blades { 220.f, 39.f, 258.f, -0.35f, 0.7f, "medium" }, // medium shot of grass patch }; static const int numViews = sizeof(views) / sizeof(views[0]); static int currentView = 0; static int convergenceFrames = 0; static bool waitingForRender = true; screenshotFrameCounter++; // Set camera for current view on first frame or after capture if (screenshotFrameCounter == 1 || convergenceFrames == 0) { renderPath.setCamera(views[currentView].x, views[currentView].y, views[currentView].z, views[currentView].pitch, views[currentView].yaw); } bool hasRendered = renderPath.renderer.getGpuMeshQuadCount() > 0; if (hasRendered) convergenceFrames++; // Wait 50 frames for AO convergence per view if (convergenceFrames >= 50) { char filename[64]; snprintf(filename, sizeof(filename), "bvle_screenshot_%s.png", views[currentView].name); wi::helper::saveTextureToFile(renderPath.getVoxelRT(), filename); currentView++; if (currentView >= numViews) { PostQuitMessage(0); } else { convergenceFrames = 0; renderPath.setCamera(views[currentView].x, views[currentView].y, views[currentView].z, views[currentView].pitch, views[currentView].yaw); renderPath.resetAOHistory(); // invalidate temporal AO for new view } } } } } wi::jobsystem::ShutDown(); return (int)msg.wParam; }