c3program: Include digits in uniform names
[simavr] / examples / shared / libc3 / src / c3geometry.c
1 /*
2         c3geometry.c
3
4         Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
5
6         This file is part of libc3.
7
8         libc3 is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         libc3 is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with libc3.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include <stdio.h>
24 #include <math.h>
25 #include "c3object.h"
26 #include "c3context.h"
27 #include "c3driver_geometry.h"
28 #include "c3driver_context.h"
29
30 static void
31 _c3geometry_dispose(
32                 c3geometry_p  g,
33                 const struct c3driver_geometry_t *d)
34 {
35         /*
36          * If we're still attached to an object, detach
37          */
38         if (g->object) {
39                 for (int oi = 0; oi < g->object->geometry.count; oi++)
40                         if (g->object->geometry.e[oi] == g) {
41                                 c3geometry_array_delete(&g->object->geometry, oi, 1);
42                                 c3object_set_dirty(g->object, true);
43                                 break;
44                         }
45                 g->object = NULL;
46         }
47         /* let the context driver have a chance to clear it's own stuff */
48         if (g->object && g->object->context)
49                 C3_DRIVER(g->object->context, geometry_dispose, g);
50         str_free(g->name);
51         c3vertex_array_free(&g->vertice);
52         c3tex_array_free(&g->textures);
53         c3colorf_array_free(&g->colorf);
54         free(g);
55 //      C3_DRIVER_INHERITED(g, d, dispose);
56 }
57
58 static void
59 _c3geometry_project(
60                 c3geometry_p g,
61                 const struct c3driver_geometry_t *d,
62                 c3mat4p m)
63 {
64         /* make sure there are actual elements in the array
65          * if it had been purged, the element count might remain
66          * but the array size is zero
67          */
68         if (g->vertice.count < g->vertice.size) {
69                 for (int vi = 0; vi < g->vertice.count; vi++) {
70                         c3vec3 v = c3mat4_mulv3(m, g->vertice.e[vi]);
71                         if (vi == 0)
72                                 g->bbox.min = g->bbox.max = v;
73                         else {
74                                 g->bbox.max = c3vec3_min(g->bbox.min, v);
75                                 g->bbox.max = c3vec3_max(g->bbox.max, v);
76                         }
77                 }
78         }
79         /* else -- do not clear bbox on purged arrays
80                 g->bbox.min = g->bbox.max = c3vec3f(0,0,0); */
81
82         if (g->object && g->object->context)
83                 C3_DRIVER(g->object->context, geometry_project, g, m);
84         g->dirty = 0;
85 //      C3_DRIVER_INHERITED(g, d, project);
86 }
87
88 static void
89 _c3geometry_draw(
90                 c3geometry_p g,
91                 const struct c3driver_geometry_t *d)
92 {
93         if (g->object && g->object->context)
94                 C3_DRIVER(g->object->context, geometry_draw, g);
95 //      C3_DRIVER_INHERITED(g, d, draw);
96 }
97
98 const  c3driver_geometry_t c3geometry_driver = {
99         .dispose = _c3geometry_dispose,
100         .project = _c3geometry_project,
101         .draw = _c3geometry_draw,
102 };
103
104 c3geometry_p
105 c3geometry_new(
106                 c3geometry_type_t type,
107                 c3object_p o /* = NULL */)
108 {
109         c3geometry_p res = malloc(sizeof(c3geometry_t));
110         return c3geometry_init(res, type, o);
111 }
112
113 c3geometry_p
114 c3geometry_init(
115                 c3geometry_p g,
116                 c3geometry_type_t type,
117                 struct c3object_t * o /* = NULL */)
118 {
119         memset(g, 0, sizeof(*g));
120         static const c3driver_geometry_t * list[] = {
121                         &c3geometry_driver, NULL,
122         };
123         g->driver = list;
124         g->type = type;
125         g->dirty = 1;
126         if (o)
127                 c3object_add_geometry(o, g);
128         return g;
129 }
130
131 c3driver_geometry_p
132 c3geometry_get_custom(
133                 c3geometry_p g )
134 {
135         if (g->custom)
136                 return (c3driver_geometry_p)g->driver[0];
137         int cnt = 0;
138         for (int di = 0; g->driver[di]; di++)
139                 cnt++;
140         c3driver_geometry_p * newd = malloc(sizeof(c3driver_geometry_p) * (cnt + 2));
141         memcpy(&newd[1], g->driver, (cnt + 1) * sizeof(c3driver_geometry_p));
142         newd[0] = malloc(sizeof(c3driver_geometry_t));
143         memset(newd[0], 0, sizeof(c3driver_geometry_t));
144         g->custom = 1;
145         g->driver = (typeof(g->driver))newd;
146         return newd[0];
147 }
148
149 void
150 c3geometry_dispose(
151                 c3geometry_p g)
152 {
153         C3_DRIVER(g, dispose);
154 }
155
156 void
157 c3geometry_project(
158                 c3geometry_p g,
159                 c3mat4p m)
160 {
161         if (!g->dirty)
162                 return;
163         C3_DRIVER(g, project, m);
164 }
165
166 void
167 c3geometry_draw(
168                 c3geometry_p g )
169 {
170         C3_DRIVER(g, draw);
171 }
172
173 void
174 c3geometry_factor(
175                 c3geometry_p g,
176                 c3f tolerance,
177                 c3f normaltolerance)
178 {
179         printf("%s has %d/%d/%d vertices/normals/tex and %d indexes\n", __func__,
180                         g->vertice.count, g->normals.count, g->textures.count,
181                         g->indices.count);
182
183         c3f tolerance2 = tolerance * tolerance;
184
185         int in_index = g->indices.count;
186         int vcount = in_index ? in_index : g->vertice.count;
187         int input = 0;
188         int output = 0;
189         g->indices.count = 0;
190         while (input < vcount) {
191                 int current = in_index ? g->indices.e[input] : input;
192                 c3vec3 v = g->vertice.e[current];
193                 c3vec3 n = g->normals.count ? g->normals.e[current] : c3vec3f(0,0,0);
194                 c3vec3 np = c3vec3_polar(n);    // normal in polar coord
195
196                 int oi = -1;
197                 for (int ci = 0; ci < output && oi == -1; ci++)
198                         if (c3vec3_length2(c3vec3_sub(g->vertice.e[ci], v)) < tolerance2) {
199                                 if (g->normals.count) {
200                                         c3vec3 nc = g->normals.e[ci];
201                                         c3vec3 pc = c3vec3_polar(nc);
202
203                                         c3vec3 d = c3vec3_sub(np, pc);
204                                         while (d.n[0] <= -M_PI) d.n[0] += (2*M_PI);
205                                         while (d.n[1] <= -M_PI) d.n[1] += (2*M_PI);
206
207                                         if (fabs(d.n[0]) < normaltolerance &&
208                                                         fabs(d.n[1]) < normaltolerance) {
209                                                 oi = ci;
210                                                 // replace the compared normal with the 'merged' one
211                                                 // that should hopefully trim it to the right direction
212                                                 // somehow. Not perfect obviously
213                                                 g->normals.e[ci] = c3vec3_add(n, nc);
214                                         }
215                                 } else
216                                         oi = ci;
217                         }
218                 if (oi == -1) {
219                         oi = output;
220                         g->vertice.e[output] = g->vertice.e[current];
221                         if (g->textures.count)
222                                 g->textures.e[output] = g->textures.e[current];
223                         if (g->normals.count)
224                                 g->normals.e[output] = n;
225                         if (g->colorf.count)
226                                 g->colorf.e[output] = g->colorf.e[current];
227                         output++;
228                 }
229                 c3indices_array_add(&g->indices, oi);
230                 input++;
231         }
232         g->vertice.count = output;
233         c3vertex_array_realloc(&g->vertice, output);
234         if (g->textures.count) {
235                 g->textures.count = output;
236                 c3tex_array_realloc(&g->textures, output);
237         }
238         if (g->normals.count) {
239                 g->normals.count = output;
240                 c3vertex_array_realloc(&g->normals, output);
241                 for (int ni = 0; ni < output; ni++)
242                         g->normals.e[ni] = c3vec3_normalize(g->normals.e[ni]);
243         }
244         if (g->colorf.count) {
245                 g->colorf.count = output;
246                 c3colorf_array_realloc(&g->colorf, output);
247         }
248         c3geometry_set_dirty(g, 1);
249
250         printf("%s converted to %d vertices and %d indexes\n",  __func__,
251                         g->vertice.count, g->indices.count);
252 }
253
254 void
255 c3geometry_set_dirty(
256                 c3geometry_p g,
257                 int dirty )
258 {
259         g->dirty = dirty;
260         if (dirty && g->object)
261                 c3object_set_dirty(g->object, 1);
262 }