1

I want to make simple solar system in OpenGL, with four cameras.

What I want is simple, just locate a camera at earth's one side.

In follow code, I get MODELVIEW_MATRIX by glGetFloatv(GL_MODELVIEW_MATRIX) (line 116)

So I thought that { MODELVIEW_MATRIX multiple [[0],[0],[0],[1]] matrix } get a Origin point of planet in world coordinate system.

But it doesn't work well and so I need some help.

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

import math

import numpy as np

WINDOW_WIDTH = 600
WINDOW_HEIGHT = 600
WINDOW_POSITION_X = 0
WINDOW_POSITION_Y = 0

earthRevolveAngle = 180
earthRotateAngle = 0
satelliteRevolveAngle = 180
satellitePlaneAngle = 0
plutoRevolveAngle = 180
plutoRotateAngle = 0

plutoCamera = np.array([0, 0, 0])
earthPosition = np.array([0, 0, 0])


class Camera :

    def __init__(self):  #constructor
        self.loc = np.array([0.0, 50.0,  0.0])
        self.tar = np.array([0.0, 0.0, 0.0])
        self.up  = np.array([1.0, 0.0,  0.0])
        self.right = np.array([1.0, 0.0, 0.0])
        self.dir = np.array([0.0, 0.0, -1.0])
        self.asp = 1.0
        self.fov = 70
        self.near= 0.1
        self.far = 500.0


    def setCameraLoc(self, loc):
        self.loc = loc
        self.tar = self.loc + self.dir

    def setCamera(self, loc, tar, up):
        self.loc, self.tar, self.up = loc, tar, up

        self.dir = self.tar - self.loc
        l = np.linalg.norm(self.dir)
        if l > 0.0 :
            self.dir = self.dir / l

        l = np.linalg.norm(self.up)
        if l > 0.0 :
            self.up = self.up / l

        self.right = np.cross(self.dir, self.up)


    def setLens(self, fov, asp, near, far):
        self.fov, self.asp, self.near, self.far = fov, asp, near, far

    def applyCamera(self):
        gluLookAt(self.loc[0], self.loc[1], self.loc[2],
                  self.tar[0], self.tar[1], self.tar[2],
                  self.up [0], self.up [1], self.up [2])

    def applyLens(self):
        gluPerspective(self.fov, self.asp, self.near, self.far)

    def moveForward(self, step=1.0):
        self.tar += self.dir*step
        self.loc += self.dir*step

    def zoomIn(self, step=1.0):
        self.loc += self.dir*step

    def zoomOut(self, step=1.0):
        self.loc -= self.dir*step



def drawPlanet(semiMajor, semiMinor, revolveAngle, rotateAngle, shape, slope,           axisTilt) :

    global plutoCamera, earthPosition

    a = semiMajor
    b = semiMinor

    #Orbit's slope
    glRotatef(slope, 1, 0, 0)


    #Start draw orbit
    glBegin(GL_LINE_STRIP)
    for i in range(0, 361):
        theta = 2.0 * 3.141592 * i / 360.0
        x = a*math.cos(theta)
        z = b*math.sin(theta)
        glVertex3f(x, 0, z)
    glEnd()
    #End draw orbit

    theta = 2.0 * 3.141592 * (revolveAngle%360) / 360.0
    x = a * math.cos(theta)
    z = b * math.sin(theta)


    glRotatef(revolveAngle, 0, 1, 0) 
    glTranslatef( math.sqrt( x**2 + z**2 ) , 0, 0) 


    glRotatef(rotateAngle, 0, 1, 0)
    glRotatef(axisTilt, 0, 0, 1)

    t = glGetFloatv(GL_MODELVIEW_MATRIX)

    if(shape == "satellite"):
        glScalef(0.4,0.4,0.4)
        glutSolidTetrahedron()
        glScalef(2.5,2.5,2.5)

    elif(shape == "earth"):
        glutWireCube(1)
        earthPosition = t * np.matrix( [[0],[0],[0],[1]] )

    elif(shape == "pluto"):
        glScalef(0.4,0.4,0.4)
        glutWireOctahedron()
        glScalef(2.5,2.5,2.5)


def drawScene() :
    global earthRevolveAngle, earthRotateAngle, satelliteAngle, satelliteRevolveAngle, satellitePlaneAngle, plutoRevolveAngle, plutoRotateAngle


    # draw solar
    glColor3f(1,0,0)
    glutWireSphere(1.0, 20, 20)

    glPushMatrix()


    # draw earth
    glColor3f(0,0.5,1.0)
    earthRevolveAngle+=0.05 # earth's revolution
    earthRotateAngle+=0.2
    drawPlanet(5, 5, earthRevolveAngle, earthRotateAngle, "earth",0,15)


    # draw satellite

    glColor3f(0.7,0.7,0.7)
    satelliteRevolveAngle+=1.5
    satellitePlaneAngle += 0.25
    glRotatef(satellitePlaneAngle, 1, 0, 0)
    drawPlanet(1, 1, satelliteRevolveAngle, 1, "satellite",0,0)


    # draw pluto
    glPopMatrix() # comeback to solar central coordinate
    glPushMatrix()

    glColor3f(0.9,0.7,0.26)
    plutoRevolveAngle+=0.0125 # pluto's revolution
    plutoRotateAngle+=0.1 # pluto's rotation
    drawPlanet(10, 8, plutoRevolveAngle,plutoRotateAngle, "pluto",0,0)
    glPopMatrix()

Cam = Camera()

