380 lines
9.0 KiB
C
380 lines
9.0 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 <string.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;
|
|
};
|
|
|
|
|
|
/* ICOSPHERE */
|
|
|
|
/* Local containers */
|
|
static vec3 base_vertices[12] = {
|
|
{-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}
|
|
};
|
|
|
|
/* Utils */
|
|
static int
|
|
middle_point (struct map * map,
|
|
struct vertex_array * v,
|
|
unsigned int a, unsigned int b)
|
|
{
|
|
unsigned int hash;
|
|
struct item * item;
|
|
unsigned int id;
|
|
vec3 v1;
|
|
vec3 v2;
|
|
vec3 middle;
|
|
|
|
hash = hash_2_int (a, b);
|
|
item = map_get_item (map, hash);
|
|
id = v->nb_vertices;
|
|
|
|
if (item != NULL) { return item->id; }
|
|
|
|
v1 = v->vertices[a];
|
|
v2 = v->vertices[b];
|
|
middle = sweet_vector_middle3 (v1, v2);
|
|
|
|
if (v->buffer < (v->nb_vertices * sizeof (vec3)))
|
|
{
|
|
vec3 * p;
|
|
v->buffer = v->buffer * 2;
|
|
p = realloc (v->vertices, v->buffer);
|
|
if (p == NULL) { return -1; }
|
|
v->vertices = p;
|
|
}
|
|
v->vertices[v->nb_vertices++] = sweet_vector_normalize3 (middle);
|
|
|
|
if (map_add_item (map, hash, id) == -1) { return -1; }
|
|
return id;
|
|
}
|
|
|
|
static struct face_item *
|
|
first_iteration (struct vertex_array * v)
|
|
{
|
|
struct face_item * root;
|
|
int i;
|
|
for (i = 0; i < v->nb_vertices; i++)
|
|
{
|
|
v->vertices[i] = sweet_vector_normalize3 (base_vertices[i]);
|
|
}
|
|
|
|
root = face_add (NULL, 0, 11, 5);
|
|
root = face_add (root, 0, 5, 1);
|
|
root = face_add (root, 0, 1, 7);
|
|
root = face_add (root, 0, 7, 10);
|
|
root = face_add (root, 0, 10, 11);
|
|
|
|
root = face_add (root, 1, 5, 9);
|
|
root = face_add (root, 5, 11, 4);
|
|
root = face_add (root, 11, 10, 2);
|
|
root = face_add (root, 10, 7, 6);
|
|
root = face_add (root, 7, 1, 8);
|
|
|
|
root = face_add (root, 3, 9, 4);
|
|
root = face_add (root, 3, 4, 2);
|
|
root = face_add (root, 3, 2, 6);
|
|
root = face_add (root, 3, 6, 8);
|
|
root = face_add (root, 3, 8, 9);
|
|
|
|
root = face_add (root, 4, 9, 5);
|
|
root = face_add (root, 2, 4, 11);
|
|
root = face_add (root, 6, 2, 10);
|
|
root = face_add (root, 8, 6, 7);
|
|
root = face_add (root, 9, 8, 1);
|
|
|
|
return root;
|
|
}
|
|
|
|
static void
|
|
free_all (struct map * map, struct vertex_array * array, struct face_item * root, void * a1, void * a2)
|
|
{
|
|
map_free (map);
|
|
if (array->vertices != NULL) { free (array->vertices); }
|
|
face_free (root);
|
|
if (a1 != NULL) { free (a1); }
|
|
if (a2 != NULL) { free (a2); }
|
|
}
|
|
|
|
int
|
|
icosphere (unsigned int nb_iterations, float scale,
|
|
unsigned int ** mesh_indices, unsigned int * count_indices,
|
|
float ** mesh_vertices, unsigned int * count_vertices)
|
|
{
|
|
struct face_item * root;
|
|
struct face_item * f;
|
|
unsigned int nb_faces;
|
|
struct map map;
|
|
struct vertex_array v;
|
|
|
|
unsigned int i;
|
|
unsigned int j;
|
|
|
|
map_init (&map);
|
|
|
|
*mesh_vertices = NULL;
|
|
*mesh_indices = NULL;
|
|
nb_faces = 20;
|
|
v.nb_vertices = 12;
|
|
v.buffer = v.nb_vertices * sizeof (vec3);
|
|
v.vertices = malloc (v.buffer);
|
|
if (v.vertices == NULL) { return 1; }
|
|
|
|
root = first_iteration (&v);
|
|
|
|
for (i = 1; i < nb_iterations; i++)
|
|
{
|
|
struct face_item * face = root;
|
|
while (face != NULL)
|
|
{
|
|
int a = middle_point (&map, &v, face->v[0], face->v[1]);
|
|
int b = middle_point (&map, &v, face->v[1], face->v[2]);
|
|
int c = middle_point (&map, &v, face->v[2], face->v[0]);
|
|
struct face_item * tmp;
|
|
|
|
if (a == -1 || b == -1 || c == -1)
|
|
{
|
|
free_all (&map, &v, root, *mesh_vertices, *mesh_indices);
|
|
*count_vertices = 0;
|
|
*count_indices = 0;
|
|
return 1;
|
|
}
|
|
|
|
tmp = face->next;
|
|
root = face_rm (root, face);
|
|
root = face_add (root, face->v[0], a, c);
|
|
root = face_add (root, face->v[1], b, a);
|
|
root = face_add (root, face->v[2], c, b);
|
|
root = face_add (root, a, b, c);
|
|
free (face);
|
|
|
|
nb_faces += 3;
|
|
|
|
face = tmp;
|
|
}
|
|
}
|
|
|
|
(*mesh_indices) = malloc (nb_faces * sizeof (unsigned int) * 3);
|
|
(*mesh_vertices) = malloc (v.nb_vertices * sizeof (float) * 3);
|
|
|
|
if (*mesh_indices == NULL || *mesh_vertices == NULL)
|
|
{
|
|
free_all (&map, &v, root, *mesh_vertices, *mesh_indices);
|
|
*count_vertices = 0;
|
|
*count_indices = 0;
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
for (j = 0, i = 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;
|
|
}
|
|
|
|
*count_vertices = v.nb_vertices;
|
|
*count_indices = nb_faces * 3;
|
|
|
|
free_all (&map, &v, root, NULL, NULL);
|
|
return 0;
|
|
}
|
|
|