diff options
Diffstat (limited to 'src/tests/3d/test_3d_render.cc')
| -rw-r--r-- | src/tests/3d/test_3d_render.cc | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/src/tests/3d/test_3d_render.cc b/src/tests/3d/test_3d_render.cc new file mode 100644 index 0000000..eee46ba --- /dev/null +++ b/src/tests/3d/test_3d_render.cc @@ -0,0 +1,326 @@ +// This file is part of the 64k demo project. +// Standalone "mini-demo" for testing the 3D renderer. + +#include "3d/camera.h" +#include "3d/object.h" +#include "3d/renderer.h" +#include "3d/scene.h" +#include "generated/assets.h" +#include "gpu/effects/shaders.h" +#include "gpu/texture_manager.h" +#include "platform/platform.h" +#include "procedural/generator.h" +#include <cmath> +#include <cstdio> +#include <cstring> +#include <vector> + +// Global State +static Renderer3D g_renderer; +static TextureManager g_textures; +static Scene g_scene; +static Camera g_camera; +static WGPUDevice g_device = nullptr; +static WGPUQueue g_queue = nullptr; +static WGPUSurface g_surface = nullptr; +static WGPUAdapter g_adapter = nullptr; +static WGPUTextureFormat g_format = WGPUTextureFormat_Undefined; + +// ... (init_wgpu implementation same as before) +void init_wgpu(PlatformState* platform_state) { + WGPUInstance instance = wgpuCreateInstance(nullptr); + if (!instance) { + fprintf(stderr, "Failed to create WGPU instance.\n"); + exit(1); + } + + g_surface = platform_create_wgpu_surface(instance, platform_state); + if (!g_surface) { + fprintf(stderr, "Failed to create WGPU surface.\n"); + exit(1); + } + + WGPURequestAdapterOptions adapter_opts = {}; + adapter_opts.compatibleSurface = g_surface; + adapter_opts.powerPreference = WGPUPowerPreference_HighPerformance; + +#if defined(DEMO_CROSS_COMPILE_WIN32) + auto on_adapter = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, + const char* message, void* userdata) { + if (status == WGPURequestAdapterStatus_Success) { + *(WGPUAdapter*)userdata = adapter; + } + }; + wgpuInstanceRequestAdapter(instance, &adapter_opts, on_adapter, &g_adapter); +#else + auto on_adapter = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, + WGPUStringView message, void* userdata, void* user2) { + (void)user2; + if (status == WGPURequestAdapterStatus_Success) { + *(WGPUAdapter*)userdata = adapter; + } + }; + WGPURequestAdapterCallbackInfo adapter_cb = {}; + adapter_cb.mode = WGPUCallbackMode_WaitAnyOnly; + adapter_cb.callback = on_adapter; + adapter_cb.userdata1 = &g_adapter; + wgpuInstanceRequestAdapter(instance, &adapter_opts, adapter_cb); +#endif + + while (!g_adapter) { + platform_wgpu_wait_any(instance); + } + + WGPUDeviceDescriptor device_desc = {}; + +#if defined(DEMO_CROSS_COMPILE_WIN32) + auto on_device = [](WGPURequestDeviceStatus status, WGPUDevice device, + const char* message, void* userdata) { + if (status == WGPURequestDeviceStatus_Success) { + *(WGPUDevice*)userdata = device; + } + }; + wgpuAdapterRequestDevice(g_adapter, &device_desc, on_device, &g_device); +#else + auto on_device = [](WGPURequestDeviceStatus status, WGPUDevice device, + WGPUStringView message, void* userdata, void* user2) { + (void)user2; + if (status == WGPURequestDeviceStatus_Success) { + *(WGPUDevice*)userdata = device; + } + }; + WGPURequestDeviceCallbackInfo device_cb = {}; + device_cb.mode = WGPUCallbackMode_WaitAnyOnly; + device_cb.callback = on_device; + device_cb.userdata1 = &g_device; + wgpuAdapterRequestDevice(g_adapter, &device_desc, device_cb); +#endif + + while (!g_device) { + platform_wgpu_wait_any(instance); + } + + g_queue = wgpuDeviceGetQueue(g_device); + + WGPUSurfaceCapabilities caps = {}; + wgpuSurfaceGetCapabilities(g_surface, g_adapter, &caps); + g_format = caps.formats[0]; + + WGPUSurfaceConfiguration config = {}; + config.device = g_device; + config.format = g_format; + config.usage = WGPUTextureUsage_RenderAttachment; + config.width = platform_state->width; + config.height = platform_state->height; + config.presentMode = WGPUPresentMode_Fifo; + config.alphaMode = WGPUCompositeAlphaMode_Opaque; + wgpuSurfaceConfigure(g_surface, &config); +} + +void setup_scene() { + g_scene.clear(); + srand(12345); // Fixed seed + + // Large floor, use BOX type (SDF) at index 0 + Object3D floor(ObjectType::BOX); + floor.position = vec3(0, -2.0f, 0); + floor.scale = vec3(25.0f, 0.2f, 25.0f); + floor.color = vec4(0.8f, 0.8f, 0.8f, 1.0f); + g_scene.add_object(floor); + + // Large center Torus (SDF) + Object3D center(ObjectType::TORUS); + center.position = vec3(0, 1.0f, 0); + center.scale = vec3(2.5f, 2.5f, 2.5f); + center.color = vec4(1, 0.2, 0.2, 1); + g_scene.add_object(center); + + // Moving Sphere (SDF) + Object3D sphere(ObjectType::SPHERE); + sphere.position = vec3(4.0f, 2.0f, 0); + sphere.scale = vec3(1.5f, 1.5f, 1.5f); + sphere.color = vec4(0.2, 1, 0.2, 1); + g_scene.add_object(sphere); + + // Mesh Object (Rasterized) + Object3D mesh_obj(ObjectType::MESH); + mesh_obj.position = vec3(-4.0f, 2.0f, 0); + mesh_obj.scale = vec3(2.0f, 2.0f, 2.0f); + mesh_obj.color = vec4(0.2, 0.2, 1, 1); + mesh_obj.mesh_asset_id = AssetId::ASSET_MESH_CUBE; + g_scene.add_object(mesh_obj); + + // Dodecahedron (Rasterized) + Object3D dodeca(ObjectType::MESH); + dodeca.position = vec3(4.0f, 2.0f, 2.0f); + dodeca.scale = vec3(1.0f, 1.0f, 1.0f); + dodeca.color = vec4(1.0, 0.5, 0.0, 1); // Orange + dodeca.mesh_asset_id = AssetId::ASSET_DODECAHEDRON; + g_scene.add_object(dodeca); + + // Random objects + for (int i = 0; i < 30; ++i) { + ObjectType type = ObjectType::SPHERE; + int r = rand() % 3; + if (r == 1) + type = ObjectType::TORUS; + if (r == 2) + type = ObjectType::BOX; + + Object3D obj(type); + float angle = (rand() % 360) * 0.01745f; + float dist = 3.0f + (rand() % 100) * 0.05f; + float height = 0.5f + (rand() % 100) * 0.04f; + obj.position = vec3(std::cos(angle) * dist, height, std::sin(angle) * dist); + + // Random non-uniform scale for debugging + float s = 0.6f + (rand() % 100) * 0.008f; + obj.scale = vec3(s, s * 1.2f, s * 0.8f); + + obj.color = vec4((rand() % 100) / 100.0f, (rand() % 100) / 100.0f, + (rand() % 100) / 100.0f, 1.0f); + g_scene.add_object(obj); + } +} + +// Wrapper to generate periodic noise +bool gen_periodic_noise(uint8_t* buffer, int w, int h, const float* params, + int num_params) { + if (!procedural::gen_noise(buffer, w, h, params, num_params)) + return false; + float p_params[] = {0.1f}; // 10% overlap + return procedural::make_periodic(buffer, w, h, p_params, 1); +} + +int main(int argc, char** argv) { + printf("Running 3D Renderer Test...\n"); + +#if !defined(STRIP_ALL) + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--debug") == 0) { + Renderer3D::SetDebugEnabled(true); + } + if (strcmp(argv[i], "--no-bvh") == 0) { + g_renderer.SetBvhEnabled(false); + } + } +#else + (void)argc; + (void)argv; +#endif + + PlatformState platform_state = platform_init(false, 1280, 720); + + // The test's own WGPU init sequence + init_wgpu(&platform_state); + + InitShaderComposer(); + + g_renderer.init(g_device, g_queue, g_format); + g_renderer.resize(platform_state.width, platform_state.height); + + g_textures.init(g_device, g_queue); + + // GPU Noise texture (replaces CPU procedural) + GpuProceduralParams noise_params = {}; + noise_params.width = 256; + noise_params.height = 256; + float noise_vals[2] = {1234.0f, 16.0f}; + noise_params.params = noise_vals; + noise_params.num_params = 2; + g_textures.create_gpu_noise_texture("noise", noise_params); + g_renderer.set_noise_texture(g_textures.get_texture_view("noise")); + + // GPU Perlin texture for sky (replaces CPU procedural) + GpuProceduralParams sky_params = {}; + sky_params.width = 512; + sky_params.height = 256; + float sky_vals[5] = {42.0f, 4.0f, 1.0f, 0.5f, 6.0f}; + sky_params.params = sky_vals; + sky_params.num_params = 5; + g_textures.create_gpu_perlin_texture("sky", sky_params); + g_renderer.set_sky_texture(g_textures.get_texture_view("sky")); + + // GPU Grid texture (new!) + GpuProceduralParams grid_params = {}; + grid_params.width = 256; + grid_params.height = 256; + float grid_vals[2] = {32.0f, 2.0f}; // grid_size, thickness + grid_params.params = grid_vals; + grid_params.num_params = 2; + g_textures.create_gpu_grid_texture("grid", grid_params); + + setup_scene(); + + g_camera.position = vec3(0, 5, 10); + g_camera.target = vec3(0, 0, 0); + + while (!platform_should_close(&platform_state)) { + platform_poll(&platform_state); + float time = (float)platform_state.time; + + float cam_radius = 10.0f + std::sin(time * 0.3f) * 4.0f; + float cam_height = 5.0f + std::cos(time * 0.4f) * 3.0f; + g_camera.set_look_at(vec3(std::sin(time * 0.5f) * cam_radius, cam_height, + std::cos(time * 0.5f) * cam_radius), + vec3(0, 0, 0), vec3(0, 1, 0)); + g_camera.aspect_ratio = platform_state.aspect_ratio; + + for (size_t i = 1; i < g_scene.objects.size(); ++i) { + // Rotation around a random-ish 3D axis + vec3 axis = + vec3(std::sin((float)i), std::cos((float)i), 0.5f).normalize(); + g_scene.objects[i].rotation = quat::from_axis(axis, time * 2.0f + i); + + // Non-uniform scaling variance + float s = 0.5f + 0.1f * std::sin(time * 0.5f + i); + g_scene.objects[i].scale = vec3(s, s * 1.4f, s * 0.8f); + + g_scene.objects[i].position.y = std::sin(time * 3.0f + i) * 1.5f; + } + +#if !defined(STRIP_ALL) + Renderer3D::SetDebugEnabled(true); + VisualDebug& dbg = g_renderer.GetVisualDebug(); + dbg.add_cross(vec3(0, 0, 0), 1.0f, vec3(1, 0, 0)); + dbg.add_sphere(vec3(std::sin(time) * 2.0f, 3.0f, std::cos(time) * 2.0f), + 0.5f, vec3(0, 1, 1)); + dbg.add_line(vec3(0, 0, 0), vec3(0, 5, 0), vec3(1, 0, 1)); + + // Cone (Spotlight visualization) + dbg.add_cone(vec3(0, 5, 0), vec3(0, -1, 0), 2.0f, 1.0f, vec3(1, 1, 0)); + + // Trajectory path + std::vector<vec3> path; + for (int i = 0; i <= 32; ++i) { + float a = i * 6.28318f / 32.0f; + path.push_back(vec3(std::sin(a) * 4.0f, 0.5f, std::cos(a) * 4.0f)); + } + dbg.add_trajectory(path, vec3(0, 0.5f, 1.0f)); +#endif + + WGPUSurfaceTexture surface_tex; + wgpuSurfaceGetCurrentTexture(g_surface, &surface_tex); + if (surface_tex.status == + WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal) { + const WGPUTextureViewDescriptor view_desc = { + .format = g_format, + .dimension = WGPUTextureViewDimension_2D, + .mipLevelCount = 1, + .arrayLayerCount = 1, + }; + + const WGPUTextureView view = + wgpuTextureCreateView(surface_tex.texture, &view_desc); + g_renderer.render(g_scene, g_camera, time, view); + wgpuTextureViewRelease(view); + wgpuSurfacePresent(g_surface); + wgpuTextureRelease(surface_tex.texture); + } + } + + g_renderer.shutdown(); + g_textures.shutdown(); + platform_shutdown(&platform_state); + return 0; +}
\ No newline at end of file |
