0
$\begingroup$

I am currently working with a script that was originally written for Blender 2.49, the last part of the code is meant to copy a group of objects multiple times in a pre-arranged way

I have upgraded it into 3.0, but I am still stuck on two parts, getting the mesh data, and duplicating the objects.

I think i fixed the first part.

if obj in scndict:
                oolist.append(scndict[obj].getData(mesh=1))
                found = 1

I replaced oolist.append(scndict[obj].getData(mesh=1))with oolist.append(scndict[obj].data) to get the mesh data.

I tried to pass it but the objects.link only takes objects so I replaced it with oolist.append(scndict[obj])

I then tried to pass it to new_o = scn.objects.new(o) but it got an attribute error, the point of the function is to create new objects, I could not find a replacement in the new Blender API for scene.objects.new()

Therefore, to make due I replacef it with scn.collection.objects.link()

When I run it the code runs but only adds one copy to the master collection and says the object is already linked and shows no new mesh. What I seek is how to pass mesh data into an object and create multiple copies of that object. Any guidance on this would be appreciated.

Full code below

Updated Code :

import bpy
import os
import math
import mathutils
import re
import array
import itertools
from bpy.props import StringProperty, BoolProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
from math import radians
from mathutils import Matrix

extents_re = re.compile(r"^x, y, and z extents")
objects_start_re = re.compile(r"<Objects>")
start_name_re    = re.compile(r"^(\d+)?\s*(?:\\\$([^\s]*))?")
name_re    = re.compile(r"^\s*\\\$([^\s]*)")
end_re     = re.compile("^#\s*")

def make_scndict(scn):
    scndict = {}
    for i in scn.objects:
        scndict[i.name] = i
    return scndict

def read_grid(infile):
    li = infile.readline()
    dim = [int(x) for x in li.rstrip().split(' ')]
    grid = array.array('L', itertools.repeat(0, dim[0]*dim[1]*dim[2]))
    for z in range(0, dim[2]):
        li = infile.readline()
        for x in range(0, dim[0]):
            li = [int(it) for it in infile.readline().rstrip().split()]
            for y in range(0, dim[1]):
                grid[z*dim[1]*dim[0] + x*dim[1] + y] = li[y]
    return (dim, grid)

def read_object(infile, li):
    res = start_name_re.match(li)
    grps = res.groups()
    if(grps[0] != None):
        num = int(grps[0])
    else:
        num = None
    entry = []
##  Apparently, we don't want to use any objects the appear on
##  the same line as the number, so don't use any other groups
##    if(grps[1] != None):
##        entry.append(grps[1])
    li = infile.readline()
    res = end_re.match(li)
    while res == None:
        res = name_re.match(li)
        assert(res != None)
        entry.append(res.group(1))
        li = infile.readline()
        res = end_re.match(li)
    return (num, entry)

def read_objects(infile):
    scenefile = infile.readline()
    objs = []
    li = infile.readline()
    while li != "":
        if(li.rstrip() != ""):
            num, entry = read_object(infile, li)
            print(num, entry)
            objs.append(entry)
        li = infile.readline()
    return (objs,scenefile)

def read_synth(infile):
    ## Look for start of grid (skip until extent warning)
    li = infile.readline()
    res = extents_re.match(li)
    while li != "" and res == None:
        li = infile.readline()
        res = extents_re.match(li)
    (dim, grid) = read_grid(infile)
    print ("Read %d x %d x %d grid" % (dim[0], dim[1], dim[2]))
    ## Look for start of objects (skip until object tag)
    li = infile.readline()
    res = objects_start_re.match(li)
    while li != "" and res == None:
        li = infile.readline()
        res = objects_start_re.match(li)
    (objs, scenefile) = read_objects(infile)
    print("Read %d object groups" % (len(objs),))
    return (dim, grid, objs, scenefile)

def fuse_objects(scndict, objs):

    res = [None]
    found = 0
    missing = 0
    for olist in objs:
        oolist = []
        for obj in olist:
            if obj in scndict:
                oolist.append(scndict[obj].data)
                found = 1
            else:
                missing = 1
        res.append(oolist)
    
    return (res, found, missing)

def object_array(scn, dim, grid, objs, units):
    for z in range(0, dim[2]):
        for x in range(0, dim[0]):
            for y in range(0, dim[1]):
                current_objs = objs[grid[z*dim[1]*dim[0] + x*dim[1] + y]]
                if current_objs == None:
                    continue
                mat = mathutils.Matrix(([1.0, 0.0, 0.0, 0.0],
                                               [0.0, 1.0, 0.0, 0.0],
                                               [0.0, 0.0, 1.0, 0.0],
                                               [x*units, y*units, z*units, 1.0]))

                for o in current_objs:
                    new_o = scn.collection.objects.link(o)
                     
                    o.matrix_world = mat
        print ("Done with plane ", z)

def read_file(filename):

    infile = open(filename + ".txt")

    scn = bpy.context.window.scene
    scndict = make_scndict(bpy.context.window.scene)
    (dim, grid, objs, scenefile) = read_synth(infile)
    (objs2, found, missing) = fuse_objects(scndict, objs)
    scenefile = scenefile.rstrip();
    
    if missing == 1:
        print("Blender Scenes/" + scenefile + " is not open.")
        fullpath = Blender.sys.expandpath("//" + scenefile)
        if Blender.sys.exists(fullpath) == 1:
            print("Opening " + scenefile + "...")
            Blender.Load(fullpath)
            print("a")
            scn = bpy.data.scenes.active
            print("b")
            scndict = make_scndict(bpy.data.scenes.active)
            print("c")
            (objs2, found, missing) = fuse_objects(scndict, objs)
            print("d")
            print(found)
    
    if found == 1:
        unit = 10
        print("Building object array")
        object_array(scn, dim, grid, objs2, unit)
        print("Done")
    else:
        print("The objects required in the example model could not be found in the current scene.Open the file Blender Scenes/" + scenefile + " and run the script again.")
    
    if found == 1 and missing == 1:
        print("Some of the objects are missing.  Check if the file Blender Scenes/" + scenefile + " is open.")
            
    infile.close()

class OT_TestOpenFilebrowser(Operator, ImportHelper):

    bl_idname = "test.open_filebrowser"
    bl_label = "Select Model"
    
    filter_glob: StringProperty(
        default='*.txt',
        options={'HIDDEN'}
    )
    
    
    def execute(self,context):
        filename, extension = os.path.splitext(self.filepath)
        read_file(filename)
        return {'FINISHED'}
        
def register():
    bpy.utils.register_class(OT_TestOpenFilebrowser)

def unregister():
    bpy.utils.unregister_class(OT_TestOpenFilebrowser)

if __name__ == '__main__':
    register()
    bpy.ops.test.open_filebrowser('INVOKE_DEFAULT')

Old Code:

import Blender
import bpy
import math
import re
import array
import itertools

extents_re = re.compile(r"^x, y, and z extents")
objects_start_re = re.compile(r"<Objects>")
start_name_re    = re.compile(r"^(\d+)?\s*(?:\\\$([^\s]*))?")
name_re    = re.compile(r"^\s*\\\$([^\s]*)")
end_re     = re.compile("^#\s*")

def make_scndict(scn):
    scndict = {}
    for i in scn.objects:
        scndict[i.name] = i
    return scndict

def read_grid(infile):
    li = infile.readline()
    dim = [int(x) for x in li.rstrip().split(' ')]
    grid = array.array('L', itertools.repeat(0, dim[0]*dim[1]*dim[2]))
    for z in xrange(0, dim[2]):
        li = infile.readline()
        for x in xrange(0, dim[0]):
            li = [int(it) for it in infile.readline().rstrip().split()]
            for y in xrange(0, dim[1]):
                grid[z*dim[1]*dim[0] + x*dim[1] + y] = li[y]
    return (dim, grid)

def read_object(infile, li):
    res = start_name_re.match(li)
    grps = res.groups()
    if(grps[0] != None):
        num = int(grps[0])
    else:
        num = None
    entry = []
##  Apparently, we don't want to use any objects the appear on
##  the same line as the number, so don't use any other groups
##    if(grps[1] != None):
##        entry.append(grps[1])
    li = infile.readline()
    res = end_re.match(li)
    while res == None:
        res = name_re.match(li)
        assert(res != None)
        entry.append(res.group(1))
        li = infile.readline()
        res = end_re.match(li)
    return (num, entry)

def read_objects(infile):
    scenefile = infile.readline()
    objs = []
    li = infile.readline()
    while li != "":
        if(li.rstrip() != ""):
            num, entry = read_object(infile, li)
            print (num, entry)
            objs.append(entry)
        li = infile.readline()
    return (objs,scenefile)

def read_synth(infile):
    ## Look for start of grid (skip until extent warning)
    li = infile.readline()
    res = extents_re.match(li)
    while li != "" and res == None:
        li = infile.readline()
        res = extents_re.match(li)
    (dim, grid) = read_grid(infile)
    print ("Read %d x %d x %d grid" % (dim[0], dim[1], dim[2]))
    ## Look for start of objects (skip until object tag)
    li = infile.readline()
    res = objects_start_re.match(li)
    while li != "" and res == None:
        li = infile.readline()
        res = objects_start_re.match(li)
    (objs, scenefile) = read_objects(infile)
    print ("Read %d object groups" % (len(objs),))
    return (dim, grid, objs, scenefile)

