|
M3G 1.1 -- Jun 22, 2005 | |||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
See:
Description
Class Summary | |
AnimationController | Controls the position, speed and weight of an animation sequence. |
AnimationTrack | Associates a KeyframeSequence with an AnimationController and an animatable property. |
Appearance | A set of component objects that define the rendering attributes of a Mesh or Sprite3D. |
Background | Defines whether and how to clear the viewport. |
Camera | A scene graph node that defines the position of the viewer in the scene and the projection from 3D to 2D. |
CompositingMode | An Appearance component encapsulating per-pixel compositing attributes. |
Fog | An Appearance component encapsulating attributes for fogging. |
Graphics3D | A singleton 3D graphics context that can be bound to a rendering target. |
Group | A scene graph node that stores an unordered set of nodes as its children. |
Image2D | A two-dimensional image that can be used as a texture, background or sprite image. |
IndexBuffer | An abstract class defining how to connect vertices to form a geometric object. |
KeyframeSequence | Encapsulates animation data as a sequence of time-stamped, vector-valued keyframes. |
Light | A scene graph node that represents different kinds of light sources. |
Loader | Downloads and deserializes scene graph nodes and node components, as well as entire scene graphs. |
Material | An Appearance component encapsulating material attributes for lighting computations. |
Mesh | A scene graph node that represents a 3D object defined as a polygonal surface. |
MorphingMesh | A scene graph node that represents a vertex morphing polygon mesh. |
Node | An abstract base class for all scene graph nodes. |
Object3D | An abstract base class for all objects that can be part of a 3D world. |
PolygonMode | An Appearance component encapsulating polygon-level attributes. |
RayIntersection | A RayIntersection object is filled in by the pick methods in
Group. |
SkinnedMesh | A scene graph node that represents a skeletally animated polygon mesh. |
Sprite3D | A scene graph node that represents a 2-dimensional image with a 3D position. |
Texture2D | An Appearance component encapsulating a two-dimensional texture image and a set of attributes specifying how the image is to be applied on submeshes. |
Transform | A generic 4x4 floating point matrix, representing a transformation. |
Transformable | An abstract base class for Node and Texture2D, defining common methods for manipulating node and texture transformations. |
TriangleStripArray | TriangleStripArray defines an array of triangle strips. |
VertexArray | An array of integer vectors representing vertex positions, normals, colors, or texture coordinates. |
VertexBuffer | VertexBuffer holds references to VertexArrays that contain the positions, colors, normals, and texture coordinates for a set of vertices. |
World | A special Group node that is a top-level container for scene graphs. |
Defines an API for rendering three-dimensional (3D) graphics at interactive frame rates, including a scene graph structure and a corresponding file format for efficient management and deployment of 3D content.
The function of this API is to provide Java application programmers with an efficient and flexible means to display animated 3D graphics in real time on embedded devices. To cater for the needs of different types of applications, both an easy-to-use scene graph structure and an immediate mode interface are provided. All animation and rendering features are available for scene graph objects and individually rendered objects alike. The developer therefore does not need to choose between the immediate mode and the scene graph, but rather can mix and match both within the same application.
Besides the API itself, a corresponding file format for efficient storage and transfer of all necessary data is also defined. This data includes meshes, textures, scene hierarchies, material properties, animation keyframes, and so on. Data is written into a file by content creation tools on a PC, and loaded into the API through the Loader class.
The example applications at the end
of this page provide a good means to get a quick overview of this
API. Of the individual classes, Graphics3D
is perhaps the most important,
because all rendering is done there. The World
class is crucial because it 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, and also the place where
animations are applied. We also recommend you to read the rest of this
package description.
Because of its optional nature, this API may not always be available on every platform. Each profile and platform may have their own methods for J2ME package discovery as there is no universal method existing at this time. An additional method for package discovery of the Mobile 3D Graphics API is by using a system properties query. To discover this package, call System.getProperty with a key of microedition.m3g.version. If the API is present, the value returned is the version of the API (this version is "1.1", and the previous version was "1.0"). If the API is not present then the key is also not present and null will be returned.
The following general conventions are observed in the documentation of this API.
Coordinate systems. All 2D coordinate systems follow the MIDP convention where the origin is in the upper left corner and integer coordinates are at pixel boundaries. By default, 3D coordinate systems are right-handed, and rotations obey the right-hand rule: looking along the positive axis of rotation, positive angles are clockwise. The camera coordinate system follows the OpenGL convention where the view direction coincides with the negative z-axis, the positive x-axis points right, and the positive y-axis points up. However, the application is free to set up left-handed 3D coordinate systems by use of transformation matrices.
Matrix notation. Matrices are denoted as upper case bold letters, and vectors as lower case bold letters. For example, M denotes a matrix and v a vector. Matrices have 4x4 and vectors 4 elements, unless stated otherwise. Vectors are always column vectors, and are consequently on the right hand side when multiplied with a matrix: v' = M v.
Numeric intervals. Closed intervals are denoted with square brackets and open intervals with round brackets. For example, [0, 10) denotes the values from zero to ten, including zero but not including ten. Depending on the context, a numeric interval may consist of real numbers or integers.
OpenGL references. All references to OpenGL in this specification are to version 1.3. See Related Literature on the overview page.
Diagram notation. The following common notation is used in diagrams that involve scene graph nodes and node components.
By default, vertices, indices, triangles, and fragments are processed as in OpenGL. In particular, triangle rasterization is done as specified in section 3.5.1 of the OpenGL specification.
The reference geometry and fragment pipelines are shown below. A rough mapping of Mesh components and other objects to the pipeline stages is also shown. Note that the ordering of the stages is the same as in OpenGL. Implementations may optimize their operation by doing things in a different order, but only if the result is exactly the same as it would be with the reference pipelines.
The floating point format used for input and output is the standard IEEE float, having an 8-bit exponent and a 24-bit mantissa normalized to [1.0, 2.0). To facilitate efficient operation without floating point hardware, implementations are allowed to substitute more constrained representations internally. The internal format, and conversion from the input format to the internal format, must satisfy the following:
These requirements also apply to elementary arithmetic operations, which include addition, subtraction and multiplication. The operands are then taken to be in the internal format rather than the input format, and the value against which the precision is measured is taken to be the mathematically correct result, rounded to the nearest representable value. In addition, elementary arithmetic operations must satisfy the following:
These requirements apply to all operations in this API, except rasterization and per-fragment operations, such as depth buffering, blending, and interpolation of colors and texture coordinates. In particular, the requirements do apply to node transformations in the scene graph; vertex coordinate, texture coordinate and normal vector transformations; picking; keyframe interpolation; mesh morphing; skinning; and all methods in the Transform class.
Blending, interpolation, comparisons and other operations on color, alpha and (screen-space) depth values must have a numeric range, minimum absolute value, and precision at least equivalent to the corresponding channel in the frame buffer. For example, an 8-bit color channel has R = [0, 1], d = 1/255, and p = 8. Within that domain, the rules are as specified above, with two additional requirements:
Loss of precision is allowed when converting the result of the operation into the frame buffer format, which is commonly fixed-point. The higher-precision internal value may be rounded to either of the two closest representable values in the frame buffer format. Note that the final precision will get progressively worse as the intermediate result approaches zero. In the worst case, all significant bits except the leading zero or one will be lost.
When querying the value of some property in the API, the returned
value does not need to be exactly the same that was set with the
corresponding set
method. Instead, it may be any value
that produces an equivalent result. The returned values are
also not required to be in any "canonical" or "normalized" form. In
the case of node orientation, for example, there are a number of
different axis-angle combinations that specify the same orientation,
and any one of them may be returned.
The returned value may also be an approximation of the original value, as long as the accuracy constraints for the particular type of data are satisfied.
Object3D instances are always held by reference rather than copied
in. Changes to an Object3D therefore have immediate effect in any
referring Object3D. For example, changes to an Image2D attached to a
Background take effect without having to call the
Background.setImage
method again.
Objects that are not instances of Object3D are copied in by default. Any exceptions to this rule are clearly documented in the individual method descriptions. Note that arrays are Objects in Java, and are therefore copied in rather than held by reference. Also note that the Transform class, although defined in this API, is not derived from Object3D.
To clarify the handling of arrays, consider a hypothetical class X that takes in an Object3D array in its constructor. The constructor copies in the array, but stores the elements of the array by reference. Thus, replacing one Object3D in the array with another will have no effect on the instance of X that was just created. Indeed, the application may freely reuse the array or leave it for garbage collection. By contrast, any modifications to the actual Object3D instances that were contained in the array will automatically be reflected in the new instance of X.
The scene graph as well as individual objects are allowed to remain in an incomplete or invalid state for as long as their contents are not actually needed by the implementation (for rendering or some other purpose). An IllegalStateException is thrown only when the objects really must be valid. This kind of deferred error checking is necessary for aggregate objects, whose validity depends on other objects that the application can add, remove or change at any time. There are four operations in this API that can throw these deferred exceptions: the render methods in Graphics3D, the pick methods in Group, the align method in Node, and the animate method in Object3D.
The fact that deferred exceptions may or may not be thrown, depending on whether the implementation actually needs the offending data, can cause varying behavior between different implementations. For example, some implementations may use visibility culling to remove objects from further processing without having to check their vertex arrays, while others may use a brute-force approach and push all objects through the rendering pipeline. To reduce this variability without restricting innovation, implementations must obey the following rules when rendering or picking:
A Node can be disabled by clearing its rendering and picking enable flags. A submesh can be disabled by setting its Appearance to null. By definition, all objects are disabled when rendering from a Camera that has zero view volume.
Implementations must not crash or throw an exception as a result of being accessed from multiple threads at the same time. However, the results of the requested operation in that case may be unpredictable.
No method in this API is allowed to block waiting for a resource, such as a rendering target, to be released. This is to guarantee that no deadlock situations will occur. Also, any resources required by a method must be released upon return. No method is allowed to leave its host object or other resources locked.
Several different pixel formats are supported in rendering targets, textures, sprites, and background images. Depending on the case, a mismatch between the source and destination pixel formats may require a format conversion to be done. The general rules that are obeyed throughout the API are as follows:
More specific rules related to pixel formats are specified on a case-by-case basis in classes dealing with images and the frame buffer. These include Graphics3D, Image2D, Texture2D, CompositingMode and Background.
Two example MIDlets using the API are presented below. The first MIDlet is a pure immediate mode application that displays a rotating, texture-mapped cube. It shows how to initialize a 3D graphics context, bind it to a MIDP Canvas, and render some simple content with it. It also illustrates how to create a Mesh object "manually", that is, how to set up the coordinates, triangle connectivity, texture maps, and materials. In practice, this is usually not done programmatically, but with a 3D modeling tool. Loading a ready-made Mesh object with all the necessary attributes is a simple matter of calling the load method in Loader.
The other example MIDlet is a retained mode application that plays back a ready-made animation that it downloads over http.
import javax.microedition.lcdui.*; import javax.microedition.m3g.*; public class MyCanvas extends Canvas { private Graphics3D iG3D; private Camera iCamera; private Light iLight; private float iAngle = 0.0f; private Transform iTransform = new Transform(); private Background iBackground = new Background(); private VertexBuffer iVb; // positions, normals, colors, texcoords private IndexBuffer iIb; // indices to iVB, forming triangle strips private Appearance iAppearance; // material, texture, compositing, ... private Material iMaterial = new Material(); private Image iImage; /** * Construct the Displayable. */ public MyCanvas() { // set up this Displayable to listen to command events setCommandListener(new CommandListener() { public void commandAction(Command c, Displayable d) { if (c.getCommandType() == Command.EXIT) { // exit the MIDlet MIDletMain.quitApp(); } } }); try { init(); } catch(Exception e) { e.printStackTrace(); } } /** * Component initialization. */ private void init() throws Exception { // add the Exit command addCommand(new Command("Exit", Command.EXIT, 1)); // get the singleton Graphics3D instance iG3D = Graphics3D.getInstance(); // create a camera iCamera = new Camera(); iCamera.setPerspective( 60.0f, // field of view (float)getWidth()/ (float)getHeight(), // aspectRatio 1.0f, // near clipping plane 1000.0f ); // far clipping plane // create a light iLight = new Light(); iLight.setColor(0xffffff); // white light iLight.setIntensity(1.25f); // overbright // init some arrays for our object (cube) // Each line in this array declaration represents a triangle strip for // one side of a cube. The only primitive we can draw with is the // triangle strip so if we want to make a cube with hard edges we // need to construct one triangle strip per face of the 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); // The per-vertex normals for the cube; these match with the vertices // above. Each normal is perpendicular to the surface of the object at // the corresponding vertex. 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); // per vertex texture coordinates 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); // the length of each triangle strip int[] stripLen = { 4, 4, 4, 4, 4, 4 }; // create the VertexBuffer for our object VertexBuffer vb = iVb = new VertexBuffer(); vb.setPositions(vertArray, 1.0f, null); // unit scale, zero bias vb.setNormals(normArray); vb.setTexCoords(0, texArray, 1.0f, null); // unit scale, zero bias // create the index buffer for our object (this tells how to // create triangle strips from the contents of the vertex buffer). iIb = new TriangleStripArray( 0, stripLen ); // load the image for the texture iImage = Image.createImage( "/texture.png" ); // create the Image2D (we need this so we can make a Texture2D) Image2D image2D = new Image2D( Image2D.RGB, iImage ); // create the Texture2D and enable mipmapping // texture color is to be modulated with the lit material color Texture2D texture = new Texture2D( image2D ); texture.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST); texture.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP); texture.setBlending(Texture2D.FUNC_MODULATE); // create the appearance iAppearance = new Appearance(); iAppearance.setTexture(0, texture); iAppearance.setMaterial(iMaterial); iMaterial.setColor(Material.DIFFUSE, 0xFFFFFFFF); // white iMaterial.setColor(Material.SPECULAR, 0xFFFFFFFF); // white iMaterial.setShininess(100.0f); iBackground.setColor(0xf54588); // set the background color } /** * Paint the scene. */ protected void paint(Graphics g) { // Bind the Graphics of this Canvas to our Graphics3D. The // viewport is automatically set to cover the entire clipping // rectangle of the Graphics object. The parameters indicate // that z-buffering, dithering and true color rendering are // enabled, but antialiasing is disabled. 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, 30.0f); iG3D.setCamera(iCamera, transform); // set up a "headlight": a directional light shining // from the direction of the camera iG3D.resetLights(); iG3D.addLight(iLight, transform); // update our transform (this will give us a rotating cube) iAngle += 1.0f; iTransform.setIdentity(); iTransform.postRotate(iAngle, // rotate 1 degree per frame 1.0f, 1.0f, 1.0f); // rotate around this axis // Render our cube. We provide the vertex and index buffers // to specify the geometry; the appearance so we know what // material and texture to use; and the transform to tell // where to render the object iG3D.render(iVb, iIb, iAppearance, iTransform); // flush iG3D.releaseTarget(); } }
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import java.util.*; public class MIDletMain extends MIDlet { static MIDletMain instance; MyCanvas displayable = new MyCanvas(); Timer iTimer = new Timer(); /** * Construct the midlet. */ public MIDletMain() { this.instance = this; } /** * Main method. */ public void startApp() { Display.getDisplay(this).setCurrent(displayable); iTimer.schedule( new MyTimerTask(), 0, 40 ); } /** * Handle pausing the MIDlet. */ public void pauseApp() { } /** * Handle destroying the MIDlet. */ public void destroyApp(boolean unconditional) { } /** * Quit the MIDlet. */ public static void quitApp() { instance.destroyApp(true); instance.notifyDestroyed(); instance = null; } /** * Our timer task for providing animation. */ class MyTimerTask extends TimerTask { public void run() { if( displayable != null ) { displayable.repaint(); } } } }
import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.CommandListener; import java.util.Timer; import java.util.TimerTask; import javax.microedition.m3g.*; public class JesterTestlet extends MIDlet implements CommandListener { private Display myDisplay = null; private JesterCanvas myCanvas = null; private Timer myRefreshTimer = new Timer(); private TimerTask myRefreshTask = null; private Command exitCommand = new Command("Exit", Command.ITEM, 1); private World myWorld = null; /** * JesterTestlet - default constructor. */ public JesterTestlet() { // Set up the user interface. myDisplay = Display.getDisplay(this); myCanvas = new JesterCanvas(this); myCanvas.setCommandListener(this); myCanvas.addCommand(exitCommand); } /** * startApp() */ public void startApp() throws MIDletStateChangeException { myDisplay.setCurrent(myCanvas); try { // Load a file. Object3D[] roots = Loader.load("http://www.example.com/m3g/samples/simple.m3g"); // Assume the world is the first root node loaded. myWorld = (World)roots[0]; // Force a repaint so that we get the update loop started. myCanvas.repaint(); } catch(Exception e) { e.printStackTrace(); } } /** * pauseApp() */ public void pauseApp() { // Release resources. myWorld = null; } /** * destroyApp() */ public void destroyApp(boolean unconditional) throws MIDletStateChangeException { myRefreshTimer.cancel(); myRefreshTimer = null; // Release resources. myWorld = null; } /** * MIDlet paint method. */ public void paint(Graphics g) { // We are not fully initialised yet; just return. if(myCanvas == null || myWorld == null) return; // Delete any pending refresh tasks. if(myRefreshTask != null) { myRefreshTask.cancel(); myRefreshTask = null; } // Get the current time. long currentTime = System.currentTimeMillis(); // Update the world to the current time. int validity = myWorld.animate((int)currentTime); // Render to our Graphics. Graphics3D myGraphics3D = Graphics3D.getInstance(); myGraphics3D.bindTarget(g); myGraphics3D.render(myWorld); myGraphics3D.releaseTarget(); // Subtract time taken to do the update. validity -= System.currentTimeMillis() - currentTime; if(validity < 1) { // The validity is too small; allow a minimum of 1ms. validity = 1; } // If the validity is not infinite schedule a refresh task. if(validity < 0x7fffffff) { // Create a new refresh task. myRefreshTask = new RefreshTask(); // Schedule an update. myRefreshTimer.schedule(myRefreshTask, validity); } } /** * Handle commands. */ public void commandAction(Command cmd, Displayable disp) { if (cmd == exitCommand) { try { destroyApp(false); notifyDestroyed(); } catch(Exception e) { e.printStackTrace(); } } } /** * Inner class for refreshing the view. */ private class RefreshTask extends TimerTask { public void run() { // Get the canvas to repaint itself. myCanvas.repaint(); } } /** * Inner class for handling the canvas. */ class JesterCanvas extends Canvas { JesterTestlet myTestlet; /** * Construct a new canvas */ JesterCanvas(JesterTestlet Testlet) { myTestlet = Testlet; } /** * Initialize self. */ void init() { } /** * Cleanup and destroy. */ void destroy() { } /** * Ask myTestlet to paint itself */ protected void paint(Graphics g) { myTestlet.paint(g); } } }
|
M3G 1.1 -- Jun 22, 2005 | |||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |