1
$\begingroup$

I'm trying to write a script in blender that will take an object and save it to PostgreSQL db. All I need to do is to get the list of vertices per face in a clockwise manner (how postgis knows inside vs outside). I am starting with a simple cube example. The syntax I need for a cube follows:

NOTE: the first node is repeated to close the face

SELECT ST_GeomFromText('POLYHEDRALSURFACE Z(
   ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),
   ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),
   ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),
   ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)),
   ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)),
   ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )');

So far I can access a list of faces and a list of vertices, how can I get a list of vertices per face (clockwise) so it prints as above. Here is my current effort:

import bpy

ob = bpy.context.active_object
me = ob.data
bm = bmesh.from_edit_mesh(me)


for f in bm.faces:
    if f.select:
        print(f.index)
        for v in bm.verts:
            print(v.co.x, v.co.y, v.co.z)

@@@### EDIT ###@@@

OK, so here is where I am now, getting close. I've included rounding to make it easier to understand.

print("SELECT ST_GeomFromText('POLYHEDRALSURFACE Z( ")
#print("="*40) # printing marker
for polygon in current_obj.data.polygons:
    verts_in_face = polygon.vertices[:]
    #print("face index", polygon.index, end=" ")
    #print("normal", polygon.normal)
    print ("((", end=" ")
    #
    for vert in verts_in_face:

        print(
        str(round(current_obj.data.vertices[vert].co.x, 1)), 
        str(round(current_obj.data.vertices[vert].co.y, 1)), 
        str(round(current_obj.data.vertices[vert].co.z, 1)),",", end=" ")                        
    print (")),")   

So it still needs the first vertex to be repeated and if it is the last 'print' exclude the last commas. I have commented out the normals indicator incase extra needs to be added to ensure the clockwise construction direction. Result:

(( 1.0 1.0 0.0 , 1.0 0.0 0.0 , 0.0 0.0 0.0 , 0.0 1.0 0.0 , )),
(( 1.0 1.0 1.0 , 0.0 1.0 1.0 , 0.0 0.0 1.0 , 1.0 -0.0 1.0 , )),
(( 1.0 1.0 0.0 , 1.0 1.0 1.0 , 1.0 -0.0 1.0 , 1.0 0.0 0.0 , )),
(( 1.0 0.0 0.0 , 1.0 -0.0 1.0 , 0.0 0.0 1.0 , 0.0 0.0 0.0 , )),
(( 0.0 0.0 0.0 , 0.0 0.0 1.0 , 0.0 1.0 1.0 , 0.0 1.0 0.0 , )),
(( 1.0 1.0 1.0 , 1.0 1.0 0.0 , 0.0 1.0 0.0 , 0.0 1.0 1.0 , )),
$\endgroup$
1
  • 1
    $\begingroup$ Maybe you should be looking at loops rather than vertices? Note they are ordered anti-clockwise, so lists would need reversing. $\endgroup$
    – Robin Betts
    Commented Nov 1, 2019 at 9:18

1 Answer 1

2
$\begingroup$

This way starts with mesh loops rather than vertices, because, although it seems true, I can find no guarantee that polygons list their vertices in order with respect to their normals. (Maybe someone can correct me on that.) So there's a bit of extra wrangling.

As is typical of 3D applications, loops are ordered anticlockwise with respect to forward-facing normals, so have to be reversed, and the (formerly) last element repeated.

import bpy

me = bpy.context.object.data

# per-poly lists of loop indices
pli = (p.loop_indices for p in me.polygons)
# .. to per-poly lists of vertex indices
pvi = ([me.loops[l].vertex_index for l in ll] for ll in pli)
# ..to per-poly lists of vertices
p_verts = ([me.vertices[id] for id in idl] for idl in pvi)

# floating point precision
prec = 2

co_lists =[]

for v_list in p_verts:
    co_str = '    (('
    co_strs = []
    for v in [*v_list[::-1],v_list[-1]]:
        co_strs.append(f'{v.co[0]: .{prec}f} '
                       f'{v.co[1]: .{prec}f} '
                       f'{v.co[2]: .{prec}f}')
    co_str += ', '.join(co_strs)
    co_str += '))'
    co_lists.append(co_str)

DBstr =  "SELECT ST_GeomFromText('POLYHEDRALSURFACE Z(\n"    
DBstr += ",\n".join(co_lists)
DBstr += " )');"


print(DBstr) 

If nothing else, maybe you can pick the slice [::-1]... [-1] out of it for reversing and repeating your elements, and/or the str.join() for dealing with your extra terminal comma.

$\endgroup$
11
  • $\begingroup$ Thank you, I've been amending this because I get the error ` File "\Text", line 21 co_strs.append(f"{v.co[0]: .{prec}f} "+` it does not like the final double quotation mark. $\endgroup$ Commented Nov 2, 2019 at 9:50
  • $\begingroup$ I'm in edit mode and using it on the default cube. $\endgroup$ Commented Nov 2, 2019 at 9:52
  • 1
    $\begingroup$ @SpatialDigger yeah f string formatting f"{foo}" needs python 3.6 or greater. Both 2.8 and 2.79x are shipped with at least 3.6.x. What version of blender are you using. For python 3.5 or prior use "%3.1f" % foo for example. RobinBetts pretty sure the face verts are in winding order as it has come up before re the "bowtie" effect for poorly ordered quads. (Definitely isn't arbitrary) $\endgroup$
    – batFINGER
    Commented Nov 2, 2019 at 13:19
  • 1
    $\begingroup$ @batFINGER I was just reading up on the append function and saw that stated about the f-string, I'll see if I can use one of the other methods. $\endgroup$ Commented Nov 2, 2019 at 13:24
  • 1
    $\begingroup$ I upgraded blender, the script works, although wrapping it in a PostGIS volume query gives a result of -1 rather than 1. Does this mean the construction direction is backwards? SELECT ST_Volume(ST_MakeSolid(ST_GeomFromText('POLYHEDRALSURFACE Z( $\endgroup$ Commented Nov 2, 2019 at 13:41

You must log in to answer this question.

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