def fuse_objects(scndict, objs):
    res = [None]
    found = 0
    missing = 0
    for olist in objs:
        oolist = []
        for obj in olist:
            if obj in scndict:
                oolist.append(scndict[obj].getData(mesh=1))
                found = 1
            else:
                missing = 1
        res.append(oolist)
    
    return (res, found, missing)

def object_array(scn, dim, grid, objs, units):
    for z in xrange(0, dim[2]):
        for x in xrange(0, dim[0]):
            for y in xrange(0, dim[1]):
                current_objs = objs[grid[z*dim[1]*dim[0] + x*dim[1] + y]]
                if current_objs == None:
                    continue
                mat = Blender.Mathutils.Matrix([1.0, 0.0, 0.0, 0.0],
                                               [0.0, 1.0, 0.0, 0.0],
                                               [0.0, 0.0, 1.0, 0.0],
                                               [x*units, y*units, z*units, 1.0])

                for o in current_objs:
                    new_o = scn.objects.new(o)
                    new_o.setMatrix(mat)
        print ("Done with plane ", z)

def read_file(filename):
    infile = open(filename)

    scn = bpy.data.scenes.active
    scndict = make_scndict(bpy.data.scenes.active)
    (dim, grid, objs, scenefile) = read_synth(infile)
    (objs2, found, missing) = fuse_objects(scndict, objs)
    scenefile = scenefile.rstrip();
    
    if missing == 1:
        print ("Blender Scenes/" + scenefile + " is not open. ")
        fullpath = Blender.sys.expandpath("//" + scenefile)
        if Blender.sys.exists(fullpath) == 1:
            print ("Opening " + scenefile + "...")
            Blender.Load(fullpath)
            print ("a")
            scn = bpy.data.scenes.active
            print ("b")
            scndict = make_scndict(bpy.data.scenes.active)
            print ("c")
            (objs2, found, missing) = fuse_objects(scndict, objs)
            print ("d")
            print (found)
    
    if found == 1:
        unit = 10
        print ("Building object array")
        object_array(scn, dim, grid, objs2, unit)
        print ("Done")
    else:
        print ("The objects required in the example model could not be found in the current scene.  Open the file Blender Scenes/" + scenefile + " and run the script again.")
    
    if found == 1 and missing == 1:
        print ("Some of the objects are missing.  Check if the file Blender Scenes/" + scenefile + " is open.")
            
    infile.close()

if __name__ == '__main__':
    Blender.Window.FileSelector(read_file,"Select Model")
$\endgroup$

2 Answers 2

2
$\begingroup$

here are some tips about your question:

  1. to access mesh data, use data property of the object (you are doing it right):
# get object reference
ob = bpy.data.objects[<ojbName>]
ob_data = ob.data

# now you can access all data related to the mesh,
# for example uv_layers, vertex_colors etc...
  1. to duplicate objects, you should duplicate the object and the data related to it with copy() method, and I suggest you to check these answers:

From Blender 2.8x, every object has to be linked to a Collection, not to the Scene:

Hope this could help.

$\endgroup$
0
$\begingroup$

Thanks @g-garone, I looked at the the links you provided and found the first one most useful.

I replaced oolist.append(scndict[obj].data) with oolist.append(scndict[obj]) therefore calling the entire object.

I then replaced

for o in current_objs:
                    new_o = scn.collection.objects.link(o)
                     
                    o.matrix_world = mat

With

for o in current_objs:
                    new_o = bpy.data.objects.new(o.name, o.data)
                    scn.collection.objects.link(new_o) 
                    new_o.matrix_world = mat

I added new_o = bpy.data.objects.new(o.name, o.data) what this does is make a new object based on the names and the entire data of the objects stored in current_objs . I then linked new_o to the master collection. When I ran the script it worked as intended, except that for some reason the matrix stored in mat did not get applied, I think it has to do with the old code.

Anyway I now consider the question answered, and again thank you @g.garone for pointing me in the right direction.

Final script below:

import bpy
import os
import math
import mathutils
import re
import array
import itertools
from bpy.props import StringProperty, BoolProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
from math import radians
from mathutils import Matrix

extents_re = re.compile(r"^x, y, and z extents")
objects_start_re = re.compile(r"<Objects>")
start_name_re    = re.compile(r"^(\d+)?\s*(?:\\\$([^\s]*))?")
name_re    = re.compile(r"^\s*\\\$([^\s]*)")
end_re     = re.compile("^#\s*")

def make_scndict(scn):
    scndict = {}
    for i in scn.objects:
        scndict[i.name] = i
    return scndict

def read_grid(infile):
    li = infile.readline()
    dim = [int(x) for x in li.rstrip().split(' ')]
    grid = array.array('L', itertools.repeat(0, dim[0]*dim[1]*dim[2]))
    for z in range(0, dim[2]):
        li = infile.readline()
        for x in range(0, dim[0]):
            li = [int(it) for it in infile.readline().rstrip().split()]
            for y in range(0, dim[1]):
                grid[z*dim[1]*dim[0] + x*dim[1] + y] = li[y]
    return (dim, grid)

