Removed shaders, textures are now supported

This commit is contained in:
Thomas FORGIONE 2016-11-29 15:40:30 +01:00
parent 85c14c8b09
commit 1443ea35ba
5 changed files with 225 additions and 103 deletions

View File

@ -4,6 +4,8 @@ from math import sqrt
from ..geometry import Vector from ..geometry import Vector
from .mesh import Material, MeshPart
Vertex = Vector Vertex = Vector
Normal = Vertex Normal = Vertex
TexCoord = Vertex TexCoord = Vertex
@ -43,22 +45,14 @@ class Face:
self.c = FaceVertex().from_array(arr[2]) self.c = FaceVertex().from_array(arr[2])
return self return self
class Material:
def __init__(self, name):
self.name = name
self.Ka = None
self.Kd = None
self.Ks = None
self.map_Kd = None
class ModelParser: class ModelParser:
def __init__(self): def __init__(self):
self.vertices = [] self.vertices = []
self.normals = [] self.normals = []
self.tex_coords = [] self.tex_coords = []
self.faces = [] self.parts = []
self.current_part = None
self.bounding_box = BoundingBox() self.bounding_box = BoundingBox()
self.center_and_scale = True self.center_and_scale = True
self.vertex_vbo = None self.vertex_vbo = None
@ -66,6 +60,10 @@ class ModelParser:
self.normal_vbo = None self.normal_vbo = None
self.path = None self.path = None
def init_textures(self):
for part in self.parts:
part.init_texture()
def add_vertex(self, vertex): def add_vertex(self, vertex):
self.vertices.append(vertex) self.vertices.append(vertex)
self.bounding_box.add(vertex) self.bounding_box.add(vertex)
@ -77,7 +75,12 @@ class ModelParser:
self.normals.append(normal) self.normals.append(normal)
def add_face(self, face): def add_face(self, face):
self.faces.append(face) if self.current_part is None or face.material != self.current_part.material:
self.current_part = MeshPart(self)
self.current_part.material = face.material
self.parts.append(self.current_part)
self.current_part.add_face(face)
def parse_line(self, string): def parse_line(self, string):
pass pass
@ -90,11 +93,8 @@ class ModelParser:
self.parse_line(line) self.parse_line(line)
def draw(self): def draw(self):
import OpenGL.GL as gl import OpenGL.GL as gl
gl.glColor3f(1.0,0.0,0.0)
if self.center_and_scale: if self.center_and_scale:
center = self.bounding_box.get_center() center = self.bounding_box.get_center()
scale = self.bounding_box.get_scale() / 2 scale = self.bounding_box.get_scale() / 2
@ -102,104 +102,43 @@ class ModelParser:
gl.glScalef(1/scale, 1/scale, 1/scale) gl.glScalef(1/scale, 1/scale, 1/scale)
gl.glTranslatef(-center.x, -center.y, -center.z) gl.glTranslatef(-center.x, -center.y, -center.z)
if self.vertex_vbo is not None: for part in self.parts:
part.draw()
self.vertex_vbo.bind()
gl.glEnableClientState(gl.GL_VERTEX_ARRAY);
gl.glVertexPointerf(self.vertex_vbo)
self.vertex_vbo.unbind()
self.normal_vbo.bind()
gl.glEnableClientState(gl.GL_NORMAL_ARRAY);
gl.glNormalPointerf(self.normal_vbo)
self.normal_vbo.unbind()
gl.glDrawArrays(gl.GL_TRIANGLES, 0, len(self.vertex_vbo.data) * 9)
else:
gl.glBegin(gl.GL_TRIANGLES)
for face in self.faces:
v1 = self.vertices[face.a.vertex]
v2 = self.vertices[face.b.vertex]
v3 = self.vertices[face.c.vertex]
if face.a.normal is not None:
n1 = self.normals[face.a.normal]
n2 = self.normals[face.b.normal]
n3 = self.normals[face.c.normal]
if face.a.normal is not None:
gl.glNormal3f(n1.x, n1.y, n1.z)
gl.glVertex3f(v1.x, v1.y, v1.z)
if face.b.normal is not None:
gl.glNormal3f(n2.x, n2.y, n2.z)
gl.glVertex3f(v2.x, v2.y, v2.z)
if face.c.normal is not None:
gl.glNormal3f(n3.x, n3.y, n3.z)
gl.glVertex3f(v3.x, v3.y, v3.z)
gl.glEnd()
if self.center_and_scale: if self.center_and_scale:
gl.glPopMatrix() gl.glPopMatrix()
def generate_vbos(self): def generate_vbos(self):
from OpenGL.arrays import vbo for part in self.parts:
from numpy import array part.generate_vbos()
# Build VBO
v = []
n = []
t = []
for face in self.faces:
v1 = self.vertices[face.a.vertex]
v2 = self.vertices[face.b.vertex]
v3 = self.vertices[face.c.vertex]
v += [[v1.x, v1.y, v1.z], [v2.x, v2.y, v2.z], [v3.x, v3.y, v3.z]]
if face.a.normal is not None:
n1 = self.normals[face.a.normal]
n2 = self.normals[face.b.normal]
n3 = self.normals[face.c.normal]
n += [[n1.x, n1.y, n1.z], [n2.x, n2.y, n2.z], [n3.x, n3.y, n3.z]]
if face.a.tex_coord is not None:
t1 = self.tex_coords[face.a.tex_coord]
t2 = self.tex_coords[face.b.tex_coord]
t3 = self.tex_coords[face.c.tex_coord]
t += [[t1.x, t1.y], [t2.x, t2.y], [t3.x, t3.y]]
self.vertex_vbo = vbo.VBO(array(v, 'f'))
self.normal_vbo = vbo.VBO(array(n, 'f'))
self.texture_vbo = vbo.VBO(array(t, 'f'))
def generate_vertex_normals(self): def generate_vertex_normals(self):
self.normals = [Normal() for i in self.vertices] self.normals = [Normal() for i in self.vertices]
for face in self.faces: for part in self.parts:
v1 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.b.vertex]) for face in part.faces:
v2 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.c.vertex]) v1 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.b.vertex])
cross = Vertex.cross_product(v1, v2) v2 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.c.vertex])
self.normals[face.a.vertex] += cross cross = Vertex.cross_product(v1, v2)
self.normals[face.b.vertex] += cross self.normals[face.a.vertex] += cross
self.normals[face.c.vertex] += cross self.normals[face.b.vertex] += cross
self.normals[face.c.vertex] += cross
for normal in self.normals: for normal in self.normals:
normal.normalize() normal.normalize()
for face in self.faces: for part in self.parts:
face.a.normal = face.a.vertex for face in part.faces:
face.b.normal = face.b.vertex face.a.normal = face.a.vertex
face.c.normal = face.c.vertex face.b.normal = face.b.vertex
face.c.normal = face.c.vertex
def generate_face_normals(self): def generate_face_normals(self):
self.normals = [Normal() for i in self.faces] self.normals = [Normal() for i in sum(list(map(lambda x: len(x), self.faces)))]
for (index, face) in enumerate(self.faces): for (index, face) in enumerate(sum(self.faces, [])):
v1 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.b.vertex]) v1 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.b.vertex])
v2 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.c.vertex]) v2 = Vertex.from_points(self.vertices[face.a.vertex], self.vertices[face.c.vertex])

