UNPKG

war3-model-docs

Version:

Warcraft 3 model parser, generator, convertor and previewer

1,395 lines (1,389 loc) 329 kB
/*! war3-model v2.2.1-1 https://github.com/4eb0da/war3-model Released under the MIT License. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var glMatrix = require('gl-matrix'); var TextureFlags; (function (TextureFlags) { TextureFlags[TextureFlags["WrapWidth"] = 1] = "WrapWidth"; TextureFlags[TextureFlags["WrapHeight"] = 2] = "WrapHeight"; })(TextureFlags || (TextureFlags = {})); var FilterMode; (function (FilterMode) { FilterMode[FilterMode["None"] = 0] = "None"; FilterMode[FilterMode["Transparent"] = 1] = "Transparent"; FilterMode[FilterMode["Blend"] = 2] = "Blend"; FilterMode[FilterMode["Additive"] = 3] = "Additive"; FilterMode[FilterMode["AddAlpha"] = 4] = "AddAlpha"; FilterMode[FilterMode["Modulate"] = 5] = "Modulate"; FilterMode[FilterMode["Modulate2x"] = 6] = "Modulate2x"; })(FilterMode || (FilterMode = {})); var LineType; (function (LineType) { LineType[LineType["DontInterp"] = 0] = "DontInterp"; LineType[LineType["Linear"] = 1] = "Linear"; LineType[LineType["Hermite"] = 2] = "Hermite"; LineType[LineType["Bezier"] = 3] = "Bezier"; })(LineType || (LineType = {})); var LayerShading; (function (LayerShading) { LayerShading[LayerShading["Unshaded"] = 1] = "Unshaded"; LayerShading[LayerShading["SphereEnvMap"] = 2] = "SphereEnvMap"; LayerShading[LayerShading["TwoSided"] = 16] = "TwoSided"; LayerShading[LayerShading["Unfogged"] = 32] = "Unfogged"; LayerShading[LayerShading["NoDepthTest"] = 64] = "NoDepthTest"; LayerShading[LayerShading["NoDepthSet"] = 128] = "NoDepthSet"; })(LayerShading || (LayerShading = {})); var MaterialRenderMode; (function (MaterialRenderMode) { MaterialRenderMode[MaterialRenderMode["ConstantColor"] = 1] = "ConstantColor"; MaterialRenderMode[MaterialRenderMode["SortPrimsFarZ"] = 16] = "SortPrimsFarZ"; MaterialRenderMode[MaterialRenderMode["FullResolution"] = 32] = "FullResolution"; })(MaterialRenderMode || (MaterialRenderMode = {})); var GeosetAnimFlags; (function (GeosetAnimFlags) { GeosetAnimFlags[GeosetAnimFlags["DropShadow"] = 1] = "DropShadow"; GeosetAnimFlags[GeosetAnimFlags["Color"] = 2] = "Color"; })(GeosetAnimFlags || (GeosetAnimFlags = {})); var NodeFlags; (function (NodeFlags) { NodeFlags[NodeFlags["DontInheritTranslation"] = 1] = "DontInheritTranslation"; NodeFlags[NodeFlags["DontInheritRotation"] = 2] = "DontInheritRotation"; NodeFlags[NodeFlags["DontInheritScaling"] = 4] = "DontInheritScaling"; NodeFlags[NodeFlags["Billboarded"] = 8] = "Billboarded"; NodeFlags[NodeFlags["BillboardedLockX"] = 16] = "BillboardedLockX"; NodeFlags[NodeFlags["BillboardedLockY"] = 32] = "BillboardedLockY"; NodeFlags[NodeFlags["BillboardedLockZ"] = 64] = "BillboardedLockZ"; NodeFlags[NodeFlags["CameraAnchored"] = 128] = "CameraAnchored"; })(NodeFlags || (NodeFlags = {})); var NodeType; (function (NodeType) { NodeType[NodeType["Helper"] = 0] = "Helper"; NodeType[NodeType["Bone"] = 256] = "Bone"; NodeType[NodeType["Light"] = 512] = "Light"; NodeType[NodeType["EventObject"] = 1024] = "EventObject"; NodeType[NodeType["Attachment"] = 2048] = "Attachment"; NodeType[NodeType["ParticleEmitter"] = 4096] = "ParticleEmitter"; NodeType[NodeType["CollisionShape"] = 8192] = "CollisionShape"; NodeType[NodeType["RibbonEmitter"] = 16384] = "RibbonEmitter"; })(NodeType || (NodeType = {})); var CollisionShapeType; (function (CollisionShapeType) { CollisionShapeType[CollisionShapeType["Box"] = 0] = "Box"; CollisionShapeType[CollisionShapeType["Sphere"] = 2] = "Sphere"; })(CollisionShapeType || (CollisionShapeType = {})); var ParticleEmitterFlags; (function (ParticleEmitterFlags) { ParticleEmitterFlags[ParticleEmitterFlags["EmitterUsesMDL"] = 32768] = "EmitterUsesMDL"; ParticleEmitterFlags[ParticleEmitterFlags["EmitterUsesTGA"] = 65536] = "EmitterUsesTGA"; })(ParticleEmitterFlags || (ParticleEmitterFlags = {})); var ParticleEmitter2Flags; (function (ParticleEmitter2Flags) { ParticleEmitter2Flags[ParticleEmitter2Flags["Unshaded"] = 32768] = "Unshaded"; ParticleEmitter2Flags[ParticleEmitter2Flags["SortPrimsFarZ"] = 65536] = "SortPrimsFarZ"; ParticleEmitter2Flags[ParticleEmitter2Flags["LineEmitter"] = 131072] = "LineEmitter"; ParticleEmitter2Flags[ParticleEmitter2Flags["Unfogged"] = 262144] = "Unfogged"; ParticleEmitter2Flags[ParticleEmitter2Flags["ModelSpace"] = 524288] = "ModelSpace"; ParticleEmitter2Flags[ParticleEmitter2Flags["XYQuad"] = 1048576] = "XYQuad"; })(ParticleEmitter2Flags || (ParticleEmitter2Flags = {})); var ParticleEmitter2FilterMode; (function (ParticleEmitter2FilterMode) { ParticleEmitter2FilterMode[ParticleEmitter2FilterMode["Blend"] = 0] = "Blend"; ParticleEmitter2FilterMode[ParticleEmitter2FilterMode["Additive"] = 1] = "Additive"; ParticleEmitter2FilterMode[ParticleEmitter2FilterMode["Modulate"] = 2] = "Modulate"; ParticleEmitter2FilterMode[ParticleEmitter2FilterMode["Modulate2x"] = 3] = "Modulate2x"; ParticleEmitter2FilterMode[ParticleEmitter2FilterMode["AlphaKey"] = 4] = "AlphaKey"; })(ParticleEmitter2FilterMode || (ParticleEmitter2FilterMode = {})); // Not actually mapped to mdx flags (0: Head, 1: Tail, 2: Both) var ParticleEmitter2FramesFlags; (function (ParticleEmitter2FramesFlags) { ParticleEmitter2FramesFlags[ParticleEmitter2FramesFlags["Head"] = 1] = "Head"; ParticleEmitter2FramesFlags[ParticleEmitter2FramesFlags["Tail"] = 2] = "Tail"; })(ParticleEmitter2FramesFlags || (ParticleEmitter2FramesFlags = {})); var LightType; (function (LightType) { LightType[LightType["Omnidirectional"] = 0] = "Omnidirectional"; LightType[LightType["Directional"] = 1] = "Directional"; LightType[LightType["Ambient"] = 2] = "Ambient"; })(LightType || (LightType = {})); var ParticleEmitterPopcornFlags; (function (ParticleEmitterPopcornFlags) { ParticleEmitterPopcornFlags[ParticleEmitterPopcornFlags["Unshaded"] = 32768] = "Unshaded"; ParticleEmitterPopcornFlags[ParticleEmitterPopcornFlags["SortPrimsFarZ"] = 65536] = "SortPrimsFarZ"; ParticleEmitterPopcornFlags[ParticleEmitterPopcornFlags["Unfogged"] = 262144] = "Unfogged"; })(ParticleEmitterPopcornFlags || (ParticleEmitterPopcornFlags = {})); var model = /*#__PURE__*/Object.freeze({ __proto__: null, get TextureFlags () { return TextureFlags; }, get FilterMode () { return FilterMode; }, get LineType () { return LineType; }, get LayerShading () { return LayerShading; }, get MaterialRenderMode () { return MaterialRenderMode; }, get GeosetAnimFlags () { return GeosetAnimFlags; }, get NodeFlags () { return NodeFlags; }, get NodeType () { return NodeType; }, get CollisionShapeType () { return CollisionShapeType; }, get ParticleEmitterFlags () { return ParticleEmitterFlags; }, get ParticleEmitter2Flags () { return ParticleEmitter2Flags; }, get ParticleEmitter2FilterMode () { return ParticleEmitter2FilterMode; }, get ParticleEmitter2FramesFlags () { return ParticleEmitter2FramesFlags; }, get LightType () { return LightType; }, get ParticleEmitterPopcornFlags () { return ParticleEmitterPopcornFlags; } }); var _a$2; var State$1 = /** @class */ (function () { function State(str) { this.str = str; this.pos = 0; } State.prototype.char = function () { if (this.pos >= this.str.length) { throwError(this, 'incorrect model data'); } return this.str[this.pos]; }; return State; }()); function throwError(state, str) { if (str === void 0) { str = ''; } throw new Error("SyntaxError, near " + state.pos + (str ? ', ' + str : '')); } function parseComment(state) { if (state.char() === '/' && state.str[state.pos + 1] === '/') { state.pos += 2; while (state.pos < state.str.length && state.str[++state.pos] !== '\n') ; ++state.pos; return true; } return false; } var spaceRE = /\s/i; function parseSpace(state) { while (state.pos < state.str.length && spaceRE.test(state.char())) { ++state.pos; } } var keywordFirstCharRE = /[a-z]/i; var keywordOtherCharRE = /[a-z0-9]/i; function parseKeyword(state) { if (!keywordFirstCharRE.test(state.char())) { return null; } var keyword = state.char(); ++state.pos; while (keywordOtherCharRE.test(state.char())) { keyword += state.str[state.pos++]; } parseSpace(state); return keyword; } function parseSymbol(state, symbol) { if (state.char() === symbol) { ++state.pos; parseSpace(state); } } function strictParseSymbol(state, symbol) { if (state.char() !== symbol) { throwError(state, "extected " + symbol); } ++state.pos; parseSpace(state); } function parseString(state) { if (state.char() === '"') { var start = ++state.pos; // " while (state.char() !== '"') { ++state.pos; } ++state.pos; // " var res = state.str.substring(start, state.pos - 1); parseSpace(state); return res; } return null; } var numberFirstCharRE = /[-0-9]/; var numberOtherCharRE = /[-+.0-9e]/i; function parseNumber(state) { if (numberFirstCharRE.test(state.char())) { var start = state.pos; ++state.pos; while (numberOtherCharRE.test(state.char())) { ++state.pos; } var res = parseFloat(state.str.substring(start, state.pos)); parseSpace(state); return res; } return null; } function parseArray(state, arr, pos) { if (state.char() !== '{') { return null; } if (!arr) { arr = []; pos = 0; } strictParseSymbol(state, '{'); while (state.char() !== '}') { var num = parseNumber(state); if (num === null) { throwError(state, 'expected number'); } arr[pos++] = num; parseSymbol(state, ','); } strictParseSymbol(state, '}'); return arr; } function parseArrayOrSingleItem(state, arr) { if (state.char() !== '{') { arr[0] = parseNumber(state); return arr; } var pos = 0; strictParseSymbol(state, '{'); while (state.char() !== '}') { var num = parseNumber(state); if (num === null) { throwError(state, 'expected number'); } arr[pos++] = num; parseSymbol(state, ','); } strictParseSymbol(state, '}'); return arr; } function parseObject(state) { var prefix = null; var obj = {}; if (state.char() !== '{') { prefix = parseString(state); if (prefix === null) { prefix = parseNumber(state); } if (prefix === null) { throwError(state, 'expected string or number'); } } strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Interval') { var array = new Uint32Array(2); obj[keyword] = parseArray(state, array, 0); } else if (keyword === 'MinimumExtent' || keyword === 'MaximumExtent') { var array = new Float32Array(3); obj[keyword] = parseArray(state, array, 0); } else { obj[keyword] = parseArray(state) || parseString(state); if (obj[keyword] === null) { obj[keyword] = parseNumber(state); } } parseSymbol(state, ','); } strictParseSymbol(state, '}'); return [prefix, obj]; } function parseVersion$1(state, model) { var _a = parseObject(state); _a[0]; var obj = _a[1]; if (obj.FormatVersion) { model.Version = obj.FormatVersion; } } function parseModelInfo$1(state, model) { var _a = parseObject(state), name = _a[0], obj = _a[1]; model.Info = obj; model.Info.Name = name; } function parseSequences$1(state, model) { parseNumber(state); // count, not used strictParseSymbol(state, '{'); var res = []; while (state.char() !== '}') { parseKeyword(state); // Anim var _a = parseObject(state), name_1 = _a[0], obj = _a[1]; obj.Name = name_1; obj.NonLooping = 'NonLooping' in obj; obj.MoveSpeed = obj.MoveSpeed || 0; obj.Rarity = obj.Rarity || 0; res.push(obj); } strictParseSymbol(state, '}'); model.Sequences = res; } function parseTextures$1(state, model) { var res = []; parseNumber(state); // count, not used strictParseSymbol(state, '{'); while (state.char() !== '}') { parseKeyword(state); // Bitmap var _a = parseObject(state); _a[0]; var obj = _a[1]; obj.Flags = 0; if ('WrapWidth' in obj) { obj.Flags += TextureFlags.WrapWidth; delete obj.WrapWidth; } if ('WrapHeight' in obj) { obj.Flags += TextureFlags.WrapHeight; delete obj.WrapHeight; } res.push(obj); } strictParseSymbol(state, '}'); model.Textures = res; } var AnimVectorType$2; (function (AnimVectorType) { AnimVectorType[AnimVectorType["INT1"] = 0] = "INT1"; AnimVectorType[AnimVectorType["FLOAT1"] = 1] = "FLOAT1"; AnimVectorType[AnimVectorType["FLOAT3"] = 2] = "FLOAT3"; AnimVectorType[AnimVectorType["FLOAT4"] = 3] = "FLOAT4"; })(AnimVectorType$2 || (AnimVectorType$2 = {})); var animVectorSize$2 = (_a$2 = {}, _a$2[AnimVectorType$2.INT1] = 1, _a$2[AnimVectorType$2.FLOAT1] = 1, _a$2[AnimVectorType$2.FLOAT3] = 3, _a$2[AnimVectorType$2.FLOAT4] = 4, _a$2); function parseAnimKeyframe(state, frame, type, lineType) { var res = { Frame: frame, Vector: null }; var Vector = type === AnimVectorType$2.INT1 ? Int32Array : Float32Array; var itemCount = animVectorSize$2[type]; res.Vector = parseArrayOrSingleItem(state, new Vector(itemCount)); strictParseSymbol(state, ','); if (lineType === LineType.Hermite || lineType === LineType.Bezier) { parseKeyword(state); // InTan res.InTan = parseArrayOrSingleItem(state, new Vector(itemCount)); strictParseSymbol(state, ','); parseKeyword(state); // OutTan res.OutTan = parseArrayOrSingleItem(state, new Vector(itemCount)); strictParseSymbol(state, ','); } return res; } function parseAnimVector(state, type) { var animVector = { LineType: LineType.DontInterp, GlobalSeqId: null, Keys: [] }; parseNumber(state); // count, not used strictParseSymbol(state, '{'); var lineType = parseKeyword(state); if (lineType === 'DontInterp' || lineType === 'Linear' || lineType === 'Hermite' || lineType === 'Bezier') { animVector.LineType = LineType[lineType]; } strictParseSymbol(state, ','); while (state.char() !== '}') { var keyword = parseKeyword(state); if (keyword === 'GlobalSeqId') { animVector[keyword] = parseNumber(state); strictParseSymbol(state, ','); } else { var frame = parseNumber(state); if (frame === null) { throwError(state, 'expected frame number or GlobalSeqId'); } strictParseSymbol(state, ':'); animVector.Keys.push(parseAnimKeyframe(state, frame, type, animVector.LineType)); } } strictParseSymbol(state, '}'); return animVector; } function parseLayer(state, model) { var res = { Alpha: null, TVertexAnimId: null, Shading: 0, CoordId: 0 }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); var isStatic = false; if (!keyword) { throwError(state); } if (keyword === 'static') { isStatic = true; keyword = parseKeyword(state); } if (!isStatic && keyword === 'TextureID') { res[keyword] = parseAnimVector(state, AnimVectorType$2.INT1); } else if (!isStatic && (keyword === 'Alpha')) { res[keyword] = parseAnimVector(state, AnimVectorType$2.FLOAT1); } else if (keyword === 'Unshaded' || keyword === 'SphereEnvMap' || keyword === 'TwoSided' || keyword === 'Unfogged' || keyword === 'NoDepthTest' || keyword === 'NoDepthSet') { res.Shading |= LayerShading[keyword]; } else if (keyword === 'FilterMode') { var val = parseKeyword(state); if (val === 'None' || val === 'Transparent' || val === 'Blend' || val === 'Additive' || val === 'AddAlpha' || val === 'Modulate' || val === 'Modulate2x') { res.FilterMode = FilterMode[val]; } } else if (keyword === 'TVertexAnimId') { res.TVertexAnimId = parseNumber(state); } else if (model.Version >= 900 && keyword === 'EmissiveGain') { if (isStatic) { res[keyword] = parseNumber(state); } else { res[keyword] = parseAnimVector(state, AnimVectorType$2.FLOAT1); } } else if (model.Version >= 1000 && keyword === 'FresnelColor') { if (isStatic) { var array = new Float32Array(3); res[keyword] = parseArray(state, array, 0); } else { res[keyword] = parseAnimVector(state, AnimVectorType$2.FLOAT3); } } else if (model.Version >= 1000 && (keyword === 'FresnelOpacity' || keyword === 'FresnelTeamColor')) { if (isStatic) { res[keyword] = parseNumber(state); } else { res[keyword] = parseAnimVector(state, AnimVectorType$2.FLOAT1); } } else { var val = parseNumber(state); if (val === null) { val = parseKeyword(state); } res[keyword] = val; } parseSymbol(state, ','); } strictParseSymbol(state, '}'); return res; } function parseMaterials$1(state, model) { var res = []; parseNumber(state); // count, not used strictParseSymbol(state, '{'); while (state.char() !== '}') { var obj = { RenderMode: 0, Layers: [] }; parseKeyword(state); // Material strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Layer') { obj.Layers.push(parseLayer(state, model)); } else if (keyword === 'PriorityPlane' || keyword === 'RenderMode') { obj[keyword] = parseNumber(state); } else if (keyword === 'ConstantColor' || keyword === 'SortPrimsFarZ' || keyword === 'FullResolution') { obj.RenderMode |= MaterialRenderMode[keyword]; } else if (model.Version >= 900 && keyword === 'Shader') { obj[keyword] = parseString(state); } else { throw new Error('Unknown material property ' + keyword); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); res.push(obj); } strictParseSymbol(state, '}'); model.Materials = res; } var GeosetPartType; (function (GeosetPartType) { GeosetPartType[GeosetPartType["INT"] = 0] = "INT"; GeosetPartType[GeosetPartType["FLOAT"] = 1] = "FLOAT"; })(GeosetPartType || (GeosetPartType = {})); function parseGeosetPart(state, countPerObj, type) { var count = parseNumber(state); var arr = new (type === GeosetPartType.FLOAT ? Float32Array : Uint8Array)(count * countPerObj); strictParseSymbol(state, '{'); for (var index = 0; index < count; ++index) { parseArray(state, arr, index * countPerObj); strictParseSymbol(state, ','); } strictParseSymbol(state, '}'); return arr; } function parseGeoset(state, model) { var res = { Vertices: null, Normals: null, TVertices: [], VertexGroup: new Uint8Array(0), Faces: null, Groups: null, TotalGroupsCount: null, MinimumExtent: null, MaximumExtent: null, BoundsRadius: 0, Anims: [], MaterialID: null, SelectionGroup: null, Unselectable: false }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Vertices' || keyword === 'Normals' || keyword === 'TVertices') { var countPerObj = 3; if (keyword === 'TVertices') { countPerObj = 2; } var arr = parseGeosetPart(state, countPerObj, GeosetPartType.FLOAT); if (keyword === 'TVertices') { res.TVertices.push(arr); } else { res[keyword] = arr; } } else if (keyword === 'VertexGroup') { res[keyword] = new Uint8Array(res.Vertices.length / 3); parseArray(state, res[keyword], 0); } else if (keyword === 'Faces') { parseNumber(state); // group count, always 1? var indexCount = parseNumber(state); res.Faces = new Uint16Array(indexCount); strictParseSymbol(state, '{'); parseKeyword(state); // Triangles strictParseSymbol(state, '{'); parseArray(state, res.Faces, 0); parseSymbol(state, ','); strictParseSymbol(state, '}'); strictParseSymbol(state, '}'); } else if (keyword === 'Groups') { var groups = []; parseNumber(state); // groups count, unused res.TotalGroupsCount = parseNumber(state); // summed in subarrays strictParseSymbol(state, '{'); while (state.char() !== '}') { parseKeyword(state); // Matrices groups.push(parseArray(state)); parseSymbol(state, ','); } strictParseSymbol(state, '}'); res.Groups = groups; } else if (keyword === 'MinimumExtent' || keyword === 'MaximumExtent') { var arr = new Float32Array(3); res[keyword] = parseArray(state, arr, 0); strictParseSymbol(state, ','); } else if (keyword === 'BoundsRadius' || keyword === 'MaterialID' || keyword === 'SelectionGroup') { res[keyword] = parseNumber(state); strictParseSymbol(state, ','); } else if (keyword === 'Anim') { var _a = parseObject(state); _a[0]; var obj = _a[1]; if (obj.Alpha === undefined) { obj.Alpha = 1; } res.Anims.push(obj); } else if (keyword === 'Unselectable') { res.Unselectable = true; strictParseSymbol(state, ','); } else if (model.Version >= 900) { if (keyword === 'LevelOfDetail') { res.LevelOfDetail = parseNumber(state); strictParseSymbol(state, ','); } else if (keyword === 'Name') { res.Name = parseString(state); strictParseSymbol(state, ','); } else if (keyword === 'Tangents') { res.Tangents = parseGeosetPart(state, 4, GeosetPartType.FLOAT); } else if (keyword === 'SkinWeights') { var count = parseNumber(state); var arr = new Uint8Array(count * 4); res.SkinWeights = parseArray(state, arr, 0); } } } strictParseSymbol(state, '}'); model.Geosets.push(res); } function parseGeosetAnim(state, model) { var res = { GeosetId: -1, Alpha: 1, Color: null, Flags: 0 }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); var isStatic = false; if (!keyword) { throwError(state); } if (keyword === 'static') { isStatic = true; keyword = parseKeyword(state); } if (keyword === 'Alpha') { if (isStatic) { res.Alpha = parseNumber(state); } else { res.Alpha = parseAnimVector(state, AnimVectorType$2.FLOAT1); } } else if (keyword === 'Color') { if (isStatic) { var array = new Float32Array(3); res.Color = parseArray(state, array, 0); res.Color.reverse(); } else { res.Color = parseAnimVector(state, AnimVectorType$2.FLOAT3); for (var _i = 0, _a = res.Color.Keys; _i < _a.length; _i++) { var key = _a[_i]; key.Vector.reverse(); if (key.InTan) { key.InTan.reverse(); key.OutTan.reverse(); } } } } else if (keyword === 'DropShadow') { res.Flags |= GeosetAnimFlags[keyword]; } else { res[keyword] = parseNumber(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.GeosetAnims.push(res); } function parseNode$1(state, type, model) { var name = parseString(state); var node = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType[type] }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling' || keyword === 'Visibility') { var vectorType = AnimVectorType$2.FLOAT3; if (keyword === 'Rotation') { vectorType = AnimVectorType$2.FLOAT4; } else if (keyword === 'Visibility') { vectorType = AnimVectorType$2.FLOAT1; } node[keyword] = parseAnimVector(state, vectorType); } else if (keyword === 'BillboardedLockZ' || keyword === 'BillboardedLockY' || keyword === 'BillboardedLockX' || keyword === 'Billboarded' || keyword === 'CameraAnchored') { node.Flags |= NodeFlags[keyword]; } else if (keyword === 'DontInherit') { strictParseSymbol(state, '{'); var val = parseKeyword(state); if (val === 'Translation') { node.Flags |= NodeFlags.DontInheritTranslation; } else if (val === 'Rotation') { node.Flags |= NodeFlags.DontInheritRotation; } else if (val === 'Scaling') { node.Flags |= NodeFlags.DontInheritScaling; } strictParseSymbol(state, '}'); } else if (keyword === 'Path') { node[keyword] = parseString(state); } else { var val = parseKeyword(state) || parseNumber(state); if (keyword === 'GeosetId' && val === 'Multiple' || keyword === 'GeosetAnimId' && val === 'None') { val = null; } node[keyword] = val; } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.Nodes[node.ObjectId] = node; return node; } function parseBone(state, model) { var node = parseNode$1(state, 'Bone', model); model.Bones.push(node); } function parseHelper(state, model) { var node = parseNode$1(state, 'Helper', model); model.Helpers.push(node); } function parseAttachment(state, model) { var node = parseNode$1(state, 'Attachment', model); model.Attachments.push(node); } function parsePivotPoints$1(state, model) { var count = parseNumber(state); var res = []; strictParseSymbol(state, '{'); for (var i = 0; i < count; ++i) { res.push(parseArray(state, new Float32Array(3), 0)); strictParseSymbol(state, ','); } strictParseSymbol(state, '}'); model.PivotPoints = res; } function parseEventObject(state, model) { var name = parseString(state); var res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, EventTrack: null, Flags: NodeType.EventObject }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'EventTrack') { var count = parseNumber(state); // EventTrack count res.EventTrack = parseArray(state, new Uint32Array(count), 0); } else if (keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling') { var type = keyword === 'Rotation' ? AnimVectorType$2.FLOAT4 : AnimVectorType$2.FLOAT3; res[keyword] = parseAnimVector(state, type); } else { res[keyword] = parseNumber(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.EventObjects.push(res); model.Nodes.push(res); } function parseCollisionShape(state, model) { var name = parseString(state); var res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Shape: CollisionShapeType.Box, Vertices: null, Flags: NodeType.CollisionShape }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Sphere') { res.Shape = CollisionShapeType.Sphere; } else if (keyword === 'Box') { res.Shape = CollisionShapeType.Box; } else if (keyword === 'Vertices') { var count = parseNumber(state); var vertices = new Float32Array(count * 3); strictParseSymbol(state, '{'); for (var i = 0; i < count; ++i) { parseArray(state, vertices, i * 3); strictParseSymbol(state, ','); } strictParseSymbol(state, '}'); res.Vertices = vertices; } else if (keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling') { var type = keyword === 'Rotation' ? AnimVectorType$2.FLOAT4 : AnimVectorType$2.FLOAT3; res[keyword] = parseAnimVector(state, type); } else { res[keyword] = parseNumber(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.CollisionShapes.push(res); model.Nodes.push(res); } function parseGlobalSequences$1(state, model) { var res = []; var count = parseNumber(state); strictParseSymbol(state, '{'); for (var i = 0; i < count; ++i) { var keyword = parseKeyword(state); if (keyword === 'Duration') { res.push(parseNumber(state)); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.GlobalSequences = res; } function parseUnknownBlock(state) { var opened; while (state.char() !== undefined && state.char() !== '{') { ++state.pos; } opened = 1; ++state.pos; while (state.char() !== undefined && opened > 0) { if (state.char() === '{') { ++opened; } else if (state.char() === '}') { --opened; } ++state.pos; } parseSpace(state); } function parseParticleEmitter(state, model) { var res = { ObjectId: null, Parent: null, Name: null, Flags: 0 }; res.Name = parseString(state); strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); var isStatic = false; if (!keyword) { throwError(state); } if (keyword === 'static') { isStatic = true; keyword = parseKeyword(state); } if (keyword === 'ObjectId' || keyword === 'Parent') { res[keyword] = parseNumber(state); } else if (keyword === 'EmitterUsesMDL' || keyword === 'EmitterUsesTGA') { res.Flags |= ParticleEmitterFlags[keyword]; } else if (!isStatic && (keyword === 'Visibility' || keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling' || keyword === 'EmissionRate' || keyword === 'Gravity' || keyword === 'Longitude' || keyword === 'Latitude')) { var type = AnimVectorType$2.FLOAT3; if (keyword === 'Visibility' || keyword === 'EmissionRate' || keyword === 'Gravity' || keyword === 'Longitude' || keyword === 'Latitude') { type = AnimVectorType$2.FLOAT1; } else if (keyword === 'Rotation') { type = AnimVectorType$2.FLOAT4; } res[keyword] = parseAnimVector(state, type); } else if (keyword === 'Particle') { strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword2 = parseKeyword(state); var isStatic2 = false; if (keyword2 === 'static') { isStatic2 = true; keyword2 = parseKeyword(state); } if (!isStatic2 && (keyword2 === 'LifeSpan' || keyword2 === 'InitVelocity')) { res[keyword2] = parseAnimVector(state, AnimVectorType$2.FLOAT1); } else if (keyword2 === 'LifeSpan' || keyword2 === 'InitVelocity') { res[keyword2] = parseNumber(state); } else if (keyword2 === 'Path') { res.Path = parseString(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); } else { res[keyword] = parseNumber(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.ParticleEmitters.push(res); } function parseParticleEmitter2(state, model) { var name = parseString(state); var res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType.ParticleEmitter, FrameFlags: 0 }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); var isStatic = false; if (!keyword) { throwError(state); } if (keyword === 'static') { isStatic = true; keyword = parseKeyword(state); } if (!isStatic && (keyword === 'Speed' || keyword === 'Latitude' || keyword === 'Visibility' || keyword === 'EmissionRate' || keyword === 'Width' || keyword === 'Length' || keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling' || keyword === 'Gravity' || keyword === 'Variation')) { var type = AnimVectorType$2.FLOAT3; switch (keyword) { case 'Rotation': type = AnimVectorType$2.FLOAT4; break; case 'Speed': case 'Latitude': case 'Visibility': case 'EmissionRate': case 'Width': case 'Length': case 'Gravity': case 'Variation': type = AnimVectorType$2.FLOAT1; break; } res[keyword] = parseAnimVector(state, type); } else if (keyword === 'Variation' || keyword === 'Gravity') { res[keyword] = parseNumber(state); } else if (keyword === 'SortPrimsFarZ' || keyword === 'Unshaded' || keyword === 'LineEmitter' || keyword === 'Unfogged' || keyword === 'ModelSpace' || keyword === 'XYQuad') { res.Flags |= ParticleEmitter2Flags[keyword]; } else if (keyword === 'Both') { res.FrameFlags |= ParticleEmitter2FramesFlags.Head | ParticleEmitter2FramesFlags.Tail; } else if (keyword === 'Head' || keyword === 'Tail') { res.FrameFlags |= ParticleEmitter2FramesFlags[keyword]; } else if (keyword === 'Squirt') { res[keyword] = true; } else if (keyword === 'DontInherit') { strictParseSymbol(state, '{'); var val = parseKeyword(state); if (val === 'Translation') { res.Flags |= NodeFlags.DontInheritTranslation; } else if (val === 'Rotation') { res.Flags |= NodeFlags.DontInheritRotation; } else if (val === 'Scaling') { res.Flags |= NodeFlags.DontInheritScaling; } strictParseSymbol(state, '}'); } else if (keyword === 'SegmentColor') { var colors = []; strictParseSymbol(state, '{'); while (state.char() !== '}') { parseKeyword(state); // Color var colorArr = new Float32Array(3); parseArray(state, colorArr, 0); // bgr order, inverse from mdx var temp = colorArr[0]; colorArr[0] = colorArr[2]; colorArr[2] = temp; colors.push(colorArr); parseSymbol(state, ','); } strictParseSymbol(state, '}'); res.SegmentColor = colors; } else if (keyword === 'Alpha') { res.Alpha = new Uint8Array(3); parseArray(state, res.Alpha, 0); } else if (keyword === 'ParticleScaling') { res[keyword] = new Float32Array(3); parseArray(state, res[keyword], 0); } else if (keyword === 'LifeSpanUVAnim' || keyword === 'DecayUVAnim' || keyword === 'TailUVAnim' || keyword === 'TailDecayUVAnim') { res[keyword] = new Uint32Array(3); parseArray(state, res[keyword], 0); } else if (keyword === 'Transparent' || keyword === 'Blend' || keyword === 'Additive' || keyword === 'AlphaKey' || keyword === 'Modulate' || keyword === 'Modulate2x') { res.FilterMode = ParticleEmitter2FilterMode[keyword]; } else { res[keyword] = parseNumber(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.ParticleEmitters2.push(res); model.Nodes.push(res); } function parseCamera(state, model) { var res = { Name: null, Position: null, FieldOfView: 0, NearClip: 0, FarClip: 0, TargetPosition: null }; res.Name = parseString(state); strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Position') { res.Position = new Float32Array(3); parseArray(state, res.Position, 0); } else if (keyword === 'FieldOfView' || keyword === 'NearClip' || keyword === 'FarClip') { res[keyword] = parseNumber(state); } else if (keyword === 'Target') { strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword2 = parseKeyword(state); if (keyword2 === 'Position') { res.TargetPosition = new Float32Array(3); parseArray(state, res.TargetPosition, 0); } else if (keyword2 === 'Translation') { res.TargetTranslation = parseAnimVector(state, AnimVectorType$2.FLOAT3); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); } else if (keyword === 'Translation' || keyword === 'Rotation') { res[keyword] = parseAnimVector(state, keyword === 'Rotation' ? AnimVectorType$2.FLOAT1 : AnimVectorType$2.FLOAT3); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.Cameras.push(res); } function parseLight(state, model) { var name = parseString(state); var res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType.Light, LightType: 0 }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); var isStatic = false; if (!keyword) { throwError(state); } if (keyword === 'static') { isStatic = true; keyword = parseKeyword(state); } if (!isStatic && (keyword === 'Visibility' || keyword === 'Color' || keyword === 'Intensity' || keyword === 'AmbIntensity' || keyword === 'AmbColor' || keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling' || keyword === 'AttenuationStart' || keyword === 'AttenuationEnd')) { var type = AnimVectorType$2.FLOAT3; switch (keyword) { case 'Rotation': type = AnimVectorType$2.FLOAT4; break; case 'Visibility': case 'Intensity': case 'AmbIntensity': case 'AttenuationStart': case 'AttenuationEnd': type = AnimVectorType$2.FLOAT1; break; } res[keyword] = parseAnimVector(state, type); if (keyword === 'Color' || keyword === 'AmbColor') { for (var _i = 0, _a = res[keyword].Keys; _i < _a.length; _i++) { var key = _a[_i]; key.Vector.reverse(); if (key.InTan) { key.InTan.reverse(); key.OutTan.reverse(); } } } } else if (keyword === 'Omnidirectional' || keyword === 'Directional' || keyword === 'Ambient') { res.LightType = LightType[keyword]; } else if (keyword === 'Color' || keyword === 'AmbColor') { var color = new Float32Array(3); parseArray(state, color, 0); // bgr order, inverse from mdx var temp = color[0]; color[0] = color[2]; color[2] = temp; res[keyword] = color; } else { res[keyword] = parseNumber(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.Lights.push(res); model.Nodes.push(res); } function parseTextureAnims$1(state, model) { var res = []; parseNumber(state); // count, not used strictParseSymbol(state, '{'); while (state.char() !== '}') { var obj = {}; parseKeyword(state); // TVertexAnim strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling') { var type = keyword === 'Rotation' ? AnimVectorType$2.FLOAT4 : AnimVectorType$2.FLOAT3; obj[keyword] = parseAnimVector(state, type); } else { throw new Error('Unknown texture anim property ' + keyword); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); res.push(obj); } strictParseSymbol(state, '}'); model.TextureAnims = res; } function parseRibbonEmitter(state, model) { var name = parseString(state); var res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType.RibbonEmitter, HeightAbove: null, HeightBelow: null, Alpha: null, Color: null, LifeSpan: null, TextureSlot: null, EmissionRate: null, Rows: null, Columns: null, MaterialID: null, Gravity: null, Visibility: null }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); var isStatic = false; if (!keyword) { throwError(state); } if (keyword === 'static') { isStatic = true; keyword = parseKeyword(state); } if (!isStatic && (keyword === 'Visibility' || keyword === 'HeightAbove' || keyword === 'HeightBelow' || keyword === 'Translation' || keyword === 'Rotation' || keyword === 'Scaling' || keyword === 'Alpha' || keyword === 'TextureSlot')) { var type = AnimVectorType$2.FLOAT3; switch (keyword) { case 'Rotation': type = AnimVectorType$2.FLOAT4; break; case 'Visibility': case 'HeightAbove': case 'HeightBelow': case 'Alpha': type = AnimVectorType$2.FLOAT1; break; case 'TextureSlot': type = AnimVectorType$2.INT1; break; } res[keyword] = parseAnimVector(state, type); } else if (keyword === 'Color') { var color = new Float32Array(3); parseArray(state, color, 0); // bgr order, inverse from mdx var temp = color[0]; color[0] = color[2]; color[2] = temp; res[keyword] = color; } else { res[keyword] = parseNumber(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.RibbonEmitters.push(res); model.Nodes.push(res); } function parseFaceFX$1(state, model) { if (model.Version < 900) { throwError(state, 'Unexpected model chunk FaceFX'); } var name = parseString(state); var res = { Name: name, Path: '' }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); if (!keyword) { throwError(state); } if (keyword === 'Path') { res.Path = parseString(state); } parseSymbol(state, ','); } strictParseSymbol(state, '}'); model.FaceFX = model.FaceFX || []; model.FaceFX.push(res); } function parseBindPose$1(state, model) { if (model.Version < 900) { throwError(state, 'Unexpected model chunk BindPose'); } var res = { Matrices: [] }; strictParseSymbol(state, '{'); parseKeyword(state); // Matrices var count = parseNumber(state); strictParseSymbol(state, '{'); for (var i = 0; i < count; ++i) { var matrix = new Float32Array(12); parseArray(state, matrix, 0); res.Matrices.push(matrix); } strictParseSymbol(state, '}'); strictParseSymbol(state, '}'); model.BindPoses = model.BindPoses || []; model.BindPoses.push(res); } function parseParticleEmitterPopcorn$1(state, model) { if (model.Version < 900) { throwError(state, 'Unexpected model chunk ParticleEmitterPopcorn'); } var name = parseString(state); var res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType.ParticleEmitter }; strictParseSymbol(state, '{'); while (state.char() !== '}') { var keyword = parseKeyword(state); var isStatic = false; if (!keyword) { throwError(state); } if (keyword === 'static') { isStatic = true; keyword = parseKeyword(state); } if (!isStatic && (keyword === 'LifeSpan' || keyword === 'EmissionRate' || keyword === 'Speed' || keyword === 'Color' || keyword === 'Alpha' || keyword === 'Visibility' || keyword === 'Rotation' || keyword === 'Scaling' || keyword === 'Translation')) { var type = AnimVectorType$2.FLOAT3; switch (keyword) { case 'LifeSpan': case 'EmissionRate': case 'Speed': case 'Alpha': case 'Visibility': type = AnimVectorType$2.FLOAT1; break; } res[keyword] = parseAnimVector(state, type); } else if (keyword === 'LifeSpan' || keyword === 'EmissionRate' || keyword === 'Speed' || keyword === 'Alpha') { res[keyword] = parseNumber(state); } else if (keyword === 'Color') { var array = new Float32Array(3); res[keyword] = parseArray(state, array, 0); } else if (keyword === 'ReplaceableId') { res[keyword] = parseNumber(state);