Rotating a sphere around another using OpenGL ES o

2019-08-01 17:14发布

问题:

I have the following problem: I'm trying to create a solar system in OpenGL ES on Android. I already have some spheres. Now I want to make the spheres rotate in an orbit around the biggest sphere in the center (like the planets rotate around the sun). How do I do that? The rotation of a sphere around itself works, but I don't know how to make it rotate around another sphere.

Also, I have a problem with colors. There must be a problem somewhere in my code, because all my "planets" are grey. I tried to make the sun red, but it sticks to grey and I don't know why. I used gl.glColor4f and that should work, but it doesn't.

Please help, this is VERY urgent, because I need the thing running by Monday, the 28th of January, because this is a program I'm doing for my studies and I really have no idea how to fix these problems. Please help. Thank you.

Here is my code:

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;

import my.pack.graphics.primitives.Sphere;

import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.widget.Toast;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;

public class GLES06 extends Activity {
    private GLSurfaceView touchableGLSurfaceView;

    private final int MENU_RESET = 1, MENU_PAN = 2, MENU_ZOOM = 3;
    private final int GROUP_DEFAULT = 0, GROUP_PAN = 1, GROUP_ZOOM = 2;
    private boolean PAN = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    touchableGLSurfaceView = new TouchableGLSurfaceView(this);
    setContentView(touchableGLSurfaceView);
    touchableGLSurfaceView.setFocusableInTouchMode(true);
    touchableGLSurfaceView.requestFocus();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    menu.add(GROUP_DEFAULT, MENU_RESET, 0, "Reset");
    menu.add(GROUP_PAN, MENU_PAN, 0, "Pan");
    menu.add(GROUP_ZOOM, MENU_ZOOM, 0, "Zoom");
    return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
    if (PAN) {
        menu.setGroupVisible(GROUP_PAN, false);
        menu.setGroupVisible(GROUP_ZOOM, true);
    } else {
        menu.setGroupVisible(GROUP_PAN, true);
        menu.setGroupVisible(GROUP_ZOOM, false);
    }
    return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case MENU_RESET:
            TouchableGLSurfaceView.resetViewing();
            Toast.makeText(this, "trackball reset",               
                            Toast.LENGTH_SHORT).show();
            touchableGLSurfaceView.requestRender();
            return true;
        case MENU_PAN:
            Toast.makeText(this, "panning activated", 
                            Toast.LENGTH_SHORT).show();
            PAN = true;
            TouchableGLSurfaceView.guiZoom = false;
            return true;
        case MENU_ZOOM:
            Toast.makeText(this, "zooming activated", 
                            Toast.LENGTH_SHORT).show();
            PAN = false;
            TouchableGLSurfaceView.guiZoom = true;
            return true;
    }
    return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onResume() {
    super.onResume();
    touchableGLSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
    super.onPause();
    touchableGLSurfaceView.onPause();
    }
 }

 // touchable GLSurfaceView with
 // an implementation of a virtual trackball rotation control
class TouchableGLSurfaceView extends GLSurfaceView {
    private OurRenderer ourRenderer;

    static public boolean guiZoom = true;
    // possible touch states
    final static int NONE = 0;
    final static int ROTATE = 1;
    final static int ZOOM = 2;
    final static int PAN = 3;
    int touchState = NONE;

    final static float MIN_DIST = 50;
    static int oldDistance = 0;
    static int centerX = 0, centerY = 0;
    static int oldCenterX = 0, oldCenterY = 0;

    static float EYE_DISTANCE, EYE_DISTANCE_INC;
    static float PAN_X, PAN_Y, PAN_INC;
    static float CURRENT_QUATERNION[], LAST_QUATERNION[];
    static float TRANSFORM_MATRIX[];

    static int OLD_MOUSE_X, OLD_MOUSE_Y, MOUSE_BUTTON_PRESSED;

    static int WINDOW_W = 600;
    static int WINDOW_H = 800;

    static float zNear = 1.0f, zFar = 1000.0f;

    static {
    CURRENT_QUATERNION = new float[4];
    LAST_QUATERNION = new float[4];
    TRANSFORM_MATRIX = new float[16];
    }

