Last active
August 29, 2015 14:06
-
-
Save ruby0x1/a13f224d4b8398a9d76c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package enhaxe.resource.impl; | |
import enhaxe.math.Aabb; | |
import enhaxe.math.DualQuat; | |
import enhaxe.math.Mat44; | |
import enhaxe.math.Quat; | |
import enhaxe.math.Vec3; | |
import enhaxe.rendering.IGeometry; | |
import enhaxe.resource.Resource.IResourceData; | |
import enhaxe.resource.Resource.ResourceContext; | |
import enhaxe.resource.Resource.IGPUResourceData; | |
import enhaxe.resource.Resource.GPUResourceContext; | |
import foo3d.RenderDevice; | |
import haxe.io.Bytes; | |
import haxe.io.BytesData; | |
import haxe.io.BytesInput; | |
import haxe.io.BytesOutput; | |
typedef IqmHeader = { | |
var filesize:Int; | |
var flags:Int; | |
var num_text:Int; var ofs_text:Int; | |
var num_meshes:Int; var ofs_meshes:Int; | |
var num_vertexarrays:Int; var num_vertices:Int; var ofs_vertexarrays:Int; | |
var num_triangles:Int; var ofs_triangles:Int; var ofs_adjacency:Int; | |
var num_joints:Int; var ofs_joints:Int; | |
var num_poses:Int; var ofs_poses:Int; | |
var num_anims:Int; var ofs_anims:Int; | |
var num_frames:Int; var num_framechannels:Int; var ofs_frames:Int; var ofs_bounds:Int; | |
var num_comment:Int; var ofs_comment:Int; | |
var num_extensions:Int; var ofs_extensions:Int; | |
} | |
typedef IqmVertexArray = { | |
var type:Int; | |
var flags:Int; | |
var format:Int; | |
var size:Int; | |
var offset:Int; | |
} | |
typedef IqmMesh = { | |
var name:String; | |
var material:String; | |
var vstart:Int; var bcount:Int; | |
var aabb:Aabb; | |
} | |
class IqmJoint { | |
public var name:String; | |
public var parent:Int; | |
public var pos:Vec3; | |
public var scale:Vec3; | |
public var rot:Quat; | |
public var mBase:Mat44; | |
public var mInvBase:Mat44; | |
public var dqBase:DualQuat; | |
public var dqInvBase:DualQuat; | |
public function new() { | |
name = ""; | |
parent = -1; | |
pos = new Vec3(0,0,0); | |
scale = new Vec3(1,1,1); | |
rot = new Quat(0,0,0,1); | |
mBase = new Mat44(); | |
mInvBase = new Mat44(); | |
dqBase = new DualQuat(); | |
dqInvBase = new DualQuat(); | |
} | |
} | |
typedef IqmAnim = { | |
var name:String; | |
var first_frame:Int; var num_frames:Int; | |
var framerate:Float; | |
var flags:Int; | |
} | |
typedef IqmPose = { | |
var parent:Int; | |
var mask:Int; | |
var channeloffset:Array<Float>; | |
var channelscale:Array<Float>; | |
} | |
typedef IqmData = { | |
layout:Int, | |
vBufs:Array<VBufferInfo>, | |
iBuf:IBufferInfo | |
} | |
class Iqm | |
implements IResourceData | |
implements IGPUResourceData | |
implements IGeometry | |
{ | |
public static var NULL:Iqm = null; | |
inline public static var IQM_POSITION:Int = 0; // float, 3 | |
inline public static var IQM_TEXCOORD:Int = 1; // float, 2 | |
inline public static var IQM_NORMAL:Int = 2; // float, 3 | |
inline public static var IQM_TANGENT:Int = 3; // float, 4 | |
inline public static var IQM_BLENDINDEXES:Int = 4; // ubyte, 4 | |
inline public static var IQM_BLENDWEIGHTS:Int = 5; // ubyte, 4 | |
inline public static var IQM_COLOR:Int = 6; // ubyte, 4 | |
inline public static var IQM_BYTE:Int = 0; | |
inline public static var IQM_UBYTE:Int = 1; | |
inline public static var IQM_SHORT:Int = 2; | |
inline public static var IQM_USHORT:Int = 3; | |
inline public static var IQM_INT:Int = 4; | |
inline public static var IQM_UINT:Int = 5; | |
inline public static var IQM_HALF:Int = 6; | |
inline public static var IQM_FLOAT:Int = 7; | |
inline public static var IQM_DOUBLE:Int = 8; | |
inline public static var IQM_LOOP:Int = (1<<0); | |
public var src:String; | |
public var ctx:ResourceContext; | |
public var gpuCtx:GPUResourceContext; | |
public var data:GeometryData; | |
public var meshes:Array<IqmMesh>; | |
public var joints:Array<IqmJoint>; | |
public var anims:Array<IqmAnim>; | |
public var poses:Array<IqmPose>; | |
public var frames:Array<Mat44>; | |
public var dqFrames:Array<DualQuat>; | |
static var defaultVertexLayout:Int = -1; | |
static var skinnedVertexLayout:Int = -1; | |
public static function create(_src:String):Iqm { | |
return new Iqm(_src); | |
} | |
public function new(_src:String) { | |
src = _src; | |
ctx = new ResourceContext(); | |
gpuCtx = new GPUResourceContext(); | |
} | |
public static function registerVertexLayout(_rd:RenderDevice):Void { | |
defaultVertexLayout = _rd.registerVertexLayout([ | |
new RDIVertexLayoutAttrib("vPos", 0, 3, 0), | |
new RDIVertexLayoutAttrib("vUv", 1, 2, 0), | |
new RDIVertexLayoutAttrib("vNormal", 2, 3, 0), | |
new RDIVertexLayoutAttrib("vTangent", 3, 4, 0) | |
]); | |
skinnedVertexLayout = _rd.registerVertexLayout([ | |
new RDIVertexLayoutAttrib("vPos", 0, 3, 0), | |
new RDIVertexLayoutAttrib("vUv", 1, 2, 0), | |
new RDIVertexLayoutAttrib("vNormal", 2, 3, 0), | |
new RDIVertexLayoutAttrib("vTangent", 3, 3, 0), | |
new RDIVertexLayoutAttrib("vJointIndices", 4, 4, 0, RDIDataType.UNSIGNED_BYTE), | |
new RDIVertexLayoutAttrib("vWeights", 5, 4, 0, RDIDataType.UNSIGNED_BYTE) | |
]); | |
} | |
public function load(_data:Dynamic):Int { | |
var time:Int = Std.int(haxe.Timer.stamp()*1000); | |
var assetData = Core.app.assets.bytes(src); | |
if (assetData == null) { | |
enhaxe.Core.log("[Iqm::load]: Unknown file: " + src, 0); | |
return 0; | |
} | |
try { | |
var fin:BytesInput = new BytesInput(assetData.bytes); | |
fin.bigEndian = false; | |
var id = fin.readString(16); | |
var version = fin.readInt32(); | |
if (!StringTools.startsWith(id, "INTERQUAKEMODEL") || version != 2) { | |
enhaxe.Core.log("ERROR [Iqm::load]: Invalid geometry file! " + src, 0); | |
return 0; | |
} | |
_data.geometry = _parse(fin); | |
enhaxe.Core.log("Iqm parsing: " + (Std.int(haxe.Timer.stamp()*1000) - time) + "ms of " + src); | |
} catch (e:Dynamic) { | |
enhaxe.Core.log("[Iqm::load]: " + e, 0); | |
return 0; | |
} | |
return 1; | |
} | |
public function onLoadComplete(_data:Dynamic):Void { | |
this.data = new GeometryData( | |
_data.geometry.layout, | |
cast _data.geometry.vBufs, | |
cast _data.geometry.iBuf, | |
null | |
); | |
//this.meshes = _data.geometry.meshes; | |
//this.anims = _data.geometry.anims; | |
} | |
public function onUnloadComplete():Void { | |
this.dqFrames = null; | |
this.frames = null; | |
this.joints = null; | |
this.meshes = null; | |
this.anims = null; | |
this.data = null; | |
} | |
public function prepare(_rd:Dynamic):Void { | |
var time:Int = Std.int(haxe.Timer.stamp()*1000); | |
for (i in 0...this.data.vBufs.length) { | |
var vb = this.data.vBufs[i]; | |
vb.handle = _rd.createVertexBuffer(vb.raw.length, vb.raw, RDIBufferUsage.STATIC, vb.stride); | |
} | |
var ib = this.data.iBuf; | |
ib.handle = _rd.createIndexBuffer(ib.raw.length, ib.raw, RDIBufferUsage.STATIC); | |
enhaxe.Core.log("Iqm upload: " + (Std.int(haxe.Timer.stamp()*1000) - time) + "ms of " + src); | |
} | |
public function unprepare(_rd:Dynamic):Void { | |
for (i in 0...this.data.vBufs.length) { | |
_rd.destroyBuffer(this.data.vBufs[i].handle); | |
this.data.vBufs[i].handle = -1; | |
} | |
_rd.destroyBuffer(this.data.iBuf.handle); | |
this.data.iBuf.handle = -1; | |
this.data.jMats = null; | |
} | |
inline public function getNull():IResourceData { | |
if (Iqm.NULL == null) { | |
registerVertexLayout(Core.renderDevice); | |
NULL = create("null.iqm"); | |
var data = { geometry: { | |
layout: defaultVertexLayout, | |
vBufs: [], | |
iBuf: new IBufferInfo(-1, RDIDataType.UNSIGNED_INT, haxe.io.Bytes.alloc(0)), | |
meshes: [] | |
}}; | |
//NULL.load(data); | |
NULL.onLoadComplete(data); | |
NULL.prepare(Core.renderDevice); | |
NULL.ctx.state = ResourceContext.LOADED; | |
NULL.gpuCtx.state = GPUResourceContext.PREPARED; | |
} | |
return NULL; | |
} | |
inline function _parse(_fin:BytesInput):IqmData { | |
// parse header | |
var hdr:IqmHeader = { | |
filesize: _fin.readInt32(), | |
flags: _fin.readInt32(), | |
num_text: _fin.readInt32(), ofs_text: _fin.readInt32(), | |
num_meshes: _fin.readInt32(), ofs_meshes: _fin.readInt32(), | |
num_vertexarrays: _fin.readInt32(), num_vertices: _fin.readInt32(), ofs_vertexarrays: _fin.readInt32(), | |
num_triangles: _fin.readInt32(), ofs_triangles: _fin.readInt32(), ofs_adjacency: _fin.readInt32(), | |
num_joints: _fin.readInt32(), ofs_joints: _fin.readInt32(), | |
num_poses: _fin.readInt32(), ofs_poses: _fin.readInt32(), | |
num_anims: _fin.readInt32(), ofs_anims: _fin.readInt32(), | |
num_frames: _fin.readInt32(), num_framechannels: _fin.readInt32(), ofs_frames: _fin.readInt32(), ofs_bounds: _fin.readInt32(), | |
num_comment: _fin.readInt32(), ofs_comment: _fin.readInt32(), | |
num_extensions: _fin.readInt32(), ofs_extensions: _fin.readInt32(), | |
}; | |
trace(hdr); | |
// vertexarrays are non-interleaved component buffers | |
_fin.position = hdr.ofs_vertexarrays; | |
var vBufs:Array<VBufferInfo> = []; | |
if (hdr.num_vertexarrays > 0) { | |
for (i in 0...hdr.num_vertexarrays) { | |
var va:IqmVertexArray = { | |
type: _fin.readInt32(), | |
flags: _fin.readInt32(), | |
format: _fin.readInt32(), | |
size: _fin.readInt32(), | |
offset: _fin.readInt32() | |
}; | |
var valid:Bool = false; | |
var elmSize:Int = 0; | |
switch(va.type) { | |
case IQM_POSITION: va.format != IQM_FLOAT || va.size != 3 ? | |
Core.log("[Iqm::_parse]: IQM_POSITION is invalid!", 0) : valid = true; elmSize = 4; | |
case IQM_NORMAL: va.format != IQM_FLOAT || va.size != 3 ? | |
Core.log("[Iqm::_parse]: IQM_NORMAL is invalid!", 0) : valid = true; elmSize = 4; | |
case IQM_TANGENT: va.format != IQM_FLOAT || va.size != 4 ? | |
Core.log("[Iqm::_parse]: IQM_TANGENT is invalid!", 0) : valid = true; elmSize = 4; | |
case IQM_TEXCOORD: va.format != IQM_FLOAT || va.size != 2 ? | |
Core.log("[Iqm::_parse]: IQM_TEXCOORD is invalid!", 0) : valid = true; elmSize = 4; | |
case IQM_BLENDINDEXES: va.format != IQM_UBYTE || va.size != 4 ? | |
Core.log("[Iqm::_parse]: IQM_BLENDINDEXES are invalid!", 0) : valid = true; elmSize = 1; | |
case IQM_BLENDWEIGHTS: va.format != IQM_UBYTE || va.size != 4 ? | |
Core.log("[Iqm::_parse]: IQM_BLENDWEIGHTS are invalid!", 0) : valid = true; elmSize = 1; | |
case IQM_COLOR: va.format != IQM_UBYTE || va.size != 4 ? | |
Core.log("[Iqm::_parse]: IQM_COLOR is invalid!", 0) : valid = true; elmSize = 1; | |
default: | |
Core.log("[Iqm::_parse]: Unknown vertexarray type! " + va.type, 1); | |
} | |
if (!valid) | |
continue; | |
var oldPos = _fin.position; | |
_fin.position = va.offset; | |
var v = new BytesOutput(); | |
for (i in 0...hdr.num_vertices*va.size*elmSize) { | |
v.writeByte(_fin.readByte()); // just move the bytes | |
} | |
_fin.position = oldPos; | |
vBufs.push(new VBufferInfo(-1, 0, va.size*elmSize, v.getBytes())); | |
} | |
} | |
// triangles are indexbuffers | |
_fin.position = hdr.ofs_triangles; | |
var inds = new BytesOutput(); | |
for (i in 0...hdr.num_triangles*3*4) { | |
inds.writeByte(_fin.readByte()); // just move the bytes | |
} | |
var iBuf = new IBufferInfo(-1, RDIDataType.UNSIGNED_INT, inds.getBytes()); | |
// meshes | |
this.meshes = []; | |
for (i in 0...hdr.num_meshes) { | |
_fin.position = hdr.ofs_meshes + (i * 24); | |
var namePos = _fin.readInt32() + hdr.ofs_text; | |
var materialPos = _fin.readInt32() + hdr.ofs_text; | |
_fin.position += 8; // skip vertices | |
var m:IqmMesh = { | |
name: "", | |
material: "", | |
vstart: _fin.readInt32()*3*4, | |
bcount: _fin.readInt32()*3, | |
aabb: new Aabb() | |
} | |
// read strings | |
m.name = _readString(_fin, namePos); | |
m.material = _readString(_fin, materialPos); | |
// calc the aabb for this mesh | |
m.aabb.beginExtend(); | |
var x:Float = 0; | |
var y:Float = 0; | |
var z:Float = 0; | |
var vert:Int = 0; | |
var vBuf = new snow.utils.Float32Array(snow.utils.ByteArray.fromBytes(vBufs[0].raw)); // index 0 is position | |
var iBuf = new snow.utils.UInt32Array(snow.utils.ByteArray.fromBytes(iBuf.raw)); | |
var start = Std.int(m.vstart/4); // factor out byte-count | |
var end = start + Std.int(m.bcount); | |
for (i in start...end) { | |
vert = (iBuf[i]*3); | |
x = vBuf[vert]; | |
y = vBuf[vert+1]; | |
z = vBuf[vert+2]; | |
m.aabb.extendByFloats(x, y, z); | |
} | |
this.meshes.push(m); | |
} | |
// joints | |
this.joints = []; | |
for (i in 0...hdr.num_joints) { | |
_fin.position = hdr.ofs_joints + (i * 48); | |
var namePos = _fin.readInt32() + hdr.ofs_text; | |
var j:IqmJoint = new IqmJoint(); | |
j.parent = _fin.readInt32(); | |
j.pos.set(_fin.readFloat(), _fin.readFloat(), _fin.readFloat()); | |
j.rot.set(_fin.readFloat(), _fin.readFloat(), _fin.readFloat(), _fin.readFloat()); | |
j.scale.set(_fin.readFloat(), _fin.readFloat(), _fin.readFloat()); | |
j.rot.invert(); // TODO: move to exporter! should be done when converting coord-system! | |
j.rot.normalize(); | |
j.mBase.recompose(j.rot, j.scale, j.pos); | |
j.mInvBase.recompose(j.rot, j.scale, j.pos); | |
j.mInvBase.invert(); | |
j.dqBase.pack(j.rot, j.pos); | |
j.dqInvBase = j.dqBase.inverted(); | |
if (j.parent >= 0) { | |
j.mBase = this.joints[j.parent].mBase * j.mBase; | |
j.mInvBase *= this.joints[j.parent].mInvBase; | |
j.dqBase = this.joints[j.parent].dqBase * j.dqBase; | |
j.dqInvBase *= this.joints[j.parent].dqInvBase; | |
} | |
j.name = _readString(_fin, namePos); | |
this.joints.push(j); | |
} | |
// animations | |
this.anims = []; | |
for (i in 0...hdr.num_anims) { | |
_fin.position = hdr.ofs_anims + (i * 20); | |
var namePos = _fin.readInt32() + hdr.ofs_text; | |
var a:IqmAnim = { | |
name: "", | |
first_frame: _fin.readInt32(), | |
num_frames: _fin.readInt32(), | |
framerate: _fin.readFloat(), | |
flags: _fin.readInt32() | |
}; | |
a.name = _readString(_fin, namePos); | |
this.anims.push(a); | |
} | |
// read poses, these are the animation-frames | |
this.poses = []; | |
_fin.position = hdr.ofs_poses; | |
for (i in 0...hdr.num_poses) { | |
var p:IqmPose = { | |
parent: _fin.readInt32(), | |
mask: _fin.readInt32(), | |
channeloffset: [], | |
channelscale: [], | |
} | |
for (j in 0...10) | |
p.channeloffset.push(_fin.readFloat()); | |
for (j in 0...10) | |
p.channelscale.push(_fin.readFloat()); | |
this.poses.push(p); | |
} | |
this.frames = []; | |
this.dqFrames = []; | |
_fin.position = hdr.ofs_frames; | |
for (i in 0...hdr.num_frames) { | |
for (j in 0...hdr.num_poses) { | |
var p = poses[j]; | |
var pos = new Vec3(p.channeloffset[0], p.channeloffset[1], p.channeloffset[2]); | |
var rot = new Quat(p.channeloffset[3], p.channeloffset[4], p.channeloffset[5], p.channeloffset[6]); | |
var scale = new Vec3(p.channeloffset[7], p.channeloffset[8], p.channeloffset[9]); | |
if (p.mask&0x01!=0) pos.x += _fin.readUInt16() * p.channelscale[0]; | |
if (p.mask&0x02!=0) pos.y += _fin.readUInt16() * p.channelscale[1]; | |
if (p.mask&0x04!=0) pos.z += _fin.readUInt16() * p.channelscale[2]; | |
if (p.mask&0x08!=0) rot.x += _fin.readUInt16() * p.channelscale[3]; | |
if (p.mask&0x10!=0) rot.y += _fin.readUInt16() * p.channelscale[4]; | |
if (p.mask&0x20!=0) rot.z += _fin.readUInt16() * p.channelscale[5]; | |
if (p.mask&0x40!=0) rot.w += _fin.readUInt16() * p.channelscale[6]; | |
if (p.mask&0x80!=0) scale.x += _fin.readUInt16() * p.channelscale[7]; | |
if (p.mask&0x100!=0) scale.y += _fin.readUInt16() * p.channelscale[8]; | |
if (p.mask&0x200!=0) scale.z += _fin.readUInt16() * p.channelscale[9]; | |
rot.invert(); // TODO: move to exporter! should be done when converting coord-system! | |
rot.normalize(); | |
// TODO: building the matrix is redundant. we are using dqs directly now. | |
var m = new Mat44(); | |
m.recompose(rot, scale, pos); | |
var dq = DualQuat.create(rot, pos); | |
if (p.parent >= 0) { | |
m = this.frames[(i*hdr.num_poses) + p.parent] * m; | |
dq = this.dqFrames[(i*hdr.num_poses) + p.parent] * dq; | |
} | |
this.frames.push(m); | |
this.dqFrames.push(dq); | |
} | |
} | |
return { | |
layout: vBufs.length > 4 ? skinnedVertexLayout : defaultVertexLayout, | |
vBufs: vBufs, | |
iBuf: iBuf | |
} | |
} | |
inline function _readString(_fin:BytesInput, _pos:Int):String { | |
_fin.position = _pos; | |
var buf = new StringBuf(); | |
while(true) { | |
var c = _fin.readByte(); | |
if (c == 0x00) | |
break; | |
buf.addChar(c); | |
} | |
return buf.toString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment