基于pyopengl实现可操作魔方

效果:

右边按钮从上倒下功能依次为:锁定视角,随机打乱,自动还原

基于python的opengl库实现

  1. 魔方渲染

渲染魔方本质是渲染27立方体,在opengl中渲染一个立方体只需给出一个立方体8个顶点在模型空间中的坐标即可

但在魔方渲染中,每一个面需要单独着色,若每个模型只给出8给顶点的位置数据和颜色数据在片元着色器中会根据顶点颜色插值像素颜色,无法实现每个面单独着色,因此对每个立方体需要给出6*6 = 36个顶点的位置数据和颜色数据。

对于27个魔方的渲染,选择以循环的方式,对x,y,z轴分别施加1.1的偏移,得到27个魔方的位置坐标

对于27个方块的颜色数据,为了实现只让每个方块显露在外面的面有颜色,其余以灰色显示,在魔方坐标循环中加入颜色遮罩,根据魔方的行列面数对相应的面施加遮罩,例如右边一排的左边会被遮住,中间一排左右都会被遮住,左边一排右边会被遮住。

根据方块位置得到其对应的x,y,z轴的遮罩,合并,并给对应的顶点附上灰色。

魔方显现:

在三维空间中魔方应呈现一个近大远小的样子,这需要在渲染循环中对魔方顶点进行MVP变换,即MODEL-VIEW-PROJECTION变换,即可将魔方坐标转换到透视投影空间下

魔方旋转:

魔方的旋转本质上是坐标的旋转,由于前面直接将27个立方体作为一个物体进行渲染,因此可以直接对魔方中特定的顶点进行旋转操作,再对变换后的所有顶点做mvp变换即可,

具体而言,通过对顶点的坐标进行筛选,筛选出某一面的每一个立方体的所有顶点。

为了保证转动的流畅性,需要在渲染循环中对模型空间坐标进行修改,每次乘以一个微笑度数的旋转矩阵。当转动总度数达到90度时停止。

鼠标操作部分:

鼠标拖动视角:

鼠标拖动视角本质是根据鼠标在屏幕空间上的位置变化对魔方的顶点进行旋转变换,通过计算每一帧中鼠标分别在x,y轴上的位置变化量,将其分别作为模型在x和y轴中旋转的度数,并生成对应的旋转矩阵施加到模型坐标上。

双击进行逆时针旋转:

双击判定,在鼠标回调函数中加入条件判断,如果右键响应,则记录当前时间,并与上一时刻时间做差,若小于0.3s,则判定为双击,同时将当前时间赋值给上一时刻时间,

当双击判定成功时,首先会获得鼠标当前位置屏幕空间的颜色,根据颜色确定鼠标点击的面,接着发送相应的指令,对魔方中特定坐标的顶点进行旋转。

右键顺时针旋转:

实现原理与逆时针旋转基本相同,仅将响应键改为右键

按钮实现

按钮渲染

渲染按钮本质是渲染一个矩形,因此直接给出四个点坐标即可,接着在渲染循环中对其坐标进行变换,通常希望在界面中的按钮是一个方正的矩形,因此可不考虑透视中的近大远小,直接用正交投影矩阵即可,需要注意的是,按钮是在界面的最前面的,为了实现这一点,在渲染按钮时需要关闭深度测试。

功能响应:按钮功能响应通过检测鼠标在标准化设备空间(NDC)中的坐标是否在按钮坐标范围内,若在其范围内且右键相应,则启动相应功能。
  1. 视角锁定,视角锁定重新设置回调函数,在新的回调函数中删去旋转部分代码即可。
  2. 随机打乱,随机打乱首先生成一个随机长度的操作指令序列,并在渲染循环中施加,为了保证指令序列正常执行,需要在每次旋转结束后才执行下一步操作,通过检测每一轮旋转的总旋转度数是否大于90度实现。

同时,为了保证打乱期间不因为误操作而产生错误,在打乱期间将鼠标回调函数设置为空。

自动还原

自动还原采用两阶段算法,每一个阶段用IDA算法进行搜索,输入当前状态的魔方序列,得到一个还原的操作序列,

魔方状态序列计算:

在渲染开始前将魔方初始化,接着没执行一次操作指令,状态序列就根据操作指令进行对应的变化,保证魔方状态与状态序列的同步

当自动还原按钮响应时,将当前魔方状态序列输入两阶段算法,得到对应的还原操作序列,并将还原操作序列传入渲染循环中,以执行还原操作序列中的操作。

最后附上完整代码

import pandas as pd
import numpy as np
import glfw
from OpenGL.GL import *
import numpy as np
import OpenGL.GL.shaders
import glm
import time
import kociemba


vertices = [
        -0.5, -0.5, -0.5,  
         0.5, -0.5, -0.5, 
         0.5,  0.5, -0.5, 
         0.5,  0.5, -0.5,  
        -0.5,  0.5, -0.5,  
        -0.5, -0.5, -0.5, 

        -0.5, -0.5,  0.5, 
         0.5, -0.5,  0.5,
         0.5,  0.5,  0.5, 
         0.5,  0.5,  0.5, 
        -0.5,  0.5,  0.5,
        -0.5, -0.5,  0.5, 

        -0.5,  0.5,  0.5, 
        -0.5,  0.5, -0.5,  
        -0.5, -0.5, -0.5, 
        -0.5, -0.5, -0.5, 
        -0.5, -0.5,  0.5, 
        -0.5,  0.5,  0.5, 

         0.5,  0.5,  0.5, 
         0.5,  0.5, -0.5, 
         0.5, -0.5, -0.5,  
         0.5, -0.5, -0.5, 
         0.5, -0.5,  0.5, 
         0.5,  0.5,  0.5, 

        -0.5, -0.5, -0.5, 
         0.5, -0.5, -0.5, 
         0.5, -0.5,  0.5, 
         0.5, -0.5,  0.5, 
        -0.5, -0.5,  0.5,  
        -0.5, -0.5, -0.5, 

        -0.5,  0.5, -0.5, 
         0.5,  0.5, -0.5, 
         0.5,  0.5,  0.5, 
         0.5,  0.5,  0.5, 
        -0.5,  0.5,  0.5, 
        -0.5,  0.5, -0.5, 

    
]
color = [
        #白色  后面
        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,0.0,0.0,
        1.0,0.0,0.0,
        1.0,0.0,0.0,
        1.0,0.0,0.0,
        1.0,0.0,0.0,
        1.0,0.0,0.0,

        #绿色 左面
        0.0,1.0,0.0,
        0.0,1.0,0.0,
        0.0,1.0,0.0,
        0.0,1.0,0.0,
        0.0,1.0,0.0,
        0.0,1.0,0.0,

        #蓝色 右面
        0.0,0.0,1.0,
        0.0,0.0,1.0,
        0.0,0.0,1.0,
        0.0,0.0,1.0,
        0.0,0.0,1.0,
        0.0,0.0,1.0,

        #下面  灰色
        1.0,1.0,0.0,
        1.0,1.0,0.0,
        1.0,1.0,0.0,
        1.0,1.0,0.0,
        1.0,1.0,0.0,
        1.0,1.0,0.0,
        #浅蓝  上面
        0.0,1.0,1.0,
        0.0,1.0,1.0,
        0.0,1.0,1.0,
        0.0,1.0,1.0,
        0.0,1.0,1.0,
        0.0,1.0,1.0,
]

all_cubes_vertices = []
all_cubes_color = []
# 立方体之间的间隔
spacing = 1.1
offset_list = [-1,0,1]


z_occlusion = [[1],[0,1],[0]]
x_occlusion = [[3],[2,3],[2]]
y_occlusion = [[5],[4,5],[4]]


x_index = 0
y_index = 0
z_index = 0

# 生成9个立方体的顶点坐标
for z in offset_list:  # 3层
    y_index = 0
    for y in offset_list:  # 3行
        x_index = 0
        for x in offset_list:  # 3列

            # 计算当前立方体的偏移量
            offset = [x * spacing, y * spacing, z * spacing]

            # 为每个立方体的顶点添加偏移量
            for i in range(0, len(vertices), 3):
                new_vertex = [vertices[i] + offset[0],
                              vertices[i+1] + offset[1],
                              vertices[i+2] + offset[2]]
                all_cubes_vertices.extend(new_vertex)

            final_occlusion =  x_occlusion[x_index]+y_occlusion[y_index]+z_occlusion[z_index] 
            temp_color = color.copy()
            for a in final_occlusion:
                for b in range(6):
                    for c in range(3):
                        temp_color[a*18 + b *3 +c ] = 0.2

            all_cubes_color.extend(temp_color) 

            x_index += 1
        y_index += 1
    z_index += 1
# 转换为数组
all_cubes_vertices = np.array(all_cubes_vertices, dtype=np.float32)



def mouse_move(window, xpos, ypos):
    global last_x, last_y, rot_x, rot_y, is_dragging,get_color
    if is_dragging:
        dx, dy = xpos - last_x, ypos - last_y
        rot_x += dy * 0.1
        rot_y += dx * 0.1
    last_x, last_y = xpos, ypos
    
    ypos = 600 - ypos
    # 读取鼠标位置的像素
    glReadBuffer(GL_FRONT)
    pixel_data = glReadPixels(xpos, ypos, 1, 1, GL_RGB, GL_UNSIGNED_BYTE)
    get_color = np.frombuffer(pixel_data, dtype=np.uint8)
    


def mouse_move_static(window, xpos, ypos):
    global get_color
    ypos = 600 - ypos

    # 读取鼠标位置的像素
    glReadBuffer(GL_FRONT)
    pixel_data = glReadPixels(xpos, ypos, 1, 1, GL_RGB, GL_UNSIGNED_BYTE)
    get_color = np.frombuffer(pixel_data, dtype=np.uint8)



def mouse_button_callback(window, button, action, mods):
    global is_dragging ,state, last_click_time,get_color,motion,mess_series,solve_series,cube
    x, y = glfw.get_cursor_pos(window)
    # 转换为标准化设备坐标
    nx = (x / 800) * 2 - 1
    ny = (y / 600) * -2 + 1
    # 检查是否点击了按钮
    if 0.6 <= nx <= 0.8 and 0.6 <= ny <= 0.7:
        if button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS:
            if(state%2 == 0):
                glfw.set_cursor_pos_callback(window, mouse_move_static)
                state += 1
            else:
                state += 1
                glfw.set_cursor_pos_callback(window, mouse_move)
    elif 0.6 <= nx <= 0.8 and 0.4 <= ny <= 0.5:
        if button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS:
            #随机序列生成
            for i in range(np.random.randint(20,30)):
                mess_series.append(np.random.randint(0,12))
    elif 0.6 <= nx <= 0.8 and 0.2 <= ny <= 0.3:
        if button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS:
            cube_ser = ""
            for i in cube:
                cube_ser += i
            solve_series = translate_kociemba_sequence(kociemba.solve(cube_ser)).split()


    else:
        if button == glfw.MOUSE_BUTTON_LEFT:
            if(state%2 == 0):
                is_dragging = action==glfw.PRESS
            else:
                pass
        if button == glfw.MOUSE_BUTTON_LEFT and action == glfw.PRESS:
            current_time = time.time()
            
            # 检查两次点击的时间间隔是否小于设定的阈值,例如0.3秒
            if current_time - last_click_time < 0.3:
                if(list(get_color) == [255,255,255]):
                    motion = 1
                elif(list(get_color) == [255,0,0]):
                    motion = 3
                elif(list(get_color) == [0,255,0]):
                    motion = 4
                elif(list(get_color) == [0,0,255]):
                    motion = 6
                elif(list(get_color) == [255,255,0]):
                    motion = 10
                elif(list(get_color) == [0,255,255]):
                    motion = 8

            last_click_time = current_time

        if button == glfw.MOUSE_BUTTON_RIGHT and action == glfw.PRESS:
            
            if(list(get_color) == [255,255,255]):
                motion = 0
            elif(list(get_color) == [255,0,0]):
                motion = 2
            elif(list(get_color) == [0,255,0]):
                motion = 5
            elif(list(get_color) == [0,0,255]):
                motion = 7
            elif(list(get_color) == [255,255,0]):
                motion = 11
            elif(list(get_color) == [0,255,255]):
                motion = 9
                    
            
def mouse_button_callback_stop(window, button, action, mods):
    pass    


def rotate_face(cube, face, clockwise=True):
    # 旋转一个面的边缘块
    face_indices = [
        face * 9, face * 9 + 1, face * 9 + 2,
        face * 9 + 3, face * 9 + 5,
        face * 9 + 6, face * 9 + 7, face * 9 + 8
    ]

    if clockwise:
        cube[face_indices] = cube[np.array(face_indices)[[5,3,0,6,1,7,4,2]]]
    else:
        cube[face_indices] = cube[np.array(face_indices)[[2,4,7,1,6,0,3,5]]]

        

def rotate_cube(cube, move):
    if move == "R":
        rotate_face(cube, 1)
        temp = cube[[2,5,8]]
        cube[[2,5,8]] = cube[[20,23,26]]
        cube[[20,23,26]] = cube[[29,32,35]]
        cube[[29,32,35]] = cube[[51,48,45]]
        cube[[51,48,45]] = temp

    elif move == "R'":
        rotate_face(cube, 1, False)
        temp = cube[[51,48,45]]
        cube[[51,48,45]] = cube[[29,32,35]]
        cube[[29,32,35]] = cube[[20,23,26]]
        cube[[20,23,26]] = cube[[2,5,8]]
        cube[[2,5,8]] = temp     
    elif move == "L":
        rotate_face(cube, 4)
        #print(cube)
        temp = cube[[47,50,53]]
        cube[[47,50,53]] = cube[[33,30,27]]
        cube[[27,30,33]] = cube[[18,21,24]]
        cube[[18,21,24]] = cube[[0,3,6]]
        cube[[6,3,0]] = temp
    elif move == "L'":
        rotate_face(cube, 4, False)
        temp = cube[[0,3,6]]
        cube[[0,3,6]] = cube[[18,21,24]]
        cube[[18,21,24]] = cube[[27,30,33]]
        cube[[27,30,33]] = cube[[53,50,47]]
        cube[[53,50,47]] = temp  
    elif move == "U":
        rotate_face(cube, 0)
        #print(cube)
        temp = cube[[9,10,11]]
        cube[[9,10,11]] = cube[[45,46,47]]
        cube[[45,46,47]] = cube[[36,37,38]]
        cube[[36,37,38]] = cube[[18,19,20]]
        cube[[18,19,20]] = temp
    elif move == "U'":
        rotate_face(cube, 0, False)
        temp = cube[[45,46,47]]
        cube[[45,46,47]] = cube[[9,10,11]]
        cube[[9,10,11]] = cube[[18,19,20]]
        cube[[18,19,20]] = cube[[36,37,38]]
        cube[[36,37,38]] = temp
    elif move == "D":
        rotate_face(cube, 3)
        #print(cube)
        temp = cube[[15,16,17]]
        cube[[15,16,17]] = cube[[24,25,26]]
        cube[[24,25,26]] = cube[[42,43,44]]
        cube[[42,43,44]] = cube[[51,52,53]]
        cube[[51,52,53]] = temp     
    elif move == "D'":
        rotate_face(cube, 3, False)
        temp = cube[[51,52,53]]
        cube[[51,52,53]] = cube[[42,43,44]]
        cube[[42,43,44]] = cube[[24,25,26]]
        cube[[24,25,26]] = cube[[15,16,17]]
        cube[[15,16,17]] = temp
    elif move == "F":
        rotate_face(cube, 2)
        temp = cube[[6,7,8]]
        cube[[6,7,8]] = cube[[44,41,38]]
        cube[[44,41,38]] = cube[[29,28,27]]
        cube[[27,28,29]] = cube[[15,12,9]]
        cube[[9,12,15]] = temp    
    elif move == "F'":
        rotate_face(cube, 2, False)
        temp = cube[[9,12,15]]
        cube[[9,12,15]] = cube[[29,28,27]]
        cube[[27,28,29]] = cube[[38,41,44]]
        cube[[44,41,38]] = cube[[6,7,8]]
        cube[[6,7,8]] = temp           
    elif move == "B":
        rotate_face(cube, 5)
        temp = cube[[0,1,2]]
        cube[[0,1,2]] = cube[[11,14,17]]
        cube[[11,14,17]] = cube[[35,34,33]]
        cube[[33,34,35]] = cube[[36,39,42]]
        cube[[42,39,36]] = temp  
    elif move == "B'":
        rotate_face(cube, 5, False)
        temp = cube[[0,1,2]]
        cube[[0,1,2]] = cube[[42,39,36]]
        cube[[36,39,42]] = cube[[33,34,35]]
        cube[[33,34,35]] = cube[[17,14,11]]
        cube[[11,14,17]] = temp
        

def apply_moves(cube, moves):
    for move in moves.split():
        rotate_cube(cube, move)


def translate_kociemba_sequence(sequence):
    moves = sequence.split()
    translated_moves = []

    for move in moves:
        if move.endswith('2'):
            # 如果操作以 '2' 结尾,将其转换为两个相同的操作
            translated_moves.append(move[0])
            translated_moves.append(move[0])
        else:
            # 否则,直接添加操作
            translated_moves.append(move)

    return ' '.join(translated_moves)



vertex_shader = """
# version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

uniform mat4 transform;
void main()
{   
    gl_Position = transform * vec4(aPos, 1.0);
    ourColor = aColor;
}
"""

fragment_shader = """
# version 330 core
out vec4 FragColor;
  
in vec3 ourColor;

void main()
{
    FragColor = vec4(ourColor, 1.0);
}
"""




# 初始化GLFW
if not glfw.init():
    raise Exception("GLFW无法初始化")

# 创建窗口
window = glfw.create_window(800, 600, "magic cube", None, None)
if not window:
    glfw.terminate()
    raise Exception("GLFW窗口无法创建")

glfw.set_window_pos(window, 400, 200)
glfw.make_context_current(window)

# 定义顶点和片段着色器


# 编译着色器
shader = OpenGL.GL.shaders.compileProgram(
    OpenGL.GL.shaders.compileShader(vertex_shader, GL_VERTEX_SHADER),
    OpenGL.GL.shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER)
)


all_cubes_vertices1 = all_cubes_vertices*0.3


# 合并顶点和颜色数据
combined_vertices = []
for i in range(0, len(all_cubes_vertices1), 3):
    combined_vertices.extend(all_cubes_vertices1[i:i+3])
    combined_vertices.extend(all_cubes_color[i:i+3])

combined_vertices = np.array(combined_vertices, dtype=np.float32)

indices = list(range(len(all_cubes_vertices1)))


indices = np.array(indices, dtype=np.uint32)

button_vertices = np.array([
    0.6, 0.7, 0.0, 1.0, 0, 1.0, # 右上角
    0.6, 0.6, 0.0, 1.0, 0, 1.0, # 右下角
    0.8, 0.6, 0.0, 1.0, 0, 1.0,# 左下角
    0.8, 0.7, 0.0, 1.0, 0, 1.0 # 左上角
    
], dtype=np.float32)

button_vertices1 = np.array([
    0.6, 0.5, 0.0, 1.0, 0, 1.0, # 右上角
    0.6, 0.4, 0.0, 1.0, 0, 1.0, # 右下角
    0.8, 0.4, 0.0, 1.0, 0, 1.0,# 左下角
    0.8, 0.5, 0.0, 1.0, 0, 1.0 # 左上角
    
], dtype=np.float32)

button_vertices2 = np.array([
    0.6, 0.3, 0.0, 1.0, 0, 1.0, # 右上角
    0.6, 0.2, 0.0, 1.0, 0, 1.0, # 右下角
    0.8, 0.2, 0.0, 1.0, 0, 1.0,# 左下角
    0.8, 0.3, 0.0, 1.0, 0, 1.0 # 左上角
    
], dtype=np.float32)

button_indices = np.array(list(range(4)),dtype = np.uint32)

# 设置顶点数组对象(VAO)和顶点缓冲对象(VBO)和EBO(Element Buffer Object)
VAO = glGenVertexArrays(1)
VBO = glGenBuffers(1)
EBO = glGenBuffers(1)

glBindVertexArray(VAO)

glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, combined_vertices.nbytes, combined_vertices, GL_STATIC_DRAW)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW)

# 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
# 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)

glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)


#________________________________________________________________________________________________


VAO_button = glGenVertexArrays(1)
VBO_button = glGenBuffers(1)
EBO_button = glGenBuffers(1)

glBindVertexArray(VAO_button)

glBindBuffer(GL_ARRAY_BUFFER, VBO_button)
glBufferData(GL_ARRAY_BUFFER, button_vertices.nbytes, button_vertices, GL_STATIC_DRAW)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_button)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, button_indices.nbytes, button_indices, GL_STATIC_DRAW)

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)  # 顶点位置属性

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)

glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)


#___________________________________________________________________________________

VAO_button1 = glGenVertexArrays(1)
VBO_button1 = glGenBuffers(1)
EBO_button1 = glGenBuffers(1)

glBindVertexArray(VAO_button1)

glBindBuffer(GL_ARRAY_BUFFER, VBO_button1)
glBufferData(GL_ARRAY_BUFFER, button_vertices1.nbytes, button_vertices1, GL_STATIC_DRAW)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_button)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, button_indices.nbytes, button_indices, GL_STATIC_DRAW)

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)  # 顶点位置属性

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)

glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)


#_____________________________________________________________________________________


VAO_button2 = glGenVertexArrays(1)
VBO_button2 = glGenBuffers(1)
EBO_button2 = glGenBuffers(1)

glBindVertexArray(VAO_button2)

glBindBuffer(GL_ARRAY_BUFFER, VBO_button2)
glBufferData(GL_ARRAY_BUFFER, button_vertices2.nbytes, button_vertices2, GL_STATIC_DRAW)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_button)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, button_indices.nbytes, button_indices, GL_STATIC_DRAW)

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)  # 顶点位置属性

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)


glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)

glUseProgram(shader)


# 启用深度测试
glEnable(GL_DEPTH_TEST)

rot_x, rot_y = 0.0, 0.0
last_x, last_y = 0.0, 0.0
is_dragging = False

#扭动部分全局变量
motion = 100

state = 0 
last_click_time = 0
get_color = []

# 设置鼠标回调
glfw.set_cursor_pos_callback(window, mouse_move)
glfw.set_mouse_button_callback(window, mouse_button_callback)

button_x, button_y = 50, 50  # 按钮位置
button_width, button_height = 100, 50  # 按钮尺寸
button_pressed = False  # 按钮状态

#操作部分全局变量
mess_series = []
solve_series = []

#角度设置等
fov = glm.radians(45.0)
aspect = 800 / 600
near = 0.1
far = 100.0
projection = glm.perspective(fov, aspect, near, far)
angle_local = 0
total_angle = 0

mess_index = 0
solve_index = 0

new_all_cubics_vertices = all_cubes_vertices1.copy()



#魔方状态初始化
cube = np.array(["U"] * 9 + ["R"] * 9 + ["F"] * 9 + ["D"] * 9 + ["L"] * 9 + ["B"] * 9)

motion_code = ["B'","B","F","F'","L","L'","R'","R","U'","U","D","D'"]

# 渲染循环
while not glfw.window_should_close(window):
    glfw.poll_events()
    global rot_x, rot_y
    # 清除颜色和深度缓冲

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    if(mess_series != []):
        motion = mess_series[mess_index]
        glfw.set_mouse_button_callback(window, mouse_button_callback_stop)
    
    if(solve_series != []):
        motion = motion_code.index(solve_series[solve_index])
        glfw.set_mouse_button_callback(window, mouse_button_callback_stop)
    
    #旋转操作
    if(motion != 100):
        if(total_angle == 0):
            motion_coding = motion_code[motion]
            apply_moves(cube, motion_coding)



        glfw.set_mouse_button_callback(window, mouse_button_callback_stop)
        angle_local = 0.5
        total_angle += angle_local
        if(total_angle > 90):
            total_angle = 0
            motion = 100
            if(mess_index + 1 >= len(mess_series)):
                mess_series = []
                mess_index = 0
            else:
                mess_index += 1
            
            if(solve_index +1 >= len(solve_series)):
                solve_series = []
                solve_index = 0
            else:
                solve_index += 1

            glfw.set_mouse_button_callback(window, mouse_button_callback)


    print(cube)
    
    transformlocal = glm.mat4(1.0)
    #局部变换矩阵
    if((motion == 0)| (motion == 2)):
        transformlocal = glm.rotate(glm.mat4(1), glm.radians(-angle_local), glm.vec3(0, 0, 1))
    elif((motion == 3)| (motion == 1)):
        transformlocal = glm.rotate(glm.mat4(1), glm.radians(angle_local), glm.vec3(0, 0, 1))
    elif((motion == 9)| (motion == 11)):
        transformlocal = glm.rotate(glm.mat4(1), glm.radians(-angle_local), glm.vec3(0, 1, 0))
    elif((motion == 10)| (motion == 8)):
        transformlocal = glm.rotate(glm.mat4(1), glm.radians(angle_local), glm.vec3(0, 1, 0))
    elif((motion == 4)| (motion == 6)):
        transformlocal = glm.rotate(glm.mat4(1), glm.radians(angle_local), glm.vec3(1, 0, 0))
    elif((motion == 5)| (motion == 7)):
        transformlocal = glm.rotate(glm.mat4(1), glm.radians(-angle_local), glm.vec3(1, 0, 0))
    

    for vertex_pos in range(0,len(all_cubes_vertices1),3):
        temp_pos = np.array([new_all_cubics_vertices[vertex_pos],new_all_cubics_vertices[vertex_pos+1],new_all_cubics_vertices[vertex_pos+2],1])
        if((motion == 0)|(motion == 1)):
            if(((temp_pos[2] < -0.47)&(temp_pos[2] > -0.49) )|((temp_pos[2] < -0.17)&(temp_pos[2] > -0.19))):
                temp_pos = np.array(transformlocal).dot(temp_pos)
        elif((motion == 2)|(motion == 3)):
            if(((temp_pos[2] > 0.47)&(temp_pos[2] < 0.49) )|((temp_pos[2] > 0.17)&(temp_pos[2] < 0.19))):
                temp_pos = np.array(transformlocal).dot(temp_pos)
        elif((motion == 4)|(motion == 5)):
            if(((temp_pos[0] < -0.47)&(temp_pos[0] > -0.49) )|((temp_pos[0] < -0.17)&(temp_pos[0] > -0.19))):
                temp_pos = np.array(transformlocal).dot(temp_pos)
        elif((motion == 6)|(motion == 7)):
            if(((temp_pos[0] > 0.47)&(temp_pos[0] < 0.49) )|((temp_pos[0] > 0.17)&(temp_pos[0] < 0.19))):
                temp_pos = np.array(transformlocal).dot(temp_pos)
        elif((motion == 8)|(motion == 9)):
            if(((temp_pos[1] > 0.47)&(temp_pos[1] < 0.49) )|((temp_pos[1] > 0.17)&(temp_pos[1] < 0.19))):
                temp_pos = np.array(transformlocal).dot(temp_pos)
        elif((motion == 10)|(motion == 11)):
            if(((temp_pos[1] < -0.47)&(temp_pos[1] > -0.49) )|((temp_pos[1] < -0.17)&(temp_pos[1] > -0.19))):
                temp_pos = np.array(transformlocal).dot(temp_pos)
                
        for sub in range(3):
            new_all_cubics_vertices[vertex_pos + sub] = temp_pos[sub]

    new_all_cubics_vertices = np.array(new_all_cubics_vertices)
    #new_all_cubics_vertices = new_all_cubics_vertices.astype(np.float32)
    combined_vertices1 = []
    for i in range(0, len(new_all_cubics_vertices), 3):
        combined_vertices1.extend(new_all_cubics_vertices[i:i+3])
        combined_vertices1.extend(all_cubes_color[i:i+3])
            
    combined_vertices1 = np.array(combined_vertices1)
    combined_vertices1 = combined_vertices1.astype(np.float32)




    # 视图矩阵和模型矩阵
    view = glm.lookAt(glm.vec3(0, 0, 3), glm.vec3(0, 0, 0), glm.vec3(0, 1, 0))
    model = glm.mat4(1.0)

    # 总变换矩阵
    transform1 = projection * view * model

    # 创建变换矩阵
    transform = glm.mat4(1.0)  
    transform = glm.rotate(transform, glm.radians(rot_x), glm.vec3(1.0, 0.0, 0.0))  # 绕x轴旋转
    transform = glm.rotate(transform, glm.radians(rot_y), glm.vec3(0.0, 1.0, 0.0))  # 绕y轴旋转
    final_transform = transform1 * transform

    
    # 获取变换矩阵的uniform位置并设置矩阵
    transformLoc = glGetUniformLocation(shader, "transform")
    glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm.value_ptr(final_transform))
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, combined_vertices1.nbytes, combined_vertices1, GL_STATIC_DRAW)

    glBindVertexArray(VAO)
    
    glDrawElements(GL_TRIANGLES, len(indices), GL_UNSIGNED_INT, None)
    glBindVertexArray(0)


    glDisable(GL_DEPTH_TEST)
    glBindVertexArray(VAO_button)
    button_transform = glm.ortho(-1, 1, -1, 1) * glm.mat4(1)
    glUniformMatrix4fv(glGetUniformLocation(shader, "transform"), 1, GL_FALSE, glm.value_ptr(button_transform))
    glDrawElements(GL_QUADS, len(button_indices), GL_UNSIGNED_INT, None)

    glBindVertexArray(VAO_button1)
    button_transform = glm.ortho(-1, 1, -1, 1) * glm.mat4(1)
    glUniformMatrix4fv(glGetUniformLocation(shader, "transform"), 1, GL_FALSE, glm.value_ptr(button_transform))
    glDrawElements(GL_QUADS, len(button_indices), GL_UNSIGNED_INT, None)

    glBindVertexArray(VAO_button2)
    button_transform = glm.ortho(-1, 1, -1, 1) * glm.mat4(1)
    glUniformMatrix4fv(glGetUniformLocation(shader, "transform"), 1, GL_FALSE, glm.value_ptr(button_transform))
    glDrawElements(GL_QUADS, len(button_indices), GL_UNSIGNED_INT, None)
    
    glBindVertexArray(0)

    glEnable(GL_DEPTH_TEST)

    glfw.swap_buffers(window)

# 释放资源
glfw.terminate()

碎碎念:第一次写博客,如果有错还望指正

这也是笔者第一次拿opengl实现一个完整项目,确实是受益匪浅,实实在在地加深了对图形学编程的理解,但这个项目还有很多需要完善的部分,旋转的功能做的也不够完善,后期如果有机会会把这个项目再加工

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值