0
$\begingroup$

I wrote this script to put vertexes and their colors into a JSON file for a project I'm working on. I was using this answer to get the colors. Here's the script that I wrote with that.

import bpy
import json

ob = bpy.context.active_object
me = ob.data

#check for edit mode
editmode = False
if ob.mode == 'EDIT':
    editmode =True
    #the following sets mode to object by default
    bpy.ops.object.mode_set()
        

j = {
    "name": "Fish",
    "mesh": []
}


verts = [ bpy.context.object.matrix_world @ v.co for v in bpy.context.object.data.vertices ]

color_layer = me.vertex_colors["Col"]

my_object = bpy.context.active_object.data
vert_list = my_object.vertices
color_map = my_object.vertex_colors.new()


# Get the active vertex color layer
color_data = me.vertex_colors.active.data

size = 15

i = 0
for poly in my_object.polygons:
    if poly.select:
        p = []
        r = []
        g = []
        b = []
        a = []
        for idx in poly.loop_indices:
            loop = my_object.loops[idx]
            v = loop.vertex_index
            x = vert_list[v].co.x * size
            y = vert_list[v].co.y * size
            z = vert_list[v].co.z * size
            p.append([y, z, x])
            r.append(color_map.data[i].color[0])
            g.append(color_map.data[i].color[1])
            b.append(color_map.data[i].color[2])
            a.append(color_map.data[i].color[3])
            
            i += 1
        tri = {
            "p0": [p[0][0], p[0][1], p[0][2]], # y and z are 
            "p1": [p[1][0], p[1][1], p[1][2]], # switched in 
            "p2": [p[2][0], p[2][1], p[2][2]], # my game
            "color": {
                "r": sum(r)/len(r) * 255,
                "g": sum(g)/len(g) * 255,
                "b": sum(b)/len(b) * 255,
                "a": sum(a)/len(a) * 255
            }
        }
        j["mesh"].append(tri)
        #for index in range(len(p.vertices)): # for each vertex in triangle 0, 1, 2
            #print(verts[p.vertices[index]]) # prints each vertex in triangle

#obj = json.loads(str(j))
json_formatted_str = json.dumps(j, indent=4)

try:
    f = open("F:/Leviathan/points.txt", "w")
    f.write(json_formatted_str)
    f.close()
except:
    pass

#done editing, restore edit mode if needed
if editmode:
    bpy.ops.object.mode_set(mode = 'EDIT')

It worked great until it didn't. I'm not sure what I did but i get an error at r.append(color_map.data[i].color[0]) where it says AttributeError: 'NoneType' has no attribute 'data'. If I make a new project, paint the object, and copy the script, it works fine. I used it for a few minutes before i got the error. Any help is appreciated

$\endgroup$
5
  • $\begingroup$ @Jakemoyo by printing my_object i get Mesh("Cube") and so I'm not quite sure thats the issue... If it is the issue, how should i fix it? $\endgroup$
    – OwenPT
    Commented Jun 8, 2022 at 19:35
  • 1
    $\begingroup$ r.append(color_map.data[i].color[0]) to r.append(color_map.elements[i].color[0]) $\endgroup$
    – kemplerart
    Commented Jun 8, 2022 at 19:36
  • $\begingroup$ I didn't scroll down to see the whole script, I deleted my last comment. The issue is to do with the variable on line 27, color_map. $\endgroup$
    – Jakemoyo
    Commented Jun 8, 2022 at 19:36
  • $\begingroup$ @kemplerart that gives me the error AttributeError: 'NoneType' has no attribute 'elements' $\endgroup$
    – OwenPT
    Commented Jun 8, 2022 at 19:41
  • $\begingroup$ @Jakemoyo I agree. Do you know why it would only work sometimes? $\endgroup$
    – OwenPT
    Commented Jun 8, 2022 at 19:42

2 Answers 2

0
$\begingroup$

Not sure why it's this way, but I just had to change

r.append(color_map.data[i].color[0])
g.append(color_map.data[i].color[1])
b.append(color_map.data[i].color[2])
a.append(color_map.data[i].color[3])

to

r.append(color_data[i].color[0])
g.append(color_data[i].color[1])
b.append(color_data[i].color[2])
a.append(color_data[i].color[3])

and now it works perfectly.

$\endgroup$
0
$\begingroup$

I'm not totally sure why this would be only working intermittently.