173
d3/model/mesh.py Normal file
View File

@ -0,0 +1,173 @@
#!/usr/bin/env python
class Material:
def __init__(self, name):
self.name = name
self.Ka = None
self.Kd = None
self.Ks = None
self.map_Kd = None
self.id = None
def init_texture(self):
import OpenGL.GL as gl
# Already initialized
if self.id is not None:
return
# If no map_Kd, nothing to do
if self.map_Kd is None:
return
try:
ix, iy, image = self.map_Kd.size[0], self.map_Kd.size[1], self.map_Kd.tobytes("raw", "RGBA", 0, -1)
except SystemError:
ix, iy, image = self.map_Kd.size[0], self.map_Kd.size[1], self.map_Kd.tobytes("raw", "RGBX", 0, -1)
self.id = gl.glGenTextures(1)
gl.glBindTexture(gl.GL_TEXTURE_2D, self.id)
gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT,1)
gl.glTexImage2D(
gl.GL_TEXTURE_2D, 0, 3, ix, iy, 0,
gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, image
)
def bind(self):
from OpenGL import GL as gl
gl.glEnable(gl.GL_TEXTURE_2D)
gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST)
gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST)
gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_DECAL)
gl.glBindTexture(gl.GL_TEXTURE_2D, self.id)
def unbind(self):
pass
class MeshPart:
def __init__(self, parent):
self.parent = parent
self.material = None
self.vertex_vbo = None
self.tex_coord_vbo = None
self.normal_vbo = None
self.faces = []
def init_texture(self):
if self.material is not None:
self.material.init_texture()
def add_face(self, face):
self.faces.append(face)
def generate_vbos(self):
from OpenGL.arrays import vbo
from numpy import array
# Build VBO
v = []
n = []
t = []
for face in self.faces:
v1 = self.parent.vertices[face.a.vertex]
v2 = self.parent.vertices[face.b.vertex]
v3 = self.parent.vertices[face.c.vertex]
v += [[v1.x, v1.y, v1.z], [v2.x, v2.y, v2.z], [v3.x, v3.y, v3.z]]
if face.a.normal is not None:
n1 = self.parent.normals[face.a.normal]
n2 = self.parent.normals[face.b.normal]
n3 = self.parent.normals[face.c.normal]
n += [[n1.x, n1.y, n1.z], [n2.x, n2.y, n2.z], [n3.x, n3.y, n3.z]]
if face.a.tex_coord is not None:
t1 = self.parent.tex_coords[face.a.tex_coord]
t2 = self.parent.tex_coords[face.b.tex_coord]
t3 = self.parent.tex_coords[face.c.tex_coord]
t += [[t1.x, t1.y], [t2.x, t2.y], [t3.x, t3.y]]
self.vertex_vbo = vbo.VBO(array(v, 'f'))
if len(n) > 0:
self.normal_vbo = vbo.VBO(array(n, 'f'))
if len(t) > 0:
self.tex_coord_vbo = vbo.VBO(array(t, 'f'))
def draw(self):
if self.material is not None:
self.material.bind()
if self.vertex_vbo is not None:
self.draw_from_vbos()
else:
self.draw_from_arrays()
if self.material is not None:
self.material.unbind()
def draw_from_vbos(self):
import OpenGL.GL as gl
self.vertex_vbo.bind()
gl.glEnableClientState(gl.GL_VERTEX_ARRAY);
gl.glVertexPointerf(self.vertex_vbo)
self.vertex_vbo.unbind()
if self.normal_vbo is not None:
self.normal_vbo.bind()
gl.glEnableClientState(gl.GL_NORMAL_ARRAY)
gl.glNormalPointerf(self.normal_vbo)
self.normal_vbo.unbind()
if self.tex_coord_vbo is not None:
if self.material is not None:
self.material.bind()
self.tex_coord_vbo.bind()
gl.glEnableClientState(gl.GL_TEXTURE_COORD_ARRAY)
gl.glTexCoordPointerf(self.tex_coord_vbo)
self.tex_coord_vbo.unbind()
gl.glDrawArrays(gl.GL_TRIANGLES, 0, len(self.vertex_vbo.data) * 9)
def draw_from_arrays(self):
pass
class Mesh:
def __init__(self):
self.vertices = []
self.tex_coords = []
self.normals = []
self.parts = []
self.current_material = None
self.current_part = None
def draw(self):
for part in self.parts:
part.draw()
def add_face(self, face):
if self.current_part is None or face.material != self.current_part.material:
self.current_part = MeshPart(self)
self.current_part.material = face.material
self.parts.append(self.current_part)
self.current_part.add_face(face)
def generate_vbos(self):
for part in self.parts:
part.generate_vbos()

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from .basemodel import ModelParser, Exporter, Vertex, TexCoord, Normal, FaceVertex, Face, Material from .basemodel import ModelParser, Exporter, Vertex, TexCoord, Normal, FaceVertex, Face
from .mesh import Material, MeshPart
from functools import reduce from functools import reduce
import os.path import os.path
import PIL.Image import PIL.Image
@ -24,7 +25,7 @@ class OBJParser(ModelParser):
split = split[1:] split = split[1:]
if first == 'usemtl': if first == 'usemtl':
self.current_material = split[0] self.current_material = self.mtl[split[0]]
elif first == 'mtllib': elif first == 'mtllib':
path = os.path.join(os.path.dirname(self.path), split[0]) path = os.path.join(os.path.dirname(self.path), split[0])
self.mtl = MTLParser(self) self.mtl = MTLParser(self)
@ -73,7 +74,7 @@ class MTLParser:
elif first == 'Ks': elif first == 'Ks':
self.current_mtl.Ks = Vertex().from_array(split) self.current_mtl.Ks = Vertex().from_array(split)
elif first == 'map_Kd': elif first == 'map_Kd':
self.map_Kd = PIL.Image.open(os.path.join(os.path.dirname(self.parent.path), split[0])) self.current_mtl.map_Kd = PIL.Image.open(os.path.join(os.path.dirname(self.parent.path), split[0]))
def parse_file(self, path): def parse_file(self, path):
@ -82,6 +83,11 @@ class MTLParser:
line = line.rstrip() line = line.rstrip()
self.parse_line(line) self.parse_line(line)
def __getitem__(self, key):
for material in self.materials:
if material.name == key:
return material
class OBJExporter(Exporter): class OBJExporter(Exporter):
def __init__(self, model): def __init__(self, model):

