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 .mesh import Material, MeshPart
Vertex = Vector
Normal = Vertex
TexCoord = Vertex
@ -43,22 +45,14 @@ class Face:
self.c = FaceVertex().from_array(arr[2])
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:
def __init__(self):
self.vertices = []
self.normals = []
self.tex_coords = []
self.faces = []
self.parts = []
self.current_part = None
self.bounding_box = BoundingBox()
self.center_and_scale = True
self.vertex_vbo = None
@ -66,6 +60,10 @@ class ModelParser:
self.normal_vbo = None
self.path = None
def init_textures(self):
for part in self.parts:
part.init_texture()
def add_vertex(self, vertex):
self.vertices.append(vertex)
self.bounding_box.add(vertex)
@ -77,7 +75,12 @@ class ModelParser:
self.normals.append(normal)
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):
pass
@ -90,11 +93,8 @@ class ModelParser:
self.parse_line(line)
def draw(self):
import OpenGL.GL as gl
gl.glColor3f(1.0,0.0,0.0)
if self.center_and_scale:
center = self.bounding_box.get_center()
scale = self.bounding_box.get_scale() / 2
@ -102,104 +102,43 @@ class ModelParser:
gl.glScalef(1/scale, 1/scale, 1/scale)
gl.glTranslatef(-center.x, -center.y, -center.z)
if self.vertex_vbo is not None:
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()
for part in self.parts:
part.draw()
if self.center_and_scale:
gl.glPopMatrix()
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.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'))
for part in self.parts:
part.generate_vbos()
def generate_vertex_normals(self):
self.normals = [Normal() for i in self.vertices]
for face in self.faces:
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])
cross = Vertex.cross_product(v1, v2)
self.normals[face.a.vertex] += cross
self.normals[face.b.vertex] += cross
self.normals[face.c.vertex] += cross
for part in self.parts:
for face in part.faces:
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])
cross = Vertex.cross_product(v1, v2)
self.normals[face.a.vertex] += cross
self.normals[face.b.vertex] += cross
self.normals[face.c.vertex] += cross
for normal in self.normals:
normal.normalize()
for face in self.faces:
face.a.normal = face.a.vertex
face.b.normal = face.b.vertex
face.c.normal = face.c.vertex
for part in self.parts:
for face in part.faces:
face.a.normal = face.a.vertex
face.b.normal = face.b.vertex
face.c.normal = face.c.vertex
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])
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
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
import os.path
import PIL.Image
@ -24,7 +25,7 @@ class OBJParser(ModelParser):
split = split[1:]
if first == 'usemtl':
self.current_material = split[0]
self.current_material = self.mtl[split[0]]
elif first == 'mtllib':
path = os.path.join(os.path.dirname(self.path), split[0])
self.mtl = MTLParser(self)
@ -73,7 +74,7 @@ class MTLParser:
elif first == 'Ks':
self.current_mtl.Ks = Vertex().from_array(split)
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):
@ -82,6 +83,11 @@ class MTLParser:
line = line.rstrip()
self.parse_line(line)
def __getitem__(self, key):
for material in self.materials:
if material.name == key:
return material
class OBJExporter(Exporter):
def __init__(self, model):

View File

@ -82,6 +82,9 @@ class PLYExporter(Exporter):
super().__init__(model)
def __str__(self):
faces = sum([part.faces for part in self.model.parts], [])
# Header
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"
# 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"
# End header
@ -100,7 +103,7 @@ class PLYExporter(Exporter):
for vertex in self.model.vertices:
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"
return string

View File

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