×
Namespaces

Variants
Actions
Revision as of 16:27, 8 December 2008 by seppo_fn (Talk | contribs)

Using M3G Animation in Java ME

From Nokia Developer Wiki
Jump to: navigation, search


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: seppo_fn (08 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

import java.io.IOException;
 
import java.util.Timer;
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.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.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;
 
 
/**
*
*/

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() {
// Set up the user interface.
AnimationMIDlet.instance = this;
canvas3d = new Canvas3D();
iTimer = new Timer();
 
}
 
/**
* Returns a display instance.
* @return the display instance.
*/

public Display getDisplay () {
return Display.getDisplay(this);
}
 
/**
* 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 );
}
 
/**
* 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. */
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{
/**
* 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
*/

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

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();
}
 
/**
* 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();
}
}
/**
* 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;
}
/**
*
* @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

215 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×