본문 바로가기

코딩/잡다한 코딩

[openGL][python3] 미니멀 태양계








결과물은 위와 같다.


태양, 지구, 명왕성, 그리고 지구를 회전하는 인공위성이 있다.



인공위성은 공전축이 매 회전마다 조금씩 바뀌고


명왕성은 공전궤도가 타원이다.




왼쪽위부터 



태양계 전체를 보는 화면,


태양에서 12시 방향의 우주,


명왕성에서 본 지구,


지구의 한국에서 본 하늘



을 표현하고 있다.


 

constant.py는 전역변수들을 저장하고 있다.




물론 이것도 과제다.





constant.py

main.py



main.py

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from constant import *
import math

def drawSatellite():
    global satelliteRevolveAxisCount
    global satelliteNowX
    global satelliteNowY
    global satelliteNowZ

    glColor3fv(satelliteColor)
    distance = satelliteDistance

    #인공위성 궤도 그리기
    for k in range(0, 6):
        glPushMatrix()
        glRotate(satelliteRevolveAxisPeriod, 0, 0, 1)
        glBegin(GL_LINE_STRIP)
        for i in range(0, 361):
            theta = 2.0 * 3.141592 * i / 360.0
            x = distance * math.cos(theta)
            y = distance * math.sin(theta)
            #z = 2.0 * math.pi * satelliteRevolveAxisPeriod * theta / 360.0
            glVertex3f(x, 0, y)
        glEnd()
    for k in range(0,6) : glPopMatrix()

    if(satelliteRevolveAngle >= satelliteRevolveAxisCount * math.pi * 2):
        satelliteRevolveAxisCount += 1



    glPushMatrix()
    theta = satelliteRevolveAxisPeriod * (satelliteRevolveAxisCount - 1)
    glRotate(theta , 0, 0, 1)
    nowx = distance * math.cos(satelliteRevolveAngle + startingAngle / 2)
    nowz = distance * math.sin(satelliteRevolveAngle + startingAngle / 2)
    glTranslatef(nowx, 0, nowz)
    glRotate(90.0, 0,0,1)
    glScale(0.5,0.5,0.5)
    glutSolidTetrahedron()
    glPopMatrix()




def drawPluto():
    global plutoNowX
    global plutoNowZ
    global plutoYear
    global plutoDay

    glColor3fv(plutoColor)
    glBegin(GL_LINE_STRIP)
    for i in range(0, 361):
        theta = 2.0 * 3.141592 * i / 360.0
        x = plutoDistance_b * math.cos(theta)
        y = plutoDistance_a * math.sin(theta)
        glVertex3f(x, 0, y)
    glEnd()

    nowx = plutoDistance_b * math.cos(math.pi * 2 - plutoRevolveAngle + startingAngle)
    nowz = plutoDistance_a * math.sin(math.pi * 2 - plutoRevolveAngle + startingAngle)

    glPushMatrix()
    glTranslatef(nowx, 0, nowz) #공전 위치로 이동
    glRotate(plutoRotateAngle, 0, 1, 0) #자전

    if(plutoRotateAngle >= 360 * (plutoDay+1)):
        plutoDay += 1
    if(plutoRevolveAngle >= math.pi * 2 * (plutoYear +1)):
        plutoYear +=1

    glutSolidDodecahedron()
    glPopMatrix()

    plutoNowX = nowx
    plutoNowZ = nowz



def drawEarth(earthRevolveAngle, earthRotationAngle):
    global earthNowX
    global earthNowZ
    global earthYear
    global earthDay


    distance = earthDistance

    glColor3fv(earthColor)
    glBegin(GL_LINE_STRIP)
    for i in range(0, 361):
        theta = 2.0 * 3.141592 * i / 360.0
        x = distance * math.cos(theta)
        y = distance * math.sin(theta)
        glVertex3f(x, 0, y)
    glEnd()

    nowx = distance * math.cos(math.pi * 2 - earthRevolveAngle + startingAngle) #math.pi를 더하는 이유는 오른쪽에서 시작하기 위해서.
    nowz = distance * math.sin(math.pi * 2 - earthRevolveAngle + startingAngle)

    glPushMatrix()
    glTranslatef(nowx, 0, nowz) #공전 위치로 이동

    glPushMatrix()
    drawSatellite() #인공위성 그리기
    glPopMatrix()

    glRotatef(earthRotationAxis, 0, 0, 1) #자전축만큼 기욺
    glRotatef(earthRotationAngle, 0, 1, 0) #자전 시행

    glColor3fv(earthColor)
    glutSolidCube(earthRadius)

    if(earthRotationAngle >= 360 * (earthDay+1)):
        earthDay += 1
    if(earthRevolveAngle >= math.pi * 2 * (earthYear +1)):
        earthYear +=1

    glPopMatrix()

    earthNowX = nowx
    earthNowZ = nowz



