Monday 13 June 2016

Adventures in Blender scripting - automating workflow

Blender Addons

I have decided to have a go at writing a Blender Addon to ease my Second Life Mesh development workflow.

I will approach this in a modular manner hoping to learn a lot about both Python and Blender as I go.

When I build in Blender I tend to either start with a Mesh Studio model and perhaps generate a series of LODs using that tool or create a High LOD model and duplicate it for the lower LODs. Having duplicated them I can then set about reducing the complexity.

The first task is to create a simple operator to take a selected objects and create duplicates that will be used as the Medium, Low, Lowest and perhaps even Physics meshes.



I am going to be using the rather awesome looking code autocomplete blender addon to help me navigate my way through the peculiarities of Blender.

Task 1 - Creating the objects that we will use for the lower LODs

We are going to select an object and then duplicate it 4 times, renaming each duplicate with the LOD that it will represent as a suffix.

You would image that duplicating objects would be simple enough given the relative frequency that it must happen when modelling but in fact in is not quite so straightforward. I found this blog post from someone who had gone this way before and had, happily chosen to share their experience.

To quote Bret's blog
One has to create a new mesh, then copy the data of the source object, then link to the scene. Here's the short function I'm using to do this.

# The following function is adapted from 
# Nick Keeline "Cloud Generator" addNewObject 
# from object_cloud_gen.py (an addon that comes with the Blender 2.6 package)
#
def duplicateObject(scene, name, copyobj):
 
    # Create new mesh
    mesh = bpy.data.meshes.new(name)
 
    # Create new object associated with the mesh
    ob_new = bpy.data.objects.new(name, mesh)
 
    # Copy data block from the old object into the new object
    ob_new.data = copyobj.data.copy()
    ob_new.scale = copyobj.scale
    ob_new.location = copyobj.location
 
    # Link new object to the given scene and select it
    scene.objects.link(ob_new)
    ob_new.select = True
 
    return ob_new

What we want to do though is iterate through a list of selected objects and for each that we find, create four copies, each named with the correct suffix and then push those copies to different layers in the scene.

Pushing to layers is something I do for practical reasons.

    def moveToLayers(self, obj, layers_tuple):
        obj.layers = [ i in layers_tuple for i in range(len(obj.layers)) ]
        return
This function can then be used as follows.
            moveToLayers(medLOD, {1})  
Or for more than one layer
            moveToLayers(medLOD, {1, 3,4,5})  
Putting this all together, and adding a check for pre-existing LOD models we get
import bpy

class MakeLODModelsFromSelection(bpy.types.Operator):
    bl_idname = "my_operator.make_lodmodels_from_selection"
    bl_label = "Make LOD models From Selection"
    bl_description = ""
    bl_options = {"REGISTER"}

    def createNewLODModel(self, orig_obj, LOD):
        new_name = orig_obj.name+"_"+LOD
        # check if it already exists
        if bpy.data.objects.get(new_name) is not None:
            return bpy.data.objects.get(new_name)
        # create a new mesh with name
        mesh = bpy.data.meshes.new(new_name)
        # create a new object associated to that mesh
        ob_new = bpy.data.objects.new(new_name, mesh)
        #copy the data block from the old object ot the new one
        ob_new.data = orig_obj.data.copy()
        ob_new.scale = orig_obj.scale
        ob_new.location = orig_obj.location
        # Link new object to the given scene and select it
        bpy.context.scene.objects.link(ob_new)
        return ob_new
    
    def moveToLayers(self, obj, layers_tuple):
        obj.layers = [ i in layers_tuple for i in range(len(obj.layers)) ]
        return
    
    def execute(self, context):
        for object in context.selected_objects:
            self.moveToLayers(object, {0})
            medLOD=self.createNewLODModel(object, "MED")
            self.moveToLayers(medLOD, {1})        
            lowLOD=self.createNewLODModel(object, "LOW")
            self.moveToLayers(lowLOD, {2})
            lowestLOD=self.createNewLODModel(object, "LOWEST")
            self.moveToLayers(lowestLOD, {3})
            physics=self.createNewLODModel(object, "PHYS")
            self.moveToLayers(physics, {4})            
        return {"FINISHED"}

I think that's enough for now.

Even on this task there are still many things to do
I ought to be able to select which LODs I want to generate. I should be able to clone a medium to a low if I imported say a High and Medium from Mesh Studio and this is just the very tip of the iceberg.

Lots more to come here I hope.

No comments:

Post a Comment