def disp() :

    global plutoCamera, earthPosition, Cam

    # reset buffer
    glClear(GL_COLOR_BUFFER_BIT)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    # Camera view setting
    Cam.setLens(30,1.0,0.1,1000)
    Cam.applyLens()


    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()



    # first quadrant
    glViewport(int(WINDOW_POSITION_X+WINDOW_WIDTH/2), int(WINDOW_POSITION_Y + WINDOW_HEIGHT/2), int(WINDOW_WIDTH/2), int(WINDOW_HEIGHT/2)) 
    glPushMatrix()
    Cam.setCamera( np.array([0,0,1]), np.array([0,0,100]), np.array([0,1,0]))
    Cam.applyCamera()

    drawScene()
    glPopMatrix()



    # second quadrant
    glViewport(int(WINDOW_POSITION_X), int(WINDOW_POSITION_Y + WINDOW_HEIGHT/2), int(WINDOW_WIDTH/2), int(WINDOW_HEIGHT/2) ) 
    glPushMatrix()
    Cam.setCamera( np.array([30,30,30]), np.array([0,0,0]), np.array([0,1,0]))
    Cam.applyCamera()

    drawScene()
    glPopMatrix()



    # third quadrant
    glViewport(WINDOW_POSITION_X, WINDOW_POSITION_Y, int(WINDOW_WIDTH/2) , int(WINDOW_HEIGHT/2) )
    glPushMatrix()
    Cam.setCamera( plutoCamera, np.array([0,0,0]), np.array([0,1,0]))
    Cam.applyCamera()
    drawScene()
    glPopMatrix()


    # fourth quadrant
    glViewport(int(WINDOW_POSITION_X+WINDOW_WIDTH/2), WINDOW_POSITION_Y, int(WINDOW_WIDTH/2), int(WINDOW_HEIGHT/2) ) 
    glPushMatrix()
    Cam.setCamera( earthPosition, np.array([0,0,0]) , np.array([0,1,0]))
    Cam.applyCamera()
    drawScene()
    glPopMatrix()

    glFlush()


def main():
    # 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_201624489_ParkChangHae")

    glClearColor(0, 0.0, 0.0, 0)

    # register callbacks
    glutDisplayFunc(disp)
    glutIdleFunc(disp)

    # enter main infinite-loop
    glutMainLoop()

if __name__=="__main__":
    main()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Park changhae
  • 59
  • 1
  • 6
  • take a look at [Is it possible to make realistic n-body solar system simulation in matter of size and mass?](https://stackoverflow.com/a/28020934/2521214) for some inspiration... – Spektre Nov 05 '18 at 10:45

1 Answers1

0

The * operator doesn't do what you expect it to do, it is an array multiplication, but not a matrix multiplication. It would perform a componentwise multiplication of the elements. See how does multiplication differ for NumPy Matrix vs Array classes? and Numerical operations on arrays.

Use numpy.dot or numpy.matmul to transform a vector by a matrix.
The result of the transformation of a 4 component vector (Homogeneous coordinates) by 4*4 matrix, is still a 4 component vector. In general you would have to do a perspective divide after the transformation. But the model view matrix is an Orthogonal matrix, so it is sufficient to use the first 3 components of the result, since the 4th component is always 1:

pos = np.array( [0,0,0,1] )
pos = np.dot( pos, t )
earthPosition = pos[0:3]

But note, the view space position of the coordinate (0, 0, 0, 1) is the translation part (the 4th row) of the model view matrix:

earthPosition = t[3][0:3]

Sadly this is not what you want to do, because you want to know the world position of the earth, but not the view position. Since glGetFloatv(GL_MODELVIEW_MATRIX) returns the model view matrix, the transformation calculates the view position, but not the world position. You have to transform by the model matrix, but not the model view matrix. Since you can't separated model matrix from the model view matrix, this is not that easy. What you can get get is the view matrix. With the view matrix and the model view matrix you van get the world position. A transformation by the model matrix is the same as a transformation by the model view matrix and the inverse view matrix:

p_world = inverse(view_matrix) * model_view_matrix * p_model

I recommend to get the view matrix and to calculate the inverse model view matrix in the Cam class right after setting it by lookAt. The inverse matrix can be calculated by numpy.linalg.inv:

def applyCamera(self):
    gluLookAt(self.loc[0], self.loc[1], self.loc[2],
              self.tar[0], self.tar[1], self.tar[2],
              self.up [0], self.up [1], self.up [2])
    self.viewmat = glGetFloatv(GL_MODELVIEW_MATRIX)
    self.inv_viewmat = np.linalg.inv(self.viewmat)

Finally the world position is a simple transformation of the 4th row of the model view matrix by the inverse view matrix:

global plutoCamera, earthPosition, Cam

.....

model_view = glGetFloatv(GL_MODELVIEW_MATRIX)

if(shape == "satellite"):
    glScalef(0.4,0.4,0.4)
    glutSolidTetrahedron()
    glScalef(2.5,2.5,2.5)

elif(shape == "earth"):
    glutWireCube(1)

    pos = np.dot( model_view[3], Cam.inv_viewmat )
    earthPosition = pos[0:3]

elif(shape == "pluto"):
    glScalef(0.4,0.4,0.4)
    glutWireOctahedron()
    glScalef(2.5,2.5,2.5)

    pos = np.dot( model_view[3], Cam.inv_viewmat )
    plutoCamera = pos[0:3]

Preview:

Rabbid76
  • 202,892
  • 27
  • 131
  • 174