def drawScene():
    # drawing
    # sun
    glColor3f(1, 0, 0)
    glutSolidSphere(sunRadius, 20, 20)
    # earth
    drawEarth(earthRevolveAngle, earthRotationAngle)
    #pluto
    drawPluto()


def disp():    # reset buffer
    global earthRevolveAngle
    global earthRotationAngle
    global satelliteRevolveAngle
    global satelliteRevolveAxisCount
    global plutoRevolveAngle
    global plutoRotateAngle
    global satelliteNowZ
    global satelliteNowY
    global satelliteNowX

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    startTime = 0.0
    endTime = 0.0
    startTime = glutGet(GLUT_ELAPSED_TIME)
    while(True):
        endTime = glutGet(GLUT_ELAPSED_TIME)
        if(pause): break
        if(endTime - startTime >= 10):
            earthRevolveAngle += earthRevolveSpeed / 100
            earthRotationAngle += earthRotationSpeed / 100
            satelliteRevolveAngle += satelliteRevolveSpeed  / 100
            plutoRevolveAngle += plutoRevolveSpeed / 100
            plutoRotateAngle += plutoRotateSpeed / 100
            break;

    glClear(GL_COLOR_BUFFER_BIT)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(30, 1.0, 0.1, 1000)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    korea = math.pi / 12
    xD = math.cos(earthRotationAngle / angleSpeedConstant) * math.cos(korea) * earthRadius
    yD = math.sin(korea) * earthRadius
    zD = math.sin(earthRotationAngle / angleSpeedConstant) * math.cos(korea) * earthRadius

    xDelta = xD * math.cos(earthRotationAxis / angleSpeedConstant) - yD * math.sin(earthRotationAxis / angleSpeedConstant)
    yDelta = xD * math.sin(earthRotationAxis / angleSpeedConstant) + yD * math.cos(earthRotationAxis / angleSpeedConstant)
    zDelta = zD

    camToX = earthNowX + xDelta * 2
    camToY = yDelta * 2
    camToZ = earthNowZ + zDelta * 2
    cameraX = earthNowX + xDelta
    cameraY = yDelta
    cameraZ = earthNowZ + zDelta


    theta = satelliteRevolveAxisPeriod * (satelliteRevolveAxisCount - 1)
    satelliteNowX = 0.0
    satelliteNowY = 0.0
    satelliteNowZ = 0.0

    satelliteNowX = satelliteDistance * math.cos(satelliteRevolveAngle + startingAngle / 2)
    satelliteNowZ = satelliteDistance * math.sin(satelliteRevolveAngle + startingAngle / 2)

    xD = satelliteNowX * math.cos(theta / angleSpeedConstant) - satelliteNowY * math.sin(theta / angleSpeedConstant)
    yD = satelliteNowX * math.sin(theta / angleSpeedConstant) + satelliteNowY * math.cos(theta / angleSpeedConstant)
    satelliteNowX = xD
    satelliteNowY = yD

    satelliteNowX += earthNowX
    satelliteNowZ += earthNowZ


    #2사분면
    glViewport(WINDOW_POSITION_X, WINDOW_POSITION_Y + int(WINDOW_HEIGHT / 2), int(WINDOW_WIDTH / 2), int(WINDOW_HEIGHT / 2))
    glPushMatrix()
    gluLookAt(0, 120 * ViewDist, -170 * ViewDist, 0, 0, 0, 0, 1, 0)
    drawScene()

    earthText = "Eyear = " + str(earthYear) + " , Eday = " + str(earthDay)
    plutoText = "Pyear = " + str(plutoYear) + " , Pday = " + str(plutoDay)
    displayText(45*ViewDist, 50*ViewDist, 0, earthText)
    displayText(0*ViewDist, 50*ViewDist, 0, plutoText)
    glPopMatrix()


    #1사분면
    glViewport(int(WINDOW_POSITION_X + WINDOW_WIDTH / 2), WINDOW_POSITION_Y + int(WINDOW_HEIGHT / 2), int(WINDOW_WIDTH / 2), int(WINDOW_HEIGHT / 2))
    glPushMatrix()
    gluLookAt(0, 0, sunRadius + 0.1, 0, 0, 100, 0, 1, 0)
    drawScene()
    glPopMatrix()

    #3사분면
    glViewport(WINDOW_POSITION_X, WINDOW_POSITION_Y, int(WINDOW_WIDTH / 2), int(WINDOW_HEIGHT / 2))
    glPushMatrix()
    gluLookAt(plutoNowX, plutoRadius, plutoNowZ, earthNowX, 0, earthNowZ, 0, 1, 0)
    drawScene()
    glPopMatrix()

    #4사분면
    glViewport(int(WINDOW_POSITION_X + WINDOW_WIDTH / 2), WINDOW_POSITION_Y, int(WINDOW_WIDTH / 2), int(WINDOW_HEIGHT / 2))
    glPushMatrix()
    if(LookToEarth):
        gluLookAt(satelliteNowX, satelliteNowY+1, satelliteNowZ, earthNowX, 0, earthNowZ, 0, 1, 0)
    else:
        gluLookAt(cameraX, cameraY, cameraZ, camToX, camToY, camToZ, 0, 1, 0)
    drawScene()
    glPopMatrix()
    glFlush()

