This updates Three.js to 0.183.2 and MapLibre GL to 5.19.0, and rewires GLTFLoader utility imports to local vendored modules for browser module compatibility. Made-with: Cursor
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+4
-4
File diff suppressed because one or more lines are too long
Vendored
+385
-272
File diff suppressed because it is too large
Load Diff
Vendored
+3196
-37807
File diff suppressed because one or more lines are too long
+91
-29
@@ -11,6 +11,30 @@ import {
|
|||||||
Vector3,
|
Vector3,
|
||||||
} from '../three.module.js';
|
} from '../three.module.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module BufferGeometryUtils
|
||||||
|
* @three_import import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes vertex tangents using the MikkTSpace algorithm. MikkTSpace generates the same tangents consistently,
|
||||||
|
* and is used in most modelling tools and normal map bakers. Use MikkTSpace for materials with normal maps,
|
||||||
|
* because inconsistent tangents may lead to subtle visual issues in the normal map, particularly around mirrored
|
||||||
|
* UV seams.
|
||||||
|
*
|
||||||
|
* In comparison to this method, {@link BufferGeometry#computeTangents} (a custom algorithm) generates tangents that
|
||||||
|
* probably will not match the tangents in other software. The custom algorithm is sufficient for general use with a
|
||||||
|
* custom material, and may be faster than MikkTSpace.
|
||||||
|
*
|
||||||
|
* Returns the original BufferGeometry. Indexed geometries will be de-indexed. Requires position, normal, and uv attributes.
|
||||||
|
*
|
||||||
|
* @param {BufferGeometry} geometry - The geometry to compute tangents for.
|
||||||
|
* @param {Object} MikkTSpace - Instance of `examples/jsm/libs/mikktspace.module.js`, or `mikktspace` npm package.
|
||||||
|
* Await `MikkTSpace.ready` before use.
|
||||||
|
* @param {boolean} [negateSign=true] - Whether to negate the sign component (.w) of each tangent.
|
||||||
|
* Required for normal map conventions in some formats, including glTF.
|
||||||
|
* @return {BufferGeometry} The updated geometry.
|
||||||
|
*/
|
||||||
function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
|
function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
|
||||||
|
|
||||||
if ( ! MikkTSpace || ! MikkTSpace.isReady ) {
|
if ( ! MikkTSpace || ! MikkTSpace.isReady ) {
|
||||||
@@ -100,9 +124,11 @@ function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<BufferGeometry>} geometries
|
* Merges a set of geometries into a single instance. All geometries must have compatible attributes.
|
||||||
* @param {Boolean} useGroups
|
*
|
||||||
* @return {BufferGeometry}
|
* @param {Array<BufferGeometry>} geometries - The geometries to merge.
|
||||||
|
* @param {boolean} [useGroups=false] - Whether to use groups or not.
|
||||||
|
* @return {?BufferGeometry} The merged geometry. Returns `null` if the merge does not succeed.
|
||||||
*/
|
*/
|
||||||
function mergeGeometries( geometries, useGroups = false ) {
|
function mergeGeometries( geometries, useGroups = false ) {
|
||||||
|
|
||||||
@@ -296,8 +322,11 @@ function mergeGeometries( geometries, useGroups = false ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<BufferAttribute>} attributes
|
* Merges a set of attributes into a single instance. All attributes must have compatible properties and types.
|
||||||
* @return {BufferAttribute}
|
* Instances of {@link InterleavedBufferAttribute} are not supported.
|
||||||
|
*
|
||||||
|
* @param {Array<BufferAttribute>} attributes - The attributes to merge.
|
||||||
|
* @return {?BufferAttribute} The merged attribute. Returns `null` if the merge does not succeed.
|
||||||
*/
|
*/
|
||||||
function mergeAttributes( attributes ) {
|
function mergeAttributes( attributes ) {
|
||||||
|
|
||||||
@@ -389,10 +418,12 @@ function mergeAttributes( attributes ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BufferAttribute}
|
* Performs a deep clone of the given buffer attribute.
|
||||||
* @return {BufferAttribute}
|
*
|
||||||
|
* @param {BufferAttribute} attribute - The attribute to clone.
|
||||||
|
* @return {BufferAttribute} The cloned attribute.
|
||||||
*/
|
*/
|
||||||
export function deepCloneAttribute( attribute ) {
|
function deepCloneAttribute( attribute ) {
|
||||||
|
|
||||||
if ( attribute.isInstancedInterleavedBufferAttribute || attribute.isInterleavedBufferAttribute ) {
|
if ( attribute.isInstancedInterleavedBufferAttribute || attribute.isInterleavedBufferAttribute ) {
|
||||||
|
|
||||||
@@ -411,8 +442,11 @@ export function deepCloneAttribute( attribute ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<BufferAttribute>} attributes
|
* Interleaves a set of attributes and returns a new array of corresponding attributes that share a
|
||||||
* @return {Array<InterleavedBufferAttribute>}
|
* single {@link InterleavedBuffer} instance. All attributes must have compatible types.
|
||||||
|
*
|
||||||
|
* @param {Array<BufferAttribute>} attributes - The attributes to interleave.
|
||||||
|
* @return {?Array<InterleavedBufferAttribute>} An array of interleaved attributes. If interleave does not succeed, the method returns `null`.
|
||||||
*/
|
*/
|
||||||
function interleaveAttributes( attributes ) {
|
function interleaveAttributes( attributes ) {
|
||||||
|
|
||||||
@@ -475,8 +509,13 @@ function interleaveAttributes( attributes ) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a new, non-interleaved version of the provided attribute
|
/**
|
||||||
export function deinterleaveAttribute( attribute ) {
|
* Returns a new, non-interleaved version of the given attribute.
|
||||||
|
*
|
||||||
|
* @param {InterleavedBufferAttribute} attribute - The interleaved attribute.
|
||||||
|
* @return {BufferAttribute} The non-interleaved attribute.
|
||||||
|
*/
|
||||||
|
function deinterleaveAttribute( attribute ) {
|
||||||
|
|
||||||
const cons = attribute.data.array.constructor;
|
const cons = attribute.data.array.constructor;
|
||||||
const count = attribute.count;
|
const count = attribute.count;
|
||||||
@@ -523,8 +562,12 @@ export function deinterleaveAttribute( attribute ) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deinterleaves all attributes on the geometry
|
/**
|
||||||
export function deinterleaveGeometry( geometry ) {
|
* Deinterleaves all attributes on the given geometry.
|
||||||
|
*
|
||||||
|
* @param {BufferGeometry} geometry - The geometry to deinterleave.
|
||||||
|
*/
|
||||||
|
function deinterleaveGeometry( geometry ) {
|
||||||
|
|
||||||
const attributes = geometry.attributes;
|
const attributes = geometry.attributes;
|
||||||
const morphTargets = geometry.morphTargets;
|
const morphTargets = geometry.morphTargets;
|
||||||
@@ -567,8 +610,10 @@ export function deinterleaveGeometry( geometry ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BufferGeometry} geometry
|
* Returns the amount of bytes used by all attributes to represent the geometry.
|
||||||
* @return {number}
|
*
|
||||||
|
* @param {BufferGeometry} geometry - The geometry.
|
||||||
|
* @return {number} The estimate bytes used.
|
||||||
*/
|
*/
|
||||||
function estimateBytesUsed( geometry ) {
|
function estimateBytesUsed( geometry ) {
|
||||||
|
|
||||||
@@ -590,9 +635,11 @@ function estimateBytesUsed( geometry ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BufferGeometry} geometry
|
* Returns a new geometry with vertices for which all similar vertex attributes (within tolerance) are merged.
|
||||||
* @param {number} tolerance
|
*
|
||||||
* @return {BufferGeometry}
|
* @param {BufferGeometry} geometry - The geometry to merge vertices for.
|
||||||
|
* @param {number} [tolerance=1e-4] - The tolerance value.
|
||||||
|
* @return {BufferGeometry} - The new geometry with merged vertices.
|
||||||
*/
|
*/
|
||||||
function mergeVertices( geometry, tolerance = 1e-4 ) {
|
function mergeVertices( geometry, tolerance = 1e-4 ) {
|
||||||
|
|
||||||
@@ -753,9 +800,12 @@ function mergeVertices( geometry, tolerance = 1e-4 ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BufferGeometry} geometry
|
* Returns a new indexed geometry based on `TrianglesDrawMode` draw mode.
|
||||||
* @param {number} drawMode
|
* This mode corresponds to the `gl.TRIANGLES` primitive in WebGL.
|
||||||
* @return {BufferGeometry}
|
*
|
||||||
|
* @param {BufferGeometry} geometry - The geometry to convert.
|
||||||
|
* @param {number} drawMode - The current draw mode.
|
||||||
|
* @return {BufferGeometry} The new geometry using `TrianglesDrawMode`.
|
||||||
*/
|
*/
|
||||||
function toTrianglesDrawMode( geometry, drawMode ) {
|
function toTrianglesDrawMode( geometry, drawMode ) {
|
||||||
|
|
||||||
@@ -864,9 +914,13 @@ function toTrianglesDrawMode( geometry, drawMode ) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the morphed attributes of a morphed/skinned BufferGeometry.
|
* Calculates the morphed attributes of a morphed/skinned BufferGeometry.
|
||||||
* Helpful for Raytracing or Decals.
|
*
|
||||||
* @param {Mesh | Line | Points} object An instance of Mesh, Line or Points.
|
* Helpful for Raytracing or Decals (i.e. a `DecalGeometry` applied to a morphed Object with a `BufferGeometry`
|
||||||
* @return {Object} An Object with original position/normal attributes and morphed ones.
|
* will use the original `BufferGeometry`, not the morphed/skinned one, generating an incorrect result.
|
||||||
|
* Using this function to create a shadow `Object3`D the `DecalGeometry` can be correctly generated).
|
||||||
|
*
|
||||||
|
* @param {Mesh|Line|Points} object - The 3D object to compute morph attributes for.
|
||||||
|
* @return {Object} An object with original position/normal attributes and morphed ones.
|
||||||
*/
|
*/
|
||||||
function computeMorphedAttributes( object ) {
|
function computeMorphedAttributes( object ) {
|
||||||
|
|
||||||
@@ -1142,6 +1196,12 @@ function computeMorphedAttributes( object ) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the {@link BufferGeometry#groups} for the given geometry.
|
||||||
|
*
|
||||||
|
* @param {BufferGeometry} geometry - The geometry to modify.
|
||||||
|
* @return {BufferGeometry} - The updated geometry
|
||||||
|
*/
|
||||||
function mergeGroups( geometry ) {
|
function mergeGroups( geometry ) {
|
||||||
|
|
||||||
if ( geometry.groups.length === 0 ) {
|
if ( geometry.groups.length === 0 ) {
|
||||||
@@ -1244,15 +1304,14 @@ function mergeGroups( geometry ) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the supplied geometry if it is non-indexed, otherwise creates a new,
|
* Modifies the supplied geometry if it is non-indexed, otherwise creates a new,
|
||||||
* non-indexed geometry. Returns the geometry with smooth normals everywhere except
|
* non-indexed geometry. Returns the geometry with smooth normals everywhere except
|
||||||
* faces that meet at an angle greater than the crease angle.
|
* faces that meet at an angle greater than the crease angle.
|
||||||
*
|
*
|
||||||
* @param {BufferGeometry} geometry
|
* @param {BufferGeometry} geometry - The geometry to modify.
|
||||||
* @param {number} [creaseAngle]
|
* @param {number} [creaseAngle=Math.PI/3] - The crease angle in radians.
|
||||||
* @return {BufferGeometry}
|
* @return {BufferGeometry} - The updated geometry
|
||||||
*/
|
*/
|
||||||
function toCreasedNormals( geometry, creaseAngle = Math.PI / 3 /* 60 degrees */ ) {
|
function toCreasedNormals( geometry, creaseAngle = Math.PI / 3 /* 60 degrees */ ) {
|
||||||
|
|
||||||
@@ -1363,6 +1422,9 @@ export {
|
|||||||
computeMikkTSpaceTangents,
|
computeMikkTSpaceTangents,
|
||||||
mergeGeometries,
|
mergeGeometries,
|
||||||
mergeAttributes,
|
mergeAttributes,
|
||||||
|
deepCloneAttribute,
|
||||||
|
deinterleaveAttribute,
|
||||||
|
deinterleaveGeometry,
|
||||||
interleaveAttributes,
|
interleaveAttributes,
|
||||||
estimateBytesUsed,
|
estimateBytesUsed,
|
||||||
mergeVertices,
|
mergeVertices,
|
||||||
|
|||||||
+490
@@ -0,0 +1,490 @@
|
|||||||
|
import {
|
||||||
|
AnimationClip,
|
||||||
|
AnimationMixer,
|
||||||
|
Matrix4,
|
||||||
|
Quaternion,
|
||||||
|
QuaternionKeyframeTrack,
|
||||||
|
SkeletonHelper,
|
||||||
|
Vector3,
|
||||||
|
VectorKeyframeTrack
|
||||||
|
} from '../three.module.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module SkeletonUtils
|
||||||
|
* @three_import import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getBoneName( bone, options ) {
|
||||||
|
|
||||||
|
if ( options.getBoneName !== undefined ) {
|
||||||
|
|
||||||
|
return options.getBoneName( bone );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.names[ bone.name ];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retargets the skeleton from the given source 3D object to the
|
||||||
|
* target 3D object.
|
||||||
|
*
|
||||||
|
* @param {Object3D} target - The target 3D object.
|
||||||
|
* @param {Object3D} source - The source 3D object.
|
||||||
|
* @param {module:SkeletonUtils~RetargetOptions} options - The options.
|
||||||
|
*/
|
||||||
|
function retarget( target, source, options = {} ) {
|
||||||
|
|
||||||
|
const quat = new Quaternion(),
|
||||||
|
scale = new Vector3(),
|
||||||
|
relativeMatrix = new Matrix4(),
|
||||||
|
globalMatrix = new Matrix4();
|
||||||
|
|
||||||
|
options.preserveBoneMatrix = options.preserveBoneMatrix !== undefined ? options.preserveBoneMatrix : true;
|
||||||
|
options.preserveBonePositions = options.preserveBonePositions !== undefined ? options.preserveBonePositions : true;
|
||||||
|
options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
|
||||||
|
options.hip = options.hip !== undefined ? options.hip : 'hip';
|
||||||
|
options.hipInfluence = options.hipInfluence !== undefined ? options.hipInfluence : new Vector3( 1, 1, 1 );
|
||||||
|
options.scale = options.scale !== undefined ? options.scale : 1;
|
||||||
|
options.names = options.names || {};
|
||||||
|
|
||||||
|
const sourceBones = source.isObject3D ? source.skeleton.bones : getBones( source ),
|
||||||
|
bones = target.isObject3D ? target.skeleton.bones : getBones( target );
|
||||||
|
|
||||||
|
let bone, name, boneTo,
|
||||||
|
bonesPosition;
|
||||||
|
|
||||||
|
// reset bones
|
||||||
|
|
||||||
|
if ( target.isObject3D ) {
|
||||||
|
|
||||||
|
target.skeleton.pose();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
options.useTargetMatrix = true;
|
||||||
|
options.preserveBoneMatrix = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( options.preserveBonePositions ) {
|
||||||
|
|
||||||
|
bonesPosition = [];
|
||||||
|
|
||||||
|
for ( let i = 0; i < bones.length; i ++ ) {
|
||||||
|
|
||||||
|
bonesPosition.push( bones[ i ].position.clone() );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( options.preserveBoneMatrix ) {
|
||||||
|
|
||||||
|
// reset matrix
|
||||||
|
|
||||||
|
target.updateMatrixWorld();
|
||||||
|
|
||||||
|
target.matrixWorld.identity();
|
||||||
|
|
||||||
|
// reset children matrix
|
||||||
|
|
||||||
|
for ( let i = 0; i < target.children.length; ++ i ) {
|
||||||
|
|
||||||
|
target.children[ i ].updateMatrixWorld( true );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( let i = 0; i < bones.length; ++ i ) {
|
||||||
|
|
||||||
|
bone = bones[ i ];
|
||||||
|
name = getBoneName( bone, options );
|
||||||
|
|
||||||
|
boneTo = getBoneByName( name, sourceBones );
|
||||||
|
|
||||||
|
globalMatrix.copy( bone.matrixWorld );
|
||||||
|
|
||||||
|
if ( boneTo ) {
|
||||||
|
|
||||||
|
boneTo.updateMatrixWorld();
|
||||||
|
|
||||||
|
if ( options.useTargetMatrix ) {
|
||||||
|
|
||||||
|
relativeMatrix.copy( boneTo.matrixWorld );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
relativeMatrix.copy( target.matrixWorld ).invert();
|
||||||
|
relativeMatrix.multiply( boneTo.matrixWorld );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore scale to extract rotation
|
||||||
|
|
||||||
|
scale.setFromMatrixScale( relativeMatrix );
|
||||||
|
relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) );
|
||||||
|
|
||||||
|
// apply to global matrix
|
||||||
|
|
||||||
|
globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
|
||||||
|
|
||||||
|
if ( target.isObject3D ) {
|
||||||
|
|
||||||
|
if ( options.localOffsets ) {
|
||||||
|
|
||||||
|
if ( options.localOffsets[ bone.name ] ) {
|
||||||
|
|
||||||
|
globalMatrix.multiply( options.localOffsets[ bone.name ] );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
globalMatrix.copyPosition( relativeMatrix );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( name === options.hip ) {
|
||||||
|
|
||||||
|
globalMatrix.elements[ 12 ] *= options.scale * options.hipInfluence.x;
|
||||||
|
globalMatrix.elements[ 13 ] *= options.scale * options.hipInfluence.y;
|
||||||
|
globalMatrix.elements[ 14 ] *= options.scale * options.hipInfluence.z;
|
||||||
|
|
||||||
|
if ( options.hipPosition !== undefined ) {
|
||||||
|
|
||||||
|
globalMatrix.elements[ 12 ] += options.hipPosition.x * options.scale;
|
||||||
|
globalMatrix.elements[ 13 ] += options.hipPosition.y * options.scale;
|
||||||
|
globalMatrix.elements[ 14 ] += options.hipPosition.z * options.scale;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bone.parent ) {
|
||||||
|
|
||||||
|
bone.matrix.copy( bone.parent.matrixWorld ).invert();
|
||||||
|
bone.matrix.multiply( globalMatrix );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
bone.matrix.copy( globalMatrix );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
|
||||||
|
|
||||||
|
bone.updateMatrixWorld();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( options.preserveBonePositions ) {
|
||||||
|
|
||||||
|
for ( let i = 0; i < bones.length; ++ i ) {
|
||||||
|
|
||||||
|
bone = bones[ i ];
|
||||||
|
name = getBoneName( bone, options ) || bone.name;
|
||||||
|
|
||||||
|
if ( name !== options.hip ) {
|
||||||
|
|
||||||
|
bone.position.copy( bonesPosition[ i ] );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( options.preserveBoneMatrix ) {
|
||||||
|
|
||||||
|
// restore matrix
|
||||||
|
|
||||||
|
target.updateMatrixWorld( true );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retargets the animation clip of the source object to the
|
||||||
|
* target 3D object.
|
||||||
|
*
|
||||||
|
* @param {Object3D} target - The target 3D object.
|
||||||
|
* @param {Object3D} source - The source 3D object.
|
||||||
|
* @param {AnimationClip} clip - The animation clip.
|
||||||
|
* @param {module:SkeletonUtils~RetargetOptions} options - The options.
|
||||||
|
* @return {AnimationClip} The retargeted animation clip.
|
||||||
|
*/
|
||||||
|
function retargetClip( target, source, clip, options = {} ) {
|
||||||
|
|
||||||
|
options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
|
||||||
|
|
||||||
|
// Calculate the fps from the source clip based on the track with the most frames, unless fps is already provided.
|
||||||
|
options.fps = options.fps !== undefined ? options.fps : ( Math.max( ...clip.tracks.map( track => track.times.length ) ) / clip.duration );
|
||||||
|
options.names = options.names || [];
|
||||||
|
|
||||||
|
if ( ! source.isObject3D ) {
|
||||||
|
|
||||||
|
source = getHelperFromSkeleton( source );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
|
||||||
|
delta = clip.duration / ( numFrames - 1 ),
|
||||||
|
convertedTracks = [],
|
||||||
|
mixer = new AnimationMixer( source ),
|
||||||
|
bones = getBones( target.skeleton ),
|
||||||
|
boneDatas = [];
|
||||||
|
|
||||||
|
let positionOffset,
|
||||||
|
bone, boneTo, boneData,
|
||||||
|
name;
|
||||||
|
|
||||||
|
mixer.clipAction( clip ).play();
|
||||||
|
|
||||||
|
// trim
|
||||||
|
|
||||||
|
let start = 0, end = numFrames;
|
||||||
|
|
||||||
|
if ( options.trim !== undefined ) {
|
||||||
|
|
||||||
|
start = Math.round( options.trim[ 0 ] * options.fps );
|
||||||
|
end = Math.min( Math.round( options.trim[ 1 ] * options.fps ), numFrames ) - start;
|
||||||
|
|
||||||
|
mixer.update( options.trim[ 0 ] );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
mixer.update( 0 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
source.updateMatrixWorld();
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
for ( let frame = 0; frame < end; ++ frame ) {
|
||||||
|
|
||||||
|
const time = frame * delta;
|
||||||
|
|
||||||
|
retarget( target, source, options );
|
||||||
|
|
||||||
|
for ( let j = 0; j < bones.length; ++ j ) {
|
||||||
|
|
||||||
|
bone = bones[ j ];
|
||||||
|
name = getBoneName( bone, options ) || bone.name;
|
||||||
|
boneTo = getBoneByName( name, source.skeleton );
|
||||||
|
|
||||||
|
if ( boneTo ) {
|
||||||
|
|
||||||
|
boneData = boneDatas[ j ] = boneDatas[ j ] || { bone: bone };
|
||||||
|
|
||||||
|
if ( options.hip === name ) {
|
||||||
|
|
||||||
|
if ( ! boneData.pos ) {
|
||||||
|
|
||||||
|
boneData.pos = {
|
||||||
|
times: new Float32Array( end ),
|
||||||
|
values: new Float32Array( end * 3 )
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( options.useFirstFramePosition ) {
|
||||||
|
|
||||||
|
if ( frame === 0 ) {
|
||||||
|
|
||||||
|
positionOffset = bone.position.clone();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bone.position.sub( positionOffset );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boneData.pos.times[ frame ] = time;
|
||||||
|
|
||||||
|
bone.position.toArray( boneData.pos.values, frame * 3 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! boneData.quat ) {
|
||||||
|
|
||||||
|
boneData.quat = {
|
||||||
|
times: new Float32Array( end ),
|
||||||
|
values: new Float32Array( end * 4 )
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boneData.quat.times[ frame ] = time;
|
||||||
|
|
||||||
|
bone.quaternion.toArray( boneData.quat.values, frame * 4 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( frame === end - 2 ) {
|
||||||
|
|
||||||
|
// last mixer update before final loop iteration
|
||||||
|
// make sure we do not go over or equal to clip duration
|
||||||
|
mixer.update( delta - 0.0000001 );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
mixer.update( delta );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
source.updateMatrixWorld();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( let i = 0; i < boneDatas.length; ++ i ) {
|
||||||
|
|
||||||
|
boneData = boneDatas[ i ];
|
||||||
|
|
||||||
|
if ( boneData ) {
|
||||||
|
|
||||||
|
if ( boneData.pos ) {
|
||||||
|
|
||||||
|
convertedTracks.push( new VectorKeyframeTrack(
|
||||||
|
'.bones[' + boneData.bone.name + '].position',
|
||||||
|
boneData.pos.times,
|
||||||
|
boneData.pos.values
|
||||||
|
) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
convertedTracks.push( new QuaternionKeyframeTrack(
|
||||||
|
'.bones[' + boneData.bone.name + '].quaternion',
|
||||||
|
boneData.quat.times,
|
||||||
|
boneData.quat.values
|
||||||
|
) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mixer.uncacheAction( clip );
|
||||||
|
|
||||||
|
return new AnimationClip( clip.name, - 1, convertedTracks );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the given 3D object and its descendants, ensuring that any `SkinnedMesh` instances are
|
||||||
|
* correctly associated with their bones. Bones are also cloned, and must be descendants of the
|
||||||
|
* object passed to this method. Other data, like geometries and materials, are reused by reference.
|
||||||
|
*
|
||||||
|
* @param {Object3D} source - The 3D object to clone.
|
||||||
|
* @return {Object3D} The cloned 3D object.
|
||||||
|
*/
|
||||||
|
function clone( source ) {
|
||||||
|
|
||||||
|
const sourceLookup = new Map();
|
||||||
|
const cloneLookup = new Map();
|
||||||
|
|
||||||
|
const clone = source.clone();
|
||||||
|
|
||||||
|
parallelTraverse( source, clone, function ( sourceNode, clonedNode ) {
|
||||||
|
|
||||||
|
sourceLookup.set( clonedNode, sourceNode );
|
||||||
|
cloneLookup.set( sourceNode, clonedNode );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
clone.traverse( function ( node ) {
|
||||||
|
|
||||||
|
if ( ! node.isSkinnedMesh ) return;
|
||||||
|
|
||||||
|
const clonedMesh = node;
|
||||||
|
const sourceMesh = sourceLookup.get( node );
|
||||||
|
const sourceBones = sourceMesh.skeleton.bones;
|
||||||
|
|
||||||
|
clonedMesh.skeleton = sourceMesh.skeleton.clone();
|
||||||
|
clonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );
|
||||||
|
|
||||||
|
clonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {
|
||||||
|
|
||||||
|
return cloneLookup.get( bone );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
clonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal helper
|
||||||
|
|
||||||
|
function getBoneByName( name, skeleton ) {
|
||||||
|
|
||||||
|
for ( let i = 0, bones = getBones( skeleton ); i < bones.length; i ++ ) {
|
||||||
|
|
||||||
|
if ( name === bones[ i ].name )
|
||||||
|
|
||||||
|
return bones[ i ];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBones( skeleton ) {
|
||||||
|
|
||||||
|
return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getHelperFromSkeleton( skeleton ) {
|
||||||
|
|
||||||
|
const source = new SkeletonHelper( skeleton.bones[ 0 ] );
|
||||||
|
source.skeleton = skeleton;
|
||||||
|
|
||||||
|
return source;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function parallelTraverse( a, b, callback ) {
|
||||||
|
|
||||||
|
callback( a, b );
|
||||||
|
|
||||||
|
for ( let i = 0; i < a.children.length; i ++ ) {
|
||||||
|
|
||||||
|
parallelTraverse( a.children[ i ], b.children[ i ], callback );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retarget options of `SkeletonUtils`.
|
||||||
|
*
|
||||||
|
* @typedef {Object} module:SkeletonUtils~RetargetOptions
|
||||||
|
* @property {boolean} [useFirstFramePosition=false] - Whether to use the position of the first frame or not.
|
||||||
|
* @property {number} [fps] - The FPS of the clip.
|
||||||
|
* @property {Object<string,string>} [names] - A dictionary for mapping target to source bone names.
|
||||||
|
* @property {function(string):string} [getBoneName] - A function for mapping bone names. Alternative to `names`.
|
||||||
|
* @property {Array<number>} [trim] - Whether to trim the clip or not. If set the array should hold two values for the start and end.
|
||||||
|
* @property {boolean} [preserveBoneMatrix=true] - Whether to preserve bone matrices or not.
|
||||||
|
* @property {boolean} [preserveBonePositions=true] - Whether to preserve bone positions or not.
|
||||||
|
* @property {boolean} [useTargetMatrix=false] - Whether to use the target matrix or not.
|
||||||
|
* @property {string} [hip='hip'] - The name of the source's hip bone.
|
||||||
|
* @property {Vector3} [hipInfluence=(1,1,1)] - The hip influence.
|
||||||
|
* @property {number} [scale=1] - The scale.
|
||||||
|
**/
|
||||||
|
|
||||||
|
export {
|
||||||
|
retarget,
|
||||||
|
retargetClip,
|
||||||
|
clone,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user