I can tell you for certain you need to go through your script and clean up the completely random use of bpy.context.active_object and bpy.context.active_object.data, in addition to a number of other things.

What might be happening is the reference to the object becomes old, and when you constantly try to refer to it directly rather than through a variable, it might return None, which means it has no attributes and trying to get a vertex color attribute from a NoneType object.

I'll show you what I mean in your script.

import bpy
import json

# This should be the main reference to your object.
ob = bpy.context.active_object

# Unless you change the object data in some dramatic way
# you shouldn't need to refresh the variable. Especially 
# in a script like this where you just read some data and 
# write it to a json file.

# same with the mesh data
me = ob.data

# now that you have these two variables declared just work
# with them in the rest of the script


editmode = False
if ob.mode == 'EDIT':
    editmode = True
 
    bpy.ops.object.mode_set()

j = {
    "name": "Fish",
    "mesh": []
}
# See right here, you are making a redundant reference to the context.object 
# when you should just reference the ob variable you already declared

# I would also do this under the next relevant variable you're declaring, "vert_list"

# So don't do this: 
### verts = [ bpy.context.object.matrix_world @ v.co for v in bpy.context.object.data.vertices ]

# Do this:
vert_list = me.vertices
verts = [ob.matrix_world @ v.co for v in vert_list]

# It seems like you can delete this color_layer variable, it doesn't have any references
# that I can see...(?)

### color_layer = me.vertex_colors["Col"]

# redundant reference again to the mesh data, now calling it "my_object" even though it's 
# a mesh, which confuses things:

### my_object = bpy.context.active_object.data

color_map = me.vertex_colors.new()

# Since you alread have a reference to the "mesh.vertex_colors" 
# you can use that in this next line, 

# Don't do this:
### color_data = me.vertex_colors.active.data

#Do this:
color_data = color_map.active.data

# That's the whole point of variables, to take away redundant typing and 
# to ensure we're always referring to the same object in the computer memory

size = 15


i = 0
# there are also a few Pythonic tricks we can use in here, unrelated to your 
# issue.

for poly in my_object.polygons:
    if poly.select:
        p = []
        # uppercase these so we can use this trick down the line.
        R = []
        G = []
        B = []
        A = []
        for idx in poly.loop_indices:
            loop = ob.loops[idx]
            v = loop.vertex_index
            # this bit right here you can do a little easier 
            # with "tuple unpacking"
            #Don't do this:
            ### x = vert_list[v].co.x * size
            ### y = vert_list[v].co.y * size
            ### z = vert_list[v].co.z * size
            # Do this:
            
            # x, y, z = vert_list[v].co.xyz * size

            # Also, you can access Vector information in basically every 
            # possible configuration, not just in "xyz" so you can theoretically 
            # get access to these vectors in "yzx" order, right out of the box,
            # reducing all of this to one or two lines like: 
            locs = vert_list[v].co.yzx * size
            # and then just append that 
            p.append(locs)
            # Same thing here, shorten it up and make it easier to work with.
            r, g, b, a = color_map.data[i].color
            R.append(r)
            G.append(g)
            B.append(b)
            A.append(a)
            # not a huge deal but it's just alot easier to read
            
            i += 1
        tri = {
            "p0": [p[0][0], p[0][1], p[0][2]], # y and z are 
            "p1": [p[1][0], p[1][1], p[1][2]], # switched in 
            "p2": [p[2][0], p[2][1], p[2][2]], # my game
            "color": {
                "r": sum(R)/len(R) * 255,
                "g": sum(G)/len(G) * 255,
                "b": sum(B)/len(B) * 255,
                "a": sum(A)/len(A) * 255
            }
        }
        j["mesh"].append(tri)
        #for index in range(len(p.vertices)): # for each vertex in triangle 0, 1, 2
            #print(verts[p.vertices[index]]) # prints each vertex in triangle

#obj = json.loads(str(j))
json_formatted_str = json.dumps(j, indent=4)

try:
    f = open("F:/Leviathan/points.txt", "w")
    f.write(json_formatted_str)
    f.close()

# Also don't ever use a plain "except" in a "try/except" block. This would
# even pass on things like "NameError: name 'json' is not defined" if you 
# forgot to import the json module. Always use the relevant Error object, 
# like:

# except AttributeError:
  #  pass

# or whatever.
except:
    pass

#done editing, restore edit mode if needed
if editmode:
    bpy.ops.object.mode_set(mode='EDIT')
$\endgroup$

You must log in to answer this question.

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