파이썬과 openGL을 이용하여 조그마한 당구장을 만들었다
물론 학교 과제다
결과물은 위와 같다.
기본적으로 카메라가 회전을 하고(회전 여부를 키보드로 설정 가능)
h를 눌리면 공이 랜덤한 방향으로 움직인다
그러다 당구장 벽면에 부딪치면 튕긴다.
벽면에 부딪치는건 간단하게 방향벡터를 x축 대칭이나 y축 대칭을 하는것으로 해결했다.
또한 마찰에 의해서 속도가 조금씩 느려진다.
이것은 간단하게 속도를 프레임마다 조금씩 늦추는 것으로 구현했다.
아래를 클릭하면 다운 가능하고,
floor.jpg와 wall2.jpg는 간단한 텍스쳐이다.
저작권 문제가 있을 수 있기 때문에 업로드를 하지 않으니, 혹시 실행해보고 싶으신 분들은 구글링으로 아무거나 검색해서 다운받으시면 된다.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import math
import numpy as np
import random
from PIL import Image
Window_Width = 800
Window_Height = 600
# Light Properties
Ld = [1, 1, 1, 0]
Ls = [1, 1, 1, 0]
La = [0.5, 0.5, 0.5, 0]
# Material Properties
Md = [1, 1, 0, 1]
Ms = [1, 1, 1, 1]
Ma = [0.5, 0.5, 0.5, 0]
shininess = [80]
isLightOn_0 = True
isRotateCamera = True
rotateSpeed = math.pi/500
ballMove = False
ballSpeed = 0
ballSpeedInit = 1700 / 10000
ballMoveAngle = 0
ballFriction = 3 / 10000
cameraPosX = 15
cameraPosY = 10
cameraPosZ = 25
pool_camera_dist = 40
pool_camera_y_angle = math.pi/6
pool_camera_x_angel = math.pi/6
Color_Wall = [0.4, 0.7, 0.9]
Color_Floor = [0.3, 0.3, 0.7]
Color_Box = [0.5, 0.9, 0.9]
Color_Ball_1 = [0.7, 0.3, 0.3]
poolwall = [
[[-10, 1, -20], [10, 1, -20]]
, [[10, 1, -20], [10, 1, 20]]
, [[10, 1, 20], [-10, 1, 20]]
, [[-10, 1, 20], [-10, 1, -20]]
]
ballPos_x = 0
ballPos_y = 1
ballPos_z = 0
def loadImage(imageName) :
img = Image.open(imageName)
img_data = np.array(list(img.getdata()), np.uint16)
return img.size[0], img.size[1], img_data
wallix, walliy, wallimg = loadImage("wall2.jpg")
floorix, flooriy, floorimg = loadImage("floor.jpg")
def computeNormal(p1, p2, p3):
u = np.array([p2[i] - p1[i] for i in range(0, 3)])
v = np.array([p3[i] - p1[i] for i in range(0, 3)])
N = np.cross(u, v)
N = N / np.linalg.norm(N)
return N
def drawVerts(v, f, color_vector):
glColor3fv(color_vector)
glBegin(GL_QUADS)
for i in range(len(f)):
p1, p2, p3, p4 = f[i][0], f[i][1], f[i][2], f[i][3]
N = computeNormal(v[p1], v[p2], v[p3])
glNormal3fv(N)
glVertex3fv(v[p1])
glVertex3fv(v[p2])
glVertex3fv(v[p3])
glVertex3fv(v[p4])
glEnd()
def loadMesh(filename):
print(filename)
with open(filename, "rt") as mesh:
nV = int(next(mesh))
verts = [[0, 0, 0] for idx in range(nV)]
for i in range(0, nV):
verts[i][0], verts[i][1], verts[i][2] = [float(x) for x in next(mesh).split()]
nF = int(next(mesh))
faces = [[0, 0, 0, 0] for idx in range(nF)]
for i in range(0, nF):
faces[i][0], faces[i][1], faces[i][2], faces[i][3] = [int(x) for x in next(mesh).split()]
return verts, faces
def drawPoolBox():
drawVerts(PoolBox_V, PoolBox_F, Color_Box)
def drawWall():
glColor3fv([1,1,1])
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, wallix, walliy, 0, GL_RGB, GL_UNSIGNED_BYTE, wallimg)
glBegin(GL_QUADS)
glTexCoord2fv([0, 1])
glVertex3fv([-50, 50, 50])
glTexCoord2fv([1, 1])
glVertex3fv([-50, 50, -50])
glTexCoord2fv([1, 0])
glVertex3fv([-50, -50,-50])
glTexCoord2fv([0, 0])
glVertex3fv([-50, -50, 50])
glTexCoord2fv([0, 1])
glVertex3fv([50, 50, 50])
glTexCoord2fv([1, 1])
glVertex3fv([50, 50, -50])
glTexCoord2fv([1, 0])
glVertex3fv([50, -50,-50])
glTexCoord2fv([0, 0])
glVertex3fv([50, -50, 50])
glTexCoord2fv([0, 1])
glVertex3fv([-50, -50, -50])
glTexCoord2fv([1, 1])
glVertex3fv([50, -50, -50])
glTexCoord2fv([1, 0])
glVertex3fv([50, 50, -50])
glTexCoord2fv([0, 0])
glVertex3fv([-50, 50, -50])
glTexCoord2fv([0, 1])
glVertex3fv([-50, -50, 50])
glTexCoord2fv([1, 1])
glVertex3fv([50, -50, 50])
glTexCoord2fv([1, 0])
glVertex3fv([50, 50, 50])
glTexCoord2fv([0, 0])
glVertex3fv([-50, 50, 50])
glEnd()
def drawFloor():
glColor3fv([1,1,1])
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, floorix, flooriy, 0, GL_RGB, GL_UNSIGNED_BYTE, floorimg)
glBegin(GL_QUADS)
glTexCoord2fv([0, 1])
glVertex3fv([-60, -5, -60])
glTexCoord2fv([1, 1])
glVertex3fv([60, -5, -60])
glTexCoord2fv([1, 0])
glVertex3fv([60, -5, 60])
glTexCoord2fv([0, 0])
glVertex3fv([-60, -5, 60])
glEnd()
def drawPoolRoom():
drawPoolBox()
drawFloor()
drawWall()
def CalcDistWallBall(wall, ball_x, ball_y, ball_z):
if(wall[0][1] != ball_y) : return
wall_dx = wall[0][0] - wall[1][0]
wall_dz = wall[0][2] - wall[1][2]
a = wall_dz
b = wall_dx
c = wall_dz * wall[0][0] - wall_dx * wall[0][2]
dist = (a * ball_x + b * ball_z + c) / math.sqrt(a*a + b*b)
return dist
def CalcBallPos():
global ballPos_x, ballPos_y, ballPos_z, ballSpeed, ballMoveAngle, ballFriction, ballMove, poolwall
if(ballMove):
ballSpeed -= ballFriction
if(ballSpeed <= 0):
ballMove = False
return
ballPos_x += ballSpeed * math.cos(ballMoveAngle)
ballPos_z += ballSpeed * math.sin(ballMoveAngle)
for i in range(0, len(poolwall)) :
dist = CalcDistWallBall(poolwall[i], ballPos_x, ballPos_y, ballPos_z)
if(dist >= -1.05) :
if(i == 0 or i == 2):
ballMoveAngle *= -1
if(i == 1 or i == 3):
ballMoveAngle += math.pi
ballMoveAngle *= -1
def drawPoolBall():
global ballPos_x, ballPos_y, ballPos_z
glPushMatrix()
glTranslatef(ballPos_x, ballPos_y, ballPos_z)
glColor3fv(Color_Ball_1)
glutSolidSphere(1, 10, 10)
glPopMatrix()
# initialization
def GLinit():
# 색상,z-buffer, 조명 설정
glClearColor(0, 0, 0, 0)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glLightfv(GL_LIGHT0, GL_DIFFUSE, Ld)
glLightfv(GL_LIGHT0, GL_AMBIENT, La)
glLightfv(GL_LIGHT0, GL_SPECULAR, Ls)
glMaterialfv(GL_FRONT, GL_DIFFUSE, Md)
glMaterialfv(GL_FRONT, GL_SPECULAR, Ms)
glMaterialfv(GL_FRONT, GL_AMBIENT, Ma)
glMaterialfv(GL_FRONT, GL_SHININESS, shininess)
glEnable(GL_TEXTURE_2D)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
def keyboardEvent(key, x, y):
global isRotateCamera, pool_camera_dist, ballMove, ballMoveAngle, ballSpeed, ballSpeedInit
global isLightOn_0
if (key == b'n' or key == b'N'):
if(isLightOn_0 == False):
glEnable(GL_LIGHT0)
isLightOn_0 = True
if(key == b'f' or key == b'F'):
if (isLightOn_0):
glDisable(GL_LIGHT0)
isLightOn_0 = False
if(key == b'i' or key == b'I'):
if(pool_camera_dist >= 10):
pool_camera_dist -= 1
if(key == b'o' or key == b'O'):
if(pool_camera_dist <= 50):
pool_camera_dist += 1
if(key == b'r' or key == b'R'):
if(isRotateCamera):
isRotateCamera = False
else:
isRotateCamera = True
if(key == b'h' or key == b'H'):
if(ballMove == False):
ballMoveAngle = random.random() * 2 * math.pi
ballSpeed = ballSpeedInit
ballMove = True
def CalcCameraPos():
global pool_camera_dist, pool_camera_x_angel, pool_camera_y_angle
x = pool_camera_dist * math.cos(pool_camera_y_angle) *math.cos(pool_camera_x_angel)
y = pool_camera_dist * math.sin(pool_camera_y_angle)
z = pool_camera_dist * math.cos(pool_camera_y_angle) *math.sin(pool_camera_x_angel)
return x, y, z
# display callback
def display():
global cameraPosX, cameraPosY, cameraPosZ, pool_camera_x_angel, rotateSpeed
if(isRotateCamera):
pool_camera_x_angel += rotateSpeed
if(rotateSpeed >= math.pi):
rotateSpeed = 0
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# CAMERA SETTING
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60.0, Window_Width / Window_Height, 0.1, 1000)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
cameraPosX, cameraPosY, cameraPosZ = CalcCameraPos()
gluLookAt(cameraPosX, cameraPosY, cameraPosZ, 0, 0, 0, 0, 1, 0)
glLightfv(GL_LIGHT0, GL_POSITION, [-30, +30, +30, 1])
# draw vertices
drawPoolRoom()
CalcBallPos()
drawPoolBall()
glFlush()
#######################################################
PoolBox_V, PoolBox_F = loadMesh("PoolBox.txt")
# windowing
glutInit(sys.argv)
glutInitDisplayMode(GLUT_SINGLE | GLUT_DEPTH | GLUT_RGBA)
glutInitWindowSize(Window_Width, Window_Height)
glutInitWindowPosition(10, 10)
glutCreateWindow(b"Light on Mesh in file ")
GLinit()
# register callbacks
glutDisplayFunc(display)
glutIdleFunc(display)
glutKeyboardFunc(keyboardEvent)
# enter main-loop
glutMainLoop()
'코딩 > 잡다한 코딩' 카테고리의 다른 글
Git : 내 컴퓨터에도 필요하고 원격에도 필요한데 변경사항을 반영하기 싫을 때 (0) | 2019.03.28 |
---|---|
Webstorm 이용한 Spring boot + Vue.js 연동 (0) | 2018.11.26 |
intellij idea + spring boot lombok이 안먹힐 때 (0) | 2018.11.25 |
[openGL][python3] 미니멀 태양계 (0) | 2018.01.03 |
[하드웨어] STM32를 이용한 방공시스템 제작 (0) | 2018.01.01 |