×
Namespaces

Variants
Actions
(Difference between revisions)

Using M3G Animation in Java ME

From Nokia Developer Wiki
Jump to: navigation, search
seppo_fn (Talk | contribs)
IlGolub (Talk | contribs)
Line 28: Line 28:
  
 
<code java>
 
<code java>
import java.io.IOException;
+
//most part of code is the same as in "Using M3G to Draw in Java ME"
+
//with some differences
 +
 
 
import java.util.Timer;
 
import java.util.Timer;
 
import java.util.TimerTask;
 
import java.util.TimerTask;
 +
 +
...
 
   
 
   
import javax.microedition.lcdui.Canvas;
 
import javax.microedition.lcdui.Command;
 
import javax.microedition.lcdui.CommandListener;
 
import javax.microedition.lcdui.Display;
 
import javax.microedition.lcdui.Displayable;
 
import javax.microedition.lcdui.Graphics;
 
import javax.microedition.lcdui.Image;
 
 
import javax.microedition.midlet.MIDlet;
 
 
import javax.microedition.m3g.AnimationController;
 
import javax.microedition.m3g.AnimationController;
 
import javax.microedition.m3g.AnimationTrack;
 
import javax.microedition.m3g.AnimationTrack;
import javax.microedition.m3g.Appearance;
 
import javax.microedition.m3g.Background;
 
import javax.microedition.m3g.Camera;
 
import javax.microedition.m3g.Graphics3D;
 
import javax.microedition.m3g.Image2D;
 
import javax.microedition.m3g.IndexBuffer;
 
 
import javax.microedition.m3g.KeyframeSequence;
 
import javax.microedition.m3g.KeyframeSequence;
import javax.microedition.m3g.Light;
+
 
import javax.microedition.m3g.Material;
+
...  
import javax.microedition.m3g.Mesh;
+
 
import javax.microedition.m3g.Texture2D;
+
import javax.microedition.m3g.Transform;
+
import javax.microedition.m3g.TriangleStripArray;
+
import javax.microedition.m3g.VertexArray;
+
import javax.microedition.m3g.VertexBuffer;
+
+
+
 
/**
 
/**
 
  *  
 
  *  
Line 82: Line 63:
 
     */
 
     */
 
     public AnimationMIDlet() {
 
     public AnimationMIDlet() {
         // Set up the user interface.  
+
         ...
        AnimationMIDlet.instance = this;
+
        canvas3d = new Canvas3D();
+
 
         iTimer = new Timer();
 
         iTimer = new Timer();
 
    }
 
 
    /**
 
    * Returns a display instance.
 
    * @return the display instance.
 
    */
 
    public Display getDisplay () {
 
        return Display.getDisplay(this);
 
 
     }
 
     }
 +
 +
    ...
 
   
 
   
 
     /**
 
     /**
Line 108: Line 80:
 
     }
 
     }
 
   
 
   
     /**
+
     ...
    * From MIDlet.
+
    * Called when MIDlet is paused.
+
    */
+
    public void pauseApp() {
+
        //Empty implementation
+
    }
+
+
    /**
+
    * From MIDlet.
+
    * Called to signal the MIDlet to terminate.
+
    * @param unconditional if true, then the MIDlet has to be unconditionally
+
    * terminated and all resources has to be released.
+
    */
+
    public void destroyApp(boolean unconditional) {
+
        //Empty implementation
+
    }   
+
   
+
    /**
+
    * Quit the MIDlet.
+
    */
+
    public static void quitApp() {
+
        instance.destroyApp(true);
+
        instance.notifyDestroyed();
+
        instance = null;
+
    }
+
 
   
 
   
 
     /** * Our timer task for providing animation. */  
 
     /** * Our timer task for providing animation. */  
