Closed lukkio88 closed 5 years ago
Yes, the "Export Mesh" button in the GUI lets you export the mesh and its uv coordinates as an obj file
But the UV coordinates are a parameterization of the mesh, how do I get the actual 3D flat mesh? Basically when I do export and I load the output mesh I can see the original mesh, but with assigned UV (derived using your tool) . What I'd like to get is the actual flattened mesh.
One way to do this would be to write a custom (obj) loader that replaces the 3D vertex positions (v flag) with the 2D uv coordinates (vt flag). Tools such as maya, modo, meshlab etc might let you export the uv mesh directly, but I'm not sure.
So you would set $z = 0$ for all vertices and replace $x,y$ with $u,v$, right? I mean I can write it from scratch but I want to be sure I do the right thing.
Yes, that's right
So you would set $z = 0$ for all vertices and replace $x,y$ with $u,v$, right? I mean I can write it from scratch but I want to be sure I do the right thing.
Can you explain a bit more how you did this lukkio? I'm trying to write something for this in grasshopper/py for Rhino3d. I have little experience with OBJ files so I'm not sure how to "extract" the uv coordinates that BFF is exporting. When I open the exported mesh in rhino, it just appears to be the same mesh I put into BFF. I'd like to make a flattened mesh based on BFF mapping.
So you would set $z = 0$ for all vertices and replace $x,y$ with $u,v$, right? I mean I can write it from scratch but I want to be sure I do the right thing.
Can you explain a bit more how you did this lukkio? I'm trying to write something for this in grasshopper/py for Rhino3d. I have little experience with OBJ files so I'm not sure how to "extract" the uv coordinates that BFF is exporting. When I open the exported mesh in rhino, it just appears to be the same mesh I put into BFF. I'd like to make a flattened mesh based on BFF mapping.
Are you using C++ or do you want to parse the obj?
I'm not familiar with Rhino, but if you have to use python you'll have to parse the obj.
The obj will have a list of vertices, denoted with v
; a list of uv coordinates, denoted with vt
and a list of faces, denoted with f
in the file.
The BFF generates entries for f
of the form
f v1/vt1 v2/vt2 v3/vt3
What that entry encodes is that in 3D space the face f
is defined by walking counterclock wise the vertices with indices v1, v2, v3
. The corresponding triangle in UV space (flat space) is given by vt1,vt2,vt3
.
Note that in this obj each wedge
has an associated (u,v) coords, so you might have repeated (u,v)'s in the file.
What I would do in python would be generating three tables, vertices, (u,v)'s and faces. Then I would do something like
For each `vi` in `vTable`
Find the first face `f` in `fTable` such that `vi` is a vertex of `f`
Let j be the index of the pair f.(vj,vtj) such that vi = f.vj
vi.x = vtj.x;
vi.y = vtj.y;
vi.z = 0.0;
This will give you the flat mesh.
If you're using C++ you need to know the HalfEdge data structure, so designing the equivalent should be easier, let me know if you need any help with that.
Thanks @lukkio88 for your help! That was just what I needed. Here's a python script I run in Rhino to make flat and 3D mesh from the obj file in case anyone needs to do this in the future.
`# Import points from a text file
vertices = [] vt = [] f2d = [] f3d = []
import rhinoscriptsyntax as rs
def ImportPoints():
filter = "Text file (*.txt)|*.txt|All Files (*.*)|*.*||"
filename = rs.OpenFileName("Open Point File", filter)
if not filename: return
#read each line from the file
file = open(filename, "r")
contents = file.readlines()
file.close()
def processLine(line):
lineData = line.split(' ')
type = lineData[0]
if type == 'v':
x = float(lineData[1])
y = float(lineData[2])
z = float(lineData[3])
#print "vertex: {},{},{}".format(x,y,z)
global vertices
vertices.append((x,y,z))
elif type =='vt':
u = float(lineData[1])
v = float(lineData[2])
#print "texture: {},{}".format(u,v)
global vt
vt.append((u,v,0))
elif type =='f':
v0, vt0 = map(int,lineData[1].split('/',1))
v1, vt1 = map(int,lineData[2].split('/',1))
v2, vt2 = map(int,lineData[3].split('/',1))
#print "face: {},{},{}".format(v0,v1,v2)
global f
f2d.append((vt0-1,vt1-1,vt2-1))
f3d.append((v0-1,v1-1,v2-1 ))
contents = [processLine(line) for line in contents]
if( name == "main" ): ImportPoints()
rs.AddMesh(vertices,f3d)
#2D mesh from UV coord
rs.AddMesh(vt,f2d)
`
If it works, glad I could help!
Thanks @lukkio88 for your help! That was just what I needed. Here's a python script I run in Rhino to make flat and 3D mesh from the obj file in case anyone needs to do this in the future.
`# Import points from a text file
vertices = [] vt = [] f2d = [] f3d = []
import rhinoscriptsyntax as rs
def ImportPoints():
prompt the user for a file to import
filter = "Text file (.txt)|.txt|All Files (.)|.||" filename = rs.OpenFileName("Open Point File", filter) if not filename: return
#read each line from the file file = open(filename, "r") contents = file.readlines() file.close() def processLine(line): lineData = line.split(' ') type = lineData[0] if type == 'v': x = float(lineData[1]) y = float(lineData[2]) z = float(lineData[3]) #print "vertex: {},{},{}".format(x,y,z) global vertices vertices.append((x,y,z)) elif type =='vt': u = float(lineData[1]) v = float(lineData[2]) #print "texture: {},{}".format(u,v) global vt vt.append((u,v,0)) elif type =='f': v0, vt0 = map(int,lineData[1].split('/',1)) v1, vt1 = map(int,lineData[2].split('/',1)) v2, vt2 = map(int,lineData[3].split('/',1)) #print "face: {},{},{}".format(v0,v1,v2) global f f2d.append((vt0-1,vt1-1,vt2-1)) f3d.append((v0-1,v1-1,v2-1 )) contents = [processLine(line) for line in contents]
if( name == "main" ): ImportPoints()
original 3D mesh
rs.AddMesh(vertices,f3d)
2D mesh from UV coord
rs.AddMesh(vt,f2d) `
Thanks so much @AnnieLocke, this saved me tons of time. In case anyone else does not have Rhino:
vertices = [] vt = [] f2d = [] f3d = []
import numpy as np
def ImportPoints():
filename = 'obj_file.obj'
if not filename: return
#read each line from the file
file = open(filename, "r")
contents = file.readlines()
file.close()
def processLine(line):
lineData = line.split(' ')
type = lineData[0]
if type == 'v':
x = float(lineData[1])
y = float(lineData[2])
z = float(lineData[3])
#print "vertex: {},{},{}".format(x,y,z)
global vertices
vertices.append((x,y,z))
elif type =='vt':
u = float(lineData[1])
v = float(lineData[2])
#print "texture: {},{}".format(u,v)
global vt
vt.append((u,v,0))
elif type =='f':
v0, vt0 = map(int,lineData[1].split('/',1))
v1, vt1 = map(int,lineData[2].split('/',1))
v2, vt2 = map(int,lineData[3].split('/',1))
#print "face: {},{},{}".format(v0,v1,v2)
global f
f2d.append((vt0-1,vt1-1,vt2-1))
f3d.append((v0-1,v1-1,v2-1 ))
contents = [processLine(line) for line in contents]
ImportPoints()
print(np.asarray(vt))
Also for those that don't have rhino, or who just want to convert the BFF exported mesh to it's flattened 2D format.
I updated the code above to generate a new 2D OBJ file that will open directly in an OBJ viewer:
`# define variables vertices = [] vt = [] f2d = [] f3d = []
import numpy as np
def ImportPoints():
filename = 'filename.obj'
if not filename: return
# read each line from the file
file = open(filename, "r")
contents = file.readlines()
print(contents)
file.close()
# extract v, vt, and f variables.
def processLine(line):
lineData = line.split(' ')
type = lineData[0]
if type == 'v':
x = float(lineData[1])
y = float(lineData[2])
z = float(lineData[3])
# print("vertex: {},{},{}".format(x,y,z))
global vertices
vertices.append((x,y,z))
elif type =='vt':
u = float(lineData[1])
v = float(lineData[2])
# print("texture: {},{}".format(u,v))
global vt
vt.append('{} {} {}'.format(u, v, 0))
print(vt)
elif type =='f':
v0, vt0 = map(int,lineData[1].split('/',1))
v1, vt1 = map(int,lineData[2].split('/',1))
v2, vt2 = map(int,lineData[3].split('/',1))
# print("face: {},{},{}".format(v0,v1,v2))
global f2d
f2d.append('{}/{} {}/{} {}/{}'.format(vt0, vt0, vt1, vt1, vt2, vt2))
print(f2d)
contents = [processLine(line) for line in contents]
# write each line to create the 2D OBJ file
with open('newfilename.obj', 'w') as string:
for line in vt:
string.write(f"v {line}\n")
for line in vt:
string.write(f"vt {line}\n")
for line in f2d:
string.write(f"f {line}\n")
ImportPoints()
print(np.asarray(vt))`
Hi, is it possible to export the flattened mesh as an obj or ply?