Неправильно отображаются цвета куба OpenGL Rubik при повороте - PullRequest
0 голосов
/ 24 июня 2019

Я пытался закодировать трехмерный кубик Рубика с помощью opengl, но у меня возникли проблемы с пониманием концепции каждого элемента, имеющего собственную матрицу, и использования glMultMatrixf (), как это делают многие другие примеры. Вместо этого я переключал цвета каждого отдельного куба, когда я поворачивал сторону куба. Это иногда вызывает проблемы, когда я поворачиваю вправо, а затем вверх цвета становятся неправильными. Мне было интересно, возможно ли это сделать по-своему, переключая цвета, и если это так, помогите мне отладить мою проблему. Спасибо!

Например, сейчас, если я выполняю turn_x, а затем turn_y, цвета не отображаются правильно, так как я переключаю цвета на кубе. Есть ли способ изменить функции lr и ud, чтобы цвета могли переключаться правильно? Любая помощь будет принята с благодарностью!

главный:

dim = 3
cube = []

for x in range(-1, 2):
    for y in range(-1, 2):
        for z in range(-1, 2):
            matrix = np.matrix([[x, 0, 0], [0, y, 0], [0, 0, z]])
            cube.append(Piece(x, y, z, matrix))

def turn_x(direction):
    trans_matrix = np.matrix([[0, 1], [-1, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.x == direction:
            new_point = trans_matrix * np.matrix([[block.y], [block.z]])
            block.update(block.x, new_point.item(0), new_point.item(1))
            block.lr(direction)


def turn_y(direction):
    trans_matrix = np.matrix([[0, 1], [-1, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.y == direction:
            new_point = trans_matrix * np.matrix([[block.x], [block.z]])
            block.update(new_point.item(0), block.y, new_point.item(1))
            block.ud(direction)


def main():
    pygame.init()
    display = (1200, 1000)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

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

    glClearColor(0.6, 0.6, 0.6, 0)
    glTranslatef(0.0, 0.0, -20)
    glRotatef(45, 1, 1, 0)

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

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_3:
                    turn_x(1)
                if event.key == pygame.K_5:
                    turn_y(1)

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        for c in cube:
            c.draw()
        pygame.display.flip()
        pygame.time.wait(10)
main()

часть:

class Piece:

    def __init__(self, x, y, z, position):
        self.x = x
        self.y = y
        self.z = z
        self.len = 0.5
        self.v = [
            (self.x - self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y + self.len, self.z + self.len),
            (self.x - self.len, self.y + self.len, self.z + self.len),
        ]
        self.edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6),
                      (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
        self.surfaces = [(0, 1, 2, 3), (5, 4, 7, 6), (4, 0, 3, 7), (1, 5, 6, 2),
                         (4, 5, 1, 0), (3, 2, 6, 7)]
        self.colors = [ # blue
                       (0, 0, 1),
                        # green
                       (0, 1, 0),
                        # orange
                       (1, 0.5, 0.1),
                        # red
                       (1, 0, 0),
                        # yellow
                       (1, 1, 0),
                        # white
                       (1, 1, 1)]
        self.matrix = position

    def lr(self, dir):
        color_ind = []
        if dir == -1:
            color_ind = [4, 5, 2, 3, 1, 0]
        else:
            color_ind = [5, 4, 2, 3, 0, 1]
        new_colors = []
        for i in color_ind:
            new_colors.append(self.colors[i])
        self.colors = new_colors

    def ud(self, dir):
        color_ind = []
        if dir == 1:
            color_ind = [2, 3, 1, 0, 4, 5]
        else:
            color_ind = [3, 2, 0, 1, 4, 5]
        new_colors = []
        for i in color_ind:
            new_colors.append(self.colors[i])
        self.colors = new_colors

    def update(self, x, y, z):
        self.matrix = np.matrix([[x, 0, 0], [0, y, 0], [0, 0, z]])
        print(self.matrix)
        self.x = x
        self.y = y
        self.z = z
        self.update_vertices()

    def update_vertices(self):
        self.v = [
            (self.x - self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y - self.len, self.z - self.len),
            (self.x + self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y + self.len, self.z - self.len),
            (self.x - self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y - self.len, self.z + self.len),
            (self.x + self.len, self.y + self.len, self.z + self.len),
            (self.x - self.len, self.y + self.len, self.z + self.len),
        ]

    def draw(self):
        glEnable(GL_DEPTH_TEST)

        glEnable(GL_POLYGON_OFFSET_FILL)
        glPolygonOffset(1.0, 1.0)

        glLineWidth(5)
        glColor3fv((0, 0, 0))
        glBegin(GL_LINES)
        for e in self.edges:
            glVertex3fv(self.v[e[0]])
            glVertex3fv(self.v[e[1]])
        glEnd()

        glBegin(GL_QUADS)
        for i in range(len(self.surfaces)):
            glColor3fv(self.colors[i])
            for j in self.surfaces[i]:
                glVertex3fv(self.v[j])
        glEnd()

1 Ответ

0 голосов
/ 24 июня 2019

Нет необходимости в матричной конкатенации, поскольку все, что вам нужно сделать, это повернуть вектор влево или вправо на 90 °.

Поворот (2-мерного) вектора на 90 градусов можно получить, поменяв местами компоненты x и y. Для поворота вправо (математическое) x-компонент результата должен быть инвертирован. Для поворота влево компонент y результата должен быть инвертирован:

v       = (x, y)
v_right = (-y, x)
v_left  = (y, -x)

Воспользуйтесь этим в функциях turn_x() и turn_y():

def turn_x(layer, direction):
    for i in range(len(cube)):
        block = cube[i]
        if block.x == layer:
            block.update(block.x, block.z*direction, -block.y*direction)
            block.lr(direction)

def turn_y(layer, direction):
    for i in range(len(cube)):
        block = cube[i]
        if block.y == layer:
            block.update(block.z*direction, block.y, -block.x*direction)
            block.ud(direction)

Если вы хотите сделать то же самое, используя numpy.matrix, то вы должны умножить матрицу 2x2 на матрицу 2x1:

def turn_x(layer, direction):
    trans_matrix = np.matrix([[0, 1*direction], [-1*direction, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.x == layer:
            new_point = trans_matrix * np.matrix([[block.y], [block.z]])
            block.update(block.x, new_point.item(0), new_point.item(1))
            block.lr(direction)

def turn_y(layer, direction):
    trans_matrix = np.matrix([[0, 1*direction], [-1*direction, 0]])
    for i in range(len(cube)):
        block = cube[i]
        if block.y == layer:
            new_point = trans_matrix * np.matrix([[block.x], [block.z]])
            block.update(new_point.item(0), block.y, new_point.item(1))
            block.ud(direction)

Далее вы должны убедиться, что функция turn_x() и метод Piece.lr() соответственно turn_y() и turn_y.ud() выполняют вращения в одном направлении.

Если dir==1, то .lr() и .ud() должны повернуться влево. Это относится к .lr(), но неправильно к .ud():

def ud(self, dir):
    color_ind = []
    if dir == -1:   # <------------------ == -1 instead of == 1
        color_ind = [2, 3, 1, 0, 4, 5]
    else:
        color_ind = [3, 2, 0, 1, 4, 5]

Используйте следующую основную программу. Сцена может вращаться с помощью клавиш <</kbd>, > , ^ и v , чтобы исследовать повороты.
3 вращает внешний левый слой вверх и 4 вниз.
5 поворачивает верхний слой вправо и 6 влево.

def main():
    pygame.init()
    display = (400, 400)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

    glMatrixMode(GL_PROJECTION)
    gluPerspective(45, (display[0] / display[1]), 0.1, 50)

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

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_3:
                    turn_x(1, 1)
                if event.key == pygame.K_4:
                    turn_x(1, -1)
                if event.key == pygame.K_5:
                    turn_y(1, 1)
                if event.key == pygame.K_6:
                    turn_y(1, -1)
        keys = pygame.key.get_pressed()
        if keys[pygame.K_UP]:
            angle_y += 5
        if keys[pygame.K_DOWN]:
            angle_y -= 5
        if keys[pygame.K_RIGHT]:
            angle_x += 5
        if keys[pygame.K_LEFT]:
            angle_x -= 5

        glClearColor(0.6, 0.6, 0.6, 0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslatef(0.0, 0.0, -10)
        glRotatef(45, 1, 1, 0)

        glRotatef(angle_y, -1, 0, 0)
        glRotatef(angle_x, 0, 1, 0)

        for c in cube:
            c.draw()
        pygame.display.flip()
        pygame.time.wait(10)

main()

В любом случае, я рекомендую не менять местами грани куба и не менять вершины куба.
Создайте куб с минимумом (-1, -1, -1) и максимумом (1, 1, 1).
Положение куба определяется атрибутами (self.x, self.y, self.z), а его масштаб определяется self.len.
Добавьте атрибут матрицы вращения 3x3, который определяет текущее вращение. Матрица вращения инициализируется как [[1, 0, 0], [0, 1, 0], [0, 0, 1]]:

class Piece:

    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z
        self.len = 0.5
        self.rotation = np.matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])

        self.v = [(-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1), (-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1)]
        self.edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6),
                      (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
        self.surfaces = [(0, 1, 2, 3), (5, 4, 7, 6), (4, 0, 3, 7), (1, 5, 6, 2),
                         (4, 5, 1, 0), (3, 2, 6, 7)]
        self.colors = [ 
            (0, 0, 1),     # blue
            (0, 1, 0),     # green
            (1, 0.5, 0.1), # orange
            (1, 0, 0),     # red
            (1, 1, 0),     # yellow
            (1, 1, 1)]     # white

Когда куб нарисован, используйте glScale, чтобы масштабировать куб до его размера, и glTranslate, чтобы установить куб в исходное положение.
Наконец, масштабированный и переведенный куб поворачивается на свое место, как если бы это был «настоящий» кубик Рубика. Для этого матрицу 3х3 необходимо преобразовать в матрицу 4х4 и умножить на текущую матрицу на glMultMatrix

class Piece:

    # [...]

    def draw(self):

        r = np.array(self.rotation).reshape(9)
        rm44 = [r[0],r[1],r[2],0, r[3],r[4],r[5],0, r[6],r[7],r[8],0, 0,0,0,1]

        glEnable(GL_DEPTH_TEST)

        glEnable(GL_POLYGON_OFFSET_FILL)
        glPolygonOffset(1.0, 1.0)

        glPushMatrix()
        glMultMatrixf(rm44)
        glTranslatef(self.x, self.y, self.z)
        glScalef(self.len, self.len, self.len)

        glLineWidth(5)
        glColor3fv((0, 0, 0))
        glBegin(GL_LINES)
        for e in self.edges:
            glVertex3fv(self.v[e[0]])
            glVertex3fv(self.v[e[1]])
        glEnd()

        glBegin(GL_QUADS)
        for i in range(len(self.surfaces)):
            glColor3fv(self.colors[i])
            for j in self.surfaces[i]:
                glVertex3fv(self.v[j])
        glEnd()

        glPopMatrix()

Создайте 3 метода, которые могут повернуть куб вокруг оси 3:

class Piece:

    # [...]

    def rotX(self, dir):
        rot_m = np.matrix([[1, 0, 0], [0, 0, dir], [0, -dir, 0]])
        self.update(rot_m)

    def rotY(self, dir):
        rot_m = np.matrix([[0, 0, -dir], [0, 1, 0], [dir, 0, 0]])
        self.update(rot_m)

    def rotZ(self, dir):
        rot_m = np.matrix([[0, dir, 0], [-dir, 0, 0], [0, 0, 1]])
        self.update(rot_m)

    def update(self, rot_mat):
        self.rotation = self.rotation * rot_mat

Наконец, вам нужен метод, который возвращает текущую позицию куба. Этот метод нужен функциям поворота (turn_x, turn_y) для определения затронутых кубов. Этот метод поворачивает вектор положения (self.x, self.y, self.z) куба на текущее вращение (self.rotation) и возвращает результат:

class Piece:

    # [...]

    def location(self):
        current_pos = np.matrix([self.x, self.y, self.z]) * self.rotation
        return (current_pos.item(0), current_pos.item(1), current_pos.item(2))

Когда кубы построены, тогда матрица не требуется:

cube = [Piece(x, y, z) for x in range(-1, 2) for y in range(-1, 2) for z in range(-1, 2)]

Операции поворота просто должны вызывать методы .rotX, .rotY соответственно .rotZ с параметром direction. направление должно быть либо 1, либо -1:

def turn_x(layer, direction):
    [c.rotX(direction) for c in cube if c.location()[0] == layer]

def turn_y(layer, direction):
    [c.rotY(direction) for c in cube if c.location()[1] == layer]

def turn_z(layer, direction):
    [c.rotZ(direction) for c in cube if c.location()[2] == layer]
...