GeometryCollective / boundary-first-flattening

MIT License
752 stars 96 forks source link

Exporting flattened mesh as obj/ply file #21

Closed lukkio88 closed 5 years ago

lukkio88 commented 5 years ago

Hi, is it possible to export the flattened mesh as an obj or ply?

rohan-sawhney commented 5 years ago

Yes, the "Export Mesh" button in the GUI lets you export the mesh and its uv coordinates as an obj file

lukkio88 commented 5 years ago

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.

rohan-sawhney commented 5 years ago

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.

lukkio88 commented 5 years ago

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.

rohan-sawhney commented 5 years ago

Yes, that's right

AnnieLocke commented 5 years ago

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.

lukkio88 commented 5 years ago

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.

AnnieLocke commented 5 years ago

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)

`

lukkio88 commented 5 years ago

If it works, glad I could help!

bensterl15 commented 3 years ago

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():

prompt the user for a file to import

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))

ehansen66 commented 1 year ago

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

imports the 3D OBJ, replaces 'v' with the flattened 'vt' values and 'f' (v/vt) with the flattened f (vt/vt) values.

then exports the results to a new OBJ file.

def ImportPoints():

prompt the user for a file to import

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))`