pygame围绕轴旋转立方体 [英] Pygame rotating cubes around axis

查看:71
本文介绍了pygame围绕轴旋转立方体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在玩旋转立方体的示例


最小示例:

 导入数学导入pygamedef工程(矢量,w,h,fov,距离):系数= math.atan(fov/2 * math.pi/180)/(距离+ vector.z)x = vector.x *因数* w + w/2y = -vector.y *因子* w + h/2返回pygame.math.Vector3(x,y,vector.z)def rotation_vertices(顶点,角度,轴):返回[v在顶点处的v.rotate(角度,轴)]def scale_vertices(vertices,s):对于顶点中的v返回[pygame.math.Vector3(v [0] * s [0],v [1] * s [1],v [2] * s [2])def translate_vertices(vertices,t):返回[v + pygame.math.Vector3(t)为顶点中的v]def project_vertices(顶点,w,h,fov,距离):返回[在顶点的v的project(v,w,h,fov,distance)]Mesh()类:def __init __(自身,顶点,面):self .__ vertices = [py中的v的pygame.math.Vector3(v)]self .__ faces =面孔def旋转(自身,角度,轴):self .__ vertices = rotate_vertices(self .__ vertices,角度,轴)def比例(自我,s):self .__ vertices = scale_vertices(self .__ vertices,s)def translation(self,t):self .__ vertices = translate_vertices(self .__ vertices,t)def compute_average_z(self,vertices):返回[(i,sum([顶点[j] .z表示f中的j]]/len(f))表示i,f的enumerate(self .__ faces)]def get_face(自我,索引):返回self .__ faces [index]def get_vertices(self):返回self .__ verticesdef create_polygon(自身,面,顶点):在[* face,face [0]]中为i返回[(vertices [i] .x,vertices [i] .y)类场景:def __init __(自身,mehses,fov,距离):self.meshes =麻将self.fov = fovself.distance =距离self.euler_angles = [0,0,0]def transform_vertices(self,vertices,width,height):transform_vertices =顶点axis_list = [(1,0,0),(0,1,0),(0,0,1)]对于角度,反向轴(list(zip(list(list(self.euler_angles),axis_list)))):transform_vertices = rotation_vertices(transformed_vertices,角度,轴)transform_vertices = project_vertices(transformed_vertices,宽度,高度,self.fov,self.distance)返回transform_verticesdef绘制(自身,表面):多边形= []对于self.meshes中的网格:transform_vertices = self.transform_vertices(mesh.get_vertices(),* surface.get_size())avg_z = mesh.calculate_average_z(transformed_vertices)对于avg_z中的z:#for z中的已排序(avg_z,键= lambda x:x [1],reverse = True):点列表= mesh.create_polygon(mesh.get_face(z [0]),transformed_vertices)polygons.append((pointpoint,z [1]))#pygame.draw.polygon(surface,(128,128,192),点列表)#pygame.draw.polygon(surface,(0,0,0),点列表,3)对于已排序的poly(多边形,键= lambda x:x [1],reverse = True):pygame.draw.polygon(surface,(128,128,192),poly [0])pygame.draw.polygon(surface,(0,0,0),poly [0],3)顶点= [(-1,-1,1),(1,-1,1),(1,1,1),(-1,1,1),(-1,-1,-1),(1,-1,-1),(1,1,-1),(-1,1,-1)]面= [(0,1,2,3),(1,5,6,2),(5,4,7,6),(4,0,3,7),(3,2,6,7),(1,0,4,5)]cube_origins = [(-1,-1,0),(0,-1,0),(1,-1,0),(1,0,0),(1,1,0),(0,1,0),(-1,1,0),(-1,0,0)]网格= []对于cube_origins中的原点:立方体=网格(顶点,面)cube.scale((0.5,0.5,0.5))cube.translate(原点)meshes.append(立方体)场景=场景(网格,90,5)pygame.init()窗口= pygame.display.set_mode((400,300))时钟= pygame.time.Clock()运行=真运行时:clock.tick(60)对于pygame.event.get()中的事件:如果event.type == pygame.QUIT:运行=错误window.fill((255,255,255))scene.draw(窗口)scene.euler_angles [1] + = 1pygame.display.flip()pygame.quit() 

I have been playing around with the example of a rotating cube here. I have generated 2 cubes that should rotate around the Y-axis. However, it doesn't seem to work as expected and I can't figure out what the problem of it is.

Here is a working code example:

import sys
import math
import pygame

from pygame.math import Vector3
from enum import Enum


class Color(Enum):
    BLACK = (0, 0, 0)
    SILVER = (192,192,192)


class Cube():

    def __init__(self, vectors, screen_width, screen_height, initial_angle=25):
        self._vectors = vectors
        self._angle = initial_angle
        self._screen_width = screen_width
        self._screen_height = screen_height

        # Define the vectors that compose each of the 6 faces
        self._faces  = [(0,1,2,3),
                       (1,5,6,2),
                       (5,4,7,6),
                       (4,0,3,7),
                       (0,4,5,1),
                       (3,2,6,7)]

        self._setup_initial_positions(initial_angle)

    def _setup_initial_positions(self, angle):
        tmp = []
        for vector in self._vectors:
            rotated_vector = vector.rotate_x(angle).rotate_y(angle)#.rotateZ(self.angle)
            tmp.append(rotated_vector)

        self._vectors = tmp

    def transform_vectors(self, new_angle):
        # It will hold transformed vectors.
        transformed_vectors = []

        for vector in self._vectors:
            # Rotate the point around X axis, then around Y axis, and finally around Z axis.
            mod_vector = vector.rotate_y(new_angle)
            # Transform the point from 3D to 2D
            mod_vector = self._project(mod_vector, self._screen_width, self._screen_height, 256, 4)
            # Put the point in the list of transformed vectors
            transformed_vectors.append(mod_vector)

        return transformed_vectors

    def _project(self, vector, win_width, win_height, fov, viewer_distance):
        factor = fov / (viewer_distance + vector.z)
        x = vector.x * factor + win_width / 2
        y = -vector.y * factor + win_height / 2
        return Vector3(x, y, vector.z)

    def calculate_average_z(self, vectors):
        avg_z = []
        for i, face in enumerate(self._faces):
            # for each point of a face calculate the average z value
            z = (vectors[face[0]].z + 
                 vectors[face[1]].z + 
                 vectors[face[2]].z + 
                 vectors[face[3]].z) / 4.0
            avg_z.append([i, z])

        return avg_z

    def get_face(self, index):
        return self._faces[index]

    def create_polygon(self, face, transformed_vectors):
        return [(transformed_vectors[face[0]].x, transformed_vectors[face[0]].y), 
                (transformed_vectors[face[1]].x, transformed_vectors[face[1]].y),
                (transformed_vectors[face[2]].x, transformed_vectors[face[2]].y),
                (transformed_vectors[face[3]].x, transformed_vectors[face[3]].y),
                (transformed_vectors[face[0]].x, transformed_vectors[face[0]].y)]


class Simulation:
    def __init__(self, win_width=640, win_height=480):
        pygame.init()

        self.screen = pygame.display.set_mode((win_width, win_height))

        self.clock = pygame.time.Clock()

        cube = Cube([
            Vector3(0, 0.5, -0.5),
            Vector3(0.5, 0.5, -0.5),
            Vector3(0.5, 0, -0.5),
            Vector3(0, 0, -0.5),
            Vector3(0, 0.5, 0),
            Vector3(0.5, 0.5, 0),
            Vector3(0.5, 0, 0),
            Vector3(0, 0, 0)
        ], win_width, win_height)

        cube2 = Cube([
            Vector3(0.5, 0.5, -0.5),
            Vector3(1, 0.5, -0.5),
            Vector3(1, 0, -0.5),
            Vector3(0.5, 0, -0.5),
            Vector3(0.5, 0.5, 0),
            Vector3(1, 0.5, 0),
            Vector3(1, 0, 0),
            Vector3(0.5, 0, 0)
        ], win_width, win_height)

        self._angle = 30

        self._cubes = [cube, cube2]

    def run(self):
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()

            self.clock.tick(50)
            self.screen.fill(Color.BLACK.value)

            for cube in self._cubes:
                transformed_vectors = cube.transform_vectors(self._angle)
                avg_z = cube.calculate_average_z(transformed_vectors)

                # Draw the faces using the Painter's algorithm:
                # Distant faces are drawn before the closer ones.
                for avg_z in sorted(avg_z, key=lambda x: x[1], reverse=True):
                    face_index = avg_z[0]
                    face = cube._faces[face_index]
                    pointlist = cube.create_polygon(face, transformed_vectors)

                    pygame.draw.polygon(self.screen, Color.SILVER.value,pointlist)
                    pygame.draw.polygon(self.screen, Color.BLACK.value, pointlist, 3)
                    # break 

            self._angle += 1

            pygame.display.flip()

if __name__ == "__main__":
    Simulation().run()

Both cubes should rotate around the Y-axis in this example. For the future I'd like to have a solution so they can rotate around any axis.

解决方案

It is not sufficient to sort the faces of each cube separately by its depth. You've to sort the faces of all objects of the entire scene by its depth.

Create a list of tuples, which consists of the projected (transformed) points ofa face and the average depth (z value):

polygons = []
for cube in self._cubes:
    transformed_vectors = cube.transform_vectors(self._angle)
    avg_z = cube.calculate_average_z(transformed_vectors)
    for z in avg_z:
        face_index = z[0]
        face = cube._faces[face_index]
        pointlist = cube.create_polygon(face, transformed_vectors)
        polygons.append((pointlist, z[1]))

Draw the faces of all objects in (reverse) sorted order:

for poly in sorted(polygons, key=lambda x: x[1], reverse=True):
    pygame.draw.polygon(self.screen, Color.SILVER.value,poly[0])
    pygame.draw.polygon(self.screen, Color.BLACK.value, poly[0], 3)


Minimal example:

import math
import pygame

def project(vector, w, h, fov, distance):
    factor = math.atan(fov / 2 * math.pi / 180) / (distance + vector.z)
    x = vector.x * factor * w + w / 2
    y = -vector.y * factor * w + h / 2
    return pygame.math.Vector3(x, y, vector.z)

def rotate_vertices(vertices, angle, axis):
    return [v.rotate(angle, axis) for v in vertices]
def scale_vertices(vertices, s):
    return [pygame.math.Vector3(v[0]*s[0], v[1]*s[1], v[2]*s[2]) for v in vertices]
def translate_vertices(vertices, t):
    return [v + pygame.math.Vector3(t) for v in vertices]
def project_vertices(vertices, w, h, fov, distance):
    return [project(v, w, h, fov, distance) for v in vertices]

class Mesh():

    def __init__(self, vertices, faces):
        self.__vertices = [pygame.math.Vector3(v) for v in vertices]
        self.__faces = faces

    def rotate(self, angle, axis):
        self.__vertices = rotate_vertices(self.__vertices, angle, axis)
    def scale(self, s):
        self.__vertices = scale_vertices(self.__vertices, s)
    def translate(self, t):
        self.__vertices = translate_vertices(self.__vertices, t)

    def calculate_average_z(self, vertices):
        return [(i, sum([vertices[j].z for j in f]) / len(f)) for i, f in enumerate(self.__faces)]

    def get_face(self, index):
        return self.__faces[index]
    def get_vertices(self):
        return self.__vertices

    def create_polygon(self, face, vertices):
        return [(vertices[i].x, vertices[i].y) for i in [*face, face[0]]]
       
class Scene:
    def __init__(self, mehses, fov, distance):
        self.meshes = mehses
        self.fov = fov
        self.distance = distance 
        self.euler_angles = [0, 0, 0]

    def transform_vertices(self, vertices, width, height):
        transformed_vertices = vertices
        axis_list = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
        for angle, axis in reversed(list(zip(list(self.euler_angles), axis_list))):
            transformed_vertices = rotate_vertices(transformed_vertices, angle, axis)
        transformed_vertices = project_vertices(transformed_vertices, width, height, self.fov, self.distance)
        return transformed_vertices

    def draw(self, surface):
        
        polygons = []
        for mesh in self.meshes:
            transformed_vertices = self.transform_vertices(mesh.get_vertices(), *surface.get_size())
            avg_z = mesh.calculate_average_z(transformed_vertices)
            for z in avg_z:
            #for z in sorted(avg_z, key=lambda x: x[1], reverse=True):
                pointlist = mesh.create_polygon(mesh.get_face(z[0]), transformed_vertices)
                polygons.append((pointlist, z[1]))
                #pygame.draw.polygon(surface, (128, 128, 192), pointlist)
                #pygame.draw.polygon(surface, (0, 0, 0), pointlist, 3)

        for poly in sorted(polygons, key=lambda x: x[1], reverse=True):
            pygame.draw.polygon(surface, (128, 128, 192), poly[0])
            pygame.draw.polygon(surface, (0, 0, 0), poly[0], 3)
        

vertices = [(-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1), (-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1)]
faces = [(0,1,2,3), (1,5,6,2), (5,4,7,6), (4,0,3,7), (3,2,6,7), (1,0,4,5)]

cube_origins = [(-1, -1, 0), (0, -1, 0), (1, -1, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (-1, 1, 0), (-1, 0, 0)]
meshes = []
for origin in cube_origins:
    cube = Mesh(vertices, faces)
    cube.scale((0.5, 0.5, 0.5))
    cube.translate(origin)
    meshes.append(cube)

scene = Scene(meshes, 90, 5)

pygame.init()
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill((255, 255, 255))
    scene.draw(window)
    scene.euler_angles[1] += 1
    pygame.display.flip()

pygame.quit()

这篇关于pygame围绕轴旋转立方体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