Refactor el big #2
[convexer.git] / src / convexer.c
1 /*
2 CONVEXER v0.8
3
4 A GNU/Linux-first Source1 Hammer replacement
5 built with Blender, for mapmakers
6
7 Copyright (C) 2022 Harry Godden (hgn)
8
9 Features:
10 - Brush decomposition into convex pieces for well defined geometry
11 - Freely form displacements without limits
12 - Build your entire map in Blender
13 - Compile models and model groups easily
14 - Light patch BSP files; remove unwanted realtime effects
15 - Fastest VTF compressor (thanks to Richgel999 and stb)
16
17 Program structure:
18
19 File/folder Lang Purpose
20 src/
21 __init__.py Python Blender plugin interface
22 convexer.c C Heavy lifting; brush decomp, mesh processing
23 cxr_math.h C Vector maths and other handy things
24 cxr_mem.h C Automatic resizing buffers
25 nbvtf/
26 nbvtf.h C VTF processing interface
27 librgcx.h C++ Rich Geldreich's DXT1/DXT5 compressors
28 stb/ C Sean Barrets image I/O
29 */
30
31 const char *cxr_build_time = __DATE__ " @" __TIME__;
32
33 #include <stdio.h>
34 #include <math.h>
35 #include <stdint.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 typedef uint8_t u8;
41 typedef uint16_t u16;
42 typedef uint32_t u32;
43 typedef uint64_t u64;
44 typedef int8_t i8;
45 typedef int16_t i16;
46 typedef int32_t i32;
47 typedef int64_t i64;
48
49 typedef unsigned int uint;
50
51 typedef double v2f[2];
52 typedef double v3f[3];
53 typedef double v4f[4];
54 typedef v3f m3x3f[3];
55 typedef v3f m4x3f[4];
56 typedef v3f boxf[2];
57
58 #define CXR_EPSILON 0.001
59 #define CXR_PLANE_SIMILARITY_MAX 0.998
60 #define CXR_BIG_NUMBER 1e300
61 #define CXR_INTERIOR_ANGLE_MAX 0.998
62 #define CXR_API
63 #define CXR_DIRTY_OPTIMISATION 1
64 #define CXR_DEBUG_WRITE_MESH 1
65 #define CXR_DEBUG_ALLOCATORS 1
66 #define CXR_MANIFOLD_DEBUG 0
67
68 #define CXR_ERROR_DEGEN_IMPLICIT 0x1
69 #define CXR_ERROR_BAD_MANIFOLD 0x2
70 #define CXR_ERROR_NO_SOLIDS 0x4
71
72 #include "cxr_math.h"
73 #include "cxr_mem.h"
74
75 static v4f colours_random[] =
76 {
77 { 0.863, 0.078, 0.235, 0.4 },
78 { 0.000, 0.980, 0.604, 0.4 },
79 { 0.118, 0.565, 1.000, 0.4 },
80 { 0.855, 0.439, 0.839, 0.4 },
81 { 0.824, 0.412, 0.118, 0.4 },
82 { 0.125, 0.698, 0.667, 0.4 },
83 { 0.541, 0.169, 0.886, 0.4 },
84 { 1.000, 0.843, 0.000, 0.4 }
85 };
86
87 static int cxr_range(int x, int bound)
88 {
89 if( x < 0 )
90 x += bound * (x/bound + 1);
91
92 return x % bound;
93 }
94
95 typedef struct cxr_edge cxr_edge;
96 typedef struct cxr_input_mesh cxr_input_mesh;
97 typedef struct cxr_input_loop cxr_input_loop;
98 typedef struct cxr_polygon cxr_polygon;
99 typedef struct cxr_material cxr_material;
100 typedef struct cxr_loop cxr_loop;
101 typedef struct cxr_solid cxr_solid;
102 typedef struct cxr_mesh cxr_mesh;
103 typedef struct cxr_texinfo cxr_texinfo;
104 typedef struct cxr_vdf cxr_vdf;
105
106 struct cxr_input_mesh
107 {
108 v3f *vertices;
109
110 struct cxr_edge
111 {
112 i32 i0, i1;
113 i32 freestyle;
114 }
115 *edges;
116
117 struct cxr_input_loop
118 {
119 i32 index,
120 edge_index;
121 v2f uv;
122 }
123 *loops;
124
125 struct cxr_polygon
126 {
127 i32 loop_start, loop_total;
128 v3f normal;
129 v3f center;
130 i32 material_id; /* -1: interior material (nodraw) */
131 }
132 *polys;
133
134 struct cxr_material
135 {
136 i32 res[2];
137 const char *vmt_path;
138 }
139 *materials;
140
141 i32 poly_count,
142 vertex_count,
143 edge_count,
144 loop_count,
145 material_count;
146 };
147
148 struct cxr_loop
149 {
150 i32 poly_left,
151 poly_right,
152 edge_index,
153 index;
154 v2f uv;
155 };
156
157 struct cxr_solid
158 {
159 i32 *polygons;
160 };
161
162 struct cxr_mesh
163 {
164 struct cxr_abuffer
165 abedges,
166 abloops,
167 abpolys,
168
169 *p_abverts; /* This data is stored externally because the data is often
170 shared between solids. */
171
172 /* Valid when update() is called on this mesh,
173 * Invalid when data is appended to them */
174 struct cxr_edge *edges;
175 struct cxr_polygon *polys;
176 struct cxr_loop *loops;
177 };
178
179 struct cxr_texinfo
180 {
181 v3f uaxis, vaxis;
182 v2f offset, scale;
183 double winding;
184 };
185
186 /*
187 * Simplified VDF writing interface. No allocations or nodes, just write to file
188 */
189 struct cxr_vdf
190 {
191 FILE *fp;
192 i32 level;
193 };
194
195 static struct cxr_settings
196 {
197 i32 debug,
198 lightmap_scale;
199
200 double light_scale;
201 }
202 cxr_settings = {
203 .debug = 0,
204 .lightmap_scale = 12,
205
206 .light_scale = 1.0/5.0
207 };
208
209 static struct cxr_context
210 {
211 i32 brush_count,
212 entity_count,
213 face_count;
214
215 double scale_factor;
216 double offset_z;
217 }
218 cxr_context = {
219 .brush_count = 0,
220 .entity_count = 0,
221 .face_count = 0,
222 .scale_factor = 32.0,
223 .offset_z = 0.0
224 };
225
226 static struct cxr_material cxr_nodraw = {
227 .res = { 512, 512 },
228 .vmt_path = "tools/toolsnodraw"
229 };
230
231 /*
232 * This should be called after appending any data to those buffers
233 */
234 static void cxr_mesh_update( cxr_mesh *mesh )
235 {
236 mesh->edges = cxr_ab_ptr(&mesh->abedges, 0);
237 mesh->polys = cxr_ab_ptr(&mesh->abpolys, 0);
238 mesh->loops = cxr_ab_ptr(&mesh->abloops, 0);
239 }
240
241 static v4f colour_error = { 1.0f, 0.0f, 0.0f, 1.0f };
242 static v4f colour_face_graph = { 1.0f, 1.0f, 1.0f, 0.03f };
243 static v4f colour_success = { 0.0f, 1.0f, 0.0f, 1.0f };
244
245 static void (*cxr_log_func)(const char *str);
246 static void (*cxr_line_func)( v3f p0, v3f p1, v4f colour );
247
248 static void cxr_log( const char *fmt, ... )
249 {
250 char buf[512];
251
252 va_list args;
253 va_start( args, fmt );
254 vsnprintf( buf, sizeof(buf)-1, fmt, args );
255 va_end(args);
256
257 if( cxr_log_func )
258 cxr_log_func( buf );
259
260 fputs(buf,stdout);
261 }
262
263
264 CXR_API void cxr_context_reset(void)
265 {
266 cxr_context.brush_count = 0;
267 cxr_context.entity_count = 0;
268 cxr_context.face_count = 0;
269 cxr_context.offset_z = 0.0;
270 cxr_context.scale_factor = 32.0;
271 }
272
273 CXR_API void cxr_set_offset(double offset)
274 {
275 cxr_context.offset_z = offset;
276 }
277
278 CXR_API void cxr_set_scale_factor(double scale)
279 {
280 cxr_context.scale_factor = scale;
281 }
282
283 CXR_API cxr_vdf *cxr_vdf_open(const char *path)
284 {
285 cxr_vdf *vdf = malloc(sizeof(cxr_vdf));
286
287 vdf->level = 0;
288 vdf->fp = fopen( path, "w" );
289
290 if( !vdf->fp )
291 {
292 free( vdf );
293 return NULL;
294 }
295
296 return vdf;
297 }
298
299 CXR_API void cxr_vdf_close(cxr_vdf *vdf)
300 {
301 fclose( vdf->fp );
302 free( vdf );
303 }
304
305 CXR_API void cxr_vdf_put(cxr_vdf *vdf, const char *str)
306 {
307 for( int i=0; i<vdf->level; i++ )
308 fputs( " ", vdf->fp );
309
310 fputs( str, vdf->fp );
311 }
312
313 static void cxr_vdf_printf( cxr_vdf *vdf, const char *fmt, ... )
314 {
315 cxr_vdf_put(vdf,"");
316
317 va_list args;
318 va_start( args, fmt );
319 vfprintf( vdf->fp, fmt, args );
320 va_end(args);
321 }
322
323 CXR_API void cxr_vdf_node(cxr_vdf *vdf, const char *str)
324 {
325 cxr_vdf_put( vdf, str );
326 putc( (u8)'\n', vdf->fp );
327 cxr_vdf_put( vdf, "{\n" );
328
329 vdf->level ++;
330 }
331
332 CXR_API void cxr_vdf_edon( cxr_vdf *vdf )
333 {
334 vdf->level --;
335 cxr_vdf_put( vdf, "}\n" );
336 }
337
338 CXR_API void cxr_vdf_kv( cxr_vdf *vdf, const char *strk, const char *strv )
339 {
340 cxr_vdf_printf( vdf, "\"%s\" \"%s\"\n", strk, strv );
341 }
342
343 /*
344 * Data-type specific Keyvalues
345 */
346 static void cxr_vdf_ki32( cxr_vdf *vdf, const char *strk, i32 val )
347 {
348 cxr_vdf_printf( vdf, "\"%s\" \"%d\"\n", strk, val );
349 }
350
351 static void cxr_vdf_kdouble( cxr_vdf *vdf, const char *strk, double val )
352 {
353 cxr_vdf_printf( vdf, "\"%s\" \"%f\"\n", strk, val );
354 }
355
356 static void cxr_vdf_kaxis( cxr_vdf *vdf, const char *strk,
357 v3f normal, double offset, double scale
358 ){
359 cxr_vdf_printf( vdf, "\"%s\" \"[%f %f %f %f] %f\"\n",
360 strk, normal[0], normal[1],normal[2], offset, scale );
361 }
362
363 static void cxr_vdf_kv3f( cxr_vdf *vdf, const char *strk, v3f v )
364 {
365 cxr_vdf_printf( vdf, "\"%s\" \"[%f %f %f]\"\n", strk, v[0], v[1], v[2] );
366 }
367
368 static void cxr_vdf_karrdouble( cxr_vdf *vdf, const char *strk,
369 int id, double *doubles, int count
370 ){
371 cxr_vdf_put(vdf,"");
372 fprintf( vdf->fp, "\"%s%d\" \"", strk, id );
373 for( int i=0; i<count; i++ )
374 {
375 if( i == count-1 ) fprintf( vdf->fp, "%f", doubles[i] );
376 else fprintf( vdf->fp, "%f ", doubles[i] );
377 }
378 fprintf( vdf->fp, "\"\n" );
379 }
380
381 static void cxr_vdf_karrv3f( cxr_vdf *vdf, const char *strk,
382 int id, v3f *vecs, int count
383 ){
384 cxr_vdf_put(vdf,"");
385 fprintf( vdf->fp, "\"%s%d\" \"", strk, id );
386 for( int i=0; i<count; i++ )
387 {
388 const char *format = i == count-1? "%f %f %f": "%f %f %f ";
389 fprintf( vdf->fp, format, vecs[i][0], vecs[i][1], vecs[i][2] );
390 }
391 fprintf( vdf->fp, "\"\n" );
392 }
393
394 static void cxr_vdf_plane( cxr_vdf *vdf, const char *strk, v3f a, v3f b, v3f c )
395 {
396 cxr_vdf_printf( vdf, "\"%s\" \"(%f %f %f) (%f %f %f) (%f %f %f)\"\n",
397 strk, a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2] );
398 }
399
400 static void cxr_vdf_colour255(cxr_vdf *vdf, const char *strk, v4f colour)
401 {
402 v4f scale;
403 v4_muls( colour, 255.0, scale );
404 cxr_vdf_printf( vdf, "\"%s\" \"%d %d %d %d\"\n",
405 strk,(int)scale[0], (int)scale[1], (int)scale[2], (int)scale[3]);
406 }
407
408 /*
409 * Debugging line drawing
410 */
411 static void cxr_debug_line( v3f p0, v3f p1, v4f colour )
412 {
413 if( cxr_line_func )
414 cxr_line_func( p0, p1, colour );
415 }
416
417 static void cxr_debug_box( v3f p0, double sz, v4f colour )
418 {
419 v3f a,b,c,d,
420 a1,b1,c1,d1;
421 v3_add(p0, (v3f){-sz,-sz,-sz}, a);
422 v3_add(p0, (v3f){-sz, sz,-sz}, b);
423 v3_add(p0, (v3f){ sz, sz,-sz}, c);
424 v3_add(p0, (v3f){ sz,-sz,-sz}, d);
425 v3_add(p0, (v3f){-sz,-sz,sz}, a1);
426 v3_add(p0, (v3f){-sz, sz,sz}, b1);
427 v3_add(p0, (v3f){ sz, sz,sz}, c1);
428 v3_add(p0, (v3f){ sz,-sz,sz}, d1);
429
430 cxr_debug_line( a,b, colour );
431 cxr_debug_line( b,c, colour );
432 cxr_debug_line( c,d, colour );
433 cxr_debug_line( d,a, colour );
434 cxr_debug_line( a1,b1, colour );
435 cxr_debug_line( b1,c1, colour );
436 cxr_debug_line( c1,d1, colour );
437 cxr_debug_line( d1,a1, colour );
438 cxr_debug_line( a,a1, colour );
439 cxr_debug_line( b,b1, colour );
440 cxr_debug_line( c,c1, colour );
441 cxr_debug_line( d,d1, colour );
442 }
443
444 /*
445 * Draw arrow with the tips oriented along normal
446 */
447 static void cxr_debug_arrow( v3f p0, v3f p1, v3f normal, double sz, v4f colour )
448 {
449 v3f dir, tan, p2, p3;
450 v3_sub(p1,p0,dir);
451 v3_normalize(dir);
452
453 v3_cross(dir,normal,tan);
454 v3_muladds( p1,dir, -sz, p2 );
455 v3_muladds( p2,tan,sz,p3 );
456 cxr_debug_line( p1, p3, colour );
457 v3_muladds( p2,tan,-sz,p3 );
458 cxr_debug_line( p1, p3, colour );
459 cxr_debug_line( p0, p1, colour );
460 }
461
462 /*
463 * Draw arrows CCW around polygon, draw normal vector from center
464 */
465 static void cxr_debug_poly( cxr_mesh *mesh, cxr_polygon *poly, v4f colour )
466 {
467 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
468
469 for( int i=0; i<poly->loop_total; i++ )
470 {
471 int lp0 = poly->loop_start+i,
472 lp1 = poly->loop_start+cxr_range(i+1,poly->loop_total);
473
474 int i0 = mesh->loops[ lp0 ].index,
475 i1 = mesh->loops[ lp1 ].index;
476
477 v3f p0, p1;
478
479 v3_lerp( verts[i0], poly->center, 0.02, p0 );
480 v3_lerp( verts[i1], poly->center, 0.02, p1 );
481
482 cxr_debug_arrow( p0, p1, poly->normal, 0.05, colour );
483 }
484
485 v3f nrm0;
486 v3_muladds( poly->center, poly->normal, 0.3, nrm0 );
487
488 cxr_debug_line( poly->center, nrm0, colour );
489 }
490
491 static void cxr_debug_mesh(cxr_mesh *mesh, v4f colour )
492 {
493 for( int i=0; i<mesh->abpolys.count; i++ )
494 {
495 cxr_polygon *poly = &mesh->polys[i];
496 cxr_debug_poly( mesh, poly, colour );
497 }
498 }
499
500 /*
501 * abverts is a pointer to an existing vertex buffer
502 */
503 static cxr_mesh *cxr_alloc_mesh( int edge_count, int loop_count, int poly_count,
504 cxr_abuffer *abverts
505 ){
506 cxr_mesh *mesh = malloc(sizeof(cxr_mesh));
507 cxr_ab_init(&mesh->abedges, sizeof(cxr_edge), edge_count);
508 cxr_ab_init(&mesh->abloops, sizeof(cxr_loop), loop_count);
509 cxr_ab_init(&mesh->abpolys, sizeof(cxr_polygon), poly_count);
510 mesh->p_abverts = abverts;
511
512 cxr_mesh_update( mesh );
513
514 return mesh;
515 }
516
517 static void cxr_free_mesh( cxr_mesh *mesh )
518 {
519 cxr_ab_free(&mesh->abedges);
520 cxr_ab_free(&mesh->abloops);
521 cxr_ab_free(&mesh->abpolys);
522 free(mesh);
523 }
524
525 /*
526 * Rebuilds edge data for mesh (useful to get rid of orphaned edges)
527 */
528 static void cxr_mesh_clean_edges( cxr_mesh *mesh )
529 {
530 cxr_abuffer new_edges;
531 cxr_ab_init( &new_edges, sizeof(cxr_edge), mesh->abedges.count );
532
533 for( int i=0; i<mesh->abpolys.count; i++ )
534 {
535 cxr_polygon *poly = &mesh->polys[i];
536 for( int j=0; j<poly->loop_total; j++ )
537 {
538 cxr_loop
539 *lp0 = &mesh->loops[poly->loop_start+j],
540 *lp1 = &mesh->loops[poly->loop_start+cxr_range(j+1,poly->loop_total)];
541
542 int i0 = cxr_min(lp0->index, lp1->index),
543 i1 = cxr_max(lp0->index, lp1->index);
544
545 /* Check if edge exists before adding */
546 for( int k=0; k<new_edges.count; k++ )
547 {
548 cxr_edge *edge = cxr_ab_ptr(&new_edges,k);
549
550 if( edge->i0 == i0 && edge->i1 == i1 )
551 {
552 lp0->edge_index = k;
553 goto IL_EDGE_CREATED;
554 }
555 }
556
557 int orig_edge_id = lp0->edge_index;
558 lp0->edge_index = new_edges.count;
559
560 cxr_edge edge = { i0, i1 };
561
562 /*
563 * Copy extra information from original edges
564 */
565
566 if( orig_edge_id < mesh->abedges.count )
567 {
568 cxr_edge *orig_edge = &mesh->edges[ orig_edge_id ];
569 edge.freestyle = orig_edge->freestyle;
570 }
571 else
572 {
573 edge.freestyle = 0;
574 }
575
576 cxr_ab_push( &new_edges, &edge );
577
578 IL_EDGE_CREATED:;
579 }
580 }
581
582 cxr_ab_free( &mesh->abedges );
583 mesh->abedges = new_edges;
584
585 cxr_mesh_update( mesh );
586 }
587
588 /*
589 * Remove 0-length faces from mesh (we mark them light that for deletion
590 * Remove all unused loops as a result of removing those faces
591 */
592 static void cxr_mesh_clean_faces( cxr_mesh *mesh )
593 {
594 cxr_abuffer loops_new;
595 cxr_ab_init( &loops_new, sizeof(cxr_loop), mesh->abloops.count );
596
597 int new_length=0;
598 for( int i=0; i<mesh->abpolys.count; i++ )
599 {
600 cxr_polygon *src = &mesh->polys[i],
601 *dst = &mesh->polys[new_length];
602
603 if( src->loop_total > 0 )
604 {
605 int src_start = src->loop_start,
606 src_total = src->loop_total;
607
608 *dst = *src;
609 dst->loop_start = loops_new.count;
610
611 for( int j=0; j<src_total; j++ )
612 {
613 cxr_loop *loop = &mesh->loops[src_start+j],
614 *ldst = cxr_ab_ptr(&loops_new,dst->loop_start+j);
615 *ldst = *loop;
616 ldst->poly_left = new_length;
617 }
618
619 loops_new.count += src_total;
620 new_length ++;
621 }
622 }
623
624 cxr_ab_free( &mesh->abloops );
625 mesh->abloops = loops_new;
626 mesh->abpolys.count = new_length;
627
628 cxr_mesh_update( mesh );
629 }
630
631 /*
632 * Links loop's poly_left and poly_right
633 * Does not support more than 2 polys to one edge
634 *
635 * Returns 0 if there is non-manifold geomtry (aka: not watertight)
636 */
637 static int cxr_mesh_link_loops( cxr_mesh *mesh )
638 {
639 i32 *polygon_edge_map = malloc(mesh->abedges.count*2 *sizeof(i32));
640
641 for( int i = 0; i < mesh->abedges.count*2; i ++ )
642 polygon_edge_map[i] = -1;
643
644 for( int i = 0; i < mesh->abpolys.count; i ++ )
645 {
646 cxr_polygon *poly = &mesh->polys[i];
647
648 for( int j = 0; j < poly->loop_total; j ++ )
649 {
650 cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
651 loop->poly_left = i;
652
653 for( int k = 0; k < 2; k ++ )
654 {
655 i32 *edge = &polygon_edge_map[loop->edge_index*2+k];
656
657 if( *edge == -1 )
658 {
659 *edge = i;
660 break;
661 }
662 }
663 }
664 }
665 for( int i = 0; i < mesh->abpolys.count; i ++ )
666 {
667 cxr_polygon *poly = &mesh->polys[i];
668
669 for( int j = 0; j < poly->loop_total; j ++ )
670 {
671 cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
672
673 i32 *face_map = &polygon_edge_map[ loop->edge_index*2 ];
674
675 if( face_map[0] == loop->poly_left ) loop->poly_right = face_map[1];
676 else loop->poly_right = face_map[0];
677 }
678 }
679
680
681 for( int i=0; i<mesh->abedges.count*2; i++ )
682 {
683 if( polygon_edge_map[i] == -1 )
684 {
685 free( polygon_edge_map );
686 return 0;
687 }
688 }
689
690 free( polygon_edge_map );
691 return 1;
692 }
693
694 /*
695 * Create new empty polygon with known loop count
696 * Must be filled and completed by the following functions!
697 */
698 static int cxr_create_poly( cxr_mesh *mesh, int loop_count )
699 {
700 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
701
702 if( loop_count < 3 )
703 {
704 cxr_log( "tried to add new poly with length %d!\n", loop_count );
705 return 0;
706 }
707
708 cxr_ab_reserve( &mesh->abpolys, 1 );
709 cxr_ab_reserve( &mesh->abloops, loop_count );
710 cxr_mesh_update( mesh );
711
712 cxr_polygon *poly = &mesh->polys[ mesh->abpolys.count ];
713
714 poly->loop_start = mesh->abloops.count;
715 poly->loop_total = 0;
716 poly->material_id = -1;
717 v3_zero( poly->center );
718
719 return 1;
720 }
721
722 /*
723 * Add one index to the polygon created by the above function
724 */
725 static void cxr_poly_push_index( cxr_mesh *mesh, int id )
726 {
727 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
728
729 int nface_id = mesh->abpolys.count;
730 cxr_polygon *poly = &mesh->polys[ nface_id ];
731
732 cxr_loop *new_loop = &mesh->loops[ poly->loop_start + poly->loop_total ];
733
734 new_loop->poly_left = nface_id;
735 new_loop->poly_right = -1;
736 new_loop->index = id;
737 new_loop->edge_index = 0;
738 v2_zero(new_loop->uv);
739
740 v3_add( poly->center, verts[new_loop->index], poly->center );
741
742 poly->loop_total ++;
743 mesh->abloops.count ++;
744 }
745
746 /*
747 * Finalize and commit polygon into mesh
748 */
749 static void cxr_poly_finish( cxr_mesh *mesh )
750 {
751 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
752
753 int nface_id = mesh->abpolys.count;
754 cxr_polygon *poly = &mesh->polys[nface_id];
755
756 /* Average center and calc normal */
757
758 v3_divs( poly->center, poly->loop_total, poly->center );
759 cxr_loop *lp0 = &mesh->loops[ poly->loop_start],
760 *lp1 = &mesh->loops[ poly->loop_start+1 ],
761 *lp2 = &mesh->loops[ poly->loop_start+2 ];
762
763 tri_normal(
764 verts[lp0->index], verts[lp1->index], verts[lp2->index], poly->normal);
765
766 mesh->abpolys.count ++;
767 }
768
769 /*
770 * Extract the next island from mesh
771 *
772 * Returns NULL if mesh is one contigous object
773 */
774 static cxr_mesh *cxr_pull_island( cxr_mesh *mesh )
775 {
776 cxr_mesh_link_loops(mesh);
777
778 int *island_current = malloc(mesh->abpolys.count*sizeof(int)),
779 island_len = 1,
780 loop_count = 0,
781 last_count;
782
783 island_current[0] = 0;
784
785 island_grow:
786 last_count = island_len;
787
788 for( int i=0; i<island_len; i++ )
789 {
790 cxr_polygon *poly = &mesh->polys[ island_current[i] ];
791
792 for( int j=0; j<poly->loop_total; j++ )
793 {
794 cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
795
796 if( loop->poly_right != -1 )
797 {
798 int face_present = 0;
799
800 for( int k=0; k<island_len; k++ )
801 {
802 if( island_current[k] == loop->poly_right )
803 {
804 face_present = 1;
805 break;
806 }
807 }
808
809 if( !face_present )
810 island_current[ island_len ++ ] = loop->poly_right;
811 }
812 }
813 }
814
815 if( island_len > last_count )
816 goto island_grow;
817
818 /* Check for complete object */
819 if( island_len == mesh->abpolys.count )
820 {
821 free( island_current );
822 return NULL;
823 }
824
825 for( int i=0; i<island_len; i++ )
826 {
827 cxr_polygon *poly = &mesh->polys[ island_current[i] ];
828 loop_count += poly->loop_total;
829 }
830
831 /* Create and update meshes */
832 cxr_mesh *newmesh = cxr_alloc_mesh( mesh->abedges.count,
833 loop_count,
834 island_len,
835 mesh->p_abverts );
836
837 for( int i=0; i<island_len; i++ )
838 {
839 cxr_polygon *src = &mesh->polys[ island_current[i] ];
840 cxr_polygon *dst = cxr_ab_ptr(&newmesh->abpolys, i);
841
842 *dst = *src;
843 dst->loop_start = newmesh->abloops.count;
844
845 for( int j=0; j<src->loop_total; j++ )
846 {
847 cxr_loop
848 *lsrc = &mesh->loops[ src->loop_start+j ],
849 *ldst = cxr_ab_ptr(&newmesh->abloops, dst->loop_start+j);
850
851 *ldst = *lsrc;
852 ldst->poly_left = i;
853 ldst->poly_right = -1;
854 }
855
856 newmesh->abloops.count += src->loop_total;
857 src->loop_total = -1;
858 }
859
860 newmesh->abpolys.count = island_len;
861 newmesh->abedges.count = mesh->abedges.count;
862 memcpy( cxr_ab_ptr(&newmesh->abedges,0),
863 mesh->edges,
864 mesh->abedges.count * sizeof(cxr_edge));
865
866 cxr_mesh_clean_faces(mesh);
867 cxr_mesh_clean_edges(mesh);
868 cxr_mesh_clean_edges(newmesh);
869
870 free( island_current );
871 return newmesh;
872 }
873
874 /*
875 * Invalid solid is when there are vertices that are coplanar to a face, but are
876 * outside the polygons edges.
877 */
878 static int cxr_valid_solid( cxr_mesh *mesh, int *solid, int len )
879 {
880 v3f *verts = cxr_ab_ptr(mesh->p_abverts, 0);
881
882 for( int i=0; i<len; i++ )
883 {
884 cxr_polygon *polyi = &mesh->polys[ solid[i] ];
885
886 v4f plane;
887 normal_to_plane(polyi->normal, polyi->center, plane);
888
889 for( int j=0; j<len; j++ )
890 {
891 if( i==j ) continue;
892
893 cxr_polygon *polyj = &mesh->polys[ solid[j] ];
894
895 for( int k=0; k<polyj->loop_total; k++ )
896 {
897 cxr_loop *lpj = &mesh->loops[ polyj->loop_start+k ];
898
899 /* Test if the vertex is not referenced by the polygon */
900 for( int l=0; l<polyi->loop_total; l++ )
901 {
902 cxr_loop *lpi = &mesh->loops[ polyi->loop_start+l ];
903
904 if( lpi->index == lpj->index )
905 goto skip_vertex;
906 }
907
908 if( fabs(plane_polarity(plane, verts[lpj->index])) < 0.001 )
909 return 0;
910
911 skip_vertex:;
912 }
913 }
914 }
915
916 return 1;
917 }
918
919 /*
920 * Use when iterating the loops array, to get a unique set of edges
921 * Better than using the edges array and doing many more checks
922 */
923 static int cxr_loop_unique_edge( cxr_loop *lp )
924 {
925 if( lp->poly_left > lp->poly_right )
926 return 0;
927
928 return 1;
929 }
930
931 /*
932 * Identify edges in the mesh where the two connected face's normals
933 * are opposing eachother (or close to identical)
934 */
935 static int *cxr_mesh_reflex_edges( cxr_mesh *mesh )
936 {
937 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
938 int *edge_tagged = malloc( mesh->abedges.count * sizeof(int) );
939
940 for( int i=0; i<mesh->abloops.count; i++ )
941 {
942 cxr_loop *lp = &mesh->loops[i];
943 if( !cxr_loop_unique_edge( lp ) ) continue;
944
945 edge_tagged[lp->edge_index] = 0;
946
947 cxr_polygon *polya = &mesh->polys[ lp->poly_left ],
948 *polyb = &mesh->polys[ lp->poly_right ];
949
950 v4f planeb;
951 normal_to_plane(polyb->normal, polyb->center, planeb);
952
953 for( int j=0; j<polya->loop_total; j++ )
954 {
955 cxr_loop *lp1 = &mesh->loops[ polya->loop_start+j ];
956
957 if(( plane_polarity( planeb, verts[lp1->index] ) > 0.001 ) ||
958 ( v3_dot(polya->normal,polyb->normal) > CXR_PLANE_SIMILARITY_MAX ))
959 {
960 edge_tagged[lp->edge_index] = 1;
961 break;
962 }
963 }
964 }
965
966 return edge_tagged;
967 }
968
969 /*
970 * Same logic as above function except it will apply it to each vertex
971 */
972 static int *cxr_mesh_reflex_vertices( cxr_mesh *mesh )
973 {
974 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
975
976 int *vertex_tagged = malloc( mesh->p_abverts->count*sizeof(int) );
977 int *connected_planes = malloc( mesh->abpolys.count*sizeof(int) );
978
979 for( int i=0; i<mesh->p_abverts->count; i++ )
980 {
981 vertex_tagged[i]=0;
982 int num_connected = 0;
983
984 /* Create a list of polygons that refer to this vertex */
985 for( int j=0; j<mesh->abpolys.count; j++ )
986 {
987 cxr_polygon *poly = &mesh->polys[j];
988 for( int k=0; k<poly->loop_total; k++ )
989 {
990 cxr_loop *loop = &mesh->loops[poly->loop_start+k];
991 if( loop->index == i )
992 {
993 connected_planes[num_connected ++] = j;
994 break;
995 }
996 }
997 }
998
999 /* Check all combinations for a similar normal */
1000 for( int j=0; j<num_connected-1; j++ )
1001 {
1002 for( int k=j+1; k<num_connected; k++ )
1003 {
1004 cxr_polygon *polyj = &mesh->polys[connected_planes[j]],
1005 *polyk = &mesh->polys[connected_planes[k]];
1006
1007 if( v3_dot(polyj->normal,polyk->normal) > CXR_PLANE_SIMILARITY_MAX )
1008 goto tag_vert;
1009 }
1010 }
1011
1012 /*
1013 * Check if all connected planes either:
1014 * - Bound this vert
1015 * - Coplanar with it
1016 */
1017 for( int j=0; j<num_connected; j++ )
1018 {
1019 for( int k=j+1; k<num_connected; k++ )
1020 {
1021 cxr_polygon *jpoly = &mesh->polys[ connected_planes[j] ],
1022 *kpoly = &mesh->polys[ connected_planes[k] ];
1023
1024 v4f plane;
1025 normal_to_plane( kpoly->normal, kpoly->center, plane );
1026 for( int l=0; l<jpoly->loop_total; l++ )
1027 {
1028 cxr_loop *lp = &mesh->loops[ jpoly->loop_start+l ];
1029
1030 if( plane_polarity( plane, verts[lp->index] ) > 0.001 )
1031 goto tag_vert;
1032 }
1033 }
1034 }
1035
1036 continue;
1037 tag_vert:
1038 vertex_tagged[i] = 1;
1039 }
1040
1041 free( connected_planes );
1042 return vertex_tagged;
1043 }
1044
1045 /*
1046 * Detect if potential future edges create a collision with any of the
1047 * existing edges in the mesh
1048 */
1049 static int cxr_solid_overlap( cxr_mesh *mesh,
1050 cxr_polygon *pa,
1051 cxr_polygon *pb,
1052 int common_edge_index,
1053 int debug )
1054 {
1055 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
1056 cxr_edge *common_edge = &mesh->edges[common_edge_index];
1057
1058 int unique_a = pa->loop_total-2,
1059 unique_b = pb->loop_total-2;
1060
1061 int *unique_verts = malloc( (unique_a+unique_b)*sizeof(int) );
1062 int unique_total = 0;
1063
1064 for( int j=0; j<2; j++ )
1065 {
1066 cxr_polygon *poly = (cxr_polygon *[2]){pa,pb}[j];
1067
1068 for( int i=0; i<poly->loop_total; i++ )
1069 {
1070 cxr_loop *lp = &mesh->loops[poly->loop_start+i];
1071
1072 if( lp->index == common_edge->i0 || lp->index == common_edge->i1 )
1073 continue;
1074
1075 unique_verts[ unique_total ++ ] = lp->index;
1076 }
1077 }
1078
1079 v3f ca, cb;
1080
1081 for( int i=0; i<unique_a; i++ )
1082 {
1083 for( int j=unique_a; j<unique_total; j++ )
1084 {
1085 int i0 = unique_verts[i],
1086 i1 = unique_verts[j];
1087
1088 if( debug )
1089 cxr_debug_line( verts[i0], verts[i1], colours_random[2] );
1090
1091 for( int k=0; k<mesh->abedges.count; k++ )
1092 {
1093 cxr_edge *edge = &mesh->edges[k];
1094
1095 if( edge->i0 == i0 || edge->i0 == i1 ||
1096 edge->i1 == i0 || edge->i1 == i1 ) continue;
1097
1098 double *a0 = verts[i0],
1099 *a1 = verts[i1],
1100 *b0 = verts[edge->i0],
1101 *b1 = verts[edge->i1];
1102
1103 double dist = segment_segment_dist( a0, a1, b0, b1, ca, cb );
1104
1105 if( dist < 0.025 )
1106 {
1107 if( debug )
1108 {
1109 cxr_debug_box( ca, 0.025, colour_error );
1110 cxr_debug_box( cb, 0.025, colour_error );
1111 cxr_debug_line( a0, a1, colour_error );
1112 cxr_debug_line( b0, b1, colour_error );
1113
1114 continue;
1115 }
1116 else
1117 {
1118 free( unique_verts );
1119 return 1;
1120 }
1121 }
1122 }
1123 }
1124 }
1125
1126 if( debug )
1127 {
1128 cxr_debug_line( verts[mesh->edges[common_edge_index].i0],
1129 verts[mesh->edges[common_edge_index].i1],
1130 colour_success );
1131 }
1132
1133 free( unique_verts );
1134 return 0;
1135 }
1136
1137 /*
1138 * Creates the 'maximal' solid that originates from this faceid
1139 *
1140 * Returns the number of faces used
1141 */
1142 static int cxr_buildsolid(
1143 cxr_mesh *mesh,
1144 int faceid,
1145 int *solid,
1146 int *reflex_edges,
1147 int *faces_tagged )
1148 {
1149 faces_tagged[faceid] = faceid;
1150
1151 int solid_len = 0;
1152 solid[solid_len++] = faceid;
1153
1154 int search_start = 0;
1155
1156 search_iterate:;
1157
1158 int changed = 0;
1159 for( int j=search_start; j<solid_len; j++ )
1160 {
1161 cxr_polygon *poly = &mesh->polys[ solid[j] ];
1162
1163 for( int k=0; k<poly->loop_total; k++ )
1164 {
1165 cxr_loop *loop = &mesh->loops[ poly->loop_start+k ];
1166 cxr_edge *edge = &mesh->edges[ loop->edge_index ];
1167
1168 if( faces_tagged[ loop->poly_right ] == -1 )
1169 {
1170 if( !reflex_edges[loop->edge_index] )
1171 {
1172 /* Check for dodgy edges */
1173 cxr_polygon *newpoly = &mesh->polys[loop->poly_right];
1174
1175 if( cxr_solid_overlap(mesh,poly,newpoly,loop->edge_index,0))
1176 goto skip_plane;
1177
1178 /* Looking ahead by one step gives us an early out for invalid
1179 * configurations. This might just all be handled by the new
1180 * edge overlap detector, though.
1181 */
1182 for( int l=0; l < newpoly->loop_total; l++ )
1183 {
1184 cxr_loop *lp1 = &mesh->loops[ newpoly->loop_start+l ];
1185 cxr_polygon *future_face = &mesh->polys[ lp1->poly_right ];
1186
1187 if( reflex_edges[ lp1->edge_index ]
1188 || lp1->poly_right == loop->poly_right )
1189 goto dont_check;
1190
1191 for( int m=0; m<solid_len; m++ )
1192 if( solid[m] == lp1->poly_right )
1193 goto dont_check;
1194
1195 for( int m=0; m<solid_len; m++ )
1196 {
1197 cxr_polygon *polym = &mesh->polys[solid[m]];
1198 double pdist = v3_dot( polym->normal,future_face->normal);
1199
1200 if( pdist > CXR_PLANE_SIMILARITY_MAX )
1201 goto dont_check;
1202 }
1203
1204 dont_check:;
1205 }
1206
1207 /* Check for vertices in the new polygon that exist on a current
1208 * plane. This condition is invalid */
1209 solid[ solid_len ] = loop->poly_right;
1210
1211 if( cxr_valid_solid(mesh,solid,solid_len+1 ) )
1212 {
1213 faces_tagged[ loop->poly_right ] = faceid;
1214 changed = 1;
1215 solid_len ++;
1216 }
1217 }
1218
1219 skip_plane:;
1220 }
1221 }
1222 }
1223 search_start = solid_len;
1224 if(changed)
1225 goto search_iterate;
1226
1227 return solid_len;
1228 }
1229
1230 struct csolid
1231 {
1232 int start, count, edge_count;
1233 v3f center;
1234 };
1235
1236 struct temp_manifold
1237 {
1238 struct manifold_loop
1239 {
1240 cxr_loop loop;
1241 int split;
1242 }
1243 *loops;
1244
1245 int loop_count,
1246 split_count;
1247
1248 enum manifold_status
1249 {
1250 k_manifold_err,
1251 k_manifold_none,
1252 k_manifold_fragmented,
1253 k_manifold_complete,
1254 }
1255 status;
1256 };
1257
1258 /*
1259 * Create polygon from entire manifold structure.
1260 *
1261 * Must be completely co-planar
1262 */
1263 static void cxr_create_poly_full( cxr_mesh *mesh, struct temp_manifold *src )
1264 {
1265 if( cxr_create_poly( mesh, src->loop_count ) )
1266 {
1267 for( int l=0; l<src->loop_count; l++ )
1268 cxr_poly_push_index( mesh, src->loops[ l ].loop.index);
1269
1270 cxr_poly_finish( mesh );
1271 }
1272 }
1273
1274 /*
1275 * Links up all edges into a potential new manifold
1276 *
1277 * The return status can be:
1278 * (err): Critical programming error
1279 * none: No manifold to create
1280 * fragmented: Multiple sections exist, not just one
1281 * complete: Optimial manifold was created
1282 */
1283 static void cxr_link_manifold(
1284 cxr_mesh *mesh,
1285 struct csolid *solid,
1286 int *solid_buffer,
1287 struct temp_manifold *manifold
1288 )
1289 {
1290 cxr_loop **edge_list = malloc( sizeof(*edge_list) * solid->edge_count );
1291
1292 int init_reverse = 0;
1293 int unique_edge_count = 0;
1294
1295 /* Gather list of unique edges */
1296
1297 for( int j=0; j<solid->count; j++ )
1298 {
1299 cxr_polygon *poly = &mesh->polys[ solid_buffer[solid->start+j] ];
1300
1301 for( int k=0; k<poly->loop_total; k++ )
1302 {
1303 cxr_loop *loop = &mesh->loops[ poly->loop_start+k ];
1304
1305 for( int l=0; l<unique_edge_count; l++ )
1306 if( edge_list[l]->edge_index == loop->edge_index )
1307 goto skip_edge;
1308
1309 for( int l=0; l<solid->count; l++ )
1310 if( loop->poly_right == solid_buffer[solid->start+l] )
1311 goto skip_edge;
1312
1313 edge_list[ unique_edge_count ] = loop;
1314
1315 if( unique_edge_count == 0 )
1316 {
1317 cxr_edge *edgeptr = &mesh->edges[ loop->edge_index ];
1318 if( edgeptr->i1 == loop->index )
1319 init_reverse = 1;
1320 }
1321
1322 unique_edge_count ++;
1323 skip_edge:;
1324 }
1325 }
1326
1327 if( unique_edge_count == 0 )
1328 {
1329 free( edge_list );
1330 manifold->status = k_manifold_none;
1331 return;
1332 }
1333
1334 /* Link edges together to form manifold */
1335 manifold->loops = malloc( solid->edge_count*sizeof(struct manifold_loop));
1336 manifold->split_count = 0;
1337 manifold->loop_count = 0;
1338
1339 cxr_edge *current = &mesh->edges[ edge_list[0]->edge_index ];
1340
1341 int endpt = (!init_reverse)? current->i0: current->i1,
1342 start = endpt,
1343 curface = edge_list[0]->poly_left;
1344
1345 manifold_continue:
1346 for( int j=0; j<unique_edge_count; j++ )
1347 {
1348 cxr_edge *other = &mesh->edges[ edge_list[j]->edge_index ];
1349 if( other == current )
1350 continue;
1351
1352 if( other->i0 == endpt || other->i1 == endpt )
1353 {
1354 current = other;
1355 int lastpt = endpt;
1356
1357 if( other->i0 == endpt ) endpt = current->i1;
1358 else endpt = current->i0;
1359
1360 struct manifold_loop *ml = &manifold->loops[ manifold->loop_count ++ ];
1361
1362 if( curface==edge_list[j]->poly_left )
1363 {
1364 ml->split = 1;
1365 manifold->split_count ++;
1366 }
1367 else
1368 ml->split = 0;
1369
1370 ml->loop.edge_index = edge_list[j]->edge_index;
1371 ml->loop.poly_left = edge_list[j]->poly_left;
1372 ml->loop.index = lastpt;
1373 ml->loop.poly_right = edge_list[j]->poly_right;
1374
1375 curface = edge_list[j]->poly_left;
1376
1377 if(endpt == start)
1378 {
1379 if( manifold->loop_count < unique_edge_count )
1380 manifold->status = k_manifold_fragmented;
1381 else
1382 manifold->status = k_manifold_complete;
1383
1384 goto manifold_complete;
1385 }
1386
1387 goto manifold_continue;
1388 }
1389 }
1390
1391 /* Incomplete links */
1392 manifold->status = k_manifold_err;
1393
1394 manifold_complete:
1395
1396 free( edge_list );
1397 return;
1398 }
1399
1400 /*
1401 * Reconstruct implied internal geometry where the manifold doesn't have
1402 * enough information (vertices) to create a full result.
1403 */
1404 static int cxr_build_implicit_geo( cxr_mesh *mesh, int new_polys, int start )
1405 {
1406 for( int i=0; i<new_polys-2; i++ )
1407 {
1408 for( int j=i+1; j<new_polys-1; j++ )
1409 {
1410 for( int k=j+1; k<new_polys; k++ )
1411 {
1412 cxr_polygon *ptri = &mesh->polys[ start+i ],
1413 *ptrj = &mesh->polys[ start+j ],
1414 *ptrk = &mesh->polys[ start+k ];
1415
1416 v4f planei, planej, planek;
1417 normal_to_plane(ptri->normal,ptri->center,planei);
1418 normal_to_plane(ptrj->normal,ptrj->center,planej);
1419 normal_to_plane(ptrk->normal,ptrk->center,planek);
1420
1421 v3f intersect;
1422
1423 if( plane_intersect(planei,planej,planek,intersect) )
1424 {
1425 /* Make sure the point is inside the convex region */
1426
1427 int point_valid = 1;
1428 for( int l=0; l<mesh->abpolys.count; l++ )
1429 {
1430 cxr_polygon *ptrl = &mesh->polys[l];
1431 v4f planel;
1432
1433 normal_to_plane(ptrl->normal, ptrl->center, planel);
1434
1435 if( plane_polarity( planel, intersect ) > 0.01 )
1436 {
1437 cxr_log( "degen vert, planes %d, %d, %d [max:%d]\n",
1438 i,j,k, new_polys );
1439
1440 cxr_debug_poly( mesh, ptri, colours_random[3] );
1441 cxr_debug_poly( mesh, ptrj, colours_random[1] );
1442 cxr_debug_poly( mesh, ptrk, colours_random[2] );
1443
1444 return 0;
1445 }
1446 }
1447
1448 /* Extend faces to include this vert */
1449
1450 int nvertid = mesh->p_abverts->count;
1451 cxr_ab_push( mesh->p_abverts, intersect );
1452
1453 ptrj->loop_start += 1;
1454 ptrk->loop_start += 2;
1455
1456 cxr_ab_reserve( &mesh->abloops, 3);
1457
1458 int newi = ptri->loop_start+ptri->loop_total,
1459 newj = ptrj->loop_start+ptrj->loop_total,
1460 newk = ptrk->loop_start+ptrk->loop_total;
1461
1462 cxr_loop
1463 *lloopi = cxr_ab_empty_at(&mesh->abloops, newi),
1464 *lloopj = cxr_ab_empty_at(&mesh->abloops, newj),
1465 *lloopk = cxr_ab_empty_at(&mesh->abloops, newk);
1466
1467 lloopi->index = nvertid;
1468 lloopi->edge_index = 0;
1469 lloopi->poly_left = start + i;
1470 lloopi->poly_right = -1;
1471
1472 lloopj->index = nvertid;
1473 lloopj->poly_left = start + j;
1474 lloopj->edge_index = 0;
1475 lloopj->poly_right = -1;
1476
1477 lloopk->index = nvertid;
1478 lloopk->edge_index = 0;
1479 lloopk->poly_left = start + k;
1480 lloopk->poly_right = -1;
1481
1482 v2_zero(lloopi->uv);
1483 v2_zero(lloopj->uv);
1484 v2_zero(lloopk->uv);
1485
1486 ptri->loop_total ++;
1487 ptrj->loop_total ++;
1488 ptrk->loop_total ++;
1489
1490 double qi = 1.0/(double)ptri->loop_total,
1491 qj = 1.0/(double)ptrj->loop_total,
1492 qk = 1.0/(double)ptrk->loop_total;
1493
1494 /* Adjust centers of faces */
1495 v3_lerp( ptri->center, intersect, qi, ptri->center );
1496 v3_lerp( ptrj->center, intersect, qj, ptrj->center );
1497 v3_lerp( ptrk->center, intersect, qk, ptrk->center );
1498 }
1499 }
1500 }
1501 }
1502
1503 return 1;
1504 }
1505
1506 /*
1507 * Convexer's main algorithm
1508 *
1509 * Return the best availible convex solid from mesh, and patch the existing mesh
1510 * to fill the gap where the new mesh left it.
1511 *
1512 * Returns NULL if shape is already convex or empty.
1513 * This function will not preserve edge data such as freestyle, sharp etc.
1514 */
1515 static cxr_mesh *cxr_pull_best_solid(
1516 cxr_mesh *mesh,
1517 int preserve_more_edges,
1518 u32 *error )
1519 {
1520 if( !cxr_mesh_link_loops(mesh) )
1521 {
1522 cxr_log( "non-manifold edges are in the mesh: "
1523 "implicit internal geometry does not have full support\n" );
1524
1525 return NULL;
1526 }
1527
1528 int *edge_tagged = cxr_mesh_reflex_edges( mesh );
1529 int *vertex_tagged = cxr_mesh_reflex_vertices( mesh );
1530
1531 /*
1532 * Connect all marked vertices that share an edge
1533 */
1534
1535 int *edge_important = malloc(mesh->abedges.count*sizeof(int));
1536 for( int i=0; i< mesh->abedges.count; i++ )
1537 edge_important[i] = 0;
1538
1539 for( int i=0; i<mesh->abpolys.count; i ++ )
1540 {
1541 cxr_polygon *poly = &mesh->polys[i];
1542 int not_tagged = -1,
1543 tag_count = 0;
1544
1545 for( int j=0; j<poly->loop_total; j++ )
1546 {
1547 cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
1548
1549 if( !edge_tagged[ loop->edge_index ] )
1550 {
1551 if( not_tagged == -1 )
1552 not_tagged = loop->edge_index;
1553 else
1554 goto edge_unimportant;
1555 }
1556 }
1557
1558 if( not_tagged != -1 )
1559 edge_important[not_tagged]=1;
1560
1561 edge_unimportant:;
1562 }
1563
1564 /*
1565 * Connect edges where both vertices are reflex, only if we are not
1566 * preserving them
1567 */
1568 for( int i=0; i<mesh->abedges.count; i ++ )
1569 {
1570 if( edge_important[i] && preserve_more_edges ) continue;
1571
1572 cxr_edge *edge = &mesh->edges[i];
1573 if( vertex_tagged[edge->i0] && vertex_tagged[edge->i1] )
1574 edge_tagged[i] = 1;
1575 }
1576
1577 free( edge_important );
1578
1579 int *faces_tagged = malloc(mesh->abpolys.count*sizeof(int));
1580 for( int i=0; i<mesh->abpolys.count; i++ )
1581 faces_tagged[i] = -1;
1582
1583 struct csolid *candidates;
1584 int *solid_buffer = malloc( mesh->abpolys.count*sizeof(int) ),
1585 solid_buffer_len = 0,
1586 candidate_count = 0;
1587
1588 candidates = malloc( mesh->abpolys.count *sizeof(struct csolid) );
1589
1590 /*
1591 * Create a valid, non-overlapping solid for every face present in the mesh
1592 */
1593 for( int i=0; i<mesh->abpolys.count; i++ )
1594 {
1595 if( faces_tagged[i] != -1 ) continue;
1596 faces_tagged[i] = i;
1597
1598 int *solid = &solid_buffer[ solid_buffer_len ];
1599 int len = cxr_buildsolid( mesh, i, solid, edge_tagged, faces_tagged );
1600
1601 /* add entry */
1602 struct csolid *csolid = &candidates[candidate_count ++];
1603 csolid->start = solid_buffer_len;
1604 csolid->count = len;
1605 csolid->edge_count = 0;
1606
1607 v3_zero( csolid->center );
1608 for( int j=0; j<len; j++ )
1609 {
1610 cxr_polygon *polyj = &mesh->polys[ solid[j] ];
1611 v3_add( polyj->center, csolid->center, csolid->center );
1612 csolid->edge_count += polyj->loop_total;
1613 }
1614 v3_divs( csolid->center, len, csolid->center );
1615 solid_buffer_len += len;
1616 }
1617
1618 free( edge_tagged );
1619 free( vertex_tagged );
1620 free( faces_tagged );
1621
1622 /*
1623 * Choosing the best solid: most defined manifold
1624 */
1625 struct csolid *best_solid = NULL;
1626 int fewest_manifold_splits = INT32_MAX;
1627
1628 struct temp_manifold best_manifold = { .loops = NULL, .loop_count = 0 };
1629 int max_solid_faces = 0;
1630
1631 for( int i=0; i<candidate_count; i++ )
1632 {
1633 struct csolid *solid = &candidates[i];
1634 max_solid_faces = cxr_max(max_solid_faces,solid->count);
1635
1636 if( solid->count <= 2 )
1637 continue;
1638
1639 struct temp_manifold manifold;
1640 cxr_link_manifold( mesh, solid, solid_buffer, &manifold);
1641
1642 if( manifold.status == k_manifold_err )
1643 {
1644 *error = CXR_ERROR_BAD_MANIFOLD;
1645 free(solid_buffer);
1646 free(candidates);
1647 free(manifold.loops);
1648 free(best_manifold.loops);
1649 return NULL;
1650 }
1651
1652 if( manifold.status == k_manifold_complete )
1653 {
1654 if( manifold.split_count < fewest_manifold_splits )
1655 {
1656 fewest_manifold_splits = manifold.split_count;
1657 best_solid = solid;
1658
1659 free( best_manifold.loops );
1660 best_manifold = manifold;
1661 continue;
1662 }
1663 }
1664
1665 if( manifold.status != k_manifold_none )
1666 free( manifold.loops );
1667 }
1668
1669 if( max_solid_faces < 2 )
1670 {
1671 *error = CXR_ERROR_NO_SOLIDS;
1672 free(solid_buffer);
1673 free(candidates);
1674 free(best_manifold.loops);
1675 return NULL;
1676 }
1677
1678 if( best_solid != NULL )
1679 {
1680 cxr_mesh *pullmesh = cxr_alloc_mesh( best_solid->edge_count,
1681 best_solid->edge_count,
1682 best_solid->count,
1683 mesh->p_abverts );
1684
1685 for( int i=0; i<best_solid->count; i++ )
1686 {
1687 int nface_id = pullmesh->abpolys.count;
1688 int exist_plane_id = solid_buffer[best_solid->start+i];
1689
1690 cxr_polygon *exist_face = &mesh->polys[ exist_plane_id ],
1691 *new_face = cxr_ab_empty( &pullmesh->abpolys );
1692
1693 *new_face = *exist_face;
1694 new_face->loop_start = pullmesh->abloops.count;
1695
1696 for( int j=0; j<exist_face->loop_total; j++ )
1697 {
1698 cxr_loop *exist_loop = &mesh->loops[ exist_face->loop_start+j ],
1699 *new_loop = cxr_ab_empty(&pullmesh->abloops);
1700
1701 new_loop->index = exist_loop->index;
1702 new_loop->poly_left = nface_id;
1703 new_loop->poly_right = -1;
1704 new_loop->edge_index = 0;
1705 v2_copy( exist_loop->uv, new_loop->uv );
1706 }
1707
1708 exist_face->loop_total = -1;
1709 }
1710
1711 int new_polys = 0;
1712 int pullmesh_new_start = pullmesh->abpolys.count;
1713
1714 if( fewest_manifold_splits != 0 )
1715 {
1716 /* Unusual observation:
1717 * If the split count is odd, the manifold can be created easily
1718 *
1719 * If it is even, implicit internal geometry is needed to be
1720 * constructed. So the manifold gets folded as we create it segment
1721 * by segment.
1722 *
1723 * I'm not sure if this is a well defined rule of geometry, but seems
1724 * to apply to the data we care about.
1725 */
1726 int collapse_used_segments = (u32)fewest_manifold_splits & 0x1? 0: 1;
1727
1728 manifold_repeat:
1729
1730 for( int j=0; j < best_manifold.loop_count; j++ )
1731 {
1732 if( !best_manifold.loops[j].split ) continue;
1733
1734 cxr_loop *loop = &best_manifold.loops[j].loop;
1735
1736 for( int k=1; k< best_manifold.loop_count; k++ )
1737 {
1738 int index1 = cxr_range(j+k, best_manifold.loop_count );
1739 cxr_loop *loop1 = &best_manifold.loops[index1].loop;
1740
1741 if( best_manifold.loops[index1].split )
1742 {
1743 if( k==1 )
1744 break;
1745
1746 new_polys ++;
1747
1748 if( new_polys > best_manifold.loop_count )
1749 {
1750 cxr_log( "Programming error: Too many new polys!\n" );
1751 exit(1);
1752 }
1753
1754 if( cxr_create_poly( pullmesh, k+1 ) )
1755 {
1756 for( int l=0; l<k+1; l++ )
1757 {
1758 int i0 = cxr_range(j+l, best_manifold.loop_count ),
1759 index = best_manifold.loops[ i0 ].loop.index;
1760
1761 cxr_poly_push_index( pullmesh, index );
1762 }
1763 cxr_poly_finish( pullmesh );
1764 }
1765
1766 /* Collapse down manifold */
1767 if( collapse_used_segments )
1768 {
1769 best_manifold.loops[j].split = 0;
1770 best_manifold.loops[index1].split = 0;
1771
1772 int new_length = (best_manifold.loop_count-(k-1));
1773
1774 struct temp_manifold new_manifold = {
1775 .loop_count = new_length
1776 };
1777 new_manifold.loops =
1778 malloc( new_length*sizeof(*new_manifold.loops) );
1779
1780 for( int l=0; l<new_length; l ++ )
1781 {
1782 int i_src = cxr_range( j+k+l, best_manifold.loop_count);
1783 new_manifold.loops[l] = best_manifold.loops[i_src];
1784 }
1785
1786 free( best_manifold.loops );
1787 best_manifold = new_manifold;
1788
1789 goto manifold_repeat;
1790 }
1791
1792 j=j+k-1;
1793 break;
1794 }
1795 }
1796 }
1797
1798 if( best_manifold.loop_count && collapse_used_segments )
1799 {
1800 cxr_create_poly_full( pullmesh, &best_manifold );
1801 new_polys ++;
1802 }
1803 }
1804 else
1805 {
1806 cxr_create_poly_full( pullmesh, &best_manifold );
1807 new_polys = 1;
1808 }
1809
1810 if( new_polys >= 3 )
1811 {
1812 if( !cxr_build_implicit_geo( pullmesh, new_polys, pullmesh_new_start ))
1813 {
1814 free(solid_buffer);
1815 free(candidates);
1816 free(best_manifold.loops);
1817
1818 cxr_free_mesh( pullmesh );
1819 *error = CXR_ERROR_DEGEN_IMPLICIT;
1820 return NULL;
1821 }
1822 }
1823
1824 /*
1825 * Copy faces from the pullmesh into original, to patch up where there
1826 * would be gaps created
1827 */
1828 for( int i=0; i<new_polys; i++ )
1829 {
1830 int rface_id = mesh->abpolys.count;
1831 cxr_polygon *pface = &pullmesh->polys[pullmesh_new_start+i],
1832 *rip_face = cxr_ab_empty(&mesh->abpolys);
1833
1834 rip_face->loop_start = mesh->abloops.count;
1835 rip_face->loop_total = pface->loop_total;
1836 rip_face->material_id = -1;
1837
1838 for( int j=0; j<rip_face->loop_total; j++ )
1839 {
1840 cxr_loop *ploop =
1841 &pullmesh->loops[ pface->loop_start+pface->loop_total-j-1 ],
1842 *rloop = cxr_ab_empty(&mesh->abloops);
1843
1844 rloop->index = ploop->index;
1845 rloop->poly_left = rface_id;
1846 rloop->poly_right = -1;
1847 rloop->edge_index = 0;
1848 v2_copy( ploop->uv, rloop->uv );
1849 }
1850
1851 v3_copy( pface->center, rip_face->center );
1852 v3_negate( pface->normal, rip_face->normal );
1853 }
1854
1855 cxr_mesh_update( mesh );
1856 cxr_mesh_update( pullmesh );
1857
1858 cxr_mesh_clean_faces( mesh );
1859 cxr_mesh_clean_edges( mesh );
1860 cxr_mesh_clean_faces( pullmesh );
1861 cxr_mesh_clean_edges( pullmesh );
1862
1863 free(solid_buffer);
1864 free(candidates);
1865 free(best_manifold.loops);
1866
1867 return pullmesh;
1868 }
1869
1870 free(solid_buffer);
1871 free(candidates);
1872 free(best_manifold.loops);
1873
1874 return NULL;
1875 }
1876
1877 /*
1878 * Convert from the format we recieve from blender into our internal format
1879 * with auto buffers.
1880 */
1881 static cxr_mesh *cxr_to_internal_format(
1882 cxr_input_mesh *src,
1883 cxr_abuffer *abverts
1884 ){
1885 cxr_mesh *mesh = cxr_alloc_mesh( src->edge_count, src->loop_count,
1886 src->poly_count, abverts );
1887
1888 cxr_ab_init( abverts, sizeof(v3f), src->vertex_count );
1889
1890 memcpy( mesh->abedges.arr, src->edges, src->edge_count*sizeof(cxr_edge));
1891 memcpy( mesh->abpolys.arr, src->polys, src->poly_count*sizeof(cxr_polygon));
1892 memcpy( abverts->arr, src->vertices, src->vertex_count*sizeof(v3f));
1893 mesh->abedges.count = src->edge_count;
1894 mesh->abloops.count = src->loop_count;
1895 mesh->abpolys.count = src->poly_count;
1896
1897 cxr_mesh_update( mesh );
1898
1899 for( int i=0; i<src->loop_count; i++ )
1900 {
1901 cxr_loop *lp = &mesh->loops[i];
1902
1903 lp->index = src->loops[i].index;
1904 lp->edge_index = src->loops[i].edge_index;
1905 v2_copy( src->loops[i].uv, lp->uv );
1906 }
1907
1908 abverts->count = src->vertex_count;
1909 return mesh;
1910 }
1911
1912 /*
1913 * Find most extreme point along a given direction
1914 */
1915 static double support_distance( v3f verts[3], v3f dir, double coef )
1916 {
1917 return cxr_maxf
1918 (
1919 coef * v3_dot( verts[0], dir ),
1920 cxr_maxf
1921 (
1922 coef * v3_dot( verts[1], dir ),
1923 coef * v3_dot( verts[2], dir )
1924 )
1925 );
1926 }
1927
1928 /*
1929 * Convert regular UV'd triangle int Source's u/vaxis vectors
1930 *
1931 * This supports affine move, scale, rotation, parallel skewing
1932 */
1933 static void cxr_calculate_axis( cxr_texinfo *transform, v3f verts[3],
1934 v2f uvs[3], v2f texture_res
1935 ){
1936 v2f tT, bT; /* Tangent/bitangent pairs for UV space and world */
1937 v3f tW, bW;
1938
1939 v2_sub( uvs[0], uvs[1], tT );
1940 v2_sub( uvs[2], uvs[1], bT );
1941 v3_sub( verts[0], verts[1], tW );
1942 v3_sub( verts[2], verts[1], bW );
1943
1944 /* Use arbitrary projection if there is no UV */
1945 if( v2_length( tT ) < 0.0001 || v2_length( bT ) < 0.0001 )
1946 {
1947 v3f uaxis, normal, vaxis;
1948
1949 v3_copy( tW, uaxis );
1950 v3_normalize( uaxis );
1951
1952 v3_cross( tW, bW, normal );
1953 v3_cross( normal, uaxis, vaxis );
1954 v3_normalize( vaxis );
1955
1956 v3_copy( uaxis, transform->uaxis );
1957 v3_copy( vaxis, transform->vaxis );
1958 v2_zero( transform->offset );
1959
1960 v2_div( (v2f){128.0, 128.0}, texture_res, transform->scale );
1961 transform->winding = 1.0;
1962 return;
1963 }
1964
1965 /* Detect if UV is reversed */
1966 double winding = v2_cross( tT, bT ) >= 0.0f? 1.0f: -1.0f;
1967
1968 /* UV projection reference */
1969 v2f vY, vX;
1970 v2_muls((v2f){1,0}, winding, vX);
1971 v2_muls((v2f){0,1}, winding, vY);
1972
1973 /* Reproject reference into world space, including skew */
1974 v3f uaxis1, vaxis1;
1975
1976 v3_muls( tW, v2_cross(vX,bT) / v2_cross(bT,tT), uaxis1 );
1977 v3_muladds( uaxis1, bW, v2_cross(vX, tT) / v2_cross(tT,bT), uaxis1 );
1978
1979 v3_muls( tW, v2_cross(vY,bT) / v2_cross(bT,tT), vaxis1 );
1980 v3_muladds( vaxis1, bW, v2_cross(vY,tT) / v2_cross(tT,bT), vaxis1 );
1981
1982 v3_normalize( uaxis1 );
1983 v3_normalize( vaxis1 );
1984
1985 /* Apply source transform to axis (yes, they also need to be swapped) */
1986 v3f norm, uaxis, vaxis;
1987
1988 v3_cross( bW, tW, norm );
1989 v3_normalize(norm);
1990 v3_cross( vaxis1, norm, uaxis );
1991 v3_cross( uaxis1, norm, vaxis );
1992
1993 /* real UV scale */
1994 v2f uvmin, uvmax, uvdelta;
1995 v2_minv( uvs[0], uvs[1], uvmin );
1996 v2_minv( uvmin, uvs[2], uvmin );
1997 v2_maxv( uvs[0], uvs[1], uvmax );
1998 v2_maxv( uvmax, uvs[2], uvmax );
1999
2000 v2_sub( uvmax, uvmin, uvdelta );
2001
2002 /* world-uv scale */
2003 v2f uvminw, uvmaxw, uvdeltaw;
2004 uvminw[0] = -support_distance( verts, uaxis, -1.0f );
2005 uvmaxw[0] = support_distance( verts, uaxis, 1.0f );
2006 uvminw[1] = -support_distance( verts, vaxis, -1.0f );
2007 uvmaxw[1] = support_distance( verts, vaxis, 1.0f );
2008
2009 v2_sub( uvmaxw, uvminw, uvdeltaw );
2010
2011 /* VMf uv scale */
2012 v2f uv_scale;
2013 v2_div( uvdeltaw, uvdelta, uv_scale );
2014 v2_div( uv_scale, texture_res, uv_scale );
2015
2016 /* Find offset via 'natural' point */
2017 v2f target_uv, natural_uv, tex_offset;
2018 v2_mul( uvs[0], texture_res, target_uv );
2019
2020 natural_uv[0] = v3_dot( uaxis, verts[0] );
2021 natural_uv[1] = -v3_dot( vaxis, verts[0] );
2022 v2_div( natural_uv, uv_scale, natural_uv );
2023
2024 tex_offset[0] = target_uv[0]-natural_uv[0];
2025 tex_offset[1] = -(target_uv[1]-natural_uv[1]);
2026
2027 /* Copy everything into output */
2028 v3_copy( uaxis, transform->uaxis );
2029 v3_copy( vaxis, transform->vaxis );
2030 v2_copy( tex_offset, transform->offset );
2031 v2_copy( uv_scale, transform->scale );
2032 transform->winding = winding;
2033 }
2034
2035 CXR_API cxr_input_mesh *cxr_write_test_data( cxr_input_mesh *src )
2036 {
2037 FILE *fp = fopen(
2038 "/home/harry/Documents/blender_addons_remote/addons/convexer/src/solid.h",
2039 "w" );
2040
2041 fprintf( fp, "v3f test_verts[] = {\n" );
2042 for( int i=0; i<src->vertex_count; i ++ )
2043 {
2044 fprintf( fp, " { %f, %f, %f },\n",
2045 src->vertices[i][0],
2046 src->vertices[i][1],
2047 src->vertices[i][2] );
2048 }
2049 fprintf( fp, "};\n" );
2050
2051 fprintf( fp, "struct cxr_input_loop test_loops[] = {\n" );
2052 for( int i=0; i<src->loop_count; i ++ )
2053 {
2054 fprintf( fp, " {%d, %d},\n",
2055 src->loops[i].index,
2056 src->loops[i].edge_index);
2057 }
2058 fprintf( fp, "};\n" );
2059
2060 fprintf( fp, "struct cxr_polygon test_polys[] = {\n" );
2061 for( int i=0; i <src->poly_count; i++ )
2062 {
2063 fprintf( fp, " {%d, %d, {%f, %f, %f}, {%f, %f, %f}},\n",
2064 src->polys[i].loop_start,
2065 src->polys[i].loop_total,
2066 src->polys[i].normal[0],
2067 src->polys[i].normal[1],
2068 src->polys[i].normal[2],
2069 src->polys[i].center[0],
2070 src->polys[i].center[1],
2071 src->polys[i].center[2] );
2072 }
2073 fprintf( fp, "};\n" );
2074
2075 fprintf( fp, "struct cxr_edge test_edges[] = {\n" );
2076 for( int i=0; i<src->edge_count; i++ )
2077 {
2078 fprintf( fp, " {%d, %d, %d},\n",
2079 src->edges[i].i0,
2080 src->edges[i].i1,
2081 src->edges[i].freestyle
2082 );
2083 }
2084 fprintf( fp, "};\n" );
2085
2086 fprintf( fp, "struct cxr_input_mesh test_mesh = {\n" );
2087 fprintf( fp, " .vertices = test_verts,\n" );
2088 fprintf( fp, " .loops = test_loops,\n" );
2089 fprintf( fp, " .edges = test_edges,\n" );
2090 fprintf( fp, " .polys = test_polys,\n" );
2091 fprintf( fp, " .poly_count=%d,\n", src->poly_count );
2092 fprintf( fp, " .vertex_count=%d,\n", src->vertex_count );
2093 fprintf( fp, " .edge_count=%d,\n",src->edge_count );
2094 fprintf( fp, " .loop_count=%d\n", src->loop_count );
2095 fprintf( fp, "};\n" );
2096
2097 fclose( fp );
2098
2099 return NULL;
2100 }
2101
2102 static int cxr_cardinal( v3f a, int ignore )
2103 {
2104 int component = 0;
2105 double component_max = -CXR_BIG_NUMBER;
2106
2107 for( int i=0; i<3; i++ )
2108 {
2109 if( i == ignore ) continue;
2110
2111 if( fabs(a[i]) > component_max )
2112 {
2113 component_max = fabs(a[i]);
2114 component = i;
2115 }
2116 }
2117 double d = a[component] >= 0.0? 1.0: -1.0;
2118 v3_zero( a );
2119 a[component] = d;
2120
2121 return component;
2122 }
2123
2124 /*
2125 * Convert contiguous mesh to displacement patch
2126 */
2127 static void cxr_write_disp( cxr_mesh *mesh, cxr_input_mesh *inputmesh,
2128 cxr_vdf *output
2129 ){
2130 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
2131
2132 struct vertinfo
2133 {
2134 int con_start, con_count;
2135 int boundary,
2136 used,
2137 search,
2138 corner;
2139
2140 double alpha;
2141 }
2142 *vertinfo = malloc( sizeof(struct vertinfo)*mesh->p_abverts->count );
2143 int *graph = malloc( sizeof(int) * mesh->abedges.count*2 );
2144
2145 int con_pos = 0;
2146 for( int i=0; i<mesh->p_abverts->count; i++ )
2147 {
2148 struct vertinfo *info = &vertinfo[i];
2149 info->con_start = con_pos;
2150 info->con_count = 0;
2151 info->boundary = 0;
2152 info->corner = 0;
2153 info->used = 0;
2154 info->search = 0;
2155 info->alpha = 0.0;
2156
2157 for( int j=0; j<mesh->abedges.count; j++ )
2158 {
2159 cxr_edge *edge = &mesh->edges[j];
2160
2161 if( edge->i0 == i || edge->i1 == i )
2162 {
2163 graph[ con_pos ++ ] = edge->i0 == i? edge->i1: edge->i0;
2164 info->con_count ++;
2165
2166 if( edge->freestyle )
2167 info->boundary = 1;
2168 }
2169 }
2170 }
2171
2172 /*
2173 * Find best normal for brush patch. VBSP uses the original brush as an
2174 * reference for decal projection in-game
2175 */
2176
2177 v3f avg_normal, refv, refu, refn;
2178 v3_zero(refv); v3_zero(refu); v3_zero(refn);
2179
2180 for( int i=0; i<mesh->abpolys.count; i++ )
2181 {
2182 cxr_polygon *poly = &mesh->polys[i];
2183 v3_add( poly->normal, avg_normal, avg_normal );
2184 }
2185 v3_divs( avg_normal, mesh->abpolys.count, avg_normal );
2186
2187 if( v3_length( avg_normal ) <= 1e-6 )
2188 v3_copy( (v3f){ 0.0, 0.0, 1.0 }, avg_normal );
2189 else
2190 v3_normalize( avg_normal );
2191
2192 /*
2193 * Approximately match the area of the result brush faces to the actual
2194 * area.
2195 *
2196 * Necessary for accuracy and even lightmap texel allocation
2197 */
2198
2199 double uv_area = 0.0, face_area = 0.0, sf;
2200 v2f uvboundmin, uvboundmax;
2201 v3f faceboundmin, faceboundmax;
2202 v2f uv_center;
2203 v3f face_center;
2204
2205 v2_fill( uvboundmin, CXR_BIG_NUMBER );
2206 v2_fill( uvboundmax, -CXR_BIG_NUMBER );
2207 v3_fill( faceboundmin, CXR_BIG_NUMBER );
2208 v3_fill( faceboundmax, -CXR_BIG_NUMBER );
2209
2210 for( int i=0; i<mesh->abpolys.count; i++ )
2211 {
2212 cxr_polygon *poly = &mesh->polys[i];
2213
2214 for( int j=0; j<poly->loop_total; j++ )
2215 {
2216 cxr_loop *lp0 = &mesh->loops[ poly->loop_start+j ];
2217 v2_minv( lp0->uv, uvboundmin, uvboundmin);
2218 v2_maxv( lp0->uv, uvboundmax, uvboundmax);
2219 v3_minv( verts[lp0->index], faceboundmin, faceboundmin );
2220 v3_maxv( verts[lp0->index], faceboundmax, faceboundmax );
2221 }
2222
2223 for( int j=0; j<poly->loop_total-2; j++ )
2224 {
2225 cxr_loop *lp0 = &mesh->loops[poly->loop_start],
2226 *lp1 = &mesh->loops[poly->loop_start+j+1],
2227 *lp2 = &mesh->loops[poly->loop_start+j+2];
2228
2229 v3f va, vb, orth;
2230 v3_sub( verts[lp1->index], verts[lp0->index], va );
2231 v3_sub( verts[lp2->index], verts[lp0->index], vb );
2232 v3_cross( va, vb, orth );
2233
2234 face_area += v3_length( orth ) / 2.0;
2235
2236 v2f uva, uvb;
2237 v2_sub( lp1->uv, lp0->uv, uva );
2238 v2_sub( lp2->uv, lp0->uv, uvb );
2239
2240 uv_area += fabs(v2_cross( uva, uvb )) / 2.0;
2241 }
2242 }
2243
2244 v3_add( faceboundmax, faceboundmin, face_center );
2245 v3_muls( face_center, 0.5, face_center );
2246 v2_add( uvboundmin, uvboundmax, uv_center );
2247 v2_muls( uv_center, 0.5, uv_center );
2248
2249 sf = sqrt( face_area / uv_area );
2250 int corner_count = 0;
2251
2252 /*
2253 * Vertex classification
2254 * boundary vertices: they exist on a freestyle edge
2255 * corners: only connected to other boundaries
2256 */
2257 for( int i=0; i<mesh->p_abverts->count; i++ )
2258 {
2259 struct vertinfo *info = &vertinfo[i];
2260 if( !info->boundary ) continue;
2261
2262 int count = 0,
2263 non_manifold = 1;
2264
2265 for( int j=0; j<info->con_count; j++ )
2266 {
2267 int con = graph[info->con_start+j];
2268
2269 if( vertinfo[con].boundary )
2270 count ++;
2271 else
2272 non_manifold = 0;
2273 }
2274
2275 if( count > 2 || non_manifold )
2276 {
2277 info->corner = 1;
2278 corner_count ++;
2279 }
2280 }
2281
2282 /*
2283 * TODO(harry): This currently only supports power 2 displacements
2284 * its quite straightforward to upgrade it.
2285 *
2286 * TODO(harry): Error checking is needed here for bad input data
2287 */
2288
2289 int dispedge[16];
2290 v2f corner_uvs[4];
2291 int dispedge_count;
2292 int disp_count = 0;
2293
2294 for( int i=0; i<mesh->abpolys.count; i++ )
2295 {
2296 cxr_polygon *basepoly = &mesh->polys[i];
2297
2298 for( int h=0; h<basepoly->loop_total; h ++ )
2299 {
2300 int i0 = h,
2301 i1 = cxr_range(h+1,basepoly->loop_total);
2302
2303 cxr_loop *l0 = &mesh->loops[ basepoly->loop_start+i0 ],
2304 *l1 = &mesh->loops[ basepoly->loop_start+i1 ];
2305 struct vertinfo *info = &vertinfo[ l0->index ];
2306
2307 if( !info->corner )
2308 continue;
2309
2310 int corner_count = 1;
2311
2312 cxr_material *matptr =
2313 basepoly->material_id < 0 || inputmesh->material_count == 0?
2314 &cxr_nodraw:
2315 &inputmesh->materials[ basepoly->material_id ];
2316
2317 dispedge_count = 2;
2318 dispedge[0] = l0->index;
2319 dispedge[1] = l1->index;
2320 v2_copy( l0->uv, corner_uvs[0] );
2321
2322 /* Consume (use) face from orignal mesh */
2323 basepoly->loop_total = -1;
2324
2325 while( dispedge_count < 17 )
2326 {
2327 struct vertinfo *edge_head =
2328 &vertinfo[dispedge[dispedge_count-1]];
2329
2330 int newvert = 0;
2331
2332 if( edge_head->corner )
2333 {
2334 /* Find polygon that has edge C-1 -> C */
2335 for( int j=0; j<mesh->abpolys.count && !newvert; j++ )
2336 {
2337 cxr_polygon *poly = &mesh->polys[j];
2338
2339 for( int k=0; k<poly->loop_total; k ++ )
2340 {
2341 int i0 = k,
2342 i1 = cxr_range(k+1,poly->loop_total);
2343
2344 cxr_loop *l0 = &mesh->loops[ poly->loop_start+i0 ],
2345 *l1 = &mesh->loops[ poly->loop_start+i1 ];
2346
2347 if( l0->index == dispedge[dispedge_count-2] &&
2348 l1->index == dispedge[dispedge_count-1] )
2349 {
2350 /* Take the next edge */
2351 v2_copy( l1->uv, corner_uvs[corner_count ++] );
2352
2353 int i2 = cxr_range(i1+1,poly->loop_total);
2354 cxr_loop *l2 = &mesh->loops[ poly->loop_start+i2 ];
2355
2356 dispedge[dispedge_count ++] = l2->index;
2357 newvert = 1;
2358 poly->loop_total = -1;
2359 break;
2360 }
2361 }
2362 }
2363 }
2364 else
2365 {
2366 for( int j=0; j<edge_head->con_count; j++ )
2367 {
2368 int con = graph[edge_head->con_start+j];
2369
2370 if( con == -1 )
2371 continue;
2372
2373 if( dispedge_count > 1 )
2374 if( con == dispedge[dispedge_count-2] )
2375 continue;
2376
2377 struct vertinfo *coninfo = &vertinfo[con];
2378
2379 if( !coninfo->boundary )
2380 continue;
2381
2382 dispedge[ dispedge_count ++ ] = con;
2383 newvert = 1;
2384
2385 break;
2386 }
2387 }
2388
2389 if( !newvert )
2390 {
2391 cxr_debug_box( verts[dispedge[dispedge_count-1]], 0.1,
2392 colour_error);
2393 break;
2394 }
2395 }
2396
2397 /* All edges collected */
2398
2399 v2f va, vb;
2400 v2_sub( corner_uvs[1], corner_uvs[0], va );
2401 v2_sub( corner_uvs[2], corner_uvs[0], vb );
2402
2403 /* Connect up the grid
2404 *
2405 * 0 1 2 3 4
2406 * 15 a b c d
2407 * 14 e f g h
2408 * 13 i j k l
2409 * 12 m n o p
2410 *
2411 * Example: a := common unused vertex that is connected to
2412 * by 1 and 15. Or y-1, and x-1 on the grid.
2413 * g := c and f common vert ^
2414 */
2415
2416 int grid[25];
2417
2418 for( int j=0; j<5; j++ ) grid[j] = dispedge[j];
2419 for( int j=1; j<5; j++ ) grid[j*5+4] = dispedge[j+4];
2420 for( int j=0; j<4; j++ ) grid[4*5+3-j] = dispedge[j+9];
2421 for( int j=1; j<4; j++ ) grid[j*5] = dispedge[16-j];
2422
2423 /* Fill in grid */
2424 for( int j=1; j<4; j++ )
2425 {
2426 for( int k=1; k<4; k++ )
2427 {
2428 int s0 = grid[(j-1)*5+k],
2429 s1 = grid[j*5+k-1];
2430
2431 struct vertinfo *va = &vertinfo[s0],
2432 *vb = &vertinfo[s1];
2433
2434 /* Find common non-used vertex */
2435 for( int l=0; l<va->con_count; l ++ )
2436 {
2437 for( int m=0; m<vb->con_count; m ++ )
2438 {
2439 int cona = graph[va->con_start+l],
2440 conb = graph[vb->con_start+m];
2441
2442 if( cona == conb )
2443 {
2444 if( vertinfo[cona].used || vertinfo[cona].boundary )
2445 continue;
2446
2447 grid[ j*5+k ] = cona;
2448 vertinfo[cona].used = 1;
2449
2450 goto next_cell;
2451 }
2452 }
2453 }
2454
2455 cxr_log( "Broken displacement!\n" );
2456 free( graph );
2457 free( vertinfo );
2458 return;
2459
2460 next_cell:;
2461 }
2462 }
2463
2464 /*
2465 * Create V reference based on first displacement.
2466 * TODO(harry): This is not the moststable selection method!
2467 * faces can come in any order, so the first disp will of
2468 * course always vary. Additionaly the triangle can be oriented
2469 * differently.
2470 *
2471 * Improvement can be made by selecting a first disp/triangle
2472 * based on deterministic factors.
2473 */
2474 if( disp_count == 0 )
2475 {
2476 cxr_texinfo tx;
2477 v3f tri_ref[3];
2478 v3_copy( verts[dispedge[0]], tri_ref[0] );
2479 v3_copy( verts[dispedge[4]], tri_ref[1] );
2480 v3_copy( verts[dispedge[8]], tri_ref[2] );
2481 cxr_calculate_axis( &tx, tri_ref, corner_uvs, (v2f){512,512} );
2482
2483 v3_muls( tx.vaxis, -1.0, refv );
2484 int v_cardinal = cxr_cardinal( refv, -1 );
2485
2486 v3_cross( tx.vaxis, tx.uaxis, refn );
2487 v3_muls( refn, -tx.winding, refn );
2488
2489 /* Computing new reference vectors */
2490 int n1_cardinal = cxr_cardinal( refn, v_cardinal );
2491
2492 int u_cardinal = 0;
2493
2494 for( int j=0; j<2; j++ )
2495 if( u_cardinal == n1_cardinal || u_cardinal == v_cardinal )
2496 u_cardinal ++;
2497
2498 v3_zero(refu);
2499 refu[u_cardinal] = tx.uaxis[u_cardinal] > 0.0? 1.0: -1.0;
2500
2501 v3f p0, pv, pu, pn;
2502
2503 v3_copy( face_center, p0 );
2504 v3_muladds( face_center, refn, 1.5, pn );
2505 v3_muladds( face_center, refv, 1.5, pv );
2506 v3_muladds( face_center, refu, 1.5, pu );
2507
2508 /* Draw reference vectors */
2509 if( cxr_settings.debug )
2510 {
2511 cxr_debug_line( p0, pn, (v4f){0.0,0.0,1.0,1.0});
2512 cxr_debug_line( p0, pv, (v4f){0.0,1.0,0.0,1.0});
2513 cxr_debug_line( p0, pu, (v4f){1.0,0.0,0.0,1.0});
2514 cxr_debug_line(tri_ref[0],tri_ref[1],(v4f){1.0,1.0,1.0,1.0});
2515 cxr_debug_line(tri_ref[1],tri_ref[2],(v4f){1.0,1.0,1.0,1.0});
2516 cxr_debug_line(tri_ref[2],tri_ref[0],(v4f){1.0,1.0,1.0,1.0});
2517 }
2518 }
2519
2520 /* Create world coordinates */
2521 v3f world_corners[8];
2522 v2f world_uv[4];
2523
2524 for( int j=0; j<4; j++ )
2525 {
2526 v2f local_uv;
2527 v2_sub( corner_uvs[j], uv_center, local_uv );
2528 v2_copy( corner_uvs[j], world_uv[j] );
2529 v2_muls( local_uv, sf, local_uv );
2530
2531 v3_muls( refu, local_uv[0], world_corners[j] );
2532 v3_muladds( world_corners[j],refv,local_uv[1],world_corners[j] );
2533 v3_add( face_center, world_corners[j], world_corners[j] );
2534 }
2535
2536 double *colour = colours_random[cxr_range(disp_count,8)];
2537
2538 for( int j=0; j<4; j++ )
2539 v3_muladds( world_corners[j], refn, -1.0, world_corners[j+4] );
2540
2541 if( cxr_settings.debug )
2542 {
2543 for( int j=0; j<4; j++ )
2544 {
2545 double *p0 = world_corners[j],
2546 *p1 = world_corners[cxr_range(j+1,4)];
2547
2548 cxr_debug_arrow( p0, p1, avg_normal, 0.1, colour );
2549 }
2550 }
2551
2552 /* Apply world transform */
2553 for( int j=0; j<8; j++ )
2554 {
2555 double *p0 = world_corners[j];
2556 v3_muls( p0, cxr_context.scale_factor, p0 );
2557
2558 p0[2] += cxr_context.offset_z;
2559 }
2560
2561 cxr_texinfo texinfo_shared;
2562 cxr_calculate_axis( &texinfo_shared, world_corners, world_uv,
2563 (v2f){ matptr->res[0], matptr->res[1] } );
2564
2565 /* Write brush */
2566 cxr_vdf_node( output, "solid" );
2567 cxr_vdf_ki32( output, "id", ++ cxr_context.brush_count );
2568
2569 int sides[6][3] =
2570 {{ 0, 1, 2 },
2571 { 4, 6, 5 },
2572 { 4, 1, 0 },
2573 { 7, 0, 3 },
2574 { 6, 2, 1 },
2575 { 6, 3, 2 }};
2576
2577 v3f normals[25];
2578 double distances[25];
2579
2580 v3f lside0, lside1, lref, vdelta, vworld;
2581 double tx, ty;
2582
2583 for( int j=0; j<5; j++ )
2584 {
2585 ty = (double)j/(double)(5-1);
2586
2587 v3_lerp( world_corners[0], world_corners[3], ty, lside0 );
2588 v3_lerp( world_corners[1], world_corners[2], ty, lside1 );
2589
2590 for( int k=0; k<5; k++ )
2591 {
2592 int index = j*5+k;
2593
2594 tx = (double)k/(double)(5-1);
2595 v3_lerp( lside0, lside1, tx, lref );
2596 v3_muls( verts[grid[index]], cxr_context.scale_factor, vworld );
2597 vworld[2] += cxr_context.offset_z;
2598
2599 v3_sub( vworld, lref, vdelta );
2600 v3_copy( vdelta, normals[index] );
2601 v3_normalize( normals[index] );
2602 distances[index] = v3_dot( vdelta, normals[index] );
2603 }
2604 }
2605
2606 for( int j=0; j<6; j++ )
2607 {
2608 int *side = sides[j];
2609
2610 cxr_vdf_node( output, "side" );
2611 cxr_vdf_ki32( output, "id", ++ cxr_context.face_count );
2612 cxr_vdf_plane( output, "plane", world_corners[side[2]],
2613 world_corners[side[1]],
2614 world_corners[side[0]] );
2615
2616 cxr_vdf_kv( output, "material", matptr->vmt_path );
2617
2618 cxr_vdf_kaxis( output, "uaxis",
2619 texinfo_shared.uaxis,
2620 texinfo_shared.offset[0],
2621 texinfo_shared.scale[0] );
2622 cxr_vdf_kaxis( output, "vaxis",
2623 texinfo_shared.vaxis,
2624 texinfo_shared.offset[1],
2625 texinfo_shared.scale[1] );
2626
2627 cxr_vdf_kdouble( output, "rotation", 0.0 );
2628 cxr_vdf_ki32( output, "lightmapscale", cxr_settings.lightmap_scale);
2629 cxr_vdf_ki32( output, "smoothing_groups", 0 );
2630
2631 if( j == 0 )
2632 {
2633 cxr_vdf_node( output, "dispinfo" );
2634 cxr_vdf_ki32( output, "power", 2 );
2635 cxr_vdf_kv3f( output, "startposition", world_corners[0] );
2636 cxr_vdf_ki32( output, "flags", 0 );
2637 cxr_vdf_kdouble( output, "elevation", 0.0 );
2638 cxr_vdf_ki32( output, "subdiv", 0 );
2639
2640 cxr_vdf_node( output, "normals" );
2641 for( int k=0; k<5; k++ )
2642 cxr_vdf_karrv3f( output, "row", k, &normals[k*5], 5 );
2643 cxr_vdf_edon( output );
2644
2645 cxr_vdf_node( output, "distances" );
2646 for( int k=0; k<5; k++ )
2647 cxr_vdf_karrdouble( output, "row", k, &distances[k*5], 5 );
2648 cxr_vdf_edon( output );
2649
2650 /*
2651 * TODO: This might be needed for the compilers. Opens fine in
2652 * hammer
2653 */
2654
2655 /*
2656 cxr_vdf_node( output, "offsets" );
2657 for( int k=0; k<5; k++ )
2658 cxr_vdf_printf( output,
2659 "\"row%d\" \"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\"\n", k );
2660 cxr_vdf_edon( output );
2661
2662 cxr_vdf_node( output, "offset_normals" );
2663 for( int k=0; k<5; k++ )
2664 cxr_vdf_printf( output,
2665 "\"row%d\" \"0 0 1 0 0 1 0 0 1 0 0 1 0 0 1\"\n", k );
2666 cxr_vdf_edon( output );
2667
2668 cxr_vdf_node( output, "alphas" );
2669 for( int k=0; k<5; k++ )
2670 cxr_vdf_printf( output, "\"row%d\" \"0 0 0 0 0\"\n", k );
2671 cxr_vdf_edon( output );
2672
2673 cxr_vdf_node( output, "triangle_tags" );
2674 for( int k=0; k<5-1; k++ )
2675 cxr_vdf_printf( output,
2676 "\"row%d\" \"9 9 9 9 9 9 9 9\"\n", k );
2677 cxr_vdf_edon( output );
2678
2679 cxr_vdf_node( output, "allowed_verts" );
2680 cxr_vdf_printf( output,
2681 "\"10\" \"-1 -1 -1 -1 -1 -1 -1 -1 -1 -1\"\n" );
2682 cxr_vdf_edon( output );
2683 */
2684
2685 cxr_vdf_edon( output );
2686 }
2687
2688 cxr_vdf_edon( output );
2689 }
2690
2691 cxr_vdf_node( output, "editor");
2692 cxr_vdf_colour255( output, "color",
2693 colours_random[cxr_range(cxr_context.brush_count,8)]);
2694
2695 cxr_vdf_ki32( output, "visgroupshown",1);
2696 cxr_vdf_ki32( output, "visgroupautoshown",1);
2697 cxr_vdf_edon( output );
2698
2699 cxr_vdf_edon( output );
2700 disp_count ++;
2701 }
2702 }
2703
2704 free( graph );
2705 free( vertinfo );
2706 }
2707
2708 static int cxr_solid_checkerr( cxr_mesh *mesh )
2709 {
2710 v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
2711 int err_count = 0;
2712
2713 for( int i=0; i<mesh->abpolys.count; i++ )
2714 {
2715 int plane_err = 0;
2716
2717 cxr_polygon *poly = &mesh->polys[i];
2718 v4f plane;
2719
2720 normal_to_plane( poly->normal, poly->center, plane );
2721
2722 for( int j=0; j<poly->loop_total; j++ )
2723 {
2724 cxr_loop *loop = &mesh->loops[ poly->loop_start+j ];
2725 double *vert = verts[ loop->index ];
2726
2727 if( fabs(plane_polarity(plane,vert)) > 0.0025 )
2728 {
2729 err_count ++;
2730 plane_err ++;
2731
2732 v3f ref;
2733 plane_project_point( plane, vert, ref );
2734
2735 cxr_debug_line( ref, vert, colour_error );
2736 cxr_debug_box( vert, 0.1, colour_error );
2737 }
2738 }
2739
2740 if( plane_err )
2741 cxr_debug_poly( mesh, poly, colour_error );
2742 }
2743
2744 return err_count;
2745 }
2746
2747 CXR_API i32 cxr_convert_mesh_to_vmf(cxr_input_mesh *src, cxr_vdf *output)
2748 {
2749 cxr_abuffer abverts;
2750 cxr_mesh *main_mesh = cxr_to_internal_format(src, &abverts);
2751
2752 u32 error = 0x00;
2753 int invalid_count = 0;
2754
2755 struct solidinf
2756 {
2757 cxr_mesh *pmesh;
2758 int is_displacement, invalid;
2759 };
2760
2761 cxr_abuffer solids;
2762 cxr_ab_init( &solids, sizeof(struct solidinf), 2 );
2763
2764 /*
2765 * Preprocessor 1: Island seperation
2766 */
2767 while(1)
2768 {
2769 cxr_mesh *res = cxr_pull_island( main_mesh );
2770 if( res )
2771 {
2772 cxr_ab_push( &solids, &(struct solidinf){ res, 0 });
2773 }
2774 else break;
2775 }
2776 cxr_ab_push( &solids, &(struct solidinf){main_mesh,0} );
2777
2778 /*
2779 * Preprocessor 2: Displacement processing & error checks
2780 */
2781 for( int i=0; i<solids.count; i++ )
2782 {
2783 struct solidinf *pinf = cxr_ab_ptr(&solids,i);
2784
2785 for( int j=0; j<pinf->pmesh->abpolys.count; j++ )
2786 {
2787 cxr_polygon *poly = &pinf->pmesh->polys[ j ];
2788
2789 for( int k=0; k<poly->loop_total; k++ )
2790 {
2791 cxr_loop *lp = &pinf->pmesh->loops[ poly->loop_start+k ];
2792 cxr_edge *edge = &pinf->pmesh->edges[ lp->edge_index ];
2793
2794 if( edge->freestyle )
2795 goto IL_SOLID_IS_DISPLACEMENT;
2796 }
2797 }
2798
2799 if( cxr_solid_checkerr( pinf->pmesh ) )
2800 {
2801 pinf->invalid = 1;
2802 invalid_count ++;
2803 }
2804
2805 continue;
2806 IL_SOLID_IS_DISPLACEMENT:;
2807
2808 pinf->is_displacement = 1;
2809 cxr_write_disp( pinf->pmesh, src, output );
2810 }
2811
2812 /*
2813 * Main convex decomp algorithm
2814 */
2815 int sources_count = solids.count;
2816
2817 for( int i=0; i<sources_count; i++ )
2818 {
2819 struct solidinf pinf = *(struct solidinf *)cxr_ab_ptr(&solids, i);
2820
2821 if( pinf.is_displacement || pinf.invalid )
2822 continue;
2823
2824 while(1)
2825 {
2826 cxr_mesh *res = cxr_pull_best_solid( pinf.pmesh, 0, &error );
2827
2828 if( res )
2829 {
2830 cxr_ab_push( &solids, &(struct solidinf){res,0} );
2831 if( error )
2832 break;
2833 }
2834 else
2835 {
2836 if( error )
2837 {
2838 /* Retry if non-critical error, with extra edges */
2839
2840 if( error & CXR_ERROR_NO_SOLIDS )
2841 {
2842 error = 0x00;
2843 res = cxr_pull_best_solid(pinf.pmesh, 1, &error);
2844
2845 if( res ) cxr_ab_push( &solids, &(struct solidinf){res,0} );
2846 else break;
2847
2848 if( error ) break;
2849 }
2850 else
2851 break;
2852 }
2853 else
2854 break;
2855 }
2856 }
2857 }
2858
2859 if( cxr_settings.debug )
2860 {
2861 for( int i=0; i<solids.count; i++ )
2862 {
2863 struct solidinf *solid = cxr_ab_ptr(&solids,i);
2864
2865 if( !solid->is_displacement )
2866 cxr_debug_mesh( solid->pmesh, colours_random[cxr_range(i,8)] );
2867 }
2868 }
2869
2870 if( error )
2871 {
2872 for( int i=0; i<solids.count; i++ )
2873 {
2874 struct solidinf *solid = cxr_ab_ptr(&solids,i);
2875 cxr_free_mesh( solid->pmesh );
2876 }
2877
2878 cxr_ab_free( &abverts );
2879 cxr_ab_free( &solids );
2880 return error;
2881 }
2882
2883 /* Write all solids as VMF brushes */
2884 for( int i=0; i<solids.count; i++ )
2885 {
2886 struct solidinf *solid = cxr_ab_ptr(&solids,i);
2887
2888 if( solid->is_displacement ) continue;
2889
2890 cxr_vdf_node( output, "solid" );
2891 cxr_vdf_ki32( output, "id", ++ cxr_context.brush_count );
2892
2893 for( int j=0; j<solid->pmesh->abpolys.count; j++ )
2894 {
2895 cxr_polygon *poly = &solid->pmesh->polys[j];
2896 cxr_loop *ploops = &solid->pmesh->loops[poly->loop_start];
2897
2898 cxr_material *matptr =
2899 poly->material_id < 0 || src->material_count == 0?
2900 &cxr_nodraw:
2901 &src->materials[ poly->material_id ];
2902
2903 cxr_vdf_node( output, "side" );
2904 cxr_vdf_ki32( output, "id", ++ cxr_context.face_count );
2905
2906 v3f verts[3]; v2f uvs[3];
2907
2908 int i0 = ploops[0].index,
2909 i1 = ploops[1].index,
2910 i2 = ploops[2].index;
2911
2912 v3_muls( cxr_ab_ptr(&abverts,i0), cxr_context.scale_factor, verts[0] );
2913 v3_muls( cxr_ab_ptr(&abverts,i1), cxr_context.scale_factor, verts[1] );
2914 v3_muls( cxr_ab_ptr(&abverts,i2), cxr_context.scale_factor, verts[2] );
2915
2916 verts[0][2] += cxr_context.offset_z;
2917 verts[1][2] += cxr_context.offset_z;
2918 verts[2][2] += cxr_context.offset_z;
2919
2920 v2_copy( ploops[0].uv, uvs[0] );
2921 v2_copy( ploops[1].uv, uvs[1] );
2922 v2_copy( ploops[2].uv, uvs[2] );
2923
2924 cxr_vdf_plane( output, "plane", verts[2], verts[1], verts[0] );
2925 cxr_vdf_kv( output, "material", matptr->vmt_path );
2926
2927 cxr_texinfo tx;
2928 cxr_calculate_axis( &tx, verts, uvs,
2929 (double[2]){ matptr->res[0], matptr->res[1] });
2930
2931 cxr_vdf_kaxis( output, "uaxis", tx.uaxis, tx.offset[0], tx.scale[0]);
2932 cxr_vdf_kaxis( output, "vaxis", tx.vaxis, tx.offset[1], tx.scale[1]);
2933
2934 cxr_vdf_kdouble( output, "rotation", 0.0 );
2935 cxr_vdf_ki32( output, "lightmapscale", cxr_settings.lightmap_scale );
2936 cxr_vdf_ki32( output, "smoothing_groups", 0);
2937
2938 cxr_vdf_edon( output );
2939 }
2940
2941 cxr_vdf_node( output, "editor" );
2942 cxr_vdf_colour255( output, "color",
2943 colours_random[cxr_range(cxr_context.brush_count,8)]);
2944
2945 cxr_vdf_ki32( output, "visgroupshown", 1 );
2946 cxr_vdf_ki32( output, "visgroupautoshown", 1 );
2947 cxr_vdf_edon( output );
2948
2949 cxr_vdf_edon( output );
2950 }
2951
2952 for( int i=0; i<solids.count; i++ )
2953 {
2954 struct solidinf *solid = cxr_ab_ptr(&solids,i);
2955 cxr_free_mesh( solid->pmesh );
2956 }
2957
2958 cxr_ab_free( &abverts );
2959 cxr_ab_free( &solids );
2960 return 0;
2961 }
2962
2963
2964 CXR_API void cxr_set_log_function( void (*func)(const char *str) )
2965 {
2966 cxr_log_func = func;
2967 }
2968
2969 CXR_API void cxr_set_line_function( void (*func)(v3f p0, v3f p1, v4f colour) )
2970 {
2971 cxr_line_func = func;
2972 }
2973
2974 CXR_API void cxr_settings_update( struct cxr_settings *settings )
2975 {
2976 cxr_settings = *settings;
2977 }
2978
2979 /*
2980 * Valve Source SDK 2015 CS:GO
2981 */
2982 #define HEADER_LUMPS 64
2983 #define LUMP_WORLDLIGHTS 54
2984
2985 #pragma pack(push,1)
2986
2987 struct header
2988 {
2989 int ident;
2990 int version;
2991
2992 struct lump
2993 {
2994 int fileofs, filelen;
2995 int version;
2996
2997 char fourCC[4];
2998 }
2999 lumps[ HEADER_LUMPS ];
3000
3001 int mapRevision;
3002 };
3003
3004 struct worldlight
3005 {
3006 float origin[3];
3007 float intensity[3];
3008 float normal[3];
3009 float shadow_cast_offset[3];
3010 int cluster;
3011 int type;
3012 int style;
3013 float stopdot;
3014 float stopdot2;
3015 float exponent;
3016 float radius;
3017 float constant_attn;
3018 float linear_attn;
3019 float quadratic_attn;
3020 int flags;
3021 int texinfo;
3022 int owner;
3023 };
3024 #pragma pack(pop)
3025
3026 /*
3027 * Utility for patching BSP tools to remove -1 distance lights (we set them
3028 * like that, because we want these lights to go away)
3029 *
3030 * Yes, there is no way to do this in hammer
3031 * Yes, the distance KV is unused but still gets compiled to this lump
3032 * No, Entities only compile will not do this for you
3033 */
3034 CXR_API int cxr_lightpatch_bsp( const char *path )
3035 {
3036 printf( "Lightpatch: %s\n", path );
3037
3038 FILE *fp = fopen( path, "r+b" );
3039
3040 if( !fp )
3041 {
3042 cxr_log( "Could not open BSP file for editing (r+b)\n" );
3043 return 0;
3044 }
3045
3046 /* Read bsp */
3047 struct header header;
3048 fread( &header, sizeof(struct header), 1, fp );
3049 struct lump *lump = &header.lumps[ LUMP_WORLDLIGHTS ];
3050
3051 /* Read worldlight array */
3052 struct worldlight *lights = malloc( lump->filelen );
3053 fseek( fp, lump->fileofs, SEEK_SET );
3054 fread( lights, lump->filelen, 1, fp );
3055
3056 /* Remove all marked lights */
3057 int light_count = lump->filelen / sizeof(struct worldlight);
3058 int new_count = 0;
3059
3060 for( int i = 0; i < light_count; i ++ )
3061 if( lights[i].radius >= 0.0f )
3062 lights[new_count++] = lights[i];
3063
3064 lump->filelen = new_count*sizeof(struct worldlight);
3065
3066 /* Write changes back to file */
3067 fseek( fp, lump->fileofs, SEEK_SET );
3068 fwrite( lights, lump->filelen, 1, fp );
3069 fseek( fp, 0, SEEK_SET );
3070 fwrite( &header, sizeof(struct header), 1, fp );
3071 cxr_log( "removed %d marked lights\n", light_count-new_count );
3072
3073 fclose( fp );
3074 free( lights );
3075
3076 return 1;
3077 }