Loading 3ds Keyframes from Blender
3ds files can also be used for building an animation. In this view I have rigged the man from the previous tutorial and done a walk animation. Although there are 5 key frames in the walk animation in Blender, the last and first are the same so only four are necessary. The key frames are exported one at a time by clicking on the keyframe and then selecting the model rather than the skeleton and exporting the same way. I named these walk1- walk4.
In the code below an animation sequence is added to the object and there is a new function called doAnim() which moves the key frames. The indexes for the animation really start at one, since a zero is a combination of all sequences. Here the animation is set for walk (2); I started loading the animation walk sequence at walk2 because it matches an idle better for a smoother transition from idle to walk.
Note for jPCT-AE users
The basic idea for doing this in jPCT-AE is exactly the same. However, you must not call build() on the object that should get the animation assigned to it before actually adding the animation or the result will become static. The same applies to desktop jPCT when using compiled objects. In that case, use callBoundingBox(); calcNormals(); instead of build() and call build() only after the animation has been assigned.
Example code
import com.threed.jpct.*; import javax.swing.*; public class HelloWorldSoftware { private String[] textures = {"bman"}; private String thingName = "bman"; private int thingScale = 1; private World world; private FrameBuffer buffer; private Object3D thing; private JFrame frame; private int an = 2;//do a walk animation private float ind = 0; public static void main(String[] args) throws Exception { new HelloWorldSoftware().loop(); } public HelloWorldSoftware() throws Exception { frame = new JFrame("Blender Model Loading"); frame.setSize(350, 300); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); world = new World(); world.setAmbientLight(150, 150, 150); for (int i = 0; i < textures.length; ++i) { TextureManager.getInstance().addTexture(textures[i] + ".jpg", new Texture("res/" + textures[i] + ".jpg")); } thing = loadModel("res/" + thingName + ".3ds", thingScale); Animation anim = new Animation(5); anim.createSubSequence("idle"); anim.addKeyFrame(thing.getMesh().cloneMesh(true)); anim.createSubSequence("walk"); anim.addKeyFrame(loadModel("res/" + "walk2.3ds", 1).getMesh()); anim.addKeyFrame(loadModel("res/" + "walk3.3ds", 1).getMesh()); anim.addKeyFrame(loadModel("res/" + "walk4.3ds", 1).getMesh()); anim.addKeyFrame(loadModel("res/" + "walk1.3ds", 1).getMesh()); thing.setAnimationSequence(anim); world.addObject(thing); world.getCamera().setPosition(0, 0, -20); world.getCamera().lookAt(thing.getTransformedCenter()); } private void loop() throws Exception { buffer = new FrameBuffer(350, 300, FrameBuffer.SAMPLINGMODE_NORMAL); while (frame.isShowing()) { doAnim(); buffer.clear(java.awt.Color.BLUE); world.renderScene(buffer); world.draw(buffer); buffer.update(); buffer.display(frame.getGraphics()); Thread.sleep(10); } buffer.disableRenderer(IRenderer.RENDERER_OPENGL); buffer.dispose(); frame.dispose(); System.exit(0); } private Object3D loadModel(String filename, float scale) { Loader.setVertexOptimization(false); Object3D[] model = Loader.load3DS(filename, scale); Object3D o3d = new Object3D(0); Object3D temp = null; for (int i = 0; i < model.length; i++) { temp = model[i]; temp.setCenter(SimpleVector.ORIGIN); temp.rotateX((float)( -.5*Math.PI)); temp.rotateMesh(); temp.setRotationMatrix(new Matrix()); o3d = Object3D.mergeObjects(o3d, temp); o3d.build(); } return o3d; } public void doAnim() { { ind += 0.018f; if (ind > 1f) { ind -= 1f; } } thing.animate(ind, an); } }