Despues de probar python estoy pasando todos los proyectos y rehaciendo los antiguos de C++ a python.

Aunque jugué en su momento con pyOpenGL, apenas hize algunas pruebas por encima con cubos y juego de camara.

Cuando he vuelto a meterle mano al 3D y necesitado cargar objetos, he buscado algo ya hecho de lo que partir y encontré en la página de pygame el OBJFileLoader.

Me ha resultado muy útil, sin embargo he tenido que modificar algunas cosas del original para corregir algun problema de carga.

objloader.py

Implementa la carga del modelo 3D en formato obj (wavefront)

# Basic objloader from:
#  Original por http://www.pygame.org/wiki/OBJFileLoader
#  Ajustes por altaruru: https://www.altaruru.com
#  >> resolucion errores a python3, carga de archivos y recursos en directorios distintos a script/codigo

import pygame
import os
from OpenGL.GL import *

def MTL(spath2, filename):
	contents = {}
	mtl = None
	rpath=os.path.dirname(os.path.abspath(__file__)) + '/' + spath2
	print("MTL: " + filename)
	print("rpath: " + rpath)
	for line in open(rpath+filename, "r"):
		if line.startswith('#'): continue		
		values = line.split()
		if not values: continue
		if values[0] == 'newmtl':
			mtl = contents[values[1]] = {}
		elif mtl is None:
			raise ValueError("mtl file doesn't start with newmtl stmt")
		elif values[0] == 'map_Kd':
			# load the texture referred to by this declaration
			mtl[values[0]] = values[1]
			smatpath=mtl['map_Kd']			
			smatpath=smatpath.replace("\\","/")
			print(rpath + smatpath)
			surf = pygame.image.load(rpath + smatpath)
			image = pygame.image.tostring(surf, 'RGBA', 1)
			ix, iy = surf.get_rect().size
			texid = mtl['texture_Kd'] = glGenTextures(1)
			glBindTexture(GL_TEXTURE_2D, texid)
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
		else:
			mtl[values[0]] = map(float, values[1:])
	return contents

class OBJ:
	def __init__(self, filename, swapyz=False):
		"""Loads a Wavefront OBJ file. """
		self.vertices = []
		self.normals = []
		self.texcoords = []
		self.faces = []

		material = None
		print(">>" + filename)
		i=filename.rfind("/")
		
		if(i==-1):
			spath2=""
		else:
			spath2=filename[0:i] + '/'
		
		for line in open(os.path.dirname(os.path.abspath(__file__)) + '/' + filename, "r"):
			if line.startswith('#'): continue
			values = line.split()
			if not values: continue
			if values[0] == 'v':
				v = list(map(float, values[1:4]))
				if swapyz:
					v = v[0], v[2], v[1]
				self.vertices.append(v)
			elif values[0] == 'vn':
				v = list(map(float, values[1:4]))
				if swapyz:
					v = v[0], v[2], v[1]
				self.normals.append(v)
			elif values[0] == 'vt':
				self.texcoords.append(list(map(float, values[1:3])))
			elif values[0] in ('usemtl', 'usemat'):
				material = values[1]
			elif values[0] == 'mtllib':
				#spath2 es la ruta relativa al objeto, puede ser un directorio
				self.mtl = MTL(spath2, values[1])
			elif values[0] == 'f':
				face = []
				texcoords = []
				norms = []
				for v in values[1:]:
					w = v.split('/')
					face.append(int(w[0]))
					if len(w) >= 2 and len(w[1]) > 0:
						texcoords.append(int(w[1]))
					else:
						texcoords.append(0)
					if len(w) >= 3 and len(w[2]) > 0:
						norms.append(int(w[2]))
					else:
						norms.append(0)
				self.faces.append((face, norms, texcoords, material))

		self.gl_list = glGenLists(1)
		glNewList(self.gl_list, GL_COMPILE)
		glEnable(GL_TEXTURE_2D)
		glFrontFace(GL_CCW)
		for face in self.faces:
			vertices, normals, texture_coords, material = face

			mtl = self.mtl[material]
			#print("#   ")
			#print(mtl)
			if 'texture_Kd' in mtl:
				# use diffuse texmap
				glBindTexture(GL_TEXTURE_2D, mtl['texture_Kd'])
			else:
				# just use diffuse colour
				#glColor(*mtl['Kd'])
				glColor(1, 1, 1, 0.5)

			glBegin(GL_POLYGON)
			for i in range(len(vertices)):
				if normals[i] > 0:
					glNormal3fv(self.normals[normals[i] - 1])
				if texture_coords[i] > 0:
					glTexCoord2fv(self.texcoords[texture_coords[i] - 1])
				glVertex3fv(self.vertices[vertices[i] - 1])
			glEnd()
		glDisable(GL_TEXTURE_2D)
		glEndList()

