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