# Public API
libcxr_decompose = extern( "cxr_decompose", \
- [POINTER(cxr_static_mesh)], c_void_p )
+ [POINTER(cxr_static_mesh), POINTER(c_int32)], c_void_p )
libcxr_free_world = extern( "cxr_free_world", [c_void_p], None )
libcxr_write_test_data = extern( "cxr_write_test_data", \
debug_gpu_shader.bind()
- gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.depth_mask_set(False)
gpu.state.line_width_set(1.5)
gpu.state.face_culling_set('BACK')
+ gpu.state.depth_test_set('NONE')
gpu.state.blend_set('ALPHA')
if debug_gpu_lines != None:
debug_gpu_lines.draw(debug_gpu_shader)
+ gpu.state.depth_test_set('LESS_EQUAL')
gpu.state.blend_set('ADDITIVE')
if debug_gpu_mesh != None:
debug_gpu_mesh.draw(debug_gpu_shader)
return info
def mesh_cxr_format(obj):
+ orig_state = obj.mode
+ if orig_state != 'OBJECT':
+ bpy.ops.object.mode_set(mode='OBJECT')
+
dgraph = bpy.context.evaluated_depsgraph_get()
data = obj.evaluated_get(dgraph).data
mesh.loop_count = len(data.loops)
mesh.material_count = len(obj.material_slots)
+ bpy.ops.object.mode_set(mode=orig_state)
return mesh
class CXR_WRITE_VMF(bpy.types.Operator):
os.makedirs( model_dir, exist_ok=True )
# States
+ libcxr_reset_debug_lines()
material_info.references = set()
-
output_vmf = F"{directory}/{settings.project_name}.vmf"
with vdf_structure(output_vmf) as m:
nonlocal m
baked = mesh_cxr_format( brush[0] )
- world = libcxr_decompose.call( baked )
+ world = libcxr_decompose.call( baked, None )
if world == None:
return False
# World geometry
for brush in _collect.geo:
if not _buildsolid( brush[0], brush[1] ):
- print( "error" )
+ libcxr_batch_debug_lines()
+ scene_redraw()
return {'CANCELLED'}
m.edon()
else: m.kv( kv[0], str(kv[2]) )
if not _buildsolid( obj, ctx ):
+ libcxr_batch_debug_lines()
+ scene_redraw()
return {'CANCELLED'}
m.edon()
class CXR_PREVIEW_OPERATOR(bpy.types.Operator):
bl_idname="convexer.preview"
- bl_label="Preview"
+ bl_label="Preview Brushes"
+
+ LASTERR = None
+ RUNNING = False
def execute(_,context):
- libcxr_use()
+ return {'FINISHED'}
+
+ def modal(_,context,event):
+ global debug_gpu_mesh
+ static = _.__class__
+
+ if event.type == 'ESC':
+ libcxr_reset_debug_lines()
+ libcxr_batch_debug_lines()
+ debug_gpu_mesh = None
+ scene_redraw()
+
+ static.RUNNING = False
+ return {'FINISHED'}
+ return {'PASS_THROUGH'}
+
+ def invoke(_,context,event):
+ global debug_gpu_shader, debug_gpu_mesh
+ static = _.__class__
+ static.LASTERR = None
+
+ libcxr_use()
libcxr_reset_debug_lines()
mesh_src = mesh_cxr_format(context.active_object)
- world = libcxr_decompose.call( mesh_src )
-
- global debug_gpu_shader, debug_gpu_mesh
+
+ err = c_int32(0)
+ world = libcxr_decompose.call( mesh_src, pointer(err) )
if world == None:
debug_gpu_mesh = None
libcxr_batch_debug_lines()
- return {'CANCELLED'}
+ scene_redraw()
+
+ static.LASTERR = ["There is no error", \
+ "Non-Manifold",\
+ "Bad-Manifold",\
+ "No-Candidate",\
+ "Internal-Fail",\
+ "Non-Coplanar",\
+ "Non-Convex Polygon"]\
+ [err.value]
+
+ if static.RUNNING:
+ return {'CANCELLED'}
+ else:
+ context.window_manager.modal_handler_add(_)
+ return {'RUNNING_MODAL'}
ptrpreview = libcxr_world_preview.call( world )
preview = ptrpreview[0]
libcxr_free_tri_mesh.call( ptrpreview )
libcxr_free_world.call( world )
-
libcxr_batch_debug_lines()
scene_redraw()
- return {'FINISHED'}
+
+ if static.RUNNING:
+ return {'CANCELLED'}
+ if not static.RUNNING:
+ static.RUNNING = True
+ context.window_manager.modal_handler_add(_)
+ return {'RUNNING_MODAL'}
+
+class CXR_VIEW3D( bpy.types.Panel ):
+ bl_idname = "VIEW3D_PT_convexer"
+ bl_label = "Convexer"
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'UI'
+ bl_category = "Convexer"
+
+ @classmethod
+ def poll(cls, context):
+ return (context.object is not None)
+
+ def draw(_, context):
+ layout = _.layout
+ row = layout.row()
+ row.scale_y = 2
+ row.operator("convexer.preview")
+
+ if CXR_PREVIEW_OPERATOR.LASTERR != None:
+ box = layout.box()
+ box.label(text=CXR_PREVIEW_OPERATOR.LASTERR, icon='ERROR')
class CXR_INTERFACE(bpy.types.Panel):
bl_label="Convexer"
CXR_WRITE_VMF, CXR_MATERIAL_PANEL, CXR_IMAGE_SETTINGS,\
CXR_MODEL_SETTINGS, CXR_ENTITY_SETTINGS, CXR_CUBEMAP_SETTINGS,\
CXR_LIGHT_SETTINGS, CXR_SCENE_SETTINGS, CXR_DETECT_COMPILERS,\
- CXR_ENTITY_PANEL, CXR_LIGHT_PANEL, CXR_PREVIEW_OPERATOR ]
+ CXR_ENTITY_PANEL, CXR_LIGHT_PANEL, CXR_PREVIEW_OPERATOR,\
+ CXR_VIEW3D ]
def register():
global debug_draw_handler, vmt_param_dynamic_class
/* Main convexer algorithms */
/* Convex decomp from mesh */
-CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src );
+CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode );
CXR_API void cxr_free_world( cxr_world *world );
CXR_API cxr_tri_mesh *cxr_world_preview( cxr_world *world );
CXR_API void cxr_free_tri_mesh( cxr_tri_mesh *mesh );
k_soliderr_non_manifold,
k_soliderr_bad_manifold,
k_soliderr_no_solids,
- k_soliderr_degenerate_implicit
+ k_soliderr_degenerate_implicit,
+ k_soliderr_non_coplanar_vertices,
+ k_soliderr_non_convex_poly
};
/*
#ifdef CXR_DEBUG
cxr_log( "non-manifold edges are in the mesh: "
"implicit internal geometry does not have full support\n" );
+
+ v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
+
+ for( int i=0; i<mesh->abloops.count; i++ )
+ {
+ cxr_loop *lp = &mesh->loops[i];
+
+ if( lp->poly_left == -1 || lp->poly_right == -1 )
+ {
+ cxr_edge *edge = &mesh->edges[lp->edge_index];
+ cxr_debug_line( verts[edge->i0], verts[edge->i1], colour_error );
+ }
+ }
#endif
*err = k_soliderr_non_manifold;
return NULL;
return mesh;
}
+static int cxr_poly_convex( cxr_mesh *mesh, cxr_polygon *poly )
+{
+ v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
+
+ for( int i=0; i<poly->loop_total; i++ )
+ {
+ int li0 = poly->loop_start + i,
+ li1 = poly->loop_start + cxr_range( i+1, poly->loop_total ),
+ li2 = poly->loop_start + cxr_range( i+2, poly->loop_total );
+ int i0 = mesh->loops[li0].index,
+ i1 = mesh->loops[li1].index,
+ i2 = mesh->loops[li2].index;
+
+ v3f v0, v1, c;
+
+ v3_sub( verts[i1], verts[i0], v0 );
+ v3_sub( verts[i2], verts[i1], v1 );
+
+ v3_cross( v0, v1, c );
+ if( v3_dot( c, poly->normal ) <= 0.0 )
+ {
+#if CXR_DEBUG
+ cxr_debug_line( verts[i0], verts[i1], colour_error );
+ cxr_debug_box( verts[i1], 0.1, colour_error );
+ cxr_debug_line( verts[i1], verts[i2], colour_error );
+ cxr_debug_line( verts[i1], poly->center, colour_error );
+#endif
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
static int cxr_solid_checkerr( cxr_mesh *mesh )
{
v3f *verts = cxr_ab_ptr( mesh->p_abverts, 0 );
free( mesh );
}
-CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
+CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src, i32 *perrcode )
{
+ u32 error = 0x00;
cxr_world *world = malloc( sizeof(*world) );
/* Copy data to internal formats */
if( edge->freestyle )
goto displacement;
}
+
+ if( !cxr_poly_convex( pinf->pmesh, poly ) )
+ {
+ pinf->invalid = 1;
+ invalid_count ++;
+ error = k_soliderr_non_convex_poly;
+ }
}
if( cxr_solid_checkerr( pinf->pmesh ) )
{
pinf->invalid = 1;
invalid_count ++;
+ error = k_soliderr_non_coplanar_vertices;
}
continue;
* Main convex decomp algorithm
*/
int sources_count = world->absolids.count;
- u32 error = 0x00;
if( invalid_count )
goto decomp_failed;
decomp_failed:
cxr_log( "Error %d\n", error );
cxr_free_world( world );
+
+ if( perrcode )
+ *perrcode = error;
+
return NULL;
}