This commit is contained in:
Thomas FORGIONE 2016-11-30 12:11:31 +01:00
parent 9b76190292
commit ae3476d719
No known key found for this signature in database
GPG Key ID: 2A210FFC062E00C3
1 changed files with 83 additions and 8 deletions

View File

@ -11,12 +11,25 @@ Normal = Vertex
TexCoord = Vertex
class FaceVertex:
"""Contains the information a vertex needs in a face
In contains the index of the vertex, the index of the texture coordinate
and the index of the normal. It is None if it is not available.
"""
def __init__(self, vertex = None, tex_coord = None, normal = None):
"""Initializes a FaceVertex from its indices
"""
self.vertex = vertex
self.tex_coord = tex_coord
self.normal = normal
def from_array(self, arr):
"""Initializes a FaceVertex from an array
The array can be an array of strings, the first value will be the
vertex index, the second will be the texture coordinate index, and the
third will be the normal index.
"""
self.vertex = int(arr[0]) if len(arr) > 0 else None
try:
@ -32,7 +45,11 @@ class FaceVertex:
return self
class Face:
"""Represents a face with 3 vertices
"""
def __init__(self, a = None, b = None, c = None, material = None):
"""Initializes a Face with its three FaceVertex and its Material
"""
self.a = a
self.b = b
self.c = c
@ -40,14 +57,22 @@ class Face:
# Expects array of array
def from_array(self, arr):
"""Initializes a Face with an array
The array should be an array of array of objects.
Each array will represent a FaceVertex
"""
self.a = FaceVertex().from_array(arr[0])
self.b = FaceVertex().from_array(arr[1])
self.c = FaceVertex().from_array(arr[2])
return self
class ModelParser:
"""Represents a 3D model
"""
def __init__(self):
"""Initializes the model
"""
self.vertices = []
self.normals = []
self.tex_coords = []
@ -61,20 +86,37 @@ class ModelParser:
self.path = None
def init_textures(self):
"""Initializes the textures of the parts of the model
Basically, calls glGenTexture on each texture
"""
for part in self.parts:
part.init_texture()
def add_vertex(self, vertex):
"""Adds a vertex to the current model
Will also update its bounding box
"""
self.vertices.append(vertex)
self.bounding_box.add(vertex)
def add_tex_coord(self, tex_coord):
"""Adds a texture coordinate element to the current model
"""
self.tex_coords.append(tex_coord)
def add_normal(self, normal):
"""Adds a normal element to the current model
"""
self.normals.append(normal)
def add_face(self, face):
"""Adds a face to the current model
If the face has a different material than the current material, it will
create a new mesh part and update the current material.
"""
if self.current_part is None or face.material != self.current_part.material:
self.current_part = MeshPart(self)
self.current_part.material = face.material if face.material is not None else Material.DEFAULT_MATERIAL
@ -83,9 +125,15 @@ class ModelParser:
self.current_part.add_face(face)
def parse_line(self, string):
"""Parses a line of a text model
This method needs to be implemented by each subclass of ModelParser.
"""
pass
def parse_file(self, path):
"""Sets the path of the model and parse each line
"""
self.path = path
with open(path) as f:
for line in f.readlines():
@ -93,6 +141,8 @@ class ModelParser:
self.parse_line(line)
def draw(self):
"""Draws each part of the model with OpenGL
"""
import OpenGL.GL as gl
if self.center_and_scale:
@ -109,11 +159,16 @@ class ModelParser:
gl.glPopMatrix()
def generate_vbos(self):
"""Generates the VBOs of each part of the model
"""
for part in self.parts:
part.generate_vbos()
def generate_vertex_normals(self):
"""Generate the normals for each vertex of the model
A normal will be the average normal of the adjacent faces of a vertex.
"""
self.normals = [Normal() for i in self.vertices]
for part in self.parts:
@ -135,7 +190,10 @@ class ModelParser:
face.c.normal = face.c.vertex
def generate_face_normals(self):
"""Generate the normals for each face of the model
A normal will be the normal of the face
"""
# Build array of faces
faces = sum(map(lambda x: x.faces, self.parts), [])
self.normals = [Normal()] * len(faces)
@ -154,7 +212,11 @@ class ModelParser:
class BoundingBox:
"""Represents a bounding box of a 3D model
"""
def __init__(self):
"""Initializes the coordinates of the bounding box
"""
self.min_x = +float('inf')
self.min_y = +float('inf')
self.min_z = +float('inf')
@ -163,16 +225,23 @@ class BoundingBox:
self.max_y = -float('inf')
self.max_z = -float('inf')
def add(self, vertex):
self.min_x = min(self.min_x, vertex.x)
self.min_y = min(self.min_y, vertex.y)
self.min_z = min(self.min_z, vertex.z)
def add(self, vector):
"""Adds a vector to a bounding box
self.max_x = max(self.max_x, vertex.x)
self.max_y = max(self.max_y, vertex.y)
self.max_z = max(self.max_z, vertex.z)
If the vector is outside the bounding box, the bounding box will be
enlarged, otherwise, nothing will happen.
"""
self.min_x = min(self.min_x, vector.x)
self.min_y = min(self.min_y, vector.y)
self.min_z = min(self.min_z, vector.z)
self.max_x = max(self.max_x, vector.x)
self.max_y = max(self.max_y, vector.y)
self.max_z = max(self.max_z, vector.z)
def __str__(self):
"""Returns a string that represents the bounding box
"""
return "[{},{}],[{},{}],[{},{}]".format(
self.min_x,
self.min_y,
@ -182,12 +251,16 @@ class BoundingBox:
self.max_z)
def get_center(self):
"""Returns the center of the bounding box
"""
return Vertex(
(self.min_x + self.max_x) / 2,
(self.min_y + self.max_y) / 2,
(self.min_z + self.max_z) / 2)
def get_scale(self):
"""Returns the maximum edge of the bounding box
"""
return max(
abs(self.max_x - self.min_x),
abs(self.max_y - self.min_y),
@ -195,6 +268,8 @@ class BoundingBox:
class Exporter:
"""Represents an object that can export a model into a certain format
"""
def __init__(self, model):
self.model = model