This tutorial is continuation of "Matrix 2d" where i will show simple 2d solar system simulations using matrices

1. Astral objects

We will define 4 Astral objects:
- Star
- Planet
- Satellite
- Orbit

All astral object will have only one point that define their position in their local space. For this simple example all astral object will be located in their own center of local space, this mean that all astral object will hold only one point (0.0, 0.0, 1.0) except star that will be defined by 6 points.

Orbit will be wrapper for one Astral object, and will move child local space inside orbit space, this will actually move it's child without them knowing about this operation. This works just like in real live, planet is rotating around sun but people on earth do not know about this movement.


public abstract class AstralObject {
    protected final float size;
    protected final int color;
    protected Matrix modelMatrix;
    protected Matrix modelViewMatrix;

    protected float[] center = new float[] { 0.0f, 0.0f, 1.0f };
    protected float[] out = new float[] {0.0f, 0.0f, 0.0f };

    public AstralObject(float size, int color) {
        this.size = size;
        this.color = color;
        this.modelMatrix = new Matrix();
        this.modelViewMatrix = new Matrix();
    }

    public abstract void drawImpl(Graphics2D g2, Matrix modelViewMatrix);

    public abstract void update(float dt);

    public void draw(Graphics2D g2, Matrix parentMatrix) {
        calculateModelViewMatrix(parentMatrix);
        drawImpl(g2, modelViewMatrix);
    }

    public Matrix calculateModelViewMatrix(Matrix viewMatrix) {
        Matrix.mul(viewMatrix, modelMatrix, modelViewMatrix);
        return modelViewMatrix;
    }

}

Abstract object holds information about :
- size and object color
- model matrix - this is local matrix it will translate/rotate current astral abject and all child with it.
- modelViewModel - this is buffer for merged model matrix and parent matrix

Astral object will have 4 methods:
- draw - this method will merge model matrix with it parent matrix, after that it will invoke abstract drawImpl with merged matrix.
- update - this method will be executed by timer with time difference between current call and previous call
- calculateModelViewMatrix this method do actual matrix merging

1.1 Natural Satellite

First we will look at Natural satellite model, this model will rotate little of center using modelMatrix.
We will rotate two point, center and shifted. First point will only be used to draw expected rotation, second point will be our actual satellite local position.


@Override
public void update(float dt) {
    rotate += 2 * 3.14 * rotateSpeed * dt / (360 * 1000.0f);
    modelMatrix.setRotate(rotate);
}

Update method will modify total rotation angle, and setup our local model matrix with this rotation.
Our abstract draw method will merge this local matrix to global vie model matrix

Drawing method will calculate center point in global position and draw magenta circle that will represent expected shifted rotation. Radius of this circle will br equal to our shift vector length.


// apply view matrix to center point and store it in out array
modelViewMatrix.mulVector(center, 0, out , 0, 1);

// extract x and y, and cast size to int.
int x = (int)out[0];
int y = (int)out[1];
int hs = (int)size;

// draw arc, radius for local shift is equal to hals object size
g2.setColor(Color.MAGENTA);
g2.drawArc(x - hs/2, y - hs/2, hs, hs, 0, 360);

Next step draw actual satellite :


// apply view matrix to shifted point,
modelViewMatrix.mulVector(localShift, 0, out , 0, 1);

// extract and cast x,y to int
x = (int)out[0];
y = (int)out[1];

// draw rectangle using (x,y) as cater point
g2.setColor(new Color(color));
g2.fillRect(x - hs /2, y - hs / 2, hs, hs);

1.2 Orbit

This object will wrap any AstralObject and append view model matrix with it "orbit matrix" before passing it to wrapped astral object.


    private AstralObject object;

    private float angle;
    private float angleSpeed;

    private Matrix shift;
    private Matrix rotation;

This is additional data used by Orbit object:
- object - wrapped astral object
- angle / angleSpeed - this is current orbit angle and its angular speed
- shift matrix - this is orbit radius vector (object position for angle 0)
- rotation matrix - matrix created from "angle" it will represent orbit rotation

Shift and Rotation matrix merged together will produce Model Matrix, shift matrix is constant and equal to initial object shift( 0, radius)


    public void update(float dt) {
        angle += angleSpeed * dt / 1000.0f;
        rotation.setRotate(angle);

        Matrix.mul(rotation, shift, modelMatrix);

        this.object.update(dt);
    }

First we update angle and then refresh rotation matrix, then we merge rotation with shift and create modelMatrix.
Next we will draw orbit, to do this we have to use only parent matrix, to do this we override "draw" method and before executing old implementation that will merge modelMatrix with parentView Matrix we will draw orbit.


   @Override
    public void draw(Graphics2D g2, Matrix parentMatrix) {
        g2.setColor(new Color(color));

        int radius = (int)size/2;

        // parentMatrix - matrix before added modelMatrix (orbit matrix)
        parentMatrix.mulVector(center, 0, out , 0, 1);

        int x = (int)out[0];
        int y = (int)out[1];

        g2.drawArc(x - radius, y - radius, (int)radius * 2, (int)radius * 2, 0, 360);

        // this method will merge orbit matrix and pass result to drawImpl
        super.draw(g2, parentMatrix);
    }

Now we will merge matrix and pass it to wrapped astral object.


    @Override
    public void drawImpl(Graphics2D g2, Matrix modelViewMatrix) {
        object.draw(g2, modelViewMatrix);
    }