objviewer.py

Ejemplo de carga utilizando objloader.py

# Basic OBJ file viewer. needs objloader from:
#  Original por http://www.pygame.org/wiki/OBJFileLoader
#  Ajustes por altaruru: https://www.altaruru.com
#  >> correcciones a la carga desde directorios distintos a origen
#  >> reposicionamiento inicial objeto
# LMB + move: rotate
# RMB + move: pan
# Scroll wheel: zoom in/out
import sys, pygame
from pygame.locals import *
from pygame.constants import *
from OpenGL.GL import *
from OpenGL.GLU import *

# IMPORT OBJECT LOADER
from objloader import *

pygame.init()
viewport = (800,600)
hx = viewport[0]/2
hy = viewport[1]/2
srf = pygame.display.set_mode(viewport, OPENGL | DOUBLEBUF)

glLightfv(GL_LIGHT0, GL_POSITION,  (-40, 200, 100, 0.0))
glLightfv(GL_LIGHT0, GL_AMBIENT, (0.2, 0.2, 0.2, 1.0))
glLightfv(GL_LIGHT0, GL_DIFFUSE, (0.5, 0.5, 0.5, 1.0))
glEnable(GL_LIGHT0)
glEnable(GL_LIGHTING)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)           # most obj files expect to be smooth-shaded

# LOAD OBJECT AFTER PYGAME INIT
obj = OBJ(sys.argv[1], swapyz=True)

clock = pygame.time.Clock()

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
width, height = viewport
gluPerspective(90.0, width/float(height), 1, 100.0)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_MODELVIEW)

#rx, ry = (0,0)
rx, ry = (160,-172) 
#tx, ty = (0,0)
tx, ty = (0,-174)
zpos = 15
rotate = move = False
while 1:
	clock.tick(30)
	for e in pygame.event.get():
		if e.type == QUIT:
			sys.exit()
		elif e.type == KEYDOWN and e.key == K_ESCAPE:
			sys.exit()
		elif e.type == MOUSEBUTTONDOWN:
			if e.button == 4: zpos = max(1, zpos-1)
			elif e.button == 5: zpos += 1
			elif e.button == 1: rotate = True
			elif e.button == 3: move = True
		elif e.type == MOUSEBUTTONUP:
			if e.button == 1: rotate = False
			elif e.button == 3: move = False
		elif e.type == MOUSEMOTION:
			i, j = e.rel
			if rotate:
				rx += i
				ry += j
				print("rotate: x=%d y=%d z=%d; " % (rx,ry,zpos))
			if move:
				tx += i
				ty -= j
				print("move: x=%d y=%d z=%d" % (tx,ty,zpos))

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	glLoadIdentity()

	# RENDER OBJECT
	glTranslate(tx/20., ty/20., - zpos)
	glRotate(ry, 1, 0, 0)
	glRotate(rx, 0, 1, 0)
	glCallList(obj.gl_list)

	pygame.display.flip()

Deja los archivos en un directorio y el modelo 3D en un directorio y ejecuta desde Terminal:

python3 objviewer.py Charizard/P2_Charizard.obj

El modelo utilizado el Dragon Charizard de pokemon, aunque puedes usar cualquier otro.

Puedes descargar el modelo 3d de Charizard desde https://free3d.com/3d-model/charizard-85299.html .

Tienes más recursos 3d en https://free3d.com

Espero comentarios.

Feliz desarrollo!

Un comentario sobre «Opengl en python con pyOpenGL y pygame»

  1. Juan

    ¡Muchas gracias por la ayuda!

Deja tu comentario