Reputation: 2617
My goal is to render a .pmx
3D model using PyOpenGL on pygame. I've found pymeshio module that extracts vertices and normal vectors and etc. found an example code on it's github repo that renders on tkinter. I changed the code to render on pygame instead, didn't change parts related to OpenGL rendering. The output is this:
The model file is not corrupted, I checked it on Blender and MMD. I'm new with OpenGL and 3D programming but I think it might be related to sequence of vertices for back-face culling, some of triangles are clockwise and some of the others are counter clockwise.
this is rendering code. it uses draw
function to render.
class IndexedVertexArray(object):
def __init__(self):
# vertices
self.vertices=[]
self.normal=[]
self.colors=[]
self.uvlist=[]
self.b0=[]
self.b1=[]
self.w0=[]
self.materials=[]
self.indices=[]
self.buffers=[]
self.new_vertices=[]
self.new_normal=[]
def addVertex(self, pos, normal, uv, color, b0, b1, w0):
self.vertices+=pos
self.normal+=normal
self.colors+=color
self.uvlist+=uv
self.b0.append(b0)
self.b1.append(b1)
self.w0.append(w0)
def setIndices(self, indices):
self.indices=indices
def addMaterial(self, material):
self.materials.append(material)
def create_array_buffer(self, buffer_id, floats):
# print('create_array_buuffer', buffer_id)
glBindBuffer(GL_ARRAY_BUFFER, buffer_id)
glBufferData(GL_ARRAY_BUFFER,
len(floats)*4, # byte size
(ctypes.c_float*len(floats))(*floats), # 謎のctypes
GL_STATIC_DRAW)
def create_vbo(self):
self.buffers = glGenBuffers(4+1)
# print("create_vbo", self.buffers)
self.create_array_buffer(self.buffers[0], self.vertices)
self.create_array_buffer(self.buffers[1], self.normal)
self.create_array_buffer(self.buffers[2], self.colors)
self.create_array_buffer(self.buffers[3], self.uvlist)
# indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.buffers[4])
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
len(self.indices)*4, # byte size
(ctypes.c_uint*len(self.indices))(*self.indices), # 謎のctypes
GL_STATIC_DRAW)
def draw(self):
if len(self.buffers)==0:
self.create_vbo()
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, self.buffers[0]);
glVertexPointer(4, GL_FLOAT, 0, None);
glEnableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, self.buffers[1]);
glNormalPointer(GL_FLOAT, 0, None);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, self.buffers[2]);
glColorPointer(4, GL_FLOAT, 0, None);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, self.buffers[3]);
glTexCoordPointer(2, GL_FLOAT, 0, None);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.buffers[4]);
index_offset=0
for i, m in enumerate(self.materials):
# submesh
m.begin()
glDrawElements(GL_TRIANGLES, m.vertex_count, GL_UNSIGNED_INT, ctypes.c_void_p(index_offset));
index_offset+=m.vertex_count * 4 # byte size
m.end()
# cleanup
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY)
this is the part related to back-face culling
class MQOMaterial(object):
def __init__(self):
self.rgba=(1, 1, 1, 1)
self.vcol=False
self.texture=None
def __enter__(self):
self.begin()
def __exit__(self):
self.end()
def begin(self):
glColor4f(*self.rgba)
if self.texture:
self.texture.begin()
# backface culling
glEnable(GL_CULL_FACE)
glFrontFace(GL_CW)
glCullFace(GL_BACK)
# glCullFace(GL_FRONT)
# alpha test
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5);
def end(self):
if self.texture:
self.texture.end()
First I disabled alpha channel and did nothing.
I tried GL_FRONT
and GL_CCW
but it didn't work.
I tried to separate vertices groups and render them using glVertex3fv
. the original code already saves vertices in this format:
vertices = [v0.x, v0.y, v0.z, 1, v1.x, v1.y, v1.z, 1, v2.x, v2.y, v2.z, 1, ...]
___________________ ___________________ ___________________
v0 v1 v2
normal = [v0.normal.x, v0.normal.y, v0.normal.z, v1.normal.x, v1.normal.y, v1.normal.z, ...]
_____________________________________ _____________________________________
v0 v1
indices = [0, 1, 2, 1, 4, 5, 2, 4, 6, ...]
------- ------- -------
group0 group1 group2
I tried to render triangles with this code:
def _draw(self):
glBegin(GL_TRIANGLES)
for i in range(len(self.indices) // 3):
# glTexCoord2fv( tex_coords[ti] )
if i == len(self.new_normal):
break
# glNormal3fv( self.new_normal[i] )
glVertex3fv( self.new_vertices[i])
glEnd()
def new_sort(self):
for i in range(len(self.indices) // 3):
if i <= -1:
continue
k = 4 * i
j = 3 * i
if k + 2 >= len(self.vertices) or j + 2 >= len(self.normal):
break
self.new_vertices.append(tuple((self.vertices[k], self.vertices[k + 1], self.vertices[k + 2] )))
self.new_normal.append(tuple((self.normal[j], self.normal[j + 1], self.normal[j + 2] )))
the output
I thought maybe wrong points were together so shifted them with 1 and 2 to set correct points but the output became uglier. I tested this with quadrilateral and no change.
I would be appreciated for any help or hint.
Upvotes: 1
Views: 252
Reputation: 210878
The colorful images on the top seem to be rendered without depth test. You have to enable the Depth Test and clear the depth buffer:
glEnable(GL_DEPTH_TEST)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
Upvotes: 1