def read_object(infile, li):
    res = start_name_re.match(li)
    grps = res.groups()
    if(grps[0] != None):
        num = int(grps[0])
    else:
        num = None
    entry = []
##  Apparently, we don't want to use any objects the appear on
##  the same line as the number, so don't use any other groups
##    if(grps[1] != None):
##        entry.append(grps[1])
    li = infile.readline()
    res = end_re.match(li)
    while res == None:
        res = name_re.match(li)
        assert(res != None)
        entry.append(res.group(1))
        li = infile.readline()
        res = end_re.match(li)
    return (num, entry)

def read_objects(infile):
    scenefile = infile.readline()
    objs = []
    li = infile.readline()
    while li != "":
        if(li.rstrip() != ""):
            num, entry = read_object(infile, li)
            print(num, entry)
            objs.append(entry)
        li = infile.readline()
    return (objs,scenefile)

def read_synth(infile):
    ## Look for start of grid (skip until extent warning)
    li = infile.readline()
    res = extents_re.match(li)
    while li != "" and res == None:
        li = infile.readline()
        res = extents_re.match(li)
    (dim, grid) = read_grid(infile)
    print ("Read %d x %d x %d grid" % (dim[0], dim[1], dim[2]))
    ## Look for start of objects (skip until object tag)
    li = infile.readline()
    res = objects_start_re.match(li)
    while li != "" and res == None:
        li = infile.readline()
        res = objects_start_re.match(li)
    (objs, scenefile) = read_objects(infile)
    print("Read %d object groups" % (len(objs),))
    return (dim, grid, objs, scenefile)

def fuse_objects(scndict, objs):

    res = [None]
    found = 0
    missing = 0
    for olist in objs:
        oolist = []
        for obj in olist:
            if obj in scndict:
                oolist.append(scndict[obj])
                found = 1
            else:
                missing = 1
        res.append(oolist)
    
    return (res, found, missing)

def object_array(scn, dim, grid, objs, units):
    for z in range(0, dim[2]):
        for x in range(0, dim[0]):
            for y in range(0, dim[1]):
                current_objs = objs[grid[z*dim[1]*dim[0] + x*dim[1] + y]]
                if current_objs == None:
                    continue
                mat = mathutils.Matrix(([1.0, 0.0, 0.0, 0.0],
                                               [0.0, 1.0, 0.0, 0.0],
                                               [0.0, 0.0, 1.0, 0.0],
                                               [x*units, y*units, z*units, 1.0]))

                for o in current_objs:
                    new_o = bpy.data.objects.new(o.name, o.data)
                    scn.collection.objects.link(new_o) 
                    new_o.matrix_world = mat
        print ("Done with plane ", z)

def read_file(filename):

    infile = open(filename + ".txt")

    scn = bpy.context.window.scene
    scndict = make_scndict(bpy.context.window.scene)
    (dim, grid, objs, scenefile) = read_synth(infile)
    (objs2, found, missing) = fuse_objects(scndict, objs)
    scenefile = scenefile.rstrip();
    
    if missing == 1:
        print("Blender Scenes/" + scenefile + " is not open.")
        fullpath = Blender.sys.expandpath("//" + scenefile)
        if Blender.sys.exists(fullpath) == 1:
            print("Opening " + scenefile + "...")
            Blender.Load(fullpath)
            print("a")
            scn = bpy.data.scenes.active
            print("b")
            scndict = make_scndict(bpy.data.scenes.active)
            print("c")
            (objs2, found, missing) = fuse_objects(scndict, objs)
            print("d")
            print(found)
    
    if found == 1:
        unit = 10
        print("Building object array")
        object_array(scn, dim, grid, objs2, unit)
        print("Done")
    else:
        print("The objects required in the example model could not be found in the current scene.Open the file Blender Scenes/" + scenefile + " and run the script again.")
    
    if found == 1 and missing == 1:
        print("Some of the objects are missing.  Check if the file Blender Scenes/" + scenefile + " is open.")
            
    infile.close()

class OT_TestOpenFilebrowser(Operator, ImportHelper):

    bl_idname = "test.open_filebrowser"
    bl_label = "Select Model"
    
    filter_glob: StringProperty(
        default='*.txt',
        options={'HIDDEN'}
    )
    
    
    def execute(self,context):
        filename, extension = os.path.splitext(self.filepath)
        read_file(filename)
        return {'FINISHED'}
        
def register():
    bpy.utils.register_class(OT_TestOpenFilebrowser)

def unregister():
    bpy.utils.unregister_class(OT_TestOpenFilebrowser)

if __name__ == '__main__':
    register()
    bpy.ops.test.open_filebrowser('INVOKE_DEFAULT')
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .