4 #define SOLID_MAX_SIDES 512
6 typedef struct vmf_solid vmf_solid
;
7 typedef struct vmf_vert vmf_vert
;
8 typedef struct vmf_mat vmf_mat
;
9 typedef struct vmf_face vmf_face
;
11 typedef enum ESolidResult ESolidResult
;
16 k_ESolidResult_maxsides
,
17 k_ESolidResult_invalid
,
18 k_ESolidResult_errnomem
,
19 k_ESolidResult_corrupt
,
20 k_ESolidResult_degenerate
54 void solidgen_ctx_init( vmf_solid
*ctx
)
56 const u32 init_size
= 128;
58 ctx
->verts
= csr_sb_reserve( NULL
, init_size
, sizeof(vmf_vert
) );
59 ctx
->indices
= csr_sb_reserve( NULL
, init_size
, sizeof(u32
) );
62 void solidgen_ctx_free( vmf_solid
*ctx
)
64 csr_sb_free( ctx
->verts
);
65 csr_sb_free( ctx
->indices
);
68 // Compute bounds of solid gen ctx
69 void solidgen_bounds( vmf_solid
*ctx
, u32 start
, u32 end
, v3f min
, v3f max
)
71 v3f mine
= { INFINITY
, INFINITY
, INFINITY
};
72 v3f maxe
= {-INFINITY
,-INFINITY
,-INFINITY
};
74 for( int i
= start
; i
< end
; i
++ )
76 vmf_vert
*vert
= ctx
->verts
+ i
;
79 mine
[0] = fminf( mine
[0], co
[0] );
80 mine
[1] = fminf( mine
[1], co
[1] );
81 mine
[2] = fminf( mine
[2], co
[2] );
83 maxe
[0] = fmaxf( maxe
[0], co
[0] );
84 maxe
[1] = fmaxf( maxe
[1], co
[1] );
85 maxe
[2] = fmaxf( maxe
[2], co
[2] );
96 double planes
[ SOLID_MAX_SIDES
*4 ];
101 // put an extra plane into the planes list
102 void vmf_addbisector( double p
[4] )
104 double *plane
= vmf_api
.planes
+ vmf_api
.bisectors
* 4;
111 vmf_api
.bisectors
++;
114 void vmf_clearbisectors( void )
116 vmf_api
.bisectors
= 0;
119 void vmf_ignore_mat( const char *material
)
121 vmf_api
.blacklist
= csr_sb_reserve( vmf_api
.blacklist
, 1, sizeof( vmf_mat
) );
122 vmf_mat
*mat
= (vmf_mat
*)csr_sb_use( vmf_api
.blacklist
);
124 mat
->str
= csr_malloc( strlen( material
) + 1 );
125 strcpy( mat
->str
, material
);
127 mat
->hash
= djb2( ( const unsigned char * )material
);
130 void vmf_clearignore( void )
132 for( int i
= 0; i
< csr_sb_count( vmf_api
.blacklist
); i
++ )
134 free( vmf_api
.blacklist
[ i
].str
);
137 csr_sb_free( vmf_api
.blacklist
);
138 vmf_api
.blacklist
= NULL
;
141 int mat_blacklisted( const char *material
)
143 u32 hash
= djb2((const u8
*)material
);
145 for( int j
= 0; j
< csr_sb_count( vmf_api
.blacklist
); j
++ )
147 if( vmf_api
.blacklist
[ j
].hash
== hash
)
149 if( !strcmp( material
, vmf_api
.blacklist
[ j
].str
) )
159 void sort_coplanar( double p
[4], vmf_vert
*points
, u32
*indices
, u32 count
)
161 v3f center
= {0.f
, 0.f
, 0.f
};
164 v3_normalize( norm
);
166 for( int i
= 0; i
< count
; i
++ )
168 v3_add( points
[ indices
[i
] ].co
, center
, center
);
170 v3_divs( center
, count
, center
);
173 v3_sub( points
[ indices
[0] ].co
, center
, ref
);
175 // Calc angles compared to ref
176 float *angles
= (float*)alloca( sizeof(float)*count
);
177 for( int i
= 0; i
< count
; i
++ )
182 v3_sub( points
[ indices
[i
] ].co
, center
, diff
);
183 v3_cross( diff
, ref
, c
);
186 atan2f( v3_length(c
), v3_dot( diff
, ref
) )
187 * (v3_dot( c
, norm
) < 0.f
? -1.f
: 1.f
);
190 // Temporary local indexes
191 u32
*temp_indices
= (u32
*)alloca( sizeof(u32
)*count
);
192 for( u32 i
= 0; i
< count
; i
++ ) temp_indices
[i
] = i
;
194 // Slow sort on large vertex counts
199 for( int i
= 0; i
< count
-1; i
++ )
201 int s0
= i
; int s1
= i
+ 1;
203 if( angles
[temp_indices
[s0
]] > angles
[temp_indices
[s1
]] )
205 // swap indices and mirror on local
206 u32 temp
= indices
[s1
];
207 indices
[s1
] = indices
[s0
];
210 temp
= temp_indices
[s1
];
211 temp_indices
[s1
] = temp_indices
[s0
];
212 temp_indices
[s0
] = temp
;
219 if( !modified
) break;
223 int solid_has_displacement( vdf_node
*node
)
228 while( (pSide
= vdf_next(node
, "side", &it
)) )
230 if( vdf_next( pSide
, "dispinfo", NULL
) )
238 void solid_disp_tri( vmf_solid
*ctx
, u32 a
, u32 b
, u32 c
)
240 *((u32
*)csr_sb_use( ctx
->indices
)) = a
;
241 *((u32
*)csr_sb_use( ctx
->indices
)) = b
;
242 *((u32
*)csr_sb_use( ctx
->indices
)) = c
;
245 void face_add_indice( vmf_face
*f
, u32 idx
)
247 f
->indices
= csr_sb_reserve( f
->indices
, 1, sizeof( u32
) );
248 *((u32
*)csr_sb_use( f
->indices
)) = idx
;
251 ESolidResult
solidgen_push( vmf_solid
*ctx
, vdf_node
*node
)
253 ESolidResult flag
= k_ESolidResult_valid
;
255 vmf_face faces
[ SOLID_MAX_SIDES
];
257 int is_displacement
= 0;
260 // TODO: What is this for again? surely it should be the other way around... i think...
261 if( solid_has_displacement( node
) )
263 printf( "solid_has_displacement\n" );
264 num_planes
= vmf_api
.bisectors
;
266 // Add dummy stuff for globals
268 for( int k
= 0; k
< vmf_api
.bisectors
; k
++ )
270 vmf_face
*dummy
= faces
+ k
;
271 dummy
->indices
= NULL
;
272 dummy
->dispinfo
= NULL
;
273 dummy
->material
= NULL
;
281 while( (pSide
= vdf_next(node
, "side", &it
)) )
283 if( num_planes
>= SOLID_MAX_SIDES
)
285 flag
= k_ESolidResult_maxsides
;
286 fprintf( stderr
, "Solid over maxsides limit (%i)\n", SOLID_MAX_SIDES
);
292 vmf_face
*face
= faces
+ num_planes
;
293 face
->indices
= NULL
;
294 face
->dispinfo
= vdf_next( pSide
, "dispinfo", NULL
);
295 face
->material
= kv_get( pSide
, "material", "" );
296 face
->blacklisted
= mat_blacklisted( face
->material
);
298 kv_double_array( pSide
, "plane", 9, points
);
300 tri_to_plane( points
+6, points
+3, points
+0, vmf_api
.planes
+ num_planes
* 4 );
304 // Compute plane intersections
306 csr_comb_init( 3, i
);
308 v3f center
= { 0.f
, 0.f
, 0.f
};
310 u32 vert_start
= csr_sb_count( ctx
->verts
);
314 // DO something with i j k
317 if( (faces
[ i
[0] ].blacklisted
&& faces
[ i
[1] ].blacklisted
&& faces
[ i
[2] ].blacklisted
) )
320 if( !plane_intersect( vmf_api
.planes
+i
[0]*4, vmf_api
.planes
+i
[1]*4, vmf_api
.planes
+i
[2]*4, p
) )
323 // Check for illegal verts (eg: got clipped by bisectors)
325 for( int m
= 0; m
< num_planes
; m
++ )
327 if( plane_polarity( vmf_api
.planes
+m
*4, p
) > 1e-6f
)
336 ctx
->verts
= csr_sb_reserve( ctx
->verts
, 3, sizeof( vmf_vert
) );
338 // Take the vertex position and add it for centering base on average
340 v3_add( (v3f
){ p
[0], p
[1], p
[2] }, center
, center
);
342 // Store point / respecive normal for each plane that triggered the collision
343 for( int k
= 0; k
< 3; k
++ )
345 if( !faces
[ i
[k
] ].blacklisted
)
347 u32 c
= csr_sb_count( ctx
->verts
);
349 face_add_indice( faces
+ i
[k
], c
);
351 v3d_v3f( p
, ctx
->verts
[ c
].co
);
352 v3d_v3f( vmf_api
.planes
+i
[k
]*4, ctx
->verts
[ c
].nrm
);
354 csr_sb_inc( ctx
->verts
, 1 );
359 while( csr_comb( 3, num_planes
, i
) );
361 // Retrospectively set the center for each point
362 v3_divs( center
, (float)numpoints
, center
);
363 for( ; vert_start
< csr_sb_count( ctx
->verts
); vert_start
++ )
365 v2_copy( center
, ctx
->verts
[ vert_start
].xy
);
368 // Sort each faces and trianglulalate them
369 for( int k
= vmf_api
.bisectors
; k
< num_planes
; k
++ )
371 vmf_face
*face
= faces
+ k
;
373 if( face
->blacklisted
) continue;
375 if( csr_sb_count( face
->indices
) < 3 )
377 if( !vmf_api
.bisectors
)
379 flag
= k_ESolidResult_degenerate
;
380 fprintf( stderr
, "Skipping degenerate face\n" );
385 // Sort only if there is no displacements, or if this side is
386 if( !is_displacement
|| ( is_displacement
&& face
->dispinfo
) )
388 sort_coplanar( vmf_api
.planes
+k
*4, ctx
->verts
, face
->indices
, csr_sb_count( face
->indices
) );
391 if( is_displacement
)
393 // Compute displacement
396 if( csr_sb_count( face
->indices
) != 4 )
398 // Mute error if we have global planes cause they
399 // are of course gonna fuck things up here
400 if( !vmf_api
.bisectors
)
402 flag
= k_ESolidResult_degenerate
;
403 fprintf( stderr
, "Skipping degenerate displacement\n" );
408 // Match starting position
411 float dmin
= 999999.f
;
413 vdf_node
*dispinfo
= face
->dispinfo
;
414 vdf_node
*vdf_normals
= vdf_next( dispinfo
, "normals", NULL
);
415 vdf_node
*vdf_distances
= vdf_next( dispinfo
, "distances", NULL
);
417 kv_float_array( dispinfo
, "startposition", 3, start
);
419 for( int j
= 0; j
< csr_sb_count( face
->indices
); j
++ )
421 float d2
= v3_dist2( start
, ctx
->verts
[ face
->indices
[ j
] ].co
);
429 // Get corners of displacement
430 float *SW
= ctx
->verts
[ face
->indices
[ sw
] ].co
;
431 float *NW
= ctx
->verts
[ face
->indices
[ (sw
+1) % 4] ].co
;
432 float *NE
= ctx
->verts
[ face
->indices
[ (sw
+2) % 4] ].co
;
433 float *SE
= ctx
->verts
[ face
->indices
[ (sw
+3) % 4] ].co
;
435 // Can be either 5, 9, 17
436 numpoints
= pow( 2, kv_get_int( dispinfo
, "power", 2 ) ) + 1;
437 u32 reqverts
= numpoints
*numpoints
;
438 u32 reqidx
= (numpoints
-1)*(numpoints
-1)*6;
440 ctx
->verts
= csr_sb_reserve( ctx
->verts
, reqverts
, sizeof( vmf_vert
) );
441 ctx
->indices
= csr_sb_reserve( ctx
->indices
, reqidx
, sizeof( u32
) );
443 float normals
[ 17*3 ];
444 float distances
[ 17 ];
446 // Calculate displacement positions
447 for( int j
= 0; j
< numpoints
; j
++ )
450 sprintf( key
, "row%i", j
);
452 kv_float_array( vdf_normals
, key
, 17*3, normals
);
453 kv_float_array( vdf_distances
, key
, 17, distances
);
455 float dx
= (float)j
/ (float)(numpoints
- 1); //Time values for linear interpolation
457 for( int m
= 0; m
< numpoints
; m
++ )
459 vmf_vert
*vert
= &ctx
->verts
[ csr_sb_count( ctx
->verts
) + j
*numpoints
+ m
];
461 float dy
= (float)m
/ (float)(numpoints
- 1);
465 v3_lerp( SW
, SE
, dx
, lwr
);
466 v3_lerp( NW
, NE
, dx
, upr
);
467 v3_lerp( lwr
, upr
, dy
, vert
->co
);
469 v3_muladds( vert
->co
, normals
+ m
* 3, distances
[ m
], vert
->co
);
471 // Todo, put correct normal
472 v3_copy( (v3f
){ 0.f
, 0.f
, 1.f
}, vert
->nrm
);
476 // Build displacement indices
478 for( int row
= 0; row
< numpoints
- 1; row
++ )
480 for( int col
= 0; col
< numpoints
- 1; col
++ )
482 u32 c
= csr_sb_count( ctx
->verts
);
484 u32 idxsw
= c
+ ( row
+ 0 ) * numpoints
+ col
+ 0 ;
485 u32 idxse
= c
+ ( row
+ 0 ) * numpoints
+ col
+ 1 ;
486 u32 idxnw
= c
+ ( row
+ 1 ) * numpoints
+ col
+ 0 ;
487 u32 idxne
= c
+ ( row
+ 1 ) * numpoints
+ col
+ 1 ;
489 if( (condition
++) % 2 == 0 )
491 solid_disp_tri( ctx
, idxne
, idxnw
, idxsw
);
492 solid_disp_tri( ctx
, idxse
, idxne
, idxsw
);
496 solid_disp_tri( ctx
, idxse
, idxnw
, idxsw
);
497 solid_disp_tri( ctx
, idxse
, idxne
, idxnw
);
503 csr_sb_inc( ctx
->verts
, numpoints
*numpoints
);
508 u32 tris
= csr_sb_count( face
->indices
) -2;
509 ctx
->indices
= csr_sb_reserve( ctx
->indices
, tris
*3, sizeof( u32
) );
511 u32 c
= csr_sb_count( ctx
->indices
);
513 for( int j
= 0; j
< tris
; j
++ )
515 ctx
->indices
[ c
+j
*3 +0 ] = face
->indices
[ 0 ];
516 ctx
->indices
[ c
+j
*3 +1 ] = face
->indices
[ j
+1 ];
517 ctx
->indices
[ c
+j
*3 +2 ] = face
->indices
[ j
+2 ];
524 csr_sb_inc( ctx
->indices
, tris
*3 );
528 // Free temp polyon buffers
529 for( int j
= 0; j
< num_planes
; j
++ )
531 csr_sb_free( faces
[ j
].indices
);
537 void solidgen_to_obj( vmf_solid
*ctx
, const char *path
)
539 FILE *fp
= fopen( path
, "w" );
543 fprintf( fp
, "o vmf_export\n" );
547 // Write vertex block
548 for( int i
= 0; i
< csr_sb_count( ctx
->verts
); i
++ )
550 vert
= &ctx
->verts
[i
];
551 fprintf( fp
, "v %f %f %f\n", vert
->co
[0], vert
->co
[1], vert
->co
[2] );
554 // Write normals block
555 for( int i
= 0; i
< csr_sb_count( ctx
->verts
); i
++ )
557 vert
= &ctx
->verts
[i
];
558 fprintf( fp
, "vn %f %f %f\n", vert
->nrm
[0], vert
->nrm
[1], vert
->nrm
[2] );
561 fprintf( fp
, "s off\n" );
564 for( int i
= 0; i
< csr_sb_count( ctx
->indices
)/3; i
++ )
566 u32
* base
= ctx
->indices
+ i
*3;
567 fprintf( fp
, "f %u//%u %u//%u %u//%u\n",
568 base
[2]+1, base
[2]+1,
569 base
[1]+1, base
[1]+1,
578 fprintf( stderr
, "Could not open %s for writing\n", path
);