📄 md2.as
字号:
/**
* animated model
* based on MD2 loader
* by Philippe Ajoux (philippe.ajoux@gmail.com)
* also minor changes
* @author John Sword
* @version 2 - AS3
*/
package engine.objects
{
import engine.camera.Camera3D;
import engine.geom.UV;
import engine.geom.Vertex;
import engine.materials.Material;
import engine.materials.Wireframe;
import engine.utils.Frame;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.utils.getTimer;
public class MD2 extends Object3D
{
public var fps:int = 6;
/**
* Three kinds of animation sequences:
* [1] Normal (sequential, just playing)
* [2] Loop (a loop)
* [3] Stop (stopped, not animating)
*/
public static const ANIM_NORMAL:int = 1;
public static const ANIM_LOOP:int = 2;
public static const ANIM_STOP:int = 4;
/**
* Keep track of the current frame number and animation
*/
private var frames:Array = new Array();
private var keyframe:int = 0;
//private var iterator:Number = 5;
private var interp:Number = 1;
private var start:int, end:int, type:int;
private var ctime:Number = 0, otime:Number = 0;
private var animationList:Array = new Array();
private var currentAnim:String = "stand"; // default animation
// [internal] Used for loading the MD2 file
private var file:String;
private var loader:URLLoader;
private var loadScale:Number;
// [internal] Md2 file format has a bunch of header information
// that is typically just read straight into a C-style struct, but
// since this is not C =( we have to create variables for all of it.
private var ident:int, version:int;
private var skinwidth:int, skinheight:int;
private var framesize:int;
private var num_skins:int, num_vertices:int, num_st:int;
private var num_tris:int, num_glcmds:int, num_frames:int;
private var offset_skins:int, offset_st:int, offset_tris:int;
private var offset_frames:int, offset_glcmds:int, offset_end:int;
public function MD2 ( filename:String, m:Material = null )
{
isVisible = false;
flipNormals = true;
material = m;
// optimal scale
//this.scale = new Vector(4,4,4);
file = filename;
load(filename);
}
private function load ( filename:String ) : void
{
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, parse);
try
{
loader.load( new URLRequest( filename ) );
}
catch(e:Error)
{
trace("Error in loading MD2 file (" + filename + "): \n" + e.message + "\n" + e.getStackTrace());
}
}
private function parse ( event:Event ) : void
{
var a:int, b:int, c:int, ta:int, tb:int, tc:int;
var vertices:Array = super.oVertices;
var faces:Array = super.faces;
var i:int, uvs:Array = new Array();
var data:ByteArray = loader.data;
// Make sure to have this in Little Endian or you will hate you life.
// At least I did the first time I did this for a while.
data.endian = Endian.LITTLE_ENDIAN;
// Read the header and make sure it is valid MD2 file
readMd2Header(data);
if (ident != 844121161 || version != 8)
throw new Error("Error loading MD2 file (" + file + "): Not a valid MD2 file/bad version");
// Vertice setup
// Be sure to allocate memory for the vertices to the object
// These vertices will be updated each frame with the proper coordinates
for (i = 0; i < num_vertices; i++)
createVertex( new Vertex () );
//for (i = 0; i < num_vertices; i++)
// tVertices.push( new Vertex() );
// UV coordinates
// Load them!
data.position = offset_st;
for (i = 0; i < num_st; i++)
uvs.push(new UV(data.readShort() / skinwidth, 1 - ( data.readShort() / skinheight) ));
// Faces
// Creates the faces with the proper references to vertices
// NOTE: DO NOT change the order of the variable assignments here,
// or nothing will work.
data.position = offset_tris;
var faceuvs:Array = new Array();
var m:Wireframe = new Wireframe ();
for (i = 0; i < num_tris; i++) {
a = data.readUnsignedShort();
b = data.readUnsignedShort();
c = data.readUnsignedShort();
ta = data.readUnsignedShort();
tb = data.readUnsignedShort();
tc = data.readUnsignedShort();
createFace ( a, b, c, uvs[ta],uvs[tb],uvs[tc], material );
}
// Frame animation data
// This part is a little funky.
data.position = offset_frames;
readFrames(data);
loader.close();
isVisible = true;
if ( interactive ) setInteractive();
dispatchEvent ( new Event("LOAD") );
}
/**
* Reads in all the frames
*/
private function readFrames(data:ByteArray):void
{
var sx:Number, sy:Number, sz:Number;
var tx:Number, ty:Number, tz:Number;
var verts:Array, frame:Frame;
var i:int, j:int, char:int;
var x:Number,y:Number,z:Number;
// rotate model to conform with engine coordinate system
var xXrot:Number,yXrot:Number,zXrot:Number;
var xZrot:Number,yZrot:Number,zZrot:Number;
// set rotation values
var rotX:Number = 90 * (Math.PI / 180);
var rotZ:Number = rotX;
//var rotZ:Number = 90 * (Math.PI / 180);
// use a regular expression to remove frame numbers
// and get the animation name
// not a very elegant solution
var pattern:RegExp = /[0-9]/gi;
var animName:String = "";
for (i = 0; i < num_frames; i++)
{
verts = new Array();
frame = new Frame("", verts);
sx = data.readFloat();
sy = data.readFloat();
sz = data.readFloat();
tx = data.readFloat();
ty = data.readFloat();
tz = data.readFloat();
for (j = 0; j < 16; j++)
if ((char = data.readUnsignedByte()) != 0)
frame.name += String.fromCharCode(char);
animName = frame.name.replace(pattern, "");
var animation:Object = animationList[animName];
if ( animation == null )
{
var anim:Object = new Object();
anim.name = animName;
anim.start = i;
anim.end = i;
animationList[animName] = anim;
} else {
anim.end = i;
}
// Note, the extra data.position++ in the for loop is there
// to skip over a byte that holds the "vertex normal index"
for (j = 0; j < num_vertices; j++, data.position++)
{
x = ((sx * data.readUnsignedByte()) + tx) * 4;
y = ((sy * data.readUnsignedByte()) + ty) * 4;
z = ((sz * data.readUnsignedByte()) + tz) * 4;
//rotate around Z-axis
xZrot = x;
yZrot = Math.cos(rotX)*y - Math.sin(rotX)*z;
zZrot = Math.sin(rotX)*y + Math.cos(rotX)*z;
// then rotate around X-axis
xXrot = Math.cos(rotZ)*xZrot - Math.sin(rotZ)*zZrot;
yXrot = yZrot;
zXrot = Math.sin(rotZ)*xZrot + Math.cos(rotZ)*zZrot;
verts.push(new Vertex ( xXrot,yXrot,zXrot ));
}
frames.push(frame);
}
}
/**
* Reads in all that MD2 Header data that is declared as private variables.
* I know its a lot, and it looks ugly, but only way to do it in Flash
*/
private function readMd2Header(data:ByteArray):void
{
ident = data.readInt();
version = data.readInt();
skinwidth = data.readInt();
skinheight = data.readInt();
framesize = data.readInt();
num_skins = data.readInt();
num_vertices = data.readInt();
num_st = data.readInt();
num_tris = data.readInt();
num_glcmds = data.readInt();
num_frames = data.readInt();
offset_skins = data.readInt();
offset_st = data.readInt();
offset_tris = data.readInt();
offset_frames = data.readInt();
offset_glcmds = data.readInt();
offset_end = data.readInt();
}
public override function project ( cam:Camera3D, occlusion:Boolean = false ) : int
{
ctime = getTimer();
var dst:Vertex, a:Vertex, b:Vertex;
var cframe:Frame, nframe:Frame;
if ( keyframe == frames.length ) keyframe = 0;
cframe = frames[keyframe];
nframe = frames[(keyframe + 1) % frames.length];
//var len:int = oVertices.length;
//var i:int;
//for (i = 0; i < len; i++)
//{
var i:int = oVertices.length;
while ( --i > -1 )
{
dst = oVertices[i];
a = cframe.vertices[i];
b = nframe.vertices[i];
dst.x = a.x + interp * (b.x - a.x);
dst.y = a.y + interp * (b.y - a.y);
dst.z = a.z + interp * (b.z - a.z);
}
// Update the timer part, to get time based animation
if (type != ANIM_STOP)
{
interp += fps * (ctime - otime) / 1000;
if (interp >= 1)
{
if (type == ANIM_LOOP && keyframe + 1 == end)
keyframe = start;
else
keyframe++;
interp = 0;
}
}
otime = ctime;
/*
iterator ++
if ( iterator > frameSpeed ) {
keyframe ++;
iterator = 0;
}
if ( keyframe == frames.length ) keyframe = 0;
var frame:Frame = frames[keyframe];
oVertices = frame.vertices;
*/
if ( type != ANIM_STOP ) renderMe = true;
return super.project( cam, occlusion );
}
public function gotoAndPlay ( frame:int ) : void
{
keyframe = frame;
type = ANIM_NORMAL;
}
public function play ( anim:String ) : Boolean
{
if ( !currentAnim || currentAnim == anim ) return false;
var animation:Object = animationList[anim];
if ( animation )
{
loop ( animation.start, animation.end );
currentAnim = anim;
return true;
}
return false;
}
public function loop( start:int, end:int ) : void
{
this.start = (start % frames.length);
keyframe = start;
this.end = (end % frames.length);
type = ANIM_LOOP;
}
public function stop():void
{
type = ANIM_STOP;
}
public function gotoAndStop(frame:int):void
{
keyframe = frame;
type = ANIM_STOP;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -