Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

equip sound + sword grunt + sword sound #3674

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions animations-baker.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,122 @@ const {CharsetEncoder} = require('three/examples/js/libs/mmdparser.js');
});
// console.log('got animations', animations);

const comboAnimationNames = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is completely unabstracted and unreadable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no comments or functions here, so it's not clear what this is supposed to do or why it would be correct.

'sword_side_slash.fbx',
'sword_side_slash_step.fbx',
'sword_topdown_slash.fbx',
'sword_topdown_slash_step.fbx',
];
const animationComboIndices = comboAnimationNames.map(comboAnimationName => {
const animation = animations.find(a => a.name === comboAnimationName);
const {tracks, object} = animation;
// console.log('got interpolants', object, animation.name);
const bones = [];
object.traverse(o => {
if (o.isBone) {
const bone = o;
bone.initialPosition = bone.position.clone();
bone.initialQuaternion = bone.quaternion.clone();
bones.push(bone);
}
});
// console.log('got bones', bones.map(b => b.name));
const rootBone = object; // not really a bone
const leftHandBone = bones.find(b => b.name === 'mixamorigLeftHand');
const rightHandBone = bones.find(b => b.name === 'mixamorigRightHand');
const epsilon = 0.2;
const allOnesEpsilon = arr => arr.every(v => Math.abs(1 - v) < epsilon);

const bonePositionInterpolants = {};
const boneQuaternionInterpolants = {};
const tracksToRemove = [];
for (const track of tracks) {
if (/\.position$/.test(track.name)) {
const boneName = track.name.replace(/\.position$/, '');
// const bone = bones.find(b => b.name === boneName);
const boneInterpolant = new THREE.LinearInterpolant(track.times, track.values, track.getValueSize());
bonePositionInterpolants[boneName] = boneInterpolant;
} else if (/\.quaternion$/.test(track.name)) {
const boneName = track.name.replace(/\.quaternion$/, '');
// const bone = bones.find(b => b.name === boneName);
const boneInterpolant = new THREE.QuaternionLinearInterpolant(track.times, track.values, track.getValueSize());
boneQuaternionInterpolants[boneName] = boneInterpolant;
} else if (/\.scale$/.test(track.name)) {
if (allOnesEpsilon(track.values)) {
const index = tracks.indexOf(track);
tracksToRemove.push(index);
} else {
throw new Error(`This track has invalid values. All scale transforms must be set to 1. Aborting.\n Animation: ${animation.name}, Track: ${track.name}, values: \n ${track.values}`);
}
} else {
console.warn('unknown track name', animation.name, track);
}
}
// remove scale transform tracks as they won't be used;
let i = tracksToRemove.length;
while (i--) {
tracks.splice(tracksToRemove[i], 1);
}

const walkBufferSize = 256;
const leftHandDeltas = new Float32Array(walkBufferSize);
const rightHandDeltas = new Float32Array(walkBufferSize);

let lastRightHandPos = new THREE.Vector3();
let lastLeftHandPos = new THREE.Vector3();

let maxRightDeltaIndex = 0;
let maxLeftDeltaIndex = 0;

for (let i = 0; i < walkBufferSize; i++) {
const f = i / (walkBufferSize - 1);
for (const bone of bones) {
const positionInterpolant = bonePositionInterpolants[bone.name];
const quaternionInterpolant = boneQuaternionInterpolants[bone.name];
if (positionInterpolant) {
const pv = positionInterpolant.evaluate(f * animation.duration);
bone.position.fromArray(pv);
} else {
bone.position.copy(bone.initialPosition);
}
if (quaternionInterpolant) {
const qv = quaternionInterpolant.evaluate(f * animation.duration);
bone.quaternion.fromArray(qv);
} else {
bone.quaternion.copy(bone.initialQuaternion);
}
}
rootBone.updateMatrixWorld(true);
const fbxScale = 100;
const leftHand = new THREE.Vector3().setFromMatrixPosition(leftHandBone.matrixWorld).divideScalar(fbxScale);
const rightHand = new THREE.Vector3().setFromMatrixPosition(rightHandBone.matrixWorld).divideScalar(fbxScale);

const leftHandDelta = i === 0 ? 0 : lastLeftHandPos.distanceTo(leftHand);
const rightHandDelta = i === 0 ? 0 : lastRightHandPos.distanceTo(rightHand);

leftHandDeltas[i] = leftHandDelta * 1000;
rightHandDeltas[i] = rightHandDelta * 1000;

maxRightDeltaIndex = rightHandDeltas[i] > rightHandDeltas[maxRightDeltaIndex] ? i : maxRightDeltaIndex;
maxLeftDeltaIndex = leftHandDeltas[i] > leftHandDeltas[maxRightDeltaIndex] ? i : maxLeftDeltaIndex;


lastLeftHandPos.set(leftHand.x, leftHand.y, leftHand.z);
lastRightHandPos.set(rightHand.x, rightHand.y, rightHand.z);
}


// console.log('got', leftHandDeltas, rightHandDeltas, maxLeftDeltaIndex, maxRightDeltaIndex);

return {
maxRightDeltaIndex,
maxLeftDeltaIndex,
leftHandDeltas,
rightHandDeltas,
name: animation.name,
};
});

