0
$\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
    $\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 Nov 1 '19 at 9:18
1
$\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.

| improve this answer | |
$\endgroup$
  • $\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$ – Spatial Digger Nov 2 '19 at 9:50
  • $\begingroup$ I'm in edit mode and using it on the default cube. $\endgroup$ – Spatial Digger Nov 2 '19 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 Nov 2 '19 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$ – Spatial Digger Nov 2 '19 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$ – Spatial Digger Nov 2 '19 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.