1
$\begingroup$

I have written an elaborate python code that creates complex objects from scratch. [Well, technically I use the numpy functions save and load here and there to fetch integer and float array data I generated previously.] My code generates, say, a hundred or so complex objects. Now, as part of the artistic methodology I propose, I want people to be able to look at the objects the code has generated, and see if they like them. If there is something wrong, say one of them is a soccer ball that is just too small, I want them to be able to go into the code and edit the value of the radius of the ball, to make it bigger. [..I realize that there is probably some part of the api, or an addon, that will allow me to allow them to do this super efficiently, but that isn't what this question is about. Maybe I'll make a separate question about that later..]

Now, having changed that value, (..they might have changed multiple values of different objects..) I want them to be able to run the code again, to see what the effect is of the new value(s). Of course, if there wasn't anything wrong with any of the other hundred complex objects, I don't want the code to re-create them all over again from scratch. So I would like the code to have saved them all the first time around so that the next time it is called, all the parts that haven't been changed will just get re-loaded. I would have a script to identify which ones had been changed, and which not.

Now, to create an object, you need arrays for vertices, indices, where the loop of a given surface starts, and how many vertices define it. I know how to save -that- information (which is used to create the object mesh) as a set of arrays, then load it back in. If there isn't any way of saving objects, I'll just do that. What I'm wondering is whether I can go the extra step to saving the object.

To be clear about what I mean by object, it's what is produced by the following python line:

bpy.data.objects.new(). That gets followed by things like object.data.materials.append(materials[material_number]) to give it a material, and body_of_object = object.vertex_groups.new( name = material_name ) and body_of_object.add( vertex_group, wanted_weight, 'REPLACE' ) to define vertex groups.

This "object" that has been created, eventually gets linked into a scene, and displayed.

How to save all those other objects and then load them back in again all with Python?

What file format should I use? I'm reading about .obj files and .blend files. In my understanding, a .blend file is a whole big whopping thing that includes the workspace itself. Totally not what I want. But people are saying that that's the only way of saving all the information.

Still researching. Intuitively .obj is what I want, but I don't know enough about what it actually is, and I've seen places where people explicitly recommend against it, in favour of .blend . You see my confusion.

Note -> This has been edited. I would not be surprised if there is need for more edits in the future..

Comment on the comment below:

"I've discussed this with him offline. He's left out quite a bit of critical context: He's running a script. He's changing that script. He wants the script to only run the changed code, which isn't possible."

..I wouldn't say I'm selectively choosing to not run part of the code, which is how I interpret the above. My code uses loops to generate many variations of a few kinds of objects. So some of those variations would still be good and not need to be re-calculated, while others -would- need to be re-calculated. So the code would have to be changed (an if-else statement should do the trick), so that for each iteration of the loop, either it re-calculates, or it loads up the object that was created the last time.

Edit: Note some interesting opinions getting passed through Blender Artists

Edit 2: It's now looking like the answer to this question involves saving the object as a blend. So the first answer, which mentions blend-libraries, may be on the right track. I'm now researching how to save and load blend files with python (eta at least a few days). The question is how to do this while only saving precisely what I want to save so I get everything I need without wasting memory. ..and doing it quickly and elegantly, etc..

More Edits: The following pages: save to multiple blend files and load libraries seem to contain information of a great deal of interest. And now: libraries documentation

$\endgroup$

3 Answers 3

4
$\begingroup$

Eventually, the right answer will be to use the Asset Manager; but the Python interface to it is not ready for prime time. For now you could export and import from obj files or other mesh storage formats but the reason people recommend against is that none of those formats match Blender's one for one, so you can lose information; especially about materials; when you export.

For now, I would recommend using the Python interface for Data Libraries. The blend files that you use for data libraries can be fairly small. They don't need the HDRIs, images, and other data that can be read in from an external file, and you can organize them into categories with only related items in a single data file. They have the advantage that because they are blend files you don't have to worry about data loss when importing and exporting.

$\endgroup$
7
  • $\begingroup$ ..fascinating.. ..this also sounds like a rabbit hole.. thanks! ..I'm also hesitant about accepting an answer until I've thoroughly researched the suggested course of action and have begun using it. ..that might be considered "discouraged" behavior? $\endgroup$
    – juggler
    Apr 20 at 19:34
  • 3
    $\begingroup$ @juggler you should accept the answer if it solved your problem or answered your question. If it didn't, you shouldn't accept it. If you're not sure at the moment, you can still upvote the answer by clicking on the upward facing arrow in the top left of the post, as a way to thank the person who answered. You can come back at a later point to accept the answer, which is done by using the checkmark under the arrows in the top left. Cheers $\endgroup$
    – Gorgious
    Apr 20 at 19:39
  • $\begingroup$ @juggler Gorgious is right. If this isn't enough of an answer, don't accept it yet. I can add detail abut how to use data libraries; and there are plenty of other questions here on the specific topic you can investigate. You might want to read What should I do when someone answers my question? if you haven't aready. $\endgroup$ Apr 20 at 19:42
  • $\begingroup$ I'm just going to accept it. Even if I don't end up using it, which seems unlikely, it's valuable insight! $\endgroup$
    – juggler
    Apr 20 at 22:25
  • $\begingroup$ I've been informed that my question is woefully incomplete, and therefore -can't- be answered properly. I'll have to come back and edit it. $\endgroup$
    – juggler
    Apr 20 at 22:28