// format
const animationsJson = animations.map(a => a.toJSON())
.concat(mmdAnimationsJson);
Expand All @@ -547,6 +663,7 @@ const {CharsetEncoder} = require('three/examples/js/libs/mmdparser.js');
const animationsUint8Array = zbencode({
animations: animationsJson,
animationStepIndices,
animationComboIndices,
});
zbdecode(animationsUint8Array);
console.log('exporting animations');
Expand Down
3 changes: 3 additions & 0 deletions avatars/animationHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const identityQuaternion = new Quaternion();

let animations;
let animationStepIndices;
let animationComboIndices;
// let animationsBaseModel;
let jumpAnimation;
let doubleJumpAnimation;
Expand Down Expand Up @@ -179,6 +180,7 @@ async function loadAnimations() {
animations = animationsJson.animations
.map(a => AnimationClip.parse(a));
animationStepIndices = animationsJson.animationStepIndices;
animationComboIndices = animationsJson.animationComboIndices;
animations.index = {};
for (const animation of animations) {
animations.index[animation.name] = animation;
Expand Down Expand Up @@ -1480,6 +1482,7 @@ export const _applyAnimation = (avatar, now) => {
export {
animations,
animationStepIndices,
animationComboIndices,
emoteAnimations,
// cubicBezier,
};
Expand Down
2 changes: 2 additions & 0 deletions avatars/avatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const upVector = new THREE.Vector3(0, 1, 0);
import {
animations,
animationStepIndices,
animationComboIndices,
} from './animationHelpers.js';

const cubicBezier = easing(0, 1, 0, 1);
Expand Down Expand Up @@ -2187,6 +2188,7 @@ class Avatar {
Avatar.waitForLoad = () => loadPromise;
Avatar.getAnimations = () => animations;
Avatar.getAnimationStepIndices = () => animationStepIndices;
Avatar.getanimationComboIndices = () => animationComboIndices;
Avatar.getAnimationMappingConfig = () => animationMappingConfig;

Avatar.getClosest2AnimationAngles = getClosest2AnimationAngles;
Expand Down
92 changes: 71 additions & 21 deletions character-sfx.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ const freestyleOffset = 900 / 2;
const breaststrokeDuration = 1066.6666666666666;
const breaststrokeOffset = 433.3333333333333;

const aimAnimations = {
swordSideIdle: 'sword_idle_side.fbx',
swordSideIdleStatic: 'sword_idle_side_static.fbx',
swordSideSlash: 'sword_side_slash.fbx',
swordSideSlashStep: 'sword_side_slash_step.fbx',
swordTopDownSlash: 'sword_topdown_slash.fbx',
swordTopDownSlashStep: 'sword_topdown_slash_step.fbx',
swordUndraw: 'sword_undraw.fbx',
};


// HACK: this is used to dynamically control the step offset for a particular animation
// it is useful during development to adjust sync between animations and sound
Expand Down Expand Up @@ -62,22 +72,29 @@ const _getActionFrameIndex = (f, frameTimes) => {
return i;
};

class CharacterSfx {
class CharacterSfx extends EventTarget{
constructor(player) {
super();
this.player = player;

this.lastJumpState = false;
this.lastStepped = [false, false];
this.lastWalkTime = 0;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad spacing.



this.lastSwordComboName = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are now so many random state keys on this class that it needs to be broken up further.

this.swordComboStartTime = 0;

this.lastEatFrameIndex = -1;
this.lastDrinkFrameIndex = -1;

this.narutoRunStartTime = 0;
this.narutoRunFinishTime = 0;
this.narutoRunTrailSoundStartTime = 0;
this.narutoRunTurnSoundStartTime = 0;
this.currentQ=new THREE.Quaternion();
this.preQ=new THREE.Quaternion();
this.currentQ = new THREE.Quaternion();
this.preQ = new THREE.Quaternion();
this.arr = [0, 0, 0, 0];

this.startRunningTime = 0;
Expand All @@ -86,21 +103,12 @@ class CharacterSfx {
this.oldNarutoRunSound = null;
this.lastEmote = null;

if (this.player.isLocalPlayer) {
const wearupdate = e => {
sounds.playSoundName(e.wear ? 'itemEquip' : 'itemUnequip');
};
player.addEventListener('wearupdate', wearupdate);
this.cleanup = () => {
player.removeEventListener('wearupdate', wearupdate);
};
}

this.currentStep = null;
this.currentSwimmingHand = null;
this.setSwimmingHand = true;

this.lastLandState = false;

}
update(timestamp, timeDiffS) {
if (!this.player.avatar) {
Expand Down Expand Up @@ -227,6 +235,7 @@ class CharacterSfx {
if (!this.player.hasAction('sit')) {
_handleStep();
}

const _handleSwim = () => {
if(this.player.hasAction('swim')){
// const candidateAudios = soundFiles.water;
Expand Down Expand Up @@ -342,8 +351,49 @@ class CharacterSfx {

};
_handleNarutoRun();


// combo
const dispatchComboSoundEvent = (soundIndex) =>{
this.dispatchEvent(new MessageEvent('meleewhoosh', {
data: {
index: soundIndex
},
}));
}
const _handleCombo = () => {
if (this.player.hasAction('use') && this.player.getAction('use').behavior === 'sword' && this.player.getAction('use').animationCombo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why look it up 3 times per if?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const comboAnimationName = aimAnimations[this.player.getAction('use').animationCombo[this.player.avatar.useAnimationIndex]];
if (comboAnimationName && comboAnimationName !== this.lastSwordComboName) {
this.swordComboStartTime = timeSeconds;
this.alreadyPlayComboSound = false;
}
if (comboAnimationName) {
const animations = Avatar.getAnimations();
const animation = animations.find(a => a.name === comboAnimationName);
const animationComboIndices = Avatar.getanimationComboIndices();
const animationIndices = animationComboIndices.find(i => i.name === comboAnimationName);
const handDeltas = this.player.getAction('use').boneAttachment === 'leftHand' ? animationIndices.rightHandDeltas : animationIndices.leftHandDeltas;
const maxDeltaIndex = this.player.getAction('use').boneAttachment === 'leftHand' ? animationIndices.maxRightDeltaIndex : animationIndices.maxLeftDeltaIndex;

const ratio = (timeSeconds - this.swordComboStartTime) / animation.duration;
if (ratio <= 1 && !this.alreadyPlayComboSound) {
const index = Math.floor(ratio * handDeltas.length);
if (index > maxDeltaIndex) {
this.alreadyPlayComboSound = true;
this.playGrunt('attack');
const soundIndex = this.player.avatar.useAnimationIndex * 4 + Math.floor(Math.random() * 4);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 4? Don't include random constants in the middle of the code.

Copy link
Contributor Author

@tcm390 tcm390 Sep 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dispatchComboSoundEvent(soundIndex);

}
}
}
this.lastSwordComboName = comboAnimationName;
}

};

_handleCombo();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is poor quality unstructured code. The rest of the file has the exact same problem of being random unstructured if statements with a huge state vector, but it's gotten to the point where it is difficult for anyone to review this or even understand what is going on here.

IMO this file needs a complete redesign under some sort of structured coding theory before anyone can review it.


const _handleGasp = () =>{
const isRunning = currentSpeed > 0.5;
if(isRunning){
Expand Down Expand Up @@ -459,12 +509,12 @@ class CharacterSfx {
}
}

if(index===undefined){
if (index === undefined) {
let voice = selectVoice(voiceFiles);
duration = voice.duration;
offset = voice.offset;
}
else{
else {
duration = voiceFiles[index].duration;
offset = voiceFiles[index].offset;
}
Expand All @@ -480,7 +530,7 @@ class CharacterSfx {
audioBufferSourceNode.connect(this.player.avatar.getAudioInput());

// if the oldGrunt are still playing
if(this.oldGrunt){
if (this.oldGrunt) {
this.oldGrunt.stop();
this.oldGrunt = null;
}
Expand Down Expand Up @@ -546,12 +596,12 @@ class CharacterSfx {
}
}

if(index===undefined){
if (index === undefined) {
let voice = selectVoice(voiceFiles);
duration = voice.duration;
offset = voice.offset;
}
else{
else {
duration = voiceFiles[index].duration;
offset = voiceFiles[index].offset;
}
Expand All @@ -567,7 +617,7 @@ class CharacterSfx {
audioBufferSourceNode.connect(this.player.avatar.getAudioInput());

// if the oldGrunt are still playing
if(this.oldGrunt){
if (this.oldGrunt) {
this.oldGrunt.stop();
this.oldGrunt = null;
}
Expand All @@ -590,4 +640,4 @@ class CharacterSfx {

export {
CharacterSfx,
};
};
2 changes: 2 additions & 0 deletions metaverse-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import wear from './metaverse_components/wear.js';
import pet from './metaverse_components/pet.js';
import drop from './metaverse_components/drop.js';
// import mob from './metaverse_components/mob.js';
import use from './metaverse_components/use.js';

const componentTemplates = {
wear,
// npc,
pet,
drop,
// mob,
use,
};
export {
componentTemplates,
Expand Down
Loading