Python opengl загрузка моделей

Работа с 3-х мерными объектами в Python с использованием OpenGL

В данной программе будет представлен пример, как работать с объёмными фигурами в
OpenGL на языке Python. В итоге будет представлен куб в 3-ёх мерном виде.

Среда разработки: PyCharm Community Edition 2020.1.2

Для начала работы потребуется наличие следующих инструментов или пакетов: Python, PyOpenGL, PyGame.

Как установить Python можно посмотреть на официальном сайте, ссылка на него: https://www.python.org/

Далее рассмотрим установку пакетов на Python.

Для того, чтобы загрузить PyOpenGL и PyGame, воспользуемся PyPi. В терминале IDE вводим команды:

pip install pygame pip install PyOpenGL
import pygame import OpenGL

Если эти выражения не вызовут ошибок, значит вы готовы к дальнейшей работе. Если возникнут ошибки, значит при установке пакетов произошла ошибка, либо текущие версии пакетов несовместимы с Вашей версией Python. Более детальную информацию можно узнать в документации к используемым пакетам Python’a: PyGame и PyOpenGL.

Отлично, теперь перейдем непосредственно к коду! Если у вас еще сохранился код импорта, который мы только что запускали, сотрите его: мы начнем полностью с чистого листа.

Для начала произведем импорт необходимых библиотек:

import pygame from pygame.locals import * from OpenGL.GL import * from OpenGL.GLU import *

Мы импортируем все из PyGame, а затем все из PyGame.locals. Это вполне стандартный код для PyGame.Затем мы импортируем OpenGL.GL и OpenGL.GLU. OpenGL.GL содержит в себе самые обычные функции библиотеки OpenGL, а вот OpenGL.GLU — более сложных объектов.

Изначально создадим функцию main() для удобства:

vertices= ( (1.0, -1.0, -1.0), (1.0, 1.0, -1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, -1.0), (1.0, - 1.0, 1.0), (1.0, 1.0, 1.0), (-1.0, -1.0, 1.0), (-1.0, 1.0, 1.0) )

Здесь мы определили координаты (x, y, z) каждой нашей вершины. Думаю, лучше всего представить это в относительных единицах.

Далее нам надо задать ребра:

edges = ( (0,1), (0,3), (0,4), (2,1), (2,3), (2,7), (6,3), (6,4), (6,7), (5,1), (5,4), (5,7) )

Каждое ребро представлено кортежем, состоящим из двух чисел. Эти числа соответствуют номерам вершин, а ребро их соединяет. Как принято в Python, да и во многих других языках программирования, нумерация начинается с 0. Соответственно, 0 обозначает вершину (1, -1, -1), и так далее.

Теперь, когда у нас все это есть, давайте поработаем над необходимым кодом для работы с OpenGL, чтобы фактически создать сам куб (описывать функцию Cube() мы будем вне функции main()):

def Cube(vertices, edges): glBegin(GL_LINES) for edge in edges: for vertex in edge: glVertex3fv(vertices[vertex]) glEnd()

Как обычно, мы начинаем с задания функции. Поскольку это просто функция, которая будет содержать код OpenGL, мы начинаем этот код со следующего выражения: glBegin (GL_LINES). Это уведомляет OpenGL сначала о том, что мы собираемся бросить в нее какой-то код, а затем — о том, как надо обрабатывать этот код (это указывает аргумент GL_LINES). В данном случае этот код будет рассматриваться как код для рисования линий.

Читайте также:  IDK what

Далее, мы итерируем по всем нашим ребрам (список edges), а затем каждой вершине в ребре (их там две) мы ставим в соответствие вершину из нашего списка вершин vertices (при помощи функции glVertex3fv).

glVertex3fv((1, -1, -1)) glVertex3fv((1, 1, -1))

После прохождения всех ребер работа функции заканчивается и мы вызываем glEnd(), чтобы сообщить об этом OpenGL. Подобные открывающие и закрывающие команды используются в OpenGL постоянно.

Это все, что касается нашей функции. Она создает куб, но теперь мы хотим его отобразить и указать его перспективу в нашем окружении, для этого возвращаемся в функцию main() и пишем следующее:

pygame.init() display = (800,600) pygame.display.set_mode(display, DOUBLEBUF|OPENGL)

Это тоже очень типичный для Pygame код. Единственное существенное отличие здесь в том, что после параметра display в функции pygame.display.set_mode мы добавляем еще один параметр. На самом деле это константы, уведомляющие PyGame о том, что мы будем использовать код OpenGL. Константа DOUBLEBUF расшифровывается как двойной буфер. Она обозначает тип буфферизации, в котором есть два буфера для соответствия кадровой частоте монитора. Обратите внимание, что для разделения констант используется символ «|». Мы еще столкнемся с ним в дальнейшем.