def keyboardEvent(key, x, y):
    global pause
    global globalSpeed
    global LookToEarth
    global ViewDist

    if(key == b'h' or key == b'H'):
        pause = ~pause

    if(key == b'q' or key == b'Q'):
        exit()

    if(key == b't' or key == b'T'):
        LookToEarth = ~LookToEarth

    if(key == b'-'):
        if(ViewDist <= 3.0):
            ViewDist += 0.1
    if(key == b'+'):
        if(ViewDist >= 0.2):
            ViewDist -= 0.1


def displayText(x,y,z, str) :
    glRasterPos3f(x,y,z)
    for ch in str:
        glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(ch))




# windowing
glutInit(sys.argv)
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT)
glutInitWindowPosition(WINDOW_POSITION_X, WINDOW_POSITION_Y)
glutCreateWindow(b"Simple Solar")
glClearColor(0, 0.0, 0.0, 0)

glEnable(GL_DEPTH_TEST)

# register callbacks
glutDisplayFunc(disp)
glutIdleFunc(disp)
glutKeyboardFunc(keyboardEvent)


# enter main infinite-loop
glutMainLoop()



constant.py

import math

WINDOW_WIDTH = 1000
WINDOW_HEIGHT = 1000
WINDOW_POSITION_X = 0
WINDOW_POSITION_Y = 0

pause = False
LookToEarth = False

angleSpeedConstant = 360.0 / math.pi / 2

globalSpeed = 2 #배속

startingAngle = math.pi

sunRadius = 2.0

ViewDist = 1.0

#earth
earthRevolveAngle = 0.0 #공전
earthRevolveSpeed = 90.0 / angleSpeedConstant * globalSpeed #초당 변화할 angle #4초에 한바퀴
earthRotationAngle = 0.0 #자전
earthRotationSpeed = 360.0 * globalSpeed

earthRotationAxis = 15.0 #자전축

earthRadius = 1.5
earthDistance = 20.0

earthColor = [0, 0.5, 1.0]

earthNowZ = 0.0
earthNowX = 0.0

earthYear = 0
earthDay = 0


#satellite
satelliteRadius = 1
satelliteDistance = 5.0

satelliteColor = [0,1,0]

satelliteRevolveAxisPeriod = 30
satelliteRevolveAxisCount = 0
satelliteRevolveAngle = 0.0 #공전
satelliteRevolveSpeed = 180.0 / angleSpeedConstant * globalSpeed

satelliteNowX = 0.0
satelliteNowY = 0.0
satelliteNowZ = 0.0

#pluto
plutoRadius = 2.0
plutoDistance_a = 35.0
plutoDistance_b = 45.0

plutoRevolveAngle = 0.0
plutoRevolveSpeed = earthRevolveSpeed / 4
plutoRotateAngle = 0.0
plutoRotateSpeed = 180.0 * globalSpeed

plutoColor = [1,1,0]

plutoNowZ = 0.0
plutoNowX = 0.0

plutoYear = 0
plutoDay = 0


내용