Mesh building with Tessfaces and tessface_uv_textures

Scripting in Blender with Python, and working on the API

Moderators: jesterKing, stiv

Post Reply
ssontech
Posts: 0
Joined: Wed Dec 12, 2012 11:36 pm

Mesh building with Tessfaces and tessface_uv_textures

Post by ssontech » Mon May 20, 2013 8:37 pm

I'm trying to build a mesh with UV texture coordinates from python, continuation of http://www.blender.org/forum/viewtopic.php?t=25915 I'm trying to get the tessfaces to work.

I don't get any error messages now --- or mesh for that matter. What's the right incantation to make this work? I've wasted enough time trying to reverse engineer this.

coords = [
[ -18.75, -14.02027027, 0 ],
[ -15.625, -14.02027027, 0 ],
[ -12.5, -14.02027027, 0 ],
[ -9.375, -14.02027027, 0 ],
[ -6.25, -14.02027027, 0 ],
[ -3.125, -14.02027027, 0 ],
...
faces = [
[ 0, 1, 13 ],
[ 1, 14, 13 ],
[ 1, 2, 14 ],
[ 2, 15, 14 ],
[ 2, 3, 15 ],
...
texFaces = [
[[0, 0], [0.08333333333, 0], [0, 0.08333333333]],
[[0.08333333333, 0], [0.08333333333, 0.08333333333], [0, 0.08333333333]],
[[0.08333333333, 0], [0.1666666667, 0], [0.08333333333, 0.08333333333]],
[[0.1666666667, 0], [0.1666666667, 0.08333333333], [0.08333333333, 0.08333333333]],
...
mshobj = bpy.data.meshes.new('obj01')
mshobj.vertices.add(len(coords))
mshobj.edges.add(0)
mshobj.loops.add(sum((len(f) for f in faces)))
mshobj.tessfaces.add(len(faces))
vertices_flat = [f for v in coords for f in v]
mshobj.vertices.foreach_set("co", vertices_flat)
del vertices_flat
uvtex = mshobj.tessface_uv_textures.new()
uvtex.name = "officialUV"
for n,tf in enumerate(texFaces):
datum = uvtex.data[n]
datum.uv1 = tf[0]
datum.uv2 = tf[1]
datum.uv3 = tf[2]
mshobj.update(calc_edges=True)
mshobj.calc_normals()
ob = bpy.data.objects.new(mshobj.name, mshobj)
ob.update_tag()
scene.objects.link(ob)

I need to know what flourish is required to make this work, not a completely different method please. It's based on from_pydata code, roughly.

There's some code that I'm not including, doesn't seem to make much difference...I'm not sure how it might be supposed to factor in here.

loop_index = 0
for i, p in enumerate(mshobj.polygons):
f = faces
loop_len = len(f)
p.loop_start = loop_index
p.loop_total = loop_len
p.vertices = f
loop_index += loop_len


I very much wish that developers would stop making changes in the python API that are not backwards compatible. The time spent providing compatibility will save many times that for users. If there is a break in compatibility, then how to work around it has to be explained super-thoroughly... ie it should be quicker to provide backwards compatibility than to have to explain why it is not. Having blender updates that break existing scripts and force them to be redeveloped doesn't help move blender forward.

CoDEmanX
Posts: 0
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Post by CoDEmanX » Mon May 20, 2013 10:42 pm

the api change was absolutely necessary due the complete overhauling of the mesh modelling system. Since 2.63, Blender can handle Ngons, this made it impossible to stay compatible in mesh data and API.

Using loops along with tessfaces seems just wrong (should be either tessfaces + tessface_uv_textures or polygons + loops + uv_layers), read the docs:

http://www.blender.org/documentation/bl ... tion-faces
I'm sitting, waiting, wishing, building Blender in superstition...

ssontech
Posts: 0
Joined: Wed Dec 12, 2012 11:36 pm

Post by ssontech » Tue May 21, 2013 12:37 am

Adding new stuff was necessary, but breaking the old stuff is less obvious. What I'm doing is quite the common case. I've seen those docs, they aren't particularly helpful. They just say... use this not that, but not what the details of using this are, and there's substantial hidden undisclosed fine print. I've looked at the OBJ importer a bit too, the import and creation are tangled together.

The three arrays are quite simple, surely creating a mesh with them can't be more than 10-20 lines, right? Surely this should be trivial! I believe that a relatively straightforward fix of the code I show should work. Or the new API. Either way it is a simple task.

A new API shouldn't replace an old API until it can easily do what the old API does easily. Interoperability support for blender users depends on the API being stable or at least well documented.

CoDEmanX
Posts: 0
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Post by CoDEmanX » Tue May 21, 2013 12:52 am

see import_3ds.py or maybe use bmesh module instead, it's easier to use as you don't need to worry about loops.
I'm sitting, waiting, wishing, building Blender in superstition...

ssontech
Posts: 0
Joined: Wed Dec 12, 2012 11:36 pm

Post by ssontech » Tue May 21, 2013 1:09 am

"maybe use bmesh module instead" --- LOL! I would do that, if there was something that told me how. I don't have a couple days to waste figuring something out that worked fine before.

I need a piece of code that does what I said above. It was, and should be, simple. Other people are looking for it to, from what I've seen. If blender wants to be taken seriously, you can't just scorch existing work like this. "We broke it and you should be able figure out how to fix it" --- that just doesn't cut it.

CoDEmanX
Posts: 0
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Post by CoDEmanX » Tue May 21, 2013 10:26 am

http://www.blender.org/documentation/bl ... bmesh.html - especially CustomData access

and look at the bmesh.types
I'm sitting, waiting, wishing, building Blender in superstition...

CoDEmanX
Posts: 0
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Post by CoDEmanX » Tue May 21, 2013 12:50 pm

This generates a .py script that contains the mesh data and the code to create an object from that data:

Code: Select all

import bpy

ob = bpy.context.object
if ob is None or ob.type != 'MESH':
    raise TypeError("Mesh object required")

me = ob.data

if not me.tessfaces and me.polygons:
    me.calc_tessface()

# Adjust this path!
file = open("D:\\house.py", "w")

file.write("import bpy\n")
file.write("from bpy_extras.io_utils import unpack_list, unpack_face_list\n")
file.write("\n")
file.write("coords = (\n")

for v in me.vertices:
    file.write("\t(%f, %f, %f),\n" % v.co[:])
    
file.write(")\n")
file.write("\n")

file.write("faces = (\n")

for f in me.tessfaces:
    fv = f.vertices
    if len(fv) == 3:
        tris = (fv[0], fv[1], fv[2]),
    else:
        tris = (fv[0], fv[1], fv[2]), (fv[2], fv[3], fv[0])
        
    for tri in tris:
        file.write("\t%s,\n" % str(tri))

file.write(")\n")
file.write("\n")

file.write("texFaces = (\n")

uv_tex = me.tessface_uv_textures.active.data
for f in me.tessfaces:
    if len(f.vertices) == 3:
        tris = (0, 1, 2),
    else:
        tris = (0, 1, 2),(2, 3, 0)
    
    uv_face = uv_tex[f.index]
    for tri in tris:
        file.write("(")
        file.write(", ".join("(%f, %f)" % (uv_face.uv[i][:]) for i in tri))
        file.write("),\n")

file.write(")\n")
file.write("\n")


script = """
me = bpy.data.meshes.new('obj01')
me.vertices.add(len(coords))
vertices_flat = [vv for v in coords for vv in v] 
me.vertices.foreach_set("co", vertices_flat) 

#mshobj.edges.add(0) 
#mshobj.loops.add(sum((len(f) for f in faces))) 

me.tessfaces.add(len(faces)) 
me.tessfaces.foreach_set("vertices_raw", unpack_face_list(faces))

uv_tex = me.tessface_uv_textures.new(name="officialUV")

for i, face in enumerate(faces):
    tface = uv_tex.data[i]
    tface.uv1 = texFaces[i][0]
    tface.uv2 = texFaces[i][1]
    tface.uv3 = texFaces[i][2]

me.validate()
me.update(calc_edges=True) 
me.calc_normals() 

ob = bpy.data.objects.new(me.name, me)
bpy.context.scene.objects.link(ob)
bpy.context.scene.update()
"""

file.write(script)
file.close()
I'm sitting, waiting, wishing, building Blender in superstition...

ssontech
Posts: 0
Joined: Wed Dec 12, 2012 11:36 pm

Post by ssontech » Wed May 22, 2013 5:15 pm

mshobj = bpy.data.meshes.new('myObject')
mshobj.vertices.add(len(coords))
vertices_flat = [f for v in coords for f in v]
mshobj.vertices.foreach_set("co", vertices_flat)
del vertices_flat
nbr_faces = len(faces)
mshobj.polygons.add(nbr_faces)
mshobj.loops.add(nbr_faces * 3)
eekadoodle_faces = []
for face in faces:
v1, v2, v3 = face
eekadoodle_faces.extend((v3, v1, v2) if v3 == 0 else (v1, v2, v3))
mshobj.polygons.foreach_set("loop_start", range(0, nbr_faces * 3, 3))
mshobj.polygons.foreach_set("loop_total", (3,) * nbr_faces)
mshobj.loops.foreach_set("vertex_index", eekadoodle_faces)
uvtex = mshobj.uv_textures.new()
uvl = mshobj.uv_layers.active.data[:]
for fidx, pl in enumerate(mshobj.polygons):
face = faces[fidx]
v1, v2, v3 = face
# eekadoodle
if v3 == 0:
uvl[pl.loop_start].uv = texFaces[fidx][2]
uvl[pl.loop_start + 1].uv = texFaces[fidx][0]
uvl[pl.loop_start + 2].uv = texFaces[fidx][1]
else:
uvl[pl.loop_start].uv = texFaces[fidx][0]
uvl[pl.loop_start + 1].uv = texFaces[fidx][1]
uvl[pl.loop_start + 2].uv = texFaces[fidx][2]
# always a tri
mshobj.validate()
mshobj.update()
mshobj.calc_normals()

(sorry about the spacing, code option dnw)

stiv
Posts: 0
Joined: Tue Aug 05, 2003 7:58 am
Location: 45N 86W

Post by stiv » Wed May 22, 2013 5:24 pm

(sorry about the spacing, code option dnw)
You are welcome to try again. Your friendly moderators will be happy to delete the broken post. Call us lazy, but that is just too much work to figure out.

CoDEmanX
Posts: 0
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Post by CoDEmanX » Wed May 22, 2013 9:05 pm

code

Code: Select all

-tags dang it and say what you mean...
I'm sitting, waiting, wishing, building Blender in superstition...

Post Reply