three-m2loader
Advanced tools
Comparing version 2.0.1 to 2.1.0
704
M2Loader.js
@@ -7,2 +7,3 @@ import { | ||
CompressedTexture, | ||
DataTexture, | ||
DoubleSide, | ||
@@ -13,2 +14,7 @@ FileLoader, | ||
Group, | ||
InterpolateLinear, | ||
InterpolateSmooth, | ||
InterpolateDiscrete, | ||
LinearFilter, | ||
LinearMipmapLinearFilter, | ||
Loader, | ||
@@ -21,2 +27,3 @@ LoaderUtils, | ||
Quaternion, | ||
QuaternionKeyframeTrack, | ||
RepeatWrapping, | ||
@@ -28,9 +35,9 @@ RGBA_S3TC_DXT1_Format, | ||
Skeleton, | ||
SkinnedMesh, | ||
SRGBColorSpace, | ||
Uint8BufferAttribute, | ||
Vector2, | ||
Vector3, | ||
VectorKeyframeTrack, | ||
DataTexture, | ||
LinearFilter, | ||
LinearMipmapLinearFilter, | ||
SRGBColorSpace | ||
Vector4, | ||
VectorKeyframeTrack | ||
} from 'three'; | ||
@@ -74,8 +81,4 @@ | ||
this.parse( buffer, url, function ( object ) { | ||
this.parse( buffer, url ).then( onLoad ); | ||
onLoad( object ); | ||
} ); | ||
} catch ( e ) { | ||
@@ -109,3 +112,3 @@ | ||
*/ | ||
parse( buffer, url, onLoad, onError ) { | ||
async parse( buffer, url ) { | ||
@@ -160,3 +163,4 @@ const parser = new BinaryParser( buffer ); | ||
const textureWeightDefinitions = this._readTextureWeightDefinitions( parser, header ); | ||
// const boneDefinitions = this._readBoneDefinitions( parser, header ); | ||
const globalSequences = this._readGlobalSequences( parser, header ); // eslint-disable-line no-unused-vars | ||
const boneDefinitions = this._readBoneDefinitions( parser, header ); | ||
@@ -183,62 +187,21 @@ // lookup tables | ||
// textures | ||
// load textures and skin data asynchronously | ||
const texturePromises = this._buildTextures( textureDefinitions, textureLoader, name, chunks ); | ||
const textures = await Promise.all( this._loadTextures( textureDefinitions, textureLoader, name, chunks ) ); | ||
const skinData = await this._loadSkin( header, parser, skinLoader, name, chunks ); | ||
// skins | ||
// build scene | ||
Promise.all( texturePromises ).then( ( textures ) => { | ||
const geometries = this._buildGeometries( skinData, vertices ); | ||
const skeleton = this._buildSkeleton( boneDefinitions, globalSequences ); | ||
const materials = this._buildMaterials( materialDefinitions ); | ||
const textureTransforms = this._buildTextureTransforms( textureTransformDefinitions, globalSequences ); | ||
const textureWeights = this._buildTextureWeights( textureWeightDefinitions, globalSequences ); | ||
const group = this._buildObjects( name, geometries, skeleton, materials, textures, textureTransforms, textureWeights, skinData, lookupTables ); | ||
// skins | ||
return group; | ||
let promise; | ||
if ( header.version <= M2_VERSION_THE_BURNING_CRUSADE ) { | ||
promise = Promise.resolve( this._readEmbeddedSkinData( parser, header ) ); | ||
} else { | ||
let filename = ( name + '00.skin' ).toLowerCase(); // default skin name based on .m2 file | ||
const skinFileDataIDs = chunks.get( 'SFID' ); | ||
if ( skinFileDataIDs !== undefined ) { | ||
filename = skinFileDataIDs[ 0 ] + '.skin'; | ||
} | ||
promise = new Promise( ( resolve, reject ) => { | ||
skinLoader.load( filename, resolve, undefined, () => { | ||
reject( new Error( 'THREE.M2Loader: Failed to load skin file: ' + filename ) ); | ||
} ); | ||
} ); | ||
} | ||
// build | ||
promise.then( skinData => { | ||
const geometries = this._buildGeometries( skinData, vertices ); | ||
// const skeleton = this._buildSkeleton( boneDefinitions ); | ||
const materials = this._buildMaterials( materialDefinitions ); | ||
const textureTransforms = this._buildTextureTransforms( textureTransformDefinitions ); | ||
const textureWeights = this._buildTextureWeights( textureWeightDefinitions ); | ||
const group = this._buildObjects( name, geometries, materials, textures, textureTransforms, textureWeights, skinData, lookupTables ); | ||
onLoad( group ); | ||
} ).catch( onError ); | ||
} ).catch( onError ); | ||
} | ||
_buildObjects( name, geometries, materials, textures, textureTransforms, textureWeights, skinData, lookupTables ) { | ||
_buildObjects( name, geometries, skeleton, materials, textures, textureTransforms, textureWeights, skinData, lookupTables ) { | ||
@@ -259,7 +222,7 @@ const group = new Group(); | ||
const geometry = geometries[ batch.skinSectionIndex ]; | ||
const material = materials[ batch.materialIndex ]; | ||
const material = materials[ batch.materialIndex ].clone(); // cloning is required since the same material might be animated differently | ||
if ( animationMap.has( material ) === false ) { | ||
animationMap.set( material, new Set() ); // store animations targeting this material | ||
animationMap.set( material, [] ); // store animations targeting this material | ||
@@ -274,7 +237,7 @@ } | ||
material.map = textures[ textureIndex ].clone(); // cloning is required since the same texture might be transformed/animated differently | ||
material.map = textures[ textureIndex ].clone(); // cloning is required since the same texture might be animated differently | ||
if ( animationMap.has( material.map ) === false ) { | ||
animationMap.set( material.map, new Set() ); // store animations targeting this texture | ||
animationMap.set( material.map, [] ); // store animations targeting this texture | ||
@@ -296,3 +259,3 @@ } | ||
const textureAnimations = animationMap.get( material.map ); | ||
textureAnimations.add( ...textureTransform ); | ||
textureAnimations.push( ...textureTransform ); | ||
@@ -314,3 +277,3 @@ } | ||
const materialAnimations = animationMap.get( material ); | ||
materialAnimations.add( ...textureWeight ); | ||
materialAnimations.push( ...textureWeight ); | ||
@@ -323,3 +286,23 @@ } | ||
const mesh = new Mesh( geometry, material ); | ||
let mesh; | ||
if ( skeleton !== null ) { | ||
mesh = new SkinnedMesh( geometry, material ); | ||
mesh.bind( skeleton ); | ||
for ( let bone of skeleton.bones ) { | ||
if ( bone.parent === null ) mesh.add( bone ); | ||
} | ||
animationMap.set( group, skeleton.userData.animations ); | ||
} else { | ||
mesh = new Mesh( geometry, material ); | ||
} | ||
group.add( mesh ); | ||
@@ -348,2 +331,4 @@ | ||
const uv = []; | ||
const skinIndex = []; | ||
const skinWeight = []; | ||
@@ -360,2 +345,4 @@ for ( let i = 0; i < localVertexList.length; i ++ ) { | ||
uv.push( vertex.texCoords[ 0 ].x, vertex.texCoords[ 0 ].y ); | ||
skinIndex.push( vertex.boneIndices.x, vertex.boneIndices.y, vertex.boneIndices.z, vertex.boneIndices.w ); | ||
skinWeight.push( vertex.boneWeights.x, vertex.boneWeights.y, vertex.boneWeights.z, vertex.boneWeights.w ); | ||
@@ -367,2 +354,4 @@ } | ||
const uvAttribute = new Float32BufferAttribute( uv, 2 ); | ||
const skinIndexAttribute = new Uint8BufferAttribute( skinIndex, 4 ); | ||
const skinWeightAttribute = new Uint8BufferAttribute( skinWeight, 4, true ); | ||
@@ -383,2 +372,4 @@ // geometries | ||
geometry.setAttribute( 'uv', uvAttribute ); | ||
geometry.setAttribute( 'skinIndex', skinIndexAttribute ); | ||
geometry.setAttribute( 'skinWeight', skinWeightAttribute ); | ||
geometry.setIndex( index ); | ||
@@ -452,3 +443,3 @@ | ||
_buildSkeleton( boneDefinitions ) { | ||
_buildSkeleton( boneDefinitions, globalSequences ) { | ||
@@ -461,2 +452,5 @@ // TODO: Find out a better way for detecting static models | ||
const bones = []; | ||
const animations = []; | ||
const keyframes = []; | ||
const globalKeyframes = []; | ||
@@ -466,51 +460,152 @@ for ( let i = 0; i < boneDefinitions.length; i ++ ) { | ||
const boneDefinition = boneDefinitions[ i ]; | ||
const bone = new Bone(); | ||
bone.pivot = boneDefinition.pivot; // TODO: three.js does not support pivot points so this hack is required as well as overwriting Object3D.updateMatrix() | ||
bones.push( bone ); | ||
// build hierarchy | ||
const parentIndex = boneDefinition.parentBone; | ||
if ( parentIndex !== - 1 ) bones[ parentIndex ].add( bone ); | ||
} | ||
// animations | ||
return new Skeleton( bones ); | ||
const translationData = boneDefinition.translation; | ||
const rotationData = boneDefinition.rotation; | ||
const scaleData = boneDefinition.scale; | ||
} | ||
for ( let j = 0; j < translationData.timestamps.length; j ++ ) { | ||
_buildTextures( textureDefinitions, loader, name, chunks ) { | ||
let maxTimeStamp = null; | ||
const promises = []; | ||
if ( translationData.globalSequence >= 0 ) { | ||
for ( let i = 0; i < textureDefinitions.length; i ++ ) { | ||
maxTimeStamp = globalSequences[ translationData.globalSequence ] / 1000; | ||
const textureDefinition = textureDefinitions[ i ]; | ||
let filename = textureDefinition.filename; | ||
} | ||
// if the filename is empty, use the FileDataID field from the TXID chunk | ||
const ti = translationData.timestamps[ j ]; | ||
const vi = translationData.values[ j ]; | ||
if ( filename === '' ) { | ||
const times = []; | ||
const values = []; | ||
const textureFileDataIds = chunks.get( 'TXID' ); | ||
// ignore empty tracks | ||
if ( textureFileDataIds !== undefined ) { | ||
if ( ti.length <= 1 ) continue; | ||
filename = textureFileDataIds[ i ] + '.blp'; | ||
// times | ||
for ( let k = 0; k < ti.length; k ++ ) { | ||
times.push( ti[ k ] / 1000 ); | ||
} | ||
if ( maxTimeStamp !== null ) { | ||
times[ times.length - 1 ] = maxTimeStamp; | ||
} | ||
// values | ||
for ( let k = 0; k < vi.length; k += 3 ) { | ||
values.push( vi[ k ] ); | ||
values.push( vi[ k + 1 ] ); | ||
values.push( vi[ k + 2 ] ); | ||
} | ||
// interpolation type | ||
const interpolation = this._getInterpolation( translationData.interpolationType ); | ||
// keyframe track | ||
if ( maxTimeStamp !== null ) { | ||
if ( globalKeyframes[ j ] === undefined ) globalKeyframes[ j ] = []; | ||
globalKeyframes[ j ].push( new VectorKeyframeTrack( bone.uuid + '.position', times, values, interpolation ) ); | ||
} else { | ||
if ( keyframes[ j ] === undefined ) keyframes[ j ] = []; | ||
keyframes[ j ].push( new VectorKeyframeTrack( bone.uuid + '.position', times, values, interpolation ) ); | ||
} | ||
} | ||
// fallback: if the first texture has an empty name, use .m2 name | ||
for ( let j = 0; j < rotationData.timestamps.length; j ++ ) { | ||
if ( filename === '' ) { | ||
let maxTimeStamp = null; | ||
if ( i === 0 ) { | ||
if ( rotationData.globalSequence >= 0 ) { | ||
filename = ( name + '.blp' ).toLowerCase(); | ||
maxTimeStamp = globalSequences[ rotationData.globalSequence ] / 1000; | ||
} | ||
const ti = rotationData.timestamps[ j ]; | ||
const vi = rotationData.values[ j ]; | ||
const times = []; | ||
const values = []; | ||
// ignore empty tracks | ||
if ( ti.length <= 1 ) continue; | ||
// times | ||
for ( let k = 0; k < ti.length; k ++ ) { | ||
times.push( ti[ k ] / 1000 ); | ||
} | ||
if ( maxTimeStamp !== null ) { | ||
times[ times.length - 1 ] = maxTimeStamp; | ||
} | ||
// values | ||
for ( let k = 0; k < vi.length; k += 4 ) { | ||
values.push( vi[ k ] ); | ||
values.push( vi[ k + 1 ] ); | ||
values.push( vi[ k + 2 ] ); | ||
values.push( vi[ k + 3 ] ); | ||
} | ||
// interpolation type | ||
const interpolation = this._getInterpolation( rotationData.interpolationType ); | ||
// keyframe track | ||
if ( maxTimeStamp !== null ) { | ||
if ( globalKeyframes[ j ] === undefined ) globalKeyframes[ j ] = []; | ||
globalKeyframes[ j ].push( new QuaternionKeyframeTrack( bone.uuid + '.quaternion', times, values, interpolation ) ); | ||
} else { | ||
continue; | ||
if ( keyframes[ j ] === undefined ) keyframes[ j ] = []; | ||
keyframes[ j ].push( new QuaternionKeyframeTrack( bone.uuid + '.quaternion', times, values, interpolation ) ); | ||
} | ||
@@ -520,28 +615,99 @@ | ||
// | ||
for ( let j = 0; j < scaleData.timestamps.length; j ++ ) { | ||
const promise = new Promise( ( resolve, reject ) => { | ||
let maxTimeStamp = null; | ||
const config = { | ||
url: filename, | ||
flags: textureDefinition.flags | ||
}; | ||
if ( scaleData.globalSequence >= 0 ) { | ||
loader.load( config, resolve, undefined, () => { | ||
maxTimeStamp = globalSequences[ scaleData.globalSequence ] / 1000; | ||
reject( new Error( 'THREE.M2Loader: Failed to load texture: ' + config.url ) ); | ||
} | ||
} ); | ||
const ti = scaleData.timestamps[ j ]; | ||
const vi = scaleData.values[ j ]; | ||
} ); | ||
const times = []; | ||
const values = []; | ||
promises.push( promise ); | ||
// ignore empty tracks | ||
if ( ti.length <= 1 ) continue; | ||
// times | ||
for ( let k = 0; k < ti.length; k ++ ) { | ||
times.push( ti[ k ] / 1000 ); | ||
} | ||
if ( maxTimeStamp !== null ) { | ||
times[ times.length - 1 ] = maxTimeStamp; | ||
} | ||
// values | ||
for ( let k = 0; k < vi.length; k += 3 ) { | ||
values.push( vi[ k ] ); | ||
values.push( vi[ k + 1 ] ); | ||
values.push( vi[ k + 2 ] ); | ||
} | ||
// interpolation type | ||
const interpolation = this._getInterpolation( translationData.interpolationType ); | ||
// keyframe track | ||
if ( maxTimeStamp !== null ) { | ||
if ( globalKeyframes[ j ] === undefined ) globalKeyframes[ j ] = []; | ||
globalKeyframes[ j ].push( new VectorKeyframeTrack( bone.uuid + '.scale', times, values, interpolation ) ); | ||
} else { | ||
if ( keyframes[ j ] === undefined ) keyframes[ j ] = []; | ||
keyframes[ j ].push( new VectorKeyframeTrack( bone.uuid + '.scale', times, values, interpolation ) ); | ||
} | ||
} | ||
} | ||
return promises; | ||
for ( let j = 0; j < keyframes.length; j ++ ) { | ||
if ( keyframes[ j ] === undefined ) continue; | ||
const clip = new AnimationClip( 'SkeletonAnimation_' + j, - 1, [ ... keyframes[ j ] ] ); | ||
animations.push( clip ); | ||
} | ||
for ( let j = 0; j < globalKeyframes.length; j ++ ) { | ||
if ( globalKeyframes[ j ] === undefined ) continue; | ||
const clip = new AnimationClip( 'GlobalSkeletonAnimation_' + j, - 1, [ ... globalKeyframes[ j ] ] ); | ||
animations.push( clip ); | ||
} | ||
const skeleton = new Skeleton( bones ); | ||
skeleton.userData = {}; | ||
skeleton.userData.animations = animations; | ||
return skeleton; | ||
} | ||
_buildTextureTransforms( textureTransformDefinitions ) { | ||
_buildTextureTransforms( textureTransformDefinitions, globalSequences ) { | ||
@@ -558,10 +724,22 @@ const textureTransforms = []; | ||
const keyframes = []; | ||
const globalKeyframes = []; | ||
const translationData = textureTransformDefinition.translation; | ||
const rotationData = textureTransformDefinition.rotation; | ||
// translation | ||
for ( let j = 0; j < textureTransformDefinition.translation.timestamps.length; j ++ ) { | ||
for ( let j = 0; j < translationData.timestamps.length; j ++ ) { | ||
const ti = textureTransformDefinition.translation.timestamps[ j ]; | ||
const vi = textureTransformDefinition.translation.values[ j ]; | ||
let maxTimeStamp = null; | ||
if ( translationData.globalSequence >= 0 ) { | ||
maxTimeStamp = globalSequences[ translationData.globalSequence ] / 1000; | ||
} | ||
const ti = translationData.timestamps[ j ]; | ||
const vi = translationData.values[ j ]; | ||
const times = []; | ||
@@ -582,2 +760,8 @@ const values = []; | ||
if ( maxTimeStamp !== null ) { | ||
times[ times.length - 1 ] = maxTimeStamp; | ||
} | ||
// values | ||
@@ -594,6 +778,23 @@ | ||
if ( keyframes[ j ] === undefined ) keyframes[ j ] = []; | ||
// interpolation type | ||
keyframes[ j ].push( new VectorKeyframeTrack( '.offset', times, values ) ); | ||
const interpolation = this._getInterpolation( translationData.interpolationType ); | ||
// keyframe track | ||
if ( maxTimeStamp !== null ) { | ||
if ( globalKeyframes[ j ] === undefined ) globalKeyframes[ j ] = []; | ||
globalKeyframes[ j ].push( new VectorKeyframeTrack( '.offset', times, values, interpolation ) ); | ||
} else { | ||
if ( keyframes[ j ] === undefined ) keyframes[ j ] = []; | ||
keyframes[ j ].push( new VectorKeyframeTrack( '.offset', times, values, interpolation ) ); | ||
} | ||
} | ||
@@ -609,7 +810,15 @@ | ||
for ( let j = 0; j < textureTransformDefinition.rotation.timestamps.length; j ++ ) { | ||
for ( let j = 0; j < rotationData.timestamps.length; j ++ ) { | ||
const ti = textureTransformDefinition.rotation.timestamps[ j ]; | ||
const vi = textureTransformDefinition.rotation.values[ j ]; | ||
let maxTimeStamp = null; | ||
if ( translationData.globalSequence >= 0 ) { | ||
maxTimeStamp = globalSequences[ translationData.globalSequence ]; | ||
} | ||
const ti = rotationData.timestamps[ j ]; | ||
const vi = rotationData.values[ j ]; | ||
const times = []; | ||
@@ -630,2 +839,8 @@ const values = []; | ||
if ( maxTimeStamp !== null ) { | ||
times[ times.length - 1 ] = maxTimeStamp; | ||
} | ||
// values | ||
@@ -654,6 +869,23 @@ | ||
if ( keyframes[ j ] === undefined ) keyframes[ j ] = []; | ||
// interpolation type | ||
keyframes[ j ].push( new NumberKeyframeTrack( '.rotation', times, values ) ); | ||
const interpolation = this._getInterpolation( rotationData.interpolationType ); | ||
// keyframe track | ||
if ( maxTimeStamp !== null ) { | ||
if ( globalKeyframes[ j ] === undefined ) globalKeyframes[ j ] = []; | ||
globalKeyframes[ j ].push( new NumberKeyframeTrack( '.offset', times, values, interpolation ) ); | ||
} else { | ||
if ( keyframes[ j ] === undefined ) keyframes[ j ] = []; | ||
keyframes[ j ].push( new NumberKeyframeTrack( '.rotation', times, values, interpolation ) ); | ||
} | ||
} | ||
@@ -668,2 +900,9 @@ | ||
for ( let j = 0; j < globalKeyframes.length; j ++ ) { | ||
const clip = new AnimationClip( 'GlobalTextureTransform_' + j, - 1, [ ... globalKeyframes[ j ] ] ); | ||
animations.push( clip ); | ||
} | ||
textureTransforms.push( animations ); | ||
@@ -679,3 +918,3 @@ | ||
_buildTextureWeights( textureWeightDefinitions ) { | ||
_buildTextureWeights( textureWeightDefinitions, globalSequences ) { | ||
@@ -692,5 +931,14 @@ const textureWeights = []; | ||
const opacityKeyFrames = []; | ||
const globalOpacityKeyFrames = []; | ||
for ( let j = 0; j < textureWeightDefinition.timestamps.length; j ++ ) { | ||
let maxTimeStamp = null; | ||
if ( textureWeightDefinition.globalSequence >= 0 ) { | ||
maxTimeStamp = globalSequences[ textureWeightDefinition.globalSequence ]; | ||
} | ||
const ti = textureWeightDefinition.timestamps[ j ]; | ||
@@ -715,2 +963,8 @@ const vi = textureWeightDefinition.values[ j ]; | ||
if ( maxTimeStamp !== null ) { | ||
times[ times.length - 1 ] = maxTimeStamp; | ||
} | ||
// values | ||
@@ -724,4 +978,18 @@ | ||
opacityKeyFrames.push( new NumberKeyframeTrack( '.opacity', times, values ) ); | ||
// interpolation type | ||
const interpolation = this._getInterpolation( textureWeightDefinition.interpolationType ); | ||
// keyframe track | ||
if ( maxTimeStamp !== null ) { | ||
globalOpacityKeyFrames.push( new NumberKeyframeTrack( '.opacity', times, values, interpolation ) ); | ||
} else { | ||
opacityKeyFrames.push( new NumberKeyframeTrack( '.opacity', times, values, interpolation ) ); | ||
} | ||
} | ||
@@ -736,2 +1004,9 @@ | ||
for ( let j = 0; j < globalOpacityKeyFrames.length; j ++ ) { | ||
const clip = new AnimationClip( 'GlobalOpacity_' + j, - 1, [ globalOpacityKeyFrames[ j ] ] ); | ||
animations.push( clip ); | ||
} | ||
textureWeights.push( animations ); | ||
@@ -747,2 +1022,132 @@ | ||
_getInterpolation( type ) { | ||
let interpolation; | ||
switch ( type ) { | ||
case 0: | ||
interpolation = InterpolateDiscrete; | ||
break; | ||
case 1: | ||
interpolation = InterpolateLinear; | ||
break; | ||
case 2: | ||
case 3: | ||
interpolation = InterpolateSmooth; | ||
break; | ||
default: | ||
console.warn( 'THREE.M2Loader: Unsupported interpolation type.' ); | ||
interpolation = InterpolateLinear; // fallback | ||
break; | ||
} | ||
return interpolation; | ||
} | ||
_loadSkin( header, parser, skinLoader, name, chunks ) { | ||
let promise; | ||
if ( header.version <= M2_VERSION_THE_BURNING_CRUSADE ) { | ||
promise = Promise.resolve( this._readEmbeddedSkinData( parser, header ) ); | ||
} else { | ||
let filename = ( name + '00.skin' ).toLowerCase(); // default skin name based on .m2 file | ||
const skinFileDataIDs = chunks.get( 'SFID' ); | ||
if ( skinFileDataIDs !== undefined ) { | ||
filename = skinFileDataIDs[ 0 ] + '.skin'; | ||
} | ||
promise = new Promise( ( resolve, reject ) => { | ||
skinLoader.load( filename, resolve, undefined, () => { | ||
reject( new Error( 'THREE.M2Loader: Failed to load skin file: ' + filename ) ); | ||
} ); | ||
} ); | ||
} | ||
return promise; | ||
} | ||
_loadTextures( textureDefinitions, loader, name, chunks ) { | ||
const promises = []; | ||
for ( let i = 0; i < textureDefinitions.length; i ++ ) { | ||
const textureDefinition = textureDefinitions[ i ]; | ||
let filename = textureDefinition.filename; | ||
// if the filename is empty, use the FileDataID field from the TXID chunk | ||
if ( filename === '' ) { | ||
const textureFileDataIds = chunks.get( 'TXID' ); | ||
if ( textureFileDataIds !== undefined ) { | ||
filename = textureFileDataIds[ i ] + '.blp'; | ||
} | ||
} | ||
// fallback: if the first texture has an empty name, use .m2 name | ||
if ( filename === '' ) { | ||
if ( i === 0 ) { | ||
filename = ( name + '.blp' ).toLowerCase(); | ||
} else { | ||
continue; | ||
} | ||
} | ||
// | ||
const promise = new Promise( ( resolve, reject ) => { | ||
const config = { | ||
url: filename, | ||
flags: textureDefinition.flags | ||
}; | ||
loader.load( config, resolve, undefined, () => { | ||
reject( new Error( 'THREE.M2Loader: Failed to load texture: ' + config.url ) ); | ||
} ); | ||
} ); | ||
promises.push( promise ); | ||
} | ||
return promises; | ||
} | ||
_readHeader( parser ) { | ||
@@ -1002,2 +1407,24 @@ | ||
_readGlobalSequences( parser, header ) { | ||
const length = header.globalLoopsLength; | ||
const offset = header.globalLoopsOffset; | ||
parser.saveState(); | ||
parser.moveTo( offset ); | ||
const timestamps = []; | ||
for ( let i = 0; i < length; i ++ ) { | ||
timestamps.push( parser.readUInt32() ); | ||
} | ||
parser.restoreState(); | ||
return timestamps; | ||
} | ||
_readMaterialDefinitions( parser, header ) { | ||
@@ -1317,9 +1744,16 @@ | ||
values.push( | ||
parser.readFloat32(), | ||
parser.readFloat32(), | ||
parser.readFloat32(), | ||
parser.readFloat32() | ||
); | ||
// conversion from short to float, see https://wowdev.wiki/Quaternion_values_and_2.x | ||
let x = parser.readInt16(); | ||
let y = parser.readInt16(); | ||
let z = parser.readInt16(); | ||
let w = parser.readInt16(); | ||
x = ( x < 0 ? x + 32768 : x - 32767 ) / 32767; | ||
y = ( y < 0 ? y + 32768 : y - 32767 ) / 32767; | ||
z = ( z < 0 ? z + 32768 : z - 32767 ) / 32767; | ||
w = ( w < 0 ? w + 32768 : w - 32767 ) / 32767; | ||
values.push( x, y, z, w ); | ||
break; | ||
@@ -1392,15 +1826,11 @@ | ||
vertex.boneWeights.push( | ||
parser.readUInt8(), | ||
parser.readUInt8(), | ||
parser.readUInt8(), | ||
parser.readUInt8() | ||
); | ||
vertex.boneWeights.x = parser.readUInt8(); | ||
vertex.boneWeights.y = parser.readUInt8(); | ||
vertex.boneWeights.z = parser.readUInt8(); | ||
vertex.boneWeights.w = parser.readUInt8(); | ||
vertex.boneIndices.push( | ||
parser.readUInt8(), | ||
parser.readUInt8(), | ||
parser.readUInt8(), | ||
parser.readUInt8() | ||
); | ||
vertex.boneIndices.x = parser.readUInt8(); | ||
vertex.boneIndices.y = parser.readUInt8(); | ||
vertex.boneIndices.z = parser.readUInt8(); | ||
vertex.boneIndices.w = parser.readUInt8(); | ||
@@ -2158,4 +2588,4 @@ vertex.normal.x = parser.readFloat32(); | ||
this.pos = new Vector3(); | ||
this.boneWeights = []; | ||
this.boneIndices = []; | ||
this.boneWeights = new Vector4(); | ||
this.boneIndices = new Vector4(); | ||
this.normal = new Vector3(); | ||
@@ -2162,0 +2592,0 @@ this.texCoords = [ new Vector2(), new Vector2() ]; |
{ | ||
"name": "three-m2loader", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"description": "three.js loader for importing M2 assets from World of Warcraft.", | ||
@@ -5,0 +5,0 @@ "main": "M2Loader.js", |
57828
1557