#include #include #include #include #include #include #include #if METACORE_HAS_PANDA3D #include #include #include #include #include #include #include #include #include #include #include #endif namespace { void log_step(const std::string& message) { std::cout << "[PandaCubeDemo] " << message << std::endl; } #if METACORE_HAS_PANDA3D void configure_runtime_environment() { const auto panda_bin = std::filesystem::path(METACORE_PANDA3D_ROOT_PATH) / "bin"; const auto panda_etc = std::filesystem::path(METACORE_PANDA3D_ROOT_PATH) / "etc"; if (std::filesystem::exists(panda_bin)) { SetDllDirectoryW(panda_bin.wstring().c_str()); log_step("DLL search path set to " + panda_bin.string()); const std::wstring existing_path = [&]() { const DWORD required = GetEnvironmentVariableW(L"PATH", nullptr, 0); if (required == 0) { return std::wstring{}; } std::wstring buffer(required, L'\0'); GetEnvironmentVariableW(L"PATH", buffer.data(), required); if (!buffer.empty() && buffer.back() == L'\0') { buffer.pop_back(); } return buffer; }(); const std::wstring new_path = panda_bin.wstring() + L";" + existing_path; SetEnvironmentVariableW(L"PATH", new_path.c_str()); log_step("PATH prefixed with Panda bin"); } if (std::filesystem::exists(panda_etc)) { SetEnvironmentVariableW(L"PANDA_PRC_DIR", panda_etc.wstring().c_str()); log_step("PANDA_PRC_DIR set to " + panda_etc.string()); } wchar_t exe_path_buffer[MAX_PATH]; const DWORD path_length = GetModuleFileNameW(nullptr, exe_path_buffer, MAX_PATH); if (path_length > 0 && path_length < MAX_PATH) { std::filesystem::path exe_path(exe_path_buffer); std::filesystem::current_path(exe_path.parent_path()); log_step("Working directory set to " + exe_path.parent_path().string()); } } void configure_local_prc() { wchar_t exe_path_buffer[MAX_PATH]; const DWORD path_length = GetModuleFileNameW(nullptr, exe_path_buffer, MAX_PATH); if (path_length == 0 || path_length >= MAX_PATH) { return; } const std::filesystem::path exe_dir = std::filesystem::path(exe_path_buffer).parent_path(); const std::filesystem::path prc_path = exe_dir / "PandaCubeDemo.prc"; const std::filesystem::path panda_bin = std::filesystem::path(METACORE_PANDA3D_ROOT_PATH) / "bin"; const std::string panda_bin_forward = panda_bin.generic_string(); std::ofstream prc_file(prc_path, std::ios::trunc); prc_file << "plugin-path " << panda_bin_forward << "\n" << "load-display pandagl\n" << "aux-display p3tinydisplay\n" << "window-type onscreen\n" << "win-size 1280 720\n" << "sync-video #f\n" << "audio-library-name null\n"; prc_file.close(); log_step("Local PRC written to " + prc_path.string()); load_prc_file(Filename::from_os_specific(prc_path.string())); log_step("Local PRC loaded"); } NodePath create_colored_cube(const NodePath& parent) { LineSegs segments("demo-cube"); segments.set_thickness(2.5F); segments.set_color(0.85F, 0.9F, 1.0F, 1.0F); const LPoint3f corners[] = { {-0.5F, -0.5F, -0.5F}, {0.5F, -0.5F, -0.5F}, {0.5F, 0.5F, -0.5F}, {-0.5F, 0.5F, -0.5F}, {-0.5F, -0.5F, 0.5F}, {0.5F, -0.5F, 0.5F}, {0.5F, 0.5F, 0.5F}, {-0.5F, 0.5F, 0.5F}, }; const int edges[][2] = { {0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}, }; for (const auto& edge : edges) { segments.move_to(corners[edge[0]]); segments.draw_to(corners[edge[1]]); } return parent.attach_new_node(segments.create()); } float movement_axis(const SHORT positive_key, const SHORT negative_key) { const float positive = (GetAsyncKeyState(positive_key) & 0x8000) != 0 ? 1.0F : 0.0F; const float negative = (GetAsyncKeyState(negative_key) & 0x8000) != 0 ? 1.0F : 0.0F; return positive - negative; } #endif } // namespace int main() { #if METACORE_HAS_PANDA3D log_step("Starting Panda3D C++ cube demo."); configure_runtime_environment(); configure_local_prc(); PandaFramework framework; log_step("Calling open_framework()"); framework.open_framework(); log_step("open_framework() succeeded"); framework.set_window_title("Panda3D Cube Demo"); WindowProperties props; props.set_title("Panda3D Cube Demo"); props.set_size(1280, 720); log_step("Calling open_window()"); WindowFramework* window = framework.open_window(props, 0); if (window == nullptr) { log_step("open_window() returned null"); framework.close_framework(); return 2; } log_step("open_window() succeeded"); if (GraphicsWindow* graphics_window = window->get_graphics_window(); graphics_window != nullptr) { graphics_window->set_clear_color(LColor(0.06F, 0.07F, 0.10F, 1.0F)); } NodePath render_root = window->get_render(); NodePath cube = create_colored_cube(render_root); cube.set_scale(1.5F); NodePath camera = window->get_camera_group(); float camera_x = 0.0F; float camera_y = -8.0F; float camera_z = 2.0F; float heading = 0.0F; float pitch = -10.0F; log_step("Controls: WASD move, RF up/down, arrow keys look, Esc closes."); while (window->get_graphics_window() != nullptr && !window->get_graphics_window()->is_closed()) { if ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) != 0) { break; } const float move_speed = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0) ? 0.18F : 0.08F; const float turn_speed = 1.2F; heading += movement_axis(VK_RIGHT, VK_LEFT) * turn_speed; pitch += movement_axis(VK_UP, VK_DOWN) * turn_speed; if (pitch > 89.0F) { pitch = 89.0F; } if (pitch < -89.0F) { pitch = -89.0F; } const float heading_radians = heading * 3.1415926535F / 180.0F; const float forward_x = std::sin(heading_radians); const float forward_y = std::cos(heading_radians); const float right_x = std::cos(heading_radians); const float right_y = -std::sin(heading_radians); const float forward_axis = movement_axis('W', 'S'); const float strafe_axis = movement_axis('D', 'A'); const float vertical_axis = movement_axis('R', 'F'); camera_x += forward_x * forward_axis * move_speed; camera_y += forward_y * forward_axis * move_speed; camera_x += right_x * strafe_axis * move_speed; camera_y += right_y * strafe_axis * move_speed; camera_z += vertical_axis * move_speed; camera.set_pos(camera_x, camera_y, camera_z); camera.set_hpr(heading, pitch, 0.0F); cube.set_h(cube.get_h() + 0.2F); framework.do_frame(nullptr); } framework.close_window(window); framework.close_framework(); log_step("Demo closed cleanly."); return 0; #else log_step("Panda3D support is not enabled in this build."); return 1; #endif }