===== Interactive Cast3D Application Sample ===== This example is to show a full complete cycle on how to get a 3D model ( character) motion controlled by a user keyboard commands. Starting with a single 3D model file created in Blender 2.45 modeling tool ( it could be any other modeling tool that supports COLLADA file format export). We will go with step by step instructions from import/export process to creation of 3D model instance in Cast3D and further. First, make sure that you have Cast3D and import utilities version 0.96 or later. ==== Main steps for this sample ==== - import of .blend file into Cast3D .Xc3 xml file. - working with motion groups - setting up rendering scene, loading and displaying 3D model - setting up navigation ==== 1. Importing 3D model ==== * Take this blender [[http://cast3d.org/demos/models/walkfig/figureCompleteExportReady.zip| model]] * Load it into [[http://blender.org|Blender 3D modeling tool]] and export into COLLADA 1.4 file * With 'Dae2Xc3' utility convert into [[http://cast3d.org/demos/models/walkfig/fig.xc3| fig.xc3]] file. C:\work> Dae2X3 -flip -xm walk model.blend mymodel.xc3 As we know that Blender uses Right-Handled coordinate system but flash rendering engines like papervission3D and Sandy3D use left-Handed, '-flip' option is essential. ( otherwise 3d scene most likely will be empty, due to camera inverse settings) Before exporting file from Blender you'll need to install [[fixes:blenderfix |updated (fixed)]] COLLADA export scripts. ==== 2. Working with motion groups ==== == this section is optional == Another option '-xm walk' is very important if you want to attach some other motions like lets say 'jump' from another file. What '-xm' option is going to do is to extract motion into a separate file(s). If there are a number of s, each will be saved in file with name + ordinal number + '.xci' (if no extension specified). The content of will be replaced with statement in main output file. For example, in this case following files will be created 'walk0.xci', 'walk1.xci', walk2.xci' ... To add external motion you need to extract a different type of motion by creating a new motion in original 3D modeling tool, in this case Blender and export in the same way. Lets say we exported it into 'jump0.xci' file. Now you will need to open first imported model file 'mymodel.xc3' and add following lines within element // // to attach motion find destination node element // // and add following element // // One more tip. Now since there are two motion groups attached to 3D model, you do not need to both motion to work at a time. So need to make sure that only one default is enabled by checking and setting attribute og MotionGroup 'enable' = "true/false". // // ==== 2. Setting up 3D scene, loading and displaying 3D model==== Setting up scene consists of following steps: * initial setup of rendering engine. In this tutorial we will be using Papervision3D rendering engine. This has been descried in previous samples. /** * initial setup for Papervision3D. */ public function setupPpv3D(): void { this.viewport = new Viewport3D(300, 400, true, false,false,false); addChild( viewport ); this.scene = new Scene3D(); this.camera = new Camera3D(); this.renderer = new BasicRenderEngine(); } * initial setup of Cast3D animation framework. /** * initial setup for Cast3D. */ public function setupCast3D(): void { // init loading status this.loaded = false; // new Cast3D instance with created earlier 3D scene and camera this.animator = new Cast3d(this.scene, this.camera); // set animation type this.animator.animationType = Cast3d.ANIMATION_TYPE_BYFRAME; // set desired animation frame rate this.animator.fps = 22; // run it once this.animator.autoRewind = false; } * loading file. /** * Function performs 3D data load from a X3c file. */ private function loadData(): void { /** stealth - simple model, no animation */ this.loader = new Xc3Loader("../models/walk/fig.xc3"); this.loader.resourcePath = "../models/walk"; // optimize animation this.loader.preCalcMotion = true; // register notification of load completion this.loader.addEventListener(LoadEvent.LOAD_COMPLETE, this.cast3dLoadComplete); // register notification of load error this.loader.addEventListener(LoadEvent.LOAD_ERROR, this.cast3dLoadError); // proceed with load this.loader.load(this.animator.source); } If you noticed the line this.loader.resourcePath = "../models/walk"; indicated the place where you want to put texture images and other media materials like sound, movie and video and 'include' file ( for example motion group). ==== 3. Setting up navigation ==== * on successful load completion, call 'setupNavigator' to set up navigation handlers /** * Handles the load complete event */ private function cast3dLoadComplete(event: LoadEvent): void { trace("cast3dLoadComplete "); setupNavigator(); this.manipulator = new TrackBall(this.animator,this.stage, this.viewport.viewportWidth, this.viewport.viewportHeight, Manipulator.Y_UP, Manipulator.X_S,Manipulator.Z_S,Manipulator.NY_S ); cp.manipulator = this.manipulator; this.loaded = true; removeTimer(); if (loader.loaderror.length) { statusText.textColor = 0xff0000; statusText.text = loader.loaderror; } else { statusText.visible = false; } this.animator.play(); } * see comments for details /** * Sets up Navigation Controller for Character node */ private function setupNavigator():void { // First lets find the root node of a character // we know by looking at source file, its Id is 'Cube' var nodename:String = "Cube"; var node:Node3d = this.animator.source.find(nodename) as Node3d; if (!node) { statusText.visible = true; statusText.text = "Failed to set Navigation control for node: " + nodename; return; } // create navigation conrloller fo this node. var nc:NavigationController = new NavigationController(node,"navigator"); _navigations.push(nc); // We know that "Cube" node actually represents Skinned geometry, which means // the motion is controlled by another skeleton node(s), in this case "lowerBack" node is // root skeleton node( see source file) nodename = "lowerBack"; node = this.animator.source.find(nodename) as Node3d; if (!node) { statusText.visible = true; statusText.text = "Failed to set Navigation control for node: " + nodename; return; } var tarck_id:String; var motionAlias:String; // now we add a 'walking' motion which is represented by MotionGroup class instance with id == "lowerBack_motion" // again, we know that by looking at source file. // Notice that 'motion' is produced by different node that we created nvigation controlled, which in that case "cube" tarck_id = "lowerBack_motion"; motionAlias = "walk"; if (!nc.addMotion(node, tarck_id, motionAlias)) { statusText.visible = true; statusText.text = "Failed to add Motion " + motionAlias + " for track: " + tarck_id; return; } // another motion motionAlias = "jump"; tarck_id = "lowerBack_motionjump"; if (!nc.addMotion(node, tarck_id, motionAlias)) { statusText.visible = true; statusText.text = "Failed to add Motion " + motionAlias + " for track: " + tarck_id; return; } nc.position.x -= 5.0; nc.rotation.x = 0; nc.rotation.y = 0; nc.rotation.z = 1; nc.rotation.w = 30 * Math.PI/180.0 ; this.stage.addEventListener(KeyboardEvent.KEY_DOWN, this.keyDownHandler); } /** * Navigation keyboard handler */ public function keyDownHandler( event :KeyboardEvent ):void { var nc:NavigationController = _navigations[_current_nav]; if (!nc) return; trace(event.target + "(" + event.currentTarget + "): " + event.keyCode + "/" + event.charCode); switch( event.keyCode ) { case 9: // TAB _current_nav++; if (_current_nav >= _navigations.length) _current_nav = 0; break; case 37: // left // start walking motion ant rotate model over 1/4 of motion cycle ( which is one step) // in local coordinates by 30 degree rotation about Z nc.run("walk",0.25, null, { x:0, y:0, z:1, w: 30.0*Math.PI/180.0 }); break; case 38: // up // start walking motion by moving model over 1/4 of motion cycle ( which is one step) // and propogation node forward in local coordinates by Y = -0.33 nc.run("walk",.25,{x:0, y:-.33, z:0}); break; case 39: // right // start walking motion ant rotate model over 1/4 of motion cycle ( which is one step) // in local coordinates by 30 degree rotation about -Z nc.run("walk",.25, null, {x:0, y:0, z:-1, w:30.0*Math.PI/180.0 }); break; case 40: // down // start walking motion by moving model over 1/4 of motion cycle ( which is one step) // and propogation node backwards in local coordinates by Y = 0.33 // also we reverse timing (last argument) for that motion so characted walks backwards. nc.run("walk",.25,{x:0, y:.33, z:0}, null, true); break; case 32: // space nc.run("jump",1.0); break; } } ==== results ==== So, if done it right you should see something like these: ---- Complete source code of this [[navigsource|sample]].