4 A GNU/Linux-first Source1 Hammer replacement
5 built with Blender, for mapmakers
7 Copyright (C) 2022 Harry Godden (hgn)
10 - Brush decomposition into convex pieces for well defined geometry
11 - Freely form displacements without limits
12 - Build your entire map in Blender
13 - Compile models and model groups easily
14 - Light patch BSP files; remove unwanted realtime effects
15 - Fastest VTF compressor (thanks to Richgel999 and stb)
18 - high quality level overviews automatically for CS:GO (csRadar)
22 File/folder Lang Purpose
24 __init__.py Python Blender plugin interface
25 convexer.c 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
29 nbvtf.h C VTF processing interface
30 librgcx.h C++ Rich Geldreich's DXT1/DXT5 compressors
31 stb/ C Sean Barrets image I/O
34 const char *cxr_build_time
= __DATE__
" @" __TIME__
;
52 typedef unsigned int uint
;
54 typedef double v2f
[2];
55 typedef double v3f
[3];
56 typedef double v4f
[4];
61 #define CXR_EPSILON 0.001
62 #define CXR_PLANE_SIMILARITY_MAX 0.998
63 #define CXR_BIG_NUMBER 1e300
64 #define CXR_INTERIOR_ANGLE_MAX 0.998
66 #define CXR_DIRTY_OPTIMISATION 1
67 #define CXR_DEBUG_WRITE_MESH 1
68 #define CXR_DEBUG_ALLOCATORS 1
69 #define CXR_MANIFOLD_DEBUG 0
71 #define CXR_ERROR_DEGEN_IMPLICIT 0x1
72 #define CXR_ERROR_BAD_MANIFOLD 0x2
73 #define CXR_ERROR_NO_SOLIDS 0x4
79 // ============================================
81 static v4f colours_random
[] =
83 { 0.863, 0.078, 0.235, 0.4 },
84 { 0.000, 0.980, 0.604, 0.4 },
85 { 0.118, 0.565, 1.000, 0.4 },
86 { 0.855, 0.439, 0.839, 0.4 },
87 { 0.824, 0.412, 0.118, 0.4 },
88 { 0.125, 0.698, 0.667, 0.4 },
89 { 0.541, 0.169, 0.886, 0.4 },
90 { 1.000, 0.843, 0.000, 0.4 }
93 static int cxr_range(int x
, int bound
)
96 x
+= bound
* (x
/bound
+ 1);
102 typedef struct cxr_edge cxr_edge
;
103 typedef struct cxr_input_mesh cxr_input_mesh
;
104 typedef struct cxr_input_loop cxr_input_loop
;
105 typedef struct cxr_polygon cxr_polygon
;
106 typedef struct cxr_material cxr_material
;
107 typedef struct cxr_loop cxr_loop
;
108 typedef struct cxr_solid cxr_solid
;
109 typedef struct cxr_mesh cxr_mesh
;
110 typedef struct cxr_texinfo cxr_texinfo
;
111 typedef struct cxr_vdf cxr_vdf
;
113 struct cxr_input_mesh
124 struct cxr_input_loop
134 i32 loop_start
, loop_total
;
137 i32 material_id
; // -1: interior material
144 const char *vmt_path
;
171 struct cxr_auto_buffer
176 *p_abverts
; /* This data is stored externally because the data is often
177 shared between solids. */
179 /* Valid when update() is called on this mesh,
180 * Invalid when data is appended to them */
181 struct cxr_edge
*edges
;
182 struct cxr_polygon
*polys
;
183 struct cxr_loop
*loops
;
193 // simple VDF writing interface
200 static struct cxr_settings
209 .lightmap_scale
= 12,
211 .light_scale
= 1.0/5.0
214 static struct cxr_context
227 .scale_factor
= 32.0,
231 static struct cxr_material cxr_nodraw
= {
233 .vmt_path
= "tools/toolsnodraw"
237 * This should be called after appending any data to those buffers
239 static void cxr_mesh_update( cxr_mesh
*mesh
)
241 mesh
->edges
= cxr_ab_ptr(&mesh
->abedges
, 0);
242 mesh
->polys
= cxr_ab_ptr(&mesh
->abpolys
, 0);
243 mesh
->loops
= cxr_ab_ptr(&mesh
->abloops
, 0);
246 // Debugging callbacks
247 // =============================================================================
249 static v4f colour_error
= { 1.0f
, 0.0f
, 0.0f
, 1.0f
};
250 static v4f colour_face_graph
= { 1.0f
, 1.0f
, 1.0f
, 0.03f
};
251 static v4f colour_success
= { 0.0f
, 1.0f
, 0.0f
, 1.0f
};
253 static void (*cxr_log_func
)(const char *str
);
254 static void (*cxr_line_func
)( v3f p0
, v3f p1
, v4f colour
);
256 static void cxr_log( const char *fmt
, ... )
261 va_start( args
, fmt
);
262 vsnprintf( buf
, sizeof(buf
)-1, fmt
, args
);
271 static void cxr_debug_line( v3f p0
, v3f p1
, v4f colour
)
274 cxr_line_func( p0
, p1
, colour
);
277 static void cxr_debug_box( v3f p0
, double sz
, v4f colour
)
281 v3_add(p0
, (v3f
){-sz
,-sz
,-sz
}, a
);
282 v3_add(p0
, (v3f
){-sz
, sz
,-sz
}, b
);
283 v3_add(p0
, (v3f
){ sz
, sz
,-sz
}, c
);
284 v3_add(p0
, (v3f
){ sz
,-sz
,-sz
}, d
);
285 v3_add(p0
, (v3f
){-sz
,-sz
,sz
}, a1
);
286 v3_add(p0
, (v3f
){-sz
, sz
,sz
}, b1
);
287 v3_add(p0
, (v3f
){ sz
, sz
,sz
}, c1
);
288 v3_add(p0
, (v3f
){ sz
,-sz
,sz
}, d1
);
290 cxr_debug_line( a
,b
, colour
);
291 cxr_debug_line( b
,c
, colour
);
292 cxr_debug_line( c
,d
, colour
);
293 cxr_debug_line( d
,a
, colour
);
294 cxr_debug_line( a1
,b1
, colour
);
295 cxr_debug_line( b1
,c1
, colour
);
296 cxr_debug_line( c1
,d1
, colour
);
297 cxr_debug_line( d1
,a1
, colour
);
298 cxr_debug_line( a
,a1
, colour
);
299 cxr_debug_line( b
,b1
, colour
);
300 cxr_debug_line( c
,c1
, colour
);
301 cxr_debug_line( d
,d1
, colour
);
304 static void cxr_debug_arrow( v3f p0
, v3f p1
, v3f normal
, double sz
, v4f colour
)
306 v3f dir
, tan
, p2
, p3
;
310 v3_cross(dir
,normal
,tan
);
311 v3_muladds( p1
,dir
, -sz
, p2
);
312 v3_muladds( p2
,tan
,sz
,p3
);
313 cxr_debug_line( p1
, p3
, colour
);
314 v3_muladds( p2
,tan
,-sz
,p3
);
315 cxr_debug_line( p1
, p3
, colour
);
316 cxr_debug_line( p0
, p1
, colour
);
320 // =========================================================================
322 CXR_API
void cxr_context_reset(void)
324 cxr_context
.brush_count
= 0;
325 cxr_context
.entity_count
= 0;
326 cxr_context
.face_count
= 0;
327 cxr_context
.offset_z
= 0.0;
328 cxr_context
.scale_factor
= 32.0;
331 CXR_API
void cxr_set_offset(double offset
)
333 cxr_context
.offset_z
= offset
;
336 CXR_API
void cxr_set_scale_factor(double scale
)
338 cxr_context
.scale_factor
= scale
;
341 CXR_API cxr_vdf
*cxr_vdf_open(const char *path
)
343 cxr_vdf
*vdf
= malloc(sizeof(cxr_vdf
));
346 vdf
->fp
= fopen( path
, "w" );
357 CXR_API
void cxr_vdf_close(cxr_vdf
*vdf
)
363 CXR_API
void cxr_vdf_put(cxr_vdf
*vdf
, const char *str
)
365 for( int i
=0; i
<vdf
->level
; i
++ )
366 fputs( " ", vdf
->fp
);
368 fputs( str
, vdf
->fp
);
371 static void cxr_vdf_printf( cxr_vdf
*vdf
, const char *fmt
, ... )
376 va_start( args
, fmt
);
377 vfprintf( vdf
->fp
, fmt
, args
);
381 CXR_API
void cxr_vdf_node(cxr_vdf
*vdf
, const char *str
)
383 cxr_vdf_put( vdf
, str
);
384 putc( (u8
)'\n', vdf
->fp
);
385 cxr_vdf_put( vdf
, "{\n" );
390 CXR_API
void cxr_vdf_edon(cxr_vdf
*vdf
)
393 cxr_vdf_put( vdf
, "}\n" );
396 CXR_API
void cxr_vdf_kv(cxr_vdf
*vdf
, const char *strk
, const char *strv
)
398 cxr_vdf_printf( vdf
, "\"%s\" \"%s\"\n", strk
, strv
);
401 static void cxr_vdf_ki32(cxr_vdf
*vdf
, const char *strk
, i32 val
)
403 cxr_vdf_printf( vdf
, "\"%s\" \"%d\"\n", strk
, val
);
405 static void cxr_vdf_kdouble(cxr_vdf
*vdf
, const char *strk
, double val
)
407 cxr_vdf_printf( vdf
, "\"%s\" \"%f\"\n", strk
, val
);
409 static void cxr_vdf_kaxis(cxr_vdf
*vdf
, const char *strk
, v3f normal
, double offset
, double scale
)
411 cxr_vdf_printf( vdf
, "\"%s\" \"[%f %f %f %f] %f\"\n", strk
, normal
[0],normal
[1],normal
[2],offset
,scale
);
413 static void cxr_vdf_kv3f(cxr_vdf
*vdf
, const char *strk
, v3f v
)
415 cxr_vdf_printf( vdf
, "\"%s\" \"[%f %f %f]\"\n", strk
, v
[0], v
[1], v
[2] );
417 static void cxr_vdf_karrdouble(cxr_vdf
*vdf
, const char *strk
, int id
, double *doubles
, int count
)
420 fprintf( vdf
->fp
, "\"%s%d\" \"", strk
, id
);
421 for( int i
=0; i
<count
; i
++ )
423 if( i
== count
-1 ) fprintf( vdf
->fp
, "%f", doubles
[i
] );
424 else fprintf( vdf
->fp
, "%f ", doubles
[i
] );
426 fprintf( vdf
->fp
, "\"\n" );
428 static void cxr_vdf_karrv3f(cxr_vdf
*vdf
, const char *strk
, int id
, v3f
*vecs
, int count
)
431 fprintf( vdf
->fp
, "\"%s%d\" \"", strk
, id
);
432 for( int i
=0; i
<count
; i
++ )
434 if( i
== count
-1 ) fprintf( vdf
->fp
, "%f %f %f", vecs
[i
][0], vecs
[i
][1], vecs
[i
][2] );
435 else fprintf( vdf
->fp
, "%f %f %f ", vecs
[i
][0], vecs
[i
][1], vecs
[i
][2] );
437 fprintf( vdf
->fp
, "\"\n" );
439 static void cxr_vdf_plane(cxr_vdf
*vdf
, const char *strk
, v3f a
, v3f b
, v3f c
)
441 cxr_vdf_printf( vdf
, "\"%s\" \"(%f %f %f) (%f %f %f) (%f %f %f)\"\n",
442 strk
, a
[0], a
[1], a
[2], b
[0], b
[1], b
[2], c
[0], c
[1], c
[2] );
444 static void cxr_vdf_colour255(cxr_vdf
*vdf
, const char *strk
, v4f colour
)
447 v4_muls( colour
, 255.0, scale
);
448 cxr_vdf_printf( vdf
, "\"%s\" \"%d %d %d %d\"\n",strk
,(int)scale
[0], (int)scale
[1], (int)scale
[2], (int)scale
[3]);
452 // =========================================================================
454 static void cxr_debug_poly(cxr_mesh
*mesh
,
458 v3f
*verts
= cxr_ab_ptr( mesh
->p_abverts
, 0 );
460 for( int i
=0; i
<poly
->loop_total
; i
++ )
462 int lp0
= poly
->loop_start
+i
,
463 lp1
= poly
->loop_start
+cxr_range(i
+1,poly
->loop_total
);
465 int i0
= mesh
->loops
[ lp0
].index
,
466 i1
= mesh
->loops
[ lp1
].index
;
470 v3_lerp( verts
[i0
], poly
->center
, 0.02, p0
);
471 v3_lerp( verts
[i1
], poly
->center
, 0.02, p1
);
473 cxr_debug_arrow( p0
, p1
, poly
->normal
, 0.05, colour
);
477 v3_muladds( poly
->center
, poly
->normal
, 0.3, nrm0
);
479 cxr_debug_line( poly
->center
, nrm0
, colour
);
482 static void cxr_debug_mesh(cxr_mesh
*mesh
, v4f colour
)
484 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
486 cxr_polygon
*poly
= &mesh
->polys
[i
];
487 cxr_debug_poly( mesh
, poly
, colour
);
491 static cxr_mesh
*cxr_alloc_mesh(int edge_count
,
494 cxr_auto_buffer
*abverts
)
496 cxr_mesh
*mesh
= malloc(sizeof(cxr_mesh
));
497 cxr_ab_init(&mesh
->abedges
, sizeof(cxr_edge
), edge_count
);
498 cxr_ab_init(&mesh
->abloops
, sizeof(cxr_loop
), loop_count
);
499 cxr_ab_init(&mesh
->abpolys
, sizeof(cxr_polygon
), poly_count
);
500 mesh
->p_abverts
= abverts
;
502 cxr_mesh_update( mesh
);
507 static void cxr_free_mesh(cxr_mesh
*mesh
)
509 cxr_ab_free(&mesh
->abedges
);
510 cxr_ab_free(&mesh
->abloops
);
511 cxr_ab_free(&mesh
->abpolys
);
516 * Rebuilds edge data for mesh (useful to get rid of orphaned edges)
518 static void cxr_mesh_clean_edges(cxr_mesh
*mesh
)
520 cxr_auto_buffer new_edges
;
521 cxr_ab_init( &new_edges
, sizeof(cxr_edge
), mesh
->abedges
.count
);
523 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
525 cxr_polygon
*poly
= &mesh
->polys
[i
];
526 for( int j
=0; j
<poly
->loop_total
; j
++ )
529 *lp0
= &mesh
->loops
[poly
->loop_start
+j
],
530 *lp1
= &mesh
->loops
[poly
->loop_start
+cxr_range(j
+1,poly
->loop_total
)];
532 int i0
= cxr_min(lp0
->index
, lp1
->index
),
533 i1
= cxr_max(lp0
->index
, lp1
->index
);
535 // See if edge exists before adding it
536 for( int k
=0; k
<new_edges
.count
; k
++ )
538 cxr_edge
*edge
= cxr_ab_ptr(&new_edges
,k
);
540 if( edge
->i0
== i0
&& edge
->i1
== i1
)
543 goto IL_EDGE_CREATED
;
547 int orig_edge_id
= lp0
->edge_index
;
548 lp0
->edge_index
= new_edges
.count
;
550 cxr_edge edge
= { i0
, i1
};
552 // Copy extra information (sharp,freestyle.. etc) here!
554 if( orig_edge_id
< mesh
->abedges
.count
)
556 cxr_edge
*orig_edge
= &mesh
->edges
[ orig_edge_id
];
557 edge
.freestyle
= orig_edge
->freestyle
;
565 cxr_ab_push( &new_edges
, &edge
);
571 cxr_ab_free( &mesh
->abedges
);
572 mesh
->abedges
= new_edges
;
574 cxr_mesh_update( mesh
);
578 * Remove 0-length faces from mesh (we mark them light that for deletion
579 * Remove all unused loops as a result of removing those faces
581 static void cxr_mesh_clean_faces(cxr_mesh
*mesh
)
583 cxr_auto_buffer loops_new
;
584 cxr_ab_init( &loops_new
, sizeof(cxr_loop
), mesh
->abloops
.count
);
587 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
589 cxr_polygon
*src
= &mesh
->polys
[i
],
590 *dst
= &mesh
->polys
[new_length
];
592 if( src
->loop_total
> 0 )
594 int src_start
= src
->loop_start
,
595 src_total
= src
->loop_total
;
598 dst
->loop_start
= loops_new
.count
;
600 for( int j
=0; j
<src_total
; j
++ )
602 cxr_loop
*loop
= &mesh
->loops
[src_start
+j
],
603 *ldst
= cxr_ab_ptr(&loops_new
,dst
->loop_start
+j
);
605 ldst
->poly_left
= new_length
;
608 loops_new
.count
+= src_total
;
613 cxr_ab_free( &mesh
->abloops
);
614 mesh
->abloops
= loops_new
;
615 mesh
->abpolys
.count
= new_length
;
617 cxr_mesh_update( mesh
);
621 * Links loop's poly_left and poly_right
622 * Does not support more than 2 polys to one edge
624 * Returns 0 if there is non-manifold geomtry (aka: not watertight)
626 static int cxr_mesh_link_loops(cxr_mesh
*mesh
)
628 i32
*polygon_edge_map
= malloc(mesh
->abedges
.count
*2 *sizeof(i32
));
630 for( int i
= 0; i
< mesh
->abedges
.count
*2; i
++ )
631 polygon_edge_map
[i
] = -1;
633 for( int i
= 0; i
< mesh
->abpolys
.count
; i
++ )
635 cxr_polygon
*poly
= &mesh
->polys
[i
];
637 for( int j
= 0; j
< poly
->loop_total
; j
++ )
639 cxr_loop
*loop
= &mesh
->loops
[ poly
->loop_start
+j
];
642 for( int k
= 0; k
< 2; k
++ )
644 i32
*edge
= &polygon_edge_map
[loop
->edge_index
*2+k
];
654 for( int i
= 0; i
< mesh
->abpolys
.count
; i
++ )
656 cxr_polygon
*poly
= &mesh
->polys
[i
];
658 for( int j
= 0; j
< poly
->loop_total
; j
++ )
660 cxr_loop
*loop
= &mesh
->loops
[ poly
->loop_start
+j
];
662 i32
*face_map
= &polygon_edge_map
[ loop
->edge_index
*2 ];
664 if( face_map
[0] == loop
->poly_left
) loop
->poly_right
= face_map
[1];
665 else loop
->poly_right
= face_map
[0];
670 for( int i
=0; i
<mesh
->abedges
.count
*2; i
++ )
672 if( polygon_edge_map
[i
] == -1 )
674 free( polygon_edge_map
);
679 free( polygon_edge_map
);
684 * Add polygon to mesh based on array of loops
686 static cxr_polygon
*cxr_create_poly(
693 v3f
*verts
= cxr_ab_ptr( mesh
->p_abverts
, 0 );
697 cxr_log( "tried to add new poly with length %d!\n", len
);
701 for( int j
=0; j
<len
; j
++ )
703 int i0
= poly
[j
].index
,
704 i1
= poly
[cxr_range(j
+1,len
)].index
;
706 cxr_debug_line( verts
[i0
], verts
[i1
], colour_error
);
713 int nface_id
= mesh
->abpolys
.count
;
714 cxr_polygon
*new_face
= cxr_ab_empty(&mesh
->abpolys
);
716 /* Calculate new face information; normal, center etc */
718 new_face
->loop_start
= mesh
->abloops
.count
;
719 new_face
->loop_total
= len
;
720 new_face
->material_id
= -1;
721 v3_zero( new_face
->center
);
723 for( int j
=0; j
<len
; j
++ )
725 int i0
= poly
[cxr_range(start
+j
,max
)].index
;
726 cxr_loop
*new_loop
= cxr_ab_empty(&mesh
->abloops
);
728 new_loop
->poly_left
= nface_id
;
729 new_loop
->poly_right
= -1;
730 new_loop
->index
= i0
;
731 new_loop
->edge_index
= 0;
732 v2_zero(new_loop
->uv
);
734 v3_add( new_face
->center
, verts
[new_loop
->index
], new_face
->center
);
736 v3_divs( new_face
->center
, new_face
->loop_total
, new_face
->center
);
738 cxr_loop
*lp0
= cxr_ab_ptr( &mesh
->abloops
, new_face
->loop_start
),
739 *lp1
= cxr_ab_ptr( &mesh
->abloops
, new_face
->loop_start
+1 ),
740 *lp2
= cxr_ab_ptr( &mesh
->abloops
, new_face
->loop_start
+2 );
743 verts
[lp0
->index
], verts
[lp1
->index
], verts
[lp2
->index
], new_face
->normal
);
745 cxr_mesh_update( mesh
);
746 return &mesh
->polys
[ nface_id
];
750 * Extract the next island from mesh
752 * Returns NULL if mesh is one contigous object
754 static cxr_mesh
*cxr_pull_island( cxr_mesh
*mesh
)
756 cxr_mesh_link_loops(mesh
);
758 int *island_current
= malloc(mesh
->abpolys
.count
*sizeof(int)),
763 island_current
[0] = 0;
766 last_count
= island_len
;
768 for( int i
=0; i
<island_len
; i
++ )
770 cxr_polygon
*poly
= &mesh
->polys
[ island_current
[i
] ];
772 for( int j
=0; j
<poly
->loop_total
; j
++ )
774 cxr_loop
*loop
= &mesh
->loops
[ poly
->loop_start
+j
];
776 if( loop
->poly_right
!= -1 )
778 int face_present
= 0;
780 for( int k
=0; k
<island_len
; k
++ )
782 if( island_current
[k
] == loop
->poly_right
)
790 island_current
[ island_len
++ ] = loop
->poly_right
;
795 if( island_len
> last_count
)
798 /* Check for complete object */
799 if( island_len
== mesh
->abpolys
.count
)
801 free( island_current
);
805 for( int i
=0; i
<island_len
; i
++ )
807 cxr_polygon
*poly
= &mesh
->polys
[ island_current
[i
] ];
808 loop_count
+= poly
->loop_total
;
811 /* Create and update meshes */
812 cxr_mesh
*newmesh
= cxr_alloc_mesh( mesh
->abedges
.count
,
817 for( int i
=0; i
<island_len
; i
++ )
819 cxr_polygon
*src
= &mesh
->polys
[ island_current
[i
] ];
820 cxr_polygon
*dst
= cxr_ab_ptr(&newmesh
->abpolys
, i
);
823 dst
->loop_start
= newmesh
->abloops
.count
;
825 for( int j
=0; j
<src
->loop_total
; j
++ )
828 *lsrc
= &mesh
->loops
[ src
->loop_start
+j
],
829 *ldst
= cxr_ab_ptr(&newmesh
->abloops
, dst
->loop_start
+j
);
833 ldst
->poly_right
= -1;
836 newmesh
->abloops
.count
+= src
->loop_total
;
837 src
->loop_total
= -1;
840 newmesh
->abpolys
.count
= island_len
;
841 newmesh
->abedges
.count
= mesh
->abedges
.count
;
842 memcpy( cxr_ab_ptr(&newmesh
->abedges
,0),
844 mesh
->abedges
.count
* sizeof(cxr_edge
));
846 cxr_mesh_clean_faces(mesh
);
847 cxr_mesh_clean_edges(mesh
);
848 cxr_mesh_clean_edges(newmesh
);
850 free( island_current
);
855 * Invalid solid is when there are vertices that are coplanar to a face, but are
856 * outside the polygons edges.
858 static int cxr_valid_solid( cxr_mesh
*mesh
, int *solid
, int len
)
860 v3f
*verts
= cxr_ab_ptr(mesh
->p_abverts
, 0);
862 for( int i
=0; i
<len
; i
++ )
864 cxr_polygon
*polyi
= &mesh
->polys
[ solid
[i
] ];
867 normal_to_plane(polyi
->normal
, polyi
->center
, plane
);
869 for( int j
=0; j
<len
; j
++ )
873 cxr_polygon
*polyj
= &mesh
->polys
[ solid
[j
] ];
875 for( int k
=0; k
<polyj
->loop_total
; k
++ )
877 cxr_loop
*lpj
= &mesh
->loops
[ polyj
->loop_start
+k
];
879 /* Test if the vertex is not referenced by the polygon */
880 for( int l
=0; l
<polyi
->loop_total
; l
++ )
882 cxr_loop
*lpi
= &mesh
->loops
[ polyi
->loop_start
+l
];
884 if( lpi
->index
== lpj
->index
)
888 if( fabs(plane_polarity(plane
, verts
[lpj
->index
])) < 0.001 )
900 * Use when iterating the loops array, to get a unique set of edges
901 * Better than using the edges array and doing many more checks
903 static int cxr_loop_unique_edge( cxr_loop
*lp
)
905 if( lp
->poly_left
> lp
->poly_right
)
912 * Identify edges in the mesh where the two connected face's normals
913 * are opposing eachother (or close to identical)
915 static int *cxr_mesh_reflex_edges( cxr_mesh
*mesh
)
917 v3f
*verts
= cxr_ab_ptr( mesh
->p_abverts
, 0 );
918 int *edge_tagged
= malloc( mesh
->abedges
.count
* sizeof(int) );
920 for( int i
=0; i
<mesh
->abloops
.count
; i
++ )
922 cxr_loop
*lp
= &mesh
->loops
[i
];
923 if( !cxr_loop_unique_edge( lp
) ) continue;
925 edge_tagged
[lp
->edge_index
] = 0;
927 cxr_polygon
*polya
= &mesh
->polys
[ lp
->poly_left
],
928 *polyb
= &mesh
->polys
[ lp
->poly_right
];
931 normal_to_plane(polyb
->normal
, polyb
->center
, planeb
);
933 for( int j
=0; j
<polya
->loop_total
; j
++ )
935 cxr_loop
*lp1
= &mesh
->loops
[ polya
->loop_start
+j
];
937 if(( plane_polarity( planeb
, verts
[lp1
->index
] ) > 0.001 ) ||
938 ( v3_dot(polya
->normal
,polyb
->normal
) > CXR_PLANE_SIMILARITY_MAX
))
940 edge_tagged
[lp
->edge_index
] = 1;
950 * Same logic as above function except it will apply it to each vertex
952 static int *cxr_mesh_reflex_vertices( cxr_mesh
*mesh
)
954 v3f
*verts
= cxr_ab_ptr( mesh
->p_abverts
, 0 );
956 int *vertex_tagged
= malloc( mesh
->p_abverts
->count
*sizeof(int) );
957 int *connected_planes
= malloc( mesh
->abpolys
.count
*sizeof(int) );
959 for( int i
=0; i
<mesh
->p_abverts
->count
; i
++ )
962 int num_connected
= 0;
964 /* Create a list of polygons that refer to this vertex */
965 for( int j
=0; j
<mesh
->abpolys
.count
; j
++ )
967 cxr_polygon
*poly
= &mesh
->polys
[j
];
968 for( int k
=0; k
<poly
->loop_total
; k
++ )
970 cxr_loop
*loop
= &mesh
->loops
[poly
->loop_start
+k
];
971 if( loop
->index
== i
)
973 connected_planes
[num_connected
++] = j
;
979 /* Check all combinations for a similar normal */
980 for( int j
=0; j
<num_connected
-1; j
++ )
982 for( int k
=j
+1; k
<num_connected
; k
++ )
984 cxr_polygon
*polyj
= &mesh
->polys
[connected_planes
[j
]],
985 *polyk
= &mesh
->polys
[connected_planes
[k
]];
987 if( v3_dot(polyj
->normal
,polyk
->normal
) > CXR_PLANE_SIMILARITY_MAX
)
993 * Check if all connected planes either:
997 for( int j
=0; j
<num_connected
; j
++ )
999 for( int k
=j
+1; k
<num_connected
; k
++ )
1001 cxr_polygon
*jpoly
= &mesh
->polys
[ connected_planes
[j
] ],
1002 *kpoly
= &mesh
->polys
[ connected_planes
[k
] ];
1005 normal_to_plane( kpoly
->normal
, kpoly
->center
, plane
);
1006 for( int l
=0; l
<jpoly
->loop_total
; l
++ )
1008 cxr_loop
*lp
= &mesh
->loops
[ jpoly
->loop_start
+l
];
1010 if( plane_polarity( plane
, verts
[lp
->index
] ) > 0.001 )
1018 vertex_tagged
[i
] = 1;
1021 free( connected_planes
);
1022 return vertex_tagged
;
1026 * Detect if potential future edges create a collision with any of the
1027 * existing edges in the mesh
1029 static int cxr_solid_overlap( cxr_mesh
*mesh
,
1032 int common_edge_index
,
1035 v3f
*verts
= cxr_ab_ptr( mesh
->p_abverts
, 0 );
1036 cxr_edge
*common_edge
= &mesh
->edges
[common_edge_index
];
1038 int unique_a
= pa
->loop_total
-2,
1039 unique_b
= pb
->loop_total
-2;
1041 int *unique_verts
= malloc( (unique_a
+unique_b
)*sizeof(int) );
1042 int unique_total
= 0;
1044 for( int j
=0; j
<2; j
++ )
1046 cxr_polygon
*poly
= (cxr_polygon
*[2]){pa
,pb
}[j
];
1048 for( int i
=0; i
<poly
->loop_total
; i
++ )
1050 cxr_loop
*lp
= &mesh
->loops
[poly
->loop_start
+i
];
1052 if( lp
->index
== common_edge
->i0
|| lp
->index
== common_edge
->i1
)
1055 unique_verts
[ unique_total
++ ] = lp
->index
;
1061 for( int i
=0; i
<unique_a
; i
++ )
1063 for( int j
=unique_a
; j
<unique_total
; j
++ )
1065 int i0
= unique_verts
[i
],
1066 i1
= unique_verts
[j
];
1069 cxr_debug_line( verts
[i0
], verts
[i1
], colours_random
[2] );
1071 for( int k
=0; k
<mesh
->abedges
.count
; k
++ )
1073 cxr_edge
*edge
= &mesh
->edges
[k
];
1075 if( edge
->i0
== i0
|| edge
->i0
== i1
||
1076 edge
->i1
== i0
|| edge
->i1
== i1
) continue;
1078 double *a0
= verts
[i0
],
1080 *b0
= verts
[edge
->i0
],
1081 *b1
= verts
[edge
->i1
];
1083 double dist
= segment_segment_dist( a0
, a1
, b0
, b1
, ca
, cb
);
1089 cxr_debug_box( ca
, 0.025, colour_error
);
1090 cxr_debug_box( cb
, 0.025, colour_error
);
1091 cxr_debug_line( a0
, a1
, colour_error
);
1092 cxr_debug_line( b0
, b1
, colour_error
);
1098 free( unique_verts
);
1108 cxr_debug_line( verts
[mesh
->edges
[common_edge_index
].i0
],
1109 verts
[mesh
->edges
[common_edge_index
].i1
],
1113 free( unique_verts
);
1118 * Creates the 'maximal' solid that originates from this faceid
1120 * Returns the number of faces used
1122 static int cxr_buildsolid(
1129 faces_tagged
[faceid
] = faceid
;
1132 solid
[solid_len
++] = faceid
;
1134 int search_start
= 0;
1139 for( int j
=search_start
; j
<solid_len
; j
++ )
1141 cxr_polygon
*poly
= &mesh
->polys
[ solid
[j
] ];
1143 for( int k
=0; k
<poly
->loop_total
; k
++ )
1145 cxr_loop
*loop
= &mesh
->loops
[ poly
->loop_start
+k
];
1146 cxr_edge
*edge
= &mesh
->edges
[ loop
->edge_index
];
1148 if( faces_tagged
[ loop
->poly_right
] == -1 )
1150 if( !reflex_edges
[loop
->edge_index
] )
1152 /* Check for dodgy edges */
1153 cxr_polygon
*newpoly
= &mesh
->polys
[loop
->poly_right
];
1155 if( cxr_solid_overlap(mesh
,poly
,newpoly
,loop
->edge_index
,0))
1158 /* Looking ahead by one step gives us an early out for invalid
1159 * configurations. This might just all be handled by the new
1160 * edge overlap detector, though.
1162 for( int l
=0; l
< newpoly
->loop_total
; l
++ )
1164 cxr_loop
*lp1
= &mesh
->loops
[ newpoly
->loop_start
+l
];
1165 cxr_polygon
*future_face
= &mesh
->polys
[ lp1
->poly_right
];
1167 if( reflex_edges
[ lp1
->edge_index
] || lp1
->poly_right
== loop
->poly_right
)
1170 for( int m
=0; m
<solid_len
; m
++ )
1171 if( solid
[m
] == lp1
->poly_right
)
1174 for( int m
=0; m
<solid_len
; m
++ )
1176 cxr_polygon
*polym
= &mesh
->polys
[solid
[m
]];
1177 if( v3_dot( polym
->normal
,future_face
->normal
) > CXR_PLANE_SIMILARITY_MAX
)
1184 // Check for vertices in the new poly that exist on a current plane.
1185 // this condition is an invalid configuration and should not be added.
1187 solid
[ solid_len
] = loop
->poly_right
;
1190 if( cxr_valid_solid(mesh
,solid
,solid_len
+1 ) )
1192 faces_tagged
[ loop
->poly_right
] = faceid
;
1202 search_start
= solid_len
;
1204 goto search_iterate
;
1208 // The current method can create some invalid configurations
1209 // filter those out that dont work and un-tag the faces
1213 * I think we have good enough filters now to ignore these,
1214 * probably should make them show as warnings
1218 if( !cxr_valid_solid( mesh
, solid
, solid_len
) )
1220 for( int l
=0; l
<solid_len
; l
++ )
1221 faces_tagged
[ solid
[l
] ] = -1;
1223 goto IL_CANCEL_SOLID
;
1225 for( int j
=0; j
<solid_len
-1; j
++ )
1227 for( int k
=j
+1; k
<solid_len
; k
++ )
1229 cxr_polygon
*polyj
= cxr_ab_ptr(&mesh
->polys
, solid
[j
]),
1230 *polyk
= cxr_ab_ptr(&mesh
->polys
, solid
[k
]);
1232 if( v3_dot( polyj
->normal
, polyk
->normal
) > CXR_PLANE_SIMILARITY_MAX
)
1234 for( int l
=0; l
<solid_len
; l
++ )
1235 faces_tagged
[ solid
[l
] ] = -1;
1237 goto IL_CANCEL_SOLID
;
1248 * Return the best availible solid from mesh, and patch the existing mesh to
1249 * fill the gap where the new mesh left it.
1251 * Returns NULL if shape is already convex or empty.
1252 * This function will not preserve edge data such as freestyle, sharp etc.
1254 static cxr_mesh
*cxr_pull_best_solid(
1256 int preserve_more_edges
,
1259 if( !cxr_mesh_link_loops(mesh
) )
1261 cxr_log( "non-manifold edges are in the mesh: "
1262 "implicit internal geometry does not have full support\n" );
1267 v3f
*verts
= cxr_ab_ptr( mesh
->p_abverts
, 0 );
1268 int *edge_tagged
= cxr_mesh_reflex_edges( mesh
);
1269 int *vertex_tagged
= cxr_mesh_reflex_vertices( mesh
);
1272 * Connect all marked vertices that share an edge
1275 int *edge_important
= malloc(mesh
->abedges
.count
*sizeof(int));
1276 for( int i
=0; i
< mesh
->abedges
.count
; i
++ )
1277 edge_important
[i
] = 0;
1279 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
1281 cxr_polygon
*poly
= &mesh
->polys
[i
];
1282 int not_tagged
= -1,
1285 for( int j
=0; j
<poly
->loop_total
; j
++ )
1287 cxr_loop
*loop
= &mesh
->loops
[ poly
->loop_start
+j
];
1289 if( !edge_tagged
[ loop
->edge_index
] )
1291 if( not_tagged
== -1 )
1292 not_tagged
= loop
->edge_index
;
1294 goto edge_unimportant
;
1298 if( not_tagged
!= -1 )
1299 edge_important
[not_tagged
]=1;
1304 // Connect edges that have verts tagged, but is not a hot edge
1305 for( int i
=0; i
<mesh
->abedges
.count
; i
++ )
1307 if( edge_important
[i
] && preserve_more_edges
) continue;
1309 cxr_edge
*edge
= &mesh
->edges
[i
];
1310 if( vertex_tagged
[edge
->i0
] && vertex_tagged
[edge
->i1
] )
1314 free( edge_important
);
1317 for( int i
=0; i
<vert_buffer
->count
; i
++ )
1318 if( vertex_tagged
[i
] )
1319 cxr_debug_box( vertices
[i
], 0.03, (v4f
){0.0,0.0,0.0,1.0});
1321 for( int i
=0; i
< mesh
->edges
.count
; i
++ )
1323 cxr_edge
*edge
= cxr_ab_ptr( &mesh
->edges
, i
);
1324 if( edge_tagged
[i
] )
1325 cxr_debug_line( vertices
[ edge
->i0
], vertices
[ edge
->i1
], (v4f
){0.0,0.0,0.0,1.0});
1328 cxr_debug_line( vertices
[ edge
->i0
], vertices
[ edge
->i1
], (v4f
){0.0,1.0,1.0,1.0});
1333 int *faces_tagged
= malloc(mesh
->abpolys
.count
*sizeof(int));
1334 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
1335 faces_tagged
[i
] = -1;
1337 int *solid_buffer
= malloc( mesh
->abpolys
.count
*sizeof(int) );
1338 int solid_buffer_len
= 0;
1341 int start
,count
,edge_count
;
1344 *candidates
= malloc( mesh
->abpolys
.count
*sizeof(struct csolid
) );
1345 int candidate_count
= 0;
1351 *edge_references
= malloc( mesh
->abedges
.count
*sizeof(struct tedge
) );
1353 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
1355 if( faces_tagged
[i
] != -1 ) continue;
1356 faces_tagged
[i
] = i
;
1358 int *solid
= &solid_buffer
[ solid_buffer_len
];
1361 solid
[solid_len
++] = i
;
1363 int search_start
= 0;
1365 // Iterative search that connects regions of planes governed by rules:
1366 // - edge can add other face if the edge has less than two reflexes
1367 // - the face can no longer be used in future searches
1369 IL_SEARCH_CONTINUE
:;
1372 for( int j
=search_start
; j
<solid_len
; j
++ )
1374 cxr_polygon
*poly
= &mesh
->polys
[solid
[j
]];
1376 for( int k
=0; k
<poly
->loop_total
; k
++ )
1378 cxr_loop
*loop
= &mesh
->loops
[poly
->loop_start
+k
];
1379 cxr_edge
*edge
= &mesh
->edges
[loop
->edge_index
];
1381 if( faces_tagged
[ loop
->poly_right
] == -1 )
1383 if( !edge_tagged
[loop
->edge_index
] )
1387 // Need to look ahead 1 step to make sure he does not want
1388 // to add any more planes that are coplanar with some of
1389 // our existing group
1391 // This can sort out SOME invalid configs, but not all.
1392 // It would be nice to find a more robust clustering algorithm for this.
1395 cxr_polygon
*poly_to_add
= &mesh
->polys
[loop
->poly_right
];
1397 if( cxr_solid_overlap(mesh
, poly
, poly_to_add
, loop
->edge_index
, 0 ))
1398 goto IL_SKIP_PLANE_ADD
;
1400 for( int l
=0; l
< poly_to_add
->loop_total
; l
++ )
1402 cxr_loop
*loop1
= &mesh
->loops
[poly_to_add
->loop_start
+l
];
1403 cxr_polygon
*future_face
= &mesh
->polys
[loop1
->poly_right
];
1405 if( edge_tagged
[ loop1
->edge_index
] || loop1
->poly_right
== loop
->poly_right
)
1406 goto IL_SKIP_SIMILAR_PLANES
;
1408 for( int m
=0; m
<solid_len
; m
++ )
1409 if( solid
[m
] == loop1
->poly_right
)
1410 goto IL_SKIP_SIMILAR_PLANES
;
1412 for( int m
=0; m
<solid_len
; m
++ )
1414 cxr_polygon
*polym
= &mesh
->polys
[solid
[m
]];
1415 if( v3_dot( polym
->normal
,future_face
->normal
) > CXR_PLANE_SIMILARITY_MAX
)
1416 goto IL_SKIP_PLANE_ADD
;
1419 IL_SKIP_SIMILAR_PLANES
:;
1422 // Check for vertices in the new poly that exist on a current plane.
1423 // this condition is an invalid configuration and should not be added.
1425 solid
[ solid_len
] = loop
->poly_right
;
1428 if( cxr_valid_solid(mesh
,solid
,solid_len
+1 ) )
1430 faces_tagged
[ loop
->poly_right
] = i
;
1440 search_start
= solid_len
;
1442 goto IL_SEARCH_CONTINUE
;
1444 // The current method can create some invalid configurations
1445 // filter those out that dont work and un-tag the faces
1449 * I think we have good enough filters now to ignore these,
1450 * probably should make them show as warnings
1453 if( !cxr_valid_solid( mesh
, solid
, solid_len
) )
1455 for( int l
=0; l
<solid_len
; l
++ )
1456 faces_tagged
[ solid
[l
] ] = -1;
1458 goto IL_CANCEL_SOLID
;
1460 for( int j
=0; j
<solid_len
-1; j
++ )
1462 for( int k
=j
+1; k
<solid_len
; k
++ )
1464 cxr_polygon
*polyj
= &mesh
->polys
[solid
[j
]],
1465 *polyk
= &mesh
->polys
[ solid
[k
] ];
1467 if( v3_dot( polyj
->normal
, polyk
->normal
) > CXR_PLANE_SIMILARITY_MAX
)
1469 for( int l
=0; l
<solid_len
; l
++ )
1470 faces_tagged
[ solid
[l
] ] = -1;
1472 goto IL_CANCEL_SOLID
;
1480 struct csolid
*csolid
= &candidates
[candidate_count
++];
1481 csolid
->start
= solid_buffer_len
;
1482 csolid
->count
= solid_len
;
1483 csolid
->edge_count
= 0;
1485 v3_zero( csolid
->center
);
1486 for( int j
=0; j
<solid_len
; j
++ )
1488 cxr_polygon
*polyj
= &mesh
->polys
[ solid
[j
] ];
1489 v3_add( polyj
->center
, csolid
->center
, csolid
->center
);
1490 csolid
->edge_count
+= polyj
->loop_total
;
1492 v3_divs( csolid
->center
, solid_len
, csolid
->center
);
1494 solid_buffer_len
+= solid_len
;
1499 free( edge_references
);
1501 // Create all candidates who have one or less non-manifolds edges
1502 // Loop each candidate, determine the manifold, and pick the best one
1504 struct csolid
*best_solid
= NULL
;
1505 int fewest_manifold_splits
= INT32_MAX
;
1507 cxr_loop
*best_manifold
= malloc( mesh
->abloops
.count
*sizeof(cxr_loop
) );
1508 int *best_manifold_splits
= malloc( mesh
->abloops
.count
*sizeof(int) );
1509 int best_manifold_len
= 0;
1510 int max_solid_faces
= 0;
1513 int *edge_list = malloc( mesh->edges.count *sizeof(int) );
1514 int *edge_lefts = malloc( mesh->edges.count *sizeof(int) );
1516 cxr_loop
**edge_list
= malloc( sizeof(*edge_list
) *mesh
->abedges
.count
);
1518 cxr_loop
*manifold
= malloc(mesh
->abedges
.count
*2*sizeof(cxr_loop
));
1519 int *splits
= malloc(mesh
->abedges
.count
*2*sizeof(int));
1521 for( int i
=0; i
<candidate_count
; i
++ )
1523 struct csolid
*solid
= &candidates
[i
];
1524 max_solid_faces
= cxr_max(max_solid_faces
,solid
->count
);
1526 if( solid
->count
<= 2 )
1529 int init_reverse
= 0;
1530 int unique_edge_count
= 0;
1532 for( int j
=0; j
<solid
->count
; j
++ )
1534 cxr_polygon
*poly
= cxr_ab_ptr(&mesh
->abpolys
, solid_buffer
[solid
->start
+j
]);
1536 for( int k
=0; k
<poly
->loop_total
; k
++ )
1538 cxr_loop
*loop
= cxr_ab_ptr(&mesh
->abloops
, poly
->loop_start
+k
);
1540 for( int l
=0; l
<unique_edge_count
; l
++ )
1541 if( edge_list
[l
]->edge_index
== loop
->edge_index
)
1542 goto IL_EDGE_ALREADY_PRESENT
;
1544 // Check if right edge references a polygon that is not
1545 // present inside the current solid candidate
1546 for( int l
=0; l
<solid
->count
; l
++ )
1547 if( loop
->poly_right
== solid_buffer
[solid
->start
+l
] )
1548 goto IL_EDGE_ALREADY_PRESENT
;
1550 edge_list
[ unique_edge_count
] = loop
;
1552 if( unique_edge_count
== 0 )
1554 cxr_edge
*edgeptr
= cxr_ab_ptr(&mesh
->abedges
, loop
->edge_index
);
1555 if( edgeptr
->i1
== loop
->index
)
1559 unique_edge_count
++;
1560 IL_EDGE_ALREADY_PRESENT
:;
1564 // This solid is already fully connected
1565 if( unique_edge_count
== 0 )
1568 // Link edges together to create new manifold
1569 // Corners are created when two edges share a polygon face
1570 // This way we can split it into regions
1572 for( int j
=0; j
<mesh
->p_abverts
->count
; j
++ )
1573 vertex_tagged
[j
] = -1;
1575 // Start with a loop edge which was tagged
1576 cxr_edge
*current
= &mesh
->edges
[edge_list
[0]->edge_index
];
1578 int endpt
= (!init_reverse
)? current
->i0
: current
->i1
,
1580 curface
= edge_list
[0]->poly_left
;
1582 int manifold_len
= 0;
1583 int split_count
= 0;
1585 IL_MANIFOLD_CONTINUE
:
1586 for( int j
=0; j
<unique_edge_count
; j
++ )
1588 cxr_edge
*other
= &mesh
->edges
[ edge_list
[j
]->edge_index
];
1589 if( other
== current
)
1592 if( other
->i0
== endpt
|| other
->i1
== endpt
)
1597 if( other
->i0
== endpt
) endpt
= current
->i1
;
1598 else endpt
= current
->i0
;
1600 if( curface
==edge_list
[j
]->poly_left
)
1602 splits
[ manifold_len
] = 1;
1606 splits
[ manifold_len
] = 0;
1608 // Append to intermidiary manifold
1609 manifold
[ manifold_len
].edge_index
= edge_list
[j
]->edge_index
;
1610 manifold
[ manifold_len
].poly_left
= edge_list
[j
]->poly_left
;
1611 manifold
[ manifold_len
].index
= lastpt
;
1612 manifold
[ manifold_len
].poly_right
= edge_list
[j
]->poly_right
;
1615 curface
= edge_list
[j
]->poly_left
;
1617 if(endpt
== start
) goto IL_MANIFOLD_COMPLETE
;
1618 goto IL_MANIFOLD_CONTINUE
;
1621 cxr_log( "Failed to link manifold, count: %d\n", manifold_len
);
1623 for( int j
=0; j
<solid
->count
; j
++ )
1625 cxr_log( "p%d\n", solid_buffer
[solid
->start
+j
] );
1626 cxr_polygon
*poly
= &mesh
->polys
[ solid_buffer
[solid
->start
+j
] ];
1627 cxr_debug_poly( mesh
, poly
, (v4f
){0.2,0.0,0.0,1.0} );
1630 for( int j
=0; j
<unique_edge_count
; j
++ )
1632 cxr_edge
*uedge
= &mesh
->edges
[ edge_list
[j
]->edge_index
];
1633 cxr_debug_line( verts
[uedge
->i0
], verts
[uedge
->i1
],
1634 (v4f
){0.4,0.0,0.0,1.0});
1637 for( int j
=0; j
<manifold_len
-1; j
++ )
1639 cxr_loop
*lp0
= &manifold
[j
],
1640 *lp1
= &manifold
[cxr_range(j
+1,manifold_len
)];
1642 cxr_debug_line(verts
[lp0
->index
],verts
[lp1
->index
], colour_error
);
1645 cxr_debug_mesh( mesh
, (v4f
){0.0,0.0,0.0, 0.9} );
1646 *error
= CXR_ERROR_BAD_MANIFOLD
;
1652 free(vertex_tagged
);
1656 free(best_manifold
);
1657 free(best_manifold_splits
);
1660 IL_MANIFOLD_COMPLETE
:;
1662 if( manifold_len
< unique_edge_count
)
1666 if( split_count
< fewest_manifold_splits
)
1668 fewest_manifold_splits
= split_count
;
1671 for( int j
=0; j
<manifold_len
; j
++ )
1673 best_manifold
[j
] = manifold
[j
];
1674 best_manifold_splits
[j
] = splits
[j
];
1676 best_manifold_len
= manifold_len
;
1685 if( max_solid_faces
< 2 )
1687 *error
= CXR_ERROR_NO_SOLIDS
;
1689 free(vertex_tagged
);
1693 free(best_manifold
);
1694 free(best_manifold_splits
);
1698 if( best_solid
!= NULL
)
1700 cxr_mesh
*pullmesh
=
1701 cxr_alloc_mesh( best_solid
->edge_count
, best_solid
->edge_count
, best_solid
->count
, mesh
->p_abverts
);
1703 // Add existing faces to pullsolid, and delete from main mesh
1704 for( int i
=0; i
<best_solid
->count
; i
++ )
1706 int nface_id
= pullmesh
->abpolys
.count
;
1708 cxr_polygon
*exist_face
= &mesh
->polys
[ solid_buffer
[best_solid
->start
+i
] ];
1709 cxr_polygon
*new_face
= cxr_ab_empty( &pullmesh
->abpolys
);
1711 *new_face
= *exist_face
;
1712 new_face
->loop_start
= pullmesh
->abloops
.count
;
1714 for( int j
=0; j
<exist_face
->loop_total
; j
++ )
1716 cxr_loop
*exist_loop
= cxr_ab_ptr(&mesh
->abloops
, exist_face
->loop_start
+j
);
1717 cxr_loop
*new_loop
= cxr_ab_empty(&pullmesh
->abloops
);
1719 new_loop
->index
= exist_loop
->index
;
1720 new_loop
->poly_left
= nface_id
;
1721 new_loop
->poly_right
= -1;
1722 new_loop
->edge_index
= 0;
1723 v2_copy( exist_loop
->uv
, new_loop
->uv
);
1726 exist_face
->loop_total
= -1;
1730 int pullmesh_new_start
= pullmesh
->abpolys
.count
;
1732 if( fewest_manifold_splits
!= 0 )
1734 // This is a really strange observation, however it *seems* to apply to the kinds
1735 // of geometry we are dealing with. If the split count is odd, the manifold can be
1736 // created easily, no folding required.
1738 // When it is even, it appears that internal implicit geometry is required, so we
1739 // need to fold the loops we create. Its really weird, but for some reason works on
1740 // the geometry rules we've defined.
1741 // TODO(harry): Find a well defined rule here.
1743 int collapse_used_segments
= (u32
)fewest_manifold_splits
& 0x1? 0: 1;
1745 IL_MANIFOLD_BUILD_REPEAT
:
1747 for( int j
=0; j
<best_manifold_len
; j
++ )
1749 if( best_manifold_splits
[j
] )
1751 cxr_loop
*loop
= &best_manifold
[j
];
1753 for( int k
=1; k
<best_manifold_len
; k
++ )
1755 int index1
= cxr_range(j
+k
,best_manifold_len
);
1756 cxr_loop
*loop1
= &best_manifold
[index1
];
1758 if( best_manifold_splits
[index1
] )
1765 if( new_polys
> best_manifold_len
)
1767 cxr_log( "Programming error: Too many new polys!\n" );
1771 cxr_create_poly( pullmesh
, best_manifold
, k
+1, j
, best_manifold_len
);
1773 // Remove new section from manifold
1774 if( collapse_used_segments
)
1776 best_manifold_splits
[j
] = 0;
1777 best_manifold_splits
[index1
] = 0;
1779 int new_length
= (best_manifold_len
-(k
-1));
1781 cxr_loop
*new_manifold
= malloc( new_length
*sizeof(cxr_loop
) );
1782 int *new_manifold_splits
= malloc( new_length
*sizeof(int) );
1784 for( int l
=0; l
<new_length
; l
++ )
1786 int i_src
= cxr_range( j
+k
+l
, best_manifold_len
);
1787 new_manifold
[l
] = best_manifold
[i_src
];
1788 new_manifold_splits
[l
] = best_manifold_splits
[i_src
];
1791 free( best_manifold
);
1792 free( best_manifold_splits
);
1793 best_manifold
= new_manifold
;
1794 best_manifold_splits
= new_manifold_splits
;
1796 best_manifold_len
= new_length
;
1798 goto IL_MANIFOLD_BUILD_REPEAT
;
1808 if( best_manifold_len
&& collapse_used_segments
)
1810 cxr_create_poly( pullmesh
, best_manifold
, best_manifold_len
, 0, best_manifold_len
);
1816 cxr_create_poly( pullmesh
, best_manifold
, best_manifold_len
, 0, best_manifold_len
);
1820 // vert_buffer may be reallocated by the next section of code,
1821 // force a NULLref on vertices to catch any errors
1823 struct cxr_auto_buffer
*abverts
= mesh
->p_abverts
;
1825 // Implicit geometry reconstruction
1826 // If there's 3 or more planes, collide them together to see if any new
1827 // vertices need to be created on the mesh
1828 if( new_polys
>= 3 )
1830 for( int i
=0; i
<new_polys
-2; i
++ )
1832 for( int j
=i
+1; j
<new_polys
-1; j
++ )
1834 for( int k
=j
+1; k
<new_polys
; k
++ )
1836 cxr_polygon
*ptri
= &pullmesh
->polys
[ pullmesh_new_start
+i
],
1837 *ptrj
= &pullmesh
->polys
[ pullmesh_new_start
+j
],
1838 *ptrk
= &pullmesh
->polys
[ pullmesh_new_start
+k
];
1840 v4f planei
, planej
, planek
;
1841 normal_to_plane(ptri
->normal
,ptri
->center
,planei
);
1842 normal_to_plane(ptrj
->normal
,ptrj
->center
,planej
);
1843 normal_to_plane(ptrk
->normal
,ptrk
->center
,planek
);
1847 if( plane_intersect(planei
,planej
,planek
,intersect
) )
1849 // cxr_debug_box( intersect, 0.05, colour_error );
1851 // Make sure this point is within the convex region, otherwise treat
1852 // it as a degenerate case
1854 int point_valid
= 1;
1855 for( int l
=0; l
<pullmesh
->abpolys
.count
; l
++ )
1857 cxr_polygon
*ptrl
= &pullmesh
->polys
[l
];
1860 normal_to_plane(ptrl
->normal
, ptrl
->center
, planel
);
1862 if( plane_polarity( planel
, intersect
) > 0.01 )
1864 cxr_log( "degen vert, planes %d, %d, %d [max:%d]\n", i
,j
,k
, new_polys
);
1865 *error
= CXR_ERROR_DEGEN_IMPLICIT
;
1867 cxr_debug_poly( pullmesh
, ptri
, colours_random
[3] );
1868 cxr_debug_poly( pullmesh
, ptrj
, colours_random
[1] );
1869 cxr_debug_poly( pullmesh
, ptrk
, colours_random
[2] );
1876 if( !point_valid
) continue;
1878 // Extend faces to include this point
1879 int nvertid
= abverts
->count
;
1880 cxr_ab_push( abverts
, intersect
);
1882 ptrj
->loop_start
+= 1;
1883 ptrk
->loop_start
+= 2;
1885 cxr_ab_reserve( &pullmesh
->abloops
, 3);
1887 int newi
= ptri
->loop_start
+ptri
->loop_total
,
1888 newj
= ptrj
->loop_start
+ptrj
->loop_total
,
1889 newk
= ptrk
->loop_start
+ptrk
->loop_total
;
1892 *lloopi
= cxr_ab_empty_at(&pullmesh
->abloops
, newi
),
1893 *lloopj
= cxr_ab_empty_at(&pullmesh
->abloops
, newj
),
1894 *lloopk
= cxr_ab_empty_at(&pullmesh
->abloops
, newk
);
1896 lloopi
->index
= nvertid
;
1897 lloopi
->edge_index
= 0;
1898 lloopi
->poly_left
= pullmesh_new_start
+i
;
1899 lloopi
->poly_right
= -1;
1901 lloopj
->index
= nvertid
;
1902 lloopj
->poly_left
= pullmesh_new_start
+j
;
1903 lloopj
->edge_index
= 0;
1904 lloopj
->poly_right
= -1;
1906 lloopk
->index
= nvertid
;
1907 lloopk
->edge_index
= 0;
1908 lloopk
->poly_left
= pullmesh_new_start
+k
;
1909 lloopk
->poly_right
= -1;
1911 v2_zero(lloopi
->uv
);
1912 v2_zero(lloopj
->uv
);
1913 v2_zero(lloopk
->uv
);
1915 ptri
->loop_total
++;
1916 ptrj
->loop_total
++;
1917 ptrk
->loop_total
++;
1919 // Adjust centers of faces
1920 v3_lerp( ptri
->center
, intersect
, 1.0/(double)ptri
->loop_total
, ptri
->center
);
1921 v3_lerp( ptrj
->center
, intersect
, 1.0/(double)ptrj
->loop_total
, ptrj
->center
);
1922 v3_lerp( ptrk
->center
, intersect
, 1.0/(double)ptrk
->loop_total
, ptrk
->center
);
1929 // Copy faces from pullsolid into orig mesh
1931 for( int i
=0; i
<new_polys
; i
++ )
1933 int rface_id
= mesh
->abpolys
.count
;
1935 cxr_polygon
*pface
= cxr_ab_ptr(&pullmesh
->abpolys
,pullmesh_new_start
+i
);
1936 cxr_polygon
*rip_face
= cxr_ab_empty(&mesh
->abpolys
);
1938 rip_face
->loop_start
= mesh
->abloops
.count
;
1939 rip_face
->loop_total
= pface
->loop_total
;
1940 rip_face
->material_id
= -1;
1942 for( int j
=0; j
<rip_face
->loop_total
; j
++ )
1944 cxr_loop
*ploop
= cxr_ab_ptr(&pullmesh
->abloops
, pface
->loop_start
+pface
->loop_total
-j
-1),
1945 *rloop
= cxr_ab_empty(&mesh
->abloops
);
1947 rloop
->index
= ploop
->index
;
1948 rloop
->poly_left
= rface_id
;
1949 rloop
->poly_right
= -1;
1950 rloop
->edge_index
= 0;
1951 v2_copy( ploop
->uv
, rloop
->uv
);
1954 v3_copy( pface
->center
, rip_face
->center
);
1955 v3_negate( pface
->normal
, rip_face
->normal
);
1958 cxr_mesh_update( mesh
);
1959 cxr_mesh_update( pullmesh
);
1961 cxr_mesh_clean_faces( mesh
);
1962 cxr_mesh_clean_faces( pullmesh
);
1963 cxr_mesh_clean_edges( mesh
);
1964 cxr_mesh_clean_edges( pullmesh
);
1967 free(vertex_tagged
);
1971 free(best_manifold
);
1972 free(best_manifold_splits
);
1978 free(vertex_tagged
);
1982 free(best_manifold
);
1983 free(best_manifold_splits
);
1989 * Convert from the format we recieve from blender into our internal format
1990 * with auto buffers.
1992 static cxr_mesh
*cxr_to_internal_format(cxr_input_mesh
*src
, cxr_auto_buffer
*abverts
)
1994 cxr_mesh
*mesh
= cxr_alloc_mesh( src
->edge_count
, src
->loop_count
, src
->poly_count
, abverts
);
1995 cxr_ab_init( abverts
, sizeof(v3f
), src
->vertex_count
);
1997 memcpy( cxr_ab_ptr( &mesh
->abedges
, 0 ), src
->edges
, src
->edge_count
*sizeof(cxr_edge
));
1998 memcpy( cxr_ab_ptr( &mesh
->abpolys
, 0 ), src
->polys
, src
->poly_count
*sizeof(cxr_polygon
));
1999 memcpy( cxr_ab_ptr( abverts
, 0 ), src
->vertices
, src
->vertex_count
*sizeof(v3f
));
2000 mesh
->abedges
.count
= src
->edge_count
;
2001 mesh
->abloops
.count
= src
->loop_count
;
2002 mesh
->abpolys
.count
= src
->poly_count
;
2004 cxr_mesh_update( mesh
);
2006 for( int i
=0; i
<src
->loop_count
; i
++ )
2008 cxr_loop
*lp
= &mesh
->loops
[i
];
2010 lp
->index
= src
->loops
[i
].index
;
2011 lp
->edge_index
= src
->loops
[i
].edge_index
;
2012 v2_copy( src
->loops
[i
].uv
, lp
->uv
);
2015 abverts
->count
= src
->vertex_count
;
2020 * Find most extreme point along a given direction
2022 static double support_distance( v3f verts
[3], v3f dir
, double coef
)
2026 coef
* v3_dot( verts
[0], dir
),
2029 coef
* v3_dot( verts
[1], dir
),
2030 coef
* v3_dot( verts
[2], dir
)
2036 static void cxr_calculate_axis(
2037 cxr_texinfo
*transform
,
2043 v2f tT
, bT
; // Tangent/bitangent pairs for UV space and world
2046 v2_sub( uvs
[0], uvs
[1], tT
);
2047 v2_sub( uvs
[2], uvs
[1], bT
);
2048 v3_sub( verts
[0], verts
[1], tW
);
2049 v3_sub( verts
[2], verts
[1], bW
);
2051 // Use arbitrary projection if there is no UV
2052 if( v2_length( tT
) < 0.0001 || v2_length( bT
) < 0.0001 )
2054 v3f uaxis
, normal
, vaxis
;
2056 v3_copy( tW
, uaxis
);
2057 v3_normalize( uaxis
);
2059 v3_cross( tW
, bW
, normal
);
2060 v3_cross( normal
, uaxis
, vaxis
);
2061 v3_normalize( vaxis
);
2063 v3_copy( uaxis
, transform
->uaxis
);
2064 v3_copy( vaxis
, transform
->vaxis
);
2065 v2_zero( transform
->offset
);
2067 v2_div( (v2f
){128.0, 128.0}, texture_res
, transform
->scale
);
2068 transform
->winding
= 1.0;
2072 // Detect if UV is reversed
2073 double winding
= v2_cross( tT
, bT
) >= 0.0f
? 1.0f
: -1.0f
;
2075 // UV projection reference
2077 v2_muls((v2f
){1,0}, winding
, vX
);
2078 v2_muls((v2f
){0,1}, winding
, vY
);
2080 // Reproject reference into world space, including skew
2083 v3_muls( tW
, v2_cross(vX
,bT
) / v2_cross(bT
,tT
), uaxis1
);
2084 v3_muladds( uaxis1
, bW
, v2_cross(vX
, tT
) / v2_cross(tT
,bT
), uaxis1
);
2086 v3_muls( tW
, v2_cross(vY
,bT
) / v2_cross(bT
,tT
), vaxis1
);
2087 v3_muladds( vaxis1
, bW
, v2_cross(vY
,tT
) / v2_cross(tT
,bT
), vaxis1
);
2089 v3_normalize( uaxis1
);
2090 v3_normalize( vaxis1
);
2092 // Apply source transform to axis (yes, they also need to be swapped)
2093 v3f norm
, uaxis
, vaxis
;
2095 v3_cross( bW
, tW
, norm
);
2097 v3_cross( vaxis1
, norm
, uaxis
);
2098 v3_cross( uaxis1
, norm
, vaxis
);
2101 v2f uvmin
, uvmax
, uvdelta
;
2102 v2_minv( uvs
[0], uvs
[1], uvmin
);
2103 v2_minv( uvmin
, uvs
[2], uvmin
);
2104 v2_maxv( uvs
[0], uvs
[1], uvmax
);
2105 v2_maxv( uvmax
, uvs
[2], uvmax
);
2107 v2_sub( uvmax
, uvmin
, uvdelta
);
2110 v2f uvminw
, uvmaxw
, uvdeltaw
;
2111 uvminw
[0] = -support_distance( verts
, uaxis
, -1.0f
);
2112 uvmaxw
[0] = support_distance( verts
, uaxis
, 1.0f
);
2113 uvminw
[1] = -support_distance( verts
, vaxis
, -1.0f
);
2114 uvmaxw
[1] = support_distance( verts
, vaxis
, 1.0f
);
2116 v2_sub( uvmaxw
, uvminw
, uvdeltaw
);
2120 v2_div( uvdeltaw
, uvdelta
, uv_scale
);
2121 v2_div( uv_scale
, texture_res
, uv_scale
);
2123 // Find offset via 'natural' point
2124 v2f target_uv
, natural_uv
, tex_offset
;
2125 v2_mul( uvs
[0], texture_res
, target_uv
);
2127 natural_uv
[0] = v3_dot( uaxis
, verts
[0] );
2128 natural_uv
[1] = -v3_dot( vaxis
, verts
[0] );
2129 v2_div( natural_uv
, uv_scale
, natural_uv
);
2131 tex_offset
[0] = target_uv
[0]-natural_uv
[0];
2132 tex_offset
[1] = -(target_uv
[1]-natural_uv
[1]);
2134 // Copy everything into output
2135 v3_copy( uaxis
, transform
->uaxis
);
2136 v3_copy( vaxis
, transform
->vaxis
);
2137 v2_copy( tex_offset
, transform
->offset
);
2138 v2_copy( uv_scale
, transform
->scale
);
2139 transform
->winding
= winding
;
2142 CXR_API cxr_input_mesh
*cxr_decompose(cxr_input_mesh
*src
)
2144 FILE *yeet
= fopen( "/home/harry/Documents/blender_addons_remote/addons/convexer/src/solid.h", "w" );
2146 fprintf( yeet
, "v3f test_verts[] = {\n" );
2147 for( int i
=0; i
<src
->vertex_count
; i
++ )
2149 fprintf( yeet
, " { %f, %f, %f },\n",
2150 src
->vertices
[i
][0],
2151 src
->vertices
[i
][1],
2152 src
->vertices
[i
][2] );
2154 fprintf( yeet
, "};\n" );
2156 fprintf( yeet
, "struct cxr_input_loop test_loops[] = {\n" );
2157 for( int i
=0; i
<src
->loop_count
; i
++ )
2159 fprintf( yeet
, " {%d, %d},\n",
2160 src
->loops
[i
].index
,
2161 src
->loops
[i
].edge_index
);
2163 fprintf( yeet
, "};\n" );
2165 fprintf( yeet
, "struct cxr_polygon test_polys[] = {\n" );
2166 for( int i
=0; i
<src
->poly_count
; i
++ )
2168 fprintf( yeet
, " {%d, %d, {%f, %f, %f}, {%f, %f, %f}},\n",
2169 src
->polys
[i
].loop_start
,
2170 src
->polys
[i
].loop_total
,
2171 src
->polys
[i
].normal
[0],
2172 src
->polys
[i
].normal
[1],
2173 src
->polys
[i
].normal
[2],
2174 src
->polys
[i
].center
[0],
2175 src
->polys
[i
].center
[1],
2176 src
->polys
[i
].center
[2] );
2178 fprintf( yeet
, "};\n" );
2180 fprintf( yeet
, "struct cxr_edge test_edges[] = {\n" );
2181 for( int i
=0; i
<src
->edge_count
; i
++ )
2183 fprintf( yeet
, " {%d, %d, %d},\n",
2186 src
->edges
[i
].freestyle
2189 fprintf( yeet
, "};\n" );
2191 fprintf( yeet
, "struct cxr_input_mesh test_mesh = {\n" );
2192 fprintf( yeet
, " .vertices = test_verts,\n" );
2193 fprintf( yeet
, " .loops = test_loops,\n" );
2194 fprintf( yeet
, " .edges = test_edges,\n" );
2195 fprintf( yeet
, " .polys = test_polys,\n" );
2196 fprintf( yeet
, " .poly_count=%d,\n", src
->poly_count
);
2197 fprintf( yeet
, " .vertex_count=%d,\n", src
->vertex_count
);
2198 fprintf( yeet
, " .edge_count=%d,\n",src
->edge_count
);
2199 fprintf( yeet
, " .loop_count=%d\n", src
->loop_count
);
2200 fprintf( yeet
, "};\n" );
2207 static int cxr_cardinal( v3f a
, int ignore
)
2210 double component_max
= -CXR_BIG_NUMBER
;
2212 for( int i
=0; i
<3; i
++ )
2214 if( i
== ignore
) continue;
2216 if( fabs(a
[i
]) > component_max
)
2218 component_max
= fabs(a
[i
]);
2222 double d
= a
[component
] >= 0.0? 1.0: -1.0;
2229 // Convert contiguous mesh to patch of displacments
2231 static void cxr_write_disp(cxr_mesh
*mesh
, cxr_input_mesh
*inputmesh
,
2234 v3f
*verts
= cxr_ab_ptr( mesh
->p_abverts
, 0 );
2236 // Create a graph which maps vertices by their connections
2239 int con_start
, con_count
; // Index into the connection graph
2247 *vertinfo
= malloc( sizeof(struct vertinfo
)*mesh
->p_abverts
->count
);
2248 int *graph
= malloc( sizeof(int) * mesh
->abedges
.count
*2 );
2251 for( int i
=0; i
<mesh
->p_abverts
->count
; i
++ )
2253 struct vertinfo
*info
= &vertinfo
[i
];
2254 info
->con_start
= con_pos
;
2255 info
->con_count
= 0;
2262 for( int j
=0; j
<mesh
->abedges
.count
; j
++ )
2264 cxr_edge
*edge
= &mesh
->edges
[j
];
2266 if( edge
->i0
== i
|| edge
->i1
== i
)
2268 graph
[ con_pos
++ ] = edge
->i0
== i
? edge
->i1
: edge
->i0
;
2271 if( edge
->freestyle
)
2277 // Find best normal for brush patch. VBSP uses the original brush
2278 // as reference for decal projection.
2280 // These are clamped to be cardinal directions as to make the VMF somewhat
2283 v3f avg_normal
, refv
, refu
, refn
;
2284 v3_zero(refv
); v3_zero(refu
); v3_zero(refn
);
2286 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
2288 cxr_polygon
*poly
= &mesh
->polys
[i
];
2289 v3_add( poly
->normal
, avg_normal
, avg_normal
);
2291 v3_divs( avg_normal
, mesh
->abpolys
.count
, avg_normal
);
2292 v3_normalize( avg_normal
); // TODO(harry): This can be zero length. Should add a safety check
2293 // normalize function that checks for small length before
2294 // carrying out, otherwise we get inf/nan values...
2296 int n_cardinal
= cxr_cardinal( avg_normal
, -1 );
2298 // Approximately matching the area of the result brush faces to the actual area
2299 // this is to assign a 'best guess' amount of lightmap texels.
2301 double uv_area
= 0.0, face_area
= 0.0, sf
;
2302 v2f uvboundmin
, uvboundmax
;
2303 v3f faceboundmin
, faceboundmax
;
2307 v2_fill( uvboundmin
, CXR_BIG_NUMBER
);
2308 v2_fill( uvboundmax
, -CXR_BIG_NUMBER
);
2309 v3_fill( faceboundmin
, CXR_BIG_NUMBER
);
2310 v3_fill( faceboundmax
, -CXR_BIG_NUMBER
);
2312 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
2314 cxr_polygon
*poly
= cxr_ab_ptr( &mesh
->abpolys
, i
);
2316 for( int j
=0; j
<poly
->loop_total
; j
++ )
2318 cxr_loop
*lp0
= cxr_ab_ptr(&mesh
->abloops
, poly
->loop_start
+j
);
2319 v2_minv( lp0
->uv
, uvboundmin
, uvboundmin
);
2320 v2_maxv( lp0
->uv
, uvboundmax
, uvboundmax
);
2321 v3_minv( cxr_ab_ptr(mesh
->p_abverts
,lp0
->index
), faceboundmin
, faceboundmin
);
2322 v3_maxv( cxr_ab_ptr(mesh
->p_abverts
,lp0
->index
), faceboundmax
, faceboundmax
);
2325 for( int j
=0; j
<poly
->loop_total
-2; j
++ )
2327 cxr_loop
*lp0
= &mesh
->loops
[poly
->loop_start
],
2328 *lp1
= &mesh
->loops
[poly
->loop_start
+j
+1],
2329 *lp2
= &mesh
->loops
[poly
->loop_start
+j
+2];
2332 v3_sub( verts
[lp1
->index
], verts
[lp0
->index
], va
);
2333 v3_sub( verts
[lp2
->index
], verts
[lp0
->index
], vb
);
2334 v3_cross( va
, vb
, orth
);
2336 face_area
+= v3_length( orth
) / 2.0;
2339 v2_sub( lp1
->uv
, lp0
->uv
, uva
);
2340 v2_sub( lp2
->uv
, lp0
->uv
, uvb
);
2342 uv_area
+= fabs(v2_cross( uva
, uvb
)) / 2.0;
2346 v3_add( faceboundmax
, faceboundmin
, face_center
);
2347 v3_muls( face_center
, 0.5, face_center
);
2348 v2_add( uvboundmin
, uvboundmax
, uv_center
);
2349 v2_muls( uv_center
, 0.5, uv_center
);
2351 sf
= sqrt( face_area
/ uv_area
);
2352 int corner_count
= 0;
2354 // Vertex classification
2355 for( int i
=0; i
<mesh
->p_abverts
->count
; i
++ )
2357 struct vertinfo
*info
= &vertinfo
[i
];
2358 if( !info
->boundary
) continue;
2363 for( int j
=0; j
<info
->con_count
; j
++ )
2365 int con
= graph
[info
->con_start
+j
];
2367 if( vertinfo
[con
].boundary
)
2372 if( count
> 2 || non_manifold
)
2377 //cxr_debug_box( cxr_ab_ptr(abverts,i), 0.1, colour_success );
2381 // TODO(harry): This currently only supports power 2 displacements
2382 // its quite straightforward to upgrade it.
2389 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
2391 cxr_polygon
*basepoly
= &mesh
->polys
[i
];
2393 for( int h
=0; h
<basepoly
->loop_total
; h
++ )
2396 i1
= cxr_range(h
+1,basepoly
->loop_total
);
2398 cxr_loop
*l0
= &mesh
->loops
[ basepoly
->loop_start
+i0
],
2399 *l1
= &mesh
->loops
[ basepoly
->loop_start
+i1
];
2400 struct vertinfo
*info
= &vertinfo
[ l0
->index
];
2404 int corner_count
= 1;
2406 cxr_material
*matptr
=
2407 basepoly
->material_id
< 0 || inputmesh
->material_count
== 0?
2409 &inputmesh
->materials
[ basepoly
->material_id
];
2412 dispedge
[0] = l0
->index
;
2413 dispedge
[1] = l1
->index
;
2414 v2_copy( l0
->uv
, corner_uvs
[0] );
2416 // Consume (remove) faces we use for corners
2417 basepoly
->loop_total
= -1;
2419 //cxr_debug_box( cxr_ab_ptr(abverts,l0->index),0.08,(v4f){0.0,0.0,1.0,1.0});
2422 // --------------------
2424 while( dispedge_count
< 17 )
2426 struct vertinfo
*edge_head
= &vertinfo
[dispedge
[dispedge_count
-1]];
2429 if( edge_head
->corner
)
2431 // Find a polygon that has the edge C-1 -> C
2432 for( int j
=0; j
<mesh
->abpolys
.count
&& !newvert
; j
++ )
2434 cxr_polygon
*poly
= &mesh
->polys
[j
];
2436 for( int k
=0; k
<poly
->loop_total
; k
++ )
2439 i1
= cxr_range(k
+1,poly
->loop_total
);
2441 cxr_loop
*l0
= &mesh
->loops
[ poly
->loop_start
+i0
],
2442 *l1
= &mesh
->loops
[ poly
->loop_start
+i1
];
2444 if( l0
->index
== dispedge
[dispedge_count
-2] &&
2445 l1
->index
== dispedge
[dispedge_count
-1] )
2447 // Take the vertex after that edge
2448 v2_copy( l1
->uv
, corner_uvs
[corner_count
++] );
2450 int i2
= cxr_range(i1
+1,poly
->loop_total
);
2451 cxr_loop
*l2
= &mesh
->loops
[ poly
->loop_start
+i2
];
2453 dispedge
[dispedge_count
++] = l2
->index
;
2455 poly
->loop_total
= -1;
2463 for( int j
=0; j
<edge_head
->con_count
; j
++ )
2465 int con
= graph
[edge_head
->con_start
+j
];
2470 if( dispedge_count
> 1 )
2471 if( con
== dispedge
[dispedge_count
-2] )
2474 struct vertinfo
*coninfo
= &vertinfo
[con
];
2476 if( !coninfo
->boundary
)
2480 cxr_debug_arrow( cxr_ab_ptr(abverts,dispedge[dispedge_count-1]),
2481 cxr_ab_ptr(abverts,con),
2487 dispedge
[ dispedge_count
++ ] = con
;
2496 cxr_debug_box(verts
[dispedge
[dispedge_count
-1]], 0.1, colour_error
);
2501 // --------------------
2505 v2_sub( corner_uvs
[1], corner_uvs
[0], va
);
2506 v2_sub( corner_uvs
[2], corner_uvs
[0], vb
);
2508 // Connect up the grid
2516 // Example: a := common unused vertex that is connected to
2517 // by 1 and 15. Or y-1, and x-1 on the grid.
2518 // g := c and f common vert ^
2522 for( int j
=0; j
<5; j
++ ) grid
[j
] = dispedge
[j
];
2523 for( int j
=1; j
<5; j
++ ) grid
[j
*5+4] = dispedge
[j
+4];
2524 for( int j
=0; j
<4; j
++ ) grid
[4*5+3-j
] = dispedge
[j
+9];
2525 for( int j
=1; j
<4; j
++ ) grid
[j
*5] = dispedge
[16-j
];
2528 for( int j
=1; j
<4; j
++ )
2530 for( int k
=1; k
<4; k
++ )
2532 int s0
= grid
[(j
-1)*5+k
],
2535 struct vertinfo
*va
= &vertinfo
[s0
],
2536 *vb
= &vertinfo
[s1
];
2538 // Find a common vertex between s0 and s1
2540 for( int l
=0; l
<va
->con_count
; l
++ )
2542 for( int m
=0; m
<vb
->con_count
; m
++ )
2544 int cona
= graph
[va
->con_start
+l
],
2545 conb
= graph
[vb
->con_start
+m
];
2549 if( vertinfo
[cona
].used
|| vertinfo
[cona
].boundary
)
2552 grid
[ j
*5+k
] = cona
;
2553 vertinfo
[cona
].used
= 1;
2555 goto IL_MATCHED_DISP_INTERIOR_VERT
;
2560 // Broken displacement
2561 cxr_log( "Broken displacement!\n" );
2566 IL_MATCHED_DISP_INTERIOR_VERT
:;
2570 // Create brush vertices based on UV map
2572 // Create V reference based on first displacement.
2573 // TODO(harry): This is not the moststable selection method!
2574 // faces can come in any order, so the first disp will of course
2575 // always vary. Additionaly the triangle can be oriented differently.
2577 // Improvement can be made by selecting a first disp/triangle based
2578 // on deterministic factors.
2580 if( disp_count
== 0 )
2584 v3_copy( verts
[dispedge
[0]], tri_ref
[0] );
2585 v3_copy( verts
[dispedge
[4]], tri_ref
[1] );
2586 v3_copy( verts
[dispedge
[8]], tri_ref
[2] );
2587 cxr_calculate_axis( &tx
, tri_ref
, corner_uvs
, (v2f
){512,512} );
2589 v3_muls( tx
.vaxis
, -1.0, refv
);
2590 int v_cardinal
= cxr_cardinal( refv
, -1 );
2592 v3_cross( tx
.vaxis
, tx
.uaxis
, refn
);
2593 v3_muls( refn
, -tx
.winding
, refn
);
2595 int n1_cardinal
= cxr_cardinal( refn
, v_cardinal
);
2597 //v3_copy( avg_normal, refn );
2599 if( u_cardinal
== n1_cardinal
|| u_cardinal
== v_cardinal
) u_cardinal
++;
2600 if( u_cardinal
== n1_cardinal
|| u_cardinal
== v_cardinal
) u_cardinal
++;
2603 refu
[u_cardinal
] = tx
.uaxis
[u_cardinal
] > 0.0? 1.0: -1.0;
2607 v3_copy( face_center
, p0
);
2608 v3_muladds( face_center
, refn
, 1.5, pn
);
2609 v3_muladds( face_center
, refv
, 1.5, pv
);
2610 v3_muladds( face_center
, refu
, 1.5, pu
);
2612 if( cxr_settings
.debug
)
2614 cxr_debug_line( p0
, pn
, (v4f
){0.0,0.0,1.0,1.0});
2615 cxr_debug_line( p0
, pv
, (v4f
){0.0,1.0,0.0,1.0});
2616 cxr_debug_line( p0
, pu
, (v4f
){1.0,0.0,0.0,1.0});
2617 cxr_debug_line( tri_ref
[0], tri_ref
[1], (v4f
){1.0,1.0,1.0,1.0} );
2618 cxr_debug_line( tri_ref
[1], tri_ref
[2], (v4f
){1.0,1.0,1.0,1.0} );
2619 cxr_debug_line( tri_ref
[2], tri_ref
[0], (v4f
){1.0,1.0,1.0,1.0} );
2623 // Create world cordinates
2624 v3f world_corners
[8];
2627 for( int j
=0; j
<4; j
++ )
2630 v2_sub( corner_uvs
[j
], uv_center
, local_uv
);
2631 v2_copy( corner_uvs
[j
], world_uv
[j
] );
2632 v2_muls( local_uv
, sf
, local_uv
);
2634 v3_muls( refu
, local_uv
[0], world_corners
[j
] );
2635 v3_muladds( world_corners
[j
], refv
, local_uv
[1], world_corners
[j
] );
2636 v3_add( face_center
, world_corners
[j
], world_corners
[j
] );
2639 double *colour
= colours_random
[cxr_range(disp_count
,8)];
2641 for( int j
=0; j
<4; j
++ )
2642 v3_muladds( world_corners
[j
], refn
, -1.0, world_corners
[j
+4] );
2644 if( cxr_settings
.debug
)
2646 cxr_debug_arrow( world_corners
[0], world_corners
[1], avg_normal
, 0.1, colour
);
2647 cxr_debug_arrow( world_corners
[1], world_corners
[2], avg_normal
, 0.1, colour
);
2648 cxr_debug_arrow( world_corners
[2], world_corners
[3], avg_normal
, 0.1, colour
);
2649 cxr_debug_arrow( world_corners
[3], world_corners
[0], avg_normal
, 0.1, colour
);
2653 cxr_debug_arrow( world_corners[0+4], world_corners[1+4], avg_normal, 0.1, colour );
2654 cxr_debug_arrow( world_corners[1+4], world_corners[2+4], avg_normal, 0.1, colour );
2655 cxr_debug_arrow( world_corners[2+4], world_corners[3+4], avg_normal, 0.1, colour );
2656 cxr_debug_arrow( world_corners[3+4], world_corners[0+4], avg_normal, 0.1, colour );
2659 // Apply world transform
2660 for( int j
=0; j
<8; j
++ )
2662 v3_muls( world_corners
[j
], cxr_context
.scale_factor
, world_corners
[j
] );
2663 world_corners
[j
][2] += cxr_context
.offset_z
;
2666 cxr_texinfo texinfo_shared
;
2667 cxr_calculate_axis( &texinfo_shared
, world_corners
, world_uv
,
2668 (v2f
){ matptr
->res
[0], matptr
->res
[1] } );
2671 cxr_vdf_node( output
, "solid" );
2672 cxr_vdf_ki32( output
, "id", ++ cxr_context
.brush_count
);
2683 double distances
[25];
2685 v3f lside0
, lside1
, lref
, vdelta
, vworld
;
2688 for( int j
=0; j
<5; j
++ )
2690 ty
= (double)j
/(double)(5-1);
2692 v3_lerp( world_corners
[0], world_corners
[3], ty
, lside0
);
2693 v3_lerp( world_corners
[1], world_corners
[2], ty
, lside1
);
2695 for( int k
=0; k
<5; k
++ )
2699 tx
= (double)k
/(double)(5-1);
2700 v3_lerp( lside0
, lside1
, tx
, lref
);
2701 v3_muls( verts
[grid
[index
]], cxr_context
.scale_factor
, vworld
);
2702 vworld
[2] += cxr_context
.offset_z
;
2704 v3_sub( vworld
, lref
, vdelta
);
2705 v3_copy( vdelta
, normals
[index
] );
2706 v3_normalize( normals
[index
] );
2707 distances
[index
] = v3_dot( vdelta
, normals
[index
] );
2711 for( int j
=0; j
<6; j
++ )
2713 int *side
= sides
[j
];
2715 cxr_vdf_node( output
, "side" );
2716 cxr_vdf_ki32( output
, "id", ++ cxr_context
.face_count
);
2717 cxr_vdf_plane( output
, "plane", world_corners
[side
[2]],
2718 world_corners
[side
[1]],
2719 world_corners
[side
[0]] );
2721 cxr_vdf_kv( output
, "material", matptr
->vmt_path
);
2723 cxr_vdf_kaxis( output
, "uaxis",
2724 texinfo_shared
.uaxis
,
2725 texinfo_shared
.offset
[0],
2726 texinfo_shared
.scale
[0] );
2727 cxr_vdf_kaxis( output
, "vaxis",
2728 texinfo_shared
.vaxis
,
2729 texinfo_shared
.offset
[1],
2730 texinfo_shared
.scale
[1] );
2732 cxr_vdf_kdouble( output
, "rotation", 0.0 );
2733 cxr_vdf_ki32( output
, "lightmapscale", cxr_settings
.lightmap_scale
);
2734 cxr_vdf_ki32( output
, "smoothing_groups", 0 );
2738 cxr_vdf_node( output
, "dispinfo" );
2739 cxr_vdf_ki32( output
, "power", 2 );
2740 cxr_vdf_kv3f( output
, "startposition", world_corners
[0] );
2741 cxr_vdf_ki32( output
, "flags", 0 );
2742 cxr_vdf_kdouble( output
, "elevation", 0.0 );
2743 cxr_vdf_ki32( output
, "subdiv", 0 );
2745 cxr_vdf_node( output
, "normals" );
2746 for( int k
=0; k
<5; k
++ )
2747 cxr_vdf_karrv3f( output
, "row", k
, &normals
[k
*5], 5 );
2748 cxr_vdf_edon( output
);
2750 cxr_vdf_node( output
, "distances" );
2751 for( int k
=0; k
<5; k
++ )
2752 cxr_vdf_karrdouble( output
, "row", k
, &distances
[k
*5], 5 );
2753 cxr_vdf_edon( output
);
2755 // TODO: This might be needed for compiling...
2757 cxr_vdf_node( output, "offsets" );
2758 for( int k=0; k<5; k++ )
2759 cxr_vdf_printf( output, "\"row%d\" \"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\"\n", k );
2760 cxr_vdf_edon( output );
2762 cxr_vdf_node( output, "offset_normals" );
2763 for( int k=0; k<5; k++ )
2764 cxr_vdf_printf( output, "\"row%d\" \"0 0 1 0 0 1 0 0 1 0 0 1 0 0 1\"\n", k );
2765 cxr_vdf_edon( output );
2767 cxr_vdf_node( output, "alphas" );
2768 for( int k=0; k<5; k++ )
2769 cxr_vdf_printf( output, "\"row%d\" \"0 0 0 0 0\"\n", k );
2770 cxr_vdf_edon( output );
2772 cxr_vdf_node( output, "triangle_tags" );
2773 for( int k=0; k<5-1; k++ )
2774 cxr_vdf_printf( output, "\"row%d\" \"9 9 9 9 9 9 9 9\"\n", k );
2775 cxr_vdf_edon( output );
2777 cxr_vdf_node( output, "allowed_verts" );
2778 cxr_vdf_printf( output, "\"10\" \"-1 -1 -1 -1 -1 -1 -1 -1 -1 -1\"\n" );
2779 cxr_vdf_edon( output );
2781 cxr_vdf_edon( output
);
2784 cxr_vdf_edon( output
);
2787 cxr_vdf_node(output
, "editor");
2788 cxr_vdf_colour255(output
,"color", colours_random
[cxr_range(cxr_context
.brush_count
,8)]);
2789 cxr_vdf_ki32(output
,"visgroupshown",1);
2790 cxr_vdf_ki32(output
,"visgroupautoshown",1);
2791 cxr_vdf_edon(output
);
2793 cxr_vdf_edon( output
);
2803 static int cxr_solid_checkerr(cxr_mesh
*mesh
, cxr_auto_buffer
*abverts
)
2807 for( int i
=0; i
<mesh
->abpolys
.count
; i
++ )
2811 cxr_polygon
*poly
= &mesh
->polys
[i
];
2814 normal_to_plane( poly
->normal
, poly
->center
, plane
);
2816 for( int j
=0; j
<poly
->loop_total
; j
++ )
2818 cxr_loop
*loop
= &mesh
->loops
[ poly
->loop_start
+j
];
2819 double *vert
= cxr_ab_ptr(abverts
,loop
->index
);
2821 if( fabs(plane_polarity(plane
,vert
)) > 0.0025 )
2827 plane_project_point( plane
, vert
, ref
);
2829 cxr_debug_line( ref
, vert
, colour_error
);
2830 cxr_debug_box( vert
, 0.1, colour_error
);
2835 cxr_debug_poly( mesh
, poly
, colour_error
);
2841 CXR_API i32
cxr_convert_mesh_to_vmf(cxr_input_mesh
*src
, cxr_vdf
*output
)
2843 // Split mesh into islands
2844 cxr_auto_buffer abverts
;
2845 cxr_mesh
*main_mesh
= cxr_to_internal_format(src
, &abverts
);
2848 int invalid_count
= 0;
2853 int is_displacement
, invalid
;
2856 cxr_auto_buffer solids
;
2857 cxr_ab_init( &solids
, sizeof(struct solidinf
), 2 );
2859 // Preprocessor 1: Island seperation
2864 cxr_mesh
*res
= cxr_pull_island( main_mesh
);
2867 cxr_ab_push( &solids
, &(struct solidinf
){ res
, 0 });
2871 cxr_ab_push( &solids
, &(struct solidinf
){main_mesh
,0} );
2873 // Preprocessor 2: Displacement break-out and error checking
2875 for( int i
=0; i
<solids
.count
; i
++ )
2877 struct solidinf
*pinf
= cxr_ab_ptr(&solids
,i
);
2879 for( int j
=0; j
<pinf
->pmesh
->abpolys
.count
; j
++ )
2881 cxr_polygon
*poly
= cxr_ab_ptr( &pinf
->pmesh
->abpolys
, j
);
2883 for( int k
=0; k
<poly
->loop_total
; k
++ )
2885 cxr_loop
*lp
= cxr_ab_ptr( &pinf
->pmesh
->abloops
, poly
->loop_start
+k
);
2886 cxr_edge
*edge
= cxr_ab_ptr( &pinf
->pmesh
->abedges
, lp
->edge_index
);
2888 if( edge
->freestyle
)
2889 goto IL_SOLID_IS_DISPLACEMENT
;
2893 if( cxr_solid_checkerr( pinf
->pmesh
, &abverts
) )
2900 IL_SOLID_IS_DISPLACEMENT
:;
2902 pinf
->is_displacement
= 1;
2903 cxr_write_disp( pinf
->pmesh
, src
, output
);
2906 // Preprocessor 3: Breakup non-convex shapes into sub-solids
2908 int sources_count
= solids
.count
;
2910 for( int i
=0; i
<sources_count
; i
++ )
2912 struct solidinf pinf
= *(struct solidinf
*)cxr_ab_ptr(&solids
, i
);
2914 if( pinf
.is_displacement
|| pinf
.invalid
)
2919 cxr_mesh
*res
= cxr_pull_best_solid( pinf
.pmesh
, 0, &error
);
2923 cxr_ab_push( &solids
, &(struct solidinf
){res
,0} );
2931 // If no solids error we can rety while preserving 'hot' edges
2933 if( error
& CXR_ERROR_NO_SOLIDS
)
2936 res
= cxr_pull_best_solid(pinf
.pmesh
, 1, &error
);
2938 if( res
) cxr_ab_push( &solids
, &(struct solidinf
){res
,0} );
2952 if( cxr_settings
.debug
)
2954 for( int i
=0; i
<solids
.count
; i
++ )
2956 struct solidinf
*solid
= cxr_ab_ptr(&solids
,i
);
2958 if( !solid
->is_displacement
)
2959 cxr_debug_mesh( solid
->pmesh
, colours_random
[cxr_range(i
,8)] );
2965 for( int i
=0; i
<solids
.count
; i
++ )
2967 struct solidinf
*solid
= cxr_ab_ptr(&solids
,i
);
2968 cxr_free_mesh( solid
->pmesh
);
2971 cxr_ab_free( &abverts
);
2972 cxr_ab_free( &solids
);
2976 // Turn all those solids into VMF brushes
2977 // --------------------------------------
2978 for( int i
=0; i
<solids
.count
; i
++ )
2980 struct solidinf
*solid
= cxr_ab_ptr(&solids
,i
);
2982 if( solid
->is_displacement
) continue;
2984 cxr_vdf_node( output
, "solid" );
2985 cxr_vdf_ki32( output
, "id", ++ cxr_context
.brush_count
);
2987 for( int j
=0; j
<solid
->pmesh
->abpolys
.count
; j
++ )
2989 cxr_polygon
*poly
= cxr_ab_ptr( &solid
->pmesh
->abpolys
, j
);
2990 cxr_loop
*ploops
= cxr_ab_ptr(&solid
->pmesh
->abloops
, poly
->loop_start
);
2991 cxr_material
*matptr
=
2992 poly
->material_id
< 0 || src
->material_count
== 0?
2994 &src
->materials
[ poly
->material_id
];
2996 cxr_vdf_node( output
, "side" );
2997 cxr_vdf_ki32( output
, "id", ++ cxr_context
.face_count
);
2999 v3f verts
[3]; v2f uvs
[3];
3001 v3_muls( cxr_ab_ptr(&abverts
, ploops
[0].index
), cxr_context
.scale_factor
, verts
[0] );
3002 v3_muls( cxr_ab_ptr(&abverts
, ploops
[1].index
), cxr_context
.scale_factor
, verts
[1] );
3003 v3_muls( cxr_ab_ptr(&abverts
, ploops
[2].index
), cxr_context
.scale_factor
, verts
[2] );
3004 verts
[0][2] += cxr_context
.offset_z
;
3005 verts
[1][2] += cxr_context
.offset_z
;
3006 verts
[2][2] += cxr_context
.offset_z
;
3008 v2_copy( ploops
[0].uv
, uvs
[0] );
3009 v2_copy( ploops
[1].uv
, uvs
[1] );
3010 v2_copy( ploops
[2].uv
, uvs
[2] );
3012 cxr_vdf_plane( output
, "plane", verts
[2], verts
[1], verts
[0] );
3013 cxr_vdf_kv( output
, "material", matptr
->vmt_path
);
3016 cxr_calculate_axis(&trans
, verts
, uvs
, (double[2]){ matptr
->res
[0], matptr
->res
[1] });
3018 cxr_vdf_kaxis(output
, "uaxis", trans
.uaxis
, trans
.offset
[0], trans
.scale
[0]);
3019 cxr_vdf_kaxis(output
, "vaxis", trans
.vaxis
, trans
.offset
[1], trans
.scale
[1]);
3021 cxr_vdf_kdouble(output
, "rotation", 0.0 );
3022 cxr_vdf_ki32(output
, "lightmapscale", cxr_settings
.lightmap_scale
);
3023 cxr_vdf_ki32(output
, "smoothing_groups", 0);
3025 cxr_vdf_edon( output
);
3028 cxr_vdf_node(output
, "editor");
3029 cxr_vdf_colour255(output
,"color", colours_random
[cxr_range(cxr_context
.brush_count
,8)]);
3030 cxr_vdf_ki32(output
,"visgroupshown",1);
3031 cxr_vdf_ki32(output
,"visgroupautoshown",1);
3032 cxr_vdf_edon(output
);
3034 cxr_vdf_edon( output
);
3037 for( int i
=0; i
<solids
.count
; i
++ )
3039 struct solidinf
*solid
= cxr_ab_ptr(&solids
,i
);
3040 cxr_free_mesh( solid
->pmesh
);
3043 cxr_ab_free( &abverts
);
3044 cxr_ab_free( &solids
);
3049 CXR_API
void cxr_set_log_function( void (*func
)(const char *str
) )
3051 cxr_log_func
= func
;
3054 CXR_API
void cxr_set_line_function( void (*func
)(v3f p0
, v3f p1
, v4f colour
) )
3056 cxr_line_func
= func
;
3059 CXR_API
void cxr_settings_update( struct cxr_settings
*settings
)
3061 cxr_settings
= *settings
;
3064 // Valve copyright stuff probably maybe
3065 // whatever, my previous copyright decleration ends here
3066 // ----------------------------------------------------------
3068 #define HEADER_LUMPS 64
3069 #define LUMP_WORLDLIGHTS 54
3071 #pragma pack(push,1)
3080 int fileofs
, filelen
;
3085 lumps
[ HEADER_LUMPS
];
3095 float shadow_cast_offset
[3];
3103 float constant_attn
;
3105 float quadratic_attn
;
3112 // Utility for patching BSP tools to remove -1 distance lights (we set them
3113 // like that, because we want these lights to go away)
3115 // Yes, there is no way to do this in hammer
3116 // Yes, the distance KV is unused but still gets compiled to this lump
3117 // No, Entities only compile will not do this for you
3119 CXR_API
int cxr_lightpatch_bsp( const char *path
)
3121 printf( "Lightpatch: %s\n", path
);
3123 FILE *fp
= fopen( path
, "r+b" );
3127 cxr_log( "Could not open BSP file for editing (r+b)\n" );
3132 struct header header
;
3133 fread( &header
, sizeof(struct header
), 1, fp
);
3134 struct lump
*lump
= &header
.lumps
[ LUMP_WORLDLIGHTS
];
3136 // Read worldlight array
3137 struct worldlight
*lights
= malloc( lump
->filelen
);
3138 fseek( fp
, lump
->fileofs
, SEEK_SET
);
3139 fread( lights
, lump
->filelen
, 1, fp
);
3141 // Remove all marked lights
3142 int light_count
= lump
->filelen
/ sizeof(struct worldlight
);
3145 for( int i
= 0; i
< light_count
; i
++ )
3146 if( lights
[i
].radius
>= 0.0f
)
3147 lights
[new_count
++] = lights
[i
];
3149 lump
->filelen
= new_count
*sizeof(struct worldlight
);
3152 fseek( fp
, lump
->fileofs
, SEEK_SET
);
3153 fwrite( lights
, lump
->filelen
, 1, fp
);
3154 fseek( fp
, 0, SEEK_SET
);
3155 fwrite( &header
, sizeof(struct header
), 1, fp
);
3156 cxr_log( "removed %d marked lights\n", light_count
-new_count
);