2
$\begingroup$

I think your main issue is that you're confusing the term "object" with "mesh" and how it relates to data in Blender.

"Object" is a term to define a bundle of different concepts, mesh data, vertex groups, animation keyframes, material assignment, modifiers and so on.

"Mesh" is the term for a group of vertices, edges and faces, generated by BMesh, usually with a fair bit of math, to create 3D objects within Blender.

Whenever someone uses a script to generate a mesh it is floating in memory and only gets displayed when the programmer calls bm.to_mesh() to draw the mesh. Until that time it only exists as an array of verts with locations in 3D space.

I'm not sure of your exact end goal in terms of user experience, but using Python, you could easily define a number of variables using user input from a plain old input() function, ask the user "Does this mesh look like what you want?" if they click no, ask for the new values, and then re run the BMesh script with their revised values.

This is literally the simplest possible scenario and could only go upwards in terms of complexity.

beyond that. you could use the blender internal ui modules to make something like:

import bpy
import bmesh


class CreateCylinderMesh(bpy.types.Operator):
    bl_idname = "mesh.create_cylinder_custom"
    bl_label = "Create Cylinder"
    bl_options = {'REGISTER', "BLOCKING", "UNDO"}
        
    # properties that user will later define in a popup
    radius: bpy.props.FloatProperty(name="radius", default=0.5)
    height: bpy.props.FloatProperty(name="height", default=0.5)
    
    @classmethod
    def poll(cls, context):
        return True
    
    def create_cylinder(self):
        # Make a new BMesh
        bm = bmesh.new()
        bmesh.ops.create_circle(
            bm,
            cap_ends=False,
            radius=self.radius,
            segments=8)
       
        edges = [edge for edge in bm.edges if isinstance(edge, bmesh.types.BMEdge)]

        # Extrude and create geometry for the top of the cylinder
        ret = bmesh.ops.extrude_edge_only(
            bm,
            edges=edges)
        geometry = ret["geom"]
        verts = [
                elem for elem in geometry 
                if isinstance(elem, bmesh.types.BMVert)
                ]
        # Use the user input for height
        bmesh.ops.translate(
            bm,
            verts=verts,
            vec=(0.0, 0.0, self.height)
            )
        
        del ret
        # Done with creating the mesh, simply link it into the scene so we can see it

        me = bpy.data.meshes.new("Mesh")
        bm.to_mesh(me)
        bm.free()
        me.update()
        
        obj = bpy.data.objects.new("Object", me)
        bpy.context.collection.objects.link(obj)

        # Select and make active
        bpy.context.view_layer.objects.active = obj
        return {'FINISHED'}        
       
    # stuff to create the user interaction etc.
    def execute(self, context):
        return self.create_cylinder()

    def draw(self, context):
        row = self.layout
        row.prop(self, "radius", text="Enter the Radius: ")
        row.prop(self, "height", text="Enter the Height: ")

    # this will call a pop that will recreate the mesh every time until the user 
    # moves their mouse away from the dialog
# there are other options that ask for confirmation etc.
    def invoke(self, context, event):
        return context.window_manager.invoke_props_popup(self, event)
        
    
        
def register():
    bpy.utils.register_class(CreateCylinderMesh)

def unregister():
    bpy.utils.unregister_class(CreateCylinderMesh)    
        
if __name__ == "__main__":
    register()
    # test run
    bpy.ops.mesh.create_cylinder_custom('INVOKE_DEFAULT')





$\endgroup$
1
  • 1
    $\begingroup$ Your suggested script with the blender internal ui modules is very interesting and helpful. I've begun my exploration of them, and your proposed script will accelerate my efforts. Thank you! That said, this answer, technically, doesn't answer the question, which is, how to save the "objects" that have been created so they don't have to be re-created. My suspicion is that the answer has been provided by the dude from blender artists who tells me to save them as blend files. So my next step is to research that process to see if that's true. -which might mean data-libraries, a-la-Marty.. $\endgroup$
    – juggler
    Apr 21 at 17:53
0
$\begingroup$

"So the code would have to be changed (an if-else statement should do the trick), so that for each iteration of the loop, either it re-calculates, or it loads up the object that was created the last time." This isn't possible. When you run a script, the whole thing runs. You could separate your script into different self-contained pieces and execute each piece separately, and only change/reexecute the pieces you want.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.