1

Thank you very much for your time, any help would be appreciated.

I have been using the code below to shrinkwrap an Icosphere onto my 3D models, then find the vertices coordinates of the Icosphere and export them into an excel file.

    import bpy
thelist = open('The List.txt', 'r')
name = thelist.readline()

path = r"filepath"+name+".obj"
FILEPATH = str.join("", path.splitlines())

bpy.ops.import_scene.obj(filepath=FILEPATH)

for obj in bpy.context.scene.objects:
    if obj.type == 'MESH':
        obj.name = "Specimen"

bpy.data.objects["Specimen"].select = True
bpy.context.scene.objects.active = bpy.data.objects["Specimen"]
bpy.context.active_object.scale = (0.1, 0.1, 0.1)

bpy.ops.mesh.primitive_ico_sphere_add()
bpy.context.active_object.scale = (5, 5, 5)

bpy.ops.object.modifier_add(type='SHRINKWRAP')
bpy.data.objects["Icosphere"].modifiers["Shrinkwrap"].target = bpy.data.objects["Specimen"]

with open("Landmarks.csv", "a") as file:
    file.write(name)
    vs = bpy.data.meshes["Icosphere"].vertices
    for v in vs:
        file.write("%.2f, %.2f, %.2f," % (v.co[:]))

for o in bpy.context.scene.objects: if o.type == 'MESH': o.select = True bpy.ops.object.delete()

name = thelist.readline()

path = r"filepath"+name+".obj" FILEPATH = str.join("", path.splitlines())

bpy.ops.import_scene.obj(filepath=FILEPATH)

for obj in bpy.context.scene.objects: if obj.type == 'MESH': obj.name = "Specimen"

bpy.data.objects["Specimen"].select = True bpy.context.scene.objects.active = bpy.data.objects["Specimen"] bpy.context.active_object.scale = (0.1, 0.1, 0.1)

bpy.ops.mesh.primitive_ico_sphere_add() bpy.context.active_object.scale = (5, 5, 5)

bpy.ops.object.modifier_add(type='SHRINKWRAP') bpy.data.objects["Icosphere"].modifiers["Shrinkwrap"].target = bpy.data.objects["Specimen"]

with open("Landmarks.csv", "a") as file: file.write(name) vs = bpy.data.meshes["Icosphere"].vertices for v in vs: file.write("%.2f, %.2f, %.2f," % (v.co[:]))

I want to make this process automatic and go through all of my models without having to constantly click around. However, when I duplicated the code, I get a different model in blender but the same coordinates in my excel file from the first Icosphere. I already know that creating a new file clears out this memory, but it means that the programme can't be fully automatic. So how do I clear out those stored vertices without having to create a new file and stop the automatic process?

EDIT: added the duplicated code, as suggested in the comments. Once this issue has been sorted I will be turning the programme into a loop, however, going through the list.

  • 1
    Issue looks like naming convention. There can only ever by one object named "Specimen" and one named "IcoSphere" in a blend file. Others will be "Specimen.001", ... etc. eg print(context.object.name) after running the add ico operator . Similarly with its mesh. Recommend reference them, eg after ico op ico = context.object and use ico as object ref and ico.data rather than bpy.data.meshes["IcoSphere"] from then on. – batFINGER Sep 26 '21 at 13:29
  • Hi @batFINGER, thank you so much for the quick comment! I think I understand what you mean about the naming convention being the issue, but I'm not sure I quite understand the recommendation, could that be explained again, please? – Adam Manning Sep 26 '21 at 15:59
  • The issue would be in what you replaced or did not replace when you copy/pasted the code. Could you add your 2nd version in your question ? The answer will definitely be there – Gorgious Sep 26 '21 at 17:40
  • @gorgious Updated the code to what I was using when I duplicated the code. This is what gave me 2 different models but the same vertices landmarks added to the excel file. – Adam Manning Sep 26 '21 at 21:08

1 Answers1

1

Can't be certain of name.

Further to my comment,

Issue looks like naming convention. There can only ever by one object named "Specimen" and one named "IcoSphere" in a blend file. Others will be "Specimen.001", ... etc. eg print(context.object.name) after running the add ico operator . Similarly with its mesh. Recommend reference them, eg after ico op ico = context.object and use ico as object ref and ico.data rather than bpy.data.meshes["IcoSphere"] from then on.

For example, a test in the console.

Add an icosphere

>>> bpy.ops.mesh.primitive_ico_sphere_add()
{'FINISHED'}

>>> C.object bpy.data.objects['Icosphere']

>>> C.object.data bpy.data.meshes['Icosphere']

Remove it

>>> bpy.ops.object.delete()
Info: Deleted 1 object(s)
{'FINISHED'}

>>> C.object is None True

But wait!!! there's still a mesh named "Icosphere"

>>> D.meshes['
              Cube']
              Icosphere']
>>> D.meshes['Icosphere']
bpy.data.meshes['Icosphere']

Let's add another Icosphere object

>>> bpy.ops.mesh.primitive_ico_sphere_add()
{'FINISHED'}

>>> C.object bpy.data.objects['Icosphere']

And here's the issue, the object is removed, the mesh not, the new ones mesh is not named "Icosphere"

>>> C.object.data
bpy.data.meshes['Icosphere.001']

There are a number of ways to fix, including removing both the object and the mesh, but as a general rule would, reference it after adding by by operator

>>> bpy.ops.mesh.primitive_ico_sphere_add()
{'FINISHED'}

>>> ico = C.object >>> ico_mesh = ico.data >>> ico_mesh bpy.data.meshes['Icosphere.002']

and use

vs = ico_mesh.vertices

to always use the mesh of the newly added icosphere.

As a rule of thumb, rarely or never use

bpy.data.whatevers[name]

as there is a chance it will not be "Who you think it is"

Similarly, for the modifier

>>> sw = ico.modifiers.new("Foo", 'SHRINKWRAP')
>>> sw
bpy.data.objects['Icosphere.001'].modifiers["Foo"]

>>> sw.object = target

Where target is an object assigned earlier from imported.

>>> target
bpy.data.objects['Cube']

And finally, if the desired result is the locations of the modified verticies, either apply the modifier,

bpy.ops.object.modifier_apply(modifier=sw.name)

or, IMO preferably use one of How do I get a mesh data-block with modifiers and shape keys applied in Blender 2.8?

batFINGER
  • 84,216
  • 10
  • 108
  • 233