#include "pxl8_world.h" #include #include #include "pxl8_bsp.h" #include "pxl8_macros.h" #include "pxl8_procgen.h" struct pxl8_world { pxl8_bsp bsp; bool loaded; bool wireframe; u32 wireframe_color; }; pxl8_world* pxl8_world_create(void) { pxl8_world* world = (pxl8_world*)calloc(1, sizeof(pxl8_world)); if (!world) { pxl8_error("Failed to allocate world"); return NULL; } world->loaded = false; world->wireframe_color = 15; return world; } void pxl8_world_destroy(pxl8_world* world) { if (!world) return; if (world->loaded) { pxl8_bsp_destroy(&world->bsp); } free(world); } pxl8_result pxl8_world_generate(pxl8_world* world, pxl8_gfx* gfx, const pxl8_procgen_params* params) { if (!world || !gfx || !params) return PXL8_ERROR_INVALID_ARGUMENT; if (world->loaded) { pxl8_bsp_destroy(&world->bsp); world->loaded = false; } memset(&world->bsp, 0, sizeof(pxl8_bsp)); pxl8_result result = pxl8_procgen(&world->bsp, params); if (result != PXL8_OK) { pxl8_error("Failed to generate world"); pxl8_bsp_destroy(&world->bsp); return result; } world->loaded = true; return PXL8_OK; } pxl8_result pxl8_world_apply_textures(pxl8_world* world, const pxl8_world_texture* textures, u32 count) { if (!world || !world->loaded || !textures || count == 0) { return PXL8_ERROR_INVALID_ARGUMENT; } pxl8_bsp* bsp = &world->bsp; u32 max_texinfo = count * 6; bsp->texinfo = calloc(max_texinfo, sizeof(pxl8_bsp_texinfo)); if (!bsp->texinfo) { return PXL8_ERROR_OUT_OF_MEMORY; } bsp->num_texinfo = 0; for (u32 face_idx = 0; face_idx < bsp->num_faces; face_idx++) { pxl8_bsp_face* face = &bsp->faces[face_idx]; pxl8_vec3 normal = bsp->planes[face->plane_id].normal; u32 matched_texture_idx = count; for (u32 tex_idx = 0; tex_idx < count; tex_idx++) { if (textures[tex_idx].rule && textures[tex_idx].rule(&normal, face, bsp)) { matched_texture_idx = tex_idx; break; } } if (matched_texture_idx >= count) { pxl8_warn("No texture rule matched for face %u", face_idx); continue; } const pxl8_world_texture* matched = &textures[matched_texture_idx]; pxl8_vec3 u_axis, v_axis; if (fabsf(normal.y) > 0.9f) { u_axis = (pxl8_vec3){1.0f, 0.0f, 0.0f}; v_axis = (pxl8_vec3){0.0f, 0.0f, 1.0f}; } else if (fabsf(normal.x) > 0.7f) { u_axis = (pxl8_vec3){0.0f, 1.0f, 0.0f}; v_axis = (pxl8_vec3){0.0f, 0.0f, 1.0f}; } else { u_axis = (pxl8_vec3){1.0f, 0.0f, 0.0f}; v_axis = (pxl8_vec3){0.0f, 1.0f, 0.0f}; } u32 texinfo_idx = bsp->num_texinfo; bool found_existing = false; for (u32 i = 0; i < bsp->num_texinfo; i++) { if (strcmp(bsp->texinfo[i].name, matched->name) == 0 && bsp->texinfo[i].miptex == matched->texture_id && bsp->texinfo[i].u_axis.x == u_axis.x && bsp->texinfo[i].u_axis.y == u_axis.y && bsp->texinfo[i].u_axis.z == u_axis.z && bsp->texinfo[i].v_axis.x == v_axis.x && bsp->texinfo[i].v_axis.y == v_axis.y && bsp->texinfo[i].v_axis.z == v_axis.z) { texinfo_idx = i; found_existing = true; break; } } if (!found_existing) { if (bsp->num_texinfo >= max_texinfo) { pxl8_error("Too many unique texinfo entries"); return PXL8_ERROR_OUT_OF_MEMORY; } memcpy(bsp->texinfo[texinfo_idx].name, matched->name, sizeof(bsp->texinfo[texinfo_idx].name)); bsp->texinfo[texinfo_idx].name[sizeof(bsp->texinfo[texinfo_idx].name) - 1] = '\0'; bsp->texinfo[texinfo_idx].miptex = matched->texture_id; bsp->texinfo[texinfo_idx].u_offset = 0.0f; bsp->texinfo[texinfo_idx].v_offset = 0.0f; bsp->texinfo[texinfo_idx].u_axis = u_axis; bsp->texinfo[texinfo_idx].v_axis = v_axis; bsp->num_texinfo++; } face->texinfo_id = texinfo_idx; } pxl8_info("Applied %u textures to %u faces, created %u texinfo entries", count, bsp->num_faces, bsp->num_texinfo); return PXL8_OK; } pxl8_result pxl8_world_load(pxl8_world* world, const char* path) { if (!world || !path) return PXL8_ERROR_INVALID_ARGUMENT; if (world->loaded) { pxl8_bsp_destroy(&world->bsp); world->loaded = false; } memset(&world->bsp, 0, sizeof(pxl8_bsp)); pxl8_result result = pxl8_bsp_load(path, &world->bsp); if (result != PXL8_OK) { pxl8_error("Failed to load world: %s", path); return result; } world->loaded = true; pxl8_info("Loaded world: %s", path); return PXL8_OK; } void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) { if (!world || !gfx || !world->loaded) return; if (world->wireframe) { pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color); } else { pxl8_bsp_render_textured(gfx, &world->bsp, camera_pos); } } void pxl8_world_unload(pxl8_world* world) { if (!world || !world->loaded) return; pxl8_bsp_destroy(&world->bsp); world->loaded = false; } bool pxl8_world_is_loaded(const pxl8_world* world) { return world && world->loaded; }