pythonrendering3d-model

What are faces in .obj files and how do they work?


Ive got this .obj file. It's a model of a Mini-Cooper. I extracted all vertices and faces into lists that look like this:

vertices = [(14.82, -22.56, 25.83), (25.38, 29.02, 15.87), (-31.71, -57.05, 19.29), (-27.95, 46.76, 17.98), (-25.44, 47.58, 3.38), (33.63, 48.57, 7.97), (-30.81, 32.23, 22.08), (-14.13, 29.34, 27.43), (3.94, 47.76, 52.66), (25.38, -69.78, 16.93), (29.55, 4.96, 38.49), (33.31, 42.57, 13.42), (27.8, 8.16, 38.6), (-26.77, 46.93, 2.27), (-30.21, -80.45, 24.88), (-33.48, 40.09, 23.68), (33.29, 51.84, 13.67), (-9.08, 67.39, 17.91), (25.27, 31.68, 5.62), (-30.2, -36.77, 39.17)]

faces = [('1962/1/1962', '1958/1/1958', '1957/541/1957'), ('1957/541/1957', '1961/541/1961', '1962/1/1962'), ('1960/3202/1960', '1959/542/1959', '1963/542/1963'), ('1963/542/1963', '1964/3202/1964', '1960/3202/1960'), ('1966/543/1966', '1962/1/1962', '1961/541/1961'), ('1961/541/1961', '1965/544/1965', '1966/543/1966'), ('1964/3202/1964', '1963/542/1963', '1967/546/1967'), ('1967/546/1967', '1968/545/1968', '1964/3202/1964'), ('1970/543/1970', '1966/543/1966', '1965/544/1965'), ('1965/544/1965', '1969/544/1969', '1970/543/1970'), ('1968/545/1968', '1967/546/1967', '1971/546/1971'), ('1971/546/1971', '1972/545/1972', '1968/545/1968'), ('1985/547/1985', '1970/543/1970', '1969/544/1969'), ('1969/544/1969', '1984/550/1984', '1985/547/1985'), ('1993/551/1993', '1961/541/1961', '1957/541/1957'), ('1957/541/1957', '1991/551/1991', '1993/551/1993'), ('1958/1/1958', '1962/1/1962', '1994/548/1994'), ('1994/548/1994', '1992/548/1992', '1958/1/1958'), ('1965/544/1965', '1961/541/1961', '1993/551/1993'), ('1993/551/1993', '1995/550/1995', '1965/544/1965')]

These are only the first 20. I wrote a rendering program and when I put in the vertices it shows this: cooper-picture

It looks like a top-down view of a car, which makes sense. I am a complete beginner to 3d models. How can I add the faces? My program can draw triangles from 3 points, but the faces have huge numbers, so they can't just be coordinates. What are they and how can I get the triangles from them?


Solution

  • Each tuple in your faces list represents one face, which consists of three or more vertices. So, if you take the first face for example, 1962/1/1962 is the first vertex. The format for each vertex is {vertex index}/{vertex texture coordinate index}/{vertex normal index}. So, the first number 1962 represents the vertex index (the 1962nd previously defined vertex). The second number 1 is the vertex texture coordinate index, and the third 1962 is the vertex normal index. According to the specification, only the vertex index is required.

    How you could parse some of the file and substitute the vertex indices for coordinates:

    def main():
    
        import re
    
        flt = r"-?[\d\.]+"
        vertex_pattern = r"v\s+(?P<x>{})\s+(?P<y>{})\s+(?P<z>{})".format(flt, flt, flt)
        face_pattern = r"f\s+((\d+/\d+/\d+\s*){3,})"
    
        vertices = []
        faces = []
    
        with open("alfa147.obj.txt", "r") as file:
            for line in map(str.strip, file):
                match = re.match(vertex_pattern, line)
                if match is not None:
                    vertices.append(tuple(map(float, match.group("x", "y", "z"))))
                    continue
                match = re.match(face_pattern, line)
                if match is not None:
                    faces.append(tuple(tuple(map(int, vertex.split("/"))) for vertex in match.group(1).split()))
    
        print(f"The first face has {len(faces[0])} vertices:")
        for vertex in faces[0]:
            print(vertex)
    
        print("\nWe do not care about the texture coordinate indices or normal indices, so we get the three vertex indices:")
        for vertex in faces[0]:
            print(vertex[0])
    
        print("\nAfter substituting the indices for their values, the face is made up of these coordinates:")
        for vertex in faces[0]:
            print(vertices[vertex[0]-1]) # -1 because in the OBJ spec, collections start at index 1 rather than 0
    
        return 0
    
    
    if __name__ == "__main__":
        import sys
        sys.exit(main())
    

    Output:

    The first face has 3 vertices:
    (1962, 1, 1962)
    (1958, 1, 1958)
    (1957, 541, 1957)
    
    We do not care about the texture coordinate indices or normal indices, so we get the three vertex indices:
    1962
    1958
    1957
    
    After substituting the indices for their values, the face is made up of these coordinates:
    (-4.29133, 47.755795, 51.585678)
    (-4.29133, 47.716423, 51.585678)
    (-4.29133, 47.716423, 52.661789)