UNPKG

war3-model

Version:

Warcraft 3 model parser, generator, convertor and previewer

1,449 lines 487 kB
(function(global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.war3model = {})); })(this, function(exports2) { "use strict"; var TextureFlags = /* @__PURE__ */ ((TextureFlags2) => { TextureFlags2[TextureFlags2["WrapWidth"] = 1] = "WrapWidth"; TextureFlags2[TextureFlags2["WrapHeight"] = 2] = "WrapHeight"; return TextureFlags2; })(TextureFlags || {}); var FilterMode = /* @__PURE__ */ ((FilterMode2) => { FilterMode2[FilterMode2["None"] = 0] = "None"; FilterMode2[FilterMode2["Transparent"] = 1] = "Transparent"; FilterMode2[FilterMode2["Blend"] = 2] = "Blend"; FilterMode2[FilterMode2["Additive"] = 3] = "Additive"; FilterMode2[FilterMode2["AddAlpha"] = 4] = "AddAlpha"; FilterMode2[FilterMode2["Modulate"] = 5] = "Modulate"; FilterMode2[FilterMode2["Modulate2x"] = 6] = "Modulate2x"; return FilterMode2; })(FilterMode || {}); var LineType = /* @__PURE__ */ ((LineType2) => { LineType2[LineType2["DontInterp"] = 0] = "DontInterp"; LineType2[LineType2["Linear"] = 1] = "Linear"; LineType2[LineType2["Hermite"] = 2] = "Hermite"; LineType2[LineType2["Bezier"] = 3] = "Bezier"; return LineType2; })(LineType || {}); var LayerShading = /* @__PURE__ */ ((LayerShading2) => { LayerShading2[LayerShading2["Unshaded"] = 1] = "Unshaded"; LayerShading2[LayerShading2["SphereEnvMap"] = 2] = "SphereEnvMap"; LayerShading2[LayerShading2["TwoSided"] = 16] = "TwoSided"; LayerShading2[LayerShading2["Unfogged"] = 32] = "Unfogged"; LayerShading2[LayerShading2["NoDepthTest"] = 64] = "NoDepthTest"; LayerShading2[LayerShading2["NoDepthSet"] = 128] = "NoDepthSet"; return LayerShading2; })(LayerShading || {}); var MaterialRenderMode = /* @__PURE__ */ ((MaterialRenderMode2) => { MaterialRenderMode2[MaterialRenderMode2["ConstantColor"] = 1] = "ConstantColor"; MaterialRenderMode2[MaterialRenderMode2["SortPrimsFarZ"] = 16] = "SortPrimsFarZ"; MaterialRenderMode2[MaterialRenderMode2["FullResolution"] = 32] = "FullResolution"; return MaterialRenderMode2; })(MaterialRenderMode || {}); var GeosetAnimFlags = /* @__PURE__ */ ((GeosetAnimFlags2) => { GeosetAnimFlags2[GeosetAnimFlags2["DropShadow"] = 1] = "DropShadow"; GeosetAnimFlags2[GeosetAnimFlags2["Color"] = 2] = "Color"; return GeosetAnimFlags2; })(GeosetAnimFlags || {}); var NodeFlags = /* @__PURE__ */ ((NodeFlags2) => { NodeFlags2[NodeFlags2["DontInheritTranslation"] = 1] = "DontInheritTranslation"; NodeFlags2[NodeFlags2["DontInheritRotation"] = 2] = "DontInheritRotation"; NodeFlags2[NodeFlags2["DontInheritScaling"] = 4] = "DontInheritScaling"; NodeFlags2[NodeFlags2["Billboarded"] = 8] = "Billboarded"; NodeFlags2[NodeFlags2["BillboardedLockX"] = 16] = "BillboardedLockX"; NodeFlags2[NodeFlags2["BillboardedLockY"] = 32] = "BillboardedLockY"; NodeFlags2[NodeFlags2["BillboardedLockZ"] = 64] = "BillboardedLockZ"; NodeFlags2[NodeFlags2["CameraAnchored"] = 128] = "CameraAnchored"; return NodeFlags2; })(NodeFlags || {}); var NodeType = /* @__PURE__ */ ((NodeType2) => { NodeType2[NodeType2["Helper"] = 0] = "Helper"; NodeType2[NodeType2["Bone"] = 256] = "Bone"; NodeType2[NodeType2["Light"] = 512] = "Light"; NodeType2[NodeType2["EventObject"] = 1024] = "EventObject"; NodeType2[NodeType2["Attachment"] = 2048] = "Attachment"; NodeType2[NodeType2["ParticleEmitter"] = 4096] = "ParticleEmitter"; NodeType2[NodeType2["CollisionShape"] = 8192] = "CollisionShape"; NodeType2[NodeType2["RibbonEmitter"] = 16384] = "RibbonEmitter"; return NodeType2; })(NodeType || {}); var CollisionShapeType = /* @__PURE__ */ ((CollisionShapeType2) => { CollisionShapeType2[CollisionShapeType2["Box"] = 0] = "Box"; CollisionShapeType2[CollisionShapeType2["Sphere"] = 2] = "Sphere"; return CollisionShapeType2; })(CollisionShapeType || {}); var ParticleEmitterFlags = /* @__PURE__ */ ((ParticleEmitterFlags2) => { ParticleEmitterFlags2[ParticleEmitterFlags2["EmitterUsesMDL"] = 32768] = "EmitterUsesMDL"; ParticleEmitterFlags2[ParticleEmitterFlags2["EmitterUsesTGA"] = 65536] = "EmitterUsesTGA"; return ParticleEmitterFlags2; })(ParticleEmitterFlags || {}); var ParticleEmitter2Flags = /* @__PURE__ */ ((ParticleEmitter2Flags2) => { ParticleEmitter2Flags2[ParticleEmitter2Flags2["Unshaded"] = 32768] = "Unshaded"; ParticleEmitter2Flags2[ParticleEmitter2Flags2["SortPrimsFarZ"] = 65536] = "SortPrimsFarZ"; ParticleEmitter2Flags2[ParticleEmitter2Flags2["LineEmitter"] = 131072] = "LineEmitter"; ParticleEmitter2Flags2[ParticleEmitter2Flags2["Unfogged"] = 262144] = "Unfogged"; ParticleEmitter2Flags2[ParticleEmitter2Flags2["ModelSpace"] = 524288] = "ModelSpace"; ParticleEmitter2Flags2[ParticleEmitter2Flags2["XYQuad"] = 1048576] = "XYQuad"; return ParticleEmitter2Flags2; })(ParticleEmitter2Flags || {}); var ParticleEmitter2FilterMode = /* @__PURE__ */ ((ParticleEmitter2FilterMode2) => { ParticleEmitter2FilterMode2[ParticleEmitter2FilterMode2["Blend"] = 0] = "Blend"; ParticleEmitter2FilterMode2[ParticleEmitter2FilterMode2["Additive"] = 1] = "Additive"; ParticleEmitter2FilterMode2[ParticleEmitter2FilterMode2["Modulate"] = 2] = "Modulate"; ParticleEmitter2FilterMode2[ParticleEmitter2FilterMode2["Modulate2x"] = 3] = "Modulate2x"; ParticleEmitter2FilterMode2[ParticleEmitter2FilterMode2["AlphaKey"] = 4] = "AlphaKey"; return ParticleEmitter2FilterMode2; })(ParticleEmitter2FilterMode || {}); var ParticleEmitter2FramesFlags = /* @__PURE__ */ ((ParticleEmitter2FramesFlags2) => { ParticleEmitter2FramesFlags2[ParticleEmitter2FramesFlags2["Head"] = 1] = "Head"; ParticleEmitter2FramesFlags2[ParticleEmitter2FramesFlags2["Tail"] = 2] = "Tail"; return ParticleEmitter2FramesFlags2; })(ParticleEmitter2FramesFlags || {}); var LightType = /* @__PURE__ */ ((LightType2) => { LightType2[LightType2["Omnidirectional"] = 0] = "Omnidirectional"; LightType2[LightType2["Directional"] = 1] = "Directional"; LightType2[LightType2["Ambient"] = 2] = "Ambient"; return LightType2; })(LightType || {}); var ParticleEmitterPopcornFlags = /* @__PURE__ */ ((ParticleEmitterPopcornFlags2) => { ParticleEmitterPopcornFlags2[ParticleEmitterPopcornFlags2["Unshaded"] = 32768] = "Unshaded"; ParticleEmitterPopcornFlags2[ParticleEmitterPopcornFlags2["SortPrimsFarZ"] = 65536] = "SortPrimsFarZ"; ParticleEmitterPopcornFlags2[ParticleEmitterPopcornFlags2["Unfogged"] = 262144] = "Unfogged"; return ParticleEmitterPopcornFlags2; })(ParticleEmitterPopcornFlags || {}); const model = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, CollisionShapeType, FilterMode, GeosetAnimFlags, LayerShading, LightType, LineType, MaterialRenderMode, NodeFlags, NodeType, ParticleEmitter2FilterMode, ParticleEmitter2Flags, ParticleEmitter2FramesFlags, ParticleEmitterFlags, ParticleEmitterPopcornFlags, TextureFlags }, Symbol.toStringTag, { value: "Module" })); function mat4fromRotationOrigin(out, rotation2, origin) { const x = rotation2[0], y = rotation2[1], z = rotation2[2], w = rotation2[3], x2 = x + x, y2 = y + y, z2 = z + z, xx = x * x2, xy = x * y2, xz = x * z2, yy = y * y2, yz = y * z2, zz = z * z2, wx = w * x2, wy = w * y2, wz = w * z2, ox = origin[0], oy = origin[1], oz = origin[2]; out[0] = 1 - (yy + zz); out[1] = xy + wz; out[2] = xz - wy; out[3] = 0; out[4] = xy - wz; out[5] = 1 - (xx + zz); out[6] = yz + wx; out[7] = 0; out[8] = xz + wy; out[9] = yz - wx; out[10] = 1 - (xx + yy); out[11] = 0; out[12] = ox - (out[0] * ox + out[4] * oy + out[8] * oz); out[13] = oy - (out[1] * ox + out[5] * oy + out[9] * oz); out[14] = oz - (out[2] * ox + out[6] * oy + out[10] * oz); out[15] = 1; return out; } function rand(from, to) { return from + Math.random() * (to - from); } function degToRad(angle) { return angle * Math.PI / 180; } function getShader(gl, source, type) { const shader2 = gl.createShader(type); gl.shaderSource(shader2, source); gl.compileShader(shader2); if (!gl.getShaderParameter(shader2, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader2)); return null; } return shader2; } function isWebGL2(gl) { return gl instanceof WebGL2RenderingContext; } const LAYER_TEXTURE_NAME_MAP = { "TextureID": 0, "NormalTextureID": 1, "ORMTextureID": 2, "EmissiveTextureID": 3, "TeamColorTextureID": 4, "ReflectionsTextureID": 5 }; const LAYER_TEXTURE_ID_MAP = [ "TextureID", "NormalTextureID", "ORMTextureID", "EmissiveTextureID", "TeamColorTextureID", "ReflectionsTextureID" ]; let State$1 = class State { constructor(str) { this.str = str; this.pos = 0; } char() { if (this.pos >= this.str.length) { throwError(this, "incorrect model data"); } return this.str[this.pos]; } }; function throwError(state, 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; } const spaceRE = /\s/i; function parseSpace(state) { while (state.pos < state.str.length && spaceRE.test(state.char())) { ++state.pos; } } const keywordFirstCharRE = /[a-z]/i; const keywordOtherCharRE = /[a-z0-9]/i; function parseKeyword(state) { if (!keywordFirstCharRE.test(state.char())) { return null; } let keyword2 = state.char(); ++state.pos; while (keywordOtherCharRE.test(state.char())) { keyword2 += state.str[state.pos++]; } parseSpace(state); return keyword2; } 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() === '"') { const start = ++state.pos; while (state.char() !== '"') { ++state.pos; } ++state.pos; const res = state.str.substring(start, state.pos - 1); parseSpace(state); return res; } return null; } const numberFirstCharRE = /[-0-9]/; const numberOtherCharRE = /[-+.0-9e]/i; function parseNumber(state) { if (numberFirstCharRE.test(state.char())) { const start = state.pos; ++state.pos; while (numberOtherCharRE.test(state.char())) { ++state.pos; } const 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() !== "}") { const num = parseNumber(state); if (num === null) { throwError(state, "expected number"); } arr[pos++] = num; parseSymbol(state, ","); } strictParseSymbol(state, "}"); return arr; } function parseArrayCounted(state, arr, pos) { if (state.char() !== "{") { return 0; } const start = pos; strictParseSymbol(state, "{"); while (state.char() !== "}") { const num = parseNumber(state); if (num === null) { throwError(state, "expected number"); } arr[pos++] = num; parseSymbol(state, ","); } strictParseSymbol(state, "}"); return pos - start; } function parseArrayOrSingleItem(state, arr) { if (state.char() !== "{") { arr[0] = parseNumber(state); return arr; } let pos = 0; strictParseSymbol(state, "{"); while (state.char() !== "}") { const num = parseNumber(state); if (num === null) { throwError(state, "expected number"); } arr[pos++] = num; parseSymbol(state, ","); } strictParseSymbol(state, "}"); return arr; } function parseObject(state) { let prefix = null; const 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() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Interval") { const array = new Uint32Array(2); obj[keyword2] = parseArray(state, array, 0); } else if (keyword2 === "MinimumExtent" || keyword2 === "MaximumExtent") { const array = new Float32Array(3); obj[keyword2] = parseArray(state, array, 0); } else { obj[keyword2] = parseArray(state) || parseString(state); if (obj[keyword2] === null) { obj[keyword2] = parseNumber(state); } } parseSymbol(state, ","); } strictParseSymbol(state, "}"); return [prefix, obj]; } function parseVersion$1(state, model2) { const [_unused, obj] = parseObject(state); if (obj.FormatVersion) { model2.Version = obj.FormatVersion; } } function parseModelInfo$1(state, model2) { const [name, obj] = parseObject(state); model2.Info = obj; model2.Info.Name = name; } function parseSequences$1(state, model2) { parseNumber(state); strictParseSymbol(state, "{"); const res = []; while (state.char() !== "}") { parseKeyword(state); const [name, obj] = parseObject(state); obj.Name = name; obj.NonLooping = "NonLooping" in obj; obj.MoveSpeed = obj.MoveSpeed || 0; obj.Rarity = obj.Rarity || 0; res.push(obj); } strictParseSymbol(state, "}"); model2.Sequences = res; } function parseTextures$1(state, model2) { const res = []; parseNumber(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { parseKeyword(state); const [_unused, obj] = parseObject(state); 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, "}"); model2.Textures = res; } const animVectorSize$2 = { [ 0 /* INT1 */ ]: 1, [ 1 /* FLOAT1 */ ]: 1, [ 2 /* FLOAT3 */ ]: 3, [ 3 /* FLOAT4 */ ]: 4 }; function parseAnimKeyframe(state, frame, type, lineType) { const res = { Frame: frame, Vector: null }; const Vector = type === 0 ? Int32Array : Float32Array; const itemCount = animVectorSize$2[type]; res.Vector = parseArrayOrSingleItem(state, new Vector(itemCount)); strictParseSymbol(state, ","); if (lineType === LineType.Hermite || lineType === LineType.Bezier) { parseKeyword(state); res.InTan = parseArrayOrSingleItem(state, new Vector(itemCount)); strictParseSymbol(state, ","); parseKeyword(state); res.OutTan = parseArrayOrSingleItem(state, new Vector(itemCount)); strictParseSymbol(state, ","); } return res; } function parseAnimVector(state, type) { const animVector = { LineType: LineType.DontInterp, GlobalSeqId: null, Keys: [] }; parseNumber(state); strictParseSymbol(state, "{"); const lineType = parseKeyword(state); if (lineType === "DontInterp" || lineType === "Linear" || lineType === "Hermite" || lineType === "Bezier") { animVector.LineType = LineType[lineType]; } strictParseSymbol(state, ","); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (keyword2 === "GlobalSeqId") { animVector[keyword2] = parseNumber(state); strictParseSymbol(state, ","); } else { const 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, model2) { const res = { Alpha: null, TVertexAnimId: null, Shading: 0, CoordId: 0 }; strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword2 = parseKeyword(state); let isStatic = false; if (!keyword2) { throwError(state); } if (keyword2 === "static") { isStatic = true; keyword2 = parseKeyword(state); } if (!isStatic && (keyword2 === "TextureID" || model2.Version >= 1100 && keyword2 in LAYER_TEXTURE_NAME_MAP)) { res[keyword2] = parseAnimVector( state, 0 /* INT1 */ ); } else if (!isStatic && keyword2 === "Alpha") { res[keyword2] = parseAnimVector( state, 1 /* FLOAT1 */ ); } else if (keyword2 === "Unshaded" || keyword2 === "SphereEnvMap" || keyword2 === "TwoSided" || keyword2 === "Unfogged" || keyword2 === "NoDepthTest" || keyword2 === "NoDepthSet") { res.Shading |= LayerShading[keyword2]; } else if (keyword2 === "FilterMode") { const val = parseKeyword(state); if (val === "None" || val === "Transparent" || val === "Blend" || val === "Additive" || val === "AddAlpha" || val === "Modulate" || val === "Modulate2x") { res.FilterMode = FilterMode[val]; } } else if (keyword2 === "TVertexAnimId") { res.TVertexAnimId = parseNumber(state); } else if (model2.Version >= 900 && keyword2 === "EmissiveGain") { if (isStatic) { res[keyword2] = parseNumber(state); } else { res[keyword2] = parseAnimVector( state, 1 /* FLOAT1 */ ); } } else if (model2.Version >= 1e3 && keyword2 === "FresnelColor") { if (isStatic) { const array = new Float32Array(3); res[keyword2] = parseArray(state, array, 0); } else { res[keyword2] = parseAnimVector( state, 2 /* FLOAT3 */ ); } } else if (model2.Version >= 1e3 && (keyword2 === "FresnelOpacity" || keyword2 === "FresnelTeamColor")) { if (isStatic) { res[keyword2] = parseNumber(state); } else { res[keyword2] = parseAnimVector( state, 1 /* FLOAT1 */ ); } } else { let val = parseNumber(state); if (val === null) { val = parseKeyword(state); } res[keyword2] = val; } parseSymbol(state, ","); parseComment(state); parseSpace(state); } strictParseSymbol(state, "}"); return res; } function parseMaterials$1(state, model2) { const res = []; parseNumber(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { const obj = { RenderMode: 0, Layers: [] }; parseKeyword(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Layer") { obj.Layers.push(parseLayer(state, model2)); } else if (keyword2 === "PriorityPlane" || keyword2 === "RenderMode") { obj[keyword2] = parseNumber(state); } else if (keyword2 === "ConstantColor" || keyword2 === "SortPrimsFarZ" || keyword2 === "FullResolution") { obj.RenderMode |= MaterialRenderMode[keyword2]; } else if (model2.Version >= 900 && model2.Version <= 1100 && keyword2 === "Shader") { obj[keyword2] = parseString(state); } else { throw new Error("Unknown material property " + keyword2); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); res.push(obj); } strictParseSymbol(state, "}"); model2.Materials = res; } function parseGeosetPart(state, countPerObj, type) { const count = parseNumber(state); const arr = new (type === 1 ? Float32Array : Uint8Array)(count * countPerObj); strictParseSymbol(state, "{"); for (let index = 0; index < count; ++index) { parseArray(state, arr, index * countPerObj); strictParseSymbol(state, ","); } strictParseSymbol(state, "}"); return arr; } function parseGeoset(state, model2) { const 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() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Vertices" || keyword2 === "Normals" || keyword2 === "TVertices") { let countPerObj = 3; if (keyword2 === "TVertices") { countPerObj = 2; } const arr = parseGeosetPart( state, countPerObj, 1 /* FLOAT */ ); if (keyword2 === "TVertices") { res.TVertices.push(arr); } else { res[keyword2] = arr; } } else if (keyword2 === "VertexGroup") { res[keyword2] = new Uint8Array(res.Vertices.length / 3); parseArray(state, res[keyword2], 0); } else if (keyword2 === "Faces") { const groupCount = parseNumber(state); const indexCount = parseNumber(state); let pos = 0; res.Faces = new Uint16Array(indexCount); strictParseSymbol(state, "{"); const keyword22 = parseKeyword(state); if (keyword22 !== "Triangles") { throwError(state, "unexpected faces type"); } strictParseSymbol(state, "{"); for (let g = 0; g < groupCount; ++g) { const count = parseArrayCounted(state, res.Faces, pos); if (!count) { throwError(state, "expected array"); } pos += count; parseSymbol(state, ","); } if (pos !== indexCount || indexCount % 3 !== 0) { throwError(state, "mismatched faces array"); } strictParseSymbol(state, "}"); strictParseSymbol(state, "}"); } else if (keyword2 === "Groups") { const groups = []; parseNumber(state); res.TotalGroupsCount = parseNumber(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { parseKeyword(state); groups.push(parseArray(state)); parseSymbol(state, ","); } strictParseSymbol(state, "}"); res.Groups = groups; } else if (keyword2 === "MinimumExtent" || keyword2 === "MaximumExtent") { const arr = new Float32Array(3); res[keyword2] = parseArray(state, arr, 0); strictParseSymbol(state, ","); } else if (keyword2 === "BoundsRadius" || keyword2 === "MaterialID" || keyword2 === "SelectionGroup") { res[keyword2] = parseNumber(state); strictParseSymbol(state, ","); } else if (keyword2 === "Anim") { const [_unused, obj] = parseObject(state); if (obj.Alpha === void 0) { obj.Alpha = 1; } res.Anims.push(obj); } else if (keyword2 === "Unselectable") { res.Unselectable = true; strictParseSymbol(state, ","); } else if (model2.Version >= 900) { if (keyword2 === "LevelOfDetail") { res.LevelOfDetail = parseNumber(state); strictParseSymbol(state, ","); } else if (keyword2 === "Name") { res.Name = parseString(state); strictParseSymbol(state, ","); } else if (keyword2 === "Tangents") { res.Tangents = parseGeosetPart( state, 4, 1 /* FLOAT */ ); } else if (keyword2 === "SkinWeights") { res.SkinWeights = parseGeosetPart( state, 8, 0 /* INT */ ); } } } strictParseSymbol(state, "}"); model2.Geosets.push(res); } function parseGeosetAnim(state, model2) { const res = { GeosetId: -1, Alpha: 1, Color: null, Flags: 0 }; strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword2 = parseKeyword(state); let isStatic = false; if (!keyword2) { throwError(state); } if (keyword2 === "static") { isStatic = true; keyword2 = parseKeyword(state); } if (keyword2 === "Alpha") { if (isStatic) { res.Alpha = parseNumber(state); } else { res.Alpha = parseAnimVector( state, 1 /* FLOAT1 */ ); } } else if (keyword2 === "Color") { if (isStatic) { const array = new Float32Array(3); res.Color = parseArray(state, array, 0); res.Color.reverse(); } else { res.Color = parseAnimVector( state, 2 /* FLOAT3 */ ); for (const key of res.Color.Keys) { key.Vector.reverse(); if (key.InTan) { key.InTan.reverse(); key.OutTan.reverse(); } } } } else if (keyword2 === "DropShadow") { res.Flags |= GeosetAnimFlags[keyword2]; } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.GeosetAnims.push(res); } function parseNode$1(state, type, model2) { const name = parseString(state); const node = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType[type] }; strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling" || keyword2 === "Visibility") { let vectorType = 2; if (keyword2 === "Rotation") { vectorType = 3; } else if (keyword2 === "Visibility") { vectorType = 1; } node[keyword2] = parseAnimVector(state, vectorType); } else if (keyword2 === "BillboardedLockZ" || keyword2 === "BillboardedLockY" || keyword2 === "BillboardedLockX" || keyword2 === "Billboarded" || keyword2 === "CameraAnchored") { node.Flags |= NodeFlags[keyword2]; } else if (keyword2 === "DontInherit") { strictParseSymbol(state, "{"); const 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 (keyword2 === "Path") { node[keyword2] = parseString(state); } else { let val = parseKeyword(state) || parseNumber(state); if (keyword2 === "GeosetId" && val === "Multiple" || keyword2 === "GeosetAnimId" && val === "None") { val = null; } node[keyword2] = val; } parseSymbol(state, ","); parseComment(state); parseSpace(state); } strictParseSymbol(state, "}"); model2.Nodes[node.ObjectId] = node; return node; } function parseBone(state, model2) { const node = parseNode$1(state, "Bone", model2); model2.Bones.push(node); } function parseHelper(state, model2) { const node = parseNode$1(state, "Helper", model2); model2.Helpers.push(node); } function parseAttachment(state, model2) { const node = parseNode$1(state, "Attachment", model2); model2.Attachments.push(node); } function parsePivotPoints$1(state, model2) { const count = parseNumber(state); const res = []; strictParseSymbol(state, "{"); for (let i = 0; i < count; ++i) { res.push(parseArray(state, new Float32Array(3), 0)); strictParseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.PivotPoints = res; } function parseEventObject(state, model2) { const name = parseString(state); const res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, EventTrack: null, Flags: NodeType.EventObject }; strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "EventTrack") { const count = parseNumber(state); res.EventTrack = parseArray(state, new Uint32Array(count), 0); } else if (keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling") { const type = keyword2 === "Rotation" ? 3 : 2; res[keyword2] = parseAnimVector(state, type); } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.EventObjects.push(res); model2.Nodes[res.ObjectId] = res; } function parseCollisionShape(state, model2) { const name = parseString(state); const res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Shape: CollisionShapeType.Box, Vertices: null, Flags: NodeType.CollisionShape }; strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Sphere") { res.Shape = CollisionShapeType.Sphere; } else if (keyword2 === "Box") { res.Shape = CollisionShapeType.Box; } else if (keyword2 === "Vertices") { const count = parseNumber(state); const vertices = new Float32Array(count * 3); strictParseSymbol(state, "{"); for (let i = 0; i < count; ++i) { parseArray(state, vertices, i * 3); strictParseSymbol(state, ","); } strictParseSymbol(state, "}"); res.Vertices = vertices; } else if (keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling") { const type = keyword2 === "Rotation" ? 3 : 2; res[keyword2] = parseAnimVector(state, type); } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.CollisionShapes.push(res); model2.Nodes[res.ObjectId] = res; } function parseGlobalSequences$1(state, model2) { const res = []; const count = parseNumber(state); strictParseSymbol(state, "{"); for (let i = 0; i < count; ++i) { const keyword2 = parseKeyword(state); if (keyword2 === "Duration") { res.push(parseNumber(state)); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.GlobalSequences = res; } function parseUnknownBlock(state) { let opened; while (state.char() !== void 0 && state.char() !== "{") { ++state.pos; } opened = 1; ++state.pos; while (state.char() !== void 0 && opened > 0) { if (state.char() === "{") { ++opened; } else if (state.char() === "}") { --opened; } ++state.pos; } parseSpace(state); } function parseParticleEmitter(state, model2) { const res = { ObjectId: null, Parent: null, Name: null, Flags: 0 }; res.Name = parseString(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword2 = parseKeyword(state); let isStatic = false; if (!keyword2) { throwError(state); } if (keyword2 === "static") { isStatic = true; keyword2 = parseKeyword(state); } if (keyword2 === "ObjectId" || keyword2 === "Parent") { res[keyword2] = parseNumber(state); } else if (keyword2 === "EmitterUsesMDL" || keyword2 === "EmitterUsesTGA") { res.Flags |= ParticleEmitterFlags[keyword2]; } else if (!isStatic && (keyword2 === "Visibility" || keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling" || keyword2 === "EmissionRate" || keyword2 === "Gravity" || keyword2 === "Longitude" || keyword2 === "Latitude")) { let type = 2; if (keyword2 === "Visibility" || keyword2 === "EmissionRate" || keyword2 === "Gravity" || keyword2 === "Longitude" || keyword2 === "Latitude") { type = 1; } else if (keyword2 === "Rotation") { type = 3; } res[keyword2] = parseAnimVector(state, type); } else if (keyword2 === "Particle") { strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword22 = parseKeyword(state); let isStatic2 = false; if (keyword22 === "static") { isStatic2 = true; keyword22 = parseKeyword(state); } if (!isStatic2 && (keyword22 === "LifeSpan" || keyword22 === "InitVelocity")) { res[keyword22] = parseAnimVector( state, 1 /* FLOAT1 */ ); } else if (keyword22 === "LifeSpan" || keyword22 === "InitVelocity") { res[keyword22] = parseNumber(state); } else if (keyword22 === "Path") { res.Path = parseString(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.ParticleEmitters.push(res); } function parseParticleEmitter2(state, model2) { const name = parseString(state); const res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType.ParticleEmitter, FrameFlags: 0 }; strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword2 = parseKeyword(state); let isStatic = false; if (!keyword2) { throwError(state); } if (keyword2 === "static") { isStatic = true; keyword2 = parseKeyword(state); } if (!isStatic && (keyword2 === "Speed" || keyword2 === "Latitude" || keyword2 === "Visibility" || keyword2 === "EmissionRate" || keyword2 === "Width" || keyword2 === "Length" || keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling" || keyword2 === "Gravity" || keyword2 === "Variation")) { let type = 2; switch (keyword2) { case "Rotation": type = 3; break; case "Speed": case "Latitude": case "Visibility": case "EmissionRate": case "Width": case "Length": case "Gravity": case "Variation": type = 1; break; } res[keyword2] = parseAnimVector(state, type); } else if (keyword2 === "Variation" || keyword2 === "Gravity" || keyword2 === "ReplaceableId" || keyword2 === "PriorityPlane") { res[keyword2] = parseNumber(state); } else if (keyword2 === "SortPrimsFarZ" || keyword2 === "Unshaded" || keyword2 === "LineEmitter" || keyword2 === "Unfogged" || keyword2 === "ModelSpace" || keyword2 === "XYQuad") { res.Flags |= ParticleEmitter2Flags[keyword2]; } else if (keyword2 === "Both") { res.FrameFlags |= ParticleEmitter2FramesFlags.Head | ParticleEmitter2FramesFlags.Tail; } else if (keyword2 === "Head" || keyword2 === "Tail") { res.FrameFlags |= ParticleEmitter2FramesFlags[keyword2]; } else if (keyword2 === "Squirt") { res[keyword2] = true; } else if (keyword2 === "DontInherit") { strictParseSymbol(state, "{"); const 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 (keyword2 === "SegmentColor") { const colors = []; strictParseSymbol(state, "{"); while (state.char() !== "}") { parseKeyword(state); const colorArr = new Float32Array(3); parseArray(state, colorArr, 0); const temp = colorArr[0]; colorArr[0] = colorArr[2]; colorArr[2] = temp; colors.push(colorArr); parseSymbol(state, ","); } strictParseSymbol(state, "}"); res.SegmentColor = colors; } else if (keyword2 === "Alpha") { res.Alpha = new Uint8Array(3); parseArray(state, res.Alpha, 0); } else if (keyword2 === "ParticleScaling") { res[keyword2] = new Float32Array(3); parseArray(state, res[keyword2], 0); } else if (keyword2 === "LifeSpanUVAnim" || keyword2 === "DecayUVAnim" || keyword2 === "TailUVAnim" || keyword2 === "TailDecayUVAnim") { res[keyword2] = new Uint32Array(3); parseArray(state, res[keyword2], 0); } else if (keyword2 === "Transparent" || keyword2 === "Blend" || keyword2 === "Additive" || keyword2 === "AlphaKey" || keyword2 === "Modulate" || keyword2 === "Modulate2x") { res.FilterMode = ParticleEmitter2FilterMode[keyword2]; } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.ParticleEmitters2.push(res); model2.Nodes[res.ObjectId] = res; } function parseCamera(state, model2) { const res = { Name: null, Position: null, FieldOfView: 0, NearClip: 0, FarClip: 0, TargetPosition: null }; res.Name = parseString(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Position") { res.Position = new Float32Array(3); parseArray(state, res.Position, 0); } else if (keyword2 === "FieldOfView" || keyword2 === "NearClip" || keyword2 === "FarClip") { res[keyword2] = parseNumber(state); } else if (keyword2 === "Target") { strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword22 = parseKeyword(state); if (keyword22 === "Position") { res.TargetPosition = new Float32Array(3); parseArray(state, res.TargetPosition, 0); } else if (keyword22 === "Translation") { res.TargetTranslation = parseAnimVector( state, 2 /* FLOAT3 */ ); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); } else if (keyword2 === "Translation" || keyword2 === "Rotation") { res[keyword2] = parseAnimVector( state, keyword2 === "Rotation" ? 1 : 2 /* FLOAT3 */ ); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.Cameras.push(res); } function parseLight(state, model2) { const name = parseString(state); const res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType.Light, LightType: 0 }; strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword2 = parseKeyword(state); let isStatic = false; if (!keyword2) { throwError(state); } if (keyword2 === "static") { isStatic = true; keyword2 = parseKeyword(state); } if (!isStatic && (keyword2 === "Visibility" || keyword2 === "Color" || keyword2 === "Intensity" || keyword2 === "AmbIntensity" || keyword2 === "AmbColor" || keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling" || keyword2 === "AttenuationStart" || keyword2 === "AttenuationEnd")) { let type = 2; switch (keyword2) { case "Rotation": type = 3; break; case "Visibility": case "Intensity": case "AmbIntensity": case "AttenuationStart": case "AttenuationEnd": type = 1; break; } res[keyword2] = parseAnimVector(state, type); if (keyword2 === "Color" || keyword2 === "AmbColor") { for (const key of res[keyword2].Keys) { key.Vector.reverse(); if (key.InTan) { key.InTan.reverse(); key.OutTan.reverse(); } } } } else if (keyword2 === "Omnidirectional" || keyword2 === "Directional" || keyword2 === "Ambient") { res.LightType = LightType[keyword2]; } else if (keyword2 === "Color" || keyword2 === "AmbColor") { const color2 = new Float32Array(3); parseArray(state, color2, 0); const temp = color2[0]; color2[0] = color2[2]; color2[2] = temp; res[keyword2] = color2; } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.Lights.push(res); model2.Nodes[res.ObjectId] = res; } function parseTextureAnims$1(state, model2) { const res = []; parseNumber(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { const obj = {}; parseKeyword(state); strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling") { const type = keyword2 === "Rotation" ? 3 : 2; obj[keyword2] = parseAnimVector(state, type); } else { throw new Error("Unknown texture anim property " + keyword2); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); res.push(obj); } strictParseSymbol(state, "}"); model2.TextureAnims = res; } function parseRibbonEmitter(state, model2) { const name = parseString(state); const 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: 0, Gravity: null, Visibility: null }; strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword2 = parseKeyword(state); let isStatic = false; if (!keyword2) { throwError(state); } if (keyword2 === "static") { isStatic = true; keyword2 = parseKeyword(state); } if (!isStatic && (keyword2 === "Visibility" || keyword2 === "HeightAbove" || keyword2 === "HeightBelow" || keyword2 === "Translation" || keyword2 === "Rotation" || keyword2 === "Scaling" || keyword2 === "Alpha" || keyword2 === "TextureSlot")) { let type = 2; switch (keyword2) { case "Rotation": type = 3; break; case "Visibility": case "HeightAbove": case "HeightBelow": case "Alpha": type = 1; break; case "TextureSlot": type = 0; break; } res[keyword2] = parseAnimVector(state, type); } else if (keyword2 === "Color") { const color2 = new Float32Array(3); parseArray(state, color2, 0); const temp = color2[0]; color2[0] = color2[2]; color2[2] = temp; res[keyword2] = color2; } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.RibbonEmitters.push(res); model2.Nodes[res.ObjectId] = res; } function parseFaceFX$1(state, model2) { if (model2.Version < 900) { throwError(state, "Unexpected model chunk FaceFX"); } const name = parseString(state); const res = { Name: name, Path: "" }; strictParseSymbol(state, "{"); while (state.char() !== "}") { const keyword2 = parseKeyword(state); if (!keyword2) { throwError(state); } if (keyword2 === "Path") { res.Path = parseString(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.FaceFX = model2.FaceFX || []; model2.FaceFX.push(res); } function parseBindPose$1(state, model2) { if (model2.Version < 900) { throwError(state, "Unexpected model chunk BindPose"); } const res = { Matrices: [] }; strictParseSymbol(state, "{"); parseKeyword(state); const count = parseNumber(state); strictParseSymbol(state, "{"); for (let i = 0; i < count; ++i) { const matrix = new Float32Array(12); parseArray(state, matrix, 0); parseSymbol(state, ","); res.Matrices.push(matrix); } strictParseSymbol(state, "}"); strictParseSymbol(state, "}"); model2.BindPoses = model2.BindPoses || []; model2.BindPoses.push(res); } function parseParticleEmitterPopcorn$1(state, model2) { if (model2.Version < 900) { throwError(state, "Unexpected model chunk ParticleEmitterPopcorn"); } const name = parseString(state); const res = { Name: name, ObjectId: null, Parent: null, PivotPoint: null, Flags: NodeType.ParticleEmitter }; strictParseSymbol(state, "{"); while (state.char() !== "}") { let keyword2 = parseKeyword(state); let isStatic = false; if (!keyword2) { throwError(state); } if (keyword2 === "static") { isStatic = true; keyword2 = parseKeyword(state); } if (!isStatic && (keyword2 === "LifeSpan" || keyword2 === "EmissionRate" || keyword2 === "Speed" || keyword2 === "Color" || keyword2 === "Alpha" || keyword2 === "Visibility" || keyword2 === "Rotation" || keyword2 === "Scaling" || keyword2 === "Translation")) { let type = 2; switch (keyword2) { case "LifeSpan": case "EmissionRate": case "Speed": case "Alpha": case "Visibility": type = 1; break; } res[keyword2] = parseAnimVector(state, type); } else if (keyword2 === "LifeSpan" || keyword2 === "EmissionRate" || keyword2 === "Speed" || keyword2 === "Alpha") { res[keyword2] = parseNumber(state); } else if (keyword2 === "Color") { const array = new Float32Array(3); res[keyword2] = parseArray(state, array, 0); } else if (keyword2 === "ReplaceableId") { res[keyword2] = parseNumber(state); } else if (keyword2 === "Path" || keyword2 === "AnimVisibilityGuide") { res[keyword2] = parseString(state); } else if (keyword2 === "Unshaded" || keyword2 === "SortPrimsFarZ" || keyword2 === "Unfogged") { if (keyword2 === "Unshaded") { res.Flags |= ParticleEmitterPopcornFlags.Unshaded; } else if (keyword2 === "Unfogged") { res.Flags |= ParticleEmitterPopcornFlags.Unfogged; } else if (keyword2 === "SortPrimsFarZ") { res.Flags |= ParticleEmitterPopcornFlags.SortPrimsFarZ; } } else { res[keyword2] = parseNumber(state); } parseSymbol(state, ","); } strictParseSymbol(state, "}"); model2.ParticleEmitterPopcorns = model2.ParticleEmitterPopcorns || []; model2.ParticleEmitterPopcorns.push(res); model2.Nodes[res.ObjectId] = res; } const parsers$1 = { Version: parseVersion$1, Model: parseModelInfo$1, Sequences: parseSequences$1, Textures: parseTextures$1, Materials: parseMaterials$1, Geoset: parseGeoset, GeosetAnim: parseGeosetAnim, Bone: parseBone, Helper: parseHelper, Attachment: parseAttachment, PivotPoints: parsePivotPoints$1, EventObject: parseEventObject, CollisionShape: parseCollisionShape, GlobalSequences: parseGlobalSequences