Finalise implementing triangle clipping

This commit is contained in:
Abdelrahman Said 2024-07-13 18:26:15 +01:00
parent 00e7293b28
commit f6f2483b61
2 changed files with 160 additions and 33 deletions

View File

@ -8,10 +8,12 @@
#include "vector/vec.h" #include "vector/vec.h"
#include "window/window.h" #include "window/window.h"
#define INVALID_VERTEX -1
typedef struct { typedef struct {
u64 idx0; i64 idx0;
u64 idx1; i64 idx1;
u64 idx2; i64 idx2;
colour_t colour; colour_t colour;
} scene_triangle_t; } scene_triangle_t;
@ -41,8 +43,6 @@ typedef struct {
list_scene_triangle_t *triangles; list_scene_triangle_t *triangles;
} model_t; } model_t;
#define NULL_MODEL ((model_t){0}) #define NULL_MODEL ((model_t){0})
#define IS_NULL_MODEL(MODEL_PTR) \
(MODEL_PTR->vertices == NULL && MODEL_PTR->triangles == NULL)
typedef struct { typedef struct {
f32 scale; f32 scale;
@ -79,5 +79,6 @@ void draw_wireframe_triangle(window_t *wnd, Arena *arena, triangle_t triangle);
void draw_filled_triangle(window_t *wnd, Arena *arena, triangle_t triangle); void draw_filled_triangle(window_t *wnd, Arena *arena, triangle_t triangle);
void draw_shaded_triangle(window_t *wnd, Arena *arena, triangle_t triangle); void draw_shaded_triangle(window_t *wnd, Arena *arena, triangle_t triangle);
void draw_line(window_t *wnd, Arena *arena, line_t line, colour_t colour); void draw_line(window_t *wnd, Arena *arena, line_t line, colour_t colour);
bool is_null_model(const model_t *model);
#endif // !RASTERISER_H #endif // !RASTERISER_H

View File

@ -15,18 +15,24 @@ internal void render_instance(window_t *wnd, const rasteriser_scene_t *scene,
Arena *arena, mat4x4f_t camera_matrix, Arena *arena, mat4x4f_t camera_matrix,
mat3x4f_t projection_matrix, mat3x4f_t projection_matrix,
const instance_t *instance); const instance_t *instance);
internal void clip_instance(model_t *model, const rasteriser_scene_t *scene); internal void clip_instance(Arena *arena, model_t *model,
internal void clip_instance_against_plane(model_t *model, const rasteriser_scene_t *scene);
internal void clip_instance_against_plane(Arena *arena, model_t *model,
const bounding_sphere_t *sphere, const bounding_sphere_t *sphere,
const clipping_plane_t *plane); const clipping_plane_t *plane);
internal void clip_triangles_against_plane(model_t *model, internal void clip_triangles_against_plane(Arena *arena, model_t *model,
const clipping_plane_t *plane); const clipping_plane_t *plane);
internal void clip_triangle_against_plane(vec3f_t *v0, vec3f_t *v1, vec3f_t *v2, internal void clip_triangle_against_plane(Arena *arena, model_t *model,
i64 triangle_index,
const clipping_plane_t *plane); const clipping_plane_t *plane);
internal list_float *interpolate(Arena *arena, i32 i0, f32 d0, i32 i1, f32 d1); internal list_float *interpolate(Arena *arena, i32 i0, f32 d0, i32 i1, f32 d1);
internal inline void order_triangle_points(triangle_t *triangle); internal inline void order_triangle_points(triangle_t *triangle);
internal inline bounding_sphere_t internal inline bounding_sphere_t
get_bounding_sphere(const list_vec3f_t *vertices); get_bounding_sphere(const list_vec3f_t *vertices);
internal inline vec3f_t segment_intersection(const vec3f_t *a, const vec3f_t *b,
const clipping_plane_t *plane);
internal inline void copy_triangles(Arena *arena, list_scene_triangle_t *dst,
const list_scene_triangle_t *src);
internal inline f32 signed_distance(const vec3f_t *vertex, internal inline f32 signed_distance(const vec3f_t *vertex,
const clipping_plane_t *plane); const clipping_plane_t *plane);
@ -50,11 +56,17 @@ internal void render_instance(window_t *wnd, const rasteriser_scene_t *scene,
mat3x4f_t projection_matrix, mat3x4f_t projection_matrix,
const instance_t *instance) { const instance_t *instance) {
list_vec3f_t *transformed = list_create_with_capacity( list_vec3f_t *transformed = list_create_with_capacity(
vec3f_t, arena, instance->model->vertices->count * sizeof(vec3f_t)); vec3f_t, arena,
if (!transformed) { (instance->model->vertices->count + 10) * sizeof(vec3f_t));
list_scene_triangle_t *triangles = list_create_with_capacity(
scene_triangle_t, arena,
(instance->model->triangles->count + 10) * sizeof(scene_triangle_t));
if (!transformed || !triangles) {
return; return;
} }
copy_triangles(arena, triangles, instance->model->triangles);
mat4x4f_t t_mat = get_translation_matrix(instance->transform.position); mat4x4f_t t_mat = get_translation_matrix(instance->transform.position);
mat4x4f_t r_mat = get_rotation_matrix(instance->transform.rotation); mat4x4f_t r_mat = get_rotation_matrix(instance->transform.rotation);
mat4x4f_t s_mat = get_scaling_matrix((vec3f_t){instance->transform.scale, mat4x4f_t s_mat = get_scaling_matrix((vec3f_t){instance->transform.scale,
@ -79,10 +91,9 @@ internal void render_instance(window_t *wnd, const rasteriser_scene_t *scene,
list_append(vec3f_t, arena, transformed, vertex); list_append(vec3f_t, arena, transformed, vertex);
} }
model_t model = {transformed, instance->model->triangles}; model_t model = {transformed, triangles};
clip_instance(&model, scene); clip_instance(arena, &model, scene);
model_t *mdl = &model; if (is_null_model(&model)) {
if (IS_NULL_MODEL(mdl)) {
return; return;
} }
@ -101,8 +112,9 @@ internal void render_instance(window_t *wnd, const rasteriser_scene_t *scene,
list_append(vec2i_t, arena, projected, point); list_append(vec2i_t, arena, projected, point);
} }
for (u64 i = 0; i < instance->model->triangles->count; ++i) { for (u64 i = 0; i < triangles->count; ++i) {
scene_triangle_t triangle = list_get(instance->model->triangles, i); scene_triangle_t triangle = list_get(triangles, i);
triangle_t tri = { triangle_t tri = {
.p0 = list_get(projected, triangle.idx0), .p0 = list_get(projected, triangle.idx0),
.p1 = list_get(projected, triangle.idx1), .p1 = list_get(projected, triangle.idx1),
@ -112,6 +124,12 @@ internal void render_instance(window_t *wnd, const rasteriser_scene_t *scene,
.h2 = 1.0f, .h2 = 1.0f,
.colour = triangle.colour, .colour = triangle.colour,
}; };
if ((tri.p0.x == tri.p1.x && tri.p0.x == tri.p2.x) &&
(tri.p0.y == tri.p1.y && tri.p0.y == tri.p2.y)) {
continue;
}
draw_wireframe_triangle(wnd, arena, tri); draw_wireframe_triangle(wnd, arena, tri);
} }
} }
@ -269,20 +287,25 @@ void draw_line(window_t *wnd, Arena *arena, line_t line, colour_t colour) {
} }
} }
internal void clip_instance(model_t *model, const rasteriser_scene_t *scene) { bool is_null_model(const model_t *model) {
return model->vertices == NULL && model->triangles == NULL;
}
internal void clip_instance(Arena *arena, model_t *model,
const rasteriser_scene_t *scene) {
bounding_sphere_t sphere = get_bounding_sphere(model->vertices); bounding_sphere_t sphere = get_bounding_sphere(model->vertices);
const clipping_plane_t *plane = NULL; const clipping_plane_t *plane = NULL;
for (u64 i = 0; i < CLIPPING_PLANE_COUNT; ++i) { for (u64 i = 0; i < CLIPPING_PLANE_COUNT; ++i) {
plane = &(scene->planes[i]); plane = &(scene->planes[i]);
clip_instance_against_plane(model, &sphere, plane); clip_instance_against_plane(arena, model, &sphere, plane);
if (IS_NULL_MODEL(model)) { if (is_null_model(model)) {
return; return;
} }
} }
} }
internal void clip_instance_against_plane(model_t *model, internal void clip_instance_against_plane(Arena *arena, model_t *model,
const bounding_sphere_t *sphere, const bounding_sphere_t *sphere,
const clipping_plane_t *plane) { const clipping_plane_t *plane) {
f32 distance = signed_distance(&(sphere->centre), plane); f32 distance = signed_distance(&(sphere->centre), plane);
@ -293,28 +316,111 @@ internal void clip_instance_against_plane(model_t *model,
} else if (distance < neg_radius) { } else if (distance < neg_radius) {
*model = NULL_MODEL; *model = NULL_MODEL;
} else { } else {
clip_triangles_against_plane(model, plane); clip_triangles_against_plane(arena, model, plane);
} }
} }
internal void clip_triangles_against_plane(model_t *model, internal void clip_triangles_against_plane(Arena *arena, model_t *model,
const clipping_plane_t *plane) { const clipping_plane_t *plane) {
for (u64 i = 0; i < model->triangles->count; ++i) { u64 count = model->triangles->count;
scene_triangle_t triangle = list_get(model->triangles, i); for (u64 i = 0; i < count; ++i) {
vec3f_t *v0 = &(list_get(model->vertices, triangle.idx0)); clip_triangle_against_plane(arena, model, i, plane);
vec3f_t *v1 = &(list_get(model->vertices, triangle.idx1));
vec3f_t *v2 = &(list_get(model->vertices, triangle.idx2));
clip_triangle_against_plane(v0, v1, v2, plane);
} }
} }
internal void clip_triangle_against_plane(vec3f_t *v0, vec3f_t *v1, vec3f_t *v2, internal void clip_triangle_against_plane(Arena *arena, model_t *model,
i64 triangle_index,
const clipping_plane_t *plane) { const clipping_plane_t *plane) {
scene_triangle_t *triangle = &(list_get(model->triangles, triangle_index));
vec3f_t *v0 = &(list_get(model->vertices, triangle->idx0));
vec3f_t *v1 = &(list_get(model->vertices, triangle->idx1));
vec3f_t *v2 = &(list_get(model->vertices, triangle->idx2));
f32 d0 = signed_distance(v0, plane); f32 d0 = signed_distance(v0, plane);
f32 d1 = signed_distance(v1, plane); f32 d1 = signed_distance(v1, plane);
f32 d2 = signed_distance(v2, plane); f32 d2 = signed_distance(v2, plane);
if (d0 > 0.0f && d1 > 0.0f && d2 > 0.0f) { if (d0 >= 0.0f && d1 >= 0.0f && d2 >= 0.0f) {
return;
} else if (d0 < 0.0f && d1 < 0.0f && d2 < 0.0f) {
triangle->idx0 = INVALID_VERTEX;
triangle->idx1 = INVALID_VERTEX;
triangle->idx2 = INVALID_VERTEX;
} else if ((d0 < 0.0f && d1 < 0.0f) || (d0 < 0.0f && d2 < 0.0f) ||
(d1 < 0.0f && d2 < 0.0f)) {
vec3f_t *vp;
vec3f_t *vn0;
vec3f_t *vn1;
i64 *in0;
i64 *in1;
if (d0 > 0.0f) {
vp = v0;
vn0 = v1;
vn1 = v2;
in0 = &(triangle->idx1);
in1 = &(triangle->idx2);
} else if (d1 > 0.0f) {
vp = v1;
vn0 = v0;
vn1 = v2;
in0 = &(triangle->idx0);
in1 = &(triangle->idx2);
} else {
vp = v2;
vn0 = v0;
vn1 = v1;
in0 = &(triangle->idx0);
in1 = &(triangle->idx1);
}
list_append(vec3f_t, arena, model->vertices,
segment_intersection(vp, vn0, plane));
list_append(vec3f_t, arena, model->vertices,
segment_intersection(vp, vn1, plane));
*in0 = model->vertices->count - 2;
*in1 = model->vertices->count - 1;
} else {
vec3f_t *vn;
vec3f_t *vp0;
vec3f_t *vp1;
i64 *in;
i64 *ip;
if (d0 < 0.0f) {
vn = v0;
vp0 = v1;
vp1 = v2;
in = &(triangle->idx0);
ip = &(triangle->idx2);
} else if (d1 < 0.0f) {
vn = v1;
vp0 = v0;
vp1 = v2;
in = &(triangle->idx1);
ip = &(triangle->idx2);
} else {
vn = v2;
vp0 = v0;
vp1 = v1;
in = &(triangle->idx2);
ip = &(triangle->idx1);
}
list_append(vec3f_t, arena, model->vertices,
segment_intersection(vp0, vn, plane));
list_append(vec3f_t, arena, model->vertices,
segment_intersection(vp1, vn, plane));
*in = model->vertices->count - 2;
scene_triangle_t tri = {
.idx0 = *in,
.idx1 = *ip,
.idx2 = model->vertices->count - 1,
.colour = triangle->colour,
};
list_append(scene_triangle_t, arena, model->triangles, tri);
} }
} }
@ -383,8 +489,28 @@ get_bounding_sphere(const list_vec3f_t *vertices) {
}; };
} }
internal inline vec3f_t segment_intersection(const vec3f_t *a, const vec3f_t *b,
const clipping_plane_t *plane) {
f32 neg_d = plane->distance * -1.0f;
f32 na_dot = vec_dot(vec3f_t, plane->normal, *a);
vec3f_t ba = vec_sub(vec3f_t, *b, *a);
f32 nba_dot = vec_dot(vec3f_t, plane->normal, ba);
f32 t = (neg_d - na_dot) / nba_dot;
return vec_add(vec3f_t, *a, vec_mul_num(vec3f_t, ba, t));
}
internal inline void copy_triangles(Arena *arena, list_scene_triangle_t *dst,
const list_scene_triangle_t *src) {
for (u64 i = 0; i < src->count; ++i) {
scene_triangle_t tri = list_get(src, i);
list_append(scene_triangle_t, arena, dst, tri);
}
}
internal inline f32 signed_distance(const vec3f_t *vertex, internal inline f32 signed_distance(const vec3f_t *vertex,
const clipping_plane_t *plane) { const clipping_plane_t *plane) {
return (vertex->x * plane->normal.x) + (vertex->y + plane->normal.y) + return (vertex->x * plane->normal.x) + (vertex->y * plane->normal.y) +
(vertex->z + plane->normal.z) + plane->distance; (vertex->z * plane->normal.z) + plane->distance;
} }