# Flüssige Animation



## Wolfgang Lenhard (19. Sep 2008)

Hi,
ich habe für ein kleines Spiel eine kleine Animation mit Sound zu Beginn des Programmstarts. Der Einfachheit halber lade ich eine begrenzte Zahl an Bildern vorab in BufferedImages, und zeichne diese jeweils nach ca. 80 ms der Reihe nach auf ein JLabel. Die Bilder haben eine Größe von 700 * 500. Natürlich ruckelt es noch bei einem Intervall von 80 ms merklich. Das ist nicht so schlimm, da es nur ein kleines Intro sein soll. Was mich aber darüber hinaus wundert ist, dass die Prozessorlast relativ hoch ist. Sie liegt bei einem Core2 (Windows Vista) bei etwa 30 %.
Ist der Ansatz, den ich verfolge nicht gut gewählt? Wie macht man es besser?

Viele Grüße,
   Wolfgang


----------



## 0x7F800000 (19. Sep 2008)

ka... so rein theoretisch sollte man eine voxelige volumentextur mit den animationsschritten direkt in die grafikkarte laden und von der hardware dann durch die 2D-bildebene durchziehen lassen, das geht dann rasendschnell, ist beliebig fein interpolierbar, und belastet den prozessor zu 0 prozent, da alles ja dort stattfindet, wo es soll: in der grafikkarte.

praktisch kann ich dazu nicht viel empfehlen. Frage: wieso benutzst du JLabel? dieses objekt ist doch viel zu speziell und unnötig kompliziert, um drauf irgendwas rumzumalen...


----------



## Wolfgang Lenhard (19. Sep 2008)

äähhmmmm????? Kannst Du mir einen Link oder ein Tutorial schicken? Ich verstehe gerade nur Bahnhof.


----------



## muckelzwerg (19. Sep 2008)

Er meint, dass Du die Texturen in eine "3D-Textur" zusammenstapelst und dann von "hinten nach vorn" durchblätterst.

Aber wäre es nicht wesentlich einfacher die Animation in ein Videoformat zu schreiben und dann mit einem passenden Player abzuspielen? Da würde sich zum Bleistift das Java Media Framework anbieten.
Passende Tutorials findet Google.

  --  --  muckelzwerg


----------



## 0x7F800000 (19. Sep 2008)

Volumentexturen sind aber die_lösung für animierte Sprites. Ein Video lässt sich im gegenteil dazu nicht so leicht vom Frame zu Frame interpolieren. Aber wie gesagt: dafür bräuchte man ordentlichen zugriff auf die Grafikkarte, die sowas unterstützt, mit einer Ordentlichen API, da würde ich einfach anfangen das JOGL tutorial durchzulesen, wenn ich zeit hätte...


----------



## Fancy (20. Sep 2008)

Moin,

also wenn es JoGL sein darf und die Grafikkarte der Zielplattform das hergibt, sollte das relativ simpel sein:


