0

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[zdim[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[zdim[1]dim[0] + xdim[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], [xunits, yunits, zunits, 1.0]))

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

                o.matrix_world = mat
    print (&quot;Done with plane &quot;, z)

def read_file(filename):

infile = open(filename + &quot;.txt&quot;)

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(&quot;Blender Scenes/&quot; + scenefile + &quot; is not open.&quot;)
    fullpath = Blender.sys.expandpath(&quot;//&quot; + scenefile)
    if Blender.sys.exists(fullpath) == 1:
        print(&quot;Opening &quot; + scenefile + &quot;...&quot;)
        Blender.Load(fullpath)
        print(&quot;a&quot;)
        scn = bpy.data.scenes.active
        print(&quot;b&quot;)
        scndict = make_scndict(bpy.data.scenes.active)
        print(&quot;c&quot;)
        (objs2, found, missing) = fuse_objects(scndict, objs)
        print(&quot;d&quot;)
        print(found)

if found == 1:
    unit = 10
    print(&quot;Building object array&quot;)
    object_array(scn, dim, grid, objs2, unit)
    print(&quot;Done&quot;)
else:
    print(&quot;The objects required in the example model could not be found in the current scene.Open the file Blender Scenes/&quot; + scenefile + &quot; and run the script again.&quot;)

if found == 1 and missing == 1:
    print(&quot;Some of the objects are missing.  Check if the file Blender Scenes/&quot; + scenefile + &quot; is open.&quot;)

infile.close()

class OT_TestOpenFilebrowser(Operator, ImportHelper):

bl_idname = &quot;test.open_filebrowser&quot;
bl_label = &quot;Select Model&quot;

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[zdim[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[zdim[1]dim[0] + xdim[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], [xunits, yunits, zunits, 1.0])

            for o in current_objs:
                new_o = scn.objects.new(o)
                new_o.setMatrix(mat)
    print (&quot;Done with plane &quot;, 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 (&quot;Blender Scenes/&quot; + scenefile + &quot; is not open. &quot;)
    fullpath = Blender.sys.expandpath(&quot;//&quot; + scenefile)
    if Blender.sys.exists(fullpath) == 1:
        print (&quot;Opening &quot; + scenefile + &quot;...&quot;)
        Blender.Load(fullpath)
        print (&quot;a&quot;)
        scn = bpy.data.scenes.active
        print (&quot;b&quot;)
        scndict = make_scndict(bpy.data.scenes.active)
        print (&quot;c&quot;)
        (objs2, found, missing) = fuse_objects(scndict, objs)
        print (&quot;d&quot;)
        print (found)

if found == 1:
    unit = 10
    print (&quot;Building object array&quot;)
    object_array(scn, dim, grid, objs2, unit)
    print (&quot;Done&quot;)
else:
    print (&quot;The objects required in the example model could not be found in the current scene.  Open the file Blender Scenes/&quot; + scenefile + &quot; and run the script again.&quot;)

if found == 1 and missing == 1:
    print (&quot;Some of the objects are missing.  Check if the file Blender Scenes/&quot; + scenefile + &quot; is open.&quot;)

infile.close()

if name == 'main': Blender.Window.FileSelector(read_file,"Select Model")

Daklinus
  • 26
  • 5

2 Answers2

2

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.

Giuseppe G.
  • 654
  • 3
  • 5
0

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[zdim[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[zdim[1]dim[0] + xdim[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], [xunits, yunits, zunits, 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 (&quot;Done with plane &quot;, z)

def read_file(filename):

infile = open(filename + &quot;.txt&quot;)

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(&quot;Blender Scenes/&quot; + scenefile + &quot; is not open.&quot;)
    fullpath = Blender.sys.expandpath(&quot;//&quot; + scenefile)
    if Blender.sys.exists(fullpath) == 1:
        print(&quot;Opening &quot; + scenefile + &quot;...&quot;)
        Blender.Load(fullpath)
        print(&quot;a&quot;)
        scn = bpy.data.scenes.active
        print(&quot;b&quot;)
        scndict = make_scndict(bpy.data.scenes.active)
        print(&quot;c&quot;)
        (objs2, found, missing) = fuse_objects(scndict, objs)
        print(&quot;d&quot;)
        print(found)

if found == 1:
    unit = 10
    print(&quot;Building object array&quot;)
    object_array(scn, dim, grid, objs2, unit)
    print(&quot;Done&quot;)
else:
    print(&quot;The objects required in the example model could not be found in the current scene.Open the file Blender Scenes/&quot; + scenefile + &quot; and run the script again.&quot;)

if found == 1 and missing == 1:
    print(&quot;Some of the objects are missing.  Check if the file Blender Scenes/&quot; + scenefile + &quot; is open.&quot;)

infile.close()

class OT_TestOpenFilebrowser(Operator, ImportHelper):

bl_idname = &quot;test.open_filebrowser&quot;
bl_label = &quot;Select Model&quot;

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')

Daklinus
  • 26
  • 5