Идем дальше. В теле главной функции находится следующий код:

gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

Функция gluPerspective определяет перспективу, о чем, впрочем, несложно догадаться из ее названия. Ее первый параметр определяет угол поля зрения и выражается в градусах. Второй параметр — это соотношение сторон дисплея, ширина, деленная на высоту. Следующие два параметра — znear и zfar, которые представляют собой ближнюю и дальнюю плоскости отсечения.

Идем дальше. У нас есть следующая функция:

glTranslatef(0.0, -1.0, -10) glRotatef(20, 10, 0, 0)

Теперь напишем наш типичный цикл для отслеживания событий в PyGame:

while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit()

Это простой цикл отслеживания событий в PyGame, который определяет возможность выхода. Иными словами, он отслеживает нажатие клавиши «x». Далее, под оператором while продолжаем наш код:

glRotatef(1, 0, 1, 0) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) Cube() pygame.display.flip() pygame.time.wait(10)

Функция glRotatef умножает текущую матрицу на матрицу вращения. Ее параметрами являются угол вращения и координаты x, y, и z.

Читайте также:  Java iterate all classes

Затем у нас есть функция glClear, работающая, как любая другая функция очистки. Мы указываем в ее параметрах пару констант, которые сообщают OpenGL, что именно мы очищаем.

Как только мы очистим «экран», мы опять вызовем нашу функцию Cube ().
После этого мы вызываем функцию pygame.display.flip (), которая обновляет наш экран.
И наконец, мы вставляем небольшую задержку при помощи функции pygame.time.wait (10).

И в завершении в самой программе просто вызываем функцию main().

import pygame from pygame.locals import * from OpenGL.GL import * from OpenGL.GLU import * def Cube(vertices, edges): glBegin(GL_LINES) for edge in edges: for vertex in edge: glVertex3fv(vertices[vertex]) glEnd()  def main(): vertices = ( (1.0, -1.0, -1.0), (1.0, 1.0, -1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, -1.0), (1.0, - 1.0, 1.0), (1.0, 1.0, 1.0), (-1.0, -1.0, 1.0), (-1.0, 1.0, 1.0) ) edges = ( (0, 1), (0, 3), (0, 4), (2, 1), (2, 3), (2, 7), (6, 3), (6, 4), (6, 7), (5, 1), (5, 4), (5, 7) ) pygame.init() display = (800, 600) pygame.display.set_mode(display, DOUBLEBUF|OPENGL) gluPerspective(45, (display[0]/display[1]), 0.1, 50.0) glTranslatef(0.0, -1.0, -10) glRotatef(20, 10, 0, 0) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit() glRotatef(1, 0, 1, 0) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) Cube(vertices, edges) pygame.display.flip() pygame.time.wait(10) main()
Прикрепленный файл Размер
main.rar 573 байта

Источник

python stl model loading and display with opengl

This Code shows how to load in a 3 dimensional model from a stl file format which is often used for rapid prototyping and cad software. This example can load both text and binary stl files and was written to load the model files from the reprap open source 3d printer project.

import os
import struct

from OpenGL.GL import *
from OpenGL.GLU import *
import pygame
from pygame.locals import *

#class for a 3d point
class createpoint:
def __init__(self,p,c=(1,0,0)):
self.point_size=0.5
self.color=c
self.x=p[0]
self.y=p[1]
self.z=p[2]
def glvertex(self):
glVertex3f(self.x,self.y,self.z)

#class for a 3d face on a model
class createtriangle:
points=None
normal=None

def __init__(self,p1,p2,p3,n=None):
#3 points of the triangle
self.points=createpoint(p1),createpoint(p2),createpoint(p3)
#triangles normal
self.normal=createpoint(self.calculate_normal(self.points[0],self.points[1],self.points[2]))#(0,1,0)#
#calculate vector / edge
def calculate_vector(self,p1,p2):
return -p1.x+p2.x,-p1.y+p2.y,-p1.z+p2.z
def calculate_normal(self,p1,p2,p3):
a=self.calculate_vector(p3,p2)
b=self.calculate_vector(p3,p1)
#calculate the cross product returns a vector
return self.cross_product(a,b) def cross_product(self,p1,p2):
return (p1[1]*p2[2]-p2[1]*p1[2]) , (p1[2]*p2[0])-(p2[2]*p1[0]) , (p1[0]*p2[1])-(p2[0]*p1[1])

Читайте также:  Массив простых чисел python

class loader:
model=[]
#return the faces of the triangles
def get_triangles(self):
if self.model:
for face in self.model:
yield face

