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