Subscribe to News

Mobile 3D Graphics API for J2ME

Author : Equizz

From TechnologicalWiki

Jump to: navigation, search
This article doesn't belong to any category. Each article must belong at least some the next categories :
- OpenSource - HowTo - Mobile - Project - ProjectSection - UnifiedCommunications - OSAmI -

Please add a valid category to the end of the article.

Contents

[edit] Introduction

Mobile 3D Graphics API for J2ME is specified in JSR 184 and included with the WTK (Wireless Toolkit) from 2.2 version. This proposed JSR will provide a scalable, small-footprint, interactive 3D API for use it on mobile devices (CLDC). This API is based on OpenGL so the concepts are very similar.

[edit] Before to start

You need to have installed WTK 2.2 or later, and you can install a framework like netbeans or eclipse, to make your work easier.

You may also use 3D modeling applications also, like 3D Studio, Maya or Blender to generate your 3D objects. The last one, is a powerful 3D modeling program, and it's open source.

[edit] 3D Structure

The Mobile 3D Graphics API for J2ME is defined in the package javax.microedition.m3g, which provides an easy-to-use API for rendering 3D graphics in retained mode and immediate mode.

In addition to the APIs, the package defines a scene graph structure and a corresponding file format for managing and deploying 3D content efficiently, along with all other necessary data: meshes, scene hierarchies, material properties, textures, animation keyframes, and so on. This data is written to a file using content-creation tools and loaded into the API through the Loader class. The most important class is Graphics3D, because all rendering is done there. The World class serves as the root of the scene graph structure. Object3D is the base class of all objects that can be rendered or loaded from a file, as well as the place where animations are applied.

[edit] Object3D

An abstract base class for all objects that can be part of a 3D world. This includes the world itself, other scene graph nodes, animations, textures, and so on. In fact, everything in this API is an Object3D, except for Loader, Transform, RayIntersection, and Graphics3D.

Animations are applied to an object and its descendants with the animate method in this class.

Every Object3D can be assigned a user ID, either at authoring stage or at run time with the setUserID method. User IDs are typically used to find a known object in a scene loaded from a data stream.

The find method searches through all objects that are reachable from this object through a chain of references, and returns the one with the given user ID. If there are multiple objects with the same ID, the implementation may return any one of them. An object is defined to be reachable from itself and from all objects that have a chain of direct references to it. The parent reference and the alignment references in a Node do not count as direct references.

Object3D has an attribute called the user object. If an Object3D is loaded from a file by the Loader, the user object may contain a Hashtable that stores byte array values keyed by Integers. This is the case when one or more user parameters are associated with the serialized Object3D. If there are no user parameters, the user object is initially set to null. A typical example of using this type of persistent user data is to include application parameters inside a scene graph, such as in multi-level games, where a non-player character may have a series of attributes such as hit strength, armor, initial health, and so on. Although it is possible to have this information in a separate file, it is neater, easier and less error prone to associate it directly with the Object3D that represents the character.

Object3D is an abstract class, and therefore has no public constructors. When a class derived from Object3D is instantiated, the attributes inherited from Object3D will have the following default values:

   * user ID : 0
   * user object : null
   * animation tracks : none


[edit] Node

We can construct objects in a tree form. The first node is a Word object where we can add childs (more nodes), and we can add more childs to those nodes.

There are five different kinds of nodes: Camera, Mesh, Sprite3D, Light and Group.

Each node defines a local coordinate system that can be transformed relative to the coordinate system of the parent node (node transformation).

[edit] Word

A special Group node that is a top-level container for scene graphs. A scene graph is constructed from a hierarchy of nodes. In a complete scene graph, all nodes are ultimately connected to each other via a common root, which is a World node. A scene graph need not be complete in order to be rendered. An example of a complete scene graph is shown in the figure below.

This is a Node, but it has two differences: A World can not be a child of any Node, and the node transformation is ignored for World objects when rendering.

The method render(World) in Graphics3D renders a World as observed by the currently active camera of that world. If the active camera is null, or the camera is not in the world, an exception is thrown. The world can still be rendered with the render(Node, Transform) method by treating the World as a Group. In that case, however, the application must explicitly clear the background and set up the camera and lights prior to rendering.

[edit] Texture

Texture2D is an Appearance component encapsulating a two-dimensional texture image and a set of attributes specifying how the image is to be applied on submeshes. The attributes include wrapping, filtering, blending, and texture coordinate transformation.

The texture image is stored as a reference to an Image2D. The image may be in any of the formats defined in Image2D. The width and height of the image must be non-negative powers of two, but they need not be equal. The maximum allowed size for a texture image is specific to each implementation, and it can be queried with Graphics3D.getProperties().

If the referenced Image2D is modified by the application, or a new Image2D is bound as the texture image, the modifications are immediately reflected in the Texture2D.

The first step in applying a texture image onto a submesh is to apply the texture transformation to the texture coordinates of each vertex of that submesh. The transformation is defined in the Texture2D object itself, while the texture coordinates are obtained from the VertexBuffer object associated with that submesh.

[edit] Nodes

If inforamoitn were soccer, this would be a goooooal!

