`
micheal19840929
  • 浏览: 163103 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

M3G教程:进阶篇(六)动画

    博客分类:
  • J2ME
阅读更多

 

      M3G中动画的数据结构如下:

【载入World节点】

      通过M3G Viewer查看M3G Converter转换的m3g文件,我们会发现m3g文件中camera并没有在world树下,而是和world树是平级的。因为JSR184要求渲染时所有模型信息必须放到world树下,所以我们要先获取World节点,方法有两种:

      ①遍历该Object3D数组,并比较每个元素的userid如果正是World节点的useid将该元素取出;
      ②遍历该Object3D数组,并比较每个元素是不是World类的实例,那么既然World节点是场景的根节点,那么在该Object3D数组中也应该只有一个World类的实例对象。

      第一种必需通过查看文件中world节点的userId来获取,第二种比较容易操作,代码如下:

/** Loads our world */
private void loadWorld() {
	try {
		// Loading the world is very simple. Note that I like to use a
		// res-folder that I keep all files in. If you normally just put
		// your
		// resources in the project root, then load it from the root.
		Object3D[] buffer = Loader.load("/axe.m3g");

		// Find the world node, best to do it the "safe" way
		for (int i = 0; i < buffer.length; i++) {
			if (buffer[i] instanceof World) {
				world = (World) buffer[i];
				break;
			}
		}

		// Clean objects
		buffer = null;
	} catch (Exception e) {
		// ERROR!
		System.out.println("Loading error!");
		reportException(e);
	}
}

      在JSR184中,使用AnimationTrack、AnimationController和KeyframeSquence三个类来实现关键帧动画:

  AnimationTrack:是一个集合类(Composition),用来将AnimationController、KeyframeSquence和一个特定属性(Target Property)捆绑起来,这种结构也使得同一个AnimationController和KeyframeSquence可以被不同的关键帧动画复用。在每一个Object3D对象中,通过addAnimationTrack和removeAnimationTrack方法,管理一组AnimationTrack序列;当使用animate方法,计算该Object3D对象关键帧动画时,所有在序列中的AnimationTrack都会被依次计算,此外若该Object3D对象有到其他Object3D的引用,则所有引用的Object3D的animate方法也会被依次执行。

  AnimationController:通过指定采样点和采样速度,实现从WorldTime到SquenceTime的映射,从而控制动画的播放效果,如:暂停,继续,快进、倒退等。

      AnimationController有一个激活时间段属性(通过setActiveInterval(int,int)实现),通过指定最小及最大的world time值来控制这个AnimationController在这个时间段是激活状态的。如果AnimationController是在某时间是非激活状态,则系统在动画计算时直接将其忽略。

      另外,AnimationController有一个weight属性,因为系统对对象某属性进行计算时会将与此属性相关的所有动画计算的结果进行合并,而weight则表示这个AnimationController在这个计算中所占的比重,确切的说,对于一个标量属性P的计算是这样的:P = sum [ wi Pi ] 。

      AnimationController将传入Object3D.animate()方法的world time值与sequence time值进行映射,sequence time主要用于对关键帧动画数据进行抽样。sequence time会在每次调用对象的animate()方法时计算得出,而不是store it internally,这是为了避免误差的累积及由此产生的一系列状况。这种设计简化了动画系统,使动画系统能够无状态化(相对于状态机),并且效率更高。

      从world time到sequence time的映射由AnimationController中的三个常量及传入animate()方法的world time进行计算得到的。公式如下:

     

      其中的参考值t[sref]及t[wref]是通过setPosition()方法指定,而速度speed则是由setSpeed()方法指定(注意改变速度会影响到t[sref]及t[wref]值)。

      注意:虽然API及文档中并没有明确的说明这里的时间单位使用毫秒,但我们强烈建议你使用这个默认的时间单位。当然,如果确实需要使用特有有时间单位,动画系统还是支持的。

  KeyframeSquence:用来保存所有的关键帧的值,同时指定关键帧循环的方式,以及计算时的插值方法。

      无论在立即模式还是保留模式下,都是通过调用Object3D对象的animate()方法实现将对象动画数据的计算。根据使用情形,我们既可以调用World.animate(),也可以调用相应Object3D对象的animate()方法。

【示例】

以下代码实现一个灯光在红色与绿色之间转换,并沿着一条曲线路径进行移动:

   初始化时:

Light light = new Light();	// Create a light node
 
// Load a motion path from a stream, assuming it's the first object there
 
Object3D[] objects = Loader.load("http://www.ex.com/ex.m3g");
KeyframeSequence motion = (KeyframeSequence) objects[0];
 
// Create a color keyframe sequence, with keyframes at 0 ms
// and 500 ms, and a total duration of 1000 ms. The animate
// method will throw an exception if it encounters a
// KeyframeSequence whose duration has not been set or whose
// keyframes are out of order. Note that the Loader
// automatically validates any sequences that are loaded from
// a file.
 
KeyframeSequence blinking = new KeyframeSequence(2, 3, KeyframeSequence.LINEAR);
blinking.setKeyframe(0,   0, new float[] { 1.0f, 0.0f, 0.0f });
blinking.setKeyframe(1, 500, new float[] { 0.0f, 1.0f, 0.0f });
blinking.setDuration(1000);
 
AnimationTrack blink = new AnimationTrack(blinking, AnimationTrack.COLOR);
AnimationTrack move = new AnimationTrack(motion, AnimationTrack.TRANSLATION);
light.addAnimationTrack(blink);
light.addAnimationTrack(move);
 
// Create an AnimationController and make it control both the
// blinking and the movement of our light
 
AnimationController lightAnim = new AnimationController();
blink.setController(lightAnim);
move.setController(lightAnim);
 
// Start the animation when world time reaches 2 seconds, stop
// at 5 s.  There is only one reference point for this
// animation: sequence time must be zero at world time 2000
// ms. The animation will be running at normal speed (1.0, the
// default).
 
lightAnim.setActiveInterval(2000, 5000);
lightAnim.setPosition(0, 2000);

    渲染时:

appTime += 30;		// advance time by 30 ms each frame
light.animate(appTime);
 
// Assume 'myGraphics3D' is the Graphics3D object we draw into.
// In immediate mode, node transforms are ignored, so we get
// our animated transformation into a local Transform object,
// "lightToWorld". As its name implies, the transformation is
// from the Light node's local coordinates to world space.
 
light.getTransform(lightToWorld);
myGraphics3D.resetLights();
myGraphics3D.addLight(light, lightToWorld);

 

【示例:播放袋鼠跳动动画】

package study.pogoroo;



import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.m3g.AnimationController;
import javax.microedition.m3g.AnimationTrack;
import javax.microedition.m3g.Camera;
import javax.microedition.m3g.Graphics3D;
import javax.microedition.m3g.Group;
import javax.microedition.m3g.KeyframeSequence;
import javax.microedition.m3g.Loader;
import javax.microedition.m3g.Object3D;
import javax.microedition.m3g.World;


public class M3GCanvas extends GameCanvas implements Runnable {
	
	public static final int FPS = 50;	//每秒绘制的帧数
	
//	 Thread-control
	boolean running = false;

	boolean done = true;

	//UserIDs for objects we use in the scene.
    static final int POGOROO_TRANSFORM_ID = 347178853;
    static final int ROO_BOUNCE_ID = 418071423;

    // Control objects for game play
    // control for 'roo - group transform and cameras
    private AnimationController animRoo = null;
    private Group acRoo = null;
    private int animTime = 0;
    private int animLength = 0;
    private int animLastTime = 0;
    int viewport_x;
    int viewport_y;
    int viewport_width;
    int viewport_height;

	// Key array
	private boolean[] key = new boolean[5];

	// Key constants
	public static final int FIRE = 0;
	public static final int UP = 1;
	public static final int DOWN = 2;
	public static final int LEFT = 3;
	public static final int RIGHT = 4;

	// Camera rotation
	private float camRot = 0.0f;

	private Graphics3D g3d;
	private World world;
	private boolean runnable=true;
	private Thread thread;
	private Camera camera;

	protected M3GCanvas() {
		super(false);
		setFullScreenMode(true);
		g3d = Graphics3D.getInstance();
		
		//Load our world
		loadWorld();

		// Load our camera
		loadCamera();
		
		getObjects();
		
		setupAspectRatio();
	}
	
	/** Loads our camera */
	private void loadCamera() {
		// BAD!
		if (world == null)
			return;
		
		// Get the active camera from the world
		camera = world.getActiveCamera();
	}

	/** Loads our world */
	private void loadWorld() {
		try {
			// Loading the world is very simple. Note that I like to use a
			// res-folder that I keep all files in. If you normally just put
			// your
			// resources in the project root, then load it from the root.
			Object3D[] buffer = Loader.load("/pogoroo.m3g");
	
			// Find the world node, best to do it the "safe" way
			for (int i = 0; i < buffer.length; i++) {
				if (buffer[i] instanceof World) {
					world = (World) buffer[i];
					break;
				}
			}
	
			// Clean objects
			buffer = null;
		} catch (Exception e) {
			// ERROR!
			System.out.println("Loading error!");
		}
	}
	
	/**
     * Make sure that the content is rendered with the correct aspect ratio.
     */
    void setupAspectRatio() {
        viewport_x = 0;
        viewport_y = 0;
        viewport_width = getWidth();
        viewport_height = getHeight();

        float[] params = new float[4];
        int type = camera.getProjection(params);

        if (type != Camera.GENERIC) {
            //calculate window aspect ratio
            float waspect = viewport_width / viewport_height;

            if (waspect < params[1]) {
                float height = viewport_width / params[1];
                viewport_height = (int)height;
                viewport_y = (getHeight() - viewport_height) / 2;
            } else {
                float width = viewport_height * params[1];
                viewport_width = (int)width;
                viewport_x = (getWidth() - viewport_width) / 2;
            }
        }
    }
    
    /**
     * getObjects()
     * get objects from the scene tree for use in the game AI
     */
    public void getObjects() {
        try {
            acRoo = (Group)world.find(POGOROO_TRANSFORM_ID);
            animRoo = (AnimationController)world.find(ROO_BOUNCE_ID);

            // get length of animation
            AnimationTrack track = acRoo.getAnimationTrack(0);
            animLength = 1000; // default length, 1 second

            if (track != null) {
                KeyframeSequence ks = track.getKeyframeSequence();

                if (ks != null) {
                    animLength = ks.getDuration();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * animateRoo()
     * Makes sure that the hopping animation loops correctly.
     */
    private void animateRoo(int worldTime) {
        // control the kangaroo animation sequence
        if (animLastTime == 0) {
            animLastTime = worldTime;
        }

        animTime += (worldTime - animLastTime);

        // initialise animation at end of sequence
        if (animTime > animLength) // sequence is ~1000ms
         {
            animRoo.setActiveInterval(worldTime, worldTime+2000);
            //setPosition(float sequenceTime, int worldTime) 
            //Sets a new playback position, relative to world time, for this animation controller. 
            animRoo.setPosition(0, worldTime);
            animTime = 0;
        }

        // update storage of last position and time
        animLastTime = worldTime;
    }
	
	private void moveCamera() {
		// Check controls
		if (key[LEFT]) {
			camRot += 5.0f;
		} else if (key[RIGHT]) {
			camRot -= 5.0f;
		}

		// Set the orientation
		camera.setOrientation(camRot, 0.0f, 1.0f, 0.0f);

		// If the user presses the FIRE key, let's quit
		if (key[FIRE])
			System.out.println("Fire");
	}
	
	protected void process() {
		int keys = getKeyStates();

		if ((keys & GameCanvas.FIRE_PRESSED) != 0)
			key[FIRE] = true;
		else
			key[FIRE] = false;

		if ((keys & GameCanvas.UP_PRESSED) != 0)
			key[UP] = true;
		else
			key[UP] = false;

		if ((keys & GameCanvas.DOWN_PRESSED) != 0)
			key[DOWN] = true;
		else
			key[DOWN] = false;

		if ((keys & GameCanvas.LEFT_PRESSED) != 0)
			key[LEFT] = true;
		else
			key[LEFT] = false;

		if ((keys & GameCanvas.RIGHT_PRESSED) != 0)
			key[RIGHT] = true;
		else
			key[RIGHT] = false;
	}

	public void run() {
		Graphics g = getGraphics();
		while (runnable) {
			long startTime = System.currentTimeMillis();
			
			//Call the process method (computes keys)
			process();

			//Move the camera around
			moveCamera();
			
			try {
				//First bind the graphics object. We use our pre-defined rendering
				// hints.
				g3d.bindTarget(g);
				
				int st=(int)startTime;
	            // update the control and game AI
				animateRoo(st);
	            // Update the world to the current time.
	            world.animate(st);
	            
				g3d.setViewport(viewport_x, viewport_y, viewport_width, viewport_height);
				//Now, just render the world. Simple as pie!
				g3d.render(world);
			} finally {
				g3d.releaseTarget();
			}
			flushGraphics();
			
			long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;
            if(costTime<1000/FPS)
            {
                try{
                  Thread.sleep(1000/FPS-costTime);
                }
                catch(Exception e){
                   e.printStackTrace();
                }
            }
		}
		System.out.println("Canvas stopped");

	}
	
	public void start()
	{
		thread=new Thread(this);
		thread.start();
	}
	
	public void stop()
	{
		this.runnable=false;
		try {
			thread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

 

其中pogoroo.m3g的节点树关系如下:

效果如下:

 

分享到:
评论

相关推荐

    M3G API Docs

    6. **动画**:学习M3G API提供的动画机制,包括关键帧动画、路径动画和时间线动画,以实现角色动作、物体运动等动态效果。 7. **视图控制**:掌握如何使用Camera对象来控制3D场景的视角和投影,如透视投影和平行...

    M3G Viewer:查看M3G文件内容-开源

    M3G查看器是一个独立的应用程序,用于查看3D图形文件格式的内容,该格式是对移动3D图形API(M3G)的补充。

    Mobile 3D Graphics with OpenGL ES and M3G

    此外,还涉及了M3G中的动画系统,如关键帧动画和物理模拟,以及如何使用M3G与Java代码进行交互,实现事件处理和用户交互。 除了基本概念和技术,本书还提供了许多实际案例和示例代码,帮助读者理解并实践所学知识。...

    M3G 的快速模式编程

    【M3G 快速模式编程】:M3G,全称Mobile 3D Graphics API,是基于JSR 184规范定义的一种为移动设备提供标准3D图形功能的API。它分为快速模式和保留模式。快速模式专注于单个3D对象的渲染,适合进行低级别的3D操作,...

    M3M0:M3m0工具:crossed_swords:网站漏洞扫描程序和自动利用程序

    M3M0渗透测试工具 M3m0工具 :crossed_swords: 网站漏洞扫描程序和自动浏览器您可以使用此工具通过在网站中找到漏洞来检查安全性,也可以使用此工具来获取Shell | 污损| cPanels | 资料库 M3M0 :laptop:M3m0工具 :...

    宝箧印塔模型设计m3g

    通过M3G,开发者可以构建复杂的3D场景,包括几何形状、纹理映射、光照效果以及动画等,这些在资源有限的移动设备上显得尤为珍贵。 “宝箧印塔”模型的设计,需要对3D建模有深入理解。在J2ME环境下,通常采用简单...

    jsr184+M3G+API.rar

    【标题】"jsr184+M3G+API.rar" 涉及到的主要技术是Java Mobile 3D Graphics API(JSR 184)和M3G(Mobile 3D Graphics)标准,以及相关的API接口。JSR 184是Java Community Process发布的一个规范,目的是为Java ME...

    M3Gtools 3D开发必备

    5. **动画系统**:M3G支持关节动画和顶点动画,开发者需要学习如何创建和导入动画数据,并在M3GViewer中进行预览。 6. **性能优化**:由于移动设备的硬件限制,优化3D模型的复杂度和渲染效率至关重要。M3GTools可以...

    M3GToolkit-0.5.0

    M3G标准支持包括几何、纹理、光照、动画等多种3D图形特性,使得开发者可以在资源有限的移动设备上实现复杂的3D场景。 M3GToolkit-0.5.0的核心功能在于其提供的EXE文件,位于解压后的BIN目录下。用户只需双击这个...

    一款纯静态的M3U8播放器页面

    源码说明:一个纯静态的M3U8播放器页面,可以直接把M3U8的网址填进去进行播放,超级方便。 部署方法: 可以使用宝塔面板来部署 1,打开宝塔面板,添加一个网站 2,把压缩包上传到站点跟目录,然后解压 3,解压以后...

    联想BIOS_L-IG41M3 V1.1 版本:DMKT05AUS 新圆梦F208

    联想BIOS_L-IG41M3 V1.1 版本:DMKT05AUS 新圆梦F208 原机备份 支持Q9400 支持8G内存 需两条4G双面内存 两个BIOS文件 AFUWIN备份的BIOS BIOS_Backup_TooKit_V2.0备份的BIOS

    tk_m3gtk_v4_5.zip_m3g_tk_m3g_tk_m3gtk_v4_5

    tk_m3gtk_v4_5.zip tk_m3gtk_v4_5.zip 一款M3G文件查看器,Java J2ME中保留模式中必用。

    m3u8Download:java下载m3u8视频,解密并合并ts java download m3u8 video,decrypt & merge ts slice

    m3u8Dowloadjava下载m3u8视频,解密并合并tsm3u8链接获取方式以及代码分析请参见:准备JDK:1.8开发环境:IntelliJ IDEA用于解密的jar包:bcprov-jdk16-139.jar了解一些m3u8标签作用...M3u8DownloadFactory.getInstance...

    jsr_184_midlet.rar_DEMO_jsr184_jsr184-m3g.jar_m3g

    手机 JAVA JSR184 M3G 游戏 DEMO

    M3U8Loader:M3U8装载机

    M3U8装载机加载m3u8并将段连接到mp4谷歌播放谢谢翻译: 中文: TayLin

    m3u8:m3u8文件下载器库以及chrome和firefox扩展

    m3u8 m3u8文件下载器库以及chrome和firefox扩展/插件用法下载并保存m3u8文件: var p = new m3u8_processor . DownloadFileAndSaveInputParams (){ m3u8FileUrl = &lt; M3U8&gt; , OutputFileName = @" C:\abc.avi " ,};...

    cortex_m3_development:使用qemu模拟器-易于学习和cortex-m3

    arm-none-eabi-gcc -mcpu = cortex-m3 -mthumb -g -c test.c -o test.o arm-none-eabi-gcc -mcpu = cortex-m3 -mthumb -g -c startup.c- o startup.o arm-none-eabi-ld -g -T standalone.ld startup.o test.o -o ...

    M3-ExerciciNomsCiutat:Exercici Curs IT学院

    M3-ExerciciNomsCiutat:Exercici Curs IT学院

    M3u Parser:m3u和m3u8解析器-开源

    使用Java的M3u和m3u8解析器

    武汉长光科技有限公司OpticalinkM3-0400G用户手册.pdf

    武汉长光科技有限公司OpticalinkM3-0400G用户手册 武汉长光科技有限公司的OpticalinkM3-0400G用户手册是关于EPON ONU的使用指南,该手册详细介绍了OpticalinkM3-0400G ONU的性能特点、外观结构和安装方法。以下是该...

Global site tag (gtag.js) - Google Analytics