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