There are no words to describe how bdociaous this is.

[edit] Sprite3D

It defines a screen-aligned 2D image with a position in 3D space.

Sprite3D is a fast, but functionally restricted alternative to textured geometry. A Sprite3D is rendered as a screen-aligned rectangular array of pixels with a constant depth. The apparent size of a sprite may be specified directly in pixels (an unscaled sprite) or indirectly using the transformation from the Sprite3D node to the camera space (a scaled sprite).

[edit] Light

It defines the position, direction, color and other attributes of a light source. There are different kind of lights:

  • An ambient light source illuminates all objects in the scene from all directions. The intensity of light coming from an ambient light source is the same everywhere in the scene. The position and direction of an ambient light source therefore have no effect.
  • A directional light source corresponds to sunlight in the real world. It illuminates all objects in the scene from the same direction, and with a constant intensity. Similar to ambient light, the position of a directional light source has no effect. The direction of the light is along the negative Z axis of the Light node's local coordinate system.
  • An omnidirectional light source, also known as a point light, casts equal illumination in all directions from the origin of the Light node's local coordinate system. The intensity of light coming from an omnidirectional light source can be attenuated with distance. The orientation of an omnidirectional Light node has no effect; only the position matters.
  • A spot light source casts a cone of light centered around the direction of its negative Z axis. The concentration of light within the cone can be adjusted with the spot exponent. The intensity of light coming from a spot light can be attenuated with distance from the source. Both the orientation and the position of the Light node have an effect with spot lights.

The RGB intensity contributed to the lighting calculation by a Light is (IR, IG, IB), where I is the intensity of the Light and (R, G, B) is its color. Note that while 1.0 is a nominal full intensity, applications may use values higher than that for more control over highlights, for example. The intensity may also be set to negative to specify an "antilight" or "dark".

Lights can be turned on and off using Node.setRenderingEnable(boolean).

[edit] Group

Itserves as a root for scene graph branches. A scene graph node that stores an unordered set of nodes as its children.

The parent-child relationship is bidirectional in the sense that if node A is a child of node B, then B is the (one and only) parent of A. In particular, the getParent method of A will return B. Besides Group nodes, this also concerns SkinnedMesh nodes: the skeleton group is the one and only child of a SkinnedMesh.

A node can have at most one parent at a time, and cycles are prohibited. Furthermore, a World node cannot be a child of any node. These rules are enforced by the addChild method in this class, as well as the constructor of SkinnedMesh.

[edit] Graphics3D

A singleton 3D graphics context that can be bound to a rendering target. All rendering is done through the render methods in this class, including the rendering of World objects. There is no other way to draw anything in this API.

public class MyCanvas extends Canvas {
    Graphics3D myG3D = Graphics3D.getInstance();

    public void paint(Graphics g) {
        try {
            myG3D.bindTarget(g);
            // ... update the scene ...
            // ... render the scene ...
        } finally {
            myG3D.releaseTarget();
    }
}

If ifnoramtion were soccer, this would be a goooooal!

[edit] Paint loop

The paint method is called by MIDP after the application has issued a repaint request. We draw a new frame on this Canvas by binding the current Graphics object as the target, then rendering, and finally releasing the target.

protected void paint(Graphics g) {
        // Get the singleton Graphics3D instance that is associated
        // with this midlet.

        Graphics3D g3d = Graphics3D.getInstance();

        // Bind the 3D graphics context to the given MIDP Graphics
        // object. The viewport will cover the whole of this Canvas.

        g3d.bindTarget(g);

        // Apply animations, render the scene and release the Graphics.

        myWorld.animate(currentTime);
        g3d.render(myWorld);    // render a view from the active camera
        g3d.releaseTarget();    // flush the rendered image
        currentTime += 50;      // assume we can handle 20 frames per second
    }
}

[edit] Loading M3G Files

There are software that can be the hard work of creation the Object3D elelemts. Instead of programing the entire scene, we can create it using a 3D program, export it into a .m3g file, and finally load it into our application.

The class Loader,downloads and deserializes scene graph nodes and node components, as well as entire scene graphs. Downloading ready-made pieces of 3D content from an M3G file is generally the most convenient way for an application to create and populate a 3D scene.

[edit] Supported data types

The Loader can deserialize instances of any class derived from Object3D. These include scene graph nodes such as World, Group, Camera and Light; attribute classes such as Material, Appearance and Texture2D; animation classes such as AnimationTrack and KeyframeSequence; and so on.

The data to be loaded must constitute a valid M3G file. Alternatively, it may be a PNG image file, in which case a single, immutable Image2D object is returned, with the pixel format of the Image2D corresponding to the color type of the PNG.

[edit] Using the Loader

The Loader class cannot be instantiated, and its only members are the two static load methods. The methods are otherwise identical, but one of them takes in a byte array, while the other takes a named resource, such as a URI or an individual file in the JAR package. Named resources must always have an absolute path, otherwise the results are undefined.

Any external references in the given file or byte array are followed recursively. When using the load variant that takes in a URI, the references may be absolute or relative, but when using the byte array variant, only absolute references are allowed. External references are also treated as case-sensitive.