    public TouchableGLSurfaceView(Context context) {
    super(context);
    ourRenderer = new OurRenderer();
    setRenderer(ourRenderer);

    ourRenderer.autoRotate=true;
    setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();

    float p1x, p1y, p2x, p2y;
    // normalize mouse positions
    p1x = (2.0f * OLD_MOUSE_X - WINDOW_W) / WINDOW_W;
    p1y = (WINDOW_H - 2.0f * OLD_MOUSE_Y) / WINDOW_H;
    p2x = (2.0f * x - WINDOW_W) / WINDOW_W;
    p2y = (WINDOW_H - 2.0f * y) / WINDOW_H;

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            touchState = ROTATE;
            OLD_MOUSE_X = (int) x;
            OLD_MOUSE_Y = (int) y;
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            // secondary touch event starts: remember distance
            oldDistance = (int) calcDistance(event);
            // and midpoint
            calcMidpoint(event);
            oldCenterX = centerX;
            oldCenterY = centerY;
            if (oldDistance > MIN_DIST) {
                if (guiZoom) {
                    touchState = ZOOM;
                } else {
                    touchState = PAN;
                }
            }
            break;
        case MotionEvent.ACTION_MOVE:  
            if (touchState == ROTATE) {
                // single finger rotate
                Trackball.trackball(LAST_QUATERNION, p1x, p1y, 
                                                                         p2x, p2y);
                OLD_MOUSE_X = (int) x;
                OLD_MOUSE_Y = (int) y;
                Trackball.add_quats(LAST_QUATERNION, 
                                              CURRENT_QUATERNION, CURRENT_QUATERNION);
                requestRender();
            } else if (touchState == ZOOM) {
                // double-finger zoom, zoom depends on changing 
                                       distance
                int dist = (int) calcDistance(event);
                if (dist > MIN_DIST) {
                    if (dist > oldDistance)
                    EYE_DISTANCE -= EYE_DISTANCE_INC;
                    else if (dist < oldDistance)
                    EYE_DISTANCE += EYE_DISTANCE_INC;
                    oldDistance = dist;
                    requestRender();
                }
            } else if (touchState == PAN) {
                int dist = (int) calcDistance(event);
                calcMidpoint(event);
                if (dist > MIN_DIST) {
                    if (centerX > oldCenterX)
                    PAN_X -= PAN_INC;
                    if (centerX < oldCenterX)
                    PAN_X += PAN_INC;
                    if (centerY > oldCenterY)
                    PAN_Y += PAN_INC;
                    if (centerY < oldCenterY)
                    PAN_Y -= PAN_INC;
                    oldCenterX = centerX;
                    oldCenterY = centerY;
                    requestRender();
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            touchState = NONE;
            break;
        case MotionEvent.ACTION_POINTER_UP:
            touchState = ROTATE;
            // update touch down location for drag event to holding finger
            switch (event.getActionIndex()) {
                case 0:
                    OLD_MOUSE_X = (int) event.getX(1);
                    OLD_MOUSE_Y = (int) event.getY(1);
                    break;
                case 1:
                    OLD_MOUSE_X = (int) event.getX(0);
                    OLD_MOUSE_Y = (int) event.getY(0);
                    break;
            }
            break;
    }
    return true;
}

private float calcDistance(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return FloatMath.sqrt(x * x + y * y);
}

private void calcMidpoint(MotionEvent event) {
    centerX = (int) ((event.getX(0) + event.getX(1)) / 2);
    centerY = (int) ((event.getY(0) + event.getY(1)) / 2);
}

// the implementation of the renderer interface
private class OurRenderer implements GLSurfaceView.Renderer {

    //Declaration of some variables for our planets and moons
    private Sphere sun;
    private Sphere merkur;
    private Sphere venus;
    private Sphere erde;
    private Sphere neptun;
    private Sphere uranus;
    private Sphere saturn; 
    private Sphere jupiter;

    private long milSecPerRotation=20*1000;
    private double angle=0.0f;
    private long lastTime;

    public boolean autoRotate=true;

    public OurRenderer() {

        // Defining our planets and moons

        // spheres along z-axis with lightning
        // radius: the radius of the sphere
        // slices: the number of subdivisions along the z-axis
        // stacks: the number of subdivisions around the z-axis
        sun = new Sphere(4.0f, 10, 10);
        merkur = new Sphere(0.5f, 10,10);
        venus = new Sphere(0.9f, 10,10);
        erde = new Sphere(1.0f, 10,10);
        neptun = new Sphere(1.5f, 10,10);
        uranus = new Sphere(1.5f, 10,10);
        saturn = new Sphere(2.5f, 10,10);
        jupiter = new Sphere(3.0f, 10,10);



    }

    public void onDrawFrame(GL10 gl) {
        // the first thing to do: clear screen and depth buffer
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        // reset modelview matrix
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();

        // set ambient light color
        float model_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
        ByteBuffer bb1 = ByteBuffer.allocateDirect(model_ambient.length * 4);
        bb1.order(ByteOrder.nativeOrder());
        FloatBuffer fb1 = bb1.asFloatBuffer();
        fb1.put(model_ambient);
        fb1.position(0);
        gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, fb1);

        // set light position of LIGHT0
        float light_position[] = { 1.0f, 1.0f, 1.0f, 0.0f };
        ByteBuffer bb2 = ByteBuffer.allocateDirect(light_position.length * 4);
        bb2.order(ByteOrder.nativeOrder());
        FloatBuffer fb2 = bb2.asFloatBuffer();
        fb2.put(light_position);
        fb2.position(0);
        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, fb2);

        // enable ligth and lighting
        gl.glEnable(GL10.GL_LIGHT0);
        gl.glEnable(GL10.GL_LIGHTING);

        // manipulate modelview matrix by setting viewing transformation
        gl.glTranslatef(-PAN_X, -PAN_Y, -EYE_DISTANCE);
        Trackball.build_rotmatrix(TRANSFORM_MATRIX, CURRENT_QUATERNION);
        gl.glMultMatrixf(TRANSFORM_MATRIX, 0);

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        gl.glColor4x(65536, 0, 0, 65536);

        // create an automatic rotation
        long time = System.currentTimeMillis();
        long deltaTime = time-lastTime;
        lastTime=time;
        if (autoRotate) {
            angle = (float) (angle + 360.0 / milSecPerRotation * 
                                                                       deltaTime);
            if (angle > 360.0f)
                angle -= 360;

        }    


        //Building the planets and moons and defining position, rotation and 
                  color

        gl.glPushMatrix();
            gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
            sun.draw(gl);
        gl.glPopMatrix();
        gl.glPushMatrix();
            gl.glTranslatef(0.0f, 8.0f, 0.0f);
            gl.glRotatef((float) angle, 0.0f, 0.0f, -1.0f);
            merkur.draw(gl);
            gl.glPopMatrix();
        gl.glPushMatrix();
            gl.glTranslatef(0.0f, 16.0f, 0.0f);
            gl.glRotatef((float) angle, 0.0f, 0.0f, -1.0f);
            venus.draw(gl);
        gl.glPopMatrix();
        gl.glPushMatrix();
            gl.glTranslatef(0.0f, 24.0f, 0.0f);
            gl.glRotatef((float) angle, 0.0f, 0.0f, -1.0f);
            erde.draw(gl);
        gl.glPopMatrix();
        gl.glPushMatrix();
            gl.glTranslatef(0.0f, 32.0f, 0.0f);
            gl.glRotatef((float) angle, 0.0f, 0.0f, -1.0f);
            neptun.draw(gl);
        gl.glPopMatrix();
        gl.glPushMatrix();
            gl.glTranslatef(0.0f, 40.0f, 0.0f);
            gl.glRotatef((float) angle, 0.0f, 0.0f, -1.0f);
            uranus.draw(gl);
        gl.glPopMatrix();
        gl.glPushMatrix();
            gl.glTranslatef(0.0f, 48.0f, 0.0f);
            gl.glRotatef((float) angle, 0.0f, 0.0f, -1.0f);
            saturn.draw(gl);
        gl.glPopMatrix();
        gl.glPushMatrix();
            gl.glTranslatef(0.0f, 56.0f, 0.0f);
            gl.glRotatef((float) angle, 0.0f, 0.0f, -1.0f);
            jupiter.draw(gl);
        gl.glPopMatrix();
    }

