diff --git a/docs/user/face_orientation.png b/docs/user/face_orientation.png new file mode 100644 index 0000000..a5c9caa Binary files /dev/null and b/docs/user/face_orientation.png differ diff --git a/docs/user/flip_face.png b/docs/user/flip_face.png new file mode 100644 index 0000000..9fcf837 Binary files /dev/null and b/docs/user/flip_face.png differ diff --git a/docs/user/rename_node_1.png b/docs/user/jbeam_ui_1.png similarity index 100% rename from docs/user/rename_node_1.png rename to docs/user/jbeam_ui_1.png diff --git a/docs/user/jbeam_ui_2.png b/docs/user/jbeam_ui_2.png new file mode 100644 index 0000000..dd7739f Binary files /dev/null and b/docs/user/jbeam_ui_2.png differ diff --git a/docs/user/rename_node.png b/docs/user/rename_node.png new file mode 100644 index 0000000..7e5cffa Binary files /dev/null and b/docs/user/rename_node.png differ diff --git a/docs/user/rename_node_2.png b/docs/user/rename_node_2.png deleted file mode 100644 index 61a1346..0000000 Binary files a/docs/user/rename_node_2.png and /dev/null differ diff --git a/docs/user/user_docs.md b/docs/user/user_docs.md index 69d9a73..009028f 100644 --- a/docs/user/user_docs.md +++ b/docs/user/user_docs.md @@ -87,13 +87,33 @@ With the JBeam imported, you should see Blender meshes that represent the JBeam ![](usage_vehicle_selected.png) -One more step before moving on is to add a *Text Editor* view. The *Text Editor* allows you to view the changes to the JBeam file after modifying the Blender mesh in realtime and also allows you to modify it to view the changes to the Blender mesh in realtime as well! +
+ +#### Blender Text Editor + +Blender's *Text Editor* allows you to view the changes to the JBeam file after modifying the Blender mesh in realtime and also allows you to modify it to view the changes to the Blender mesh in realtime as well! Open it by placing your cursor on the top right hand corner of the viewport until you see your cursor turn into a '+' symbol and then LMB drag to the left and you should see a 2nd viewport. And then click on the top left button in that viewport that looks like a "grid and a circle" and click on *Text Editor*. Afterwards the text editor may appear blank. Just click on the JBeam part and the text editor should open the JBeam file that the part originates from. ![](opening_text_editor.gif) -We are now ready to start editing the JBeam parts! For the most part, if you know how to use Blender, editing a JBeam part is just like editing a regular mesh. For example, moving a node is exactly the same as moving a regular vertex. The same applies for adding and deleting a node as well, although renaming a node is something unique to JBeam. +
+ +#### JBeam UI + +The *JBeam UI* allows you to see the properties of a selected JBeam object. + +1. On the right hand side of the 3D Viewport next to the 3D axes arrows, click on the little arrow pointing to the left. + + ![](jbeam_ui_1.png) + +2. Several tabs will pop out such as *Item*, *Tool*, *View*, *JBeam*. Click on the *JBeam* tab and you will be greeted to a UI that shows you the JBeam part selected and its properties. + + ![](jbeam_ui_2.png) + +
+ +We are now ready to start editing JBeam parts! For the most part, if you know how to use Blender, editing a JBeam part is just like editing a regular mesh. For example, moving a node is exactly the same as moving a regular vertex. The same applies for adding and deleting a node as well, although renaming a node is something unique to JBeam. The rest of the instructions below will be using the *Square Donut* JBeam part and please have the JBeam part selected and be in *Edit Mode*. @@ -101,7 +121,7 @@ The rest of the instructions below will be using the *Square Donut* JBeam part a
-#### Undoing/Redoing +#### Undoing/Redoing (VERY IMPORTANT!) Please use **Ctrl + [ and Ctrl + ]** to undo and redo your changes. **DO NOT** use Ctrl + Z and Ctrl + Shift + Z as Blender's undo system doesn't work well with this plugin and can break things.
@@ -119,16 +139,19 @@ That wasn't too bad right? ;)
#### Renaming a Node -Renaming a node is something unique to JBeam but is easy to do :) +Renaming a node is something unique to JBeam but is easy to do :) FYI, if you want references to the node you are renaming (e.g. beams, tris, quads) to also be renamed, enable *Affect Node References* under the *Settings* panel. But with this enabled, renaming will be slow, especially with full vehicles. 1. Click on the node you want to rename. -2. Then, on the right hand side of the 3D Viewport right next to the 3D axes arrows, click on the little arrow pointing to the left. +2. In the *JBeam* UI, in the *JBeam Node ID* input box, rename the node to whatever you'd like and press enter. I renamed the node to "hey". You will see that the rename is also reflected in the node name labels on the part. - ![](rename_node_1.png) + ![](rename_node.png) -3. Several tabs will pop out such as *Item*, *Tool*, *View*, *JBeam*. Click on the *JBeam* tab and you will be greeted to a UI that shows you the JBeam part selected and allows you to rename the selected node. Rename the node to whatever you'd like and press enter. I renamed the node to "hey". You will see that the rename is also reflected in the node name labels on the part. +
+ +#### Batch Node Renaming +You can also rename a bunch of nodes quickly, by renaming nodes under a naming scheme! - ![](rename_node_2.png) +1. In the *JBeam* UI, inside the *Batch Node Renaming* panel, in the *Naming Scheme* input box, type in the node name scheme where the '#' character will be replaced by the *Node Index*. For example, naming scheme "#rr" will produce node names like "1rr", "2rr", "3rr", etc. Don't forget if required to enable *Affect Node References* under the *Settings* panel. Then press the "Start" button and nodes you click will be renamed. The *Node Index* shows you what the next node will be renamed to. You can also adjust this number if you need to.
@@ -204,6 +227,27 @@ And deleting a triangle/quad is also pretty straight forward.
+#### Triangle/Quad Face Orientation +Blender has a tool to show you which way a face is pointing, as a face has a front and back side. A blue face is the front side of a face (normal pointing towards you) and a red face is the back side (normal pointing away from you). + +1. On the upper right corner of the viewport, click on the *Overlays* downward facing arrow +2. Toggle the *Face Orientation* checkbox. + + ![](face_orientation.png) + +
+ +#### Flipping a Triangle/Quad +And you can flip the way a triangle/quad is facing. + +1. Switch into *Face select* mode +2. Click on the triangle/quad you want to flip. +3. Click on *Flip Face(s)* + + ![](flip_face.png) + +
+ #### Exporting a JBeam Part 1. When you want to export, select the JBeam part in *Object Mode*, and in the top menu bar *File* > *Export* > *Selected JBeam Parts(s)*. diff --git a/jbeam_editor/__init__.py b/jbeam_editor/__init__.py index 7ec8350..8a41daa 100644 --- a/jbeam_editor/__init__.py +++ b/jbeam_editor/__init__.py @@ -22,7 +22,7 @@ "name": "Blender JBeam Editor", "description": "Import a JBeam part, modify it, and export it directly back to the original JBeam file!", "author": "BeamNG", - "version": (0, 2, 1), + "version": (0, 2, 2), "blender": (4, 0, 2), "location": "File > Import > JBeam File / File > Export > JBeam File", "warning": "", @@ -79,6 +79,8 @@ rename_enabled = False +batch_node_renaming_enabled = False + # Refresh property input field UI def on_input_node_id_field_updated(self, context: bpy.types.Context): global _force_do_export @@ -98,7 +100,7 @@ def on_input_node_id_field_updated(self, context: bpy.types.Context): bm = bmesh.from_edit_mesh(obj_data) # Set the selected mesh's selected vertex node_id attribute to the UI node_id input field value - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] selected_vert[node_id_layer] = bytes(ui_props.input_node_id, 'utf-8') bm.free() @@ -120,6 +122,19 @@ class UIProperties(bpy.types.PropertyGroup): update=on_input_node_id_field_updated ) + batch_node_renaming_naming_scheme: bpy.props.StringProperty( + name="Naming Scheme", + description="'#' characters will be replaced with \"Node Index\" (e.g. '#rr' results in '1rr', '2rr', '3rr', etc)", + default="", + ) + + batch_node_renaming_node_idx: bpy.props.IntProperty( + name="Node Index", + description="Node index that will replace '#' characters in naming scheme", + default=1, + min=1 + ) + toggle_node_ids_text: bpy.props.BoolProperty( name="Toggle NodeIDs Text", description="Toggles the text of NodeIDs", @@ -128,7 +143,7 @@ class UIProperties(bpy.types.PropertyGroup): affect_node_references: bpy.props.BoolProperty( name="Affect Node References", - description="Toggles updating JBeam entries who references nodes. E.g. deleting a JBeam entry when a node that that entry references gets deleted.", + description="Toggles updating JBeam entries who references nodes. E.g. deleting a beam who references a node being deleted", default=False ) @@ -226,8 +241,8 @@ def invoke(self, context, event): obj = context.active_object obj_data = obj.data bm = bmesh.from_edit_mesh(obj_data) - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] export = False @@ -242,7 +257,7 @@ def invoke(self, context, event): new_verts.append(new_v) if len_selected_verts == 2: - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] e = bm.edges.new(new_verts) e[beam_indices_layer] = bytes('-1', 'utf-8') if obj.mode != 'EDIT': @@ -250,7 +265,7 @@ def invoke(self, context, event): export = True elif len_selected_verts in (3,4): - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] f = bm.faces.new(new_verts) f[face_idx_layer] = -1 if obj.mode != 'EDIT': @@ -266,6 +281,68 @@ def invoke(self, context, event): return {'FINISHED'} +# Flip JBeam faces +class JBEAM_EDITOR_OT_flip_jbeam_faces(bpy.types.Operator): + bl_idname = "jbeam_editor.flip_jbeam_faces" + bl_label = "Flip Face(s)" + + @classmethod + def poll(cls, context): + global selected_tris_quads + return len(selected_tris_quads) > 0 + + def invoke(self, context, event): + global selected_tris_quads + + obj = context.active_object + obj_data = obj.data + bm = bmesh.from_edit_mesh(obj_data) + face_flip_flag_layer = bm.faces.layers.int[constants.FL_FACE_FLIP_FLAG] + + face: bmesh.types.BMFace + face_idx: int + for (face, face_idx) in selected_tris_quads: + face[face_flip_flag_layer] = 1 + + bm.free() + + global _force_do_export + _force_do_export = True + + return {'FINISHED'} + + +# Batch node renaming +class JBEAM_EDITOR_OT_batch_node_renaming(bpy.types.Operator): + bl_idname = "jbeam_editor.batch_node_renaming" + bl_label = "Batch Node Renaming" + bl_description = "After clicking \"Start\", clicking a node will rename it. Press \"Stop\" when done" + + @classmethod + def poll(cls, context): + obj = context.active_object + if not obj: + return False + obj_data = obj.data + if not isinstance(obj_data, bpy.types.Mesh): + return False + if obj_data.get(constants.MESH_JBEAM_PART) is None: + return False + if obj.mode != 'EDIT': + return False + return True + + def invoke(self, context, event): + scene = context.scene + ui_props = scene.ui_properties + + global batch_node_renaming_enabled + batch_node_renaming_enabled = not batch_node_renaming_enabled + if not batch_node_renaming_enabled: + ui_props.batch_node_renaming_node_idx = 1 + return {'FINISHED'} + + class JBEAM_EDITOR_PT_transform_panel_ext(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -301,8 +378,11 @@ def draw(self, context): scene = context.scene ui_props = scene.ui_properties + + jbeam_part_name = obj_data[constants.MESH_JBEAM_PART] + layout = self.layout - layout.label(text=obj.name) + layout.label(text=f'{jbeam_part_name}') # If mesh isn't a JBeam mesh (it doesn't have node id attributes), give user option to convert it to one (add node id attributes) if obj_data.get(constants.MESH_JBEAM_PART) is None: @@ -313,17 +393,6 @@ def draw(self, context): box = layout.box() col = box.column() - # Add a checkbox to toggle Node IDs text - col.prop(ui_props, 'toggle_node_ids_text', text="Toggle Node IDs Text") - - box = layout.box() - col = box.column() - - col.prop(ui_props, 'affect_node_references', text="Affect Node References") - - box = layout.box() - col = box.column() - global selected_nodes global selected_beams global selected_tris_quads @@ -343,9 +412,9 @@ def draw(self, context): else: label = 'Add Quad' col.row().operator('jbeam_editor.add_beam_tri_quad', text=label) - else: - rows = [col.row() for i in range(1)] - rows[0].label(text='Select a node to rename') + + if len_selected_faces > 0: + col.row().operator('jbeam_editor.flip_jbeam_faces') bm.free() @@ -398,7 +467,7 @@ def draw(self, context): if curr_vdata is not None and 'beams' in curr_vdata: edge_data = selected_beams[0] e, beam_indices = edge_data[0], edge_data[1] - part_origin_layer = bm.edges.layers.string[constants.ELS_BEAM_PART_ORIGIN] + part_origin_layer = bm.edges.layers.string[constants.EL_BEAM_PART_ORIGIN] part_origin = e[part_origin_layer].decode('utf-8') beam_idx = int(beam_indices.split(',')[0]) @@ -435,8 +504,8 @@ def draw(self, context): face_type = 'quads' if face_type in curr_vdata: - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] - part_origin_layer = bm.faces.layers.string[constants.FLS_FACE_PART_ORIGIN] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] + part_origin_layer = bm.faces.layers.string[constants.FL_FACE_PART_ORIGIN] face_idx = f[face_idx_layer] part_origin = f[part_origin_layer].decode('utf-8') @@ -463,6 +532,68 @@ def draw(self, context): bm.free() +class JBEAM_EDITOR_PT_batch_node_renaming(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'JBeam' + bl_label = 'Batch Node Renaming' + + def draw(self, context): + scene = context.scene + ui_props = scene.ui_properties + layout = self.layout + + box = layout.box() + col = box.column() + col.row().label(text='Naming Scheme') + col.prop(ui_props, 'batch_node_renaming_naming_scheme', text = "") + col.prop(ui_props, 'batch_node_renaming_node_idx', text = "Node Index") + + operator_text = 'Stop' if batch_node_renaming_enabled else 'Start' + col.operator(JBEAM_EDITOR_OT_batch_node_renaming.bl_idname, text=operator_text) + + +class JBEAM_EDITOR_PT_jbeam_settings(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'JBeam' + bl_label = 'Settings' + + def draw(self, context): + obj = context.active_object + if not obj: + return + + obj_data = obj.data + if not isinstance(obj_data, bpy.types.Mesh): + return + + bm = None + if obj.mode == 'EDIT': + bm = bmesh.from_edit_mesh(obj_data) + else: + bm = bmesh.new() + bm.from_mesh(obj_data) + + scene = context.scene + ui_props = scene.ui_properties + layout = self.layout + + # If mesh isn't a JBeam mesh (it doesn't have node id attributes), give user option to convert it to one (add node id attributes) + if obj_data.get(constants.MESH_JBEAM_PART) is None: + # TODO: FIX FOR NEXT UPDATE + #layout.operator('jbeam_editor.convert_to_jbeam_mesh', text='Convert to JBeam Mesh') + pass + else: + box = layout.box() + col = box.column() + + col.prop(ui_props, 'toggle_node_ids_text', text="Toggle Node IDs Text") + col.prop(ui_props, 'affect_node_references', text="Affect Node References") + + bm.free() + + def refresh_curr_vdata(force_refresh=False): global prev_obj_selected global curr_vdata @@ -532,9 +663,9 @@ def draw_callback_px(context: bpy.types.Context): bm = bmesh.new() bm.from_mesh(obj_data) - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - part_origin_layer = bm.verts.layers.string[constants.VLS_NODE_PART_ORIGIN] - is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + part_origin_layer = bm.verts.layers.string[constants.VL_NODE_PART_ORIGIN] + is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] toggleNodeText = ui_props.toggle_node_ids_text ctxRegion = context.region @@ -573,8 +704,8 @@ def draw_callback_px(context: bpy.types.Context): bm = bmesh.new() bm.from_mesh(active_obj_data) - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] for v in bm.verts: if v[is_fake_layer] == 1: @@ -632,7 +763,7 @@ def draw_callback_view(context: bpy.types.Context): bm = bmesh.new() bm.from_mesh(obj_data) - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] e: bmesh.types.BMEdge for e in bm.edges: @@ -656,7 +787,7 @@ def draw_callback_view(context: bpy.types.Context): bm = bmesh.new() bm.from_mesh(active_obj_data) - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] e: bmesh.types.BMEdge for e in bm.edges: @@ -719,6 +850,8 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps selected_beams.clear() selected_tris_quads.clear() + reimporting_jbeam = False + # Don't act on reimporting mesh if type(scene.get('jbeam_editor_reimporting_jbeam')) == int: scene['jbeam_editor_reimporting_jbeam'] -= 1 @@ -726,12 +859,10 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps if scene['jbeam_editor_reimporting_jbeam'] < 0: scene['jbeam_editor_reimporting_jbeam'] = 0 else: - return_early = True + reimporting_jbeam = True - if return_early: if constants.DEBUG: - print('_depsgraph_callback: Returning early') - return + print('_depsgraph_callback: jbeam_editor_reimporting_jbeam') ui_props = scene.ui_properties @@ -765,13 +896,14 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps jbeam_filepath = active_obj_data[constants.MESH_JBEAM_FILE_PATH] text_editor.show_int_file(jbeam_filepath) - for update in depsgraph.updates: - if update.id == active_obj_eval: - #print('update.is_updated_geometry', update.is_updated_geometry, 'update.is_updated_shading', update.is_updated_shading, 'update.is_updated_transform', update.is_updated_transform) - if update.id == active_obj_eval and (update.is_updated_geometry or update.is_updated_transform): - if constants.DEBUG: - print('_depsgraph_callback: updated_geometry') - _do_export = True + if not reimporting_jbeam: + for update in depsgraph.updates: + if update.id == active_obj_eval: + #print('update.is_updated_geometry', update.is_updated_geometry, 'update.is_updated_shading', update.is_updated_shading, 'update.is_updated_transform', update.is_updated_transform) + if update.id == active_obj_eval and (update.is_updated_geometry or update.is_updated_transform): + if constants.DEBUG: + print('_depsgraph_callback: updated_geometry') + _do_export = True veh_model = active_obj_data.get(constants.MESH_VEHICLE_MODEL) if veh_model is not None: @@ -783,7 +915,6 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps context.view_layer.active_layer_collection = layer scene['jbeam_editor_veh_collection_selected'] = veh_collection - _do_export = True if active_obj.mode != 'EDIT': return @@ -791,9 +922,9 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps bm = bmesh.from_edit_mesh(active_obj_data) # Check if new vertices are added - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] # When new vertices are added, they seem to copy the data of the old vertices they were made from, # so rename their node ids to random ids (UUID) @@ -804,6 +935,17 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps continue if v.select: selected_nodes.append((v, v[init_node_id_layer].decode('utf-8'))) + + # Do batch node renaming + if batch_node_renaming_enabled: + new_node_id: str = ui_props.batch_node_renaming_naming_scheme + new_node_id = new_node_id.replace('#', f'{ui_props.batch_node_renaming_node_idx}') + v[node_id_layer] = bytes(new_node_id, 'utf-8') + ui_props.batch_node_renaming_node_idx += 1 + + global _force_do_export + _force_do_export = True + if i >= active_obj_data[constants.MESH_VERTEX_COUNT]: new_node_id = str(uuid.uuid4()) new_node_id_bytes = bytes(new_node_id, 'utf-8') @@ -815,7 +957,7 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps print('new vertex added', new_node_id) # Check if new edges are added - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] bm.edges.ensure_lookup_table() for i,e in enumerate(bm.edges): @@ -833,7 +975,7 @@ def _depsgraph_callback(context: bpy.types.Context, scene: bpy.types.Scene, deps #print(e[beam_indices_layer].decode('utf-8')) # Check if new faces are added - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] bm.faces.ensure_lookup_table() for i,f in enumerate(bm.faces): @@ -882,6 +1024,7 @@ def check_files_for_changes(): op_no_export = { 'OBJECT_OT_editmode_toggle', + JBEAM_EDITOR_OT_batch_node_renaming.bl_idname, } _last_op = None @@ -941,9 +1084,13 @@ def on_post_register(): JBEAM_EDITOR_OT_redo, #JBEAM_EDITOR_OT_convert_to_jbeam_mesh, JBEAM_EDITOR_OT_add_beam_tri_quad, + JBEAM_EDITOR_OT_flip_jbeam_faces, + JBEAM_EDITOR_OT_batch_node_renaming, JBEAM_EDITOR_PT_transform_panel_ext, JBEAM_EDITOR_PT_jbeam_panel, JBEAM_EDITOR_PT_jbeam_properties_panel, + JBEAM_EDITOR_PT_batch_node_renaming, + JBEAM_EDITOR_PT_jbeam_settings, import_jbeam.JBEAM_EDITOR_OT_import_jbeam, import_jbeam.JBEAM_EDITOR_OT_choose_jbeam, export_jbeam.JBEAM_EDITOR_OT_export_jbeam, diff --git a/jbeam_editor/constants.py b/jbeam_editor/constants.py index 7433c86..6cf0be0 100644 --- a/jbeam_editor/constants.py +++ b/jbeam_editor/constants.py @@ -41,16 +41,17 @@ MESH_VERTEX_DUPLICATES = 'mesh_vertex_duplicates' MESH_SINGLE_JBEAM_PART_DATA = 'jbeam_part_data' -# bm.verts.layers.string Attributes -VLS_INIT_NODE_ID = 'jbeam_init_node_id' -VLS_NODE_ID = 'jbeam_node_id' -VLS_NODE_PART_ORIGIN = 'jbeam_node_part_origin' -VLS_NODE_IS_FAKE = 'jbeam_node_is_fake' +# bm.verts.layers Attributes +VL_INIT_NODE_ID = 'jbeam_init_node_id' +VL_NODE_ID = 'jbeam_node_id' +VL_NODE_PART_ORIGIN = 'jbeam_node_part_origin' +VL_NODE_IS_FAKE = 'jbeam_node_is_fake' -# bm.edges.layers.string Attributes -ELS_BEAM_PART_ORIGIN = 'jbeam_beam_part_origin' -ELS_BEAM_INDICES = 'jbeam_beam_indices' +# bm.edges.layers Attributes +EL_BEAM_PART_ORIGIN = 'jbeam_beam_part_origin' +EL_BEAM_INDICES = 'jbeam_beam_indices' -# bm.faces.layers.string Attributes -FLS_FACE_PART_ORIGIN = 'jbeam_face_part_origin' -FLS_FACE_IDX = 'jbeam_face_indices' +# bm.faces.layers Attributes +FL_FACE_PART_ORIGIN = 'jbeam_face_part_origin' +FL_FACE_IDX = 'jbeam_face_indices' +FL_FACE_FLIP_FLAG = 'jbeam_face_flip_flag' diff --git a/jbeam_editor/export_jbeam.py b/jbeam_editor/export_jbeam.py index af8745f..085cb66 100644 --- a/jbeam_editor/export_jbeam.py +++ b/jbeam_editor/export_jbeam.py @@ -132,14 +132,39 @@ def export_existing_jbeam(obj: bpy.types.Object): blender_nodes, parts_nodes_actions = export_utils.get_nodes_add_delete_rename(obj, bm, part_name, init_nodes_data, affect_node_references) parts_to_update = set(parts_nodes_actions.keys()) + bm.free() - export_utils.export_file(jbeam_filepath, [obj], part_data, blender_nodes, parts_nodes_actions, affect_node_references, parts_to_update) - t1 = timeit.default_timer() - print('Exporting Time', round(t1 - t0, 2), 's') + reimport_needed = export_utils.export_file(jbeam_filepath, [obj], part_data, blender_nodes, parts_nodes_actions, affect_node_references, parts_to_update) + text_editor.check_int_files_for_changes(context, [jbeam_filepath], regenerate_mesh_on_change=reimport_needed) + + # Make sure node positions are all synced if not reimporting + if not reimport_needed: + if obj.mode == 'EDIT': + bm = bmesh.from_edit_mesh(obj_data) + else: + bm = bmesh.new() + bm.from_mesh(obj_data) + + nodes_to_move = parts_nodes_actions[part_name].nodes_to_move + + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + v: bmesh.types.BMVert + for v in bm.verts: + node_id = v[node_id_layer].decode('utf-8') + if node_id in nodes_to_move: + v.co = nodes_to_move[node_id] - text_editor.check_int_files_for_changes(context, [jbeam_filepath]) - t2 = timeit.default_timer() - print('Reimporting Time', round(t2 - t1, 2), 's') + if obj.mode == 'EDIT': + bmesh.update_edit_mesh(obj_data) + else: + bm.to_mesh(obj_data) + + bm.free() + + bpy.ops.object.location_clear() + + t1 = timeit.default_timer() + print('Exporting/reimporting Time', round(t1 - t0, 2), 's') except: traceback.print_exc() diff --git a/jbeam_editor/export_utils.py b/jbeam_editor/export_utils.py index b4281e1..b380ef9 100644 --- a/jbeam_editor/export_utils.py +++ b/jbeam_editor/export_utils.py @@ -46,7 +46,7 @@ def __init__(self): self.nodes_to_add = {} self.nodes_to_delete = set() self.nodes_to_rename = {} - self.nodes_to_move = set() + self.nodes_to_move = {} def print_ast_nodes(ast_nodes, start_idx, size, bidirectional, file=None): @@ -415,10 +415,10 @@ def get_nodes_add_delete_rename(obj: bpy.types.Object, bm: bmesh.types.BMesh, jb # parts_nodes_to_add, parts_nodes_to_delete, parts_nodes_to_rename, parts_nodes_to_move = {}, {}, {}, {} - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - part_origin_layer = bm.verts.layers.string[constants.VLS_NODE_PART_ORIGIN] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + part_origin_layer = bm.verts.layers.string[constants.VL_NODE_PART_ORIGIN] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] # Update node ids and positions from Blender into the SJSON data @@ -444,7 +444,7 @@ def get_nodes_add_delete_rename(obj: bpy.types.Object, bm: bmesh.types.BMesh, jb init_pos = init_node_data['pos'] if abs(pos.x - init_pos[0]) > 0.000001 or abs(pos.y - init_pos[1]) > 0.000001 or abs(pos.z - init_pos[2]) > 0.000001: part_actions: PartNodesActions = parts_actions.setdefault(node_part_origin, PartNodesActions()) - part_actions.nodes_to_move.add(node_id) + part_actions.nodes_to_move[node_id] = pos new_pos_tup = undo_node_move_offset_and_apply_translation_to_expr(init_node_data, pos) @@ -472,28 +472,27 @@ def get_nodes_add_delete_rename(obj: bpy.types.Object, bm: bmesh.types.BMesh, jb def get_beams_add_remove(obj: bpy.types.Object, bm: bmesh.types.BMesh, init_beams_data: list, jbeam_file_data_modified: dict, jbeam_part: str, nodes_to_delete: set, affect_node_references: bool): beams_to_add, beams_to_delete = set(), set() - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] - blender_beams = {} + blender_beams = set() # Create dictionary where key is init node id and value is current blender node id and position bm.edges.ensure_lookup_table() e: bmesh.types.BMEdge for i, e in enumerate(bm.edges): - v1, v2 = e.verts[0], e.verts[1] - v1_node_id, v2_node_id = v1[init_node_id_layer].decode('utf-8'), v2[init_node_id_layer].decode('utf-8') - beam_tup = (v1_node_id, v2_node_id) #print('beam:', v1_node_id, v2_node_id) - beam_indices = e[beam_indices_layer].decode('utf-8') if beam_indices == '': # Beam doesn't exist in JBeam data and is just part of a Blender face for example continue if beam_indices == '-1': # Newly added beam + v1, v2 = e.verts[0], e.verts[1] + v1_node_id, v2_node_id = v1[init_node_id_layer].decode('utf-8'), v2[init_node_id_layer].decode('utf-8') + beam_tup = (v1_node_id, v2_node_id) beams_to_add.add(beam_tup) continue for idx in beam_indices.split(','): - blender_beams[int(idx)] = beam_tup + blender_beams.add(int(idx)) # Get beams to delete beam_idx_in_part = 1 @@ -512,49 +511,71 @@ def get_beams_add_remove(obj: bpy.types.Object, bm: bmesh.types.BMesh, init_beam def get_faces_add_remove(obj: bpy.types.Object, bm: bmesh.types.BMesh, init_tris_data: list, init_quads_data: list, jbeam_file_data_modified: dict, jbeam_part: str, nodes_to_delete: set, affect_node_references: bool): - tris_to_add, tris_to_delete = set(), set() - quads_to_add, quads_to_delete = set(), set() + tris_to_add, tris_to_delete, tris_flipped = set(), set(), set() + quads_to_add, quads_to_delete, quads_flipped = set(), set(), set() - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] + face_flip_flag_layer = bm.faces.layers.int[constants.FL_FACE_FLIP_FLAG] - blender_tris = {} - blender_quads = {} + blender_tris = set() + blender_quads = set() # Create dictionary where key is init node id and value is current blender node id and position bm.faces.ensure_lookup_table() f: bmesh.types.BMFace for i, f in enumerate(bm.faces): num_verts = len(f.verts) if num_verts == 3: - v1, v2, v3 = f.verts[0], f.verts[1], f.verts[2] - v1_node_id, v2_node_id, v3_node_id = v1[init_node_id_layer].decode('utf-8'), v2[init_node_id_layer].decode('utf-8'), v3[init_node_id_layer].decode('utf-8') - tri_tup = (v1_node_id, v2_node_id, v3_node_id) tri_idx = f[face_idx_layer] if tri_idx == 0: # Triangle doesn't exist in JBeam data continue if tri_idx == -1: # Newly added triangle + v1, v2, v3 = f.verts[0], f.verts[1], f.verts[2] + v1_node_id, v2_node_id, v3_node_id = v1[init_node_id_layer].decode('utf-8'), v2[init_node_id_layer].decode('utf-8'), v3[init_node_id_layer].decode('utf-8') + tri_tup = (v1_node_id, v2_node_id, v3_node_id) tris_to_add.add(tri_tup) continue - # for idx in tri_indices.split(','): - # blender_tris[int(idx)] = tri_tup - blender_tris[tri_idx] = tri_tup + # Flip face if "face flip" flag set! + if f[face_flip_flag_layer] == 1: + tris_jbeam_data = jbeam_file_data_modified[jbeam_part]['triangles'] + j = 0 + for tri_jbeam_data in tris_jbeam_data: + if isinstance(tri_jbeam_data, list): + if j == tri_idx: + tri_jbeam_data[1], tri_jbeam_data[2] = tri_jbeam_data[2], tri_jbeam_data[1] + tris_flipped.add(tri_idx) + break + j += 1 + + blender_tris.add(tri_idx) + elif num_verts == 4: - v1, v2, v3, v4 = f.verts[0], f.verts[1], f.verts[2], f.verts[3] - v1_node_id, v2_node_id, v3_node_id, v4_node_id = v1[init_node_id_layer].decode('utf-8'), v2[init_node_id_layer].decode('utf-8'), v3[init_node_id_layer].decode('utf-8'), v4[init_node_id_layer].decode('utf-8') - quad_tup = (v1_node_id, v2_node_id, v3_node_id, v4_node_id) quad_idx = f[face_idx_layer] if quad_idx == 0: # Quad doesn't exist in JBeam data continue if quad_idx == -1: # Newly added quad + v1, v2, v3, v4 = f.verts[0], f.verts[1], f.verts[2], f.verts[3] + v1_node_id, v2_node_id, v3_node_id, v4_node_id = v1[init_node_id_layer].decode('utf-8'), v2[init_node_id_layer].decode('utf-8'), v3[init_node_id_layer].decode('utf-8'), v4[init_node_id_layer].decode('utf-8') + quad_tup = (v1_node_id, v2_node_id, v3_node_id, v4_node_id) quads_to_add.add(quad_tup) continue - # for idx in quad_indices.split(','): - # blender_quads[int(idx)] = quad_tup - blender_quads[quad_idx] = quad_tup + # Flip face if "face flip" flag set! + if f[face_flip_flag_layer] == 1: + quads_jbeam_data = jbeam_file_data_modified[jbeam_part]['quads'] + j = 0 + for quad_jbeam_data in quads_jbeam_data: + if isinstance(quad_jbeam_data, list): + if j == quad_idx: + quad_jbeam_data[1], quad_jbeam_data[3] = quad_jbeam_data[3], quad_jbeam_data[1] + quads_flipped.add(quad_idx) + break + j += 1 + + blender_quads.add(quad_idx) else: print("Warning! Won't export face with 5 or more vertices!", file=sys.stderr) @@ -580,7 +601,7 @@ def get_faces_add_remove(obj: bpy.types.Object, bm: bmesh.types.BMesh, init_tris quads_to_delete.add(quad_idx_in_part) quad_idx_in_part += 1 - return tris_to_add, tris_to_delete, quads_to_add, quads_to_delete + return tris_to_add, tris_to_delete, tris_flipped, quads_to_add, quads_to_delete, quads_flipped def add_jbeam_section(ast_nodes: list, jbeam_section_end_node_idx: int): @@ -670,8 +691,9 @@ def add_beams_section(ast_nodes: list, jbeam_section_end_node_idx: int): ast_nodes.insert(i + 1, ASTNode('"', 'id1:')) ast_nodes.insert(i + 2, ASTNode('wsc', ',')) ast_nodes.insert(i + 3, ASTNode('"', 'id2:')) - ast_nodes.insert(i + 4, ASTNode('wsc', ',' + NL_INDENT)) - i += 5 + ast_nodes.insert(i + 4, ASTNode(']')) + ast_nodes.insert(i + 5, ASTNode('wsc', ',' + NL_INDENT)) + i += 6 ast_nodes.insert(i + 0, ASTNode(']')) jbeam_section_end_node_idx = i + 0 ast_nodes.insert(i + 1, ASTNode('wsc', ',')) @@ -703,8 +725,9 @@ def add_triangles_section(ast_nodes: list, jbeam_section_end_node_idx: int): ast_nodes.insert(i + 3, ASTNode('"', 'id2:')) ast_nodes.insert(i + 4, ASTNode('wsc', ',')) ast_nodes.insert(i + 5, ASTNode('"', 'id3:')) - ast_nodes.insert(i + 6, ASTNode('wsc', ',' + NL_INDENT)) - i += 7 + ast_nodes.insert(i + 6, ASTNode(']')) + ast_nodes.insert(i + 7, ASTNode('wsc', ',' + NL_INDENT)) + i += 8 ast_nodes.insert(i + 0, ASTNode(']')) jbeam_section_end_node_idx = i + 0 ast_nodes.insert(i + 1, ASTNode('wsc', ',')) @@ -1072,21 +1095,21 @@ def update_ast_nodes(ast_nodes: list, current_jbeam_file_data: dict, current_jbe # and create the sections if so if add_nodes_flag: i, jbeam_section_start_node_idx, jbeam_section_end_node_idx = add_nodes_section(ast_nodes, jbeam_section_end_node_idx) - add_jbeam_nodes(ast_nodes, jbeam_section_start_node_idx, jbeam_section_end_node_idx, nodes_to_add) + i = add_jbeam_nodes(ast_nodes, jbeam_section_start_node_idx, jbeam_section_end_node_idx, nodes_to_add) i = get_next_non_wsc_node(ast_nodes, i + 1) add_nodes_flag = False if add_beams_flag: i, jbeam_section_start_node_idx, jbeam_section_end_node_idx = add_beams_section(ast_nodes, jbeam_section_end_node_idx) - add_jbeam_beams(ast_nodes, jbeam_section_start_node_idx, jbeam_section_end_node_idx, beams_to_add) + i = add_jbeam_beams(ast_nodes, jbeam_section_start_node_idx, jbeam_section_end_node_idx, beams_to_add) i = get_next_non_wsc_node(ast_nodes, i + 1) - add_nodes_flag = False + add_beams_flag = False if add_tris_flag: i, jbeam_section_start_node_idx, jbeam_section_end_node_idx = add_triangles_section(ast_nodes, jbeam_section_end_node_idx) - add_jbeam_triangles(ast_nodes, jbeam_section_start_node_idx, jbeam_section_end_node_idx, tris_to_add) + i = add_jbeam_triangles(ast_nodes, jbeam_section_start_node_idx, jbeam_section_end_node_idx, tris_to_add) i = get_next_non_wsc_node(ast_nodes, i + 1) - add_nodes_flag = False + add_tris_flag = False if add_quads_flag: i, jbeam_section_start_node_idx, jbeam_section_end_node_idx = add_quads_section(ast_nodes, jbeam_section_end_node_idx) @@ -1114,20 +1137,22 @@ def update_ast_nodes(ast_nodes: list, current_jbeam_file_data: dict, current_jbe def export_file(jbeam_filepath: str, parts: list[bpy.types.Object], data: dict, blender_nodes: dict, parts_nodes_actions: dict, affect_node_references: bool, parts_to_update: set): + reimport_needed = False + jbeam_file_str = text_editor.read_int_file(jbeam_filepath) if jbeam_file_str is None: print(f"File doesn't exist! {jbeam_filepath}", file=sys.stderr) - return + return reimport_needed jbeam_file_data, cached_changed = jbeam_io.get_jbeam(jbeam_filepath, True, False) jbeam_file_data_modified, cached_changed = jbeam_io.get_jbeam(jbeam_filepath, True, False) if jbeam_file_data is None or jbeam_file_data_modified is None: - return + return reimport_needed # The imported jbeam data is used to build an AST from ast_data = sjsonast_parse(jbeam_file_str) if ast_data is None: print("SJSON AST parsing failed!", file=sys.stderr) - return + return reimport_needed ast_nodes = ast_data['ast']['nodes'] update_all_parts = True in parts_to_update @@ -1148,9 +1173,9 @@ def export_file(jbeam_filepath: str, parts: list[bpy.types.Object], data: dict, part_nodes_actions: PartNodesActions | None = parts_nodes_actions.get(jbeam_part) if part_nodes_actions is not None: - nodes_to_add, nodes_to_delete, node_renames = part_nodes_actions.nodes_to_add, part_nodes_actions.nodes_to_delete, part_nodes_actions.nodes_to_rename + nodes_to_add, nodes_to_delete, node_renames, node_moves = part_nodes_actions.nodes_to_add, part_nodes_actions.nodes_to_delete, part_nodes_actions.nodes_to_rename, part_nodes_actions.nodes_to_move else: - nodes_to_add, nodes_to_delete, node_renames = {}, set(), {} + nodes_to_add, nodes_to_delete, node_renames, node_moves = {}, set(), {}, {} # Add "all parts" actions also part_nodes_actions: PartNodesActions | None = parts_nodes_actions.get(True) @@ -1174,7 +1199,7 @@ def export_file(jbeam_filepath: str, parts: list[bpy.types.Object], data: dict, else: beams_to_add, beams_to_delete = set(), set() - tris_to_add, tris_to_delete, quads_to_add, quads_to_delete = get_faces_add_remove(obj, bm, init_tris_data, init_quads_data, jbeam_file_data_modified, jbeam_part, nodes_to_delete, affect_node_references) + tris_to_add, tris_to_delete, tris_flipped, quads_to_add, quads_to_delete, quads_flipped = get_faces_add_remove(obj, bm, init_tris_data, init_quads_data, jbeam_file_data_modified, jbeam_part, nodes_to_delete, affect_node_references) # Remove beams that were added due to adding triangle for beam in beams_to_add.copy(): @@ -1186,12 +1211,15 @@ def export_file(jbeam_filepath: str, parts: list[bpy.types.Object], data: dict, print('nodes to add:', nodes_to_add) print('nodes to delete:', nodes_to_delete) print('node renames:', node_renames) + print('node moves:', node_moves) print('beams to add:', beams_to_add) print('beams to delete:', beams_to_delete) print('tris to add:', tris_to_add) print('tris to delete:', tris_to_delete) + print('tris flipped:', tris_flipped) print('quads to add:', quads_to_add) print('quads to delete:', quads_to_delete) + print('quads flipped:', quads_flipped) update_ast_nodes(ast_nodes, jbeam_file_data, jbeam_file_data_modified, jbeam_part, affect_node_references, nodes_to_add, nodes_to_delete, @@ -1200,23 +1228,28 @@ def export_file(jbeam_filepath: str, parts: list[bpy.types.Object], data: dict, quads_to_add, quads_to_delete) out_str_jbeam_data = sjsonast_stringify_nodes(ast_nodes) - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - - # Initial node ids now become node ids - for v in bm.verts: - node_id = v[node_id_layer].decode('utf-8') - v[init_node_id_layer] = bytes(node_id, 'utf-8') - - # f = open(jbeam_filepath, 'w', encoding='utf-8') - # f.write(out_str_jbeam_data) - # f.close() - text_editor.write_int_file(jbeam_filepath, out_str_jbeam_data) if constants.DEBUG: print(f'Exported: {jbeam_filepath}') + if not reimport_needed: + reimport_needed = ( + len(nodes_to_add) > 0 or + len(nodes_to_delete) > 0 or + len(node_renames) > 0 or + len(beams_to_add) > 0 or + len(beams_to_delete) > 0 or + len(tris_to_add) > 0 or + len(tris_to_delete) > 0 or + len(tris_flipped) > 0 or + len(quads_to_add) > 0 or + len(quads_to_delete) > 0 or + len(quads_flipped) > 0 + ) + + return reimport_needed + def export_file_to_disk(jbeam_filepath: str): res = text_editor.write_from_int_to_ext_file(jbeam_filepath) diff --git a/jbeam_editor/export_vehicle.py b/jbeam_editor/export_vehicle.py index a9d0372..896ac9d 100644 --- a/jbeam_editor/export_vehicle.py +++ b/jbeam_editor/export_vehicle.py @@ -45,7 +45,7 @@ def export(veh_collection: bpy.types.Collection, active_obj: bpy.types.Object): init_nodes_data = vdata.get('nodes') active_obj_data = active_obj.data - jbeam_part = active_obj_data[constants.MESH_JBEAM_PART] + active_jbeam_part = active_obj_data[constants.MESH_JBEAM_PART] bm = None if active_obj.mode == 'EDIT': @@ -54,14 +54,16 @@ def export(veh_collection: bpy.types.Collection, active_obj: bpy.types.Object): bm = bmesh.new() bm.from_mesh(active_obj_data) - blender_nodes, parts_nodes_actions = export_utils.get_nodes_add_delete_rename(active_obj, bm, jbeam_part, init_nodes_data, affect_node_references) + blender_nodes, parts_nodes_actions = export_utils.get_nodes_add_delete_rename(active_obj, bm, active_jbeam_part, init_nodes_data, affect_node_references) parts_to_update = set(parts_nodes_actions.keys()) jbeam_files_to_jbeam_part_objs = {} jbeam_files_to_jbeam_parts = {} + obj: bpy.types.Object for obj in veh_collection.all_objects[:]: - jbeam_filepath = obj.data[constants.MESH_JBEAM_FILE_PATH] - jbeam_part = obj.data[constants.MESH_JBEAM_PART] + obj_data = obj.data + jbeam_filepath = obj_data[constants.MESH_JBEAM_FILE_PATH] + jbeam_part = obj_data[constants.MESH_JBEAM_PART] if jbeam_filepath not in jbeam_files_to_jbeam_part_objs: jbeam_files_to_jbeam_part_objs[jbeam_filepath] = [] @@ -69,20 +71,56 @@ def export(veh_collection: bpy.types.Collection, active_obj: bpy.types.Object): jbeam_files_to_jbeam_part_objs[jbeam_filepath].append(obj) jbeam_files_to_jbeam_parts[jbeam_filepath].add(jbeam_part) + bm.free() + filepaths = [] + reimport_needed = False for jbeam_filepath, objs in jbeam_files_to_jbeam_part_objs.items(): jbeam_file_parts = jbeam_files_to_jbeam_parts[jbeam_filepath] if True in parts_to_update or any(x in parts_to_update for x in jbeam_file_parts): - export_utils.export_file(jbeam_filepath, objs, vdata, blender_nodes, parts_nodes_actions, affect_node_references, parts_to_update) + reimport_needed |= export_utils.export_file(jbeam_filepath, objs, vdata, blender_nodes, parts_nodes_actions, affect_node_references, parts_to_update) filepaths.append(jbeam_filepath) - t1 = timeit.default_timer() - print('Exporting Time', round(t1 - t0, 2), 's') - text_editor.check_int_files_for_changes(context, filepaths) - t2 = timeit.default_timer() - print('Reimporting Time', round(t2 - t1, 2), 's') + text_editor.check_int_files_for_changes(context, filepaths, regenerate_mesh_on_change=reimport_needed) + + # Make sure node positions are all synced if not reimporting + if not reimport_needed: + nodes_to_move = {} + for jbeam_part, part_node_actions in parts_nodes_actions.items(): + nodes_to_move.update(part_node_actions.nodes_to_move) + + obj: bpy.types.Object + for obj in veh_collection.all_objects[:]: + obj_data = obj.data + jbeam_filepath = obj.data[constants.MESH_JBEAM_FILE_PATH] + jbeam_part = obj.data[constants.MESH_JBEAM_PART] + + if obj.mode == 'EDIT': + bm = bmesh.from_edit_mesh(obj_data) + else: + bm = bmesh.new() + bm.from_mesh(obj_data) + + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + v: bmesh.types.BMVert + for v in bm.verts: + node_id = v[node_id_layer].decode('utf-8') + if node_id in nodes_to_move: + v.co = nodes_to_move[node_id] + + if obj.mode == 'EDIT': + bmesh.update_edit_mesh(obj_data) + else: + bm.to_mesh(obj_data) + + bm.free() + + bpy.ops.object.location_clear() + + t1 = timeit.default_timer() + print('Exporting/reimporting Time', round(t1 - t0, 2), 's') except: traceback.print_exc() diff --git a/jbeam_editor/import_jbeam.py b/jbeam_editor/import_jbeam.py index 84dec6d..2583821 100644 --- a/jbeam_editor/import_jbeam.py +++ b/jbeam_editor/import_jbeam.py @@ -123,16 +123,17 @@ def generate_part_mesh(obj: bpy.types.Object, obj_data: bpy.types.Mesh, bm: bmes bm_faces_new = bm_faces.new # Add node ID field to all vertices - init_node_id_layer = bm_verts.layers.string.new(constants.VLS_INIT_NODE_ID) - node_id_layer = bm_verts.layers.string.new(constants.VLS_NODE_ID) - node_origin_layer = bm_verts.layers.string.new(constants.VLS_NODE_PART_ORIGIN) - node_is_fake_layer = bm_verts.layers.int.new(constants.VLS_NODE_IS_FAKE) + init_node_id_layer = bm_verts.layers.string.new(constants.VL_INIT_NODE_ID) + node_id_layer = bm_verts.layers.string.new(constants.VL_NODE_ID) + node_origin_layer = bm_verts.layers.string.new(constants.VL_NODE_PART_ORIGIN) + node_is_fake_layer = bm_verts.layers.int.new(constants.VL_NODE_IS_FAKE) - beam_origin_layer = bm_edges.layers.string.new(constants.ELS_BEAM_PART_ORIGIN) - beam_indices_layer = bm_edges.layers.string.new(constants.ELS_BEAM_INDICES) + beam_origin_layer = bm_edges.layers.string.new(constants.EL_BEAM_PART_ORIGIN) + beam_indices_layer = bm_edges.layers.string.new(constants.EL_BEAM_INDICES) - face_origin_layer = bm_faces.layers.string.new(constants.FLS_FACE_PART_ORIGIN) - face_idx_layer = bm_faces.layers.int.new(constants.FLS_FACE_IDX) + face_origin_layer = bm_faces.layers.string.new(constants.FL_FACE_PART_ORIGIN) + face_idx_layer = bm_faces.layers.int.new(constants.FL_FACE_IDX) + face_flip_flag_layer = bm_faces.layers.int.new(constants.FL_FACE_FLIP_FLAG) inv_matrix_world = obj.matrix_world.inverted() bytes_part = bytes(part, 'utf-8') @@ -179,7 +180,6 @@ def generate_part_mesh(obj: bpy.types.Object, obj_data: bpy.types.Mesh, bm: bmes obj_data[constants.MESH_JBEAM_PART] = part obj_data[constants.MESH_JBEAM_FILE_PATH] = jbeam_file_path - obj_data[constants.MESH_SINGLE_JBEAM_PART_DATA] = pickle.dumps(vdata, -1) obj_data[constants.MESH_VERTEX_COUNT] = len(bm_verts) obj_data[constants.MESH_EDGE_COUNT] = len(bm_edges) obj_data[constants.MESH_FACE_COUNT] = len(bm_faces) @@ -214,6 +214,7 @@ def import_jbeam_part(context: bpy.types.Context, jbeam_file_path: str, jbeam_fi bm.to_mesh(obj_data) obj_data.update() + obj_data[constants.MESH_SINGLE_JBEAM_PART_DATA] = pickle.dumps(part_data, -1) # make collection jbeam_collection = bpy.data.collections.get('JBeam Objects') @@ -233,15 +234,13 @@ def import_jbeam_part(context: bpy.types.Context, jbeam_file_path: str, jbeam_fi return False -def reimport_jbeam(context: bpy.types.Context, jbeam_objects: bpy.types.Collection, obj: bpy.types.Object, jbeam_file_path: str): +def reimport_jbeam(context: bpy.types.Context, jbeam_objects: bpy.types.Collection, obj: bpy.types.Object, jbeam_file_path: str, regenerate_mesh_on_change: bool): try: # Reimport object jbeam_file_data, cached_changed = jbeam_io.get_jbeam(jbeam_file_path, True, True) if jbeam_file_data is None: raise Exception('Failed to load/parse JBeam file.') - context.scene['jbeam_editor_reimporting_jbeam'] = 1 # Prevents exporting jbeam - obj_data: bpy.types.Mesh = obj.data chosen_part = obj_data[constants.MESH_JBEAM_PART] part_data = jbeam_file_data[chosen_part] @@ -252,29 +251,30 @@ def reimport_jbeam(context: bpy.types.Context, jbeam_objects: bpy.types.Collecti raise Exception('JBeam processing error.') jbeam_node_beam.process(part_data) - obj_data[constants.MESH_SINGLE_JBEAM_PART_DATA] = pickle.dumps(part_data, -1) + if regenerate_mesh_on_change: + vertices, edges, tris, quads, node_ids = get_vertices_edges_faces(part_data) - vertices, edges, tris, quads, node_ids = get_vertices_edges_faces(part_data) + if obj.mode == 'EDIT': + bm = bmesh.from_edit_mesh(obj_data) + bm.clear() + else: + bm = bmesh.new() + bm.from_mesh(obj_data) + bm.clear() - if obj.mode == 'EDIT': - bm = bmesh.from_edit_mesh(obj_data) - bm.clear() - else: - bm = bmesh.new() - bm.from_mesh(obj_data) - bm.clear() + generate_part_mesh(obj, obj_data, bm, part_data, chosen_part, jbeam_file_path, vertices, edges, tris, quads, node_ids) + bm.normal_update() - generate_part_mesh(obj, obj_data, bm, part_data, chosen_part, jbeam_file_path, vertices, edges, tris, quads, node_ids) - bm.normal_update() - #export_jbeam.last_exported_jbeams[chosen_part] = {'in_filepath': jbeam_file_path} + if obj.mode == 'EDIT': + bmesh.update_edit_mesh(obj_data) + else: + bm.to_mesh(obj_data) + bm.free() + obj_data.update() - if obj.mode == 'EDIT': - bmesh.update_edit_mesh(obj_data) - else: - bm.to_mesh(obj_data) - bm.free() + obj_data[constants.MESH_SINGLE_JBEAM_PART_DATA] = pickle.dumps(part_data, -1) - obj_data.update() + context.scene['jbeam_editor_reimporting_jbeam'] = 1 # Prevents exporting jbeam print('Done reimporting JBeam.') return True @@ -283,7 +283,7 @@ def reimport_jbeam(context: bpy.types.Context, jbeam_objects: bpy.types.Collecti return False -def on_file_change(context: bpy.types.Context, filename: str, filetext: str): +def on_file_change(context: bpy.types.Context, filename: str, regenerate_mesh_on_change: bool): jbeam_objects: bpy.types.Collection | None = bpy.data.collections.get('JBeam Objects') if jbeam_objects is None: return @@ -299,20 +299,7 @@ def on_file_change(context: bpy.types.Context, filename: str, filetext: str): if jbeam_filepath is None or jbeam_filepath != filename: continue - # Check if jbeam file is parseable before reimporting jbeam part - data = utils.sjson_decode(filetext, filename) - if data is None: - continue - - # import cProfile, pstats, io - # import pstats - # pr = cProfile.Profile() - # with cProfile.Profile() as pr: - # import_vehicle.reimport_vehicle(veh_collection, filename) - # stats = pstats.Stats(pr) - # stats.strip_dirs().sort_stats('cumtime').print_stats() - - reimport_jbeam(context, jbeam_objects, obj, filename) + reimport_jbeam(context, jbeam_objects, obj, filename, regenerate_mesh_on_change) class JBEAM_EDITOR_OT_choose_jbeam(Operator): diff --git a/jbeam_editor/import_vehicle.py b/jbeam_editor/import_vehicle.py index e77ed5f..16358f1 100644 --- a/jbeam_editor/import_vehicle.py +++ b/jbeam_editor/import_vehicle.py @@ -241,16 +241,17 @@ def generate_part_mesh(obj: bpy.types.Object, obj_data: bpy.types.Mesh, bm: bmes bm_faces_new = bm_faces.new # Add node ID field to all vertices/edges/faces - init_node_id_layer = bm_verts.layers.string.new(constants.VLS_INIT_NODE_ID) - node_id_layer = bm_verts.layers.string.new(constants.VLS_NODE_ID) - node_origin_layer = bm_verts.layers.string.new(constants.VLS_NODE_PART_ORIGIN) - node_is_fake_layer = bm_verts.layers.int.new(constants.VLS_NODE_IS_FAKE) + init_node_id_layer = bm_verts.layers.string.new(constants.VL_INIT_NODE_ID) + node_id_layer = bm_verts.layers.string.new(constants.VL_NODE_ID) + node_origin_layer = bm_verts.layers.string.new(constants.VL_NODE_PART_ORIGIN) + node_is_fake_layer = bm_verts.layers.int.new(constants.VL_NODE_IS_FAKE) - beam_origin_layer = bm_edges.layers.string.new(constants.ELS_BEAM_PART_ORIGIN) - beam_indices_layer = bm_edges.layers.string.new(constants.ELS_BEAM_INDICES) + beam_origin_layer = bm_edges.layers.string.new(constants.EL_BEAM_PART_ORIGIN) + beam_indices_layer = bm_edges.layers.string.new(constants.EL_BEAM_INDICES) - face_origin_layer = bm_faces.layers.string.new(constants.FLS_FACE_PART_ORIGIN) - face_idx_layer = bm_faces.layers.int.new(constants.FLS_FACE_IDX) + face_origin_layer = bm_faces.layers.string.new(constants.FL_FACE_PART_ORIGIN) + face_idx_layer = bm_faces.layers.int.new(constants.FL_FACE_IDX) + face_flip_flag_layer = bm_faces.layers.int.new(constants.FL_FACE_FLIP_FLAG) inv_matrix_world = obj.matrix_world.inverted() bytes_part = bytes(part, 'utf-8') @@ -358,8 +359,6 @@ def _reimport_vehicle(context: bpy.types.Context, veh_collection: bpy.types.Coll pc_filepath = vehicle_bundle['config']['partConfigFilename'] parts = vehicle_bundle['chosenParts'].values() - context.scene['jbeam_editor_reimporting_jbeam'] = 1 # Prevents exporting jbeam - vertices, parts_edges, parts_tris, parts_quads, node_index_to_id = get_vertices_edges_faces(vehicle_bundle) parts_set = set() @@ -411,7 +410,6 @@ def _reimport_vehicle(context: bpy.types.Context, veh_collection: bpy.types.Coll for obj_data in obj_datas_to_remove: bpy.data.meshes.remove(obj_data, do_unlink=True) - veh_collection[constants.COLLECTION_VEHICLE_BUNDLE] = pickle.dumps(vehicle_bundle, -1) veh_collection[constants.COLLECTION_IO_CTX] = io_ctx veh_collection[constants.COLLECTION_VEH_FILES] = veh_files veh_collection[constants.COLLECTION_PC_FILEPATH] = pc_filepath @@ -422,7 +420,7 @@ def _reimport_vehicle(context: bpy.types.Context, veh_collection: bpy.types.Coll context.view_layer.objects.active = context.scene.objects[prev_active_obj_name] -def reimport_vehicle(context: bpy.types.Context, veh_collection: bpy.types.Collection, jbeam_files: dict): +def reimport_vehicle(context: bpy.types.Context, veh_collection: bpy.types.Collection, jbeam_files: dict, regenerate_mesh_on_change: bool): try: config_path = veh_collection[constants.COLLECTION_PC_FILEPATH] @@ -433,8 +431,13 @@ def reimport_vehicle(context: bpy.types.Context, veh_collection: bpy.types.Colle jbeam_parsing_errors, vehicle_bundle = load_jbeam(vehicle_directories, vehicle_config, jbeam_files) - # Create Blender meshes from JBeam data - _reimport_vehicle(context, veh_collection, vehicle_bundle) + if regenerate_mesh_on_change: + # Create Blender meshes from JBeam data + _reimport_vehicle(context, veh_collection, vehicle_bundle) + + veh_collection[constants.COLLECTION_VEHICLE_BUNDLE] = pickle.dumps(vehicle_bundle, -1) + + context.scene['jbeam_editor_reimporting_jbeam'] = 1 # Prevents exporting jbeam if not jbeam_parsing_errors: print('Done reimporting vehicle.') @@ -486,7 +489,7 @@ def import_vehicle(context: bpy.types.Context, config_path: str): return False -def on_files_change(context: bpy.types.Context, files_changed: dict): +def on_files_change(context: bpy.types.Context, files_changed: dict, regenerate_mesh_on_change: bool): collections = bpy.data.collections for collection in collections: @@ -501,12 +504,12 @@ def on_files_change(context: bpy.types.Context, files_changed: dict): # stats = pstats.Stats(pr) # stats.strip_dirs().sort_stats('cumtime').print_stats() - reimport_vehicle(context, collection, files_changed) + reimport_vehicle(context, collection, files_changed, regenerate_mesh_on_change) class JBEAM_EDITOR_OT_import_vehicle(Operator, ImportHelper): bl_idname = 'jbeam_editor.import_vehicle' - bl_label = 'Import JBeam' + bl_label = 'Import Vehicle' bl_description = 'Import a BeamNG Part Config file (.pc)' filename_ext = ".pc" diff --git a/jbeam_editor/text_editor.py b/jbeam_editor/text_editor.py index cef1a1b..c46433a 100644 --- a/jbeam_editor/text_editor.py +++ b/jbeam_editor/text_editor.py @@ -175,7 +175,7 @@ def check_open_int_file_for_changes(context: bpy.types.Context, undoing_redoing= scene[SCENE_PREV_TEXTS][short_filename] = curr_file_text import_vehicle.on_files_change(context, {filename: curr_file_text}) - import_jbeam.on_file_change(context, filename, curr_file_text) + import_jbeam.on_file_change(context, filename) file_changed = True if not undoing_redoing and file_changed: @@ -196,7 +196,7 @@ def check_open_int_file_for_changes(context: bpy.types.Context, undoing_redoing= return file_changed -def check_int_files_for_changes(context: bpy.types.Context, filenames: list, undoing_redoing=False, reimport=True): +def check_int_files_for_changes(context: bpy.types.Context, filenames: list, undoing_redoing=False, reimport=True, regenerate_mesh_on_change=True): scene = context.scene if SCENE_PREV_TEXTS not in scene: @@ -224,7 +224,7 @@ def check_int_files_for_changes(context: bpy.types.Context, filenames: list, und scene[SCENE_PREV_TEXTS][short_filename] = curr_file_text if reimport: - import_jbeam.on_file_change(context, filename, curr_file_text) + import_jbeam.on_file_change(context, filename, regenerate_mesh_on_change) if files_changed_short_names is None: files_changed_short_names = {} @@ -233,7 +233,7 @@ def check_int_files_for_changes(context: bpy.types.Context, filenames: list, und files_changed[filename] = curr_file_text if reimport and files_changed is not None: - import_vehicle.on_files_change(context, files_changed) + import_vehicle.on_files_change(context, files_changed, regenerate_mesh_on_change) if not undoing_redoing and files_changed_short_names is not None: # Insert new history into history stack diff --git a/tests/test_blender_plugin_helper.py b/tests/test_blender_plugin_helper.py index 4dcbbf6..17edc7b 100644 --- a/tests/test_blender_plugin_helper.py +++ b/tests/test_blender_plugin_helper.py @@ -100,7 +100,7 @@ def set_to_edit_mode_and_get_imported_mesh(self): assert type(obj_data) is bpy.types.Mesh bm = bmesh.from_edit_mesh(obj_data) - assert obj_data.get(constants.MESH_JBEAM_PART) == obj.name and constants.VLS_INIT_NODE_ID in bm.verts.layers.string and constants.VLS_NODE_ID in bm.verts.layers.string + assert obj_data.get(constants.MESH_JBEAM_PART) == obj.name and constants.VL_INIT_NODE_ID in bm.verts.layers.string and constants.VL_NODE_ID in bm.verts.layers.string return obj, obj_data, bm @@ -150,9 +150,9 @@ def deselect_all_vertices_edges_faces(self, bm: bmesh.types.BMesh): def select_node_by_node_id(self, bm: bmesh.types.BMesh, the_node_id): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] selected_node = False selected_vert = None @@ -174,9 +174,9 @@ def select_node_by_node_id(self, bm: bmesh.types.BMesh, the_node_id): def select_nodes_by_node_id(self, bm: bmesh.types.BMesh, node_ids_to_select: set): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] nodes_selected = set() verts_selected = set() @@ -196,8 +196,8 @@ def select_nodes_by_node_id(self, bm: bmesh.types.BMesh, node_ids_to_select: set def delete_selected_vertices(self, bm: bmesh.types.BMesh): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] v: bmesh.types.BMVert for v in bm.verts: if v.select: @@ -205,8 +205,8 @@ def delete_selected_vertices(self, bm: bmesh.types.BMesh): def move_selected_node(self, bm: bmesh.types.BMesh, new_pos): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] v: bmesh.types.BMVert for v in bm.verts: if v.select: @@ -215,8 +215,8 @@ def move_selected_node(self, bm: bmesh.types.BMesh, new_pos): def rename_selected_node(self, bm: bmesh.types.BMesh, new_node_id): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] v: bmesh.types.BMVert for v in bm.verts: if v.select: @@ -228,9 +228,9 @@ def add_nodes_from_imported_jbeam_mesh(self, node_ids: list, node_id_to_new_posi self.select_imported_jbeam_mesh() obj, obj_data, bm = self.set_to_edit_mode_and_get_imported_mesh() - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - part_origin_layer = bm.verts.layers.string[constants.VLS_NODE_PART_ORIGIN] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + part_origin_layer = bm.verts.layers.string[constants.VL_NODE_PART_ORIGIN] # Add nodes bm.verts.ensure_lookup_table() @@ -334,10 +334,10 @@ def delete_selected_faces(self, bm: bmesh.types.BMesh): def select_beams(self, bm: bmesh.types.BMesh, beams_to_select: set): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] beams_selected = set() edges_selected = set() @@ -361,10 +361,10 @@ def select_beams(self, bm: bmesh.types.BMesh, beams_to_select: set): def select_faces(self, bm: bmesh.types.BMesh, faces_to_select: set): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] tris_quads_selected = set() faces_selected = set() @@ -389,7 +389,7 @@ def add_beams_from_imported_jbeam_mesh(self, beams: list): for (n1, n2) in beams: obj, obj_data, bm = self.set_to_edit_mode_and_get_imported_mesh() - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] v1 = self.select_node_by_node_id(bm, n1) v2 = self.select_node_by_node_id(bm, n2) @@ -417,7 +417,7 @@ def add_faces_from_imported_jbeam_mesh(self, faces: list): for face in faces: obj, obj_data, bm = self.set_to_edit_mode_and_get_imported_mesh() - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] face_list = [self.select_node_by_node_id(bm, node) for node in face] len_face_list = len(face_list) assert len_face_list in (3,4) diff --git a/tests/vehicle_test_blender_plugin_helper.py b/tests/vehicle_test_blender_plugin_helper.py index 617aecf..c46b965 100644 --- a/tests/vehicle_test_blender_plugin_helper.py +++ b/tests/vehicle_test_blender_plugin_helper.py @@ -108,7 +108,7 @@ def set_to_edit_mode_and_get_imported_mesh(self, part_name): assert type(obj_data) is bpy.types.Mesh bm = bmesh.from_edit_mesh(obj_data) - assert obj_data.get(constants.MESH_JBEAM_PART) == obj.name and constants.VLS_INIT_NODE_ID in bm.verts.layers.string and constants.VLS_NODE_ID in bm.verts.layers.string + assert obj_data.get(constants.MESH_JBEAM_PART) == obj.name and constants.VL_INIT_NODE_ID in bm.verts.layers.string and constants.VL_NODE_ID in bm.verts.layers.string return obj, obj_data, bm @@ -158,9 +158,9 @@ def deselect_all_vertices_edges_faces(self, bm: bmesh.types.BMesh): def select_node_by_node_id(self, bm: bmesh.types.BMesh, the_node_id): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] selected_node = False selected_vert = None @@ -182,9 +182,9 @@ def select_node_by_node_id(self, bm: bmesh.types.BMesh, the_node_id): def select_nodes_by_node_id(self, bm: bmesh.types.BMesh, node_ids_to_select: set): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] nodes_selected = set() verts_selected = set() @@ -204,8 +204,8 @@ def select_nodes_by_node_id(self, bm: bmesh.types.BMesh, node_ids_to_select: set def delete_selected_vertices(self, bm: bmesh.types.BMesh): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] v: bmesh.types.BMVert for v in bm.verts: if v.select: @@ -213,8 +213,8 @@ def delete_selected_vertices(self, bm: bmesh.types.BMesh): def move_selected_node(self, bm: bmesh.types.BMesh, new_pos): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] v: bmesh.types.BMVert for v in bm.verts: if v.select: @@ -223,8 +223,8 @@ def move_selected_node(self, bm: bmesh.types.BMesh, new_pos): def rename_selected_node(self, bm: bmesh.types.BMesh, new_node_id): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] v: bmesh.types.BMVert for v in bm.verts: if v.select: @@ -236,9 +236,9 @@ def add_nodes_from_imported_jbeam_mesh(self, part_name: str, node_ids: list, nod self.select_jbeam_meshes(part_name) obj, obj_data, bm = self.set_to_edit_mode_and_get_imported_mesh(part_name) - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - part_origin_layer = bm.verts.layers.string[constants.VLS_NODE_PART_ORIGIN] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + part_origin_layer = bm.verts.layers.string[constants.VL_NODE_PART_ORIGIN] # Add nodes bm.verts.ensure_lookup_table() @@ -342,10 +342,10 @@ def delete_selected_faces(self, bm: bmesh.types.BMesh): def select_beams(self, bm: bmesh.types.BMesh, beams_to_select: set): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] beams_selected = set() edges_selected = set() @@ -369,10 +369,10 @@ def select_beams(self, bm: bmesh.types.BMesh, beams_to_select: set): def select_faces(self, bm: bmesh.types.BMesh, faces_to_select: set): - init_node_id_layer = bm.verts.layers.string[constants.VLS_INIT_NODE_ID] - node_id_layer = bm.verts.layers.string[constants.VLS_NODE_ID] - node_is_fake_layer = bm.verts.layers.int[constants.VLS_NODE_IS_FAKE] - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] + init_node_id_layer = bm.verts.layers.string[constants.VL_INIT_NODE_ID] + node_id_layer = bm.verts.layers.string[constants.VL_NODE_ID] + node_is_fake_layer = bm.verts.layers.int[constants.VL_NODE_IS_FAKE] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] tris_quads_selected = set() faces_selected = set() @@ -397,7 +397,7 @@ def add_beams_from_imported_jbeam_mesh(self, part_name: str, beams: list): for (n1, n2) in beams: obj, obj_data, bm = self.set_to_edit_mode_and_get_imported_mesh(part_name) - beam_indices_layer = bm.edges.layers.string[constants.ELS_BEAM_INDICES] + beam_indices_layer = bm.edges.layers.string[constants.EL_BEAM_INDICES] v1 = self.select_node_by_node_id(bm, n1) v2 = self.select_node_by_node_id(bm, n2) @@ -425,7 +425,7 @@ def add_faces_from_imported_jbeam_mesh(self, part_name: str, faces: list): for face in faces: obj, obj_data, bm = self.set_to_edit_mode_and_get_imported_mesh(part_name) - face_idx_layer = bm.faces.layers.int[constants.FLS_FACE_IDX] + face_idx_layer = bm.faces.layers.int[constants.FL_FACE_IDX] face_list = [self.select_node_by_node_id(bm, node) for node in face] len_face_list = len(face_list) assert len_face_list in (3,4) @@ -486,7 +486,7 @@ def cleanup(self, veh_name): bpy.data.collections.remove(collection) assert len(bpy.data.objects) == 3 # camera, light, cube - + bpy.context.scene.ui_properties.affect_node_references = False