The load methods only return once the entire contents of the given file (or byte array) have been loaded, including any referenced files. This means that displaying content while downloading (progressive loading) is not supported.

   Object3D[] root = Loader.load("/myFile.m3g");

[edit] Managing the loaded objects

The load methods return an array of Object3Ds. These are the root level objects in the file; in other words, those objects that are not referenced by any other objects. The array is guaranteed not to contain any null objects, but the order of the objects in the array is undefined.

The non-root objects (often the majority) can be found by following references recursively, starting from the root objects. This can be done conveniently with the getReferences method in Object3D. Another way to find a specific object is to tag it with a known user ID at the authoring stage, and search for that among the loaded objects using the find method..

Since the root-level objects are returned in an Object3D array, the application must find out their concrete types before using their full functionality. In the typical case, when the content is developed in conjunction with the application code and deployed in the same JAR file, the application should know what the root-level objects are. If this information is not available, or there is a need to check that the objects are as expected, the application can use the run-time type information that is built into Java. For example, a simple animation player application might want to check that the downloaded object is indeed a World, and display an appropriate error message otherwise.

   World myWorld = (World) root;
   Appearance myAppear = (Appearance) myWorld.find(MY_APPEAR_ID);
   ...

[edit] Transformations

Transformable is an abstract base class for Node and Texture2D that extends Object3D, defining common methods for manipulating node and texture transformations.

Node transformations and texture transformations consist of four individual components: translation (T), orientation (R), scale (S) and a generic 4x4 matrix (M). Formally, a homogeneous vector p = (x, y, z, w), representing vertex coordinates (in Node) or texture coordinates (in Texture2D), is transformed into p' = (x', y', z', w') as follows:

     p' = T R S M p

The bottom row of M must be equal to (0 0 0 1)

When a class derived from Transformable is instantiated, the attributes inherited from it will have the following default values:

   * scale : (1,1,1)
   * translation : (0,0,0)
   * orientation : angle = 0, axis = undefined
   * matrix : identity

The transformation components are initially set to identity so that they do not affect the texture coordinates or vertex coordinates in any way. Note that the orientation axis can be left undefined because the angle is zero.

The Transform class is a generic 4x4 floating point matrix, representing a transformation. By default, all methods dealing with Transform objects operate on arbitrary 4x4 matrices. Any exceptions to this rule are documented explicitly at the method level. Even though arbitrary 4x4 matrices are generally allowed, using non-invertible (singular) matrices may produce undefined results or an arithmetic exception in some situations. Specifically, if the modelview matrix of an object is non-invertible, the results of normal vector transformation and fogging are undefined for that object. We can use it to invert, translate, rotate, scale, and more transformations in 4x4 matrices.

The basic transformation methods are those:

   * scale(float sx, float sy, float sz)
   * translate(float tx, float ty, float tz)
   * postRotate(float angle, float ax, float ay, float az)
   * preRotate(float angle, float ax, float ay, float az)
   * setTransform(Transform transform)
   * setOrientation(float angle, float ax, float ay, float az)
  • scale multiplies the current scale component by the given scale factors.
  • translate adds the given offset to the current translation component.
  • postRotate multiplies the current orientation component from the right by the given orientation.
  • preRotate multiplies the current orientation component from the left by the given orientation.
  • setTransform sets the matrix component of this Transformable by copying in the given Transform.
  • setOrientation sets the orientation component of this Transformable.

[edit] Animation

Animations are applied to an object and its descendants with the animate method in this class. The animate(int time) method, updates all animated properties in this Object3D and all Object3Ds that are reachable from this Object3D. The parameter time is the world time to update the animations to.

The objects needed for animation and their relationships are shown in the figure below.

That's way the bestest asnewr so far!

[edit] KeyframeSequence

KeyframeSequence encapsulates animation data as a sequence of time-stamped, vector-valued keyframes. Each keyframe represents the value of an animated quantity at a specific time instant.

   KeyframeSequence(int numKeyframes, int numComponents, int interpolation)
  • numKeyframes - number of keyframes to allocate for this sequence
  • numComponents - number of components in each keyframe vector
  • interpolation - one of the interpolation modes listed above

The interpolation constants are: STEP, LINEAR and SPLINE that can be specified for any type of keyframes. On the other hand, SLERP and SQUAD can only be specified for 4-component keyframes, which are then interpreted as quaternions.

  • CONSTANT this sequence is to be played back just once and not repeated.
  • LINEAR linear interpolation between keyframes.
  • LOOP this sequence is to be repeated indefinitely.
  • SLERP spherical linear interpolation of quaternions.
  • SPLINE spline interpolation between keyframes.
  • SQUAD spline interpolation of quaternions.
  • STEP stepping from one keyframe value to the next.

Once this object is defined, let's create and attach the keyFrames.

   MyKeyframeSequence.setKeyframe(int index, int time, float[] value);
  • index - index of the keyframe to set
  • time - time position of the keyframe, in sequence time units
  • value - float array containing the keyframe value vector

Cool! That's a clever way of lkoonig at it!

I was really confused, and this aenswred all my questions.

Main Collaborators