View File

@ -82,6 +82,9 @@ class PLYExporter(Exporter):
super().__init__(model) super().__init__(model)
def __str__(self): def __str__(self):
faces = sum([part.faces for part in self.model.parts], [])
# Header # Header
string = "ply\nformat ascii 1.0\ncomment Automatically gnerated by model-converter\n" string = "ply\nformat ascii 1.0\ncomment Automatically gnerated by model-converter\n"
@ -90,7 +93,7 @@ class PLYExporter(Exporter):
string += "property float32 x\nproperty float32 y\nproperty float32 z\n" string += "property float32 x\nproperty float32 y\nproperty float32 z\n"
# Types : faces # Types : faces
string += "element face " + str(len(self.model.faces)) + "\n" string += "element face " + str(len(faces)) + "\n"
string += "property list uint8 int32 vertex_indices\n" string += "property list uint8 int32 vertex_indices\n"
# End header # End header
@ -100,7 +103,7 @@ class PLYExporter(Exporter):
for vertex in self.model.vertices: for vertex in self.model.vertices:
string += str(vertex.x) + " " + str(vertex.y) + " " + str(vertex.z) + "\n" string += str(vertex.x) + " " + str(vertex.y) + " " + str(vertex.z) + "\n"
for face in self.model.faces: for face in faces:
string += "3 " + str(face.a.vertex) + " " + str(face.b.vertex) + " " + str(face.c.vertex) + "\n" string += "3 " + str(face.a.vertex) + " " + str(face.b.vertex) + " " + str(face.c.vertex) + "\n"
return string return string

View File

@ -43,9 +43,10 @@ def main(args):
running = True running = True
model = load_model(args.input) model = load_model(args.input)
model.init_textures()
model.generate_vbos() model.generate_vbos()
shader = DefaultShader() # shader = DefaultShader()
while running: while running:
for event in pg.event.get(): for event in pg.event.get():
@ -72,9 +73,9 @@ def main(args):
gl.glPushMatrix() gl.glPushMatrix()
controls.apply() controls.apply()
shader.bind() # shader.bind()
model.draw() model.draw()
shader.unbind() # shader.unbind()
gl.glPopMatrix() gl.glPopMatrix()
gl.glFlush() gl.glFlush()