]> git.draconx.ca Git - upkg.git/blob - src/engine/mesh.gob
Improve GOB rebuild rules for VPATH builds.
[upkg.git] / src / engine / mesh.gob
1 %alltop{
2 /*
3  *  upkg: tool for manipulating Unreal Tournament packages.
4  *  Copyright © 2020 Nick Bowler
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 %}
20
21 %{
22 #include <stdint.h>
23 #include <uobject/loadable.h>
24 #include <uobject/exportable.h>
25 #include "pack.h"
26 %}
27
28 %h{
29 #include <uobject/uobject.h>
30 #include <engine/texture.h>
31
32 /* Hack to work around broken type parsing in GOB2. */
33 typedef float engine_mesh_vertex[3];
34
35 struct engine_mesh_face {
36         unsigned long flags;
37         unsigned short verts[3];
38         unsigned short texture;
39         float uv[3][2];
40 };
41 %}
42
43 class Engine:Mesh from U:Object (dynamic)
44         (interface U:Object:Loadable)
45         (interface U:Object:Exportable)
46 {
47 private struct upkg_file pkg_file;
48
49 protected long vert_base;
50 protected long vert_num;
51 protected long tri_base;
52 protected long tri_num;
53 protected long conn_base;
54 protected long conn_num;
55 protected long vlink_base;
56 protected long vlink_num;
57
58 protected engine_mesh_vertex *vertices;
59 protected struct engine_mesh_face *faces;
60
61 protected long num_textures = 0;
62 protected Engine:Texture **textures destroy {
63         long i;
64         for (i = 0; i < self->num_textures; i++) {
65                 if (textures[i])
66                         g_object_unref(textures[i]);
67         }
68         free(textures);
69 };
70
71 private int skip_array(struct UObject *uo, long *nent, long entsz,
72                        unsigned char *buf, size_t buflen)
73 {
74         struct upkg_file *f = uo->pkg_file;
75         unsigned long buf_offset = f->offset - buflen;
76         unsigned char mybuf[9];
77         unsigned long skip;
78         size_t sz, pos = 0;
79
80         if (!buf) {
81                 buf_offset = f->offset;
82                 buflen = upkg_export_read(f, mybuf, sizeof mybuf);
83                 buf = mybuf;
84         }
85
86         if (f->pkg->version >= 62) {
87                 if (buflen < 4)
88                         return -1;
89                 skip = unpack_32_le(buf);
90                 pos += 4;
91         }
92
93         pos += (sz = upkg_decode_index(nent, buf+pos, buflen-pos));
94         if (sz == 0)
95                 return -1;
96         if (*nent > SIZE_MAX / entsz) {
97                 u_warn(uo, "bogus array size");
98                 return -1;
99         }
100         sz = (size_t)*nent * entsz;
101
102         if (f->pkg->version >= 62 && sz != skip - f->base - buf_offset - pos) {
103                 u_warn(uo, "array skip does not match array size");
104                 return -1;
105         }
106
107         if (upkg_export_seek(f, buf_offset + pos + sz, SEEK_SET) != 0)
108                 return -1;
109
110         return 0;
111 }
112
113 private size_t fill_buf(struct upkg_file *f, unsigned char *buf,
114                         size_t pos, size_t buflen)
115 {
116         size_t rc, rem = buflen - pos;
117
118         memmove(buf, buf+pos, rem);
119         rem += upkg_export_read(f, buf+rem, buflen-rem);
120         return rem;
121 }
122
123 private int load_vertices(Self *self)
124 {
125         struct upkg_file *f = &self->_priv->pkg_file;
126         unsigned char buf[100];
127         size_t buflen, pos;
128         long i, n;
129
130         if (upkg_export_seek(f, self->vert_base, SEEK_SET) != 0)
131                 return -1;
132
133         buflen = upkg_export_read(f, buf, sizeof buf);
134         pos = upkg_decode_index(&n, buf, buflen);
135         g_return_val_if_fail(pos && n == self->vert_num, -1);
136
137         if (self->vert_num > SIZE_MAX / sizeof self->vertices[0]) {
138                 u_err(self, "failed to allocate memory");
139                 return -1;
140         }
141
142         self->vertices = malloc(self->vert_num * sizeof self->vertices[0]);
143         if (!self->vertices) {
144                 u_err(self, "failed to allocate memory");
145                 return -1;
146         }
147
148         for (i = 0; i < n; i++) {
149                 unsigned long vertex;
150
151                 if (buflen - pos < 4) {
152                         buflen = self_fill_buf(f, buf, pos, buflen);
153                         pos = 0;
154                 }
155
156                 if (buflen - pos < 4) {
157                         u_err(self, "failed to read vertex data");
158                         free(self->vertices);
159                         return -1;
160                 }
161
162                 vertex = unpack_32_le(buf+pos); pos += 4;
163                 self->vertices[i][0] = (vertex & 0x7ff) / 8.0;
164                 if (self->vertices[i][0] >= 128)
165                         self->vertices[i][0] -= 256;
166
167                 self->vertices[i][1] = ((vertex >> 11) & 0x7ff) / 8.0;
168                 if (self->vertices[i][1] >= 128)
169                         self->vertices[i][1] -= 256;
170
171                 self->vertices[i][2] = ((vertex >> 22) & 0x3ff) / 4.0;
172                 if (self->vertices[i][2] >= 128)
173                         self->vertices[i][2] -= 256;
174         }
175
176         return 0;
177 }
178
179 private int load_faces(Self *self)
180 {
181         struct upkg_file *f = &self->_priv->pkg_file;
182         unsigned char buf[100];
183         size_t buflen, pos;
184         long i, n;
185
186         if (upkg_export_seek(f, self->tri_base, SEEK_SET) != 0)
187                 return -1;
188
189         buflen = upkg_export_read(f, buf, sizeof buf);
190         pos = upkg_decode_index(&n, buf, buflen);
191         g_return_val_if_fail(pos && n == self->tri_num, -1);
192
193         if (self->tri_num > SIZE_MAX / sizeof self->faces[0]) {
194                 u_err(self, "failed to allocate memory");
195                 return -1;
196         }
197
198         self->faces = malloc(self->tri_num * sizeof self->faces[0]);
199         if (!self->faces) {
200                 u_err(self, "failed to allocate memory");
201                 return -1;
202         }
203
204         for (i = 0; i < n; i++) {
205                 struct engine_mesh_face *face = &self->faces[i];
206                 unsigned long tmp;
207                 int j;
208
209                 if (buflen - pos < 20) {
210                         buflen = self_fill_buf(f, buf, pos, buflen);
211                         pos = 0;
212                 }
213
214                 if (buflen - pos < 20)
215                         goto read_err;
216
217                 for (j = 0; j < 3; j++) {
218                         face->verts[j] = unpack_16_le(buf + pos + 2*j);
219                         if (face->verts[j] >= self->vert_num)
220                                 goto read_err;
221
222                         face->uv[j][0] = buf[pos + 6 + 2*j] / 255.0;
223                         face->uv[j][1] = 1 - buf[pos + 7 + 2*j] / 255.0;
224                 }
225
226                 face->flags = unpack_32_le(buf+pos+12);
227
228                 tmp = unpack_32_le(buf+pos+16);
229                 if (tmp > USHRT_MAX || tmp >= self->num_textures)
230                         goto read_err;
231                 face->texture = tmp;
232
233                 pos += 20;
234         }
235
236         return 0;
237 read_err:
238         u_err(self, "failed to read triangle data");
239         free(self->faces);
240         return -1;
241 }
242
243
244 interface U:Object:Loadable
245 private int load(U:Object *uo)
246 {
247         Self *self = SELF(uo);
248
249         if (self_load_vertices(self) != 0) {
250                 u_err(self, "error loading vertex data");
251                 return -1;
252         }
253
254         if (self_load_faces(self) != 0) {
255                 u_err(self, "error loading face data");
256                 free(self->vertices);
257                 return -1;
258         }
259
260         return 0;
261 }
262
263 interface U:Object:Loadable
264 private void unload(U:Object *uo)
265 {
266         Self *self = SELF(uo);
267         free(self->vertices);
268         free(self->faces);
269 }
270
271 interface U:Object:Exportable
272 private int export_name(U:Object *uo, char *buf, size_t n)
273 {
274         return snprintf(buf, n, "%s.obj", uo->pkg_file->name);
275 }
276
277 private int uv_compar(const void *a_, const void *b_)
278 {
279         const float *a = a_, *b = b_;
280
281         if (a[0] < b[0])
282                 return -1;
283         if (a[0] > b[0])
284                 return 1;
285         if (a[1] < b[1])
286                 return -1;
287         if (a[1] > b[1])
288                 return 1;
289
290         return 0;
291 }
292
293 interface U:Object:Exportable
294 private int export(U:Object *uo, FILE *f)
295 {
296         Self *self = SELF(uo);
297         int rc, ret = -1;
298         long i;
299
300         float (*uv_work)[2] = NULL;
301         long uv_num = 0;
302
303         long last_texture = -1;
304
305         if (fprintf(f, "mtllib %s.mtl\n", uo->pkg_file->name) < 0)
306                 goto out;
307
308         if (fprintf(f, "# %ld vertices\n", self->vert_num) < 0)
309                 goto out;
310
311         for (i = 0; i < self->vert_num; i++) {
312                 float *v = self->vertices[i];
313
314                 if (fprintf(f, "v %f %f %f\n", v[0], v[1], v[2]) < 0)
315                         goto out;
316         }
317
318         /* Optimize UV maps */
319         uv_work = malloc(3 * self->tri_num * sizeof uv_work[0]);
320         if (!uv_work) {
321                 u_warn(uo, "failed to allocate memory");
322                 uv_num = 3 * self->tri_num;
323         } else {
324                 for (i = 0; i < self->tri_num; i++) {
325                         int j;
326
327                         for (j = 0; j < 3; j++) {
328                                 uv_work[3*i+j][0] = self->faces[i].uv[j][0];
329                                 uv_work[3*i+j][1] = self->faces[i].uv[j][1];
330                         }
331                 }
332                 qsort(uv_work, 3 * self->tri_num, sizeof uv_work[0],
333                       self_uv_compar);
334
335                 for (i = 0; i < 3 * self->tri_num; i++) {
336                         if (i > 0 && !self_uv_compar(uv_work[i-1], uv_work[i]))
337                                 continue;
338
339                         memmove(uv_work[uv_num], uv_work[i], sizeof uv_work[0]);
340                         uv_num++;
341                 }
342         }
343
344         if (fprintf(f, "# %ld texture coords\n", uv_num) < 0)
345                 goto out;
346
347         for (i = 0; i < uv_num; i++) {
348                 float *uv;
349
350                 if (uv_work)
351                         uv = uv_work[i];
352                 else
353                         uv = self->faces[i/3].uv[i%3];
354
355                 if (fprintf(f, "vt %f %f\n", uv[0], uv[1]) < 0)
356                         goto out;
357         }
358
359         if (fprintf(f, "# %ld triangles\n", self->tri_num) < 0)
360                 return -1;
361
362         for (i = 0; i < self->tri_num; i++) {
363                 struct engine_mesh_face *face = &self->faces[i];
364                 unsigned long uv_idx[3];
365                 int j;
366
367                 if (face->texture != last_texture) {
368                         UObject *t = U_OBJECT(self->textures[face->texture]);
369
370                         if (t && fprintf(f, "usemtl %s\n", t->pkg_name) < 0)
371                                 goto out;
372
373                         last_texture = face->texture;
374                 }
375
376                 for (j = 0; j < 3; j++) {
377                         if (uv_work) {
378                                 float (*result)[2];
379
380                                 result = bsearch(&face->uv[j], uv_work,
381                                                  uv_num, sizeof uv_work[0],
382                                                  self_uv_compar);
383                                 g_return_val_if_fail(result, -1);
384
385                                 uv_idx[j] = result - &uv_work[0] + 1;
386                         } else {
387                                 uv_idx[j] = 3ul*i+j+1;
388                         }
389                 }
390
391                 rc = fprintf(f, "f %lu/%lu %lu/%lu %lu/%lu\n",
392                                 face->verts[0]+1lu, uv_idx[0],
393                                 face->verts[1]+1lu, uv_idx[1],
394                                 face->verts[2]+1lu, uv_idx[2]);
395                 if (rc < 0)
396                         goto out;
397         }
398
399         ret = 0;
400         free(uv_work);
401 out:
402         return ret;
403 }
404
405 private int
406 deserialize_animations(Self *self, unsigned char *buf, size_t *bufsz)
407 {
408         struct upkg_file *f = U_OBJECT(self)->pkg_file;
409         size_t rc, buflen, pos = 0;
410         long i, j, anim_count;
411
412         buflen = upkg_export_read(f, buf, *bufsz);
413         pos += (rc = upkg_decode_index(&anim_count, buf, buflen));
414         if (rc == 0)
415                 return -1;
416
417         for (i = 0; i < anim_count; i++) {
418                 long x, nfuncs;
419                 const char *s;
420
421                 /* Name */
422                 pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
423                 if (rc == 0)
424                         return -1;
425                 s = upkg_get_name(f->pkg, x);
426                 if (!s)
427                         return -1;
428
429                 /* Group */
430                 pos += (rc = upkg_decode_index(&x, buf+pos, buflen-pos));
431                 if (rc == 0)
432                         return -1;
433
434                 /* Start/end frame */
435                 if (buflen-pos < 8)
436                         return -1;
437                 pos += 8;
438
439                 /* Function count */
440                 pos += (rc = upkg_decode_index(&nfuncs, buf+pos, buflen-pos));
441                 if (rc == 0)
442                         return -1;
443
444                 for (j = 0; j < nfuncs; j++) {
445                         if (buflen - pos < 13) {
446                                 buflen = self_fill_buf(f, buf, pos, buflen);
447                                 pos = 0;
448                         }
449
450                         /* time */
451                         if (buflen-pos < 4)
452                                 return -1;
453                         pos += 4;
454
455                         /* function */
456                         rc = upkg_decode_index(&x, buf+pos, buflen-pos);
457                         if (rc == 0)
458                                 return -1;
459                         pos += rc;
460                 }
461
462                 /* rate */
463                 if (buflen-pos < 4)
464                         return -1;
465                 pos += 4;
466
467                 buflen = self_fill_buf(f, buf, pos, buflen);
468                 pos = 0;
469         }
470
471         if (pos != 0)
472                 memmove(buf, buf+pos, buflen-pos);
473         *bufsz = buflen-pos;
474
475         return 0;
476 }
477
478 private int deserialize_textures(Self *self, unsigned char *buf, size_t bufsz)
479 {
480         struct upkg_file *f = U_OBJECT(self)->pkg_file;
481         size_t rc, buflen, pos = 0;
482         long i, idx;
483
484         buflen = upkg_export_read(f, buf, bufsz);
485         pos += (rc = upkg_decode_index(&idx, buf, buflen));
486         if (rc == 0 || idx < 0 || idx > SIZE_MAX / sizeof self->textures[0]) {
487                 u_warn(self, "invalid texture count");
488                 return -1;
489         }
490         self->num_textures = idx;
491
492         self->textures = malloc(self->num_textures * sizeof self->textures[0]);
493         if (!self->textures) {
494                 u_warn(self, "failed to allocate memory");
495                 return -1;
496         }
497
498         for (i = 0; i < self->num_textures; i++) {
499                 GObject *obj = NULL;
500
501                 if (buflen < 5) {
502                         buflen = self_fill_buf(f, buf, pos, buflen);
503                         pos = 0;
504                 }
505
506                 pos += (rc = upkg_decode_index(&idx, buf+pos, buflen-pos));
507                 if (rc == 0) {
508                         u_warn(self, "invalid texture reference");
509                 } else if (idx == 0) {
510                         /* Seems these lists start with null references, ignore... */
511                 } else {
512                         obj = u_object_get_by_link(G_OBJECT(self), idx);
513                         if (!obj) {
514                                 u_warn(self, "failed to load texture %ld %ld", idx);
515                         }
516                 }
517                 self->textures[i] = ENGINE_TEXTURE(obj);
518         }
519
520         if (upkg_export_seek(f, (long)pos - (long)buflen, SEEK_CUR) != 0)
521                 return -1;
522
523         return 0;
524 }
525
526 override (U:Object) int deserialize(U:Object *uo)
527 {
528         Self *self = SELF(uo);
529         struct upkg_file *f = uo->pkg_file;
530         const size_t sphere_sz = 12 + 4 * (f->pkg->version >= 62);
531         size_t buflen, pos = 0;
532         long idx;
533
534         unsigned char buf[50];
535
536         PARENT_HANDLER(uo);
537
538         buflen = upkg_export_read(f, buf, sizeof buf);
539
540         /* Bounding box */
541         if (buflen - pos < 25)
542                 return -1;
543         pos += 25;
544
545         /* Bounding sphere */
546         if (buflen - pos < sphere_sz)
547                 return -1;
548         pos += sphere_sz;
549
550         self->vert_base = pos + 4*(f->pkg->version >= 61);
551         if (self_skip_array(uo, &self->vert_num, 4, buf+pos, buflen-pos) != 0) {
552                 u_err(uo, "invalid vertex array");
553                 return -1;
554         }
555
556         self->tri_base = f->offset + 4*(f->pkg->version >= 61);
557         if (self_skip_array(uo, &self->tri_num, 20, NULL, 0) != 0) {
558                 u_err(uo, "invalid triangle array");
559                 return -1;
560         }
561
562         if (self_deserialize_animations(self, buf, &buflen) != 0) {
563                 return -1;
564         }
565
566         self->conn_base = f->offset - buflen;
567         if (self_skip_array(uo, &self->conn_num, 8, buf, buflen) != 0) {
568                 u_err(uo, "invalid connection array");
569                 return -1;
570         }
571
572         buflen = upkg_export_read(f, buf, sizeof buf);
573         pos = 0;
574
575         /* Another bounding box? */
576         if (buflen - pos < 25)
577                 return -1;
578         pos += 25;
579
580         /* Another bounding sphere? */
581         if (buflen - pos < sphere_sz)
582                 return -1;
583         pos += sphere_sz;
584
585         self->vlink_base = f->offset - (buflen - pos);
586         if (self_skip_array(uo, &self->vlink_num, 4, buf+pos, buflen-pos)) {
587                 u_err(uo, "invalid vertex link array");
588                 return -1;
589         }
590
591         if (self_deserialize_textures(self, buf, buflen) != 0) {
592                 return -1;
593         }
594
595         /* More bounding boxes!? */
596         buflen = upkg_export_read(f, buf, 5);
597         pos = upkg_decode_index(&idx, buf, buflen);
598         if (pos == 0 || idx < 0 || idx > LONG_MAX / 25) {
599                 u_err(uo, "invalid bounding box array");
600                 return -1;
601         }
602
603         if (upkg_export_seek(f, (long)pos - 5 + idx * 25, SEEK_CUR) != 0)
604                 return -1;
605
606         /* More bounding spheres!? */
607         buflen = upkg_export_read(f, buf, 5);
608         pos = upkg_decode_index(&idx, buf, buflen);
609         if (pos == 0 || idx < 0 || idx > LONG_MAX / 16) {
610                 u_err(uo, "invalid bounding sphere array");
611                 return -1;
612         }
613
614         if (upkg_export_seek(f, (long)pos - 5 + idx * sphere_sz, SEEK_CUR) != 0)
615                 return -1;
616
617         self->_priv->pkg_file = *f;
618         return 0;
619 }
620
621 }