diff --git a/bleMD/__init__.py b/bleMD/__init__.py index 6b913f8..24392e4 100644 --- a/bleMD/__init__.py +++ b/bleMD/__init__.py @@ -80,8 +80,8 @@ class WM_OT_bleMDReadLAMMPSFile(Operator): my_tmp_cntr = 0 def execute(self, context): - pipeline = startOvito() - loadUpdatedData(pipeline) + ob, pipeline = startOvito() + loadUpdatedData(ob, pipeline) return {'FINISHED'} @@ -92,8 +92,9 @@ class WM_OT_bleMDRigKeyframes(Operator): def execute(self, context): scene = bpy.context.scene - nlammpsframes = scene.bleMD_props.number_of_lammps_frames - stride = scene.bleMD_props.lammps_frame_stride + obj = bpy.context.object + nlammpsframes = obj.bleMD_props.number_of_lammps_frames + stride = obj.bleMD_props.lammps_frame_stride keyInterp = context.preferences.edit.keyframe_new_interpolation_type context.preferences.edit.keyframe_new_interpolation_type = 'LINEAR' @@ -104,9 +105,9 @@ def execute(self, context): for key in ob.data.shape_keys.key_blocks: ob.shape_key_remove(key) - pipeline = startOvito() + ob, pipeline = startOvito() for i in range(nlammpsframes): - loadUpdatedData(pipeline) + loadUpdatedData(ob, pipeline) print(i) scene.frame_set(i*stride) @@ -136,6 +137,7 @@ class WM_OT_bleMDRenderAnimation(Operator): def execute(self, context): scene = bpy.context.scene + obj = bpy.context.obj # Remove shape keyframes if there are any ob = bpy.data.objects['MD_Object'] @@ -143,13 +145,13 @@ def execute(self, context): for key in ob.data.shape_keys.key_blocks: ob.shape_key_remove(key) - pipeline = startOvito() + ob, pipeline = startOvito() for frame in range(scene.frame_start, scene.frame_end + 1): print("Rendering ", frame) - scene.render.filepath = scene.bleMD_props.renderpath + \ + scene.render.filepath = obj.bleMD_props.renderpath + \ str(frame).zfill(4) scene.frame_set(frame) - loadUpdatedData(pipeline) + loadUpdatedData(ob, pipeline) bpy.ops.render.render(write_still=True) return {'FINISHED'} @@ -170,7 +172,8 @@ class WM_OT_Enumerator(Operator): def execute(self, context): scene = context.scene - mytool = scene.bleMD_props + obj = context.object + mytool = obj.bleMD_props mat = bpy.data.materials.get("my_mat") mat_nodes = mat.node_tree.nodes @@ -209,7 +212,12 @@ class OBJECT_PT_bleMDPanel(Panel): @classmethod def poll(self, context): - return context.object is not None + if not len(bpy.context.selected_objects): return False + if not context.object: return False + ob = bpy.context.object + if 'bleMD_object' not in ob.data.keys(): return False + return True + #return context.object is not None def execute(self, context): return {'FINISHED'} @@ -220,7 +228,8 @@ def draw_header(self, context): def draw(self, context): layout = self.layout scene = context.scene - mytool = scene.bleMD_props + obj = context.object + mytool = obj.bleMD_props layout.prop(mytool, "override_defaults") @@ -243,11 +252,11 @@ def draw(self, context): # # DATA FIELDS # - if len(scene.datafieldlist): + if len(obj.datafieldlist): layout.label(text="Data fields from file") row = layout.row() - row.template_list("bleMDDataFieldsList", "The_List", scene, - "datafieldlist", scene, "list_index") + row.template_list("bleMDDataFieldsList", "The_List", obj, + "datafieldlist", obj, "list_index") layout.operator("wm.read_lammps_file") layout.label(text="Basic Shader",) @@ -272,6 +281,30 @@ def draw(self, context): layout.operator("wm.render_animation") +class bleMDOpenFileDialogOperator(bpy.types.Operator): + bl_idname = "object.blemd_open_file_dialog" + bl_label = "Open MD file" + + filepath: StringProperty(subtype='FILE_PATH') + + def execute(self, context): + #resetDefaultsForMD() + #print("MY DATAFILE=",self.filepath) + ob, pipeline = startOvito(hardrefresh=True,filename=self.filepath) + #loadUpdatedData(ob, pipeline) + #bpy.context.object.bleMD_props.lammpsfile = self.filepath + return {'FINISHED'} + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + +# Only needed if you want to add into a dynamic menu. +def bleMDOpenFileDialogOperator_menu(self, context): + self.layout.operator_context = 'INVOKE_DEFAULT' + self.layout.operator(bleMDOpenFileDialogOperator.bl_idname, text="Dialog Operator") + # ------------------------------------------------------------------------ # Registration @@ -287,6 +320,7 @@ def draw(self, context): bleMDDataFieldsLIProperty, bleMDDataFieldsList, OBJECT_PT_bleMDPanel, + bleMDOpenFileDialogOperator, ) @@ -311,10 +345,10 @@ def register(): bpy.app.handlers.frame_change_post.clear() - bpy.types.Scene.bleMD_props = PointerProperty(type=bleMDProperties) + bpy.types.Object.bleMD_props = PointerProperty(type=bleMDProperties) - bpy.types.Scene.datafieldlist = CollectionProperty(type=bleMDDataFieldsLIProperty) - bpy.types.Scene.list_index = IntProperty( + bpy.types.Object.datafieldlist = CollectionProperty(type=bleMDDataFieldsLIProperty) + bpy.types.Object.list_index = IntProperty( name="Index for datafieldlist", default=0) @@ -328,14 +362,16 @@ def register(): icons_dir = os.path.join(os.path.dirname(__file__), "resources") custom_icons.load("ovito", os.path.join(icons_dir, "ovito.png"), 'IMAGE') + bpy.types.VIEW3D_MT_add.append(bleMDOpenFileDialogOperator_menu) + def unregister(): from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls) - del bpy.types.Scene.bleMD_props + del bpy.types.Object.bleMD_props bpy.utils.previews.remove(custom_icons) - + bpy.types.VIEW3D_MT_view.remove(bleMDOpenFileDialogOperator_menu) if __name__ == "__main__": diff --git a/bleMD/bleMDNodes.py b/bleMD/bleMDNodes.py index 32b0447..a4e1b65 100644 --- a/bleMD/bleMDNodes.py +++ b/bleMD/bleMDNodes.py @@ -1,7 +1,10 @@ import bpy def create_geonodes(): - obj = bpy.data.objects["MD_Object"] + #obj = bpy.data.objects["MD_Object"] + obj = bpy.context.object + if not "bleMD_object" in obj.data.keys(): + return geo_nodes = obj.modifiers.get("build_geonode") if not geo_nodes: @@ -12,7 +15,7 @@ def create_geonodes(): def create_group(name="geonode_object"): - mytool = bpy.context.scene.bleMD_props + mytool = bpy.context.object.bleMD_props group = bpy.data.node_groups.get(name) # check if a group already exists @@ -86,19 +89,21 @@ def create_material(): else: mat = bpy.data.materials["my_mat"] - obj = bpy.data.objects["MD_Object"] - obj.data.materials.append(mat) + #obj = bpy.data.objects["MD_Object"] + obj = bpy.context.object + if "bleMD_object" in obj.data.keys(): + obj.data.materials.append(mat) def updateDefaultShader(): - my_normalhigh = bpy.context.scene.bleMD_props.my_normalhigh - my_normallow = bpy.context.scene.bleMD_props.my_normallow + my_normalhigh = bpy.context.object.bleMD_props.my_normalhigh + my_normallow = bpy.context.object.bleMD_props.my_normallow my_range = my_normalhigh - my_normallow bpy.data.materials["my_mat"].node_tree.nodes["bleMD_MathNode1"].inputs[1].default_value = my_normallow bpy.data.materials["my_mat"].node_tree.nodes["bleMD_MathNode2"].inputs[1].default_value = my_range def defaultSettings(): - if not bpy.context.scene.bleMD_props.override_defaults: + if not bpy.context.object.bleMD_props.override_defaults: return for scene in bpy.data.scenes: @@ -112,7 +117,7 @@ def defaultSettings(): if space.type == 'VIEW_3D': space.shading.type = 'RENDERED' - bpy.context.space_data.context = 'OBJECT' + #bpy.context.space_data.context = 'OBJECT' def makeSun(): diff --git a/bleMD/bleMDProperties.py b/bleMD/bleMDProperties.py index d551588..8e6f03e 100644 --- a/bleMD/bleMDProperties.py +++ b/bleMD/bleMDProperties.py @@ -39,7 +39,8 @@ class bleMDProperties(bpy.types.PropertyGroup): def updateFrameStride(self, context): scene = context.scene - mytool = scene.bleMD_props + obj = context.object + mytool = obj.bleMD_props nframes = mytool.number_of_lammps_frames stride = mytool.lammps_frame_stride bpy.context.scene.frame_start = 0 @@ -69,9 +70,9 @@ def updateFrameStride(self, context): max=100 ) - def openLAMMPSFile(self, context): - resetDefaultsForMD() - startOvito(hardrefresh=True) +# def openLAMMPSFile(self, context): +# resetDefaultsForMD() +# startOvito(hardrefresh=True) lammpsfile: StringProperty( @@ -80,7 +81,7 @@ def openLAMMPSFile(self, context): default="", maxlen=1024, subtype='FILE_PATH', - update=openLAMMPSFile, + #update=openLAMMPSFile, ) renderpath: StringProperty( @@ -92,8 +93,8 @@ def openLAMMPSFile(self, context): ) def my_normalhigh_normallow_update(self,context): - my_normalhigh = bpy.context.scene.bleMD_props.my_normalhigh - my_normallow = bpy.context.scene.bleMD_props.my_normallow + my_normalhigh = bpy.context.object.bleMD_props.my_normalhigh + my_normallow = bpy.context.object.bleMD_props.my_normallow my_range = my_normalhigh - my_normallow bpy.data.materials["my_mat"].node_tree.nodes["bleMD_MathNode1"].inputs[1].default_value = my_normallow bpy.data.materials["my_mat"].node_tree.nodes["bleMD_MathNode2"].inputs[1].default_value = my_range @@ -122,7 +123,7 @@ def colormap_update(self,context): break bpy.data.materials['my_mat'].node_tree.nodes['Color Ramp'].color_ramp.elements.remove(elements[-1]) - K,R,G,B = getkeys(context.scene.bleMD_props.colormap) + K,R,G,B = getkeys(context.object.bleMD_props.colormap) bpy.data.materials['my_mat'].node_tree.nodes['Color Ramp'].color_ramp.elements[0].color = (R[0],G[0],B[0],1) for k,r,g,b in zip(K,R,G,B): @@ -149,10 +150,14 @@ def colormap_update(self,context): def updateRadius(self, context): scene = context.scene - mytool = scene.bleMD_props + obj = context.object + mytool = obj.bleMD_props radius = mytool.my_radius - obj = bpy.data.objects["MD_Object"] - if not obj: return + obj = context.object + if not "bleMD_object" in obj.data.keys(): + return + #obj = bpy.data.objects["MD_Object"] + #if not obj: return geonodes = obj.modifiers["build_geonode"] if not geonodes: return nodegroup = geonodes.node_group @@ -169,11 +174,11 @@ def updateRadius(self, context): def colorby_property_items(self,context): ret = [] - for item in context.scene.datafieldlist: + for item in context.object.datafieldlist: if item.enable: ret.append((item.name,item.name,item.name)) return ret def colorby_property_update(self,context): - prop = context.scene.bleMD_props.colorby_property + prop = context.object.bleMD_props.colorby_property print(prop) bpy.data.materials["my_mat"].node_tree.nodes['Attribute'].attribute_name = prop colorby_property: EnumProperty( diff --git a/bleMD/bleMDUtils.py b/bleMD/bleMDUtils.py index d852fd2..1bc565d 100644 --- a/bleMD/bleMDUtils.py +++ b/bleMD/bleMDUtils.py @@ -8,7 +8,7 @@ # values so that things look good the first time. # def resetDefaultsForMD(): - if not bpy.context.scene.bleMD_props.override_defaults: + if not bpy.context.object.bleMD_props.override_defaults: return # Set the camera so that objects too far away do not get clipped off @@ -30,16 +30,46 @@ def resetDefaultsForMD(): # KEY SUBROUTINE 1/2 # Opens Ovito and does basic communication with dump fil # -def startOvito(hardrefresh=False): - filename = bpy.context.scene.bleMD_props.lammpsfile - interp = bpy.context.scene.bleMD_props.lammps_frame_stride - scene = bpy.context.scene - mytool = scene.bleMD_props +def startOvito(hardrefresh=False, filename=None): + if not filename: + filename = bpy.context.object.bleMD_props.lammpsfile + + + # + # Determine whether we need to create a new object + # + + ob = bpy.context.object + createNewObject = False + if not len(bpy.context.selected_objects): createNewObject = True + if not ob: createNewObject = True + if ob: + if not "bleMD_object" in ob.data.keys(): + createNewObject = True + if createNewObject: + print("Creating new MD object") + # Object does not yet exist: create it + me = bpy.data.meshes.new("MD_Mesh") + ob = bpy.data.objects.new("MD_Object", me) + ob.data['bleMD_object'] = True + ob.show_name = True + + # Select the object + bpy.context.collection.objects.link(ob) + bpy.ops.object.select_all(action='DESELECT') + ob.select_set(True) + bpy.context.view_layer.objects.active = ob + ob.bleMD_props.lammpsfile = filename + + + interp = ob.bleMD_props.lammps_frame_stride + mytool = ob.bleMD_props # # Load the file # from ovito.io import import_file + print("FILENAME = ",filename) pipeline = import_file(filename, sort_particles=True) @@ -54,7 +84,7 @@ def startOvito(hardrefresh=False): # nframes = pipeline.source.num_frames mytool.number_of_lammps_frames = nframes - bpy.context.scene.frame_end = nframes * mytool.lammps_frame_stride + mytool.frame_end = nframes * mytool.lammps_frame_stride mytool.valid_lammps_file = True # @@ -63,29 +93,30 @@ def startOvito(hardrefresh=False): data = pipeline.compute() props = list(data.particles.keys()) if hardrefresh: - scene.datafieldlist.clear() + ob.datafieldlist.clear() for prop in props: - if prop not in [i.name for i in scene.datafieldlist]: - item = scene.datafieldlist.add() + if prop not in [i.name for i in ob.datafieldlist]: + item = ob.datafieldlist.add() item.name = prop if prop == "Position": item.enable = True item.editable = False - return pipeline + return ob, pipeline + # # KEY SUBROUTINE 2/2 # Updates the current data based on the Blender timestep # -def loadUpdatedData(pipeline): +def loadUpdatedData(ob, pipeline): # Determine what the frame (or frames if interpolating) # are that need to be pulled from frame = bpy.data.scenes[0].frame_current - interp = bpy.context.scene.bleMD_props.lammps_frame_stride + interp = bpy.context.object.bleMD_props.lammps_frame_stride # Determine interpolation (if any) fac = (frame % interp)/interp @@ -94,20 +125,22 @@ def loadUpdatedData(pipeline): print("FAC = ", fac) print("frame_lo ", frame_lo) + me = ob.data + # Set up the object or grab the existing object # TODO: how do we handle multiple objects? - if not "MD_Object" in bpy.data.objects.keys(): - print("Creating new MD object") - # Object does not yet exist: create it - me = bpy.data.meshes.new("MD_Mesh") - ob = bpy.data.objects.new("MD_Object", me) - ob.show_name = True - bpy.context.collection.objects.link(ob) - else: - # Object exists: use it - print("Using existing") - ob = bpy.data.objects['MD_Object'] - me = ob.data + #if not "MD_Object" in bpy.data.objects.keys(): + # print("Creating new MD object") + # # Object does not yet exist: create it + # me = bpy.data.meshes.new("MD_Mesh") + # ob = bpy.data.objects.new("MD_Object", me) + # ob.show_name = True + # bpy.context.collection.objects.link(ob) + #else: + # # Object exists: use it + # print("Using existing") + # ob = bpy.data.objects['MD_Object'] + # me = ob.data # Update the data - storing the appropriate Ovito data # in python data structure, but no updates yet. @@ -115,7 +148,7 @@ def loadUpdatedData(pipeline): if fac == 0: data = pipeline.compute(frame_lo) coords = [list(xyz) for xyz in data.particles.positions] - for prop in bpy.context.scene.datafieldlist: + for prop in ob.datafieldlist: if prop.enable and prop.editable: attrs[prop.name] = [x for x in data.particles[prop.name]] #c_csym = [x for x in data.particles['c_csym']] @@ -125,7 +158,7 @@ def loadUpdatedData(pipeline): data_hi = pipeline.compute(frame_hi) coords = [list((1-fac)*xyz_lo + fac*xyz_hi) for xyz_lo, xyz_hi in zip(data_lo.particles.positions, data_hi.particles.positions)] - for prop in bpy.context.scene.datafieldlist: + for prop in ob.datafieldlist: if prop.enable and prop.editable: attrs[prop.name] = [(1-fac)*x_lo + fac*x_hi for x_lo, x_hi in zip(data_lo.particles[prop.name], data_hi.particles[prop.name])] @@ -139,7 +172,7 @@ def loadUpdatedData(pipeline): me.from_pydata(coords, [], []) # Now, we go through the properties that were selected in the panel # and set each of those properties as attributes - for prop in bpy.context.scene.datafieldlist: + for prop in ob.datafieldlist: if prop.enable and prop.editable: attr = me.attributes.new(prop.name, 'FLOAT', 'POINT') attr.data.foreach_set("value", attrs[prop.name]) @@ -159,7 +192,7 @@ def loadUpdatedData(pipeline): v.co = new_location # Here we update the properties (e.g. c_csym) - for prop in bpy.context.scene.datafieldlist: + for prop in ob.datafieldlist: if prop.enable and prop.editable: if not prop.name in me.attributes.keys(): attr = me.attributes.new(prop.name, 'FLOAT', 'POINT') @@ -179,3 +212,4 @@ def loadUpdatedData(pipeline): # Call setup function - Jackson setup() +