Differences

This shows you the differences between the selected revision and the current version of the page.

clonesource 2008/10/09 13:59 current
Line 1: Line 1:
 +<code actionscript>
 +/*------------------------------------------------------------------------
 + * Copyright 2007-2008 (c) Dmitri Sviridov, cast3d.com.
 + *
 + * Permission is hereby granted, free of charge, to any person
 + * obtaining a copy of this software and associated documentation
 + * files (the "Software"), to deal in the Software without
 + * restriction, including without limitation the rights to use,
 + * copy, modify, merge, publish, distribute, sublicense, and/or sell
 + * copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following
 + * conditions:
 + *
 + * The above copyright notice and this permission notice shall be
 + * included in all copies or substantial portions of the Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 + * OTHER DEALINGS IN THE SOFTWARE.
 + *------------------------------------------------------------------------ */
 + /**
 + *
 + * @author Dmitri Sviridov - sds
 + * @version .90
 + * @date April, 23 2008
 + */
 +
 +package {
 + import flash.display.Sprite;
 + import flash.display.StageAlign;
 + import flash.display.StageScaleMode;
 + import flash.events.*;
 + import flash.utils.getTimer;
 +    import flash.utils.Timer;
 +    import flash.text.TextField;
 + import flash.text.TextFieldAutoSize;
 + import flash.text.TextFieldType;
 + import flash.display.MovieClip;
 +    import flash.net.URLRequest;
 +
 +    // Cast3D dependencies
 + import cast3d.core.Cast3d;
 + import cast3d.core.events.LoadEvent;
 + import cast3d.loader.Xc3Loader;
 + import cast3d.utils.controls.*;
 + import cast3d.utils.manipulators.Manipulator;
 + import cast3d.utils.manipulators.ppv2.TrackBall;
 + import cast3d.utils.controllers.NavigationController;
 + import cast3d.nodes.Node3d;
 + import cast3d.tracks.Track3d;
 + import cast3d.geom.Part3d;
 + import cast3d.geom.Skin3d;
 + import cast3d.frames.KeyFrame3d;
 +
 +    // Papervision3d dependencies
 + import org.papervision3d.scenes.Scene3D;
 +
 + // Import Papervision3D
 + import org.papervision3d.cameras.*;
 + import org.papervision3d.scenes.*;
 +
 + import org.papervision3d.cameras.*;
 + import org.papervision3d.scenes.*;
 + import org.papervision3d.lights.*;
 + import org.papervision3d.render.*;
 + import org.papervision3d.view.*;
 + import org.papervision3d.materials.*;
 + import org.papervision3d.core.proto.MaterialObject3D;
 +
 +
 + [SWF(backgroundColor="#335566", frameRate="30")]
 +
 + public class Sample extends Sprite
 + {
 + private var manipulator:TrackBall;
 + private var animator:Cast3d;
 + private var cp:ControlPanel;
 +
 + private var scene:Scene3D;
 +     private var camera:Camera3D;    
 + private var viewport:Viewport3D;
 + private var renderer:BasicRenderEngine;
 +    
 + private var loader:Xc3Loader;
 + private var loaded:Boolean;
 +
 + private var statusText:TextField;
 + private var statusTimer:Timer;
 +
 +     private var _navigations:Array =  new Array;
 +     private var _current_nav:int = 0;
 +
 + public function Sample()
 + {
 + setup3DScene();
 + setupStage();      
 + }
 +
 + /**
 + * Configures the Stage object
 + */
 + private function setupStage(): void
 + {
 + this.stage.scaleMode = StageScaleMode.NO_SCALE;
 + this.stage.align = StageAlign.TOP_LEFT;
 + }
 +
 + private function setup3DScene(): void
 + {
 + this.setupPpv3D();
 + this.setupCast3D();
 + this.setupControls();
 + this.loadData();
 +
 + this.addEventListener(Event.ENTER_FRAME, this.handleEnterFrame);
 + }
 +
 +  /**
 + * 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 for Cast3D.
 + */
 + public function setupCast3D(): void
 + {
 +            this.loaded = false;
 + this.animator = new Cast3d(this.scene, this.camera);
 + this.animator.animationType = Cast3d.ANIMATION_TYPE_BYFRAME; //  ANIMATION_TYPE_REAL; //
 + this.animator.animationStyle = Cast3d.ANIMATION_STYLE_FORWARD;
 + Cast3d.fps = 22;
 + this.animator.autoRewind = true;
 + }
 +
 +  /**
 + *  Function setups visual animation control panel.
 + */
 + public function setupControls(): void
 + {
 + cp = new ControlPanel(animator);
 + this.stage.addChild(cp);
 +// cp.visible = false;
 +
 + statusText = new TextField();
 + statusText.textColor = 0x0000ff;
 +            statusText.autoSize = TextFieldAutoSize.LEFT;
 +            statusText.type = TextFieldType.DYNAMIC;
 +            statusText.y = cp.height;
 +            this.stage.addChild(statusText);
 + }
 +
 +  /**
 + *  Function performs 3D data  load from a X3c file.
 + */
 + private function loadData(): void
 + {
 + var modelpath:String = loaderInfo.parameters.model;
 + var rpath:String = loaderInfo.parameters.rpath;
 +
 + this.loader = new Xc3Loader( modelpath ? modelpath:  "");
 + this.loader.resourcePath = rpath ? rpath : "";
 +
 +            if (!modelpath || modelpath.length == 0)
 +            {           
 + this.loader = new Xc3Loader("../../cast3dImport/models/walk/fig.xc3");
 + this.loader.resourcePath = "../../cast3dImport/models/walk";
 +            }
 +
 + statusText.text = "loading file: " + loader.sourceURL;
 + statusTimer = new Timer(1000, 0.2);
 + // designates listeners for the interval and completion events
 + statusTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
 + statusTimer.start();
 +
 + this.loader.preCalcMotion = false;
 + this.loader.addEventListener(LoadEvent.LOAD_COMPLETE, this.cast3dLoadComplete);
 + this.loader.addEventListener(LoadEvent.LOAD_ERROR, this.cast3dLoadError);
 + this.loader.addEventListener(LoadEvent.LOAD_PROGRESS, this.cast3dLoadProgress);
 +// this.loader.portLoader.onCreateColorMaterial = onColorMaterial;
 +// this.loader.portLoader.onCreateAssetMaterial = onAssetMaterial;
 +
 + /** Set Call back functions to modify, augumnet or subsitute
 + *  material or/and geometry data. */
 +
 + this.loader.load(this.animator.source);
 +
 + }
 +
 + private function onColorMaterial (name:String, material:MaterialObject3D):MaterialObject3D
 + {
 +     material = new WireframeMaterial(0x777700,1);
 +     material.doubleSided = true;
 + return material;
 +      }
 +     
 + private function onAssetMaterial ( name:String, material:BitmapMaterial):MaterialObject3D
 + {
 +            return new WireframeMaterial(0x00FF00,1);
 +      }
 + /**
 + * Timer handler
 + */
 +      private function onTimerComplete(event:TimerEvent):void
 +      {
 +            trace("Time's Up!");
 + statusText.textColor = 0xff0000;
 +            statusText.text = "Loading time exceeded 20 nimutes!";
 +            removeTimer();                         
 +      }
 +       
 + /**
 + * At the end of load, removes timer
 + */
 +      private function removeTimer():void
 +        {
 +          statusTimer.stop();
 +   statusTimer.removeEventListener(TimerEvent.TIMER_COMPLETE,onTimerComplete);
 +    }
 +   
 + /**
 + * Handles the ENTER_FRAME event and updates the 3D scene.
 + */
 + private function handleEnterFrame(event: Event): void
 + {
 +            if (!this.loaded) return;
 +           
 + var time:Number = getTimer();
 +
 + // Update cast3D first
 + this.animator.render();
 +
 + // then render scene
 + this.renderer.renderScene(scene, camera, viewport);
 +
 + // Update stat data
 + if (this.animator.source && cp)
 + {
 + var frame:int = this.animator.source.currentFrame;
 + var kframe:int = this.animator.source.currentKeyFrame;
 + cp.setCurrentFrame(kframe,frame);
 + cp.setCurrentTime(animator.currentTime);
 + cp.currentFps = 1000.0/(getTimer() - time);
 + if (this.manipulator) manipulator.update();
 + }
 + }
 +
 + private function daeLoadComplete(e:Event):void {
 + trace("loaded");
 +// view.singleRender();
 + 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
 + );
 +            return;
 + }
 +
 + /**
 + * Handles the load complete event
 + */
 + private function cast3dLoadComplete(event: LoadEvent): void
 + {
 +            makeClone();
 + setupNavigator();
 +
 + trace("cast3dLoadComplete ");
 + 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
 + );
 +
 +// manipulator.showCamera = true;
 +// manipulator.showCOR = true;
 + 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();
 + }
 +
 + private function cast3dLoadProgress(event: LoadEvent): void
 + {
 +            var n:Number = event.scenesTotal != 0.0 ? event.scenesLoaded/event.scenesTotal : 0;
 +            var percent:int = n*100;
 + statusText.text = "Loading " + event.file + " ....... "+ percent.toString() + "%";
 + }
 +
 + /**
 + * Handles the load Error event
 + */
 + private function cast3dLoadError(event: LoadEvent): void
 + {
 + trace("cast3dLoadError ", event.message );
 +            removeTimer();
 +  statusText.textColor = 0xff0000;                         
 + statusText.text = event.message;
 + }
 +    
 + /**
 + * This functin make a clone of Model node and sets Navigaion Contraller
 + */
 +      private function makeClone():void
 +      {
 +          // First lets find the root node of a character
 +          // we know in advance it's Id is 'Cube'
 +          var nodename:String = "Cube";
 +         var model_node:Node3d = this.animator.source.find(nodename) as Node3d;
 +         if (!model_node)
 +         {
 +   statusText.visible = true;
 +         statusText.text = "Failed to make clone. Not found node: " + nodename;
 +             return;
 +         }
 +        
 +          // next step is to make a clone instance of a character node
 +            var cloned_model_node:Node3d = model_node.clone();           
 +           
 +          // A cloned instance of a node if not attached is handing in the air
 +          // To make it visible we need to add it to a scene, whic is KeyFrame
 +          // There is only one KeyFrame ( with infinite duration) and we know Id
 +          // in advance by looking at the file.
 +          var kfname:String = "Scene_kf";
 +         var kf:KeyFrame3d = this.animator.source.find(kfname) as KeyFrame3d;
 +         if (!kf)
 +         {
 +   statusText.visible = true;
 +         statusText.text = "Failed to make clone. Not found keyframe: " + kfname;
 +             return;
 +         }
 +         // adding cloned node to Keyframe
 +            kf.addNode(cloned_model_node);
 +            // This step is also importan to understand.
 +            // Although we added node to a scene, the actual rendering is taking place
 +            // in Rendering engine, in this case papervision3D. So Cast3d is just manupulating
 +            // with transforms and geometry. This step populates rendering engine with newly created
 +            // node's data. Xc3 file loader does it for you, after it's done, you need to do that explicitly.
 +            cloned_model_node.register(this.animator, kf);
 +           
 +            // lets create navigation conrloller for new node.
 +         var nc:NavigationController = new NavigationController(cloned_model_node,"navigator");
 +         _navigations.push(nc);
 +                
 +          // We know that "cube" node actually represents Skined 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";
 +         var skeleton_node:Node3d = this.animator.source.find(nodename) as Node3d;
 +        
 +         // lets make a clone of that too. Otherwise both original chatecter skin and cloned one 
 +         // will be controlled by same skeleton node(s).
 +         var cloned_skeleton_node:Node3d = skeleton_node.clone();
 +         if (!cloned_skeleton_node)
 +         {
 +   statusText.visible = true;
 +         statusText.text = "Failed to set Cloning for node: " + nodename;
 +             return;
 +         }
 +         // add to same scene
 +            kf.addNode(cloned_skeleton_node);
 +         // populate rendering engine
 +            cloned_skeleton_node.register(this.animator, kf);
 +                    
 + // This step required only for Skin
 +            var skin:Skin3d = cloned_model_node.part as Skin3d;
 +         if (skin)
 +         {
 +         // The cloned version of skinned node holds the references to original skeleton bones
 +         // Now we need to reassign to the bones of new ( cloned) skeleton nodes
 +         // binding works in the way that it tries to find matching id of old bone in provided skeleton bone branhes
 +         // once it finds it does the replacement. If fails to find any single bone, the whole process fails.
 +         if (!skin.bindBones(cloned_skeleton_node))
 +         {
 +   statusText.visible = true;
 +         statusText.text = "Failed to make clone. Clould not bind skin: " + skin.id + " to node: " + cloned_skeleton_node.id;
 +             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.
 +         tarck_id = "lowerBack_motion";
 +         motionAlias = "walk";        
 +         if (!nc.addMotion(cloned_skeleton_node, tarck_id, motionAlias))
 +         {
 +   statusText.visible = true;
 +         statusText.text = "Failed to add Motion " + motionAlias + " for track: " + tarck_id;
 +             return;
 +         }
 +        
 +         // another motion is a 'jump' motion  with id == "lowerBack_motionjump"
 +         motionAlias = "jump";
 +         tarck_id = "lowerBack_motionjump";
 +         if (!nc.addMotion(cloned_skeleton_node, tarck_id, motionAlias))
 +         {
 +   statusText.visible = true;
 +         statusText.text = "Failed to add Motion " + motionAlias + " for track: " + tarck_id;
 +             return;
 +         }
 +        
 +         // lets move the newly created model away from intial position so it does not interlap with original
 +         nc.position.x += 5.0;
 +         nc.rotation.x = 0; nc.rotation.y = 0; nc.rotation.z = 1; nc.rotation.w = 60 * Math.PI/180.0 ;
 +     this.stage.addEventListener(KeyboardEvent.KEY_DOWN, this.keyDownHandler);
 +      }
 +    
 +      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);
 +      }
 +
 + 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;
 + }
 + }
 +    
 + }
 +}
 +
 +</code>