sweet/sweet_geometry.c

526 lines
14 KiB
C

/*
* Sweet is a small library for basic math and small matrix operations.
* Copyright 2014 Luc Girod.
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <math.h>
#include "sweet_types.h"
#include "sweet_math.h"
/* Definition of chain list for faces */
struct face_item
{
unsigned int v[3];
struct face_item * next;
struct face_item * prev;
};
static struct face_item *
face_add (struct face_item * root, unsigned int v1, unsigned int v2, unsigned int v3)
{
struct face_item * n = malloc (sizeof (struct face_item));
if (n == NULL) { return root; }
n->next = root;
n->prev = NULL;
n->v[0] = v1;
n->v[1] = v2;
n->v[2] = v3;
if (root != NULL) { root->prev = n; }
return n;
}
static struct face_item *
face_rm (struct face_item * root, struct face_item * f)
{
struct face_item * next;
struct face_item * prev;
if (f == NULL) { return root; }
next = f->next;
prev = f->prev;
f->next = NULL;
f->prev = NULL;
/* If it's not at one "edge" */
if (next != NULL && prev != NULL)
{
prev->next = next;
next->prev = prev;
} /* if f is at the end */
else if (prev != NULL)
{
prev->next = NULL;
} /* if f is at the start */
else if (next != NULL)
{
next->prev = NULL;
return next;
}
else
{
return NULL;
}
return root;
}
static void
face_free (struct face_item * root)
{
while (root != NULL)
{
struct face_item * item = root;
root = root->next;
free (item);
}
}
/* Definition of map */
struct item
{
struct item * next;
unsigned int hash;
unsigned int id;
};
#define MAP_SIZE 8192
struct map
{
struct item * items[MAP_SIZE];
unsigned int count_item;
};
static void
map_init (struct map * map)
{
int i;
for (i = 0; i < MAP_SIZE; i++)
{
map->items[i] = NULL;
}
map->count_item = 0;
}
static void
map_free (struct map * map)
{
int i;
for (i = 0; i < MAP_SIZE; i++)
{
struct item * item = map->items[i];
while (item != NULL)
{
struct item * tmp = item;
item = item->next;
free (tmp);
}
map->items[i] = NULL;
}
map->count_item = 0;
}
static struct item *
map_get_item (struct map * map, unsigned int hash)
{
struct item * item = map->items[hash % MAP_SIZE];
while (item != NULL && item->hash != hash) { item = item->next; }
return item;
}
static int
map_add_item (struct map * map, unsigned int hash, int id)
{
struct item * next;
struct item * item;
if (map_get_item (map, hash) != NULL) { return 0; }
next = map->items[hash % MAP_SIZE];
item = malloc (sizeof (struct item));
if (item == NULL) { return -1; }
item->hash = hash;
item->next = next;
item->id = id;
map->count_item++;
map->items[hash % MAP_SIZE] = item;
return 1;
}
static unsigned int
hash_2_int (unsigned int a, unsigned int b)
{
return a > b ? a * a + a + b : b * b + b + a;
}
/* helper structure */
struct vertex_array
{
vec3 * vertices;
size_t buffer;
unsigned int nb_vertices;
};
struct tcoord_array
{
vec2 * uv;
size_t buffer;
unsigned int nb_uv;
};
/* Utils */
static unsigned int
middle_point_v (struct map * map, int * state,
struct vertex_array * v,
unsigned int a, unsigned int b)
{
unsigned int hash;
struct item * item;
unsigned int id;
vec3 middle;
hash = hash_2_int (a, b);
item = map_get_item (map, hash);
id = v->nb_vertices;
if (item != NULL) { *state = 0; return item->id; }
if (map_add_item (map, hash, id) == -1) { *state = -1; return 0; }
*state = 1;
middle = sweet_vector_middle3 (v->vertices[a], v->vertices[b]);
v->vertices[v->nb_vertices++] = sweet_vector_normalize3 (middle);
return id;
}
static unsigned int
middle_point_vt (struct map * map, int * state,
struct vertex_array * v, struct tcoord_array * t,
unsigned int a, unsigned int b)
{
unsigned int id = middle_point_v (map, state, v, a, b);
if (*state == 1)
{
t->uv[t->nb_uv++] = sweet_vector_middle2 (t->uv[a], t->uv[b]);
}
return id;
}
static struct face_item *
first_iteration (struct vertex_array * v,
unsigned int nb_indices, unsigned int * indices,
unsigned int nb_vertices, float * vertices)
{
struct face_item * root;
unsigned int i;
unsigned int j;
v->nb_vertices = nb_vertices;
for (i = 0, j = 0; i < v->nb_vertices; i++, j += 3)
{
v->vertices[i] = sweet_vector_normalize3 (sweet_vector_new3 (vertices[j], vertices[j+1], vertices[j+2]));
}
root = NULL;
for (i = 0; i < nb_indices; i += 3)
{
root = face_add (root, indices[i], indices[i+1], indices[i+2]);
}
return root;
}
static struct face_item *
first_iteration_vt (struct vertex_array * v, struct tcoord_array * t,
unsigned int nb_indices, unsigned int * indices,
unsigned int nb_vertices, float * vertices, float * tcoord)
{
unsigned int i;
unsigned int j;
t->nb_uv = nb_vertices;
for (i = 0, j = 0; i < t->nb_uv; i++, j += 2)
{
t->uv[i] = sweet_vector_new2 (tcoord[j], tcoord[j+1]);
}
return first_iteration (v, nb_indices, indices, nb_vertices, vertices);
}
static void
free_all (struct map * map, struct vertex_array * v, struct tcoord_array * t, struct face_item * root, void * a1, void * a2, void * a3)
{
map_free (map);
if (v != NULL && v->vertices != NULL) { free (v->vertices); }
if (t != NULL && t->uv != NULL) { free (t->uv); }
face_free (root);
if (a1 != NULL) { free (a1); }
if (a2 != NULL) { free (a2); }
if (a3 != NULL) { free (a3); }
}
static void
copy_indices (unsigned int * mesh_indices, struct face_item * root)
{
unsigned int i;
struct face_item * f;
for (f = root, i = 0; f != NULL; i += 3)
{
mesh_indices[i] = f->v[0];
mesh_indices[i+1] = f->v[1];
mesh_indices[i+2] = f->v[2];
f = f->next;
}
}
static void
copy_vertices (float * mesh_vertices, struct vertex_array * v, float scale)
{
unsigned int i;
unsigned int j;
for (i = 0, j = 0; i < v->nb_vertices; i++, j += 3)
{
mesh_vertices[j] = v->vertices[i].x * scale;
mesh_vertices[j+1] = v->vertices[i].y * scale;
mesh_vertices[j+2] = v->vertices[i].z * scale;
}
}
static struct face_item *
iterate (struct map * map, struct vertex_array * v, struct tcoord_array * t, struct face_item * root,
unsigned int * nb_faces, unsigned int nb_iterations)
{
unsigned int i;
for (i = 0; i < nb_iterations; i++)
{
struct face_item * face = root;
while (face != NULL)
{
int r1;
int r2;
int r3;
unsigned int a;
unsigned int b;
unsigned int c;
if (t != NULL)
{
a = middle_point_vt (map, &r1, v, t, face->v[0], face->v[1]);
b = middle_point_vt (map, &r2, v, t, face->v[1], face->v[2]);
c = middle_point_vt (map, &r3, v, t, face->v[2], face->v[0]);
}
else
{
a = middle_point_v (map, &r1, v, face->v[0], face->v[1]);
b = middle_point_v (map, &r2, v, face->v[1], face->v[2]);
c = middle_point_v (map, &r3, v, face->v[2], face->v[0]);
}
if (r1 == -1 || r2 == -1 || r3 == -1)
{
free_all (map, v, t, root, NULL, NULL, NULL);
return 0;
}
r1 = face->v[1];
r2 = face->v[2];
face->v[1] = a;
face->v[2] = c;
root = face_add (root, r1, b, a);
root = face_add (root, r2, c, b);
root = face_add (root, a, b, c);
*nb_faces += 3;
face = face->next;
}
}
return root;
}
int
sweet_geometry_subdivide_mesh_vt (unsigned int nb_iterations, float scale,
unsigned int nb_indices, unsigned int * indices,
unsigned int nb_vertices, float * vcoord, float * tcoord,
unsigned int * count_indices, unsigned int ** mesh_indices,
unsigned int * count_vertices, float ** mesh_vertices, float ** mesh_tcoord)
{
struct face_item * root;
unsigned int nb_faces;
struct map map;
struct vertex_array v;
struct tcoord_array t;
unsigned int i;
unsigned int j;
map_init (&map);
*count_vertices = 0;
*count_indices = 0;
nb_faces = nb_indices / 3;
v.buffer = nb_vertices * sizeof (vec3) * pow (4, nb_iterations);
v.vertices = malloc (v.buffer);
if (v.vertices == NULL) { return 0; }
t.buffer = nb_vertices * sizeof (vec2) * pow (4, nb_iterations);
t.uv = malloc (t.buffer);
if (t.uv == NULL) { free (v.vertices); return 0; }
root = first_iteration_vt (&v, &t, nb_indices, indices, nb_vertices, vcoord, tcoord);
root = iterate (&map, &v, &t, root, &nb_faces, nb_iterations);
(*mesh_indices) = malloc (nb_faces * 3 * sizeof (unsigned int));
(*mesh_vertices) = malloc (v.nb_vertices * sizeof (float) * 3);
(*mesh_tcoord) = malloc ((*count_vertices) * sizeof (float) * 2);
if ((*mesh_indices) == NULL || (*mesh_vertices) == NULL || (*mesh_tcoord) == NULL)
{
free_all (&map, &v, &t, root, *mesh_vertices, *mesh_indices, *mesh_tcoord);
return 0;
}
copy_indices (*mesh_indices, root);
copy_vertices (*mesh_vertices, &v, scale);
for (i = 0, j = 0; i < v.nb_vertices; i++, j += 2)
{
(*mesh_tcoord)[j] = t.uv[i].x;
(*mesh_tcoord)[j+1] = t.uv[i].y;
}
free_all (&map, &v, &t, root, *mesh_vertices, *mesh_indices, *mesh_tcoord);
(*count_indices) = nb_faces * 3;
(*count_vertices) = v.nb_vertices;
return nb_faces;
}
int
sweet_geometry_subdivide_mesh_v (unsigned int nb_iterations, float scale,
unsigned int nb_indices, unsigned int * indices,
unsigned int nb_vertices, float * vcoord,
unsigned int * count_indices, unsigned int ** mesh_indices,
unsigned int * count_vertices, float ** mesh_vertices)
{
struct face_item * root;
unsigned int nb_faces;
struct map map;
struct vertex_array v;
map_init (&map);
*count_vertices = 0;
*count_indices = 0;
nb_faces = nb_indices / 3;
v.buffer = nb_vertices * sizeof (vec3) * pow (4, nb_iterations);
v.vertices = malloc (v.buffer);
if (v.vertices == NULL) { return 0; }
root = first_iteration (&v, nb_indices, indices, nb_vertices, vcoord);
root = iterate (&map, &v, NULL, root, &nb_faces, nb_iterations);
(*mesh_indices) = malloc (nb_faces * 3 * sizeof (unsigned int));
(*mesh_vertices) = malloc (v.nb_vertices * sizeof (float) * 3);
if ((*mesh_indices) == NULL || (*mesh_vertices) == NULL)
{
free_all (&map, &v, NULL, root, *mesh_vertices, *mesh_indices, NULL);
return 0;
}
copy_indices (*mesh_indices, root);
copy_vertices (*mesh_vertices, &v, scale);
free_all (&map, &v, NULL, root, NULL, NULL, NULL);
(*count_indices) = nb_faces * 3;
(*count_vertices) = v.nb_vertices;
return nb_faces;
}
/* Icosphere */
static float icosphere_vertices_v[36] = {
-1, SWEET_GOLDEN_RATIO, 0,
1, SWEET_GOLDEN_RATIO, 0,
-1, -SWEET_GOLDEN_RATIO, 0,
1, -SWEET_GOLDEN_RATIO, 0,
0, -1, SWEET_GOLDEN_RATIO,
0, 1, SWEET_GOLDEN_RATIO,
0, -1, -SWEET_GOLDEN_RATIO,
0, 1, -SWEET_GOLDEN_RATIO,
SWEET_GOLDEN_RATIO, 0, -1,
SWEET_GOLDEN_RATIO, 0, 1,
-SWEET_GOLDEN_RATIO, 0, -1,
-SWEET_GOLDEN_RATIO, 0, 1
};
static unsigned int icosphere_indices_v[60] = {
0, 11, 5,
0, 5, 1,
0, 1, 7,
0, 7, 10,
0, 10, 11,
1, 5, 9,
5, 11, 4,
11, 10, 2,
10, 7, 6,
7, 1, 8,
3, 9, 4,
3, 4, 2,
3, 2, 6,
3, 6, 8,
3, 8, 9,
4, 9, 5,
2, 4, 11,
6, 2, 10,
8, 6, 7,
9, 8, 1
};
static unsigned int nb_indices = 60;
static unsigned int nb_vertices_v = 12;
int
sweet_geometry_icosphere (unsigned int nb_iterations, float scale,
unsigned int * count_indices, unsigned int ** mesh_indices,
unsigned int * count_vertices, float ** mesh_vertices)
{
return sweet_geometry_subdivide_mesh_v (nb_iterations, scale,
nb_indices, icosphere_indices_v, nb_vertices_v, icosphere_vertices_v,
count_indices, mesh_indices, count_vertices, mesh_vertices);
}