```
package fancy.jf.texture;

import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.nio.FloatBuffer;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.BufferUtil;
import com.sun.opengl.util.FPSAnimator;
import com.sun.opengl.util.texture.TextureData;
import com.sun.opengl.util.texture.TextureIO;

public class Tex3D implements GLEventListener {
    
    // for full screen plane
    private static final float[] staticVertices = new float[] {
         1.0f,  1.0f, -1.0f, 
        -1.0f,  1.0f, -1.0f,   
        -1.0f, -1.0f, -1.0f,  
         1.0f,  1.0f, -1.0f, 
        -1.0f, -1.0f, -1.0f,  
         1.0f, -1.0f, -1.0f};
    
    // texture cords for full screen plane
    private static final float[] staticTexCord = new float[] {
         1.0f,  0.0f,  0.0f, 
         0.0f,  0.0f,  0.0f,  
         0.0f,  1.0f,  0.0f, 
         1.0f,  0.0f,  0.0f, 
         0.0f,  1.0f,  0.0f,   
         1.0f,  1.0f,  0.0f};

    
    private GL                   gl                  = null;

    private FloatBuffer          vertexArrayVertices = null;
    private FloatBuffer          vertexArrayTexture  = null;


    @Override
    public void init(GLAutoDrawable drawable) {

        gl = drawable.getGL();

        // setup vertex array
        vertexArrayVertices = BufferUtil.newFloatBuffer(staticVertices.length);
        vertexArrayVertices.put(staticVertices, 0, staticVertices.length);
        vertexArrayVertices.rewind();

        vertexArrayTexture = BufferUtil.newFloatBuffer(staticTexCord.length);
        vertexArrayTexture.put(staticTexCord, 0, staticTexCord.length);
        vertexArrayTexture.rewind();

        gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);

        // setup 3D texture
        final int[] handleTex3DBuffer = new int[1];
        gl.glGenTextures(1, handleTex3DBuffer, 0);
        final int handleTex3D = handleTex3DBuffer[0];

        gl.glBindTexture(GL.GL_TEXTURE_3D, handleTex3D);
        gl.glTexParameteri(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_R, GL.GL_REPEAT);

        try {
            final TextureData data = TextureIO.newTextureData(getClass().getResource("/3d.jpg"), false, null);
            gl.glTexImage3D(GL.GL_TEXTURE_3D, 0, GL.GL_RGB8, 500, 500, 10, 0, GL.GL_BGR, GL.GL_UNSIGNED_BYTE, data.getBuffer());
        } catch (final IOException e) {
            e.printStackTrace();
        }

        gl.glEnable(GL.GL_TEXTURE_3D);

    }


    @Override
    public void display(GLAutoDrawable drawable) {

        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl.glLoadIdentity();

        // walk through texture
        for (int i = 0; i < 6; i++)
            vertexArrayTexture.put(i * 3 + 2, vertexArrayTexture.get(i * 3 + 2) + 0.001f);

        // draw vertex array with current texture part
        gl.glTexCoordPointer(3, GL.GL_FLOAT, 0, vertexArrayTexture);
        gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertexArrayVertices);
        gl.glDrawArrays(GL.GL_TRIANGLES, 0, staticVertices.length);

    }


    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {

        if (height <= 0) height = 1;

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }


    @Override
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}


    public static void main(String[] args) {

        final Frame frame = new Frame();
        final GLCanvas canvas = new GLCanvas();
        final Animator animator = new FPSAnimator(canvas, 60, true);
        canvas.addGLEventListener(new Tex3D());
        frame.add(canvas);
        frame.setSize(500, 500);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                new Thread(new Runnable() {
                    public void run() {
                        animator.stop();
                        System.exit(0);
                    }
                }).start();
            }
        });
        frame.setVisible(true);
        animator.start();
    }

}
```

Für das Eingangsformat ist Zeile 77 – 78 maßgeblich. Alle Frames werden als eine große Bilddatei geladen, dazu müssen die einzelnen Frames untereinander angeordnet werden. Das Beispiel oben erwartet eine Auflösung von 500 x 500 Pixel pro Frame bei 10 Frames, dementsprechend muss in diesem Fall die geladene Bilddatei 500 x 5000 Pixel groß sein.

Die 0.001f in Zeile 96 gibt an wie schnell durch den Bildstapel geblendet werden soll.

Der Rest ist JoGL Standart.

Gruß,
Michael


----------



## Wolfgang Lenhard (20. Sep 2008)

Vielen Dank für die Postings. Das hilft mir sehr weiter.

Ok, noch eine Frage in Bezug auf das Code-Beispiel: Wie bekomme ich die 10 Bilder in das TextureData-Object?


----------



## muckelzwerg (20. Sep 2008)

Bei glTexImage3d gibst Du das Bild als einfache DataBuffer an.
"data.getBuffer()".
Den Buffer kannst Du mit seinen eigenen Methoden auch selbst füllen, anstatt ihn direkt aus einem einzelnen Bild zu erzeugen.
Du kannst also die Bilder einzeln laden, und dann in einen Buffer zusammenkopieren, wenn Du sie nicht in einem Bild speichern willst.
Ist dann auch etwas angenehmer und flexibler.

  --  --  muckelzwerg


----------



## Wolfgang Lenhard (21. Sep 2008)

Danke, hab's hingekriegt.


----------