DrawImpl have merged model and view matrix as a second parameter, we have to only pass this value to wrapped object.

1.3 Plane.

This object have list of Orbits, all it doest is draw itself and pass method drawImpl and update method to orbits.


  @Override
    public void drawImpl(Graphics2D g2, Matrix modelViewMatrix) {
        modelViewMatrix.mulVector(center, 0, out , 0, 1);
        int x = (int)out[0];
        int y = (int)out[1];

        g2.setColor(new Color(color));
        int hs = (int)size;
        g2.fillArc(x - hs /2, y - hs / 2, hs, hs, 0, 360);

        // pass drawing to orbits
        for(Orbit orbit : satellite) {
            orbit.draw(g2, modelViewMatrix);
        }
    }

    @Override
    public void update(float dt) {
        // pass update to orbits
        for(Orbit orbit : satellite) {
            orbit.update(dt);
        }
    }

1.4 Star

This object works like planet it holds list of orbits and pass methods to them but it also show "complex" model rotation. We will draw and rotate whole solar system and star model.

First we define our model when rotation angle is 0.


    // this angle is only used to generate model, star will have every point 60 degree from each other
    float angle = 60.0f / 360.0f;
    angle = 2.0f * 3.14f * angle;

    // 6 points (x,y w)
    starPoints = new float[] {
        0.0f, -size / 2f, 1.0f,
        size * (float)Math.sin(angle) / 2.0f, size * (float)Math.cos(angle) / 2.0f, 1.0f,
        size * (float)Math.sin(-angle) / 2.0f,  size * (float)Math.cos(-angle) / 2.0f, 1.0f,

        0.0f, size / 2f, 1.0f,
        size * (float)Math.sin(angle) / 2.0f, -size * (float)Math.cos(angle) / 2.0f, 1.0f,
        size * (float)Math.sin(-angle) / 2.0f, -size * (float)Math.cos(-angle) / 2.0f, 1.0f,

   };

   // output buffer (model after applying matrix)
   starPointsOut = new float[3 * 6];

Drawing method is simple


   @Override
    public void drawImpl(Graphics2D g2, Matrix modelViewMatrix) {
        modelViewMatrix.mulVector(starPoints, 0, starPointsOut, 0, 6);

        g2.setColor(new Color(color));
        g2.fillPolygon(new int[] {
                (int)starPointsOut[0], (int)starPointsOut[3], (int)starPointsOut[6]
        },new int [] {
                (int)starPointsOut[1], (int)starPointsOut[4], (int)starPointsOut[7]
        }, 3);

        g2.fillPolygon(new int[] {
                (int)starPointsOut[9], (int)starPointsOut[12], (int)starPointsOut[15]
        },new int [] {
                (int)starPointsOut[10], (int)starPointsOut[13], (int)starPointsOut[16]
        }, 3);

        for(Orbit orbit : satellite) {
            orbit.draw(g2, modelViewMatrix);
        }
    }

first we apply modelViewMatrix to star model, output is in startPointOut, after that we draw two overlapped triangles using fillPolygon method. This method use two separate array, firs have only "x" values, second only "y". Next we pass drawing to our orbits with modelViewMatrix so orbits will rotate with solar system. If orbit drawing was moved to draw(...) only star would rotate.

update methods is the same as in planets:


    @Override
    public void update(float dt) {
        rotate += 2 * 3.14 * rotateSpeed * dt / (360 *1000.0f);
        modelMatrix.setRotate(rotate);

        for(Orbit orbit : satellite) {
            orbit.update(dt);
        }
    }

Last part is to define our solar system :



    // create first green planet with size of 15 and add two natural satellites
    // first satellite have size of 12, starting rotation 45 degree, full rotation in 5 seconds, radius 50, rotating clockwise
    // Second satellite have size of 8, starting rotation 30, full rotation in 2 second radius 20, rotating counter clockwise
    Planet planet1 = new Planet(15.0f, 0x009900);
    planet1.addNaturalSatellite(45.f, 360.0f/5f, 50.0f, 12.0f);
    planet1.addNaturalSatellite(30.f, -360.0f/2f, 20.0f, 8.0f);

    // create second blue planet size 60 with one natural satellite
    // Natural satellite start at -30 degree, full rotation in 10 seconds, radius 60, size 15 rotating clockwise
    Planet planet2 = new Planet(60.0f, 0x000099);
    planet2.addNaturalSatellite(-30.f, 360.0f/10f, 60.0f, 15.0f);

    // create third red planet size 30 with one natural satellite
    // Natural satellite start at -30 degree, full rotation in 10 seconds, radius 50, size 15 rotating clockwise
    Planet planet3 = new Planet(30.0f, 0x990000);
    planet3.addNaturalSatellite(-30.f, 360.0f/10f, 50.0f, 15.0f);

    // create star and add three planets
    // first(green) start at 0 degree, rotate in 15 seconds, radius 100, rotate clockwise
    // second(blue) start at 220 degree, rotate in 30 seconds, radius 220, rotate counter clockwise
    // third(red) start at 120 degree, rotate in 20 seconds, radius 350, rotate clockwise
    star = new Star(80.0f);
    star.addPlanet(0.0f, 360/15.0f, 100.0f, planet1);
    star.addPlanet(220.0f, -360/30.0f, 220.0f , planet2);
    star.addPlanet(120.0f, 360/20.0f, 350.0f, planet3);

Tagi

Najnowsze
C++
Java
Android
SSH