python interface revision
authorhgn <hgodden00@gmail.com>
Tue, 12 Apr 2022 06:06:18 +0000 (07:06 +0100)
committerhgn <hgodden00@gmail.com>
Tue, 12 Apr 2022 06:06:18 +0000 (07:06 +0100)
__init__.py
cxr/cxr.h

index 544d770b19433a22b846762c0bf2ac2afe7ca5ba..fc20f056977ebaa93d2d186f258cf311167a64ca 100644 (file)
@@ -105,7 +105,7 @@ class cxr_vmf_context(Structure):
 
 # 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", \
@@ -250,15 +250,16 @@ def cxr_draw():
 
    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)
@@ -813,6 +814,10 @@ def material_info(mat):
    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
    
@@ -885,6 +890,7 @@ def mesh_cxr_format(obj):
    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):
@@ -909,8 +915,8 @@ 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:
@@ -995,7 +1001,7 @@ class CXR_WRITE_VMF(bpy.types.Operator):
             nonlocal m
 
             baked = mesh_cxr_format( brush[0] )
-            world = libcxr_decompose.call( baked )
+            world = libcxr_decompose.call( baked, None )
             
             if world == None:
                return False
@@ -1013,7 +1019,8 @@ class CXR_WRITE_VMF(bpy.types.Operator):
          # 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()
@@ -1034,6 +1041,8 @@ class CXR_WRITE_VMF(bpy.types.Operator):
                else: m.kv( kv[0], str(kv[2]) )
 
             if not _buildsolid( obj, ctx ):
+               libcxr_batch_debug_lines()
+               scene_redraw()
                return {'CANCELLED'}
 
             m.edon()
@@ -1069,22 +1078,61 @@ class CXR_DEV_OPERATOR(bpy.types.Operator):
 
 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]
@@ -1107,10 +1155,36 @@ class CXR_PREVIEW_OPERATOR(bpy.types.Operator):
 
       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"
@@ -1587,7 +1661,8 @@ classes = [ CXR_RELOAD, CXR_DEV_OPERATOR, CXR_INTERFACE, \
             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
index 5a4542af6d1a3f4055233c6611630e13b771e10b..bb904c4d00ba8ce0522935f03787d0145e11c4e8 100644 (file)
--- a/cxr/cxr.h
+++ b/cxr/cxr.h
@@ -107,7 +107,7 @@ typedef struct cxr_tri_mesh cxr_tri_mesh;
 
 /* 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 );
@@ -276,7 +276,9 @@ enum cxr_soliderr
    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
 };
 
 /*
@@ -1556,6 +1558,19 @@ static cxr_mesh *cxr_pull_best_solid(
 #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;
@@ -1948,6 +1963,40 @@ static cxr_mesh *cxr_to_internal_format(
    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 );
@@ -2113,8 +2162,9 @@ CXR_API void cxr_free_tri_mesh( cxr_tri_mesh *mesh )
    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 */
@@ -2164,12 +2214,20 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
             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;
@@ -2182,7 +2240,6 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
     * Main convex decomp algorithm
     */
    int sources_count = world->absolids.count;
-   u32 error = 0x00;
    
    if( invalid_count )
       goto decomp_failed;
@@ -2228,6 +2285,10 @@ CXR_API cxr_world *cxr_decompose( cxr_static_mesh *src )
 decomp_failed:
    cxr_log( "Error %d\n", error );
    cxr_free_world( world );
+
+   if( perrcode )
+      *perrcode = error;
+
    return NULL;
 }