    // resize of viewport
    // set projection matrix
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);

        float aspectRatio = (float) width / height;
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        GLU.gluPerspective(gl, 45.0f, aspectRatio, zNear, zFar);
        GLU.gluLookAt(gl, 0.0f, 0.0f, 5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 
                                                                             0.0f);
        gl.glMatrixMode(GL10.GL_MODELVIEW);

        lastTime = System.currentTimeMillis();
    }

    // creation of viewport
    // initialization of some opengl features
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        gl.glDisable(GL10.GL_DITHER);
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

        gl.glClearColor(0, 0, 0, 1);
        gl.glEnable(GL10.GL_CULL_FACE);
        gl.glShadeModel(GL10.GL_SMOOTH);
        //gl.glShadeModel(GL10.GL_FLAT);
        gl.glEnable(GL10.GL_DEPTH_TEST);

        resetViewing();
    }
}

// reset of view parameters
static void resetViewing() {
    EYE_DISTANCE = 0.5f;
    EYE_DISTANCE_INC = 0.5f;
    PAN_X = 0.0f;
    PAN_Y = 0.0f;
    PAN_INC = 0.1f;

    // trackball init
    Trackball.trackball(CURRENT_QUATERNION, 0.0f, 0.0f, 0.0f, 0.0f);
}
}

回答1:

To get the spheres to move like planets, rotate and then translate.

glPushMatrix();
  gluLookAt( ... );  // The camera transformation should affect everyone
  sun.draw(gl);

  // Draw the earth and moon
  glPushMatrix();
    glRotatef( day, 0, 1, 0 );
    glTranslatef( 0, earthRadius, 0 );
    glPushMatrix();
      glRotatef( hour, 0, 1, 0 );
      earth.draw(gl);
      glTranslatef( 0, moonRadius, 0 );
      moon.draw(gl); // the moon is geostationary, so it doesn't rotate
    glPopMatrix();
  glPopMatrix();

  // Draws mars
  glPushMatrix();
    glRotatef( martianDay, 0, 1, 0 );
    glTranslatef( 0, marsRadius, 0 );
    mars.draw(gl);
  glPopMatrix();

glPopMatrix();

This sequence of calls is often called "dependent transformations", since the actions of transformation influence transformations called later when they're inside of the same glPushMatrix/glPopMatrix block.

As for the color issue, it's because you have lighting enabled, and colors generated from lighting overwrite (within the OpenGL; not in your vertex arrays) passed in with the vertices. That's not a problem in itself, but it requires more than enabling the ambient part of the light model. Specifically, you need to specify material properties for your objects. Take a look at glMaterialf.