summaryrefslogtreecommitdiff
path: root/src/tests/3d/test_3d_render.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/3d/test_3d_render.cc')
-rw-r--r--src/tests/3d/test_3d_render.cc326
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