Line 151: Line 98:
 
     */
 
     */
 
     class Canvas3D extends Canvas implements CommandListener{
 
     class Canvas3D extends Canvas implements CommandListener{
         /**
+
          
        * exit midlet command
+
         ...
         */
+
 
        private Command exitCommand;
+
+
        /**
+
        * Reference to 3D graphics context that can be bound to a rendering
+
        * target. All rendering is done through the render methods
+
        * in this class
+
        */
+
        private Graphics3D iG3D;
+
        /**
+
        * Defines the position of the viewer in the scene and
+
        * the projection from 3D to 2D.  
+
        */
+
        private Camera iCamera;
+
        /**
+
        * Represents different kinds of light sources.
+
        */
+
        private Light iLight;
+
        /**
+
        * Defines whether and how to clear the viewport. It can be
+
        * image or color for solid filling.
+
        */
+
        private Background iBackground;
+
 
         /**
 
         /**
 
         * absolute counter of animation in world units
 
         * absolute counter of animation in world units
Line 184: Line 109:
 
         */
 
         */
 
         Mesh iAnimatedMesh;
 
         Mesh iAnimatedMesh;
       
 
        /**
 
        * Construct the Displayable.
 
        */
 
        public Canvas3D() {
 
            exitCommand = new Command("Exit", Command.EXIT, 1);
 
 
            addCommand(exitCommand);
 
           
 
            // set up this Displayable to listen to command events
 
            setCommandListener(this);
 
 
            initialize();
 
        }
 
 
   
 
   
 
         /**
 
         /**
Line 276: Line 187:
 
             }  
 
             }  
 
         }
 
         }
        /**
+
 
        * Creating a Material object
+
         ...
        * @param diffuseColor is specifying that the diffuse color component
+
 
        * is to be set to a Material object
+
        * @param specularColor is specifying that the specular color component
+
        * is to be set to a Material object
+
        * @param shininess is the specular exponent term in the
+
        * lighting equation
+
        * @return the prepeared Material object
+
        */
+
         private Material materialInitialization(int diffuseColor,
+
                int specularColor, float shininess) {
+
            Material material = new Material();
+
            try{
+
                //set color for Material object. if
+
                material.setColor(Material.DIFFUSE, diffuseColor);
+
                material.setColor(Material.SPECULAR, specularColor);
+
                material.setShininess(shininess);
+
            }catch(Exception e) {
+
                //TODO: write handler code
+
            }
+
            return material;
+
        }
+
        /**
+
        * Method creates a new Background object with image instead of
+
        * background color
+
        * @param imageFileName is the name of image file
+
        * @return the prepeared Background object
+
        */
+
        private Background backgroundInitialization(String imageFileName) {
+
            Background background = new Background();
+
            try {
+
                // loading image for background with the same approach as
+
                //texture image
+
                Image backImage = null;
+
                // load the image for the texture
+
                backImage = Image.createImage(imageFileName);
+
                Image2D backgroundImage2D = new Image2D( Image2D.RGB, backImage);
+
                background.setImageMode(Background.BORDER, Background.BORDER);
+
                background.setImage(backgroundImage2D);
+
            }catch(Exception e) {
+
                //TODO: write handler code
+
            }
+
            return background;
+
        }
+
        /**
+
        * Creates a camera object with perspective projection
+
        * @param angleOfView is the camera field of view
+
        * @return the prepeared Camera object
+
        */
+
        private Camera cameraInitialization(float angleOfView) {
+
            Camera camera = new Camera();
+
            try{
+
                camera.setPerspective( angleOfView,
+
                    (float)getWidth()/ (float)getHeight(), // aspectRatio
+
                    1.0f, // near clipping plane
+
                    1000.0f ); // far clipping plane
+
            }catch(Exception e) {
+
                //TODO: write handler code
+
            }
+
            return camera;
+
        }
+
        /**
+
        * Creates a light source object
+
        * @param color is the color component of light
+
        * @param lightType is the type of light source(like SPOT, OMNI and
+
        * others)
+
        * @return the prepeared Light object
+
        */
+
        private Light lightInitialization(int color, int lightType) {
+
            // create a light object
+
            Light light = new Light();
+
            try{
+
                // white light. Later this parameter will be animated
+
                light.setColor(color);
+
                //Sets the intensity of light
+
                light.setIntensity(1.0f);
+
                //sets the spot type for this light
+
                light.setMode(lightType);
+
                //sets the spot exponent, that controls the distribution of the
+
                //intensity of light within the spot cone
+
                light.setSpotExponent(1.0f);
+
                //sets the spot angle
+
                light.setSpotAngle(45);
+
            }catch(Exception e) {
+
                //TODO: write handler code
+
            }
+
            return light;
+
        }
+
        /**
+
        * Creates Cube Mesh
+
        * @param material is the Appearance component of new Mesh
+
        * @return the prepeared Mesh object
+
        */
+
        private Mesh сubeMeshInitialization(Material material) {
+
            // initialize some arrays for our object (cube)
+
            // Each line in this array declaration represents a triangle
+
            // strip for one side of a cube.
+
            // 1 * * * * * 0
+
            //  * *    *
+
            //  *  *  *
+
            //  *    * *
+
            // 3 * * * * * 2
+
            // The ascii diagram above represents the vertices in the first line
+
            // (the first tri-strip)
+
            short[] vert = {
+
                10, 10, 10, -10, 10, 10, 10,-10, 10, -10,-10, 10, // front
+
                -10, 10,-10, 10, 10,-10, -10,-10,-10, 10,-10,-10, // back
+
                -10, 10, 10, -10, 10,-10, -10,-10, 10, -10,-10,-10, // left
+
                10, 10,-10, 10, 10, 10, 10,-10,-10, 10,-10, 10, // right
+
                10, 10,-10, -10, 10,-10, 10, 10, 10, -10, 10, 10, // top
+
                10,-10, 10, -10,-10, 10, 10,-10,-10, -10,-10,-10 }; // bottom
+
            // create a VertexArray to hold the vertices for the object
+
            VertexArray vertArray = new VertexArray(vert.length / 3, 3, 2);
+
            vertArray.set(0, vert.length/3, vert);
+
            // normal vectors for the cube. used for lighting
+
            byte[] norm = {
+
                0, 0, 127,  0, 0, 127,  0, 0, 127,  0, 0, 127,
+
                0, 0,-127,  0, 0,-127,  0, 0,-127,  0, 0,-127,
+
                -127, 0, 0, -127, 0, 0, -127, 0, 0, -127, 0, 0,
+
                127, 0, 0,  127, 0, 0,  127, 0, 0,  127, 0, 0, 0,
+
                127, 0, 0,  127, 0, 0,  127, 0, 0,  127, 0, 0,-127,
+
                0, 0,-127, 0, 0,-127, 0, 0,-127, 0 };
+
+
            // create a vertex array for the normals of the object
+
            VertexArray normArray = new VertexArray(norm.length / 3, 3, 1);
+
            normArray.set(0, norm.length/3, norm);
+
            // the length of each triangle strip
+
            int[] stripLen = { 4, 4, 4, 4, 4, 4 };
+
           
+
            //Creating of instance of TriangleStripArray
+
            //(derived from IndexBuffer). It
+
            //represents an array of triangle strips.
+
            //In a triangle strip, the first three vertex indices define
+
            //the first triangle. Each subsequent index together with the two
+
            //previous indices defines a new triangle.
+
            // create the index buffer for our object (this tells how to
+
            // create triangle strips from the contents of the vertex buffer).
+
            IndexBuffer indexBuffer = new TriangleStripArray( 0, stripLen );
+
+
            // per vertex texture coordinates
+
            //values represents mapping of texture to vertices of 3d object
+
            short[] tex = {
+
                1, 0, 0, 0, 1, 1, 0, 1,
+
                1, 0, 0, 0, 1, 1, 0, 1,
+
                1, 0, 0, 0, 1, 1, 0, 1,
+
                1, 0, 0, 0, 1, 1, 0, 1,
+
                1, 0, 0, 0, 1, 1, 0, 1,
+
                1, 0, 0, 0, 1, 1, 0, 1 };
+
            // create a vertex array for the texture coordinates of the object
+
            VertexArray texArray = new VertexArray(tex.length / 2, 2, 2);
+
            texArray.set(0, tex.length/2, tex);
+
            //VertexBuffer holds references to VertexArrays that contain the
+
            //positions, colors, normals, and texture coordinates for a set
+
            //of vertices create the VertexBuffer for our object
+
            VertexBuffer vertexBuffer = new VertexBuffer();
+
            vertexBuffer.setPositions(vertArray, 1.0f, null);
+
            // unit scale, zero bias
+
            vertexBuffer.setNormals(normArray);
+
            vertexBuffer.setTexCoords(0, texArray, 1.0f, null);
+
            //Texture creating
+
            Image textureImage = null;
+
            try {
+
                // load the image for the texture
+
                textureImage = Image.createImage("/texture.png");
+
            } catch (IOException ex) {
+
                ex.printStackTrace();
+
            }
+
            // create the Image2D (we need this so we can make a Texture2D)
+
            Image2D image2D = new Image2D( Image2D.RGB, textureImage );
+
            // create the Texture2D and enable mipmapping           
+
            Texture2D texture = new Texture2D( image2D );
+
            texture.setFiltering(Texture2D.FILTER_LINEAR,
+
                                Texture2D.FILTER_LINEAR);
+
            //specifying that the texture image is to be repeated only once
+
            texture.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP);
+
            // texture color is to be modulated with the lit material color           
+
            texture.setBlending(Texture2D.FUNC_MODULATE);
+
+
            // create the appearance object
+
            //It's a set of component objects that define the rendering
+
            //attributes. Using during render operation.           
+
            Appearance cubeAppearance = new Appearance();
+
            cubeAppearance.setTexture(0, texture);
+
            cubeAppearance.setMaterial(material);
+
+
            //creating Mesh object and passing as parameters
+
            //vertex, index buffers and Appearance component
+
            Mesh mesh = new Mesh(vertexBuffer, indexBuffer, cubeAppearance);
+
            return mesh;
+
        }
+
 
         /**
 
         /**
 
         *  
 
         *  

Revision as of 10:57, 11 December 2008


Article Metadata
Tested with
Devices(s): Nokia 6131, Nokia N81
CompatibilityArticle
Keywords: javax.microedition.m3g.Graphics3D, javax.microedition.m3g.Canvas, javax.microedition.m3g.Light, javax.microedition.m3g.Transform,

javax.microedition.m3g.Mesh, javax.microedition.m3g.KeyframeSequence, javax.microedition.m3g.AnimationTrack, javax.microedition.m3g.AnimationController,

javax.microedition.m3g.Object3D.addAnimationTrack(), javax.microedition.m3g.Object3D.animate()
Created: (10 Nov 2008)
Last edited: IlGolub (11 Dec 2008)

Overview

This code example represents using of Mobile 3D Graphics API for animation with KeyframeSequence, AnimationTrack and AnimationController classes.

First of all we must define what kind of animation we want to use(see animationInitialization(Light light, Mesh mesh) method). Then we create a KeyframeSequence object which consist of some keyframes with appropriate values and create AnimationTrack object with animation type defining.

Then we create a AnimationController object, tune it, add AnimationTrack objects to 3D entity(like Light, Mesh and others)

Later in paint() method we call animate function for each 3D entity and render the scene.

Source file: AnimationMIDlet.java

//most part of code is the same as in "Using M3G to Draw in Java ME"
//with some differences
 
import java.util.Timer;
import java.util.TimerTask;
 
...
 
import javax.microedition.m3g.AnimationController;
import javax.microedition.m3g.AnimationTrack;
import javax.microedition.m3g.KeyframeSequence;
 
...
 
/**
*
*/

public class AnimationMIDlet extends MIDlet {
/**
* Reference to MIDlet instance for using it inside Canvas3D
*/

static AnimationMIDlet instance;
/**
* Canvas instance using for drawing with m3g
*/

Canvas3D canvas3d;
/**
* Timer object for animation process
*/

Timer iTimer;
 
/**
* The Canvas3D constructor.
*/

public AnimationMIDlet() {
...
iTimer = new Timer();
}
 
...
 
/**
* From MIDlet.
* Called when MIDlet is started.
* Sets object from canvas3d variable as current displayable.
* Schedules a MyTimerTask timer task.
*/

public void startApp() {
Display.getDisplay(this).setCurrent(canvas3d);
iTimer.schedule( new MyTimerTask(), 0, 50 );
}
 
...
 
/** * Our timer task for providing animation. */
class MyTimerTask extends TimerTask {
/**
* From TimerTask.
*/

public void run() {
if( canvas3d != null ) {
canvas3d.repaint();
}
}
}
 
/**
* Inner class for handling the canvas.
*/

class Canvas3D extends Canvas implements CommandListener{
 
...
 
/**
* absolute counter of animation in world units
*/

int iApplicationTime;
/**
* Mesh object that represents a cube
*/

Mesh iAnimatedMesh;
 
/**
* Initialize self.
*/

void initialize() {
// get the singleton Graphics3D instance
iG3D = Graphics3D.getInstance();
 
iBackground = backgroundInitialization("/background.png");
//Material is an Appearance component encapsulating material
//attributes for lighting computations.
Material material = materialInitialization(0xFFFFFFFF,
0xFFFFFFFF, 100.0f);
iCamera = cameraInitialization(70.0f);
iLight = lightInitialization(0xFFFFFFFF, Light.SPOT);
 
try {
//creating of cube object as Mesh
iAnimatedMesh = сubeMeshInitialization(material);
 
animationInitialization(iLight, iAnimatedMesh);
}catch(Exception e) {
//TODO: write handler code
}
 
iApplicationTime = 0;
}
 
/**
* From Canvas.
* Rendering the scene
*/

protected void paint(Graphics g) {
//incrementing world time
iApplicationTime = iApplicationTime + 50;
 
// Bind the Graphics of this Canvas to Graphics3D.
//We enable depth buffer, dithering and true color rendering(if it
//supported of course)
iG3D.bindTarget(g, true, Graphics3D.DITHER | Graphics3D.TRUE_COLOR);
// clear the color and depth buffers
iG3D.clear(iBackground);
// set up the camera in the desired position
Transform transform = new Transform();
transform.postTranslate(0.0f, 0.0f, 35.0f);
iG3D.setCamera(iCamera, transform);
 
//calcuulating parameters for animation according to
//current world time
iLight.animate(iApplicationTime);
 
// update Light object
iG3D.resetLights();
iG3D.addLight(iLight, transform);
 
transform.setIdentity();
//calcuulating parameters for animation according to
//current world time
iAnimatedMesh.animate(iApplicationTime);
// In immediate mode, node transforms are ignored, so we get
// our animated transformation into a local transform object
iAnimatedMesh.getCompositeTransform(transform);
// update our transform (this will give us a rotating cube)
iG3D.render(iAnimatedMesh, transform);
 
iG3D.releaseTarget();
}
/**
* From CommandListener.
* @param command
* @param displayable
*/

public void commandAction(Command command, Displayable displayable) {
if (command == exitCommand) {
// exit the MIDlet
AnimationMIDlet.quitApp();
}
}
 
...
 
/**
*
* @param light is the Light object for color animation
* @param mesh is the Mesh object for rotation animation
*/

private void animationInitialization(Light light, Mesh mesh) {
//Sequence of keyframes for rotation of cube
//creating color KeyframeSequence object
//with 5 keyframes with 3 components in each
//and setting the intepolation type
KeyframeSequence colorKeyframes = new KeyframeSequence(5, 3,
KeyframeSequence.LINEAR);
//then setting the duration of color animation
colorKeyframes.setDuration(4000);
//this animation will play only once without repeat
colorKeyframes.setRepeatMode(KeyframeSequence.CONSTANT);
//setting index, timestamp, and components values for 5 keyframes
colorKeyframes.setKeyframe(0, 0, new float[]{1.0f, 1.0f, 1.0f});
colorKeyframes.setKeyframe(1, 1000, new float[]{1.0f, 0.0f, 0.0f});
colorKeyframes.setKeyframe(2, 2000, new float[]{0.0f, 1.0f, 0.0f});
colorKeyframes.setKeyframe(3, 3000, new float[]{0.0f, 0.0f, 1.0f});
colorKeyframes.setKeyframe(4, 4000, new float[]{1.0f, 1.0f, 1.0f});
 
//Associates a KeyframeSequence with an AnimationController and sets
//the type of animation. In this case it's color animation.
//creating AnimationTrack object for color animation
AnimationTrack colorAnimationTrack =
new AnimationTrack(colorKeyframes, AnimationTrack.COLOR);
 
//Sequence of keyframes for color transition for light
//creating color KeyframeSequence object
//with 2 keyframes and 4 components in each
//and setting the intepolation type to SLERP
//that specifies spherical linear interpolation of quaternions
KeyframeSequence rotationKeyframes =
new KeyframeSequence(2, 4, KeyframeSequence.SLERP);
//this animation will be repeating while animation inside active
//interval
rotationKeyframes.setRepeatMode(KeyframeSequence.LOOP);
rotationKeyframes.setDuration(4000);
 
//getRotationQuaternion() method converts vector and angle
//values to quaternion
rotationKeyframes.setKeyframe(0, 0,
getRotationQuaternion(0.0f, new float[] {0.0f, 1.0f, 0.0f}));
rotationKeyframes.setKeyframe(1, 4000,
getRotationQuaternion(359.0f, new float[] {0.0f, 1.0f, 0.0f}));
 
//AnimationTrack associates a KeyframeSequence with an
//AnimationController and sets the type of animation.
//In this case it's orientation animation.
//creating AnimationTrack object for orientation animation
AnimationTrack rotationAnimationTrack =
new AnimationTrack(rotationKeyframes,
AnimationTrack.ORIENTATION);
 
//creating AnimationController which
//controls the position, speed and weight of an animation sequence
AnimationController animator = new AnimationController();
//set controller for animation tracks
rotationAnimationTrack.setController(animator);
colorAnimationTrack.setController(animator);
 
//set active time interval for controller
//8000 is not necessarily in milliseconds, it's abstract world
//time
animator.setActiveInterval(0, 8000);
//set speed to half of normal
//animator.setSpeed(0.5f, 0);
//Sets a new playback position, relative to world time
//0 - desired playback position in sequence time units
//4000 - the world time at which the
//sequence time must be equal to 0
animator.setPosition(0, 4000);
 
//adding animation tracks to our objects that we
//wanting to animate
mesh.addAnimationTrack(rotationAnimationTrack);
light.addAnimationTrack(colorAnimationTrack);
}
/**
* Calculates the quaternion for a rotation of
* angle degrees about the vector.
* @param angle - is an angle of rotation round of vector
* @param vector - 3D vector of rotation
* @return quaternion of rotation
*/

private float[] getRotationQuaternion(float angle, float[] vector) {
float[] quaternion = new float[4];
double angleRadians = Math.toRadians(angle)/2.0;
//i component
quaternion[0] = vector[0] * (float)Math.sin(angleRadians);
//j component
quaternion[1] = vector[1] * (float)Math.sin(angleRadians);
//k component
quaternion[2] = vector[2] * (float)Math.sin(angleRadians);
//w scalar component
quaternion[3] = (float)Math.cos(angleRadians);
return quaternion;
}
}
}


Postconditions

As result we have a MIDlet which draws a rotated cube mesh and animated color of light source

See also

Supplementary material

You can download this MIDlet, source code and resources from here - Simple Animation.zip

150 page views in the last 30 days.