From 7c1a9158212284b97ce96ac3b08900e629a27dc3 Mon Sep 17 00:00:00 2001 From: Chad Furman Date: Sun, 25 Feb 2024 09:05:25 -0500 Subject: [PATCH 1/5] wip update of dist --- dist/Accessor.d.ts | 24 +++++ dist/Animation.d.ts | 38 ++++++++ dist/Draco.d.ts | 15 +++ dist/Glb.d.ts | 2 + dist/InterleavedBuffer.d.ts | 24 +++++ dist/Material.d.ts | 13 +++ dist/Mesh.d.ts | 24 +++++ dist/Pose.d.ts | 14 +++ dist/Skin.d.ts | 17 ++++ dist/Texture.d.ts | 6 ++ dist/{gltf2parser.cjs.js => gltf2parser.js} | 102 +++++++++++++++++++- dist/{gltf2parser.es.js => gltf2parser.mjs} | 100 +++++++++++++++++++ dist/index.d.ts | 40 ++++++++ dist/structs.d.ts | 3 + dist/types.d.ts | 4 + package.json | 79 +++++++-------- 16 files changed, 466 insertions(+), 39 deletions(-) create mode 100644 dist/Accessor.d.ts create mode 100644 dist/Animation.d.ts create mode 100644 dist/Draco.d.ts create mode 100644 dist/Glb.d.ts create mode 100644 dist/InterleavedBuffer.d.ts create mode 100644 dist/Material.d.ts create mode 100644 dist/Mesh.d.ts create mode 100644 dist/Pose.d.ts create mode 100644 dist/Skin.d.ts create mode 100644 dist/Texture.d.ts rename dist/{gltf2parser.cjs.js => gltf2parser.js} (89%) rename dist/{gltf2parser.es.js => gltf2parser.mjs} (89%) create mode 100644 dist/index.d.ts create mode 100644 dist/structs.d.ts create mode 100644 dist/types.d.ts diff --git a/dist/Accessor.d.ts b/dist/Accessor.d.ts new file mode 100644 index 0000000..cf5518c --- /dev/null +++ b/dist/Accessor.d.ts @@ -0,0 +1,24 @@ +import { TTypeArray } from "./types"; +type TBufferView = { + byteOffset?: number; +}; +type TAccessor = { + componentType: number; + type: number; + count: number; + byteOffset?: number; + min?: Array; + max?: Array; +}; +declare class Accessor { + componentLen: number; + elementCnt: number; + byteOffset: number; + byteSize: number; + boundMin: Array | null; + boundMax: Array | null; + type: string | null; + data: TTypeArray | null; + fromBin(accessor: TAccessor, bufView: TBufferView, bin: ArrayBuffer): this; +} +export default Accessor; diff --git a/dist/Animation.d.ts b/dist/Animation.d.ts new file mode 100644 index 0000000..41bd5cb --- /dev/null +++ b/dist/Animation.d.ts @@ -0,0 +1,38 @@ +import type Accessor from './Accessor'; +type Transform = typeof ETransform[keyof typeof ETransform]; +export declare const ETransform: { + readonly Rot: 0; + readonly Pos: 1; + readonly Scl: 2; +}; +type Lerp = typeof ELerp[keyof typeof ELerp]; +export declare const ELerp: { + readonly Step: 0; + readonly Linear: 1; + readonly Cubic: 2; +}; +export declare class Track { + static Transform: { + readonly Rot: 0; + readonly Pos: 1; + readonly Scl: 2; + }; + static Lerp: { + readonly Step: 0; + readonly Linear: 1; + readonly Cubic: 2; + }; + transform: Transform; + interpolation: Lerp; + jointIndex: number; + timeStampIndex: number; + keyframes: Accessor; + static fromGltf(jointIdx: number, target: string, inter: string): Track; +} +export declare class Animation { + name: string; + timestamps: Array; + tracks: Array; + constructor(name?: string); +} +export {}; diff --git a/dist/Draco.d.ts b/dist/Draco.d.ts new file mode 100644 index 0000000..e556d31 --- /dev/null +++ b/dist/Draco.d.ts @@ -0,0 +1,15 @@ +import type { Primitive } from './Mesh'; +import Accessor from './Accessor'; +export default class Draco { + mod: any; + decoder: any; + mesh: any; + faceCnt: number; + vertCnt: number; + constructor(mod: any); + dispose(): void; + loadMesh(bin: ArrayBuffer, offset: number, len: number): this; + loadPrimitive(prim: Primitive, dAttr: any, gAttr: any, json: any): void; + parseAttribute(dIdx: number, gIdx: number, json: any): Accessor; + decodeAttributeData(id: any, type: string, len: number): any; +} diff --git a/dist/Glb.d.ts b/dist/Glb.d.ts new file mode 100644 index 0000000..9efba31 --- /dev/null +++ b/dist/Glb.d.ts @@ -0,0 +1,2 @@ +declare function parseGLB(res: Response): Promise<[JSON, ArrayBuffer] | null>; +export default parseGLB; diff --git a/dist/InterleavedBuffer.d.ts b/dist/InterleavedBuffer.d.ts new file mode 100644 index 0000000..7eee91c --- /dev/null +++ b/dist/InterleavedBuffer.d.ts @@ -0,0 +1,24 @@ +declare class Attrib { + byteOffset: number; + componentLen: number; + boundMin: Array | null; + boundMax: Array | null; + constructor(accID: number, json: any); +} +export default class InterleavedBuffer { + data: Float32Array | null; + elementCnt: number; + componentLen: number; + byteStride: number; + byteSize: number; + position: Attrib | null; + normal: Attrib | null; + tangent: Attrib | null; + texcoord_0: Attrib | null; + texcoord_1: Attrib | null; + color_0: Attrib | null; + joints_0: Attrib | null; + weights_0: Attrib | null; + constructor(attr: any, json: any, bin: ArrayBuffer); +} +export {}; diff --git a/dist/Material.d.ts b/dist/Material.d.ts new file mode 100644 index 0000000..e8ebfb3 --- /dev/null +++ b/dist/Material.d.ts @@ -0,0 +1,13 @@ +import { TVec4 } from './types'; +export declare class Material { + index: number; + name: string; + baseColor: TVec4; + metallic: number; + roughness: number; + constructor(mat: any); + get baseColorHex(): number; + get baseColorGammaHex(): number; + get baseColorString(): string; + get baseColorGammaString(): string; +} diff --git a/dist/Mesh.d.ts b/dist/Mesh.d.ts new file mode 100644 index 0000000..3134c8b --- /dev/null +++ b/dist/Mesh.d.ts @@ -0,0 +1,24 @@ +import Accessor from './Accessor'; +import InterleavedBuffer from './InterleavedBuffer'; +export declare class Mesh { + index: number | null; + name: string | null; + primitives: Array; + position: number[] | null; + rotation: number[] | null; + scale: number[] | null; +} +export declare class Primitive { + materialName: string | null; + materialIdx: number | null; + indices: Accessor | null; + position: Accessor | null; + normal: Accessor | null; + tangent: Accessor | null; + texcoord_0: Accessor | null; + texcoord_1: Accessor | null; + color_0: Accessor | null; + joints_0: Accessor | null; + weights_0: Accessor | null; + interleaved: InterleavedBuffer | null; +} diff --git a/dist/Pose.d.ts b/dist/Pose.d.ts new file mode 100644 index 0000000..601b0f3 --- /dev/null +++ b/dist/Pose.d.ts @@ -0,0 +1,14 @@ +declare class PoseJoint { + index: number; + rot?: number[]; + pos?: number[]; + scl?: number[]; + constructor(idx: number, rot?: number[], pos?: number[], scl?: number[]); +} +declare class Pose { + name: string; + joints: Array; + constructor(name?: string); + add(idx: number, rot?: number[], pos?: number[], scl?: number[]): void; +} +export { Pose, PoseJoint }; diff --git a/dist/Skin.d.ts b/dist/Skin.d.ts new file mode 100644 index 0000000..15d270c --- /dev/null +++ b/dist/Skin.d.ts @@ -0,0 +1,17 @@ +export declare class Skin { + index: number | null; + name: string | null; + joints: Array; + position: number[] | null; + rotation: number[] | null; + scale: number[] | null; +} +export declare class SkinJoint { + name: string | null; + index: number | null; + parentIndex: number | null; + bindMatrix: number[] | null; + position: number[] | null; + rotation: number[] | null; + scale: number[] | null; +} diff --git a/dist/Texture.d.ts b/dist/Texture.d.ts new file mode 100644 index 0000000..fefc3b7 --- /dev/null +++ b/dist/Texture.d.ts @@ -0,0 +1,6 @@ +export declare class Texture { + index: number | null; + name: string | null; + mime: string | null; + blob: Blob | null; +} diff --git a/dist/gltf2parser.cjs.js b/dist/gltf2parser.js similarity index 89% rename from dist/gltf2parser.cjs.js rename to dist/gltf2parser.js index 4ebf81f..92f09c7 100644 --- a/dist/gltf2parser.cjs.js +++ b/dist/gltf2parser.js @@ -55,6 +55,7 @@ const ComponentTypeMap = { 5126: [4, Float32Array, "float", "FLOAT"] }; const ComponentVarMap = { + // Component Length of Each Var Type SCALAR: 1, VEC2: 2, VEC3: 3, @@ -76,6 +77,7 @@ class Accessor { fromBin(accessor, bufView, bin) { const [ compByte, + // Type Byte Size compType, typeName ] = ComponentTypeMap[accessor.componentType]; @@ -154,6 +156,7 @@ class InterleavedBuffer { } class Draco { + // #region MAIN mod; decoder; mesh; @@ -163,12 +166,15 @@ class Draco { this.mod = mod; this.decoder = new this.mod.Decoder(); } + // #endregion + // #region METHODS dispose() { this.mod.destroy(this.decoder); if (this.mesh) this.mod.destroy(this.mesh); this.mod = null; } + // Decode BIN data into Draco Mesh Data loadMesh(bin, offset, len) { const slice = new Int8Array(bin, offset, len); const buf = new this.mod.DecoderBuffer(); @@ -180,6 +186,9 @@ class Draco { this.vertCnt = this.mesh.num_points(); return this; } + // #endregion + // #region HELPERS + // Load all the attributes of a primitive loadPrimitive(prim, dAttr, gAttr, json) { if (dAttr.POSITION != void 0) prim.position = this.parseAttribute(dAttr.POSITION, gAttr.POSITION, json); @@ -209,6 +218,7 @@ class Draco { this.faceCnt = 0; this.vertCnt = 0; } + // Parse Mesh Attribute parseAttribute(dIdx, gIdx, json) { const accessor = json.accessors[gIdx]; const id = this.decoder.GetAttributeByUniqueId(this.mesh, dIdx); @@ -224,6 +234,7 @@ class Draco { out.data = this.decodeAttributeData(id, dType, out.componentLen * this.vertCnt); return out; } + // Decoding attribute data from DracoMesh decodeAttributeData(id, type, len) { let tAry; let dAry; @@ -264,15 +275,22 @@ class Draco { this.mod.destroy(dAry); return tAry; } + // #endregion } class Mesh { index = null; + // Index in Mesh Collection name = null; + // Mesh Name primitives = []; + // Mesh is made up of more then one Primative position = null; + // Node's Position rotation = null; + // Node's Rotation scale = null; + // Node's Scale } class Primitive { materialName = null; @@ -291,20 +309,35 @@ class Primitive { class Skin { index = null; + // Index in Mesh Collection name = null; + // Skin Name joints = []; + // Collection of Joints + // Sometimes Skin Objects will have their own transform in nodes + // Tends to come from FBX to GLTF conversion in blender. position = null; + // Local Space Position rotation = null; + // Local Space Rotation scale = null; + // Local Space Scale } class SkinJoint { name = null; + // Name of Joint index = null; + // Joint Index parentIndex = null; + // Parent Joint Index, Null if its a Root Joint bindMatrix = null; + // Inverted WorldSpace Transform position = null; + // Local Space Position rotation = null; + // Local Space Rotation scale = null; + // Local Space Scale } const ETransform = { @@ -318,8 +351,11 @@ const ELerp = { Cubic: 2 }; class Track { + //#region ENUMS static Transform = ETransform; static Lerp = ELerp; + //#endregion + //#region MAIN transform = ETransform.Pos; interpolation = ELerp.Step; jointIndex = 0; @@ -352,6 +388,7 @@ class Track { } return t; } + //#endregion } class Animation { name = ""; @@ -365,12 +402,17 @@ class Animation { class Texture { index = null; + // Index in Texture Collection name = null; + // Texture Name mime = null; + // Texture Mime blob = null; + // Image Binary } class PoseJoint { + //#region MAIN index; rot; pos; @@ -381,6 +423,7 @@ class PoseJoint { this.pos = pos; this.scl = scl; } + //#endregion } class Pose { name = ""; @@ -412,6 +455,7 @@ class Material { baseColor = [0, 0, 0, 1]; metallic = 0; roughness = 0; + // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(mat) { this.name = mat.name || window.crypto.randomUUID(); if (mat.pbrMetallicRoughness) { @@ -440,6 +484,7 @@ class Material { } class Gltf2Parser { + // #region MAIN json; bin; _needsDraco = false; @@ -462,6 +507,8 @@ class Gltf2Parser { if (this._extDraco) this._extDraco.dispose(); } + // #endregion + // #region NODES getNodeByName(n) { let o, i; for (i = 0; i < this.json.nodes.length; i++) { @@ -471,6 +518,8 @@ class Gltf2Parser { } return null; } + // #endregion + // #region MESHES getMeshNames() { const json = this.json, rtn = []; let i; @@ -585,6 +634,8 @@ class Gltf2Parser { } return mesh; } + // #endregion + // #region SKINS getSkinNames() { const json = this.json, rtn = []; let i; @@ -692,6 +743,9 @@ class Gltf2Parser { } return skin; } + // #endregion + // #region MATERIALS + // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_010_Materials.md getMaterial(id) { if (!this.json.materials) { console.warn("No Materials in GLTF File"); @@ -739,6 +793,7 @@ class Gltf2Parser { } return rtn; } + // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#texture-data getTexture(id) { const js = this.json; const t = js.textures[id]; @@ -752,6 +807,43 @@ class Gltf2Parser { tex.blob = new Blob([bAry], { type: img.mimeType }); return tex; } + // #endregion + // #region ANIMATION + /* + https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations + https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation (Has math for cubic spline) + https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation + animation = { + frame_cnt : int + time : float, + times : [ float32array, float32array, etc ], + tracks : [ + { + type : "rot || pos || scl", + time_idx : 0, + joint_idx : 0, + lerp : "LINEAR || STEP || CUBICSPLINE", + data : float32array, + }, + ] + } + { + name: '', + channels: [ { + sampler: SAMPLER_INDEX, + target:{ + node : NODE_INDEX, ( NEED TO TRANSLATE NODE INDEX TO JOINT INDEX ) + path : 'translation' | 'rotation' | 'scale' + } + ] ], + samplers: [ { + input: ACCESSOR_INDEX_FOR_KEYFRAME_TIMESTAMP, + interpolation: 'LINEAR' | 'STEP' | 'CUBICSPLINE', + output: ACCESSOR_INDEX_FOR_KEYFRAME_TRANFORM_VALUE, + } ], + } + + */ getAnimationNames() { const json = this.json, rtn = []; let i; @@ -843,6 +935,8 @@ class Gltf2Parser { } return anim; } + // #endregion + // #region POSES ( CUSTOM, NOT PART OF GLTF SPEC ) getPoseByName(n) { let o, i; for (i = 0; i < this.json.poses.length; i++) { @@ -881,6 +975,9 @@ class Gltf2Parser { } return pose; } + // #endregion + // #region SUPPORT + // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md parseAccessor(accID) { const accessor = this.json.accessors[accID]; const bufView = this.json.bufferViews[accessor.bufferView]; @@ -905,6 +1002,8 @@ class Gltf2Parser { } return false; } + // #endregion + // #region STATIC static async fetch(url) { const res = await fetch(url); if (!res.ok) @@ -926,6 +1025,7 @@ class Gltf2Parser { } return null; } + // #endregion } exports.Accessor = Accessor; @@ -939,5 +1039,5 @@ exports.Skin = Skin; exports.SkinJoint = SkinJoint; exports.Texture = Texture; exports.Track = Track; -exports["default"] = Gltf2Parser; +exports.default = Gltf2Parser; exports.parseGLB = parseGLB; diff --git a/dist/gltf2parser.es.js b/dist/gltf2parser.mjs similarity index 89% rename from dist/gltf2parser.es.js rename to dist/gltf2parser.mjs index d16ecaa..df54d79 100644 --- a/dist/gltf2parser.es.js +++ b/dist/gltf2parser.mjs @@ -51,6 +51,7 @@ const ComponentTypeMap = { 5126: [4, Float32Array, "float", "FLOAT"] }; const ComponentVarMap = { + // Component Length of Each Var Type SCALAR: 1, VEC2: 2, VEC3: 3, @@ -72,6 +73,7 @@ class Accessor { fromBin(accessor, bufView, bin) { const [ compByte, + // Type Byte Size compType, typeName ] = ComponentTypeMap[accessor.componentType]; @@ -150,6 +152,7 @@ class InterleavedBuffer { } class Draco { + // #region MAIN mod; decoder; mesh; @@ -159,12 +162,15 @@ class Draco { this.mod = mod; this.decoder = new this.mod.Decoder(); } + // #endregion + // #region METHODS dispose() { this.mod.destroy(this.decoder); if (this.mesh) this.mod.destroy(this.mesh); this.mod = null; } + // Decode BIN data into Draco Mesh Data loadMesh(bin, offset, len) { const slice = new Int8Array(bin, offset, len); const buf = new this.mod.DecoderBuffer(); @@ -176,6 +182,9 @@ class Draco { this.vertCnt = this.mesh.num_points(); return this; } + // #endregion + // #region HELPERS + // Load all the attributes of a primitive loadPrimitive(prim, dAttr, gAttr, json) { if (dAttr.POSITION != void 0) prim.position = this.parseAttribute(dAttr.POSITION, gAttr.POSITION, json); @@ -205,6 +214,7 @@ class Draco { this.faceCnt = 0; this.vertCnt = 0; } + // Parse Mesh Attribute parseAttribute(dIdx, gIdx, json) { const accessor = json.accessors[gIdx]; const id = this.decoder.GetAttributeByUniqueId(this.mesh, dIdx); @@ -220,6 +230,7 @@ class Draco { out.data = this.decodeAttributeData(id, dType, out.componentLen * this.vertCnt); return out; } + // Decoding attribute data from DracoMesh decodeAttributeData(id, type, len) { let tAry; let dAry; @@ -260,15 +271,22 @@ class Draco { this.mod.destroy(dAry); return tAry; } + // #endregion } class Mesh { index = null; + // Index in Mesh Collection name = null; + // Mesh Name primitives = []; + // Mesh is made up of more then one Primative position = null; + // Node's Position rotation = null; + // Node's Rotation scale = null; + // Node's Scale } class Primitive { materialName = null; @@ -287,20 +305,35 @@ class Primitive { class Skin { index = null; + // Index in Mesh Collection name = null; + // Skin Name joints = []; + // Collection of Joints + // Sometimes Skin Objects will have their own transform in nodes + // Tends to come from FBX to GLTF conversion in blender. position = null; + // Local Space Position rotation = null; + // Local Space Rotation scale = null; + // Local Space Scale } class SkinJoint { name = null; + // Name of Joint index = null; + // Joint Index parentIndex = null; + // Parent Joint Index, Null if its a Root Joint bindMatrix = null; + // Inverted WorldSpace Transform position = null; + // Local Space Position rotation = null; + // Local Space Rotation scale = null; + // Local Space Scale } const ETransform = { @@ -314,8 +347,11 @@ const ELerp = { Cubic: 2 }; class Track { + //#region ENUMS static Transform = ETransform; static Lerp = ELerp; + //#endregion + //#region MAIN transform = ETransform.Pos; interpolation = ELerp.Step; jointIndex = 0; @@ -348,6 +384,7 @@ class Track { } return t; } + //#endregion } class Animation { name = ""; @@ -361,12 +398,17 @@ class Animation { class Texture { index = null; + // Index in Texture Collection name = null; + // Texture Name mime = null; + // Texture Mime blob = null; + // Image Binary } class PoseJoint { + //#region MAIN index; rot; pos; @@ -377,6 +419,7 @@ class PoseJoint { this.pos = pos; this.scl = scl; } + //#endregion } class Pose { name = ""; @@ -408,6 +451,7 @@ class Material { baseColor = [0, 0, 0, 1]; metallic = 0; roughness = 0; + // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(mat) { this.name = mat.name || window.crypto.randomUUID(); if (mat.pbrMetallicRoughness) { @@ -436,6 +480,7 @@ class Material { } class Gltf2Parser { + // #region MAIN json; bin; _needsDraco = false; @@ -458,6 +503,8 @@ class Gltf2Parser { if (this._extDraco) this._extDraco.dispose(); } + // #endregion + // #region NODES getNodeByName(n) { let o, i; for (i = 0; i < this.json.nodes.length; i++) { @@ -467,6 +514,8 @@ class Gltf2Parser { } return null; } + // #endregion + // #region MESHES getMeshNames() { const json = this.json, rtn = []; let i; @@ -581,6 +630,8 @@ class Gltf2Parser { } return mesh; } + // #endregion + // #region SKINS getSkinNames() { const json = this.json, rtn = []; let i; @@ -688,6 +739,9 @@ class Gltf2Parser { } return skin; } + // #endregion + // #region MATERIALS + // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_010_Materials.md getMaterial(id) { if (!this.json.materials) { console.warn("No Materials in GLTF File"); @@ -735,6 +789,7 @@ class Gltf2Parser { } return rtn; } + // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#texture-data getTexture(id) { const js = this.json; const t = js.textures[id]; @@ -748,6 +803,43 @@ class Gltf2Parser { tex.blob = new Blob([bAry], { type: img.mimeType }); return tex; } + // #endregion + // #region ANIMATION + /* + https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations + https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation (Has math for cubic spline) + https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation + animation = { + frame_cnt : int + time : float, + times : [ float32array, float32array, etc ], + tracks : [ + { + type : "rot || pos || scl", + time_idx : 0, + joint_idx : 0, + lerp : "LINEAR || STEP || CUBICSPLINE", + data : float32array, + }, + ] + } + { + name: '', + channels: [ { + sampler: SAMPLER_INDEX, + target:{ + node : NODE_INDEX, ( NEED TO TRANSLATE NODE INDEX TO JOINT INDEX ) + path : 'translation' | 'rotation' | 'scale' + } + ] ], + samplers: [ { + input: ACCESSOR_INDEX_FOR_KEYFRAME_TIMESTAMP, + interpolation: 'LINEAR' | 'STEP' | 'CUBICSPLINE', + output: ACCESSOR_INDEX_FOR_KEYFRAME_TRANFORM_VALUE, + } ], + } + + */ getAnimationNames() { const json = this.json, rtn = []; let i; @@ -839,6 +931,8 @@ class Gltf2Parser { } return anim; } + // #endregion + // #region POSES ( CUSTOM, NOT PART OF GLTF SPEC ) getPoseByName(n) { let o, i; for (i = 0; i < this.json.poses.length; i++) { @@ -877,6 +971,9 @@ class Gltf2Parser { } return pose; } + // #endregion + // #region SUPPORT + // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md parseAccessor(accID) { const accessor = this.json.accessors[accID]; const bufView = this.json.bufferViews[accessor.bufferView]; @@ -901,6 +998,8 @@ class Gltf2Parser { } return false; } + // #endregion + // #region STATIC static async fetch(url) { const res = await fetch(url); if (!res.ok) @@ -922,6 +1021,7 @@ class Gltf2Parser { } return null; } + // #endregion } export { Accessor, Animation, ELerp, ETransform, Mesh, Pose, Primitive, Skin, SkinJoint, Texture, Track, Gltf2Parser as default, parseGLB }; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..bc47de6 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,40 @@ +import parseGLB from './Glb'; +import Accessor from './Accessor'; +import Draco from './Draco'; +import { Mesh, Primitive } from './Mesh'; +import { Skin, SkinJoint } from './Skin'; +import { Animation, Track, ETransform, ELerp } from './Animation'; +import { Texture } from './Texture'; +import { Pose } from './Pose'; +import { Material } from './Material'; +declare class Gltf2Parser { + json: any; + bin: ArrayBuffer; + _needsDraco: boolean; + _extDraco?: Draco; + constructor(json: any, bin?: ArrayBuffer | null); + get needsDraco(): boolean; + useDraco(mod: any): this; + dispose(): void; + getNodeByName(n: string): [any, number] | null; + getMeshNames(): Array; + getMeshByName(n: string): [any, number] | null; + getMeshNodes(idx: number): Array; + getMesh(id: string | number | undefined): Mesh | null; + getSkinNames(): Array; + getSkinByName(n: string): [any, number] | null; + getSkin(id: string | number | undefined): Skin | null; + getMaterial(id: string | number | undefined): Material | null; + getAllMaterials(): Record; + getTexture(id: number): Texture | null; + getAnimationNames(): Array; + getAnimationByName(n: string): [any, number] | null; + getAnimation(id: string | number | undefined): any; + getPoseByName(n: string): [any, number] | null; + getPose(id?: string): Pose | null; + parseAccessor(accID: number): Accessor | null; + isAccessorInterleaved(accID: number): boolean; + static fetch(url: string): Promise; +} +export default Gltf2Parser; +export { parseGLB, Accessor, Texture, Pose, Mesh, Primitive, Skin, SkinJoint, Animation, Track, ETransform, ELerp, }; diff --git a/dist/structs.d.ts b/dist/structs.d.ts new file mode 100644 index 0000000..e1f3f00 --- /dev/null +++ b/dist/structs.d.ts @@ -0,0 +1,3 @@ +import { TTypeArrayCon } from './types'; +export declare const ComponentTypeMap: Record; +export declare const ComponentVarMap: Record; diff --git a/dist/types.d.ts b/dist/types.d.ts new file mode 100644 index 0000000..520df76 --- /dev/null +++ b/dist/types.d.ts @@ -0,0 +1,4 @@ +export type TTypeArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Uint32Array | Float32Array; +export type TTypeArrayCon = Int8ArrayConstructor | Uint8ArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor; +export type TVec3 = Array | [number, number, number]; +export type TVec4 = Array | [number, number, number, number]; diff --git a/package.json b/package.json index be1a985..7036495 100644 --- a/package.json +++ b/package.json @@ -1,46 +1,49 @@ { - "name" : "gltf2parser", - "version" : "0.0.2", - "author" : "Pedro Sousa ( Vor @ SketchPunk Labs )", - "description" : "Parse GLTF2 Files", - "keywords" : ["gltf", "webgl", "mesh"], - "license" : "MIT", - - "homepage" : "https://github.com/sketchpunklabs/gltf2parser#readme", - "repository" : { "type": "git", "url": "git+https://github.com/sketchpunklabs/gltf2parser.git" }, - "bugs" : { "url": "https://github.com/sketchpunklabs/gltf2parser/issues" }, - - "files" : [ "dist" ], - "main" : "./dist/gltf2parser.cjs.js", - "module" : "./dist/gltf2parser.es.js", - "types" : "./dist/index.d.ts", + "name": "gltf2parser", + "version": "0.0.2", + "author": "Pedro Sousa ( Vor @ SketchPunk Labs )", + "description": "Parse GLTF2 Files", + "keywords": [ + "gltf", + "webgl", + "mesh" + ], + "license": "MIT", + "homepage": "https://github.com/sketchpunklabs/gltf2parser#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/sketchpunklabs/gltf2parser.git" + }, + "bugs": { + "url": "https://github.com/sketchpunklabs/gltf2parser/issues" + }, + "files": [ + "dist" + ], + "main": "./dist/gltf2parser.js", + "module": "./dist/gltf2parser.mjs", + "types": "./dist/index.d.ts", "exports": { ".": { - "import" : "./dist/gltf2parser.es.js", - "require" : "./dist/gltf2parser.cjs.js" + "import": "./dist/gltf2parser.es.js", + "require": "./dist/gltf2parser.cjs.js" } }, - - "scripts" : { - "dev" : "vite", - "build" : "vite build", - "build:types" : "tsc --declaration --noEmit false --emitDeclarationOnly --strict false --outDir ./dist", - "build:site" : "vite build --mode site", - "pack" : "npm pack", - "preview-site" : "vite preview", - "lint" : "eslint . --ext .ts" - }, - - "dependencies": { + "scripts": { + "dev": "vite", + "build": "vite build", + "build:types": "tsc --declaration --noEmit false --emitDeclarationOnly --strict false --outDir ./dist", + "build:site": "vite build --mode site", + "pack": "npm pack", + "preview-site": "vite preview", + "lint": "eslint . --ext .ts" }, - "devDependencies": { - "typescript" : "^4.6.2", - "vite" : "^2.8.6", - "three" : "^0.138.2", - - "eslint" : "^8.10.0", - "@typescript-eslint/eslint-plugin" : "^5.13.0", - "@typescript-eslint/parser" : "^5.13.0" + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.13.0", + "eslint": "^8.10.0", + "three": "^0.138.2", + "typescript": "^4.6.2", + "vite": "^2.8.6" } -} \ No newline at end of file +} From 87aaa4b2ac82591e52bf3991ab7811b949c4d3d7 Mon Sep 17 00:00:00 2001 From: Chad Furman Date: Sun, 25 Feb 2024 09:38:00 -0500 Subject: [PATCH 2/5] allowing named import of Gltf2Parser --- dist/index.d.ts | 2 +- src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/index.d.ts b/dist/index.d.ts index bc47de6..587f9f6 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -37,4 +37,4 @@ declare class Gltf2Parser { static fetch(url: string): Promise; } export default Gltf2Parser; -export { parseGLB, Accessor, Texture, Pose, Mesh, Primitive, Skin, SkinJoint, Animation, Track, ETransform, ELerp, }; +export { parseGLB, Accessor, Texture, Pose, Mesh, Primitive, Skin, SkinJoint, Animation, Track, ETransform, ELerp, Gltf2Parser }; diff --git a/src/index.ts b/src/index.ts index 049eca7..31caa06 100644 --- a/src/index.ts +++ b/src/index.ts @@ -635,5 +635,5 @@ export { Accessor, Texture, Pose, Mesh, Primitive, Skin, SkinJoint, - Animation, Track, ETransform, ELerp, + Animation, Track, ETransform, ELerp, Gltf2Parser }; \ No newline at end of file From 13d33ddcf46e6faad6adee94aa56884b8f0e8a31 Mon Sep 17 00:00:00 2001 From: Chad Furman Date: Sun, 25 Feb 2024 09:42:21 -0500 Subject: [PATCH 3/5] fixing export / import targets in package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7036495..9c654e4 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,8 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "import": "./dist/gltf2parser.es.js", - "require": "./dist/gltf2parser.cjs.js" + "import": "./dist/gltf2parser.js", + "require": "./dist/gltf2parser.mjs" } }, "scripts": { From e2cce99cb08f4b53632a8f5f15a04f12c5418b82 Mon Sep 17 00:00:00 2001 From: Chad Furman Date: Sun, 25 Feb 2024 10:32:43 -0500 Subject: [PATCH 4/5] sticking with es and cjs, removing default export --- dist/gltf2parser.js | 1043 ------------------------------------------ dist/gltf2parser.mjs | 1027 ----------------------------------------- dist/index.d.ts | 1 - 3 files changed, 2071 deletions(-) delete mode 100644 dist/gltf2parser.js delete mode 100644 dist/gltf2parser.mjs diff --git a/dist/gltf2parser.js b/dist/gltf2parser.js deleted file mode 100644 index 92f09c7..0000000 --- a/dist/gltf2parser.js +++ /dev/null @@ -1,1043 +0,0 @@ -'use strict'; - -Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } }); - -const GLB_MAGIC = 1179937895; -const GLB_JSON = 1313821514; -const GLB_BIN = 5130562; -const GLB_VER = 2; -const GLB_MAGIC_BIDX = 0; -const GLB_VERSION_BIDX = 4; -const GLB_JSON_TYPE_BIDX = 16; -const GLB_JSON_LEN_BIDX = 12; -const GLB_JSON_BIDX = 20; -async function parseGLB(res) { - const arybuf = await res.arrayBuffer(); - const dv = new DataView(arybuf); - if (dv.getUint32(GLB_MAGIC_BIDX, true) != GLB_MAGIC) { - console.error("GLB magic number does not match."); - return null; - } - if (dv.getUint32(GLB_VERSION_BIDX, true) != GLB_VER) { - console.error("Can only accept GLB of version 2."); - return null; - } - if (dv.getUint32(GLB_JSON_TYPE_BIDX, true) != GLB_JSON) { - console.error("GLB Chunk 0 is not the type: JSON "); - return null; - } - const json_len = dv.getUint32(GLB_JSON_LEN_BIDX, true); - const chk1_bidx = GLB_JSON_BIDX + json_len; - if (dv.getUint32(chk1_bidx + 4, true) != GLB_BIN) { - console.error("GLB Chunk 1 is not the type: BIN "); - return null; - } - const bin_len = dv.getUint32(chk1_bidx, true); - const bin_idx = chk1_bidx + 8; - const txt_decoder = new TextDecoder("utf8"); - const json_bytes = new Uint8Array(arybuf, GLB_JSON_BIDX, json_len); - const json_text = txt_decoder.decode(json_bytes); - const json = JSON.parse(json_text); - const bin = arybuf.slice(bin_idx); - if (bin.byteLength != bin_len) { - console.error("GLB Bin length does not match value in header."); - return null; - } - return [json, bin]; -} - -const ComponentTypeMap = { - 5120: [1, Int8Array, "int8", "BYTE"], - 5121: [1, Uint8Array, "uint8", "UNSIGNED_BYTE"], - 5122: [2, Int16Array, "int16", "SHORT"], - 5123: [2, Uint16Array, "uint16", "UNSIGNED_SHORT"], - 5125: [4, Uint32Array, "uint32", "UNSIGNED_INT"], - 5126: [4, Float32Array, "float", "FLOAT"] -}; -const ComponentVarMap = { - // Component Length of Each Var Type - SCALAR: 1, - VEC2: 2, - VEC3: 3, - VEC4: 4, - MAT2: 4, - MAT3: 9, - MAT4: 16 -}; - -class Accessor { - componentLen = 0; - elementCnt = 0; - byteOffset = 0; - byteSize = 0; - boundMin = null; - boundMax = null; - type = null; - data = null; - fromBin(accessor, bufView, bin) { - const [ - compByte, - // Type Byte Size - compType, - typeName - ] = ComponentTypeMap[accessor.componentType]; - if (!compType) { - console.error("Unknown Component Type for Accessor", accessor.componentType); - return this; - } - this.componentLen = ComponentVarMap[accessor.type]; - this.elementCnt = accessor.count; - this.byteOffset = (accessor.byteOffset || 0) + (bufView.byteOffset || 0); - this.byteSize = this.elementCnt * this.componentLen * compByte; - this.boundMin = accessor.min ? accessor.min.slice(0) : null; - this.boundMax = accessor.max ? accessor.max.slice(0) : null; - this.type = typeName; - if (bin) { - const size = this.elementCnt * this.componentLen; - this.data = new compType(bin, this.byteOffset, size); - } - return this; - } -} - -class Attrib { - byteOffset = 0; - componentLen = 0; - boundMin = null; - boundMax = null; - constructor(accID, json) { - const accessor = json.accessors[accID]; - this.componentLen = ComponentVarMap[accessor.type]; - this.byteOffset = accessor.byteOffset; - this.boundMin = accessor.min ? accessor.min.slice(0) : null; - this.boundMax = accessor.max ? accessor.max.slice(0) : null; - } -} -class InterleavedBuffer { - data = null; - elementCnt = 0; - componentLen = 0; - byteStride = 0; - byteSize = 0; - position = null; - normal = null; - tangent = null; - texcoord_0 = null; - texcoord_1 = null; - color_0 = null; - joints_0 = null; - weights_0 = null; - constructor(attr, json, bin) { - const accessor = json.accessors[attr.POSITION]; - const bView = json.bufferViews[accessor.bufferView]; - this.elementCnt = accessor.count; - this.byteStride = bView.byteStride; - this.byteSize = bView.byteLength; - this.componentLen = this.byteStride / 4; - this.position = new Attrib(attr.POSITION, json); - if (attr.NORMAL != void 0) - this.normal = new Attrib(attr.NORMAL, json); - if (attr.TANGENT != void 0) - this.tangent = new Attrib(attr.TANGENT, json); - if (attr.TEXCOORD_0 != void 0) - this.texcoord_0 = new Attrib(attr.TEXCOORD_0, json); - if (attr.TEXCOORD_1 != void 0) - this.texcoord_1 = new Attrib(attr.TEXCOORD_1, json); - if (attr.JOINTS_0 != void 0) - this.joints_0 = new Attrib(attr.JOINTS_0, json); - if (attr.WEIGHTS_0 != void 0) - this.weights_0 = new Attrib(attr.WEIGHTS_0, json); - if (attr.COLOR_0 != void 0) - this.color_0 = new Attrib(attr.COLOR_0, json); - if (bin) { - this.data = new Float32Array(bin, bView.byteOffset || 0, this.elementCnt * this.componentLen); - } - } -} - -class Draco { - // #region MAIN - mod; - decoder; - mesh; - faceCnt = 0; - vertCnt = 0; - constructor(mod) { - this.mod = mod; - this.decoder = new this.mod.Decoder(); - } - // #endregion - // #region METHODS - dispose() { - this.mod.destroy(this.decoder); - if (this.mesh) - this.mod.destroy(this.mesh); - this.mod = null; - } - // Decode BIN data into Draco Mesh Data - loadMesh(bin, offset, len) { - const slice = new Int8Array(bin, offset, len); - const buf = new this.mod.DecoderBuffer(); - buf.Init(slice, slice.byteLength); - this.mesh = new this.mod.Mesh(); - this.decoder.DecodeBufferToMesh(buf, this.mesh); - this.mod.destroy(buf); - this.faceCnt = this.mesh.num_faces(); - this.vertCnt = this.mesh.num_points(); - return this; - } - // #endregion - // #region HELPERS - // Load all the attributes of a primitive - loadPrimitive(prim, dAttr, gAttr, json) { - if (dAttr.POSITION != void 0) - prim.position = this.parseAttribute(dAttr.POSITION, gAttr.POSITION, json); - if (dAttr.NORMAL != void 0) - prim.normal = this.parseAttribute(dAttr.NORMAL, gAttr.NORMAL, json); - if (dAttr.TEXCOORD_0 != void 0) - prim.texcoord_0 = this.parseAttribute(dAttr.TEXCOORD_0, gAttr.TEXCOORD_0, json); - const tAry = new Uint32Array(this.faceCnt * 3); - const dAry = new this.mod.DracoUInt32Array(); - let ii; - for (let i = 0; i < this.faceCnt; i++) { - this.decoder.GetFaceFromMesh(this.mesh, i, dAry); - ii = i * 3; - tAry[ii + 0] = dAry.GetValue(0); - tAry[ii + 1] = dAry.GetValue(1); - tAry[ii + 2] = dAry.GetValue(2); - } - this.mod.destroy(dAry); - prim.indices = new Accessor(); - prim.indices.componentLen = 1; - prim.indices.elementCnt = this.faceCnt; - prim.indices.byteSize = tAry.byteLength; - prim.indices.data = tAry; - prim.indices.type = "UNSIGNED_INT"; - this.mod.destroy(this.mesh); - this.mesh = void 0; - this.faceCnt = 0; - this.vertCnt = 0; - } - // Parse Mesh Attribute - parseAttribute(dIdx, gIdx, json) { - const accessor = json.accessors[gIdx]; - const id = this.decoder.GetAttributeByUniqueId(this.mesh, dIdx); - const out = new Accessor(); - const compByte = ComponentTypeMap[accessor.componentType][0]; - const dType = ComponentTypeMap[accessor.componentType][3]; - out.componentLen = ComponentVarMap[accessor.type]; - out.elementCnt = accessor.count; - out.byteSize = out.elementCnt * out.componentLen * compByte; - out.boundMin = accessor.min ? accessor.min.slice(0) : null; - out.boundMax = accessor.max ? accessor.max.slice(0) : null; - out.type = ComponentTypeMap[accessor.componentType][2]; - out.data = this.decodeAttributeData(id, dType, out.componentLen * this.vertCnt); - return out; - } - // Decoding attribute data from DracoMesh - decodeAttributeData(id, type, len) { - let tAry; - let dAry; - switch (type) { - case "BYTE": - tAry = new Uint8Array(len); - dAry = new this.mod.DracoInt8Array(); - this.decoder.GetAttributeInt8ForAllPoints(this.mesh, id, dAry); - return dAry; - case "UNSIGNED_BYTE": - tAry = new Int16Array(len); - dAry = new this.mod.DracoUInt8Array(); - this.decoder.GetAttributeUInt8ForAllPoints(this.mesh, id, dAry); - break; - case "SHORT": - tAry = new Int16Array(len); - dAry = new this.mod.DracoInt16Array(); - this.decoder.GetAttributeInt16ForAllPoints(this.mesh, id, dAry); - break; - case "UNSIGNED_SHORT": - tAry = new Uint16Array(len); - dAry = new this.mod.DracoUInt16Array(); - this.decoder.GetAttributeUInt16ForAllPoints(this.mesh, id, dAry); - break; - case "UNSIGNED_INT": - tAry = new Uint32Array(len); - dAry = new this.mod.DracoUInt32Array(); - this.decoder.GetAttributeUInt32ForAllPoints(this.mesh, id, dAry); - break; - case "FLOAT": - tAry = new Float32Array(len); - dAry = new this.mod.DracoFloat32Array(); - this.decoder.GetAttributeFloatForAllPoints(this.mesh, id, dAry); - break; - } - for (let i = 0; i < len; i++) - tAry[i] = dAry.GetValue(i); - this.mod.destroy(dAry); - return tAry; - } - // #endregion -} - -class Mesh { - index = null; - // Index in Mesh Collection - name = null; - // Mesh Name - primitives = []; - // Mesh is made up of more then one Primative - position = null; - // Node's Position - rotation = null; - // Node's Rotation - scale = null; - // Node's Scale -} -class Primitive { - materialName = null; - materialIdx = null; - indices = null; - position = null; - normal = null; - tangent = null; - texcoord_0 = null; - texcoord_1 = null; - color_0 = null; - joints_0 = null; - weights_0 = null; - interleaved = null; -} - -class Skin { - index = null; - // Index in Mesh Collection - name = null; - // Skin Name - joints = []; - // Collection of Joints - // Sometimes Skin Objects will have their own transform in nodes - // Tends to come from FBX to GLTF conversion in blender. - position = null; - // Local Space Position - rotation = null; - // Local Space Rotation - scale = null; - // Local Space Scale -} -class SkinJoint { - name = null; - // Name of Joint - index = null; - // Joint Index - parentIndex = null; - // Parent Joint Index, Null if its a Root Joint - bindMatrix = null; - // Inverted WorldSpace Transform - position = null; - // Local Space Position - rotation = null; - // Local Space Rotation - scale = null; - // Local Space Scale -} - -const ETransform = { - Rot: 0, - Pos: 1, - Scl: 2 -}; -const ELerp = { - Step: 0, - Linear: 1, - Cubic: 2 -}; -class Track { - //#region ENUMS - static Transform = ETransform; - static Lerp = ELerp; - //#endregion - //#region MAIN - transform = ETransform.Pos; - interpolation = ELerp.Step; - jointIndex = 0; - timeStampIndex = 0; - keyframes; - static fromGltf(jointIdx, target, inter) { - const t = new Track(); - t.jointIndex = jointIdx; - switch (target) { - case "translation": - t.transform = ETransform.Pos; - break; - case "rotation": - t.transform = ETransform.Rot; - break; - case "scale": - t.transform = ETransform.Scl; - break; - } - switch (inter) { - case "LINEAR": - t.interpolation = ELerp.Linear; - break; - case "STEP": - t.interpolation = ELerp.Step; - break; - case "CUBICSPLINE": - t.interpolation = ELerp.Cubic; - break; - } - return t; - } - //#endregion -} -class Animation { - name = ""; - timestamps = []; - tracks = []; - constructor(name) { - if (name) - this.name = name; - } -} - -class Texture { - index = null; - // Index in Texture Collection - name = null; - // Texture Name - mime = null; - // Texture Mime - blob = null; - // Image Binary -} - -class PoseJoint { - //#region MAIN - index; - rot; - pos; - scl; - constructor(idx, rot, pos, scl) { - this.index = idx; - this.rot = rot; - this.pos = pos; - this.scl = scl; - } - //#endregion -} -class Pose { - name = ""; - joints = []; - constructor(name) { - if (name) - this.name = name; - } - add(idx, rot, pos, scl) { - this.joints.push(new PoseJoint(idx, rot, pos, scl)); - } -} - -function gamma(v) { - return v <= 31308e-7 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055; -} -function hex(r, g, b) { - return Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255); -} -function hexString(r, g, b) { - const rr = "0" + Math.round(r * 255).toString(16); - const gg = "0" + Math.round(g * 255).toString(16); - const bb = "0" + Math.round(b * 255).toString(16); - return ("#" + rr.slice(-2) + gg.slice(-2) + bb.slice(-2)).toUpperCase(); -} -class Material { - index = -1; - name = ""; - baseColor = [0, 0, 0, 1]; - metallic = 0; - roughness = 0; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(mat) { - this.name = mat.name || window.crypto.randomUUID(); - if (mat.pbrMetallicRoughness) { - if (mat.pbrMetallicRoughness.baseColorFactor) { - this.baseColor[0] = mat.pbrMetallicRoughness.baseColorFactor[0]; - this.baseColor[1] = mat.pbrMetallicRoughness.baseColorFactor[1]; - this.baseColor[2] = mat.pbrMetallicRoughness.baseColorFactor[2]; - this.baseColor[3] = mat.pbrMetallicRoughness.baseColorFactor[3]; - } - this.metallic = mat.pbrMetallicRoughness.metallicFactor || 0; - this.roughness = mat.pbrMetallicRoughness.roughnessFactor || 0; - } - } - get baseColorHex() { - return hex(this.baseColor[0], this.baseColor[1], this.baseColor[2]); - } - get baseColorGammaHex() { - return hex(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); - } - get baseColorString() { - return hexString(this.baseColor[0], this.baseColor[1], this.baseColor[2]); - } - get baseColorGammaString() { - return hexString(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); - } -} - -class Gltf2Parser { - // #region MAIN - json; - bin; - _needsDraco = false; - _extDraco = void 0; - constructor(json, bin) { - this.json = json; - this.bin = bin || new ArrayBuffer(0); - if (json.extensionsRequired) { - this._needsDraco = json.extensionsRequired.indexOf("KHR_draco_mesh_compression") !== -1; - } - } - get needsDraco() { - return this._needsDraco; - } - useDraco(mod) { - this._extDraco = new Draco(mod); - return this; - } - dispose() { - if (this._extDraco) - this._extDraco.dispose(); - } - // #endregion - // #region NODES - getNodeByName(n) { - let o, i; - for (i = 0; i < this.json.nodes.length; i++) { - o = this.json.nodes[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - // #endregion - // #region MESHES - getMeshNames() { - const json = this.json, rtn = []; - let i; - for (i of json.meshes) - rtn.push(i.name); - return rtn; - } - getMeshByName(n) { - let o, i; - for (i = 0; i < this.json.meshes.length; i++) { - o = this.json.meshes[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getMeshNodes(idx) { - const out = []; - let n; - for (n of this.json.nodes) { - if (n.mesh == idx) - out.push(n); - } - return out; - } - getMesh(id) { - if (!this.json.meshes) { - console.warn("No Meshes in GLTF File"); - return null; - } - const json = this.json; - let m = null; - let mIdx = null; - switch (typeof id) { - case "string": { - const tup = this.getMeshByName(id); - if (tup !== null) { - m = tup[0]; - mIdx = tup[1]; - } - break; - } - case "number": - if (id < json.meshes.length) { - m = json.meshes[id]; - mIdx = id; - } - break; - default: - m = json.meshes[0]; - mIdx = 0; - break; - } - if (m == null || mIdx == null) { - console.warn("No Mesh Found", id); - return null; - } - const mesh = new Mesh(); - mesh.name = m.name; - mesh.index = mIdx; - let p, prim, attr; - for (p of m.primitives) { - attr = p.attributes; - prim = new Primitive(); - if (p.material != void 0 && p.material != null) { - prim.materialIdx = p.material; - prim.materialName = json.materials[p.material].name; - } - if (this._needsDraco && p?.extensions?.KHR_draco_mesh_compression) { - if (this._extDraco) { - const draco = p.extensions.KHR_draco_mesh_compression; - const bufView = this.json.bufferViews[draco.bufferView]; - this._extDraco.loadMesh(this.bin, bufView.byteOffset, bufView.byteLength); - this._extDraco.loadPrimitive(prim, draco.attributes, attr, this.json); - } else { - console.error("Mesh is draco compressed but ext is not loaded."); - } - } else { - if (p.indices != void 0) - prim.indices = this.parseAccessor(p.indices); - if (attr.POSITION && this.isAccessorInterleaved(attr.POSITION)) { - prim.interleaved = new InterleavedBuffer(attr, this.json, this.bin); - } else { - if (attr.POSITION != void 0) - prim.position = this.parseAccessor(attr.POSITION); - if (attr.NORMAL != void 0) - prim.normal = this.parseAccessor(attr.NORMAL); - if (attr.TANGENT != void 0) - prim.tangent = this.parseAccessor(attr.TANGENT); - if (attr.TEXCOORD_0 != void 0) - prim.texcoord_0 = this.parseAccessor(attr.TEXCOORD_0); - if (attr.TEXCOORD_1 != void 0) - prim.texcoord_1 = this.parseAccessor(attr.TEXCOORD_1); - if (attr.JOINTS_0 != void 0) - prim.joints_0 = this.parseAccessor(attr.JOINTS_0); - if (attr.WEIGHTS_0 != void 0) - prim.weights_0 = this.parseAccessor(attr.WEIGHTS_0); - if (attr.COLOR_0 != void 0) - prim.color_0 = this.parseAccessor(attr.COLOR_0); - } - } - mesh.primitives.push(prim); - } - const nodes = this.getMeshNodes(mIdx); - if (nodes?.length) { - if (nodes[0].translation) - mesh.position = nodes[0].translation.slice(0); - if (nodes[0].rotation) - mesh.rotation = nodes[0].rotation.slice(0); - if (nodes[0].scale) - mesh.scale = nodes[0].scale.slice(0); - } - return mesh; - } - // #endregion - // #region SKINS - getSkinNames() { - const json = this.json, rtn = []; - let i; - for (i of json.skins) - rtn.push(i.name); - return rtn; - } - getSkinByName(n) { - let o, i; - for (i = 0; i < this.json.skins.length; i++) { - o = this.json.skins[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getSkin(id) { - if (!this.json.skins) { - console.warn("No Skins in GLTF File"); - return null; - } - const json = this.json; - let js = null; - let idx = null; - switch (typeof id) { - case "string": { - const tup = this.getSkinByName(id); - if (tup !== null) { - js = tup[0]; - idx = tup[1]; - } - break; - } - case "number": - if (id < json.skins.length) { - js = json.meshes[id]; - idx = id; - } - break; - default: - js = json.skins[0]; - idx = 0; - break; - } - if (js == null) { - console.warn("No Skin Found", id); - return null; - } - const bind = this.parseAccessor(js.inverseBindMatrices); - if (bind && bind.elementCnt != js.joints.length) { - console.warn("Strange Error. Joint Count & Bind Matrix Count dont match"); - return null; - } - let i, bi, ni, joint, node; - const jMap = /* @__PURE__ */ new Map(); - const skin = new Skin(); - skin.name = js.name; - skin.index = idx; - for (i = 0; i < js.joints.length; i++) { - ni = js.joints[i]; - node = json.nodes[ni]; - jMap.set(ni, i); - joint = new SkinJoint(); - joint.index = i; - joint.name = node.name ? node.name : "bone_" + i; - joint.rotation = node?.rotation?.slice(0) ?? null; - joint.position = node?.translation?.slice(0) ?? null; - joint.scale = node?.scale?.slice(0) ?? null; - if (bind && bind.data) { - bi = i * 16; - joint.bindMatrix = Array.from(bind.data.slice(bi, bi + 16)); - } - if (joint.scale) { - if (Math.abs(1 - joint.scale[0]) <= 1e-6) - joint.scale[0] = 1; - if (Math.abs(1 - joint.scale[1]) <= 1e-6) - joint.scale[1] = 1; - if (Math.abs(1 - joint.scale[2]) <= 1e-6) - joint.scale[2] = 1; - } - skin.joints.push(joint); - } - let j; - for (i = 0; i < js.joints.length; i++) { - ni = js.joints[i]; - node = json.nodes[ni]; - if (node?.children?.length) { - for (j = 0; j < node.children.length; j++) { - bi = jMap.get(node.children[j]); - if (bi != void 0) - skin.joints[bi].parentIndex = i; - else - console.log("BI", bi, node); - } - } - } - if (skin.name) { - const snode = this.getNodeByName(skin.name); - if (snode) { - const n = snode[0]; - skin.rotation = n?.rotation?.slice(0) ?? null; - skin.position = n?.translation?.slice(0) ?? null; - skin.scale = n?.scale?.slice(0) ?? null; - } - } - return skin; - } - // #endregion - // #region MATERIALS - // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_010_Materials.md - getMaterial(id) { - if (!this.json.materials) { - console.warn("No Materials in GLTF File"); - return null; - } - const json = this.json; - let idx = -1; - switch (typeof id) { - case "number": - if (id >= json.materials.length) { - console.error("Material index out of bounds", id); - break; - } - idx = id; - break; - case "string": - for (let i = 0; i < json.materials.length; i++) { - if (json.materials[i].name === id) { - idx = i; - break; - } - } - break; - default: - idx = 0; - break; - } - if (idx === -1) { - console.error("Material not found ", id); - return null; - } - const mat = new Material(json.materials[idx]); - mat.index = idx; - return mat; - } - getAllMaterials() { - const rtn = {}; - if (this.json.materials) { - let mat; - for (let i = 0; i < this.json.materials.length; i++) { - mat = new Material(this.json.materials[i]); - mat.index = i; - rtn[mat.name] = mat; - } - } - return rtn; - } - // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#texture-data - getTexture(id) { - const js = this.json; - const t = js.textures[id]; - const img = js.images[t.source]; - const bv = js.bufferViews[img.bufferView]; - const bAry = new Uint8Array(this.bin, bv.byteOffset, bv.byteLength); - const tex = new Texture(); - tex.index = id; - tex.name = img.name; - tex.mime = img.mimeType; - tex.blob = new Blob([bAry], { type: img.mimeType }); - return tex; - } - // #endregion - // #region ANIMATION - /* - https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation (Has math for cubic spline) - https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation - animation = { - frame_cnt : int - time : float, - times : [ float32array, float32array, etc ], - tracks : [ - { - type : "rot || pos || scl", - time_idx : 0, - joint_idx : 0, - lerp : "LINEAR || STEP || CUBICSPLINE", - data : float32array, - }, - ] - } - { - name: '', - channels: [ { - sampler: SAMPLER_INDEX, - target:{ - node : NODE_INDEX, ( NEED TO TRANSLATE NODE INDEX TO JOINT INDEX ) - path : 'translation' | 'rotation' | 'scale' - } - ] ], - samplers: [ { - input: ACCESSOR_INDEX_FOR_KEYFRAME_TIMESTAMP, - interpolation: 'LINEAR' | 'STEP' | 'CUBICSPLINE', - output: ACCESSOR_INDEX_FOR_KEYFRAME_TRANFORM_VALUE, - } ], - } - - */ - getAnimationNames() { - const json = this.json, rtn = []; - let i; - for (i of json.animations) - rtn.push(i.name); - return rtn; - } - getAnimationByName(n) { - let o, i; - for (i = 0; i < this.json.animations.length; i++) { - o = this.json.animations[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getAnimation(id) { - if (!this.json.animations) { - console.warn("No Animations in GLTF File"); - return null; - } - const json = this.json; - let js = null; - switch (typeof id) { - case "string": { - const tup = this.getAnimationByName(id); - if (tup !== null) - js = tup[0]; - break; - } - case "number": - if (id < json.animations.length) { - js = json.animations[id]; - } - break; - default: - js = json.animations[0]; - break; - } - if (js == null) { - console.warn("No Animation Found", id); - return null; - } - const NJMap = /* @__PURE__ */ new Map(); - const timeStamps = []; - const tsMap = /* @__PURE__ */ new Map(); - const fnGetJoint = (nIdx) => { - let jIdx = NJMap.get(nIdx); - if (jIdx != void 0) - return jIdx; - for (const skin of this.json.skins) { - jIdx = skin.joints.indexOf(nIdx); - if (jIdx != -1 && jIdx != void 0) { - NJMap.set(nIdx, jIdx); - return jIdx; - } - } - return -1; - }; - const fnGetTimestamp = (sIdx) => { - let aIdx = tsMap.get(sIdx); - if (aIdx != void 0) - return aIdx; - const acc2 = this.parseAccessor(sIdx); - if (acc2) { - aIdx = timeStamps.length; - timeStamps.push(acc2); - tsMap.set(sIdx, aIdx); - return aIdx; - } - return -1; - }; - const anim = new Animation(js.name); - anim.timestamps = timeStamps; - let track; - let ch; - let jointIdx; - let sampler; - let acc; - for (ch of js.channels) { - jointIdx = fnGetJoint(ch.target.node); - sampler = js.samplers[ch.sampler]; - track = Track.fromGltf(jointIdx, ch.target.path, sampler.interpolation); - acc = this.parseAccessor(sampler.output); - if (acc) - track.keyframes = acc; - track.timeStampIndex = fnGetTimestamp(sampler.input); - anim.tracks.push(track); - } - return anim; - } - // #endregion - // #region POSES ( CUSTOM, NOT PART OF GLTF SPEC ) - getPoseByName(n) { - let o, i; - for (i = 0; i < this.json.poses.length; i++) { - o = this.json.poses[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getPose(id) { - if (!this.json.poses) { - console.warn("No Poses in GLTF File"); - return null; - } - const json = this.json; - let js = null; - switch (typeof id) { - case "string": { - const tup = this.getPoseByName(id); - if (tup !== null) - js = tup[0]; - break; - } - default: - js = json.poses[0]; - break; - } - if (js == null) { - console.warn("No Pose Found", id); - return null; - } - const pose = new Pose(js.name); - let jnt; - for (jnt of js.joints) { - pose.add(jnt.idx, jnt.rot, jnt.pos, jnt.scl); - } - return pose; - } - // #endregion - // #region SUPPORT - // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md - parseAccessor(accID) { - const accessor = this.json.accessors[accID]; - const bufView = this.json.bufferViews[accessor.bufferView]; - console.log(accID, accessor, bufView); - if (bufView.byteStride) { - const compLen = ComponentVarMap[accessor.type]; - const byteSize = ComponentTypeMap[accessor.componentType][0]; - if (bufView.byteStride !== compLen * byteSize) { - console.error("UNSUPPORTED - Parsing Interleaved Buffer With Accessor Object"); - return null; - } - } - return new Accessor().fromBin(accessor, bufView, this.bin); - } - isAccessorInterleaved(accID) { - const accessor = this.json.accessors[accID]; - const bufView = this.json.bufferViews[accessor.bufferView]; - if (bufView.byteStride) { - const compLen = ComponentVarMap[accessor.type]; - const byteSize = ComponentTypeMap[accessor.componentType][0]; - return bufView.byteStride !== compLen * byteSize; - } - return false; - } - // #endregion - // #region STATIC - static async fetch(url) { - const res = await fetch(url); - if (!res.ok) - return null; - switch (url.slice(-4).toLocaleLowerCase()) { - case "gltf": { - let bin; - const json = await res.json(); - if (json.buffers && json.buffers.length > 0) { - const path = url.substring(0, url.lastIndexOf("/") + 1); - bin = await fetch(path + json.buffers[0].uri).then((r) => r.arrayBuffer()); - } - return new Gltf2Parser(json, bin); - } - case ".glb": { - const tuple = await parseGLB(res); - return tuple ? new Gltf2Parser(tuple[0], tuple[1]) : null; - } - } - return null; - } - // #endregion -} - -exports.Accessor = Accessor; -exports.Animation = Animation; -exports.ELerp = ELerp; -exports.ETransform = ETransform; -exports.Mesh = Mesh; -exports.Pose = Pose; -exports.Primitive = Primitive; -exports.Skin = Skin; -exports.SkinJoint = SkinJoint; -exports.Texture = Texture; -exports.Track = Track; -exports.default = Gltf2Parser; -exports.parseGLB = parseGLB; diff --git a/dist/gltf2parser.mjs b/dist/gltf2parser.mjs deleted file mode 100644 index df54d79..0000000 --- a/dist/gltf2parser.mjs +++ /dev/null @@ -1,1027 +0,0 @@ -const GLB_MAGIC = 1179937895; -const GLB_JSON = 1313821514; -const GLB_BIN = 5130562; -const GLB_VER = 2; -const GLB_MAGIC_BIDX = 0; -const GLB_VERSION_BIDX = 4; -const GLB_JSON_TYPE_BIDX = 16; -const GLB_JSON_LEN_BIDX = 12; -const GLB_JSON_BIDX = 20; -async function parseGLB(res) { - const arybuf = await res.arrayBuffer(); - const dv = new DataView(arybuf); - if (dv.getUint32(GLB_MAGIC_BIDX, true) != GLB_MAGIC) { - console.error("GLB magic number does not match."); - return null; - } - if (dv.getUint32(GLB_VERSION_BIDX, true) != GLB_VER) { - console.error("Can only accept GLB of version 2."); - return null; - } - if (dv.getUint32(GLB_JSON_TYPE_BIDX, true) != GLB_JSON) { - console.error("GLB Chunk 0 is not the type: JSON "); - return null; - } - const json_len = dv.getUint32(GLB_JSON_LEN_BIDX, true); - const chk1_bidx = GLB_JSON_BIDX + json_len; - if (dv.getUint32(chk1_bidx + 4, true) != GLB_BIN) { - console.error("GLB Chunk 1 is not the type: BIN "); - return null; - } - const bin_len = dv.getUint32(chk1_bidx, true); - const bin_idx = chk1_bidx + 8; - const txt_decoder = new TextDecoder("utf8"); - const json_bytes = new Uint8Array(arybuf, GLB_JSON_BIDX, json_len); - const json_text = txt_decoder.decode(json_bytes); - const json = JSON.parse(json_text); - const bin = arybuf.slice(bin_idx); - if (bin.byteLength != bin_len) { - console.error("GLB Bin length does not match value in header."); - return null; - } - return [json, bin]; -} - -const ComponentTypeMap = { - 5120: [1, Int8Array, "int8", "BYTE"], - 5121: [1, Uint8Array, "uint8", "UNSIGNED_BYTE"], - 5122: [2, Int16Array, "int16", "SHORT"], - 5123: [2, Uint16Array, "uint16", "UNSIGNED_SHORT"], - 5125: [4, Uint32Array, "uint32", "UNSIGNED_INT"], - 5126: [4, Float32Array, "float", "FLOAT"] -}; -const ComponentVarMap = { - // Component Length of Each Var Type - SCALAR: 1, - VEC2: 2, - VEC3: 3, - VEC4: 4, - MAT2: 4, - MAT3: 9, - MAT4: 16 -}; - -class Accessor { - componentLen = 0; - elementCnt = 0; - byteOffset = 0; - byteSize = 0; - boundMin = null; - boundMax = null; - type = null; - data = null; - fromBin(accessor, bufView, bin) { - const [ - compByte, - // Type Byte Size - compType, - typeName - ] = ComponentTypeMap[accessor.componentType]; - if (!compType) { - console.error("Unknown Component Type for Accessor", accessor.componentType); - return this; - } - this.componentLen = ComponentVarMap[accessor.type]; - this.elementCnt = accessor.count; - this.byteOffset = (accessor.byteOffset || 0) + (bufView.byteOffset || 0); - this.byteSize = this.elementCnt * this.componentLen * compByte; - this.boundMin = accessor.min ? accessor.min.slice(0) : null; - this.boundMax = accessor.max ? accessor.max.slice(0) : null; - this.type = typeName; - if (bin) { - const size = this.elementCnt * this.componentLen; - this.data = new compType(bin, this.byteOffset, size); - } - return this; - } -} - -class Attrib { - byteOffset = 0; - componentLen = 0; - boundMin = null; - boundMax = null; - constructor(accID, json) { - const accessor = json.accessors[accID]; - this.componentLen = ComponentVarMap[accessor.type]; - this.byteOffset = accessor.byteOffset; - this.boundMin = accessor.min ? accessor.min.slice(0) : null; - this.boundMax = accessor.max ? accessor.max.slice(0) : null; - } -} -class InterleavedBuffer { - data = null; - elementCnt = 0; - componentLen = 0; - byteStride = 0; - byteSize = 0; - position = null; - normal = null; - tangent = null; - texcoord_0 = null; - texcoord_1 = null; - color_0 = null; - joints_0 = null; - weights_0 = null; - constructor(attr, json, bin) { - const accessor = json.accessors[attr.POSITION]; - const bView = json.bufferViews[accessor.bufferView]; - this.elementCnt = accessor.count; - this.byteStride = bView.byteStride; - this.byteSize = bView.byteLength; - this.componentLen = this.byteStride / 4; - this.position = new Attrib(attr.POSITION, json); - if (attr.NORMAL != void 0) - this.normal = new Attrib(attr.NORMAL, json); - if (attr.TANGENT != void 0) - this.tangent = new Attrib(attr.TANGENT, json); - if (attr.TEXCOORD_0 != void 0) - this.texcoord_0 = new Attrib(attr.TEXCOORD_0, json); - if (attr.TEXCOORD_1 != void 0) - this.texcoord_1 = new Attrib(attr.TEXCOORD_1, json); - if (attr.JOINTS_0 != void 0) - this.joints_0 = new Attrib(attr.JOINTS_0, json); - if (attr.WEIGHTS_0 != void 0) - this.weights_0 = new Attrib(attr.WEIGHTS_0, json); - if (attr.COLOR_0 != void 0) - this.color_0 = new Attrib(attr.COLOR_0, json); - if (bin) { - this.data = new Float32Array(bin, bView.byteOffset || 0, this.elementCnt * this.componentLen); - } - } -} - -class Draco { - // #region MAIN - mod; - decoder; - mesh; - faceCnt = 0; - vertCnt = 0; - constructor(mod) { - this.mod = mod; - this.decoder = new this.mod.Decoder(); - } - // #endregion - // #region METHODS - dispose() { - this.mod.destroy(this.decoder); - if (this.mesh) - this.mod.destroy(this.mesh); - this.mod = null; - } - // Decode BIN data into Draco Mesh Data - loadMesh(bin, offset, len) { - const slice = new Int8Array(bin, offset, len); - const buf = new this.mod.DecoderBuffer(); - buf.Init(slice, slice.byteLength); - this.mesh = new this.mod.Mesh(); - this.decoder.DecodeBufferToMesh(buf, this.mesh); - this.mod.destroy(buf); - this.faceCnt = this.mesh.num_faces(); - this.vertCnt = this.mesh.num_points(); - return this; - } - // #endregion - // #region HELPERS - // Load all the attributes of a primitive - loadPrimitive(prim, dAttr, gAttr, json) { - if (dAttr.POSITION != void 0) - prim.position = this.parseAttribute(dAttr.POSITION, gAttr.POSITION, json); - if (dAttr.NORMAL != void 0) - prim.normal = this.parseAttribute(dAttr.NORMAL, gAttr.NORMAL, json); - if (dAttr.TEXCOORD_0 != void 0) - prim.texcoord_0 = this.parseAttribute(dAttr.TEXCOORD_0, gAttr.TEXCOORD_0, json); - const tAry = new Uint32Array(this.faceCnt * 3); - const dAry = new this.mod.DracoUInt32Array(); - let ii; - for (let i = 0; i < this.faceCnt; i++) { - this.decoder.GetFaceFromMesh(this.mesh, i, dAry); - ii = i * 3; - tAry[ii + 0] = dAry.GetValue(0); - tAry[ii + 1] = dAry.GetValue(1); - tAry[ii + 2] = dAry.GetValue(2); - } - this.mod.destroy(dAry); - prim.indices = new Accessor(); - prim.indices.componentLen = 1; - prim.indices.elementCnt = this.faceCnt; - prim.indices.byteSize = tAry.byteLength; - prim.indices.data = tAry; - prim.indices.type = "UNSIGNED_INT"; - this.mod.destroy(this.mesh); - this.mesh = void 0; - this.faceCnt = 0; - this.vertCnt = 0; - } - // Parse Mesh Attribute - parseAttribute(dIdx, gIdx, json) { - const accessor = json.accessors[gIdx]; - const id = this.decoder.GetAttributeByUniqueId(this.mesh, dIdx); - const out = new Accessor(); - const compByte = ComponentTypeMap[accessor.componentType][0]; - const dType = ComponentTypeMap[accessor.componentType][3]; - out.componentLen = ComponentVarMap[accessor.type]; - out.elementCnt = accessor.count; - out.byteSize = out.elementCnt * out.componentLen * compByte; - out.boundMin = accessor.min ? accessor.min.slice(0) : null; - out.boundMax = accessor.max ? accessor.max.slice(0) : null; - out.type = ComponentTypeMap[accessor.componentType][2]; - out.data = this.decodeAttributeData(id, dType, out.componentLen * this.vertCnt); - return out; - } - // Decoding attribute data from DracoMesh - decodeAttributeData(id, type, len) { - let tAry; - let dAry; - switch (type) { - case "BYTE": - tAry = new Uint8Array(len); - dAry = new this.mod.DracoInt8Array(); - this.decoder.GetAttributeInt8ForAllPoints(this.mesh, id, dAry); - return dAry; - case "UNSIGNED_BYTE": - tAry = new Int16Array(len); - dAry = new this.mod.DracoUInt8Array(); - this.decoder.GetAttributeUInt8ForAllPoints(this.mesh, id, dAry); - break; - case "SHORT": - tAry = new Int16Array(len); - dAry = new this.mod.DracoInt16Array(); - this.decoder.GetAttributeInt16ForAllPoints(this.mesh, id, dAry); - break; - case "UNSIGNED_SHORT": - tAry = new Uint16Array(len); - dAry = new this.mod.DracoUInt16Array(); - this.decoder.GetAttributeUInt16ForAllPoints(this.mesh, id, dAry); - break; - case "UNSIGNED_INT": - tAry = new Uint32Array(len); - dAry = new this.mod.DracoUInt32Array(); - this.decoder.GetAttributeUInt32ForAllPoints(this.mesh, id, dAry); - break; - case "FLOAT": - tAry = new Float32Array(len); - dAry = new this.mod.DracoFloat32Array(); - this.decoder.GetAttributeFloatForAllPoints(this.mesh, id, dAry); - break; - } - for (let i = 0; i < len; i++) - tAry[i] = dAry.GetValue(i); - this.mod.destroy(dAry); - return tAry; - } - // #endregion -} - -class Mesh { - index = null; - // Index in Mesh Collection - name = null; - // Mesh Name - primitives = []; - // Mesh is made up of more then one Primative - position = null; - // Node's Position - rotation = null; - // Node's Rotation - scale = null; - // Node's Scale -} -class Primitive { - materialName = null; - materialIdx = null; - indices = null; - position = null; - normal = null; - tangent = null; - texcoord_0 = null; - texcoord_1 = null; - color_0 = null; - joints_0 = null; - weights_0 = null; - interleaved = null; -} - -class Skin { - index = null; - // Index in Mesh Collection - name = null; - // Skin Name - joints = []; - // Collection of Joints - // Sometimes Skin Objects will have their own transform in nodes - // Tends to come from FBX to GLTF conversion in blender. - position = null; - // Local Space Position - rotation = null; - // Local Space Rotation - scale = null; - // Local Space Scale -} -class SkinJoint { - name = null; - // Name of Joint - index = null; - // Joint Index - parentIndex = null; - // Parent Joint Index, Null if its a Root Joint - bindMatrix = null; - // Inverted WorldSpace Transform - position = null; - // Local Space Position - rotation = null; - // Local Space Rotation - scale = null; - // Local Space Scale -} - -const ETransform = { - Rot: 0, - Pos: 1, - Scl: 2 -}; -const ELerp = { - Step: 0, - Linear: 1, - Cubic: 2 -}; -class Track { - //#region ENUMS - static Transform = ETransform; - static Lerp = ELerp; - //#endregion - //#region MAIN - transform = ETransform.Pos; - interpolation = ELerp.Step; - jointIndex = 0; - timeStampIndex = 0; - keyframes; - static fromGltf(jointIdx, target, inter) { - const t = new Track(); - t.jointIndex = jointIdx; - switch (target) { - case "translation": - t.transform = ETransform.Pos; - break; - case "rotation": - t.transform = ETransform.Rot; - break; - case "scale": - t.transform = ETransform.Scl; - break; - } - switch (inter) { - case "LINEAR": - t.interpolation = ELerp.Linear; - break; - case "STEP": - t.interpolation = ELerp.Step; - break; - case "CUBICSPLINE": - t.interpolation = ELerp.Cubic; - break; - } - return t; - } - //#endregion -} -class Animation { - name = ""; - timestamps = []; - tracks = []; - constructor(name) { - if (name) - this.name = name; - } -} - -class Texture { - index = null; - // Index in Texture Collection - name = null; - // Texture Name - mime = null; - // Texture Mime - blob = null; - // Image Binary -} - -class PoseJoint { - //#region MAIN - index; - rot; - pos; - scl; - constructor(idx, rot, pos, scl) { - this.index = idx; - this.rot = rot; - this.pos = pos; - this.scl = scl; - } - //#endregion -} -class Pose { - name = ""; - joints = []; - constructor(name) { - if (name) - this.name = name; - } - add(idx, rot, pos, scl) { - this.joints.push(new PoseJoint(idx, rot, pos, scl)); - } -} - -function gamma(v) { - return v <= 31308e-7 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055; -} -function hex(r, g, b) { - return Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255); -} -function hexString(r, g, b) { - const rr = "0" + Math.round(r * 255).toString(16); - const gg = "0" + Math.round(g * 255).toString(16); - const bb = "0" + Math.round(b * 255).toString(16); - return ("#" + rr.slice(-2) + gg.slice(-2) + bb.slice(-2)).toUpperCase(); -} -class Material { - index = -1; - name = ""; - baseColor = [0, 0, 0, 1]; - metallic = 0; - roughness = 0; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(mat) { - this.name = mat.name || window.crypto.randomUUID(); - if (mat.pbrMetallicRoughness) { - if (mat.pbrMetallicRoughness.baseColorFactor) { - this.baseColor[0] = mat.pbrMetallicRoughness.baseColorFactor[0]; - this.baseColor[1] = mat.pbrMetallicRoughness.baseColorFactor[1]; - this.baseColor[2] = mat.pbrMetallicRoughness.baseColorFactor[2]; - this.baseColor[3] = mat.pbrMetallicRoughness.baseColorFactor[3]; - } - this.metallic = mat.pbrMetallicRoughness.metallicFactor || 0; - this.roughness = mat.pbrMetallicRoughness.roughnessFactor || 0; - } - } - get baseColorHex() { - return hex(this.baseColor[0], this.baseColor[1], this.baseColor[2]); - } - get baseColorGammaHex() { - return hex(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); - } - get baseColorString() { - return hexString(this.baseColor[0], this.baseColor[1], this.baseColor[2]); - } - get baseColorGammaString() { - return hexString(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); - } -} - -class Gltf2Parser { - // #region MAIN - json; - bin; - _needsDraco = false; - _extDraco = void 0; - constructor(json, bin) { - this.json = json; - this.bin = bin || new ArrayBuffer(0); - if (json.extensionsRequired) { - this._needsDraco = json.extensionsRequired.indexOf("KHR_draco_mesh_compression") !== -1; - } - } - get needsDraco() { - return this._needsDraco; - } - useDraco(mod) { - this._extDraco = new Draco(mod); - return this; - } - dispose() { - if (this._extDraco) - this._extDraco.dispose(); - } - // #endregion - // #region NODES - getNodeByName(n) { - let o, i; - for (i = 0; i < this.json.nodes.length; i++) { - o = this.json.nodes[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - // #endregion - // #region MESHES - getMeshNames() { - const json = this.json, rtn = []; - let i; - for (i of json.meshes) - rtn.push(i.name); - return rtn; - } - getMeshByName(n) { - let o, i; - for (i = 0; i < this.json.meshes.length; i++) { - o = this.json.meshes[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getMeshNodes(idx) { - const out = []; - let n; - for (n of this.json.nodes) { - if (n.mesh == idx) - out.push(n); - } - return out; - } - getMesh(id) { - if (!this.json.meshes) { - console.warn("No Meshes in GLTF File"); - return null; - } - const json = this.json; - let m = null; - let mIdx = null; - switch (typeof id) { - case "string": { - const tup = this.getMeshByName(id); - if (tup !== null) { - m = tup[0]; - mIdx = tup[1]; - } - break; - } - case "number": - if (id < json.meshes.length) { - m = json.meshes[id]; - mIdx = id; - } - break; - default: - m = json.meshes[0]; - mIdx = 0; - break; - } - if (m == null || mIdx == null) { - console.warn("No Mesh Found", id); - return null; - } - const mesh = new Mesh(); - mesh.name = m.name; - mesh.index = mIdx; - let p, prim, attr; - for (p of m.primitives) { - attr = p.attributes; - prim = new Primitive(); - if (p.material != void 0 && p.material != null) { - prim.materialIdx = p.material; - prim.materialName = json.materials[p.material].name; - } - if (this._needsDraco && p?.extensions?.KHR_draco_mesh_compression) { - if (this._extDraco) { - const draco = p.extensions.KHR_draco_mesh_compression; - const bufView = this.json.bufferViews[draco.bufferView]; - this._extDraco.loadMesh(this.bin, bufView.byteOffset, bufView.byteLength); - this._extDraco.loadPrimitive(prim, draco.attributes, attr, this.json); - } else { - console.error("Mesh is draco compressed but ext is not loaded."); - } - } else { - if (p.indices != void 0) - prim.indices = this.parseAccessor(p.indices); - if (attr.POSITION && this.isAccessorInterleaved(attr.POSITION)) { - prim.interleaved = new InterleavedBuffer(attr, this.json, this.bin); - } else { - if (attr.POSITION != void 0) - prim.position = this.parseAccessor(attr.POSITION); - if (attr.NORMAL != void 0) - prim.normal = this.parseAccessor(attr.NORMAL); - if (attr.TANGENT != void 0) - prim.tangent = this.parseAccessor(attr.TANGENT); - if (attr.TEXCOORD_0 != void 0) - prim.texcoord_0 = this.parseAccessor(attr.TEXCOORD_0); - if (attr.TEXCOORD_1 != void 0) - prim.texcoord_1 = this.parseAccessor(attr.TEXCOORD_1); - if (attr.JOINTS_0 != void 0) - prim.joints_0 = this.parseAccessor(attr.JOINTS_0); - if (attr.WEIGHTS_0 != void 0) - prim.weights_0 = this.parseAccessor(attr.WEIGHTS_0); - if (attr.COLOR_0 != void 0) - prim.color_0 = this.parseAccessor(attr.COLOR_0); - } - } - mesh.primitives.push(prim); - } - const nodes = this.getMeshNodes(mIdx); - if (nodes?.length) { - if (nodes[0].translation) - mesh.position = nodes[0].translation.slice(0); - if (nodes[0].rotation) - mesh.rotation = nodes[0].rotation.slice(0); - if (nodes[0].scale) - mesh.scale = nodes[0].scale.slice(0); - } - return mesh; - } - // #endregion - // #region SKINS - getSkinNames() { - const json = this.json, rtn = []; - let i; - for (i of json.skins) - rtn.push(i.name); - return rtn; - } - getSkinByName(n) { - let o, i; - for (i = 0; i < this.json.skins.length; i++) { - o = this.json.skins[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getSkin(id) { - if (!this.json.skins) { - console.warn("No Skins in GLTF File"); - return null; - } - const json = this.json; - let js = null; - let idx = null; - switch (typeof id) { - case "string": { - const tup = this.getSkinByName(id); - if (tup !== null) { - js = tup[0]; - idx = tup[1]; - } - break; - } - case "number": - if (id < json.skins.length) { - js = json.meshes[id]; - idx = id; - } - break; - default: - js = json.skins[0]; - idx = 0; - break; - } - if (js == null) { - console.warn("No Skin Found", id); - return null; - } - const bind = this.parseAccessor(js.inverseBindMatrices); - if (bind && bind.elementCnt != js.joints.length) { - console.warn("Strange Error. Joint Count & Bind Matrix Count dont match"); - return null; - } - let i, bi, ni, joint, node; - const jMap = /* @__PURE__ */ new Map(); - const skin = new Skin(); - skin.name = js.name; - skin.index = idx; - for (i = 0; i < js.joints.length; i++) { - ni = js.joints[i]; - node = json.nodes[ni]; - jMap.set(ni, i); - joint = new SkinJoint(); - joint.index = i; - joint.name = node.name ? node.name : "bone_" + i; - joint.rotation = node?.rotation?.slice(0) ?? null; - joint.position = node?.translation?.slice(0) ?? null; - joint.scale = node?.scale?.slice(0) ?? null; - if (bind && bind.data) { - bi = i * 16; - joint.bindMatrix = Array.from(bind.data.slice(bi, bi + 16)); - } - if (joint.scale) { - if (Math.abs(1 - joint.scale[0]) <= 1e-6) - joint.scale[0] = 1; - if (Math.abs(1 - joint.scale[1]) <= 1e-6) - joint.scale[1] = 1; - if (Math.abs(1 - joint.scale[2]) <= 1e-6) - joint.scale[2] = 1; - } - skin.joints.push(joint); - } - let j; - for (i = 0; i < js.joints.length; i++) { - ni = js.joints[i]; - node = json.nodes[ni]; - if (node?.children?.length) { - for (j = 0; j < node.children.length; j++) { - bi = jMap.get(node.children[j]); - if (bi != void 0) - skin.joints[bi].parentIndex = i; - else - console.log("BI", bi, node); - } - } - } - if (skin.name) { - const snode = this.getNodeByName(skin.name); - if (snode) { - const n = snode[0]; - skin.rotation = n?.rotation?.slice(0) ?? null; - skin.position = n?.translation?.slice(0) ?? null; - skin.scale = n?.scale?.slice(0) ?? null; - } - } - return skin; - } - // #endregion - // #region MATERIALS - // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_010_Materials.md - getMaterial(id) { - if (!this.json.materials) { - console.warn("No Materials in GLTF File"); - return null; - } - const json = this.json; - let idx = -1; - switch (typeof id) { - case "number": - if (id >= json.materials.length) { - console.error("Material index out of bounds", id); - break; - } - idx = id; - break; - case "string": - for (let i = 0; i < json.materials.length; i++) { - if (json.materials[i].name === id) { - idx = i; - break; - } - } - break; - default: - idx = 0; - break; - } - if (idx === -1) { - console.error("Material not found ", id); - return null; - } - const mat = new Material(json.materials[idx]); - mat.index = idx; - return mat; - } - getAllMaterials() { - const rtn = {}; - if (this.json.materials) { - let mat; - for (let i = 0; i < this.json.materials.length; i++) { - mat = new Material(this.json.materials[i]); - mat.index = i; - rtn[mat.name] = mat; - } - } - return rtn; - } - // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#texture-data - getTexture(id) { - const js = this.json; - const t = js.textures[id]; - const img = js.images[t.source]; - const bv = js.bufferViews[img.bufferView]; - const bAry = new Uint8Array(this.bin, bv.byteOffset, bv.byteLength); - const tex = new Texture(); - tex.index = id; - tex.name = img.name; - tex.mime = img.mimeType; - tex.blob = new Blob([bAry], { type: img.mimeType }); - return tex; - } - // #endregion - // #region ANIMATION - /* - https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#appendix-c-spline-interpolation (Has math for cubic spline) - https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation - animation = { - frame_cnt : int - time : float, - times : [ float32array, float32array, etc ], - tracks : [ - { - type : "rot || pos || scl", - time_idx : 0, - joint_idx : 0, - lerp : "LINEAR || STEP || CUBICSPLINE", - data : float32array, - }, - ] - } - { - name: '', - channels: [ { - sampler: SAMPLER_INDEX, - target:{ - node : NODE_INDEX, ( NEED TO TRANSLATE NODE INDEX TO JOINT INDEX ) - path : 'translation' | 'rotation' | 'scale' - } - ] ], - samplers: [ { - input: ACCESSOR_INDEX_FOR_KEYFRAME_TIMESTAMP, - interpolation: 'LINEAR' | 'STEP' | 'CUBICSPLINE', - output: ACCESSOR_INDEX_FOR_KEYFRAME_TRANFORM_VALUE, - } ], - } - - */ - getAnimationNames() { - const json = this.json, rtn = []; - let i; - for (i of json.animations) - rtn.push(i.name); - return rtn; - } - getAnimationByName(n) { - let o, i; - for (i = 0; i < this.json.animations.length; i++) { - o = this.json.animations[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getAnimation(id) { - if (!this.json.animations) { - console.warn("No Animations in GLTF File"); - return null; - } - const json = this.json; - let js = null; - switch (typeof id) { - case "string": { - const tup = this.getAnimationByName(id); - if (tup !== null) - js = tup[0]; - break; - } - case "number": - if (id < json.animations.length) { - js = json.animations[id]; - } - break; - default: - js = json.animations[0]; - break; - } - if (js == null) { - console.warn("No Animation Found", id); - return null; - } - const NJMap = /* @__PURE__ */ new Map(); - const timeStamps = []; - const tsMap = /* @__PURE__ */ new Map(); - const fnGetJoint = (nIdx) => { - let jIdx = NJMap.get(nIdx); - if (jIdx != void 0) - return jIdx; - for (const skin of this.json.skins) { - jIdx = skin.joints.indexOf(nIdx); - if (jIdx != -1 && jIdx != void 0) { - NJMap.set(nIdx, jIdx); - return jIdx; - } - } - return -1; - }; - const fnGetTimestamp = (sIdx) => { - let aIdx = tsMap.get(sIdx); - if (aIdx != void 0) - return aIdx; - const acc2 = this.parseAccessor(sIdx); - if (acc2) { - aIdx = timeStamps.length; - timeStamps.push(acc2); - tsMap.set(sIdx, aIdx); - return aIdx; - } - return -1; - }; - const anim = new Animation(js.name); - anim.timestamps = timeStamps; - let track; - let ch; - let jointIdx; - let sampler; - let acc; - for (ch of js.channels) { - jointIdx = fnGetJoint(ch.target.node); - sampler = js.samplers[ch.sampler]; - track = Track.fromGltf(jointIdx, ch.target.path, sampler.interpolation); - acc = this.parseAccessor(sampler.output); - if (acc) - track.keyframes = acc; - track.timeStampIndex = fnGetTimestamp(sampler.input); - anim.tracks.push(track); - } - return anim; - } - // #endregion - // #region POSES ( CUSTOM, NOT PART OF GLTF SPEC ) - getPoseByName(n) { - let o, i; - for (i = 0; i < this.json.poses.length; i++) { - o = this.json.poses[i]; - if (o.name == n) - return [o, i]; - } - return null; - } - getPose(id) { - if (!this.json.poses) { - console.warn("No Poses in GLTF File"); - return null; - } - const json = this.json; - let js = null; - switch (typeof id) { - case "string": { - const tup = this.getPoseByName(id); - if (tup !== null) - js = tup[0]; - break; - } - default: - js = json.poses[0]; - break; - } - if (js == null) { - console.warn("No Pose Found", id); - return null; - } - const pose = new Pose(js.name); - let jnt; - for (jnt of js.joints) { - pose.add(jnt.idx, jnt.rot, jnt.pos, jnt.scl); - } - return pose; - } - // #endregion - // #region SUPPORT - // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md - parseAccessor(accID) { - const accessor = this.json.accessors[accID]; - const bufView = this.json.bufferViews[accessor.bufferView]; - console.log(accID, accessor, bufView); - if (bufView.byteStride) { - const compLen = ComponentVarMap[accessor.type]; - const byteSize = ComponentTypeMap[accessor.componentType][0]; - if (bufView.byteStride !== compLen * byteSize) { - console.error("UNSUPPORTED - Parsing Interleaved Buffer With Accessor Object"); - return null; - } - } - return new Accessor().fromBin(accessor, bufView, this.bin); - } - isAccessorInterleaved(accID) { - const accessor = this.json.accessors[accID]; - const bufView = this.json.bufferViews[accessor.bufferView]; - if (bufView.byteStride) { - const compLen = ComponentVarMap[accessor.type]; - const byteSize = ComponentTypeMap[accessor.componentType][0]; - return bufView.byteStride !== compLen * byteSize; - } - return false; - } - // #endregion - // #region STATIC - static async fetch(url) { - const res = await fetch(url); - if (!res.ok) - return null; - switch (url.slice(-4).toLocaleLowerCase()) { - case "gltf": { - let bin; - const json = await res.json(); - if (json.buffers && json.buffers.length > 0) { - const path = url.substring(0, url.lastIndexOf("/") + 1); - bin = await fetch(path + json.buffers[0].uri).then((r) => r.arrayBuffer()); - } - return new Gltf2Parser(json, bin); - } - case ".glb": { - const tuple = await parseGLB(res); - return tuple ? new Gltf2Parser(tuple[0], tuple[1]) : null; - } - } - return null; - } - // #endregion -} - -export { Accessor, Animation, ELerp, ETransform, Mesh, Pose, Primitive, Skin, SkinJoint, Texture, Track, Gltf2Parser as default, parseGLB }; diff --git a/dist/index.d.ts b/dist/index.d.ts index 587f9f6..a36e647 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -36,5 +36,4 @@ declare class Gltf2Parser { isAccessorInterleaved(accID: number): boolean; static fetch(url: string): Promise; } -export default Gltf2Parser; export { parseGLB, Accessor, Texture, Pose, Mesh, Primitive, Skin, SkinJoint, Animation, Track, ETransform, ELerp, Gltf2Parser }; From 1d86c40e50260d9d2fcb2b2d1ad8e0bb8e31c315 Mon Sep 17 00:00:00 2001 From: Chad Furman Date: Sun, 25 Feb 2024 10:33:30 -0500 Subject: [PATCH 5/5] sticking with es and cjs, removing default export --- dist/gltf2parser.cjs.js | 948 ++++++++++++++++++++++++++++++++++++++++ dist/gltf2parser.es.js | 932 +++++++++++++++++++++++++++++++++++++++ package.json | 7 +- src/index.ts | 3 +- 4 files changed, 1884 insertions(+), 6 deletions(-) create mode 100644 dist/gltf2parser.cjs.js create mode 100644 dist/gltf2parser.es.js diff --git a/dist/gltf2parser.cjs.js b/dist/gltf2parser.cjs.js new file mode 100644 index 0000000..76c6d42 --- /dev/null +++ b/dist/gltf2parser.cjs.js @@ -0,0 +1,948 @@ +'use strict'; + +Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } }); + +const GLB_MAGIC = 1179937895; +const GLB_JSON = 1313821514; +const GLB_BIN = 5130562; +const GLB_VER = 2; +const GLB_MAGIC_BIDX = 0; +const GLB_VERSION_BIDX = 4; +const GLB_JSON_TYPE_BIDX = 16; +const GLB_JSON_LEN_BIDX = 12; +const GLB_JSON_BIDX = 20; +async function parseGLB(res) { + const arybuf = await res.arrayBuffer(); + const dv = new DataView(arybuf); + if (dv.getUint32(GLB_MAGIC_BIDX, true) != GLB_MAGIC) { + console.error("GLB magic number does not match."); + return null; + } + if (dv.getUint32(GLB_VERSION_BIDX, true) != GLB_VER) { + console.error("Can only accept GLB of version 2."); + return null; + } + if (dv.getUint32(GLB_JSON_TYPE_BIDX, true) != GLB_JSON) { + console.error("GLB Chunk 0 is not the type: JSON "); + return null; + } + const json_len = dv.getUint32(GLB_JSON_LEN_BIDX, true); + const chk1_bidx = GLB_JSON_BIDX + json_len; + if (dv.getUint32(chk1_bidx + 4, true) != GLB_BIN) { + console.error("GLB Chunk 1 is not the type: BIN "); + return null; + } + const bin_len = dv.getUint32(chk1_bidx, true); + const bin_idx = chk1_bidx + 8; + const txt_decoder = new TextDecoder("utf8"); + const json_bytes = new Uint8Array(arybuf, GLB_JSON_BIDX, json_len); + const json_text = txt_decoder.decode(json_bytes); + const json = JSON.parse(json_text); + const bin = arybuf.slice(bin_idx); + if (bin.byteLength != bin_len) { + console.error("GLB Bin length does not match value in header."); + return null; + } + return [json, bin]; +} + +const ComponentTypeMap = { + 5120: [1, Int8Array, "int8", "BYTE"], + 5121: [1, Uint8Array, "uint8", "UNSIGNED_BYTE"], + 5122: [2, Int16Array, "int16", "SHORT"], + 5123: [2, Uint16Array, "uint16", "UNSIGNED_SHORT"], + 5125: [4, Uint32Array, "uint32", "UNSIGNED_INT"], + 5126: [4, Float32Array, "float", "FLOAT"] +}; +const ComponentVarMap = { + SCALAR: 1, + VEC2: 2, + VEC3: 3, + VEC4: 4, + MAT2: 4, + MAT3: 9, + MAT4: 16 +}; + +class Accessor { + constructor() { + this.componentLen = 0; + this.elementCnt = 0; + this.byteOffset = 0; + this.byteSize = 0; + this.boundMin = null; + this.boundMax = null; + this.type = null; + this.data = null; + } + fromBin(accessor, bufView, bin) { + const [ + compByte, + compType, + typeName + ] = ComponentTypeMap[accessor.componentType]; + if (!compType) { + console.error("Unknown Component Type for Accessor", accessor.componentType); + return this; + } + this.componentLen = ComponentVarMap[accessor.type]; + this.elementCnt = accessor.count; + this.byteOffset = (accessor.byteOffset || 0) + (bufView.byteOffset || 0); + this.byteSize = this.elementCnt * this.componentLen * compByte; + this.boundMin = accessor.min ? accessor.min.slice(0) : null; + this.boundMax = accessor.max ? accessor.max.slice(0) : null; + this.type = typeName; + if (bin) { + const size = this.elementCnt * this.componentLen; + this.data = new compType(bin, this.byteOffset, size); + } + return this; + } +} + +class Attrib { + constructor(accID, json) { + this.byteOffset = 0; + this.componentLen = 0; + this.boundMin = null; + this.boundMax = null; + const accessor = json.accessors[accID]; + this.componentLen = ComponentVarMap[accessor.type]; + this.byteOffset = accessor.byteOffset; + this.boundMin = accessor.min ? accessor.min.slice(0) : null; + this.boundMax = accessor.max ? accessor.max.slice(0) : null; + } +} +class InterleavedBuffer { + constructor(attr, json, bin) { + this.data = null; + this.elementCnt = 0; + this.componentLen = 0; + this.byteStride = 0; + this.byteSize = 0; + this.position = null; + this.normal = null; + this.tangent = null; + this.texcoord_0 = null; + this.texcoord_1 = null; + this.color_0 = null; + this.joints_0 = null; + this.weights_0 = null; + const accessor = json.accessors[attr.POSITION]; + const bView = json.bufferViews[accessor.bufferView]; + this.elementCnt = accessor.count; + this.byteStride = bView.byteStride; + this.byteSize = bView.byteLength; + this.componentLen = this.byteStride / 4; + this.position = new Attrib(attr.POSITION, json); + if (attr.NORMAL != void 0) + this.normal = new Attrib(attr.NORMAL, json); + if (attr.TANGENT != void 0) + this.tangent = new Attrib(attr.TANGENT, json); + if (attr.TEXCOORD_0 != void 0) + this.texcoord_0 = new Attrib(attr.TEXCOORD_0, json); + if (attr.TEXCOORD_1 != void 0) + this.texcoord_1 = new Attrib(attr.TEXCOORD_1, json); + if (attr.JOINTS_0 != void 0) + this.joints_0 = new Attrib(attr.JOINTS_0, json); + if (attr.WEIGHTS_0 != void 0) + this.weights_0 = new Attrib(attr.WEIGHTS_0, json); + if (attr.COLOR_0 != void 0) + this.color_0 = new Attrib(attr.COLOR_0, json); + if (bin) { + this.data = new Float32Array(bin, bView.byteOffset || 0, this.elementCnt * this.componentLen); + } + } +} + +class Draco { + constructor(mod) { + this.faceCnt = 0; + this.vertCnt = 0; + this.mod = mod; + this.decoder = new this.mod.Decoder(); + } + dispose() { + this.mod.destroy(this.decoder); + if (this.mesh) + this.mod.destroy(this.mesh); + this.mod = null; + } + loadMesh(bin, offset, len) { + const slice = new Int8Array(bin, offset, len); + const buf = new this.mod.DecoderBuffer(); + buf.Init(slice, slice.byteLength); + this.mesh = new this.mod.Mesh(); + this.decoder.DecodeBufferToMesh(buf, this.mesh); + this.mod.destroy(buf); + this.faceCnt = this.mesh.num_faces(); + this.vertCnt = this.mesh.num_points(); + return this; + } + loadPrimitive(prim, dAttr, gAttr, json) { + if (dAttr.POSITION != void 0) + prim.position = this.parseAttribute(dAttr.POSITION, gAttr.POSITION, json); + if (dAttr.NORMAL != void 0) + prim.normal = this.parseAttribute(dAttr.NORMAL, gAttr.NORMAL, json); + if (dAttr.TEXCOORD_0 != void 0) + prim.texcoord_0 = this.parseAttribute(dAttr.TEXCOORD_0, gAttr.TEXCOORD_0, json); + const tAry = new Uint32Array(this.faceCnt * 3); + const dAry = new this.mod.DracoUInt32Array(); + let ii; + for (let i = 0; i < this.faceCnt; i++) { + this.decoder.GetFaceFromMesh(this.mesh, i, dAry); + ii = i * 3; + tAry[ii + 0] = dAry.GetValue(0); + tAry[ii + 1] = dAry.GetValue(1); + tAry[ii + 2] = dAry.GetValue(2); + } + this.mod.destroy(dAry); + prim.indices = new Accessor(); + prim.indices.componentLen = 1; + prim.indices.elementCnt = this.faceCnt; + prim.indices.byteSize = tAry.byteLength; + prim.indices.data = tAry; + prim.indices.type = "UNSIGNED_INT"; + this.mod.destroy(this.mesh); + this.mesh = void 0; + this.faceCnt = 0; + this.vertCnt = 0; + } + parseAttribute(dIdx, gIdx, json) { + const accessor = json.accessors[gIdx]; + const id = this.decoder.GetAttributeByUniqueId(this.mesh, dIdx); + const out = new Accessor(); + const compByte = ComponentTypeMap[accessor.componentType][0]; + const dType = ComponentTypeMap[accessor.componentType][3]; + out.componentLen = ComponentVarMap[accessor.type]; + out.elementCnt = accessor.count; + out.byteSize = out.elementCnt * out.componentLen * compByte; + out.boundMin = accessor.min ? accessor.min.slice(0) : null; + out.boundMax = accessor.max ? accessor.max.slice(0) : null; + out.type = ComponentTypeMap[accessor.componentType][2]; + out.data = this.decodeAttributeData(id, dType, out.componentLen * this.vertCnt); + return out; + } + decodeAttributeData(id, type, len) { + let tAry; + let dAry; + switch (type) { + case "BYTE": + tAry = new Uint8Array(len); + dAry = new this.mod.DracoInt8Array(); + this.decoder.GetAttributeInt8ForAllPoints(this.mesh, id, dAry); + return dAry; + case "UNSIGNED_BYTE": + tAry = new Int16Array(len); + dAry = new this.mod.DracoUInt8Array(); + this.decoder.GetAttributeUInt8ForAllPoints(this.mesh, id, dAry); + break; + case "SHORT": + tAry = new Int16Array(len); + dAry = new this.mod.DracoInt16Array(); + this.decoder.GetAttributeInt16ForAllPoints(this.mesh, id, dAry); + break; + case "UNSIGNED_SHORT": + tAry = new Uint16Array(len); + dAry = new this.mod.DracoUInt16Array(); + this.decoder.GetAttributeUInt16ForAllPoints(this.mesh, id, dAry); + break; + case "UNSIGNED_INT": + tAry = new Uint32Array(len); + dAry = new this.mod.DracoUInt32Array(); + this.decoder.GetAttributeUInt32ForAllPoints(this.mesh, id, dAry); + break; + case "FLOAT": + tAry = new Float32Array(len); + dAry = new this.mod.DracoFloat32Array(); + this.decoder.GetAttributeFloatForAllPoints(this.mesh, id, dAry); + break; + } + for (let i = 0; i < len; i++) + tAry[i] = dAry.GetValue(i); + this.mod.destroy(dAry); + return tAry; + } +} + +class Mesh { + constructor() { + this.index = null; + this.name = null; + this.primitives = []; + this.position = null; + this.rotation = null; + this.scale = null; + } +} +class Primitive { + constructor() { + this.materialName = null; + this.materialIdx = null; + this.indices = null; + this.position = null; + this.normal = null; + this.tangent = null; + this.texcoord_0 = null; + this.texcoord_1 = null; + this.color_0 = null; + this.joints_0 = null; + this.weights_0 = null; + this.interleaved = null; + } +} + +class Skin { + constructor() { + this.index = null; + this.name = null; + this.joints = []; + this.position = null; + this.rotation = null; + this.scale = null; + } +} +class SkinJoint { + constructor() { + this.name = null; + this.index = null; + this.parentIndex = null; + this.bindMatrix = null; + this.position = null; + this.rotation = null; + this.scale = null; + } +} + +const ETransform = { + Rot: 0, + Pos: 1, + Scl: 2 +}; +const ELerp = { + Step: 0, + Linear: 1, + Cubic: 2 +}; +const _Track = class { + constructor() { + this.transform = ETransform.Pos; + this.interpolation = ELerp.Step; + this.jointIndex = 0; + this.timeStampIndex = 0; + } + static fromGltf(jointIdx, target, inter) { + const t = new _Track(); + t.jointIndex = jointIdx; + switch (target) { + case "translation": + t.transform = ETransform.Pos; + break; + case "rotation": + t.transform = ETransform.Rot; + break; + case "scale": + t.transform = ETransform.Scl; + break; + } + switch (inter) { + case "LINEAR": + t.interpolation = ELerp.Linear; + break; + case "STEP": + t.interpolation = ELerp.Step; + break; + case "CUBICSPLINE": + t.interpolation = ELerp.Cubic; + break; + } + return t; + } +}; +let Track = _Track; +Track.Transform = ETransform; +Track.Lerp = ELerp; +class Animation { + constructor(name) { + this.name = ""; + this.timestamps = []; + this.tracks = []; + if (name) + this.name = name; + } +} + +class Texture { + constructor() { + this.index = null; + this.name = null; + this.mime = null; + this.blob = null; + } +} + +class PoseJoint { + constructor(idx, rot, pos, scl) { + this.index = idx; + this.rot = rot; + this.pos = pos; + this.scl = scl; + } +} +class Pose { + constructor(name) { + this.name = ""; + this.joints = []; + if (name) + this.name = name; + } + add(idx, rot, pos, scl) { + this.joints.push(new PoseJoint(idx, rot, pos, scl)); + } +} + +function gamma(v) { + return v <= 31308e-7 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055; +} +function hex(r, g, b) { + return Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255); +} +function hexString(r, g, b) { + const rr = "0" + Math.round(r * 255).toString(16); + const gg = "0" + Math.round(g * 255).toString(16); + const bb = "0" + Math.round(b * 255).toString(16); + return ("#" + rr.slice(-2) + gg.slice(-2) + bb.slice(-2)).toUpperCase(); +} +class Material { + constructor(mat) { + this.index = -1; + this.name = ""; + this.baseColor = [0, 0, 0, 1]; + this.metallic = 0; + this.roughness = 0; + this.name = mat.name || window.crypto.randomUUID(); + if (mat.pbrMetallicRoughness) { + if (mat.pbrMetallicRoughness.baseColorFactor) { + this.baseColor[0] = mat.pbrMetallicRoughness.baseColorFactor[0]; + this.baseColor[1] = mat.pbrMetallicRoughness.baseColorFactor[1]; + this.baseColor[2] = mat.pbrMetallicRoughness.baseColorFactor[2]; + this.baseColor[3] = mat.pbrMetallicRoughness.baseColorFactor[3]; + } + this.metallic = mat.pbrMetallicRoughness.metallicFactor || 0; + this.roughness = mat.pbrMetallicRoughness.roughnessFactor || 0; + } + } + get baseColorHex() { + return hex(this.baseColor[0], this.baseColor[1], this.baseColor[2]); + } + get baseColorGammaHex() { + return hex(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); + } + get baseColorString() { + return hexString(this.baseColor[0], this.baseColor[1], this.baseColor[2]); + } + get baseColorGammaString() { + return hexString(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); + } +} + +class Gltf2Parser { + constructor(json, bin) { + this._needsDraco = false; + this._extDraco = void 0; + this.json = json; + this.bin = bin || new ArrayBuffer(0); + if (json.extensionsRequired) { + this._needsDraco = json.extensionsRequired.indexOf("KHR_draco_mesh_compression") !== -1; + } + } + get needsDraco() { + return this._needsDraco; + } + useDraco(mod) { + this._extDraco = new Draco(mod); + return this; + } + dispose() { + if (this._extDraco) + this._extDraco.dispose(); + } + getNodeByName(n) { + let o, i; + for (i = 0; i < this.json.nodes.length; i++) { + o = this.json.nodes[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getMeshNames() { + const json = this.json, rtn = []; + let i; + for (i of json.meshes) + rtn.push(i.name); + return rtn; + } + getMeshByName(n) { + let o, i; + for (i = 0; i < this.json.meshes.length; i++) { + o = this.json.meshes[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getMeshNodes(idx) { + const out = []; + let n; + for (n of this.json.nodes) { + if (n.mesh == idx) + out.push(n); + } + return out; + } + getMesh(id) { + if (!this.json.meshes) { + console.warn("No Meshes in GLTF File"); + return null; + } + const json = this.json; + let m = null; + let mIdx = null; + switch (typeof id) { + case "string": { + const tup = this.getMeshByName(id); + if (tup !== null) { + m = tup[0]; + mIdx = tup[1]; + } + break; + } + case "number": + if (id < json.meshes.length) { + m = json.meshes[id]; + mIdx = id; + } + break; + default: + m = json.meshes[0]; + mIdx = 0; + break; + } + if (m == null || mIdx == null) { + console.warn("No Mesh Found", id); + return null; + } + const mesh = new Mesh(); + mesh.name = m.name; + mesh.index = mIdx; + let p, prim, attr; + for (p of m.primitives) { + attr = p.attributes; + prim = new Primitive(); + if (p.material != void 0 && p.material != null) { + prim.materialIdx = p.material; + prim.materialName = json.materials[p.material].name; + } + if (this._needsDraco && p?.extensions?.KHR_draco_mesh_compression) { + if (this._extDraco) { + const draco = p.extensions.KHR_draco_mesh_compression; + const bufView = this.json.bufferViews[draco.bufferView]; + this._extDraco.loadMesh(this.bin, bufView.byteOffset, bufView.byteLength); + this._extDraco.loadPrimitive(prim, draco.attributes, attr, this.json); + } else { + console.error("Mesh is draco compressed but ext is not loaded."); + } + } else { + if (p.indices != void 0) + prim.indices = this.parseAccessor(p.indices); + if (attr.POSITION && this.isAccessorInterleaved(attr.POSITION)) { + prim.interleaved = new InterleavedBuffer(attr, this.json, this.bin); + } else { + if (attr.POSITION != void 0) + prim.position = this.parseAccessor(attr.POSITION); + if (attr.NORMAL != void 0) + prim.normal = this.parseAccessor(attr.NORMAL); + if (attr.TANGENT != void 0) + prim.tangent = this.parseAccessor(attr.TANGENT); + if (attr.TEXCOORD_0 != void 0) + prim.texcoord_0 = this.parseAccessor(attr.TEXCOORD_0); + if (attr.TEXCOORD_1 != void 0) + prim.texcoord_1 = this.parseAccessor(attr.TEXCOORD_1); + if (attr.JOINTS_0 != void 0) + prim.joints_0 = this.parseAccessor(attr.JOINTS_0); + if (attr.WEIGHTS_0 != void 0) + prim.weights_0 = this.parseAccessor(attr.WEIGHTS_0); + if (attr.COLOR_0 != void 0) + prim.color_0 = this.parseAccessor(attr.COLOR_0); + } + } + mesh.primitives.push(prim); + } + const nodes = this.getMeshNodes(mIdx); + if (nodes?.length) { + if (nodes[0].translation) + mesh.position = nodes[0].translation.slice(0); + if (nodes[0].rotation) + mesh.rotation = nodes[0].rotation.slice(0); + if (nodes[0].scale) + mesh.scale = nodes[0].scale.slice(0); + } + return mesh; + } + getSkinNames() { + const json = this.json, rtn = []; + let i; + for (i of json.skins) + rtn.push(i.name); + return rtn; + } + getSkinByName(n) { + let o, i; + for (i = 0; i < this.json.skins.length; i++) { + o = this.json.skins[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getSkin(id) { + if (!this.json.skins) { + console.warn("No Skins in GLTF File"); + return null; + } + const json = this.json; + let js = null; + let idx = null; + switch (typeof id) { + case "string": { + const tup = this.getSkinByName(id); + if (tup !== null) { + js = tup[0]; + idx = tup[1]; + } + break; + } + case "number": + if (id < json.skins.length) { + js = json.meshes[id]; + idx = id; + } + break; + default: + js = json.skins[0]; + idx = 0; + break; + } + if (js == null) { + console.warn("No Skin Found", id); + return null; + } + const bind = this.parseAccessor(js.inverseBindMatrices); + if (bind && bind.elementCnt != js.joints.length) { + console.warn("Strange Error. Joint Count & Bind Matrix Count dont match"); + return null; + } + let i, bi, ni, joint, node; + const jMap = /* @__PURE__ */ new Map(); + const skin = new Skin(); + skin.name = js.name; + skin.index = idx; + for (i = 0; i < js.joints.length; i++) { + ni = js.joints[i]; + node = json.nodes[ni]; + jMap.set(ni, i); + joint = new SkinJoint(); + joint.index = i; + joint.name = node.name ? node.name : "bone_" + i; + joint.rotation = node?.rotation?.slice(0) ?? null; + joint.position = node?.translation?.slice(0) ?? null; + joint.scale = node?.scale?.slice(0) ?? null; + if (bind && bind.data) { + bi = i * 16; + joint.bindMatrix = Array.from(bind.data.slice(bi, bi + 16)); + } + if (joint.scale) { + if (Math.abs(1 - joint.scale[0]) <= 1e-6) + joint.scale[0] = 1; + if (Math.abs(1 - joint.scale[1]) <= 1e-6) + joint.scale[1] = 1; + if (Math.abs(1 - joint.scale[2]) <= 1e-6) + joint.scale[2] = 1; + } + skin.joints.push(joint); + } + let j; + for (i = 0; i < js.joints.length; i++) { + ni = js.joints[i]; + node = json.nodes[ni]; + if (node?.children?.length) { + for (j = 0; j < node.children.length; j++) { + bi = jMap.get(node.children[j]); + if (bi != void 0) + skin.joints[bi].parentIndex = i; + else + console.log("BI", bi, node); + } + } + } + if (skin.name) { + const snode = this.getNodeByName(skin.name); + if (snode) { + const n = snode[0]; + skin.rotation = n?.rotation?.slice(0) ?? null; + skin.position = n?.translation?.slice(0) ?? null; + skin.scale = n?.scale?.slice(0) ?? null; + } + } + return skin; + } + getMaterial(id) { + if (!this.json.materials) { + console.warn("No Materials in GLTF File"); + return null; + } + const json = this.json; + let idx = -1; + switch (typeof id) { + case "number": + if (id >= json.materials.length) { + console.error("Material index out of bounds", id); + break; + } + idx = id; + break; + case "string": + for (let i = 0; i < json.materials.length; i++) { + if (json.materials[i].name === id) { + idx = i; + break; + } + } + break; + default: + idx = 0; + break; + } + if (idx === -1) { + console.error("Material not found ", id); + return null; + } + const mat = new Material(json.materials[idx]); + mat.index = idx; + return mat; + } + getAllMaterials() { + const rtn = {}; + if (this.json.materials) { + let mat; + for (let i = 0; i < this.json.materials.length; i++) { + mat = new Material(this.json.materials[i]); + mat.index = i; + rtn[mat.name] = mat; + } + } + return rtn; + } + getTexture(id) { + const js = this.json; + const t = js.textures[id]; + const img = js.images[t.source]; + const bv = js.bufferViews[img.bufferView]; + const bAry = new Uint8Array(this.bin, bv.byteOffset, bv.byteLength); + const tex = new Texture(); + tex.index = id; + tex.name = img.name; + tex.mime = img.mimeType; + tex.blob = new Blob([bAry], { type: img.mimeType }); + return tex; + } + getAnimationNames() { + const json = this.json, rtn = []; + let i; + for (i of json.animations) + rtn.push(i.name); + return rtn; + } + getAnimationByName(n) { + let o, i; + for (i = 0; i < this.json.animations.length; i++) { + o = this.json.animations[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getAnimation(id) { + if (!this.json.animations) { + console.warn("No Animations in GLTF File"); + return null; + } + const json = this.json; + let js = null; + switch (typeof id) { + case "string": { + const tup = this.getAnimationByName(id); + if (tup !== null) + js = tup[0]; + break; + } + case "number": + if (id < json.animations.length) { + js = json.animations[id]; + } + break; + default: + js = json.animations[0]; + break; + } + if (js == null) { + console.warn("No Animation Found", id); + return null; + } + const NJMap = /* @__PURE__ */ new Map(); + const timeStamps = []; + const tsMap = /* @__PURE__ */ new Map(); + const fnGetJoint = (nIdx) => { + let jIdx = NJMap.get(nIdx); + if (jIdx != void 0) + return jIdx; + for (const skin of this.json.skins) { + jIdx = skin.joints.indexOf(nIdx); + if (jIdx != -1 && jIdx != void 0) { + NJMap.set(nIdx, jIdx); + return jIdx; + } + } + return -1; + }; + const fnGetTimestamp = (sIdx) => { + let aIdx = tsMap.get(sIdx); + if (aIdx != void 0) + return aIdx; + const acc2 = this.parseAccessor(sIdx); + if (acc2) { + aIdx = timeStamps.length; + timeStamps.push(acc2); + tsMap.set(sIdx, aIdx); + return aIdx; + } + return -1; + }; + const anim = new Animation(js.name); + anim.timestamps = timeStamps; + let track; + let ch; + let jointIdx; + let sampler; + let acc; + for (ch of js.channels) { + jointIdx = fnGetJoint(ch.target.node); + sampler = js.samplers[ch.sampler]; + track = Track.fromGltf(jointIdx, ch.target.path, sampler.interpolation); + acc = this.parseAccessor(sampler.output); + if (acc) + track.keyframes = acc; + track.timeStampIndex = fnGetTimestamp(sampler.input); + anim.tracks.push(track); + } + return anim; + } + getPoseByName(n) { + let o, i; + for (i = 0; i < this.json.poses.length; i++) { + o = this.json.poses[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getPose(id) { + if (!this.json.poses) { + console.warn("No Poses in GLTF File"); + return null; + } + const json = this.json; + let js = null; + switch (typeof id) { + case "string": { + const tup = this.getPoseByName(id); + if (tup !== null) + js = tup[0]; + break; + } + default: + js = json.poses[0]; + break; + } + if (js == null) { + console.warn("No Pose Found", id); + return null; + } + const pose = new Pose(js.name); + let jnt; + for (jnt of js.joints) { + pose.add(jnt.idx, jnt.rot, jnt.pos, jnt.scl); + } + return pose; + } + parseAccessor(accID) { + const accessor = this.json.accessors[accID]; + const bufView = this.json.bufferViews[accessor.bufferView]; + console.log(accID, accessor, bufView); + if (bufView.byteStride) { + const compLen = ComponentVarMap[accessor.type]; + const byteSize = ComponentTypeMap[accessor.componentType][0]; + if (bufView.byteStride !== compLen * byteSize) { + console.error("UNSUPPORTED - Parsing Interleaved Buffer With Accessor Object"); + return null; + } + } + return new Accessor().fromBin(accessor, bufView, this.bin); + } + isAccessorInterleaved(accID) { + const accessor = this.json.accessors[accID]; + const bufView = this.json.bufferViews[accessor.bufferView]; + if (bufView.byteStride) { + const compLen = ComponentVarMap[accessor.type]; + const byteSize = ComponentTypeMap[accessor.componentType][0]; + return bufView.byteStride !== compLen * byteSize; + } + return false; + } + static async fetch(url) { + const res = await fetch(url); + if (!res.ok) + return null; + switch (url.slice(-4).toLocaleLowerCase()) { + case "gltf": { + let bin; + const json = await res.json(); + if (json.buffers && json.buffers.length > 0) { + const path = url.substring(0, url.lastIndexOf("/") + 1); + bin = await fetch(path + json.buffers[0].uri).then((r) => r.arrayBuffer()); + } + return new Gltf2Parser(json, bin); + } + case ".glb": { + const tuple = await parseGLB(res); + return tuple ? new Gltf2Parser(tuple[0], tuple[1]) : null; + } + } + return null; + } +} + +exports.Accessor = Accessor; +exports.Animation = Animation; +exports.ELerp = ELerp; +exports.ETransform = ETransform; +exports.Gltf2Parser = Gltf2Parser; +exports.Mesh = Mesh; +exports.Pose = Pose; +exports.Primitive = Primitive; +exports.Skin = Skin; +exports.SkinJoint = SkinJoint; +exports.Texture = Texture; +exports.Track = Track; +exports.parseGLB = parseGLB; diff --git a/dist/gltf2parser.es.js b/dist/gltf2parser.es.js new file mode 100644 index 0000000..6f6da13 --- /dev/null +++ b/dist/gltf2parser.es.js @@ -0,0 +1,932 @@ +const GLB_MAGIC = 1179937895; +const GLB_JSON = 1313821514; +const GLB_BIN = 5130562; +const GLB_VER = 2; +const GLB_MAGIC_BIDX = 0; +const GLB_VERSION_BIDX = 4; +const GLB_JSON_TYPE_BIDX = 16; +const GLB_JSON_LEN_BIDX = 12; +const GLB_JSON_BIDX = 20; +async function parseGLB(res) { + const arybuf = await res.arrayBuffer(); + const dv = new DataView(arybuf); + if (dv.getUint32(GLB_MAGIC_BIDX, true) != GLB_MAGIC) { + console.error("GLB magic number does not match."); + return null; + } + if (dv.getUint32(GLB_VERSION_BIDX, true) != GLB_VER) { + console.error("Can only accept GLB of version 2."); + return null; + } + if (dv.getUint32(GLB_JSON_TYPE_BIDX, true) != GLB_JSON) { + console.error("GLB Chunk 0 is not the type: JSON "); + return null; + } + const json_len = dv.getUint32(GLB_JSON_LEN_BIDX, true); + const chk1_bidx = GLB_JSON_BIDX + json_len; + if (dv.getUint32(chk1_bidx + 4, true) != GLB_BIN) { + console.error("GLB Chunk 1 is not the type: BIN "); + return null; + } + const bin_len = dv.getUint32(chk1_bidx, true); + const bin_idx = chk1_bidx + 8; + const txt_decoder = new TextDecoder("utf8"); + const json_bytes = new Uint8Array(arybuf, GLB_JSON_BIDX, json_len); + const json_text = txt_decoder.decode(json_bytes); + const json = JSON.parse(json_text); + const bin = arybuf.slice(bin_idx); + if (bin.byteLength != bin_len) { + console.error("GLB Bin length does not match value in header."); + return null; + } + return [json, bin]; +} + +const ComponentTypeMap = { + 5120: [1, Int8Array, "int8", "BYTE"], + 5121: [1, Uint8Array, "uint8", "UNSIGNED_BYTE"], + 5122: [2, Int16Array, "int16", "SHORT"], + 5123: [2, Uint16Array, "uint16", "UNSIGNED_SHORT"], + 5125: [4, Uint32Array, "uint32", "UNSIGNED_INT"], + 5126: [4, Float32Array, "float", "FLOAT"] +}; +const ComponentVarMap = { + SCALAR: 1, + VEC2: 2, + VEC3: 3, + VEC4: 4, + MAT2: 4, + MAT3: 9, + MAT4: 16 +}; + +class Accessor { + constructor() { + this.componentLen = 0; + this.elementCnt = 0; + this.byteOffset = 0; + this.byteSize = 0; + this.boundMin = null; + this.boundMax = null; + this.type = null; + this.data = null; + } + fromBin(accessor, bufView, bin) { + const [ + compByte, + compType, + typeName + ] = ComponentTypeMap[accessor.componentType]; + if (!compType) { + console.error("Unknown Component Type for Accessor", accessor.componentType); + return this; + } + this.componentLen = ComponentVarMap[accessor.type]; + this.elementCnt = accessor.count; + this.byteOffset = (accessor.byteOffset || 0) + (bufView.byteOffset || 0); + this.byteSize = this.elementCnt * this.componentLen * compByte; + this.boundMin = accessor.min ? accessor.min.slice(0) : null; + this.boundMax = accessor.max ? accessor.max.slice(0) : null; + this.type = typeName; + if (bin) { + const size = this.elementCnt * this.componentLen; + this.data = new compType(bin, this.byteOffset, size); + } + return this; + } +} + +class Attrib { + constructor(accID, json) { + this.byteOffset = 0; + this.componentLen = 0; + this.boundMin = null; + this.boundMax = null; + const accessor = json.accessors[accID]; + this.componentLen = ComponentVarMap[accessor.type]; + this.byteOffset = accessor.byteOffset; + this.boundMin = accessor.min ? accessor.min.slice(0) : null; + this.boundMax = accessor.max ? accessor.max.slice(0) : null; + } +} +class InterleavedBuffer { + constructor(attr, json, bin) { + this.data = null; + this.elementCnt = 0; + this.componentLen = 0; + this.byteStride = 0; + this.byteSize = 0; + this.position = null; + this.normal = null; + this.tangent = null; + this.texcoord_0 = null; + this.texcoord_1 = null; + this.color_0 = null; + this.joints_0 = null; + this.weights_0 = null; + const accessor = json.accessors[attr.POSITION]; + const bView = json.bufferViews[accessor.bufferView]; + this.elementCnt = accessor.count; + this.byteStride = bView.byteStride; + this.byteSize = bView.byteLength; + this.componentLen = this.byteStride / 4; + this.position = new Attrib(attr.POSITION, json); + if (attr.NORMAL != void 0) + this.normal = new Attrib(attr.NORMAL, json); + if (attr.TANGENT != void 0) + this.tangent = new Attrib(attr.TANGENT, json); + if (attr.TEXCOORD_0 != void 0) + this.texcoord_0 = new Attrib(attr.TEXCOORD_0, json); + if (attr.TEXCOORD_1 != void 0) + this.texcoord_1 = new Attrib(attr.TEXCOORD_1, json); + if (attr.JOINTS_0 != void 0) + this.joints_0 = new Attrib(attr.JOINTS_0, json); + if (attr.WEIGHTS_0 != void 0) + this.weights_0 = new Attrib(attr.WEIGHTS_0, json); + if (attr.COLOR_0 != void 0) + this.color_0 = new Attrib(attr.COLOR_0, json); + if (bin) { + this.data = new Float32Array(bin, bView.byteOffset || 0, this.elementCnt * this.componentLen); + } + } +} + +class Draco { + constructor(mod) { + this.faceCnt = 0; + this.vertCnt = 0; + this.mod = mod; + this.decoder = new this.mod.Decoder(); + } + dispose() { + this.mod.destroy(this.decoder); + if (this.mesh) + this.mod.destroy(this.mesh); + this.mod = null; + } + loadMesh(bin, offset, len) { + const slice = new Int8Array(bin, offset, len); + const buf = new this.mod.DecoderBuffer(); + buf.Init(slice, slice.byteLength); + this.mesh = new this.mod.Mesh(); + this.decoder.DecodeBufferToMesh(buf, this.mesh); + this.mod.destroy(buf); + this.faceCnt = this.mesh.num_faces(); + this.vertCnt = this.mesh.num_points(); + return this; + } + loadPrimitive(prim, dAttr, gAttr, json) { + if (dAttr.POSITION != void 0) + prim.position = this.parseAttribute(dAttr.POSITION, gAttr.POSITION, json); + if (dAttr.NORMAL != void 0) + prim.normal = this.parseAttribute(dAttr.NORMAL, gAttr.NORMAL, json); + if (dAttr.TEXCOORD_0 != void 0) + prim.texcoord_0 = this.parseAttribute(dAttr.TEXCOORD_0, gAttr.TEXCOORD_0, json); + const tAry = new Uint32Array(this.faceCnt * 3); + const dAry = new this.mod.DracoUInt32Array(); + let ii; + for (let i = 0; i < this.faceCnt; i++) { + this.decoder.GetFaceFromMesh(this.mesh, i, dAry); + ii = i * 3; + tAry[ii + 0] = dAry.GetValue(0); + tAry[ii + 1] = dAry.GetValue(1); + tAry[ii + 2] = dAry.GetValue(2); + } + this.mod.destroy(dAry); + prim.indices = new Accessor(); + prim.indices.componentLen = 1; + prim.indices.elementCnt = this.faceCnt; + prim.indices.byteSize = tAry.byteLength; + prim.indices.data = tAry; + prim.indices.type = "UNSIGNED_INT"; + this.mod.destroy(this.mesh); + this.mesh = void 0; + this.faceCnt = 0; + this.vertCnt = 0; + } + parseAttribute(dIdx, gIdx, json) { + const accessor = json.accessors[gIdx]; + const id = this.decoder.GetAttributeByUniqueId(this.mesh, dIdx); + const out = new Accessor(); + const compByte = ComponentTypeMap[accessor.componentType][0]; + const dType = ComponentTypeMap[accessor.componentType][3]; + out.componentLen = ComponentVarMap[accessor.type]; + out.elementCnt = accessor.count; + out.byteSize = out.elementCnt * out.componentLen * compByte; + out.boundMin = accessor.min ? accessor.min.slice(0) : null; + out.boundMax = accessor.max ? accessor.max.slice(0) : null; + out.type = ComponentTypeMap[accessor.componentType][2]; + out.data = this.decodeAttributeData(id, dType, out.componentLen * this.vertCnt); + return out; + } + decodeAttributeData(id, type, len) { + let tAry; + let dAry; + switch (type) { + case "BYTE": + tAry = new Uint8Array(len); + dAry = new this.mod.DracoInt8Array(); + this.decoder.GetAttributeInt8ForAllPoints(this.mesh, id, dAry); + return dAry; + case "UNSIGNED_BYTE": + tAry = new Int16Array(len); + dAry = new this.mod.DracoUInt8Array(); + this.decoder.GetAttributeUInt8ForAllPoints(this.mesh, id, dAry); + break; + case "SHORT": + tAry = new Int16Array(len); + dAry = new this.mod.DracoInt16Array(); + this.decoder.GetAttributeInt16ForAllPoints(this.mesh, id, dAry); + break; + case "UNSIGNED_SHORT": + tAry = new Uint16Array(len); + dAry = new this.mod.DracoUInt16Array(); + this.decoder.GetAttributeUInt16ForAllPoints(this.mesh, id, dAry); + break; + case "UNSIGNED_INT": + tAry = new Uint32Array(len); + dAry = new this.mod.DracoUInt32Array(); + this.decoder.GetAttributeUInt32ForAllPoints(this.mesh, id, dAry); + break; + case "FLOAT": + tAry = new Float32Array(len); + dAry = new this.mod.DracoFloat32Array(); + this.decoder.GetAttributeFloatForAllPoints(this.mesh, id, dAry); + break; + } + for (let i = 0; i < len; i++) + tAry[i] = dAry.GetValue(i); + this.mod.destroy(dAry); + return tAry; + } +} + +class Mesh { + constructor() { + this.index = null; + this.name = null; + this.primitives = []; + this.position = null; + this.rotation = null; + this.scale = null; + } +} +class Primitive { + constructor() { + this.materialName = null; + this.materialIdx = null; + this.indices = null; + this.position = null; + this.normal = null; + this.tangent = null; + this.texcoord_0 = null; + this.texcoord_1 = null; + this.color_0 = null; + this.joints_0 = null; + this.weights_0 = null; + this.interleaved = null; + } +} + +class Skin { + constructor() { + this.index = null; + this.name = null; + this.joints = []; + this.position = null; + this.rotation = null; + this.scale = null; + } +} +class SkinJoint { + constructor() { + this.name = null; + this.index = null; + this.parentIndex = null; + this.bindMatrix = null; + this.position = null; + this.rotation = null; + this.scale = null; + } +} + +const ETransform = { + Rot: 0, + Pos: 1, + Scl: 2 +}; +const ELerp = { + Step: 0, + Linear: 1, + Cubic: 2 +}; +const _Track = class { + constructor() { + this.transform = ETransform.Pos; + this.interpolation = ELerp.Step; + this.jointIndex = 0; + this.timeStampIndex = 0; + } + static fromGltf(jointIdx, target, inter) { + const t = new _Track(); + t.jointIndex = jointIdx; + switch (target) { + case "translation": + t.transform = ETransform.Pos; + break; + case "rotation": + t.transform = ETransform.Rot; + break; + case "scale": + t.transform = ETransform.Scl; + break; + } + switch (inter) { + case "LINEAR": + t.interpolation = ELerp.Linear; + break; + case "STEP": + t.interpolation = ELerp.Step; + break; + case "CUBICSPLINE": + t.interpolation = ELerp.Cubic; + break; + } + return t; + } +}; +let Track = _Track; +Track.Transform = ETransform; +Track.Lerp = ELerp; +class Animation { + constructor(name) { + this.name = ""; + this.timestamps = []; + this.tracks = []; + if (name) + this.name = name; + } +} + +class Texture { + constructor() { + this.index = null; + this.name = null; + this.mime = null; + this.blob = null; + } +} + +class PoseJoint { + constructor(idx, rot, pos, scl) { + this.index = idx; + this.rot = rot; + this.pos = pos; + this.scl = scl; + } +} +class Pose { + constructor(name) { + this.name = ""; + this.joints = []; + if (name) + this.name = name; + } + add(idx, rot, pos, scl) { + this.joints.push(new PoseJoint(idx, rot, pos, scl)); + } +} + +function gamma(v) { + return v <= 31308e-7 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055; +} +function hex(r, g, b) { + return Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255); +} +function hexString(r, g, b) { + const rr = "0" + Math.round(r * 255).toString(16); + const gg = "0" + Math.round(g * 255).toString(16); + const bb = "0" + Math.round(b * 255).toString(16); + return ("#" + rr.slice(-2) + gg.slice(-2) + bb.slice(-2)).toUpperCase(); +} +class Material { + constructor(mat) { + this.index = -1; + this.name = ""; + this.baseColor = [0, 0, 0, 1]; + this.metallic = 0; + this.roughness = 0; + this.name = mat.name || window.crypto.randomUUID(); + if (mat.pbrMetallicRoughness) { + if (mat.pbrMetallicRoughness.baseColorFactor) { + this.baseColor[0] = mat.pbrMetallicRoughness.baseColorFactor[0]; + this.baseColor[1] = mat.pbrMetallicRoughness.baseColorFactor[1]; + this.baseColor[2] = mat.pbrMetallicRoughness.baseColorFactor[2]; + this.baseColor[3] = mat.pbrMetallicRoughness.baseColorFactor[3]; + } + this.metallic = mat.pbrMetallicRoughness.metallicFactor || 0; + this.roughness = mat.pbrMetallicRoughness.roughnessFactor || 0; + } + } + get baseColorHex() { + return hex(this.baseColor[0], this.baseColor[1], this.baseColor[2]); + } + get baseColorGammaHex() { + return hex(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); + } + get baseColorString() { + return hexString(this.baseColor[0], this.baseColor[1], this.baseColor[2]); + } + get baseColorGammaString() { + return hexString(gamma(this.baseColor[0]), gamma(this.baseColor[1]), gamma(this.baseColor[2])); + } +} + +class Gltf2Parser { + constructor(json, bin) { + this._needsDraco = false; + this._extDraco = void 0; + this.json = json; + this.bin = bin || new ArrayBuffer(0); + if (json.extensionsRequired) { + this._needsDraco = json.extensionsRequired.indexOf("KHR_draco_mesh_compression") !== -1; + } + } + get needsDraco() { + return this._needsDraco; + } + useDraco(mod) { + this._extDraco = new Draco(mod); + return this; + } + dispose() { + if (this._extDraco) + this._extDraco.dispose(); + } + getNodeByName(n) { + let o, i; + for (i = 0; i < this.json.nodes.length; i++) { + o = this.json.nodes[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getMeshNames() { + const json = this.json, rtn = []; + let i; + for (i of json.meshes) + rtn.push(i.name); + return rtn; + } + getMeshByName(n) { + let o, i; + for (i = 0; i < this.json.meshes.length; i++) { + o = this.json.meshes[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getMeshNodes(idx) { + const out = []; + let n; + for (n of this.json.nodes) { + if (n.mesh == idx) + out.push(n); + } + return out; + } + getMesh(id) { + if (!this.json.meshes) { + console.warn("No Meshes in GLTF File"); + return null; + } + const json = this.json; + let m = null; + let mIdx = null; + switch (typeof id) { + case "string": { + const tup = this.getMeshByName(id); + if (tup !== null) { + m = tup[0]; + mIdx = tup[1]; + } + break; + } + case "number": + if (id < json.meshes.length) { + m = json.meshes[id]; + mIdx = id; + } + break; + default: + m = json.meshes[0]; + mIdx = 0; + break; + } + if (m == null || mIdx == null) { + console.warn("No Mesh Found", id); + return null; + } + const mesh = new Mesh(); + mesh.name = m.name; + mesh.index = mIdx; + let p, prim, attr; + for (p of m.primitives) { + attr = p.attributes; + prim = new Primitive(); + if (p.material != void 0 && p.material != null) { + prim.materialIdx = p.material; + prim.materialName = json.materials[p.material].name; + } + if (this._needsDraco && p?.extensions?.KHR_draco_mesh_compression) { + if (this._extDraco) { + const draco = p.extensions.KHR_draco_mesh_compression; + const bufView = this.json.bufferViews[draco.bufferView]; + this._extDraco.loadMesh(this.bin, bufView.byteOffset, bufView.byteLength); + this._extDraco.loadPrimitive(prim, draco.attributes, attr, this.json); + } else { + console.error("Mesh is draco compressed but ext is not loaded."); + } + } else { + if (p.indices != void 0) + prim.indices = this.parseAccessor(p.indices); + if (attr.POSITION && this.isAccessorInterleaved(attr.POSITION)) { + prim.interleaved = new InterleavedBuffer(attr, this.json, this.bin); + } else { + if (attr.POSITION != void 0) + prim.position = this.parseAccessor(attr.POSITION); + if (attr.NORMAL != void 0) + prim.normal = this.parseAccessor(attr.NORMAL); + if (attr.TANGENT != void 0) + prim.tangent = this.parseAccessor(attr.TANGENT); + if (attr.TEXCOORD_0 != void 0) + prim.texcoord_0 = this.parseAccessor(attr.TEXCOORD_0); + if (attr.TEXCOORD_1 != void 0) + prim.texcoord_1 = this.parseAccessor(attr.TEXCOORD_1); + if (attr.JOINTS_0 != void 0) + prim.joints_0 = this.parseAccessor(attr.JOINTS_0); + if (attr.WEIGHTS_0 != void 0) + prim.weights_0 = this.parseAccessor(attr.WEIGHTS_0); + if (attr.COLOR_0 != void 0) + prim.color_0 = this.parseAccessor(attr.COLOR_0); + } + } + mesh.primitives.push(prim); + } + const nodes = this.getMeshNodes(mIdx); + if (nodes?.length) { + if (nodes[0].translation) + mesh.position = nodes[0].translation.slice(0); + if (nodes[0].rotation) + mesh.rotation = nodes[0].rotation.slice(0); + if (nodes[0].scale) + mesh.scale = nodes[0].scale.slice(0); + } + return mesh; + } + getSkinNames() { + const json = this.json, rtn = []; + let i; + for (i of json.skins) + rtn.push(i.name); + return rtn; + } + getSkinByName(n) { + let o, i; + for (i = 0; i < this.json.skins.length; i++) { + o = this.json.skins[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getSkin(id) { + if (!this.json.skins) { + console.warn("No Skins in GLTF File"); + return null; + } + const json = this.json; + let js = null; + let idx = null; + switch (typeof id) { + case "string": { + const tup = this.getSkinByName(id); + if (tup !== null) { + js = tup[0]; + idx = tup[1]; + } + break; + } + case "number": + if (id < json.skins.length) { + js = json.meshes[id]; + idx = id; + } + break; + default: + js = json.skins[0]; + idx = 0; + break; + } + if (js == null) { + console.warn("No Skin Found", id); + return null; + } + const bind = this.parseAccessor(js.inverseBindMatrices); + if (bind && bind.elementCnt != js.joints.length) { + console.warn("Strange Error. Joint Count & Bind Matrix Count dont match"); + return null; + } + let i, bi, ni, joint, node; + const jMap = /* @__PURE__ */ new Map(); + const skin = new Skin(); + skin.name = js.name; + skin.index = idx; + for (i = 0; i < js.joints.length; i++) { + ni = js.joints[i]; + node = json.nodes[ni]; + jMap.set(ni, i); + joint = new SkinJoint(); + joint.index = i; + joint.name = node.name ? node.name : "bone_" + i; + joint.rotation = node?.rotation?.slice(0) ?? null; + joint.position = node?.translation?.slice(0) ?? null; + joint.scale = node?.scale?.slice(0) ?? null; + if (bind && bind.data) { + bi = i * 16; + joint.bindMatrix = Array.from(bind.data.slice(bi, bi + 16)); + } + if (joint.scale) { + if (Math.abs(1 - joint.scale[0]) <= 1e-6) + joint.scale[0] = 1; + if (Math.abs(1 - joint.scale[1]) <= 1e-6) + joint.scale[1] = 1; + if (Math.abs(1 - joint.scale[2]) <= 1e-6) + joint.scale[2] = 1; + } + skin.joints.push(joint); + } + let j; + for (i = 0; i < js.joints.length; i++) { + ni = js.joints[i]; + node = json.nodes[ni]; + if (node?.children?.length) { + for (j = 0; j < node.children.length; j++) { + bi = jMap.get(node.children[j]); + if (bi != void 0) + skin.joints[bi].parentIndex = i; + else + console.log("BI", bi, node); + } + } + } + if (skin.name) { + const snode = this.getNodeByName(skin.name); + if (snode) { + const n = snode[0]; + skin.rotation = n?.rotation?.slice(0) ?? null; + skin.position = n?.translation?.slice(0) ?? null; + skin.scale = n?.scale?.slice(0) ?? null; + } + } + return skin; + } + getMaterial(id) { + if (!this.json.materials) { + console.warn("No Materials in GLTF File"); + return null; + } + const json = this.json; + let idx = -1; + switch (typeof id) { + case "number": + if (id >= json.materials.length) { + console.error("Material index out of bounds", id); + break; + } + idx = id; + break; + case "string": + for (let i = 0; i < json.materials.length; i++) { + if (json.materials[i].name === id) { + idx = i; + break; + } + } + break; + default: + idx = 0; + break; + } + if (idx === -1) { + console.error("Material not found ", id); + return null; + } + const mat = new Material(json.materials[idx]); + mat.index = idx; + return mat; + } + getAllMaterials() { + const rtn = {}; + if (this.json.materials) { + let mat; + for (let i = 0; i < this.json.materials.length; i++) { + mat = new Material(this.json.materials[i]); + mat.index = i; + rtn[mat.name] = mat; + } + } + return rtn; + } + getTexture(id) { + const js = this.json; + const t = js.textures[id]; + const img = js.images[t.source]; + const bv = js.bufferViews[img.bufferView]; + const bAry = new Uint8Array(this.bin, bv.byteOffset, bv.byteLength); + const tex = new Texture(); + tex.index = id; + tex.name = img.name; + tex.mime = img.mimeType; + tex.blob = new Blob([bAry], { type: img.mimeType }); + return tex; + } + getAnimationNames() { + const json = this.json, rtn = []; + let i; + for (i of json.animations) + rtn.push(i.name); + return rtn; + } + getAnimationByName(n) { + let o, i; + for (i = 0; i < this.json.animations.length; i++) { + o = this.json.animations[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getAnimation(id) { + if (!this.json.animations) { + console.warn("No Animations in GLTF File"); + return null; + } + const json = this.json; + let js = null; + switch (typeof id) { + case "string": { + const tup = this.getAnimationByName(id); + if (tup !== null) + js = tup[0]; + break; + } + case "number": + if (id < json.animations.length) { + js = json.animations[id]; + } + break; + default: + js = json.animations[0]; + break; + } + if (js == null) { + console.warn("No Animation Found", id); + return null; + } + const NJMap = /* @__PURE__ */ new Map(); + const timeStamps = []; + const tsMap = /* @__PURE__ */ new Map(); + const fnGetJoint = (nIdx) => { + let jIdx = NJMap.get(nIdx); + if (jIdx != void 0) + return jIdx; + for (const skin of this.json.skins) { + jIdx = skin.joints.indexOf(nIdx); + if (jIdx != -1 && jIdx != void 0) { + NJMap.set(nIdx, jIdx); + return jIdx; + } + } + return -1; + }; + const fnGetTimestamp = (sIdx) => { + let aIdx = tsMap.get(sIdx); + if (aIdx != void 0) + return aIdx; + const acc2 = this.parseAccessor(sIdx); + if (acc2) { + aIdx = timeStamps.length; + timeStamps.push(acc2); + tsMap.set(sIdx, aIdx); + return aIdx; + } + return -1; + }; + const anim = new Animation(js.name); + anim.timestamps = timeStamps; + let track; + let ch; + let jointIdx; + let sampler; + let acc; + for (ch of js.channels) { + jointIdx = fnGetJoint(ch.target.node); + sampler = js.samplers[ch.sampler]; + track = Track.fromGltf(jointIdx, ch.target.path, sampler.interpolation); + acc = this.parseAccessor(sampler.output); + if (acc) + track.keyframes = acc; + track.timeStampIndex = fnGetTimestamp(sampler.input); + anim.tracks.push(track); + } + return anim; + } + getPoseByName(n) { + let o, i; + for (i = 0; i < this.json.poses.length; i++) { + o = this.json.poses[i]; + if (o.name == n) + return [o, i]; + } + return null; + } + getPose(id) { + if (!this.json.poses) { + console.warn("No Poses in GLTF File"); + return null; + } + const json = this.json; + let js = null; + switch (typeof id) { + case "string": { + const tup = this.getPoseByName(id); + if (tup !== null) + js = tup[0]; + break; + } + default: + js = json.poses[0]; + break; + } + if (js == null) { + console.warn("No Pose Found", id); + return null; + } + const pose = new Pose(js.name); + let jnt; + for (jnt of js.joints) { + pose.add(jnt.idx, jnt.rot, jnt.pos, jnt.scl); + } + return pose; + } + parseAccessor(accID) { + const accessor = this.json.accessors[accID]; + const bufView = this.json.bufferViews[accessor.bufferView]; + console.log(accID, accessor, bufView); + if (bufView.byteStride) { + const compLen = ComponentVarMap[accessor.type]; + const byteSize = ComponentTypeMap[accessor.componentType][0]; + if (bufView.byteStride !== compLen * byteSize) { + console.error("UNSUPPORTED - Parsing Interleaved Buffer With Accessor Object"); + return null; + } + } + return new Accessor().fromBin(accessor, bufView, this.bin); + } + isAccessorInterleaved(accID) { + const accessor = this.json.accessors[accID]; + const bufView = this.json.bufferViews[accessor.bufferView]; + if (bufView.byteStride) { + const compLen = ComponentVarMap[accessor.type]; + const byteSize = ComponentTypeMap[accessor.componentType][0]; + return bufView.byteStride !== compLen * byteSize; + } + return false; + } + static async fetch(url) { + const res = await fetch(url); + if (!res.ok) + return null; + switch (url.slice(-4).toLocaleLowerCase()) { + case "gltf": { + let bin; + const json = await res.json(); + if (json.buffers && json.buffers.length > 0) { + const path = url.substring(0, url.lastIndexOf("/") + 1); + bin = await fetch(path + json.buffers[0].uri).then((r) => r.arrayBuffer()); + } + return new Gltf2Parser(json, bin); + } + case ".glb": { + const tuple = await parseGLB(res); + return tuple ? new Gltf2Parser(tuple[0], tuple[1]) : null; + } + } + return null; + } +} + +export { Accessor, Animation, ELerp, ETransform, Gltf2Parser, Mesh, Pose, Primitive, Skin, SkinJoint, Texture, Track, parseGLB }; diff --git a/package.json b/package.json index 9c654e4..606b3e1 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,12 @@ "files": [ "dist" ], - "main": "./dist/gltf2parser.js", - "module": "./dist/gltf2parser.mjs", + "module": "./dist/gltf2parser.es.js", "types": "./dist/index.d.ts", "exports": { ".": { - "import": "./dist/gltf2parser.js", - "require": "./dist/gltf2parser.mjs" + "import": "./dist/gltf2parser.es.js", + "require": "./dist/gltf2parser.cjs.js" } }, "scripts": { diff --git a/src/index.ts b/src/index.ts index 31caa06..5179ec0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -629,8 +629,7 @@ class Gltf2Parser{ // #endregion } -export default Gltf2Parser; -export { +export { parseGLB, Accessor, Texture, Pose, Mesh, Primitive,