I am pretty new in using PyOpenGL and I want to load an obj file, move it, then draw a sphere and move it. I want the two objects to move separately when I use different keyboard buttons. Is it possible? (I also need the coordinate of the center of each object).
The problem is that they both move even if I use Push and Pop matrix commands
Here is my code:
rom objloader import *
def main():
pygame.init()
display = (800,600)
pygame.display.set_mode(display, OPENGL | DOUBLEBUF)
glMatrixMode(GL_PROJECTION)
gluPerspective(45.0, display[0]/display[1], 0.1, 100.0)
glLightfv(GL_LIGHT5, GL_POSITION, (-40, 200, 100, 0.0))
glLightfv(GL_LIGHT5, GL_AMBIENT, (0.2, 0.2, 0.2, 1.0))
glLightfv(GL_LIGHT5, GL_DIFFUSE, (0.5, 0.5, 0.5, 1.0))
glEnable(GL_LIGHT5)
glEnable(GL_LIGHTING)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)
glMatrixMode(GL_MODELVIEW)
glTranslatef(0, 0, -5)
x = 0
y = 0
z = - 5
xx = 0
yy = 0
zz = -5
# LOAD OBJECT
obj = OBJ(sys.argv[1], swapyz=True)
sphere = gluNewQuadric() #Create new sphere
model = glGetDoublev(GL_MODELVIEW_MATRIX)
rx, ry = (0,0)
tx, ty = (0,0)
zpos = 5
rotate = move = False
clock = pygame.time.Clock()
while 1:
clock.tick(30)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) #Clear the screen
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE or event.key == pygame.K_RETURN:
sys.exit()
keypress = pygame.key.get_pressed()
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
if keypress[pygame.K_w]:
glTranslatef(0,-0.01,0)
y = y - 0.01
if keypress[pygame.K_s]:
glTranslatef(0,0.01,0)
y = y + 0.01
if keypress[pygame.K_d]:
glTranslatef(0.01,0,0)
x = x + 0.01
if keypress[pygame.K_a]:
glTranslatef(-0.01,0,0)
x = x - 0.01
if keypress[pygame.K_z]:
glTranslatef(0, 0, -0.01)
z = z - 0.01
if keypress[pygame.K_x]:
glTranslatef(0,0,0.01)
z = z + 0.01
if keypress[pygame.K_0]:
start = x, y, z
print('start is', start)
#model = glGetDoublev(GL_MODELVIEW_MATRIX)
glMultMatrixf(model)
model = glGetDoublev(GL_MODELVIEW_MATRIX)
glMultMatrixf(model)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glScalef(0.1, 0.1, 0.1)
glColor4f(0.5, 0.2, 0.2, 1)
gluSphere(sphere, 1.0, 32, 16)
glPopMatrix()
glPushMatrix()
glLoadIdentity()
if keypress[pygame.K_u]:
glTranslatef(0,-0.01,0)
yy = yy - 0.01
if keypress[pygame.K_j]:
glTranslatef(0,0.01,0)
yy = yy + 0.01
if keypress[pygame.K_k]:
glTranslatef(0.01,0,0)
xx = xx + 0.01
if keypress[pygame.K_h]:
glTranslatef(-0.01,0,0)
xx = xx - 0.01
if keypress[pygame.K_n]:
glTranslatef(0, 0, -0.01)
zz = zz - 0.01
if keypress[pygame.K_m]:
glTranslatef(0,0,0.01)
zz = zz + 0.01
if keypress[pygame.K_l]:
glRotatef(1, 0, 1, 5)
xx = xx*math.cos(rad)-y*math.sin(rad)
yy = xx*math.sin(rad)+yy*math.cos(rad)
if keypress[pygame.K_g]:
#glTranslatef(xx, yy, zz)
glRotatef(1, 0, 1, -5)
#glTranslatef(-xx, -yy, -zz)
rad = 1*180/math.pi
xx = xx*math.cos(rad)-yy*math.sin(rad)
yy = xx*math.sin(rad)+yy*math.cos(rad)
print(xx, yy, math.sin( rad))
if keypress [pygame.K_9]:
print('obj coord', xx, yy, zz)
glMultMatrixf(model)
model = glGetDoublev(GL_MODELVIEW_MATRIX)
glMultMatrixf(model)
#glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) --> se lo metti non vedi l'oggetto prima
glScalef(0.1, 0.1, 0.1)
glCallList(obj.gl_list)
glPopMatrix()
pygame.display.flip() #Update the screen
main()
The problem is that they both move even if I use Push and Pop matrix commands
The model matrix defines the position orientation and scale of a single object (mesh) in the scene. Thus you need a separate model matrix for each object:
def main():
# [...]
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0, display[0]/display[1], 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -5)
model_1 = glGetDoublev(GL_MODELVIEW_MATRIX)
model_2 = model_1
# [...]
while 1:
# [...]
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
# Model 1
glPushMatrix()
glLoadIdentity()
# rotate and translate model 1
# [...]
glMultMatrixf(model_1)
model_1 = glGetDoublev(GL_MODELVIEW_MATRIX)
glScalef(0.1, 0.1, 0.1)
# draw model 1
glColor4f(0.5, 0.2, 0.2, 1)
gluSphere(sphere, 1.0, 32, 16)
glPopMatrix()
# Model 2
glPushMatrix()
glLoadIdentity()
# rotate and translate model 2
# [...]
glMultMatrixf(model_2)
model_2 = glGetDoublev(GL_MODELVIEW_MATRIX)
glScalef(0.1, 0.1, 0.1)
# draw model 1
glCallList(obj.gl_list)
glPopMatrix()
pygame.display.flip() #Update the screen
i have two questions.
I's normal in most cases when i use mouse wheel to zoom in.Once I zoom in to a certain level,it will zoom in farther.(near the center)
When the object(line or triangle) through the screan,some part of the object will be covered.
as shown below
Here's my code.I don't know how should i change it.
Mouse scroll function: scroll_callback
the camera cover data: how to set the camera boundary
from math import pi
from threading import Thread
import glfw
import glm
import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import compileShader, compileProgram
gl_shape = {'lines': GL_LINES, 'line_strip': GL_LINE_STRIP, 'triangles': GL_TRIANGLES,
'triangle_strip': GL_TRIANGLE_STRIP}
gl_draw = {'seldom': GL_STATIC_DRAW, 'often': GL_DYNAMIC_DRAW, 'always': GL_STREAM_DRAW}
class MyOpenGL(Thread):
instances = {}
first_instance = {}
def __new__(cls, *args, **kwargs):
instance = kwargs.get('instance')
if instance not in cls.instances:
cls.instances[instance] = super().__new__(cls)
cls.first_instance[instance] = True # 判断是否是第一次创建
return cls.instances[instance]
def __init__(self, title: str = 'opengl_title', instance: str = None, draw_axis=True, axis_len=1.0):
super().__init__()
if not self.__class__.first_instance.get(instance): # 判断是否是第一次创建
return
self.__class__.first_instance[instance] = False
self.vertex_src = """
# version 330
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_color;
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;
out vec3 v_color;
void main()
{
gl_Position = projection * view * model * vec4(a_position, 1.0);
v_color = a_color;
}
""" # vec4(a_position, 1); 1指物体大小,越大越小
self.fragment_src = """
# version 330
in vec3 v_color;
out vec4 out_color;
void main()
{
out_color = vec4(v_color, 1.0);
}
"""
self.width = 1280
self.height = 720
self.eye = glm.vec3(0, 0, 3) # 眼睛的位置(默认z轴的正方向)
self.target = glm.vec3(0, 0, 0) # 瞄准方向的参考点(默认在坐标原点)
self.up = glm.vec3(0, 1, 0) # 定义对观察者而言的上方(默认y轴的正方向)
self.near = 0.1
self.far = 100
self.radians = glm.radians(45)
self.projection = glm.perspective(self.radians, self.width / self.height, self.near, self.far)
self.view = glm.lookAt(self.eye, self.target, self.up)
self.pan_start = None
self.orbit_start = None
self.pivot_world = None
self.draw_axis = draw_axis
self.axis_len = axis_len
self.title = title
self.window = None
self.program = None
self.vao = {}
self.to_bind_data = {}
self.vao_draw_args = {}
self.vao_index = 1
self.data_change = False
def glfw_init(self, title='opengl_title'):
if not glfw.init():
print("Cannot initialize GLFW")
exit()
self.window = glfw.create_window(self.width, self.height, title, None, None)
if not self.window:
glfw.terminate()
glfw.make_context_current(self.window)
def draw_axis_func(self, length=1.0):
axis = np.array([
length, 0.0, 0.0, 1.0, 0.0, 0.0,
-length, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, length, 0.0, 0.0, 1.0, 0.0,
0.0, -length, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, length, 0.0, 0.0, 1.0,
0.0, 0.0, -length, 0.0, 0.0, 1.0,
], np.float32)
self.vao['default_axis'] = glGenVertexArrays(1)
self.vao_draw_args['default_axis'] = glDrawArrays, (GL_LINES, 0, int(len(axis) / 6))
glBindVertexArray(self.vao['default_axis'])
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, axis.nbytes, axis, GL_STATIC_DRAW)
glEnableVertexAttribArray(0) # 准备设置glsl。即vertex_src中的layout(location = 0)的属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, axis.itemsize * 6, ctypes.c_void_p(0))
glEnableVertexAttribArray(1) # 准备设置glsl。即vertex_src中的layout(location = 1)的属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, axis.itemsize * 6, ctypes.c_void_p(12))
def depth(self, x, y):
depth_buffer = glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
depth_ = float(depth_buffer[0][0])
if depth_ == 1:
pt_drag = glm.vec3(0, 0, 0)
clip_pos = self.projection * self.view * glm.vec4(pt_drag, 1)
ndc_pos = glm.vec3(clip_pos) / clip_pos.w
if -1 < ndc_pos.z < 1:
depth_ = ndc_pos.z * 0.5 + 0.5
return depth_
# def update_fps_counter(self):
# """ 显示帧率 update_fps_counter(window)"""
# self.previous_seconds = self.__dict__.get('previous_seconds') or 0
# self.fps = self.__dict__.get('previous_seconds') or 0
# self.frame_count = self.__dict__.get('previous_seconds') or 0
# current_seconds = glfw.get_time()
# elapsed_seconds = current_seconds - self.previous_seconds
# if elapsed_seconds > 0.25:
# self.previous_seconds = current_seconds
# self.fps = self.frame_count / elapsed_seconds
# print(self.fps)
# self.frame_count = 0
# self.frame_count += 1
def window_size_callback(self, window, width, height):
glViewport(0, 0, width, height)
# 旋转速度随窗口变化而变化
self.width, self.height = width, height
self.projection = glm.perspective(self.radians, width / height, self.near, self.far)
def mouse_button_callback(self, window, button, action, mods):
x_pos, y_pos = glfw.get_cursor_pos(window)
y_pos = self.height - y_pos
if button == glfw.MOUSE_BUTTON_RIGHT and action == glfw.PRESS:
self.pan_start = glm.vec3(x_pos, y_pos, self.depth(x_pos, y_pos))
if button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS:
self.orbit_start = glm.vec3(x_pos, y_pos, self.depth(x_pos, y_pos))
self.pivot_world = glm.vec3(0, 0, 0)
def cursor_pos_callback(self, window, x_pos, y_pos):
y_pos = self.height - y_pos
# get view matrix and viewport rectangle
view, inv_view = self.view, glm.inverse(self.view)
view_rect = glm.vec4(0, 0, self.width, self.height)
if glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_RIGHT) == glfw.PRESS:
# get drag start and end
wnd_from = self.pan_start
wnd_to = glm.vec3(x_pos, y_pos, self.pan_start[2])
self.pan_start = wnd_to
# get projection and window matrix
inv_proj = glm.inverse(self.projection)
inv_wnd = glm.translate(glm.mat4(1), glm.vec3(-1, -1, -1))
inv_wnd = glm.scale(inv_wnd, glm.vec3(2 / view_rect[2], 2 / view_rect[3], 2))
inv_wnd = glm.translate(inv_wnd, glm.vec3(view_rect[0], view_rect[1], 0))
# calculate drag start and world coordinates
pt_h_world = [inv_view * inv_proj * inv_wnd * glm.vec4(*pt, 1) for pt in [wnd_from, wnd_to]]
pt_world = [glm.vec3(pt_h) / pt_h.w for pt_h in pt_h_world]
# calculate drag world translation
world_vec = pt_world[1] - pt_world[0]
# translate view position and update view matrix
inv_view = glm.translate(glm.mat4(1), world_vec * -1) * inv_view
view = glm.inverse(inv_view)
elif glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_LEFT) == glfw.PRESS:
# get the drag start and end
wnd_from = self.orbit_start
wnd_to = glm.vec3(x_pos, y_pos, self.orbit_start[2])
self.orbit_start = wnd_to
# calculate the pivot, rotation axis and angle
pivot_view = glm.vec3(view * glm.vec4(*self.pivot_world, 1))
orbit_dir = wnd_to - wnd_from
# get the projection of the up vector to the view port
# TODO
# calculate the rotation components for the rotation around the view space x axis and the world up vector
orbit_dir_x = glm.vec2(0, 1)
orbit_vec_x = glm.vec2(0, orbit_dir.y)
orbit_dir_up = glm.vec2(1, 0)
orbit_vec_up = glm.vec2(orbit_dir.x, 0)
# calculate the rotation matrix around the view space x axis through the pivot
rot_pivot_x = glm.mat4(1)
if glm.length(orbit_vec_x) > 0.5:
axis_x = glm.vec3(-1, 0, 0)
angle_x = glm.dot(orbit_dir_x, glm.vec2(orbit_vec_x.x / (view_rect[2] - view_rect[0]),
orbit_vec_x.y / (view_rect[3] - view_rect[1]))) * pi
rot_mat_x = glm.rotate(glm.mat4(1), angle_x, axis_x)
rot_pivot_x = glm.translate(glm.mat4(1), pivot_view) * rot_mat_x * glm.translate(glm.mat4(1),
-pivot_view)
# calculate the rotation matrix around the world space up vector through the pivot
rot_pivot_up = glm.mat4(1)
if glm.length(orbit_vec_up) > 0.5:
axis_up = glm.vec3(0, 0, 1)
angle_up = glm.dot(orbit_dir_up, glm.vec2(
orbit_vec_up.x / (view_rect[2] - view_rect[0]),
orbit_vec_up.y / (view_rect[3] - view_rect[1]))) * pi
rot_mat_up = glm.rotate(glm.mat4(1), angle_up, axis_up)
rot_pivot_up = glm.translate(
glm.mat4(1), self.pivot_world) * rot_mat_up * glm.translate(glm.mat4(1), -self.pivot_world)
# transform and update view matrix
view = rot_pivot_x * view * rot_pivot_up
self.view = view
def scroll_callback(self, window, x_offset, y_offset):
x, y = glfw.get_cursor_pos(window)
y = self.height - y
view_rect = glm.vec4(0, 0, self.width, self.height)
# get view, projection and window matrix
proj, inv_proj = self.projection, glm.inverse(self.projection)
view, inv_view = self.view, glm.inverse(self.view)
inv_wnd = glm.translate(glm.mat4(1), glm.vec3(-1, -1, -1))
inv_wnd = glm.scale(inv_wnd, glm.vec3(2 / view_rect[2], 2 / view_rect[3], 2))
inv_wnd = glm.translate(inv_wnd, glm.vec3(view_rect[0], view_rect[1], 0))
wnd = glm.inverse(inv_wnd)
# get world space position on view ray
pt_wnd = glm.vec3(x, y, 1.0)
# pt_world = glm.unProject(pt_wnd, view, proj, vp_rect)
pt_h_world = inv_view * inv_proj * inv_wnd * glm.vec4(*pt_wnd, 1)
pt_world = glm.vec3(pt_h_world) / pt_h_world.w
# get view position
eye = glm.vec3(inv_view[3])
# get "zoom" direction and amount
ray_cursor = glm.normalize(pt_world - eye)
# translate view position and update view matrix
inv_view = glm.translate(glm.mat4(1), ray_cursor * y_offset) * inv_view
# return new view matrix
self.view = glm.inverse(inv_view)
def set_data(self, data, data_name=None, frequency=None, shape=None, indices=None):
if data_name is None:
data_name = self.vao_index
self.vao_index += 1
# print(data_name)
self.to_bind_data[data_name] = (data, frequency, shape, indices)
return data_name
def bind_data(self):
if not self.to_bind_data:
return
for data_name in list(self.to_bind_data.keys()):
self.vao[data_name] = glGenVertexArrays(1)
data, frequency, shape, indices = self.to_bind_data[data_name]
glBindVertexArray(self.vao[data_name])
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, sum(len(i) for i in data) * 4, None, gl_draw.get(frequency, GL_STATIC_DRAW))
offset = 0
for d in data:
glBufferSubData(GL_ARRAY_BUFFER, offset, d.nbytes, d)
offset += d.nbytes
if indices is not None:
ebo = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
length = sum(len(i) for i in indices) * 4
glBufferData(GL_ELEMENT_ARRAY_BUFFER, length, None, gl_draw.get(frequency, GL_STATIC_DRAW))
offset = 0
for i, sub_arr in enumerate(indices):
arr = (ctypes.c_uint32 * sub_arr.nbytes)(*(sub_arr + 4 * i))
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, sub_arr.nbytes, arr)
offset += sub_arr.nbytes
prop = glDrawElements, (gl_shape.get(shape, GL_LINE_STRIP), length, GL_UNSIGNED_INT, None)
else:
# 最后一个参数为画几个点
prop = glDrawArrays, (gl_shape.get(shape, GL_LINE_STRIP), 0, sum(int(len(d) / 6) for d in data))
self.vao_draw_args[data_name] = prop
glEnableVertexAttribArray(0) # 准备设置glsl。即vertex_src中的layout(location = 0)的属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * 6, ctypes.c_void_p(0))
glEnableVertexAttribArray(1) # 准备设置glsl。即vertex_src中的layout(location = 1)的属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 4 * 6, ctypes.c_void_p(12)) # 3*arr.itemsize
self.to_bind_data.pop(data_name)
def draw_data(self):
for n in self.vao:
glBindVertexArray(self.vao[n])
self.vao_draw_args[n][0](*self.vao_draw_args[n][1])
# print(self.vao_draw_args[n][0],self.vao_draw_args[n][1])
def run(self):
self.glfw_init(self.title)
if self.draw_axis:
self.draw_axis_func(self.axis_len)
glfw.set_window_size_callback(self.window, self.window_size_callback)
glfw.set_mouse_button_callback(self.window, self.mouse_button_callback)
glfw.set_cursor_pos_callback(self.window, self.cursor_pos_callback)
glfw.set_scroll_callback(self.window, self.scroll_callback)
self.program = compileProgram(compileShader(self.vertex_src, GL_VERTEX_SHADER),
compileShader(self.fragment_src, GL_FRAGMENT_SHADER))
glUseProgram(self.program)
# 设置模型坐标,和glUniformMatrix4fv不能写成一行,写成一行会无法展示,可能是因为用的指针地址
mat = glm.mat4()
glUniformMatrix4fv(glGetUniformLocation(self.program, 'model'), 1, GL_FALSE, glm.value_ptr(mat))
while not glfw.window_should_close(self.window):
glUniformMatrix4fv(glGetUniformLocation(self.program, 'projection'), 1, GL_FALSE,
glm.value_ptr(self.projection))
glUniformMatrix4fv(glGetUniformLocation(self.program, 'view'), 1, GL_FALSE, glm.value_ptr(self.view))
glClearColor(0.2, 0.3, 0.3, 1.0) # 在glClear后会显示为glClearColor中的颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
self.bind_data()
self.draw_data()
glfw.poll_events()
glfw.swap_buffers(self.window)
glfw.terminate()
print('glfw close')
result = [
np.array([0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 0.5, 0.4, 0.0, 1.0, 0.0, 0.0,
0.4, 0.4, 0.0, 1.0, 0.0, 0.0, 0.4, 0.5, 0.0, 1.0, 0.0, 0.0], dtype=np.float32),
np.array([-0.5, -0.5, 0.0, 0.0, 1.0, 0.0, -0.5, -0.4, 0.0, 0.0, 1.0, 0.0,
-0.4, -0.4, 0.0, 0.0, 1.0, 0.0, -0.4, -0.5, 0.0, 0.0, 1.0, 0.0], dtype=np.float32),
np.array([0.2, 0.2, 0.0, 0.0, 0.0, 1.0, 0.2, 0.1, 0.0, 0.0, 0.0, 1.0,
0.1, 0.1, 0.0, 0.0, 0.0, 1.0, 0.1, 0.2, 0.0, 0.0, 0.0, 1.0], dtype=np.float32)
]
ins = [np.array([2, 3, 1, 3, 0, 1], dtype=np.uint32), np.array([2, 3, 1, 3, 0, 1], dtype=np.uint32),
np.array([2, 3, 1, 3, 0, 1], dtype=np.uint32)]
line = [
np.array([0.1, 0.1, 0.1, 0.0, 1.0, 0.0, 0.2, 0.2, 0.2, 0.0, 1.0, 0.0,
0.3, 0.3, 0.3, 0.0, 1.0, 0.0, 0.4, 0.4, 0.4, 0.0, 1.0, 0.0,
0.5, 0.2, 0.3, 0.0, 1.0, 0.0, 0.5, 0.3, 0.4, 0.0, 1.0, 0.0,
0.6, 0.4, 0.5, 0.0, 1.0, 0.0, 0.6, 0.6, 0.6, 0.0, 1.0, 0.0], dtype=np.float32)
]
ogl = MyOpenGL()
ogl.start()
ogl.set_data(result, data_name=f'j', frequency='always', shape='triangles', indices=ins)
ogl.set_data(line, data_name=f'l', frequency='always')