Mobile 3D Graphics API for J2ME (M3G),由 Nokia 主導制定 (JSR 184),定義了保留模式場景圖 API,支援網格、材質、貼圖、動畫、相機、光照、骨骼蒙皮,文件格式 .m3g。WTK 2.2+ 內建參考實現,S60 3rd Edition 起真機普遍支援。
res/ 目錄 (與源碼同級),打包進 JAR。package first3d;
import javax.microedition.lcdui.*;
import javax.microedition.m3g.*;
import javax.microedition.midlet.*;
public class First3DMIDlet extends MIDlet {
private First3DCanvas canvas;
protected void startApp() {
if (canvas == null) canvas = new First3DCanvas();
Display.getDisplay(this).setCurrent(canvas);
}
protected void pauseApp() {}
protected void destroyApp(boolean u) { canvas.stop(); }
}
class First3DCanvas extends Canvas implements Runnable {
private World world;
private Graphics3D g3d = Graphics3D.getInstance();
private Camera camera;
private int vx, vy, vw, vh;
private long startTime;
private Thread renderThread;
private volatile boolean running = true;
public First3DCanvas() {
renderThread = new Thread(this); renderThread.start();
try {
// 載入場景 (WTK Demo3D 附帶 swerve.m3g)
Object[] objs = Loader.load("/swerve.m3g");
world = (World) objs[0];
vx = 0; vy = 0; vw = getWidth(); vh = getHeight();
camera = world.getActiveCamera();
setupCamera();
startTime = System.currentTimeMillis();
} catch (Exception e) { e.printStackTrace(); }
}
private void setupCamera() {
float[] params = new float[4];
int type = camera.getProjection(params);
if (type != Camera.GENERIC) {
float aspect = (float)vw / vh;
if (aspect < params[1]) { // params[1] = vertical FOV factor
float h = vw / params[1];
vh = (int)h; vy = (getHeight() - vh) / 2;
} else {
float w = vh * params[1];
vw = (int)w; vx = (getWidth() - vw) / 2;
}
}
}
protected void paint(Graphics g) {
g.setColor(0x000000); g.fillRect(0, 0, getWidth(), getHeight());
g3d.bindTarget(g);
g3d.setViewport(vx, vy, vw, vh);
long t = System.currentTimeMillis() - startTime;
int validity = world.animate((int)t); // 推進動畫
try { g3d.render(world); }
finally { g3d.releaseTarget(); }
}
public void run() {
while (running) {
repaint(vx, vy, vw, vh);
try { Thread.sleep(30); } catch (InterruptedException ignored) {}
}
}
public void stop() { running = false; }
}
| 類 | 職責 |
|---|---|
World | 場景根節點,包含相機、背景、動畫控制器、根 Group |
Camera | 投影 (透視/正交)、視口、視野角、近遠裁剪面 |
Group / Mesh | 場景圖節點,Mesh 含幾何體 + 外觀 |
Appearance / Material / Texture2D | 渲染狀態:顏色、光照參數、貼圖、混合模式 |
AnimationController / KeyframeSequence | 骨骼/頂點/材質/變換關鍵幀動畫 |
Loader | 從 .m3g / 資源流反序列化整個場景圖 |
Graphics3D | 渲染上下文:bindTarget/render/releaseTarget、視口、霧效、剪裁 |
camera.setCulling(true) (預設開啟)。Graphics3D 狀態切換。world.animate(dt) 傳入固定 33ms,避免大幀時動畫加速。world = null; System.gc();。JSR 184 讓 J2ME 具備了類似 OpenGL ES 1.x 的保留模式 3D 能力,雖然硬體限制極大,但《Asphalt 3D》、《Need for Speed》、《The Sims 3》等商業大作證明了其商業可行性。現代開發者可借鑒其**場景圖、關鍵幀動畫、二進制資源格式**設計思想,應用於 WebGL/Three.js/Babylon.js 或 Vulkan/Metal 場景管理層設計中。