polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

commit f516711560551cdea1ac225e06fabcddd7400c0e
parent 6559cff442f3750fc20990d9f5bfe6003a926baa
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 24 Apr 2018 11:49:48 -0700

delaunay WIP

Diffstat:
MMakefile | 1+
Msrc/delaunay.c | 960+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/delaunay.h | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/game.c | 8++++----
Msrc/main.c | 3++-
Msrc/mat4/mat4.c | 2+-
Msrc/render.c | 2+-
Msrc/terrain.c | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/util.c | 4++--
Msrc/util.h | 2+-
10 files changed, 1123 insertions(+), 21 deletions(-)

diff --git a/Makefile b/Makefile @@ -25,6 +25,7 @@ OBJS += $(SRC)/update.o OBJS += $(SRC)/terrain.o OBJS += $(SRC)/slab.o OBJS += $(SRC)/slab_geom.o +OBJS += $(SRC)/delaunay.o OBJS += $(SRC)/geometry.o OBJS += $(SRC)/input.o OBJS += $(SRC)/util.o diff --git a/src/delaunay.c b/src/delaunay.c @@ -1 +1,961 @@ +/* +** delaunay.c : compute 2D delaunay triangulation in the plane. +** Copyright (C) 2005 Wael El Oraiby <wael.eloraiby@gmail.com> +** +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU Affero General Public License as +** published by the Free Software Foundation, either version 3 of the +** License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Affero General Public License for more details. +** +** You should have received a copy of the GNU Affero General Public License +** along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <assert.h> + +#include "delaunay.h" + +#define ON_RIGHT 1 +#define ON_SEG 0 +#define ON_LEFT -1 + +#define OUTSIDE -1 +#define ON_CIRCLE 0 +#define INSIDE 1 + +struct point2d_s; +struct face_s; +struct halfedge_s; +struct delaunay_s; + + + +#define REAL_ZERO 0.0l +#define REAL_ONE 1.0l +#define REAL_TWO 2.0l +#define REAL_FOUR 4.0l + + +typedef struct point2d_s point2d_t; +typedef struct face_s face_t; +typedef struct halfedge_s halfedge_t; +typedef struct delaunay_s delaunay_t; +typedef struct working_set_s working_set_t; + +typedef long double lreal; +typedef lreal mat3_t[3][3]; + +struct point2d_s { + real x, y; /* point coordinates */ + halfedge_t* he; /* point halfedge */ + unsigned int idx; /* point index in input buffer */ +}; + +struct face_s { + halfedge_t* he; /* a pointing half edge */ + unsigned int num_verts; /* number of vertices on this face */ +}; + +struct halfedge_s { + point2d_t* vertex; /* vertex */ + halfedge_t* pair; /* pair */ + halfedge_t* next; /* next */ + halfedge_t* prev; /* next^-1 */ + face_t* face; /* halfedge face */ +}; + +struct delaunay_s { + halfedge_t* rightmost_he; /* right most halfedge */ + halfedge_t* leftmost_he; /* left most halfedge */ + point2d_t* points; /* pointer to points */ + face_t* faces; /* faces of delaunay */ + unsigned int num_faces; /* face count */ + unsigned int start_point; /* start point index */ + unsigned int end_point; /* end point index */ +}; + +struct working_set_s { + halfedge_t* edges; /* all the edges (allocated in one shot) */ + face_t* faces; /* all the faces (allocated in one shot) */ + + unsigned int max_edge; /* maximum edge count: 2 * 3 * n where n is point count */ + unsigned int max_face; /* maximum face count: 2 * n where n is point count */ + + unsigned int num_edges; /* number of allocated edges */ + unsigned int num_faces; /* number of allocated faces */ + + halfedge_t* free_edge; /* pointer to the first free edge */ + face_t* free_face; /* pointer to the first free face */ +}; + +/* +* 3x3 matrix determinant +*/ +static lreal det3(mat3_t m) +{ + lreal res = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) + - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); + + return res; +} + +/* +* allocate a halfedge +*/ +static halfedge_t* halfedge_alloc() +{ + halfedge_t* d; + + d = (halfedge_t*)malloc(sizeof(halfedge_t)); + assert( NULL != d ); + memset(d, 0, sizeof(halfedge_t)); + + return d; +} + +/* +* free a halfedge +*/ +static void halfedge_free( halfedge_t* d ) +{ + assert( d != NULL ); + memset(d, 0, sizeof(halfedge_t)); + free(d); +} + +/* +* free all delaunay halfedges +*/ +void del_free_halfedges( delaunay_t *del ) +{ + unsigned int i; + halfedge_t *d, *sig; + + /* if there is nothing to do */ + if( del->points == NULL ) + return; + + for( i = 0; i <= (del->end_point - del->start_point); i++ ) + { + /* free all the halfedges around the point */ + d = del->points[i].he; + if( d != NULL ) + { + do { + sig = d->next; + halfedge_free( d ); + d = sig; + } while( d != del->points[i].he ); + del->points[i].he = NULL; + } + } +} + +/* +* compare 2 points when sorting +*/ +static int cmp_points( const void *_pt0, const void *_pt1 ) +{ + point2d_t *pt0, *pt1; + + pt0 = (point2d_t*)(_pt0); + pt1 = (point2d_t*)(_pt1); + + if( pt0->x < pt1->x ) + return -1; + else if( pt0->x > pt1->x ) + return 1; + else if( pt0->y < pt1->y ) + return -1; + else if( pt0->y > pt1->y ) + return 1; + printf("same: pt0 %f %f pt1 %f %f\n", + pt0->x, pt0->y, pt1->x, pt1->y); + assert(0 && "2 or more points share the same exact coordinate"); + return 0; /* Should not be given! */ +} + +/* +* classify a point relative to a segment +*/ +static int classify_point_seg( point2d_t *s, point2d_t *e, point2d_t *pt ) +{ + lreal se_x, se_y, spt_x, spt_y; + lreal res; + + se_x = e->x - s->x; + se_y = e->y - s->y; + + spt_x = pt->x - s->x; + spt_y = pt->y - s->y; + + res = (( se_x * spt_y ) - ( se_y * spt_x )); + if( res < REAL_ZERO ) + return ON_RIGHT; + else if( res > REAL_ZERO ) + return ON_LEFT; + + return ON_SEG; +} + +/* +* classify a point relative to a halfedge, -1 is left, 0 is on, 1 is right +*/ +static int del_classify_point( halfedge_t *d, point2d_t *pt ) +{ + point2d_t *s, *e; + + s = d->vertex; + e = d->pair->vertex; + + return classify_point_seg(s, e, pt); +} + +/* +* test if a point is inside a circle given by 3 points, 1 if inside, 0 if outside +*/ +static int in_circle( point2d_t *pt0, point2d_t *pt1, point2d_t *pt2, point2d_t *p ) +{ + // reduce the computational complexity by substracting the last row of the matrix + // ref: https://www.cs.cmu.edu/~quake/robust.html + lreal p0p_x, p0p_y, p1p_x, p1p_y, p2p_x, p2p_y, p0p, p1p, p2p, res; + mat3_t m; + + p0p_x = pt0->x - p->x; + p0p_y = pt0->y - p->y; + + p1p_x = pt1->x - p->x; + p1p_y = pt1->y - p->y; + + p2p_x = pt2->x - p->x; + p2p_y = pt2->y - p->y; + + p0p = p0p_x * p0p_x + p0p_y * p0p_y; + p1p = p1p_x * p1p_x + p1p_y * p1p_y; + p2p = p2p_x * p2p_x + p2p_y * p2p_y; + + m[0][0] = p0p_x; + m[0][1] = p0p_y; + m[0][2] = p0p; + + m[1][0] = p1p_x; + m[1][1] = p1p_y; + m[1][2] = p1p; + + m[2][0] = p2p_x; + m[2][1] = p2p_y; + m[2][2] = p2p; + + res = -det3(m); + + if( res < REAL_ZERO ) + return INSIDE; + else if( res > REAL_ZERO ) + return OUTSIDE; + + return ON_CIRCLE; +} + +/* +* initialize delaunay segment +*/ +static int del_init_seg( delaunay_t *del, int start ) +{ + halfedge_t *d0, *d1; + point2d_t *pt0, *pt1; + + /* init delaunay */ + del->start_point = start; + del->end_point = start + 1; + + /* setup pt0 and pt1 */ + pt0 = &(del->points[start]); + pt1 = &(del->points[start + 1]); + + /* allocate the halfedges and setup them */ + d0 = halfedge_alloc(); + d1 = halfedge_alloc(); + + d0->vertex = pt0; + d1->vertex = pt1; + + d0->next = d0->prev = d0; + d1->next = d1->prev = d1; + + d0->pair = d1; + d1->pair = d0; + + pt0->he = d0; + pt1->he = d1; + + del->rightmost_he = d1; + del->leftmost_he = d0; + + + return 0; +} + +/* +* initialize delaunay triangle +*/ +static int del_init_tri( delaunay_t *del, int start ) +{ + halfedge_t *d0, *d1, *d2, *d3, *d4, *d5; + point2d_t *pt0, *pt1, *pt2; + + /* initiate delaunay */ + del->start_point = start; + del->end_point = start + 2; + + /* setup the points */ + pt0 = &(del->points[start]); + pt1 = &(del->points[start + 1]); + pt2 = &(del->points[start + 2]); + + /* allocate the 6 halfedges */ + d0 = halfedge_alloc(); + d1 = halfedge_alloc(); + d2 = halfedge_alloc(); + d3 = halfedge_alloc(); + d4 = halfedge_alloc(); + d5 = halfedge_alloc(); + + if( classify_point_seg(pt0, pt2, pt1) == ON_LEFT ) /* first case */ + { + /* set halfedges points */ + d0->vertex = pt0; + d1->vertex = pt2; + d2->vertex = pt1; + + d3->vertex = pt2; + d4->vertex = pt1; + d5->vertex = pt0; + + /* set points halfedges */ + pt0->he = d0; + pt1->he = d2; + pt2->he = d1; + + /* next and next -1 setup */ + d0->next = d5; + d0->prev = d5; + + d1->next = d3; + d1->prev = d3; + + d2->next = d4; + d2->prev = d4; + + d3->next = d1; + d3->prev = d1; + + d4->next = d2; + d4->prev = d2; + + d5->next = d0; + d5->prev = d0; + + /* set halfedges pair */ + d0->pair = d3; + d3->pair = d0; + + d1->pair = d4; + d4->pair = d1; + + d2->pair = d5; + d5->pair = d2; + + del->rightmost_he = d1; + del->leftmost_he = d0; + + } else /* 2nd case */ + { + /* set halfedges points */ + d0->vertex = pt0; + d1->vertex = pt1; + d2->vertex = pt2; + + d3->vertex = pt1; + d4->vertex = pt2; + d5->vertex = pt0; + + /* set points halfedges */ + pt0->he = d0; + pt1->he = d1; + pt2->he = d2; + + /* next and next -1 setup */ + d0->next = d5; + d0->prev = d5; + + d1->next = d3; + d1->prev = d3; + + d2->next = d4; + d2->prev = d4; + + d3->next = d1; + d3->prev = d1; + + d4->next = d2; + d4->prev = d2; + + d5->next = d0; + d5->prev = d0; + + /* set halfedges pair */ + d0->pair = d3; + d3->pair = d0; + + d1->pair = d4; + d4->pair = d1; + + d2->pair = d5; + d5->pair = d2; + + del->rightmost_he = d2; + del->leftmost_he = d0; + } + + return 0; +} + +/* +* remove an edge given a halfedge +*/ +static void del_remove_edge( halfedge_t *d ) +{ + halfedge_t *next, *prev, *pair, *orig_pair; + + orig_pair = d->pair; + + next = d->next; + prev = d->prev; + pair = d->pair; + + assert(next != NULL); + assert(prev != NULL); + + next->prev = prev; + prev->next = next; + + + /* check to see if we have already removed pair */ + if( pair ) + pair->pair = NULL; + + /* check to see if the vertex points to this halfedge */ + if( d->vertex->he == d ) + d->vertex->he = next; + + d->vertex = NULL; + d->next = NULL; + d->prev = NULL; + d->pair = NULL; + + next = orig_pair->next; + prev = orig_pair->prev; + pair = orig_pair->pair; + + assert(next != NULL); + assert(prev != NULL); + + next->prev = prev; + prev->next = next; + + + /* check to see if we have already removed pair */ + if( pair ) + pair->pair = NULL; + + /* check to see if the vertex points to this halfedge */ + if( orig_pair->vertex->he == orig_pair ) + orig_pair->vertex->he = next; + + orig_pair->vertex = NULL; + orig_pair->next = NULL; + orig_pair->prev = NULL; + orig_pair->pair = NULL; + + + /* finally free the halfedges */ + halfedge_free(d); + halfedge_free(orig_pair); +} + +/* +* pass through all the halfedges on the left side and validate them +*/ +static halfedge_t* del_valid_left( halfedge_t* b ) +{ + point2d_t *g, *d, *u, *v; + halfedge_t *c, *du, *dg; + + g = b->vertex; /* base halfedge point */ + dg = b; + + d = b->pair->vertex; /* pair(halfedge) point */ + b = b->next; + + u = b->pair->vertex; /* next(pair(halfedge)) point */ + du = b->pair; + + v = b->next->pair->vertex; /* pair(next(next(halfedge)) point */ + + if( classify_point_seg(g, d, u) == ON_LEFT ) + { + /* 3 points aren't colinear */ + /* as long as the 4 points belong to the same circle, do the cleaning */ + assert( v != u && "1: floating point precision error"); + while( v != d && v != g && in_circle(g, d, u, v) == INSIDE ) + { + c = b->next; + du = b->next->pair; + del_remove_edge(b); + b = c; + u = du->vertex; + v = b->next->pair->vertex; + } + + assert( v != u && "2: floating point precision error"); + if( v != d && v != g && in_circle(g, d, u, v) == ON_CIRCLE ) + { + du = du->prev; + del_remove_edge(b); + } + } else /* treat the case where the 3 points are colinear */ + du = dg; + + assert(du->pair); + return du; +} + +/* +* pass through all the halfedges on the right side and validate them +*/ +static halfedge_t* del_valid_right( halfedge_t *b ) +{ + point2d_t *rv, *lv, *u, *v; + halfedge_t *c, *dd, *du; + + b = b->pair; + rv = b->vertex; + dd = b; + lv = b->pair->vertex; + b = b->prev; + u = b->pair->vertex; + du = b->pair; + + v = b->prev->pair->vertex; + + if( classify_point_seg(lv, rv, u) == ON_LEFT ) + { + assert( v != u && "1: floating point precision error"); + while( v != lv && v != rv && in_circle(lv, rv, u, v) == INSIDE ) + { + c = b->prev; + du = c->pair; + del_remove_edge(b); + b = c; + u = du->vertex; + v = b->prev->pair->vertex; + } + + assert( v != u && "1: floating point precision error"); + if( v != lv && v != rv && in_circle(lv, rv, u, v) == ON_CIRCLE ) + { + du = du->next; + del_remove_edge(b); + } + } else + du = dd; + + assert(du->pair); + return du; +} + + +/* +* validate a link +*/ +static halfedge_t* del_valid_link( halfedge_t *b ) +{ + point2d_t *g, *g_p, *d, *d_p; + halfedge_t *gd, *dd, *new_gd, *new_dd; + int a; + + g = b->vertex; + gd = del_valid_left(b); + g_p = gd->vertex; + + assert(b->pair); + d = b->pair->vertex; + dd = del_valid_right(b); + d_p = dd->vertex; + assert(b->pair); + + if( g != g_p && d != d_p ) { + a = in_circle(g, d, g_p, d_p); + + if( a != ON_CIRCLE ) { + if( a == INSIDE ) { + g_p = g; + gd = b; + } else { + d_p = d; + dd = b->pair; + } + } + } + + /* create the 2 halfedges */ + new_gd = halfedge_alloc(); + new_dd = halfedge_alloc(); + + /* setup new_gd and new_dd */ + + new_gd->vertex = gd->vertex; + new_gd->pair = new_dd; + new_gd->prev = gd; + new_gd->next = gd->next; + gd->next->prev = new_gd; + gd->next = new_gd; + + new_dd->vertex = dd->vertex; + new_dd->pair = new_gd; + new_dd->prev = dd->prev; + dd->prev->next = new_dd; + new_dd->next = dd; + dd->prev = new_dd; + + return new_gd; +} + +/* +* find the lower tangent between the two delaunay, going from left to right (returns the left half edge) +*/ +static halfedge_t* del_get_lower_tangent( delaunay_t *left, delaunay_t *right ) +{ + point2d_t *pl, *pr; + halfedge_t *right_d, *left_d, *new_ld, *new_rd; + int sl, sr; + + left_d = left->rightmost_he; + right_d = right->leftmost_he; + + do { + pl = left_d->prev->pair->vertex; + pr = right_d->pair->vertex; + + if( (sl = classify_point_seg(left_d->vertex, right_d->vertex, pl)) == ON_RIGHT ) { + left_d = left_d->prev->pair; + } + + if( (sr = classify_point_seg(left_d->vertex, right_d->vertex, pr)) == ON_RIGHT ) { + right_d = right_d->pair->next; + } + + } while( sl == ON_RIGHT || sr == ON_RIGHT ); + + /* create the 2 halfedges */ + new_ld = halfedge_alloc(); + new_rd = halfedge_alloc(); + + /* setup new_gd and new_dd */ + new_ld->vertex = left_d->vertex; + new_ld->pair = new_rd; + new_ld->prev = left_d->prev; + left_d->prev->next = new_ld; + new_ld->next = left_d; + left_d->prev = new_ld; + + new_rd->vertex = right_d->vertex; + new_rd->pair = new_ld; + new_rd->prev = right_d->prev; + right_d->prev->next = new_rd; + new_rd->next = right_d; + right_d->prev = new_rd; + + return new_ld; +} + +/* +* link the 2 delaunay together +*/ +static void del_link( delaunay_t *result, delaunay_t *left, delaunay_t *right ) +{ + point2d_t *u, *v, *ml, *mr; + halfedge_t *base; + + assert( left->points == right->points ); + + /* save the most right point and the most left point */ + ml = left->leftmost_he->vertex; + mr = right->rightmost_he->vertex; + + base = del_get_lower_tangent(left, right); + + u = base->next->pair->vertex; + v = base->pair->prev->pair->vertex; + + while( del_classify_point(base, u) == ON_LEFT || + del_classify_point(base, v) == ON_LEFT ) + { + base = del_valid_link(base); + u = base->next->pair->vertex; + v = base->pair->prev->pair->vertex; + } + + right->rightmost_he = mr->he; + left->leftmost_he = ml->he; + + /* TODO: this part is not needed, and can be optimized */ + while( del_classify_point( right->rightmost_he, right->rightmost_he->prev->pair->vertex ) == ON_RIGHT ) + right->rightmost_he = right->rightmost_he->prev; + + while( del_classify_point( left->leftmost_he, left->leftmost_he->prev->pair->vertex ) == ON_RIGHT ) + left->leftmost_he = left->leftmost_he->prev; + + result->leftmost_he = left->leftmost_he; + result->rightmost_he = right->rightmost_he; + result->points = left->points; + result->start_point = left->start_point; + result->end_point = right->end_point; +} + +/* +* divide and conquer delaunay +*/ +void del_divide_and_conquer( delaunay_t *del, int start, int end ) +{ + delaunay_t left, right; + int i, n; + + n = (end - start + 1); + + if( n > 3 ) { + i = (n / 2) + (n & 1); + left.points = del->points; + right.points = del->points; + del_divide_and_conquer( &left, start, start + i - 1 ); + del_divide_and_conquer( &right, start + i, end ); + del_link( del, &left, &right ); + } else { + if( n == 3 ) { + del_init_tri( del, start ); + } else { + if( n == 2 ) { + del_init_seg( del, start ); + } + } + } +} + +static void build_halfedge_face( delaunay_t *del, halfedge_t *d ) +{ + halfedge_t *curr; + + /* test if the halfedge has already a pointing face */ + if( d->face != NULL ) + return; + + /* TODO: optimize this */ + del->faces = (face_t*)realloc(del->faces, (del->num_faces + 1) * sizeof(face_t)); + assert( NULL != del->faces ); + + face_t *f = &(del->faces[del->num_faces]); + curr = d; + f->he = d; + f->num_verts = 0; + do { + curr->face = f; + (f->num_verts)++; + curr = curr->pair->prev; + } while( curr != d ); + + (del->num_faces)++; +} + +/* +* build the faces for all the halfedge +*/ +void del_build_faces( delaunay_t *del ) +{ + unsigned int i; + halfedge_t *curr; + + del->num_faces = 0; + del->faces = NULL; + + /* build external face first */ + build_halfedge_face(del, del->rightmost_he->pair); + + for( i = del->start_point; i <= del->end_point; i++ ) + { + curr = del->points[i].he; + + do { + build_halfedge_face( del, curr ); + curr = curr->next; + } while( curr != del->points[i].he ); + } +} + +/* +*/ +delaunay2d_t* delaunay2d_from(del_point2d_t *points, unsigned int num_points) { + delaunay2d_t* res = NULL; + delaunay_t del; + unsigned int i, j, fbuff_size = 0; + unsigned int* faces = NULL; + + /* allocate the points */ + del.points = (point2d_t*)malloc(num_points * sizeof(point2d_t)); + assert( NULL != del.points ); + memset(del.points, 0, num_points * sizeof(point2d_t)); + + /* copy the points */ + for( i = 0; i < num_points; i++ ) + { + del.points[i].idx = i; + del.points[i].x = points[i].x; + del.points[i].y = points[i].y; + } + + qsort(del.points, num_points, sizeof(point2d_t), cmp_points); + + if( num_points >= 3 ) { + del_divide_and_conquer( &del, 0, num_points - 1 ); + + del_build_faces( &del ); + + fbuff_size = 0; + for( i = 0; i < del.num_faces; i++ ) + fbuff_size += del.faces[i].num_verts + 1; + + faces = (unsigned int*)malloc(sizeof(unsigned int) * fbuff_size); + assert( NULL != faces ); + + j = 0; + for( i = 0; i < del.num_faces; i++ ) + { + halfedge_t *curr; + + faces[j] = del.faces[i].num_verts; + j++; + + curr = del.faces[i].he; + do { + faces[j] = curr->vertex->idx; + j++; + curr = curr->pair->prev; + } while( curr != del.faces[i].he ); + } + + del_free_halfedges( &del ); + + free(del.faces); + free(del.points); + } + + res = (delaunay2d_t*)malloc(sizeof(delaunay2d_t)); + assert( NULL != res ); + res->num_points = num_points; + res->points = (del_point2d_t*)malloc(sizeof(del_point2d_t) * num_points); + assert( NULL != res->points ); + memcpy(res->points, points, sizeof(del_point2d_t) * num_points); + res->num_faces = del.num_faces; + res->faces = faces; + + return res; +} + +void delaunay2d_release(delaunay2d_t *del) { + free(del->faces); + free(del->points); + free(del); +} + + +tri_delaunay2d_t* tri_delaunay2d_from(delaunay2d_t* del) { + unsigned int v_offset = del->faces[0] + 1; /* ignore external face */ + unsigned int dst_offset = 0; + unsigned int i; + + tri_delaunay2d_t* tdel = (tri_delaunay2d_t*)malloc(sizeof(tri_delaunay2d_t)); + assert( NULL != tdel ); + tdel->num_triangles = 0; + + /* count the number of triangles */ + if( 1 == del->num_faces ) { /* degenerate case: only external face exists */ + unsigned int nv = del->faces[0]; + tdel->num_triangles += nv - 2; + } else { + for( i = 1; i < del->num_faces; ++i ) { + unsigned int nv = del->faces[v_offset]; + tdel->num_triangles += nv - 2; + v_offset += nv + 1; + } + } + + /* copy points */ + tdel->num_points = del->num_points; + tdel->points = (del_point2d_t*)malloc(sizeof(del_point2d_t) * del->num_points); + assert( NULL != tdel->points ); + memcpy(tdel->points, del->points, sizeof(del_point2d_t) * del->num_points); + + /* build the triangles */ + tdel->tris = (unsigned int*)malloc(sizeof(unsigned int) * 3 * tdel->num_triangles); + assert( NULL != tdel->tris ); + + v_offset = del->faces[0] + 1; /* ignore external face */ + + if( 1 == del->num_faces ) { + /* handle the degenerated case where only the external face exists */ + unsigned int nv = del->faces[0]; + unsigned int j = 0; + v_offset = 1; + for( ; j < nv - 2; ++j ) { + tdel->tris[dst_offset] = del->faces[v_offset + j]; + tdel->tris[dst_offset + 1] = del->faces[(v_offset + j + 1) % nv]; + tdel->tris[dst_offset + 2] = del->faces[v_offset + j]; + dst_offset += 3; + } + } else { + for( i = 1; i < del->num_faces; ++i ) { + unsigned int nv = del->faces[v_offset]; + unsigned int j = 0; + unsigned int first = del->faces[v_offset + 1]; + + + for( ; j < nv - 2; ++j ) { + tdel->tris[dst_offset] = first; + tdel->tris[dst_offset + 1] = del->faces[v_offset + j + 2]; + tdel->tris[dst_offset + 2] = del->faces[v_offset + j + 3]; + dst_offset += 3; + } + + v_offset += nv + 1; + } + } + + return tdel; +} + + +void tri_delaunay2d_release(tri_delaunay2d_t* tdel) { + free(tdel->tris); + free(tdel->points); + free(tdel); +} diff --git a/src/delaunay.h b/src/delaunay.h @@ -0,0 +1,91 @@ +#ifndef DELAUNAY_H +#define DELAUNAY_H + +/* +** delaunay.c : compute 2D delaunay triangulation in the plane. +** Copyright (C) 2005 Wael El Oraiby <wael.eloraiby@gmail.com> +** +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU Affero General Public License as +** published by the Free Software Foundation, either version 3 of the +** License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Affero General Public License for more details. +** +** You should have received a copy of the GNU Affero General Public License +** along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +typedef double real; + +typedef struct { + real x, y; +} del_point2d_t; + +typedef struct { + /** input points count */ + unsigned int num_points; + + /** the input points */ + del_point2d_t* points; + + /** number of returned faces */ + unsigned int num_faces; + + /** the faces are given as a sequence: num verts, verts indices, num verts, verts indices... + * the first face is the external face */ + unsigned int* faces; +} delaunay2d_t; + +/* + * build the 2D Delaunay triangulation given a set of points of at least 3 points + * + * @points: point set given as a sequence of tuple x0, y0, x1, y1, .... + * @num_points: number of given point + * @preds: the incircle predicate + * @faces: the triangles given as a sequence: num verts, verts indices, num verts, verts indices. + * Note that the first face is the external face + * @return: the created topology + */ +delaunay2d_t* delaunay2d_from(del_point2d_t *points, unsigned int num_points); + +/* + * release a delaunay2d object + */ +void delaunay2d_release(delaunay2d_t* del); + + +typedef struct { + /** input points count */ + unsigned int num_points; + + /** input points */ + del_point2d_t* points; + + /** number of triangles */ + unsigned int num_triangles; + + /** the triangles indices v0,v1,v2, v0,v1,v2 .... */ + unsigned int* tris; +} tri_delaunay2d_t; + +/** + * build a tri_delaunay2d_t out of a delaunay2d_t object + */ +tri_delaunay2d_t* tri_delaunay2d_from(delaunay2d_t* del); + +/** + * release a tri_delaunay2d_t object + */ +void tri_delaunay2d_release(tri_delaunay2d_t* tdel); + +#ifdef __cplusplus +} +#endif + +#endif // DELAUNAY_H diff --git a/src/game.c b/src/game.c @@ -3,10 +3,10 @@ #include "game.h" mat4 *cam_init = (float[16]){ - 1, 0, 0, 0, - 0, 0.67, -0.74, 0, - 0, 0.74, 0.66, 0, - -0.1, -0.6, -3.0, 1 + 0.952107, 0.186872, -0.241083, 0.000000, + -0.302599, 0.688952, -0.656898, 0.000000, + 0.043785, 0.697792, 0.702532, 0.000000, + -26.815081, -19.055046, -8.465672, 1.000000 }; void game_init(struct game *game) { diff --git a/src/main.c b/src/main.c @@ -27,7 +27,7 @@ int main(void) "SDL2/OpenGL Demo", 0, 0, 640, 480, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); - srand(42); + srand(52); SDL_GL_CreateContext(window); init_gl(&game.test_resources); @@ -50,6 +50,7 @@ int main(void) // mesh it -> load into vbo /* Loop until the user closes the window */ + while (1) { process_events(); update(&game); diff --git a/src/mat4/mat4.c b/src/mat4/mat4.c @@ -296,7 +296,7 @@ mat4_print(const mat4 *m) { for (int i = 0; i < 16; ++i) { if (i % 4 == 0) printf("\n"); - printf("%f ", m[i]); + printf("%f, ", m[i]); } printf("\n"); } diff --git a/src/render.c b/src/render.c @@ -180,7 +180,7 @@ static void render_geom (struct resources *res, bind_ibo(&geom->buffer.index_buffer); glDrawElements( - GL_TRIANGLES, + GL_LINES, geom->num_indices, /* count */ GL_UNSIGNED_INT, /* type */ (void*)0 /* element array buffer offset */ diff --git a/src/terrain.c b/src/terrain.c @@ -1,6 +1,7 @@ #include "terrain.h" #include "util.h" +#include "delaunay.h" static const float plane_verts[] = { -1,-1,0, -1,1,0, 1,1,0, 1,-1,0 @@ -14,6 +15,11 @@ static const u32 plane_indices[] = { 2,1,0, 2,0,3 }; + +double old_noisy_boi(double x, double y) { + return sin(x) * cos(y) * 5.0; +} + void terrain_init(struct terrain *terrain) { terrain->width = 1; @@ -22,15 +28,63 @@ terrain_init(struct terrain *terrain) { void terrain_create(struct terrain *terrain) { - const int num_verts = 4; + u32 i; + const int num_verts = 1000; + del_point2d_t *points = calloc(num_verts, sizeof(*points)); + float *zs = calloc(num_verts * 3, sizeof(*zs)); + float *verts = calloc(num_verts * 3, sizeof(*verts)); + float *normals = calloc(num_verts * 3, sizeof(*normals)); + /* int *indices = calloc(num_verts, sizeof(*indices)); */ + + // 100 random samples from our noise function + for (i = 0; i < num_verts; i++) { + double x = rand_0to1() * 100.0; + double y = rand_0to1() * 100.0; + + double z = old_noisy_boi(x, y); + + points[i].x = x; + points[i].y = y; + zs[i] = z; + + verts[(i*3)] = (float)x; + verts[(i*3)+1] = (float)y; + verts[(i*3)+2] = (float)z; + } + + delaunay2d_t *del = delaunay2d_from(points, num_verts); + tri_delaunay2d_t *tri = tri_delaunay2d_from(del); - terrain->geom.num_verts = num_verts; - terrain->geom.vertices = (float*)plane_verts; - terrain->geom.normals = (float*)plane_normals; - terrain->geom.indices = (u32*)plane_indices; - terrain->geom.num_indices = ARRAY_SIZE(plane_indices); + // TODO: calc normals + for (i = 0; i < tri->num_points; i++) { + /* float y1 = */ + /* float nx = (y2-y1)*(z3-z1)-(y3-y1)*(z2*-z1); */ + /* float ny = (y2-y1)*(z3-z1)-(y3-y1)*(z2*-z1); */ + int n = i*3; + + verts[n] = (float)tri->points[i].x; + verts[n+1] = (float)tri->points[i].y; + verts[n+2] = 0; + + normals[n] = 0; + normals[n+1] = 0; + normals[n+2] = 1; + } + + terrain->geom.num_verts = tri->num_triangles * 3; + terrain->geom.vertices = (float*)verts; + terrain->geom.normals = (float*)normals; + terrain->geom.indices = tri->tris; + terrain->geom.num_indices = tri->num_triangles * 3; make_buffer_geometry(&terrain->geom); + + /* delaunay2d_release(del); */ + /* tri_delaunay2d_release(tri); */ + + /* free(points); */ + /* free(verts); */ + /* free(normals); */ } void @@ -38,8 +92,3 @@ terrain_detroy(struct terrain *terrain) { free(terrain->geom.vertices); free(terrain->geom.normals); } - - -float old_noisy_boi(float x, float y) { - return sinf(x) * cosf(y); -} diff --git a/src/util.c b/src/util.c @@ -11,8 +11,8 @@ void check_gl() { } -float rand_0to1() { - return (float) rand() / RAND_MAX; +double rand_0to1() { + return (double) rand() / RAND_MAX; } /* void glhLookAtf2( float *matrix, float *eyePosition3D, */ diff --git a/src/util.h b/src/util.h @@ -9,6 +9,6 @@ void check_gl(void); -float rand_0to1(); +double rand_0to1(); #endif /* PA_UTIL_H */