From f7abfa14cfab77d569b9c29b409006765d385531 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 28 Apr 2025 00:52:59 -0500 Subject: [PATCH] 20kdm2: add bezier patch rendering --- example/bsp/20kdm2.cpp | 259 ++++++++++++++++++++++++++++------------- example/bsp/bsp.mk | 1 + math/bezier.hpp | 104 +++++++++++++++++ math/geometry.hpp | 2 - q3bsp/debug.c | 29 ++++- q3bsp/q3bsp.h | 17 ++- q3bsp/q3bsp_patch.cpp | 138 ++++++++++++++++++++++ q3bsp/q3bsp_patch.hpp | 31 +++++ 8 files changed, 491 insertions(+), 90 deletions(-) create mode 100644 math/bezier.hpp create mode 100644 q3bsp/q3bsp_patch.cpp create mode 100644 q3bsp/q3bsp_patch.hpp diff --git a/example/bsp/20kdm2.cpp b/example/bsp/20kdm2.cpp index aa69f54..031b085 100644 --- a/example/bsp/20kdm2.cpp +++ b/example/bsp/20kdm2.cpp @@ -71,6 +71,7 @@ #include "bsp/20kdm2/textures/sfx/flame8.data.h" #include "q3bsp/q3bsp.h" +#include "q3bsp/q3bsp_patch.hpp" #include "bsp/20kdm2/maps/20kdm2.bsp.h" #include "bsp/20kdm2/texture.h" @@ -252,7 +253,7 @@ void global_polygon_type_4(ta_parameter_writer& writer, ; const uint32_t isp_tsp_instruction_word = isp_tsp_instruction_word::depth_compare_mode::greater - | isp_tsp_instruction_word::culling_mode::cull_if_negative + | isp_tsp_instruction_word::culling_mode::no_culling // cull_if_negative ; const uint32_t tsp_instruction_word = tsp_instruction_word::fog_control::no_fog @@ -601,7 +602,7 @@ float light_intensity(vec3 light_vec, vec3 n) static vec3 light_vec = {20, -20, -20}; -static inline void transfer_face(ta_parameter_writer& writer, q3bsp_face_t * face, int * last_texture, int * last_lm_index) +static inline void transfer_face_meshverts(ta_parameter_writer& writer, q3bsp_face_t * face) { uint8_t * buf = reinterpret_cast(bsp_start); q3bsp_header_t * header = reinterpret_cast(buf); @@ -617,26 +618,6 @@ static inline void transfer_face(ta_parameter_writer& writer, q3bsp_face_t * fac int triangles = face->n_meshverts / 3; - int textures_length = (sizeof (textures)) / (sizeof (textures[0])); - - bool has_texture = 1 && - (face->texture >= 0) && - (face->texture < textures_length) && - (textures[face->texture].size != 0); - - if (!has_texture) - return; - - if (face->texture != *last_texture || face->lm_index != *last_lm_index) { - *last_texture = face->texture; - *last_lm_index = face->lm_index; - if (has_texture) { - global_texture_lightmap(writer, face->texture, face->lm_index); - } else { - //global_polygon_type_1(writer, 0, 0, 0); - } - } - for (int j = 0; j < triangles; j++) { int aix = mv[j * 3 + 0].offset + face->vertex; @@ -657,60 +638,133 @@ static inline void transfer_face(ta_parameter_writer& writer, q3bsp_face_t * fac float li0 = light_intensity(light_vec, n); const float li1 = 2.0; - if (has_texture) { - float v_mul = textures[face->texture].v_mul; - vec2 at = {vert[aix].texcoord[0], vert[aix].texcoord[1] * v_mul}; - vec2 bt = {vert[bix].texcoord[0], vert[bix].texcoord[1] * v_mul}; - vec2 ct = {vert[cix].texcoord[0], vert[cix].texcoord[1] * v_mul}; + float v_mul = textures[face->texture].v_mul; + vec2 at = {vert[aix].texture[0], vert[aix].texture[1] * v_mul}; + vec2 bt = {vert[bix].texture[0], vert[bix].texture[1] * v_mul}; + vec2 ct = {vert[cix].texture[0], vert[cix].texture[1] * v_mul}; - vec2 alm = {vert[aix].lightmapcoord[0], vert[aix].lightmapcoord[1]}; - vec2 blm = {vert[bix].lightmapcoord[0], vert[bix].lightmapcoord[1]}; - vec2 clm = {vert[cix].lightmapcoord[0], vert[cix].lightmapcoord[1]}; + vec2 alm = {vert[aix].lightmap[0], vert[aix].lightmap[1]}; + vec2 blm = {vert[bix].lightmap[0], vert[bix].lightmap[1]}; + vec2 clm = {vert[cix].lightmap[0], vert[cix].lightmap[1]}; - if (ap.z < 0 || bp.z < 0 || cp.z < 0) { - render_clip_tri_type_13(writer, - ap, - bp, - cp, - at, - bt, - ct, - alm, - blm, - clm, - li0, - li1); - } else { - vec3 aps = screen_transform(ap); - vec3 bps = screen_transform(bp); - vec3 cps = screen_transform(cp); - - render_tri_type_13(writer, - aps, - bps, - cps, - at, - bt, - ct, - alm, - blm, - clm, - li0, - li1); - } + if (ap.z < 0 || bp.z < 0 || cp.z < 0) { + render_clip_tri_type_13(writer, + ap, + bp, + cp, + at, + bt, + ct, + alm, + blm, + clm, + li0, + li1); } else { - /* - render_tri_type_2(writer, - screen_transform(ap), - screen_transform(bp), - screen_transform(cp), - li); - */ + vec3 aps = screen_transform(ap); + vec3 bps = screen_transform(bp); + vec3 cps = screen_transform(cp); + + render_tri_type_13(writer, + aps, + bps, + cps, + at, + bt, + ct, + alm, + blm, + clm, + li0, + li1); } } } -void transfer_faces(ta_parameter_writer& writer) +static inline void transfer_face_patch_surfaces(ta_parameter_writer& writer, const mat4x4& trans, q3bsp_face_t * face, int face_ix) +{ + using namespace q3bsp_patch; + + patch * patch = NULL; + for (int i = 0; i < patch_count; i++) { + if (patches[i].face_ix == face_ix) { + patch = &patches[i]; + break; + } + } + assert(patch != nullptr); + + const int width = face->size[0]; + const int height = face->size[1]; + const int h_surfaces = (width - 1) / 2; + const int v_surfaces = (height - 1) / 2; + const int surface_count = h_surfaces * v_surfaces; + assert(surface_count > 0); + + const vertex_plm * vertices = &patch_vertices[patch->vertex_ix]; + const bezier::triangle * triangles = &patch_triangles[patch->triangle_ix]; + + const int triangle_count = surface_count * triangles_per_surface; + for (int i = 0; i < triangle_count; i++) { + vis_tri_count += 1; + + const bezier::triangle * triangle = &triangles[i]; + assert(triangle->a >= 0 && triangle->b >= 0 && triangle->c >= 0); + if (triangle->a == triangle->b && triangle->b == triangle->c) { + printf("face_ix %d %d\n", face_ix, i); + printf(" %d %d %d\n", triangle->a, triangle->b, triangle->c); + assert(false); + } + + const vertex_plm * av = &vertices[triangle->a]; + const vertex_plm * bv = &vertices[triangle->b]; + const vertex_plm * cv = &vertices[triangle->c]; + + vec3 ap = trans * av->l; + vec3 bp = trans * bv->l; + vec3 cp = trans * cv->l; + + if (ap.z < 0 || bp.z < 0 || cp.z < 0) { + continue; + //printf("cont %f %f %f\n", ap.x, ap.y, ap.z); + } + /* + printf("%f %f %f\n", ap.x, ap.y, ap.z); + printf("%f %f %f\n", bp.x, bp.y, bp.z); + printf("%f %f %f\n", cp.x, cp.y, cp.z); + */ + + vec3 aps = screen_transform(ap); + vec3 bps = screen_transform(bp); + vec3 cps = screen_transform(cp); + + const vec2& at = av->m; + const vec2& bt = bv->m; + const vec2& ct = cv->m; + + const vec2& alm = av->n; + const vec2& blm = bv->n; + const vec2& clm = cv->n; + + float li0 = 1.0; + float li1 = 2.0; + + render_tri_type_13(writer, + aps, + bps, + cps, + at, + bt, + ct, + alm, + blm, + clm, + li0, + li1); + } +} + +void transfer_faces(ta_parameter_writer& writer, const mat4x4& trans) { uint8_t * buf = reinterpret_cast(bsp_start); q3bsp_header_t * header = reinterpret_cast(buf); @@ -723,8 +777,30 @@ void transfer_faces(ta_parameter_writer& writer) int last_texture = -1; int last_lm_index = -1; + const int textures_length = (sizeof (textures)) / (sizeof (textures[0])); + for (int i = 0; i < face_count; i++) { - transfer_face(writer, &faces[i], &last_texture, &last_lm_index); + q3bsp_face_t * face = &faces[i]; + + bool has_texture = + (face->texture >= 0) && + (face->texture < textures_length) && + (textures[face->texture].size != 0); + + if (!has_texture) + continue; + + if (face->texture != last_texture || face->lm_index != last_lm_index) { + last_texture = face->texture; + last_lm_index = face->lm_index; + + global_texture_lightmap(writer, face->texture, face->lm_index); + } + + if (face->type == FACE_TYPE_POLYGON || face->type == FACE_TYPE_MESH) + transfer_face_meshverts(writer, face); + if (face->type == FACE_TYPE_PATCH) + transfer_face_patch_surfaces(writer, trans, face, i); } } @@ -803,9 +879,9 @@ static inline void transfer_face_billboard(ta_parameter_writer& writer, q3bsp_fa continue; } - vec2 at = {vert[aix].texcoord[0], vert[aix].texcoord[1]}; - vec2 bt = {vert[bix].texcoord[0], vert[bix].texcoord[1]}; - vec2 ct = {vert[cix].texcoord[0], vert[cix].texcoord[1]}; + vec2 at = {vert[aix].texture[0], vert[aix].texture[1]}; + vec2 bt = {vert[bix].texture[0], vert[bix].texture[1]}; + vec2 ct = {vert[cix].texture[0], vert[cix].texture[1]}; render_tri_type_7(writer, screen_transform(ap), @@ -1250,12 +1326,35 @@ void render_leaf_faces(ta_parameter_writer& writer, const mat4x4& trans, q3bsp_l int last_texture = -1; int last_lm_index = -1; + const int textures_length = (sizeof (textures)) / (sizeof (textures[0])); + for (int i = 0; i < leaf->n_leaffaces; i++) { int face_ix = lf[i].face; if (face_cache[face_ix] != 0) continue; face_cache[face_ix] = 1; - transfer_face(writer, &faces[face_ix], &last_texture, &last_lm_index); + + q3bsp_face_t * face = &faces[face_ix]; + + bool has_texture = + (face->texture >= 0) && + (face->texture < textures_length) && + (textures[face->texture].size != 0); + + if (!has_texture) + continue; + + if (face->texture != last_texture || face->lm_index != last_lm_index) { + last_texture = face->texture; + last_lm_index = face->lm_index; + + global_texture_lightmap(writer, face->texture, face->lm_index); + } + + if (face->type == FACE_TYPE_POLYGON || face->type == FACE_TYPE_MESH) + transfer_face_meshverts(writer, face); + if (face->type == FACE_TYPE_PATCH) + transfer_face_patch_surfaces(writer, trans, face, face_ix); } } @@ -1398,7 +1497,7 @@ void transfer_scene(ta_parameter_writer& writer, const mat4x4& screen_trans, con q3bsp_direntry * fe = &header->direntries[LUMP_FACES]; int face_count = fe->length / (sizeof (struct q3bsp_face)); - //transfer_faces(writer); + //transfer_faces(writer, trans); //transfer_icosphere(writer, trans); //render_matrix(writer, screen_trans); @@ -1548,7 +1647,8 @@ void transfer_textures() printf("lightmap base: %d\n", lightmap_base); - int textures_length = (sizeof (textures)) / (sizeof (textures[0])); + const int textures_length = (sizeof (textures)) / (sizeof (textures[0])); + for (int i = 0; i < textures_length; i++) { uint32_t offset = texture_memory_alloc.texture.start + font_base + lightmap_base + textures[i].offset; void * dst = reinterpret_cast(&ta_fifo_texture_memory[offset / 4]); @@ -1805,6 +1905,8 @@ int main() | isp_feed_cfg::pre_sort_mode ; + holly.FPU_SHAD_SCALE = fpu_shad_scale::simple_shadow_enable::parameter_selection_volume_mode; + system.IML6NRM = istnrm::end_of_render_tsp | istnrm::v_blank_in | istnrm::end_of_transferring_opaque_modifier_volume_list; @@ -1838,9 +1940,10 @@ int main() 0.0, 0.0, 0.0, 1.0, }; - do_get_condition(); + q3bsp_patch::triangulate_patches(bsp_start); + printf("patch_count %d\n", q3bsp_patch::patch_count); - holly.FPU_SHAD_SCALE = fpu_shad_scale::simple_shadow_enable::parameter_selection_volume_mode; + do_get_condition(); while (1) { maple::dma_wait_complete(); diff --git a/example/bsp/bsp.mk b/example/bsp/bsp.mk index a99582e..2536161 100644 --- a/example/bsp/bsp.mk +++ b/example/bsp/bsp.mk @@ -13,6 +13,7 @@ BSP_20KDM2_OBJ = \ maple/maple.o \ font/font_bitmap.o \ font/verite_8x16/verite_8x16.data.o \ + q3bsp/q3bsp_patch.o \ bsp/20kdm2/maps/20kdm2.bsp.o \ bsp/20kdm2/textures/e7/e7walldesign01b.data.o \ bsp/20kdm2/textures/e7/e7steptop2.data.o \ diff --git a/math/bezier.hpp b/math/bezier.hpp new file mode 100644 index 0000000..000569f --- /dev/null +++ b/math/bezier.hpp @@ -0,0 +1,104 @@ +#include + +#include "math/vec.hpp" + +namespace bezier { + +template +struct vec_lmn { + vec l; + vec m; + vec n; +}; + +struct triangle { + int a; + int b; + int c; +}; + +template +constexpr inline +vec_lmn +interpolate_quadratic(const T d, + const vec& l1, const vec& m1, const vec& n1, + const vec& l2, const vec& m2, const vec& n2, + const vec& l3, const vec& m3, const vec& n3) +{ + T invd = 1.0 - d; + T d1 = invd * invd; + T d2 = 2.0 * d * invd; + T d3 = d * d; + + vec l; + for (int i = 0; i < L; i++) { + l[i] = l1[i] * d1 + l2[i] * d2 + l3[i] * d3; + }; + + vec m; + for (int i = 0; i < M; i++) { + m[i] = m1[i] * d1 + m2[i] * d2 + m3[i] * d3; + } + + vec n; + for (int i = 0; i < N; i++) { + n[i] = n1[i] * d1 + n2[i] * d2 + n3[i] * d3; + }; + + return {l, m, n}; +} + +template +constexpr inline +vec_lmn +interpolate_quadratic(const T d, + const vec_lmn& va, + const vec_lmn& vb, + const vec_lmn& vc) +{ + return interpolate_quadratic(d, + va.l, va.m, va.n, + vb.l, vb.m, vb.n, + vc.l, vc.m, vc.n); +} + +template +constexpr inline +void +tessellate(const int level, + const vec_lmn * control, // [9] + vec_lmn * vertices, // [2 * level * level] + triangle * triangles, + int vertex_base = 0) +{ + vec_lmn column[3][level + 1]; + + T inv_level = 1.0 / level; + for (int i = 0; i <= level; i++) { + T d = inv_level * i; + column[0][i] = interpolate_quadratic(d, control[0], control[3], control[6]); + column[1][i] = interpolate_quadratic(d, control[1], control[4], control[7]); + column[2][i] = interpolate_quadratic(d, control[2], control[5], control[8]); + } + + for (int i = 0; i <= level; i++) { + for (int j = 0; j <= level; j++) { + T d = inv_level * j; + *vertices++ = interpolate_quadratic(d, column[0][i], column[1][i], column[2][i]); + } + } + + for (int i = 0; i < level; i++) { + for (int j = 0; j < level; j++) { + int ix = vertex_base + j * (level + 1) + i; + *triangles++ = {ix + 0, + ix + (level + 1) + 0, + ix + (level + 1) + 1}; + *triangles++ = {ix + 0, + ix + (level + 1) + 1, + ix + 1}; + } + } +} + +} diff --git a/math/geometry.hpp b/math/geometry.hpp index 7ac55f3..6b91923 100644 --- a/math/geometry.hpp +++ b/math/geometry.hpp @@ -1,5 +1,3 @@ -#include - #include "vec.hpp" namespace geometry { diff --git a/q3bsp/debug.c b/q3bsp/debug.c index 561bf0f..21d2b70 100644 --- a/q3bsp/debug.c +++ b/q3bsp/debug.c @@ -105,15 +105,32 @@ void print_faces(uint8_t * buf, struct q3bsp_header * header) q3bsp_direntry * fe = &header->direntries[LUMP_FACES]; q3bsp_face_t * faces = reinterpret_cast(&buf[fe->offset]); + q3bsp_direntry * ve = &header->direntries[LUMP_VERTEXES]; + q3bsp_vertex_t * vertexes = reinterpret_cast(&buf[ve->offset]); + int count = fe->length / (sizeof (struct q3bsp_face)); - printf("faces count: %d\n", count); - for (int i = 0; i < count; i++) { - q3bsp_face_t * face = &faces[i]; - if (face->texture == 23 || face->texture == 24 || face->type == 4){ - printf("face [%d]\n", i); - printf(" type=%d n_vertexes=%d n_meshverts=%d texture=%d lightmap=%d\n", face->type, face->n_vertexes, face->n_meshverts, face->texture, face->lm_index); + //printf("faces count: %d\n", count); + for (int face_ix = 0; face_ix < count; face_ix++) { + q3bsp_face_t * face = &faces[face_ix]; + if (face->type != 2) + continue; + //printf("face [%d]\n", face_ix); + //printf(" type=%d n_vertexes=%d n_meshverts=%d texture=%d lightmap=%d size=(%d,%d)\n", face->type, face->n_vertexes, face->n_meshverts, face->texture, face->lm_index, face->size[0], face->size[1]); + + + //printf("["); + printf("(%d, %d)", face->size[0], face->size[1]); + /* + for (int i = 0 ; i < face->n_vertexes; i++) { + q3bsp_vertex_t * vertex = &vertexes[face->vertex + i]; + printf("(%.00f,%.00f,%.00f)", (vertex->position[0]), (vertex->position[1]), (vertex->position[2])); + if (i < face->n_vertexes - 1) + printf(","); } + */ + //printf("],"); + printf("\n"); } } diff --git a/q3bsp/q3bsp.h b/q3bsp/q3bsp.h index 293df1b..832df76 100644 --- a/q3bsp/q3bsp.h +++ b/q3bsp/q3bsp.h @@ -1,3 +1,5 @@ +#pragma once + #include typedef struct q3bsp_direntry { @@ -29,7 +31,7 @@ enum q3bsp_lumps { LUMP_LIGHTMAPS = 14, LUMP_LIGHTVOLS = 15, LUMP_VISDATA = 16, -} q3bsp_lumps; +}; /* typedef struct q3bsp_entity { @@ -96,8 +98,8 @@ typedef struct q3bsp_brushside { typedef struct q3bsp_vertex { float position[3]; - float texcoord[2]; - float lightmapcoord[2]; + float texture[2]; + float lightmap[2]; float normal[3]; uint8_t color[4]; } q3bsp_vertex_t; @@ -112,10 +114,17 @@ typedef struct q3bsp_effect { int unknown; } q3bsp_effect_t; +enum q3bsp_face_type { + FACE_TYPE_POLYGON = 1, + FACE_TYPE_PATCH = 2, + FACE_TYPE_MESH = 3, + FACE_TYPE_BILLBOARD = 4, +}; + typedef struct q3bsp_face { int texture; int effect; - int type; + int type; // enum q3bsp_face_type int vertex; int n_vertexes; int meshvert; diff --git a/q3bsp/q3bsp_patch.cpp b/q3bsp/q3bsp_patch.cpp new file mode 100644 index 0000000..e05d2cb --- /dev/null +++ b/q3bsp/q3bsp_patch.cpp @@ -0,0 +1,138 @@ +#include "math/vec2.hpp" +#include "math/vec3.hpp" + +#include "q3bsp/q3bsp.h" +#include "q3bsp/q3bsp_patch.hpp" + +#include "assert.h" + +namespace q3bsp_patch { + +using vec2 = vec<2, float>; +using vec3 = vec<3, float>; + +vertex_plm patch_vertices[max_surface_count * max_vertices_per_surface]; +constexpr int max_patch_vertices = (sizeof (patch_vertices)) / (sizeof (vertex_plm)); + +bezier::triangle patch_triangles[max_surface_count * max_triangles_per_surface]; +constexpr int max_patch_triangles = (sizeof (patch_triangles)) / ((sizeof (int)) * 3); + +patch patches[max_patch_count]; +int patch_count = 0; + +static int triangulate_patch(const q3bsp_vertex_t * vertexes, + const int width, + const int height, + vertex_plm * p_vtx, + bezier::triangle * p_tri) +{ + const int h_surfaces = (width - 1) / 2; + const int v_surfaces = (height - 1) / 2; + + int vertex_base = 0; + + for (int j = 0; j < v_surfaces; j++) { + for (int k = 0; k < h_surfaces; k++) { + int ix = j * width * 2 + k * 2; + const q3bsp_vertex_t * a = &vertexes[ix + width * 0 + 0]; + const q3bsp_vertex_t * b = &vertexes[ix + width * 0 + 1]; + const q3bsp_vertex_t * c = &vertexes[ix + width * 0 + 2]; + const q3bsp_vertex_t * d = &vertexes[ix + width * 1 + 0]; + const q3bsp_vertex_t * e = &vertexes[ix + width * 1 + 1]; + const q3bsp_vertex_t * f = &vertexes[ix + width * 1 + 2]; + const q3bsp_vertex_t * g = &vertexes[ix + width * 2 + 0]; + const q3bsp_vertex_t * h = &vertexes[ix + width * 2 + 1]; + const q3bsp_vertex_t * i = &vertexes[ix + width * 2 + 2]; + + const vec3 a_p = {a->position[0], a->position[1], a->position[2]}; + const vec3 b_p = {b->position[0], b->position[1], b->position[2]}; + const vec3 c_p = {c->position[0], c->position[1], c->position[2]}; + const vec3 d_p = {d->position[0], d->position[1], d->position[2]}; + const vec3 e_p = {e->position[0], e->position[1], e->position[2]}; + const vec3 f_p = {f->position[0], f->position[1], f->position[2]}; + const vec3 g_p = {g->position[0], g->position[1], g->position[2]}; + const vec3 h_p = {h->position[0], h->position[1], h->position[2]}; + const vec3 i_p = {i->position[0], i->position[1], i->position[2]}; + + const vec2 a_t = {a->texture[0], a->texture[1]}; + const vec2 b_t = {b->texture[0], b->texture[1]}; + const vec2 c_t = {c->texture[0], c->texture[1]}; + const vec2 d_t = {d->texture[0], d->texture[1]}; + const vec2 e_t = {e->texture[0], e->texture[1]}; + const vec2 f_t = {f->texture[0], f->texture[1]}; + const vec2 g_t = {g->texture[0], g->texture[1]}; + const vec2 h_t = {h->texture[0], h->texture[1]}; + const vec2 i_t = {i->texture[0], i->texture[1]}; + + const vec2 a_l = {a->lightmap[0], a->lightmap[1]}; + const vec2 b_l = {b->lightmap[0], b->lightmap[1]}; + const vec2 c_l = {c->lightmap[0], c->lightmap[1]}; + const vec2 d_l = {d->lightmap[0], d->lightmap[1]}; + const vec2 e_l = {e->lightmap[0], e->lightmap[1]}; + const vec2 f_l = {f->lightmap[0], f->lightmap[1]}; + const vec2 g_l = {g->lightmap[0], g->lightmap[1]}; + const vec2 h_l = {h->lightmap[0], h->lightmap[1]}; + const vec2 i_l = {i->lightmap[0], i->lightmap[1]}; + + const vertex_plm control[9] = { + {a_p, a_t, a_l}, {b_p, b_t, b_l}, {c_p, c_t, c_l}, + {d_p, d_t, d_l}, {e_p, e_t, e_l}, {f_p, f_t, f_l}, + {g_p, g_t, g_l}, {h_p, h_t, h_l}, {i_p, i_t, i_l}, + }; + + bezier::tessellate(level, + control, + p_vtx, + p_tri, + vertex_base); + p_vtx += vertices_per_surface; + p_tri += triangles_per_surface; + vertex_base += vertices_per_surface; + } + } + + return h_surfaces * v_surfaces; +} + +void triangulate_patches(const void * bsp) +{ + const uint8_t * buf = reinterpret_cast(bsp); + const q3bsp_header_t * header = reinterpret_cast(buf); + + const q3bsp_direntry * fe = &header->direntries[LUMP_FACES]; + const q3bsp_face_t * faces = reinterpret_cast(&buf[fe->offset]); + + const q3bsp_direntry * ve = &header->direntries[LUMP_VERTEXES]; + const q3bsp_vertex_t * vertexes = reinterpret_cast(&buf[ve->offset]); + + int face_count = fe->length / (sizeof (struct q3bsp_face)); + + int vertex_ix = 0; + int triangle_ix = 0; + + for (int i = 0; i < face_count; i++) { + const q3bsp_face_t * face = &faces[i]; + if (face->type == FACE_TYPE_PATCH) { + assert(vertex_ix < max_patch_vertices); + assert(triangle_ix < max_patch_triangles); + + int surface_count = triangulate_patch(&vertexes[face->vertex], + face->size[0], + face->size[1], + &patch_vertices[vertex_ix], + &patch_triangles[triangle_ix]); + assert(patch_count < max_patch_count); + patches[patch_count].face_ix = i; + patches[patch_count].vertex_ix = vertex_ix; + patches[patch_count].triangle_ix = triangle_ix; + patch_count += 1; + + vertex_ix += vertices_per_surface * surface_count; + triangle_ix += triangles_per_surface * surface_count; + } + } + assert(vertex_ix <= max_patch_vertices); + assert(triangle_ix <= max_patch_triangles); +} + +} diff --git a/q3bsp/q3bsp_patch.hpp b/q3bsp/q3bsp_patch.hpp new file mode 100644 index 0000000..206d89c --- /dev/null +++ b/q3bsp/q3bsp_patch.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "math/bezier.hpp" + +namespace q3bsp_patch { + constexpr int max_patch_count = 16; // 12 + constexpr int max_surface_count = 32; // 20 + constexpr int max_level = 3; + constexpr int level = 3; + + constexpr int max_vertices_per_surface = (max_level + 1) * (max_level + 1); + constexpr int max_triangles_per_surface = max_level * max_level * 2; + + constexpr int vertices_per_surface = (level + 1) * (level + 1); + constexpr int triangles_per_surface = level * level * 2; + + void triangulate_patches(const void * bsp); + extern int patch_count; + + using vertex_plm = bezier::vec_lmn; + + struct patch { + int face_ix; + int vertex_ix; + int triangle_ix; + }; + + extern vertex_plm patch_vertices[]; + extern bezier::triangle patch_triangles[]; + extern patch patches[]; +}