#draw the models faces
def draw(self):
glBegin(GL_TRIANGLES)
for tri in self.get_triangles():
glNormal3f(tri.normal.x,tri.normal.y,tri.normal.z)
glVertex3f(tri.points[0].x,tri.points[0].y,tri.points[0].z)
glVertex3f(tri.points[1].x,tri.points[1].y,tri.points[1].z)
glVertex3f(tri.points[2].x,tri.points[2].y,tri.points[2].z)
glEnd()
#load stl file detects if the file is a text file or binary file
def load_stl(self,filename):
#read start of file to determine if its a binay stl file or a ascii stl file
fp=open(filename,’rb’)
h=fp.read(80)
type=h[0:5]
fp.close()

if type==’solid’:
print «reading text file»+str(filename)
self.load_text_stl(filename)
else:
print «reading binary stl file «+str(filename,)
self.load_binary_stl(filename)
#read text stl match keywords to grab the points to build the model
def load_text_stl(self,filename):
fp=open(filename,’r’)

for line in fp.readlines():
words=line.split()
if len(words)>0:
if words[0]==’solid’:
self.name=words[1]

if words[0]==’facet’:
center=[0.0,0.0,0.0]
triangle=[]
normal=(eval(words[2]),eval(words[3]),eval(words[4]))
if words[0]==’vertex’:
triangle.append((eval(words[1]),eval(words[2]),eval(words[3])))
if words[0]==’endloop’:
#make sure we got the correct number of values before storing
if len(triangle)==3:
self.model.append(createtriangle(triangle[0],triangle[1],triangle[2],normal))
fp.close()

#load binary stl file check wikipedia for the binary layout of the file
#we use the struct library to read in and convert binary data into a format we can use
def load_binary_stl(self,filename):
fp=open(filename,’rb’)
h=fp.read(80)

l=struct.unpack(‘I’,fp.read(4))[0]
count=0
while True:
try:
p=fp.read(12)
if len(p)==12:
n=struct.unpack(‘f’,p[0:4])[0],struct.unpack(‘f’,p[4:8])[0],struct.unpack(‘f’,p[8:12])[0]
p=fp.read(12)
if len(p)==12:
p1=struct.unpack(‘f’,p[0:4])[0],struct.unpack(‘f’,p[4:8])[0],struct.unpack(‘f’,p[8:12])[0]

p=fp.read(12)
if len(p)==12:
p2=struct.unpack(‘f’,p[0:4])[0],struct.unpack(‘f’,p[4:8])[0],struct.unpack(‘f’,p[8:12])[0]

p=fp.read(12)
if len(p)==12:
p3=struct.unpack(‘f’,p[0:4])[0],struct.unpack(‘f’,p[4:8])[0],struct.unpack(‘f’,p[8:12])[0]

new_tri=(n,p1,p2,p3)

if len(new_tri)==4:
tri=createtriangle(p1,p2,p3,n)
self.model.append(tri)
count+=1
fp.read(2)

if len(p)==0:
break
except EOFError:
break
fp.close()

class draw_scene:
def __init__(self,style=1):
#create a model instance and
self.model1=loader()
#self.model1.load_stl(os.path.abspath(»)+’/text.stl’)
self.model1.load_stl(os.path.abspath(»)+’/binary.stl’)
self.init_shading()

#solid model with a light / shading
def init_shading(self):
glShadeModel(GL_SMOOTH)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH) glDepthFunc(GL_LEQUAL)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0) glLight(GL_LIGHT0, GL_POSITION, (0, 1, 1, 0)) glMatrixMode(GL_MODELVIEW)
def resize(self,(width, height)):
if height==0:
height=1
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, 1.0*width/height, 0.1, 100.0)
#gluLookAt(0.0,0.0,45.0,0,0,0,0,40.0,0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()

def init(self):
glShadeModel(GL_SMOOTH)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH) glDepthFunc(GL_LEQUAL)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)

glEnable(GL_COLOR_MATERIAL)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0) glLight(GL_LIGHT0, GL_POSITION, (0, 1, 1, 0))
glMatrixMode(GL_MODELVIEW)

def draw(self):
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0.0,-26.0, -100.0)
self.model1.draw()

#main program loop
def main():
#initalize pygame
pygame.init()
pygame.display.set_mode((640,480), OPENGL|DOUBLEBUF)

#setup the open gl scene
scene=draw_scene()
scene.resize((640,480))
frames = 0
ticks = pygame.time.get_ticks()
while 1:
event = pygame.event.poll()
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
break
#draw the scene
scene.draw()
pygame.display.flip()
frames = frames+1

print «fps: %d» % ((frames*1000)/(pygame.time.get_ticks()-ticks))

if __name__ == ‘__main__’:
main()

You can download some stl files from the link below.

Источник

Оцените статью