refactor animation logic into dedicated class
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-05-29 15:48:57 -04:00
parent 4f663a322c
commit f7d8072122
27 changed files with 745 additions and 683 deletions

View File

@ -2060,6 +2060,7 @@ Code cleanup
More tests
Move actor masks into dedicated package
Actor code cleanup
Refactor animation logic into dedicated actor class

View File

@ -76,8 +76,8 @@ public class ClientSimulation {
if(currentActor.getLodLevel() == Actor.LOD_LEVEL_STATIC){
continue;
}
if(currentActor.isPlayingAnimation()){
currentActor.incrementAnimationTime((float)Globals.engineState.timekeeper.getSimFrameTime());
if(currentActor.getAnimationData().isPlayingAnimation()){
currentActor.getAnimationData().incrementAnimationTime((float)Globals.engineState.timekeeper.getSimFrameTime());
}
}
Globals.profiler.endCpuSample();

View File

@ -97,7 +97,7 @@ public class CharacterCustomizer {
sliderName.setMinWidth(200);
//add a slider
Slider boneSlider = Slider.createSlider((ValueChangeEvent event) -> {
if(characterActor.getStaticMorph() != null){
if(characterActor.getAnimationData().getStaticMorph() != null){
staticMorph.updateValue(attribute.getSubtype(), attribute.getPrimaryBone(), event.getAsFloat());
if(attribute.getMirrorBone() != null){
staticMorph.updateValue(attribute.getSubtype(), attribute.getMirrorBone(), event.getAsFloat());
@ -167,7 +167,7 @@ public class CharacterCustomizer {
}
}
//finally set static morph
characterActor.setActorStaticMorph(staticMorph);
characterActor.getAnimationData().setActorStaticMorph(staticMorph);
//character create button
Div createButtonContainer = Div.createDiv();

View File

@ -75,7 +75,7 @@ public class ImGuiEntityActorTab {
//animation queue
if(ImGui.collapsingHeader("Animation Queue")){
Set<ActorAnimationMaskEntry> animationQueue = actor.getAnimationQueue();
Set<ActorAnimationMaskEntry> animationQueue = actor.getAnimationData().getAnimationQueue();
for(ActorAnimationMaskEntry mask : animationQueue){
ImGui.text(mask.getAnimationName() + " - " + mask.getPriority());
ImGui.text(mask.getDuration() + " " + mask.getTime());
@ -84,10 +84,10 @@ public class ImGuiEntityActorTab {
//bone values
if(ImGui.collapsingHeader("Bone Values")){
for(Bone bone : actor.getBoneValues()){
for(Bone bone : actor.getAnimationData().getBoneValues()){
ImGui.text(bone.boneID);
ImGui.text("Position: " + actor.getBonePosition(bone.boneID));
ImGui.text("Rotation: " + actor.getBoneRotation(bone.boneID));
ImGui.text("Position: " + actor.getAnimationData().getBonePosition(bone.boneID));
ImGui.text("Rotation: " + actor.getAnimationData().getBoneRotation(bone.boneID));
ImGui.text(bone.getFinalTransform() + "");
}
}
@ -104,7 +104,7 @@ public class ImGuiEntityActorTab {
for(Animation animation : model.getAnimations()){
if(ImGui.collapsingHeader(animation.name)){
if(ImGui.button("Play")){
actor.playAnimation(animation.name, AnimationPriorities.getValue(AnimationPriorities.MODIFIER_MAX));
actor.getAnimationData().playAnimation(animation.name, AnimationPriorities.getValue(AnimationPriorities.MODIFIER_MAX));
}
for(AnimChannel channel : animation.channels){
ImGui.pushID(channel.getNodeID());
@ -131,7 +131,7 @@ public class ImGuiEntityActorTab {
if(ImGui.collapsingHeader("Print Data")){
//print bone values
if(ImGui.button("Print current bone values")){
for(Bone bone : actor.getBoneValues()){
for(Bone bone : actor.getAnimationData().getBoneValues()){
LoggerInterface.loggerRenderer.DEBUG(bone.boneID);
LoggerInterface.loggerRenderer.DEBUG("" + bone.getFinalTransform());
}

View File

@ -362,7 +362,7 @@ public class MenuGeneratorsInGame {
Entity playerEntity = Globals.clientState.playerEntity;
Actor playerActor = EntityUtils.getActor(playerEntity);
ActorStaticMorph staticMorph = playerActor.getStaticMorph();
ActorStaticMorph staticMorph = playerActor.getAnimationData().getStaticMorph();
CreatureData playeCreatureType = Globals.gameConfigCurrent.getCreatureTypeLoader().getType(CreatureUtils.getType(playerEntity));
int offset = 0;
for(VisualAttribute attribute : playeCreatureType.getVisualAttributes()){

View File

@ -138,11 +138,11 @@ public class StateTransitionUtil {
//
//Play main animation
//
if(!actor.isPlayingAnimation() && state.onComplete != null && state.startedAnimation == true){
if(!actor.getAnimationData().isPlayingAnimation() && state.onComplete != null && state.startedAnimation == true){
//state transition if this isn't set to loop
state.onComplete.run();
state.startedAnimation = false;
} else if(!actor.isPlayingAnimation() || !actor.isPlayingAnimation(animation)){
} else if(!actor.getAnimationData().isPlayingAnimation() || !actor.getAnimationData().isPlayingAnimation(animation)){
//
//if it isn't looping, only play on first go around
@ -167,10 +167,10 @@ public class StateTransitionUtil {
//
//play animation
if(animation != null){
actor.playAnimation(animation,true);
actor.getAnimationData().playAnimation(animation,true);
shouldPlayFirstPerson = true;
}
actor.incrementAnimationTime(animationOffset);
actor.getAnimationData().incrementAnimationTime(animationOffset);
}
state.startedAnimation = true;
} else if(state.animation == null && state.onComplete != null){
@ -272,8 +272,8 @@ public class StateTransitionUtil {
//
//Interrupt main animation
//
if(animation != null && actor.isPlayingAnimation() && actor.isPlayingAnimation(animation)){
actor.interruptAnimation(animation, true);
if(animation != null && actor.getAnimationData().isPlayingAnimation() && actor.getAnimationData().isPlayingAnimation(animation)){
actor.getAnimationData().interruptAnimation(animation, true);
}
//

View File

@ -208,8 +208,8 @@ public class AttachUtils {
child,
new Vector3d(offset),
new Quaterniond(AttachUtils.getRotationOffset(child)),
new Vector3d(parentActor.getBonePosition(targetBone)),
new Quaterniond(parentActor.getBoneRotation(targetBone)),
new Vector3d(parentActor.getAnimationData().getBonePosition(targetBone)),
new Quaterniond(parentActor.getAnimationData().getBoneRotation(targetBone)),
new Vector3d(EntityUtils.getPosition(parent)),
new Quaterniond(EntityUtils.getRotation(parent)),
new Vector3d(EntityUtils.getScale(parent))

View File

@ -564,11 +564,11 @@ public class ClientAttackTree implements BehaviorTree {
Actor actor = EntityUtils.getActor(parent);
if(this.currentMove != null && this.currentMove.getHitstun() != null){
String animName = this.currentMove.getAttackState().getAnimation().getNameThirdPerson();
actor.setFreezeFrames(animName, this.currentMove.getHitstun());
actor.getAnimationData().setFreezeFrames(animName, this.currentMove.getHitstun());
if(parent == Globals.clientState.playerEntity && !Globals.controlHandler.cameraIsThirdPerson()){
Actor viewmodelActor = EntityUtils.getActor(Globals.clientState.firstPersonEntity);
animName = this.currentMove.getAttackState().getAnimation().getNameFirstPerson();
viewmodelActor.setFreezeFrames(animName, this.currentMove.getHitstun());
viewmodelActor.getAnimationData().setFreezeFrames(animName, this.currentMove.getHitstun());
}
}
}

View File

@ -402,7 +402,7 @@ public class ServerAttackTree implements BehaviorTree {
if(targetBone != null){
Actor parentActor = EntityUtils.getActor(parent);
//transform bone space
spawnPosition = new Vector3d(parentActor.getBonePosition(targetBone));
spawnPosition = new Vector3d(parentActor.getAnimationData().getBonePosition(targetBone));
spawnPosition = spawnPosition.mul(((Vector3f)EntityUtils.getScale(parent)));
Quaterniond rotation = EntityUtils.getRotation(parent);
spawnPosition = spawnPosition.rotate(new Quaterniond(rotation.x,rotation.y,rotation.z,rotation.w));
@ -414,7 +414,7 @@ public class ServerAttackTree implements BehaviorTree {
// Quaternionf rotation = parentActor.getBoneRotation(targetBone);
// EntityUtils.getRotation(currentEntity).set(rotation).normalize();
// Vector3d facingAngle = CreatureUtils.getFacingVector(parent);
arrowRotation = parentActor.getBoneRotation(targetBone);
arrowRotation = parentActor.getAnimationData().getBoneRotation(targetBone);
// EntityUtils.getRotation(currentEntity).rotationTo(MathUtils.ORIGIN_VECTORF, new Vector3f((float)facingAngle.x,(float)facingAngle.y,(float)facingAngle.z)).mul(parentActor.getBoneRotation(targetBone)).normalize();
}
Vector3f initialVector = new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z).normalize();

View File

@ -106,11 +106,11 @@ public class FirstPersonTree implements BehaviorTree {
if(Globals.clientState.firstPersonEntity != null){
Actor actor = EntityUtils.getActor(Globals.clientState.firstPersonEntity);
if(
(!actor.isPlayingAnimation() || !actor.isPlayingAnimation(animationName)) &&
(!actor.getAnimationData().isPlayingAnimation() || !actor.getAnimationData().isPlayingAnimation(animationName)) &&
(Globals.assetManager.fetchModel(actor.getBaseModelPath()) != null && Globals.assetManager.fetchModel(actor.getBaseModelPath()).getAnimation(animationName) != null)
){
actor.playAnimation(animationName,priority);
actor.incrementAnimationTime(offset);
actor.getAnimationData().playAnimation(animationName,priority);
actor.getAnimationData().incrementAnimationTime(offset);
}
}
}
@ -132,11 +132,11 @@ public class FirstPersonTree implements BehaviorTree {
if(Globals.clientState.firstPersonEntity != null){
Actor actor = EntityUtils.getActor(Globals.clientState.firstPersonEntity);
if(
(!actor.isPlayingAnimation() || !actor.isPlayingAnimation(animation.getNameFirstPerson())) &&
(!actor.getAnimationData().isPlayingAnimation() || !actor.getAnimationData().isPlayingAnimation(animation.getNameFirstPerson())) &&
(Globals.assetManager.fetchModel(actor.getBaseModelPath()) != null && Globals.assetManager.fetchModel(actor.getBaseModelPath()).getAnimation(animation.getNameFirstPerson()) != null)
){
actor.playAnimation(animation, false);
actor.incrementAnimationTime(offset);
actor.getAnimationData().playAnimation(animation, false);
actor.getAnimationData().incrementAnimationTime(offset);
}
}
}
@ -149,10 +149,10 @@ public class FirstPersonTree implements BehaviorTree {
if(Globals.clientState.firstPersonEntity != null){
Actor actor = EntityUtils.getActor(Globals.clientState.firstPersonEntity);
if(
(actor.isPlayingAnimation() || actor.isPlayingAnimation(animation.getNameFirstPerson())) &&
(actor.getAnimationData().isPlayingAnimation() || actor.getAnimationData().isPlayingAnimation(animation.getNameFirstPerson())) &&
(Globals.assetManager.fetchModel(actor.getBaseModelPath()) != null && Globals.assetManager.fetchModel(actor.getBaseModelPath()).getAnimation(animation.getNameFirstPerson()) != null)
){
actor.interruptAnimation(animation, false);
actor.getAnimationData().interruptAnimation(animation, false);
}
}
}

View File

@ -361,11 +361,11 @@ public class ClientEquipState implements BehaviorTree {
if(point.getEquippedAnimation() != null){
TreeDataAnimation animation = point.getEquippedAnimation();
//play third person
if(thirdPersonActor.isPlayingAnimation() && thirdPersonActor.isPlayingAnimation(animation)){
if(thirdPersonActor.getAnimationData().isPlayingAnimation() && thirdPersonActor.getAnimationData().isPlayingAnimation(animation)){
if(animation != null){
thirdPersonActor.interruptAnimation(animation,true);
thirdPersonActor.getAnimationData().interruptAnimation(animation,true);
}
thirdPersonActor.incrementAnimationTime(0.0001);
thirdPersonActor.getAnimationData().incrementAnimationTime(0.0001);
}
//play first person
@ -451,11 +451,11 @@ public class ClientEquipState implements BehaviorTree {
if(this.hasEquippedAtPoint(point.getEquipPointId()) && point.getEquippedAnimation() != null){
TreeDataAnimation animation = point.getEquippedAnimation();
//play third person
if(!thirdPersonActor.isPlayingAnimation() || !thirdPersonActor.isPlayingAnimation(animation)){
if(!thirdPersonActor.getAnimationData().isPlayingAnimation() || !thirdPersonActor.getAnimationData().isPlayingAnimation(animation)){
if(animation != null){
thirdPersonActor.playAnimation(animation,true);
thirdPersonActor.getAnimationData().playAnimation(animation,true);
}
thirdPersonActor.incrementAnimationTime(0.0001);
thirdPersonActor.getAnimationData().incrementAnimationTime(0.0001);
}
//play first person

View File

@ -256,11 +256,11 @@ public class ClientToolbarState implements BehaviorTree {
if(targetPoint.getEquippedAnimation() != null){
TreeDataAnimation animation = targetPoint.getEquippedAnimation();
//play third person
if(thirdPersonActor.isPlayingAnimation() && thirdPersonActor.isPlayingAnimation(animation)){
if(thirdPersonActor.getAnimationData().isPlayingAnimation() && thirdPersonActor.getAnimationData().isPlayingAnimation(animation)){
if(animation != null){
thirdPersonActor.interruptAnimation(animation,true);
thirdPersonActor.getAnimationData().interruptAnimation(animation,true);
}
thirdPersonActor.incrementAnimationTime(0.0001);
thirdPersonActor.getAnimationData().incrementAnimationTime(0.0001);
}
//play first person

View File

@ -339,8 +339,8 @@ public class HitboxCollectionState {
}
PhysicsUtils.setRigidBodyTransform(collisionEngine, entityPosition, new Quaterniond(), body);
for(String boneName : this.boneHitboxMap.keySet()){
if(EntityUtils.getActor(parent).containsBone(boneName)){
Vector3d bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName);
if(EntityUtils.getActor(parent).getAnimationData().containsBone(boneName)){
Vector3d bonePosition = EntityUtils.getActor(parent).getAnimationData().getBonePosition(boneName);
for(HitboxState state : this.boneHitboxMap.get(boneName)){
DGeom geom = this.stateGeomMap.get(state);
HitboxState shapeStatus = this.geomStateMap.get(geom);

View File

@ -78,15 +78,15 @@ public class ClientIdleTree implements BehaviorTree {
if(entityActor != null){
if(
idleData != null &&
(!entityActor.isPlayingAnimation() || !entityActor.isPlayingAnimation(idleData.getAnimation())) &&
(!entityActor.getAnimationData().isPlayingAnimation() || !entityActor.getAnimationData().isPlayingAnimation(idleData.getAnimation())) &&
(
Globals.assetManager.fetchModel(entityActor.getBaseModelPath()) != null &&
Globals.assetManager.fetchModel(entityActor.getBaseModelPath()).getAnimation(idleData.getAnimation().getNameThirdPerson()) != null
)
){
entityActor.playAnimation(idleData.getAnimation(),true);
entityActor.incrementAnimationTime(0.0001);
entityActor.getAnimationData().playAnimation(idleData.getAnimation(),true);
entityActor.getAnimationData().incrementAnimationTime(0.0001);
}
if(idleData != null){
FirstPersonTree.conditionallyPlayAnimation(parent, idleData.getAnimation());

View File

@ -120,7 +120,7 @@ public class ClientFallTree implements BehaviorTree {
boolean isPlayingJump = false;
Actor entityActor = EntityUtils.getActor(parent);
if(entityActor != null && ClientJumpTree.getClientJumpTree(parent) != null){
isPlayingJump = entityActor.isPlayingAnimation(ClientJumpTree.getClientJumpTree(parent).getJumpData().getAnimationJump());
isPlayingJump = entityActor.getAnimationData().isPlayingAnimation(ClientJumpTree.getClientJumpTree(parent).getJumpData().getAnimationJump());
}
boolean rVal =
frameCurrent > ServerFallTree.MIN_FRAMES_BEFORE_ACTIVATION_SCAN &&
@ -141,10 +141,10 @@ public class ClientFallTree implements BehaviorTree {
Actor entityActor = EntityUtils.getActor(parent);
if(entityActor != null){
if(
!entityActor.isPlayingAnimation() || !entityActor.isPlayingAnimation(fallMovementSystem.getLandState().getAnimation().getNameThirdPerson())
!entityActor.getAnimationData().isPlayingAnimation() || !entityActor.getAnimationData().isPlayingAnimation(fallMovementSystem.getLandState().getAnimation().getNameThirdPerson())
){
entityActor.playAnimation(fallMovementSystem.getLandState().getAnimation().getNameThirdPerson(),AnimationPriorities.getValue(AnimationPriorities.MOVEMENT_MODIFIER));
entityActor.incrementAnimationTime(0.0001);
entityActor.getAnimationData().playAnimation(fallMovementSystem.getLandState().getAnimation().getNameThirdPerson(),AnimationPriorities.getValue(AnimationPriorities.MOVEMENT_MODIFIER));
entityActor.getAnimationData().incrementAnimationTime(0.0001);
}
FirstPersonTree.conditionallyPlayAnimation(parent, fallMovementSystem.getLandState().getAnimation());
}

View File

@ -309,9 +309,9 @@ public class ClientGroundMovementTree implements BehaviorTree {
//play animation
String animationToPlay = determineCorrectAnimation(MovementTreeState.STARTUP);
if(entityActor != null){
if(!entityActor.isPlayingAnimation(animationToPlay)){
entityActor.playAnimation(animationToPlay,AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
entityActor.incrementAnimationTime(0.0001);
if(!entityActor.getAnimationData().isPlayingAnimation(animationToPlay)){
entityActor.getAnimationData().playAnimation(animationToPlay,AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
entityActor.getAnimationData().incrementAnimationTime(0.0001);
//reset footstep tracking
this.playedFootstepFirst = false;
this.playedFootstepSecond = false;
@ -319,7 +319,7 @@ public class ClientGroundMovementTree implements BehaviorTree {
FirstPersonTree.conditionallyPlayAnimation(parent, groundMovementData.getAnimationStartup().getNameFirstPerson(), AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
}
//conditionally play footstep audio
this.playFootstepAudio(0,entityActor.getAnimationTime(animationToPlay),position);
this.playFootstepAudio(0,entityActor.getAnimationData().getAnimationTime(animationToPlay),position);
this.updateVelocity();
float velocity = this.getModifiedVelocity();
@ -347,9 +347,9 @@ public class ClientGroundMovementTree implements BehaviorTree {
//play animation
String animationToPlay = determineCorrectAnimation(MovementTreeState.MOVE);
if(entityActor != null){
if(!entityActor.isPlayingAnimation(animationToPlay)){
entityActor.playAnimation(animationToPlay,AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
entityActor.incrementAnimationTime(0.0001);
if(!entityActor.getAnimationData().isPlayingAnimation(animationToPlay)){
entityActor.getAnimationData().playAnimation(animationToPlay,AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
entityActor.getAnimationData().incrementAnimationTime(0.0001);
//reset footstep tracking
this.playedFootstepFirst = false;
this.playedFootstepSecond = false;
@ -358,7 +358,7 @@ public class ClientGroundMovementTree implements BehaviorTree {
}
//conditionally play footstep audio
this.playFootstepAudio(0,entityActor.getAnimationTime(animationToPlay),position);
this.playFootstepAudio(0,entityActor.getAnimationData().getAnimationTime(animationToPlay),position);
this.updateVelocity();
float velocity = this.getModifiedVelocity();
@ -380,20 +380,20 @@ public class ClientGroundMovementTree implements BehaviorTree {
String animationToPlay = determineCorrectAnimation(MovementTreeState.SLOWDOWN);
if(entityActor != null){
//play animations
if(!entityActor.isPlayingAnimation(animationToPlay)){
entityActor.playAnimation(animationToPlay,AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
entityActor.incrementAnimationTime(0.0001);
if(!entityActor.getAnimationData().isPlayingAnimation(animationToPlay)){
entityActor.getAnimationData().playAnimation(animationToPlay,AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
entityActor.getAnimationData().incrementAnimationTime(0.0001);
//reset footstep tracking
this.playedFootstepFirst = false;
this.playedFootstepSecond = false;
}
FirstPersonTree.conditionallyPlayAnimation(parent, groundMovementData.getAnimationWindDown().getNameFirstPerson(), AnimationPriorities.getValue(AnimationPriorities.CORE_MOVEMENT));
if(entityActor.isPlayingAnimation(determineCorrectAnimation(MovementTreeState.MOVE))){
entityActor.stopAnimation(determineCorrectAnimation(MovementTreeState.MOVE));
if(entityActor.getAnimationData().isPlayingAnimation(determineCorrectAnimation(MovementTreeState.MOVE))){
entityActor.getAnimationData().stopAnimation(determineCorrectAnimation(MovementTreeState.MOVE));
}
}
//conditionally play footstep audio
this.playFootstepAudio(0,entityActor.getAnimationTime(animationToPlay),position);
this.playFootstepAudio(0,entityActor.getAnimationData().getAnimationTime(animationToPlay),position);
//velocity stuff
this.updateVelocity();
@ -404,8 +404,8 @@ public class ClientGroundMovementTree implements BehaviorTree {
state = MovementTreeState.IDLE;
if(entityActor != null){
animationToPlay = determineCorrectAnimation(MovementTreeState.SLOWDOWN);
if(entityActor.isPlayingAnimation() && entityActor.isPlayingAnimation(animationToPlay)){
entityActor.stopAnimation(animationToPlay);
if(entityActor.getAnimationData().isPlayingAnimation() && entityActor.getAnimationData().isPlayingAnimation(animationToPlay)){
entityActor.getAnimationData().stopAnimation(animationToPlay);
}
}
CreatureUtils.setVelocity(parent, velocity);

View File

@ -95,9 +95,9 @@ public class ClientJumpTree implements BehaviorTree {
switch(state){
case ACTIVE: {
if(entityActor != null){
if(!entityActor.isPlayingAnimation() || !entityActor.isPlayingAnimation(jumpData.getAnimationJump().getNameThirdPerson())){
entityActor.playAnimation(jumpData.getAnimationJump().getNameThirdPerson(),AnimationPriorities.getValue(AnimationPriorities.MOVEMENT_MODIFIER));
entityActor.incrementAnimationTime(0.0001);
if(!entityActor.getAnimationData().isPlayingAnimation() || !entityActor.getAnimationData().isPlayingAnimation(jumpData.getAnimationJump().getNameThirdPerson())){
entityActor.getAnimationData().playAnimation(jumpData.getAnimationJump().getNameThirdPerson(),AnimationPriorities.getValue(AnimationPriorities.MOVEMENT_MODIFIER));
entityActor.getAnimationData().incrementAnimationTime(0.0001);
}
FirstPersonTree.conditionallyPlayAnimation(parent, jumpData.getAnimationJump());
}

View File

@ -42,7 +42,7 @@ public class RotatorTree implements BehaviorTree{
state = RotatorTreeState.INACTIVE;
//clear all modifications we've made up to this point
for(RotatorHierarchyNode node : nodes){
entityActor.getBoneRotator(node.getBone()).getRotation().identity();
entityActor.getAnimationData().getBoneRotator(node.getBone()).getRotation().identity();
}
}
}
@ -70,7 +70,7 @@ public class RotatorTree implements BehaviorTree{
// currentRotation.
}
if(followsView){
ActorBoneRotator currentRotator = entityActor.getBoneRotator(node.getBone());
ActorBoneRotator currentRotator = entityActor.getAnimationData().getBoneRotator(node.getBone());
//apparently this isn't needed?
//not sure I understand the math on this one
// Vector3d facingVector = CreatureUtils.getFacingVector(parent);

View File

@ -380,7 +380,7 @@ public class CommonEntityUtils {
for(RotatorItem item : system.getRotatorItems()){
//put actor rotator
ActorBoneRotator newRotator = new ActorBoneRotator();
creatureActor.addBoneRotator(item.getBoneName(), newRotator);
creatureActor.getAnimationData().addBoneRotator(item.getBoneName(), newRotator);
//construct node for tree
RotatorHierarchyNode hierarchyNode = new RotatorHierarchyNode();
hierarchyNode.setBone(item.getBoneName());
@ -394,7 +394,7 @@ public class CommonEntityUtils {
}
//bone groups
if(rawType.getBoneGroups() != null){
creatureActor.setBoneGroups(rawType.getBoneGroups());
creatureActor.getAnimationData().setBoneGroups(rawType.getBoneGroups());
}
//grid alignment
if(rawType.getGridAlignedData() != null){

View File

@ -107,7 +107,7 @@ public class CreatureUtils {
if(attributeType.getType().equals("bone")){
if(staticMorph == null){
staticMorph = new ActorStaticMorph();
creatureActor.setActorStaticMorph(staticMorph);
creatureActor.getAnimationData().setActorStaticMorph(staticMorph);
}
if(attributeType.getPrimaryBone() != null && staticMorph.getBoneTransforms(attributeType.getPrimaryBone()) == null){
staticMorph.initBoneTransforms(attributeType.getPrimaryBone());
@ -380,7 +380,7 @@ public class CreatureUtils {
Actor creatureActor = EntityUtils.getActor(rVal);
if(rawType.getBoneGroups() != null){
creatureActor.setBoneGroups(rawType.getBoneGroups());
creatureActor.getAnimationData().setBoneGroups(rawType.getBoneGroups());
}
return rVal;

View File

@ -331,9 +331,9 @@ public class ItemUtils {
Actor actor = EntityUtils.getActor(item);
if(actor != null && item.getData(EntityDataStrings.ANIM_IDLE) != null){
String idleAnim = (String)item.getData(EntityDataStrings.ANIM_IDLE);
if(!actor.isPlayingAnimation(idleAnim)){
actor.playAnimation(idleAnim,AnimationPriorities.getValue(AnimationPriorities.INTERACTION));
actor.incrementAnimationTime(0.0001);
if(!actor.getAnimationData().isPlayingAnimation(idleAnim)){
actor.getAnimationData().playAnimation(idleAnim,AnimationPriorities.getValue(AnimationPriorities.INTERACTION));
actor.getAnimationData().incrementAnimationTime(0.0001);
}
}
}

View File

@ -1,34 +1,25 @@
package electrosphere.renderer.actor;
import electrosphere.data.entity.common.treedata.TreeDataAnimation;
import electrosphere.data.entity.creature.bonegroups.BoneGroup;
import electrosphere.engine.Globals;
import electrosphere.entity.state.AnimationPriorities;
import electrosphere.logger.LoggerInterface;
import electrosphere.mem.JomlPool;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.actor.mask.ActorAnimationMaskEntry;
import electrosphere.renderer.actor.mask.ActorAnimationData;
import electrosphere.renderer.actor.mask.ActorMeshMask;
import electrosphere.renderer.actor.mask.ActorShaderMask;
import electrosphere.renderer.actor.mask.ActorTextureMask;
import electrosphere.renderer.actor.mask.ActorUniformMap;
import electrosphere.renderer.actor.mask.ActorUniformMap.UniformValue;
import electrosphere.renderer.model.Bone;
import electrosphere.renderer.model.Model;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Sphered;
import org.joml.Vector3d;
import org.joml.Vector4d;
/**
* An actor
@ -43,11 +34,6 @@ import org.joml.Vector4d;
*/
public class Actor {
/**
* Returned when the current time is requested of an animation that the actor is not playing
*/
public static final int INVALID_ANIMATION = -1;
/**
* full-resolution lod level
*/
@ -83,11 +69,6 @@ public class Actor {
*/
private boolean frustumCull = true;
/**
* scales the time that animations are played at
*/
private float animationScalar = 1.0f;
/**
* Sets scaling applied at the actor level
*/
@ -129,9 +110,9 @@ public class Actor {
private String lowResBaseModelPath;
/**
* The stack of animations being applied to a given actor
* The animation data for the actor
*/
private Set<ActorAnimationMaskEntry> animationQueue = new TreeSet<ActorAnimationMaskEntry>();
private final ActorAnimationData animationData;
/**
* Mask for overwriting meshes in a given actor
@ -148,49 +129,10 @@ public class Actor {
*/
private Map<String,ActorTextureMask> textureMap = null;
/**
* bone rotators
*/
private Map<String,ActorBoneRotator> boneRotators = new HashMap<String,ActorBoneRotator>();
/**
* static morph for this specific actor
*/
private ActorStaticMorph staticMorph;
/**
* The list of bone groups
*/
private List<BoneGroup> boneGroups;
/**
* A map of mesh -> uniforms to apply to the mesh
*/
private ActorUniformMap uniformMap = new ActorUniformMap();
/**
* Used for caching animation masks that should be removed
*/
private List<ActorAnimationMaskEntry> toRemoveMasks = new LinkedList<ActorAnimationMaskEntry>();
//
//
// DATA CACHING
//
//
/**
* Stores the positions of bones as they are updated
*/
private Map<String,Vector3d> bonePositionMap = new HashMap<String,Vector3d>();
/**
* Stores the rotations of bones as they are updated
*/
private Map<String,Quaterniond> boneRotationMap = new HashMap<String,Quaterniond>();
@ -208,354 +150,7 @@ public class Actor {
*/
public Actor(String modelPath){
this.baseModelPath = modelPath;
}
/**
* Increments the animation time of the actor
* @param deltaTime The amount of time to increment by
*/
public void incrementAnimationTime(double deltaTime){
toRemoveMasks.clear();
for(ActorAnimationMaskEntry mask : animationQueue){
if(mask.getFreezeFrames() > 0){
mask.setFreezeFrames(mask.getFreezeFrames() - 1);
} else {
mask.setTime(mask.getTime() + deltaTime * animationScalar);
if(mask.getTime() > mask.getDuration()){
toRemoveMasks.add(mask);
}
}
}
for(ActorAnimationMaskEntry mask : toRemoveMasks){
animationQueue.remove(mask);
}
}
/**
* Gets the current time of the given animation that is being played on this actor
* @param animation The animation's name
* @return The time into the animation, -1 if the animation is not being played
*/
public double getAnimationTime(String animation){
ActorAnimationMaskEntry mask = this.getAnimationMask(animation);
if(mask != null){
return mask.getTime();
}
return INVALID_ANIMATION;
}
/**
* Checks if an animation is being played on any meshes
* @param animationName The animation name
* @return true if the animation is being played on any meshes, false if the provided animation name is null or the animation is not being played on any meshes
*/
public boolean isPlayingAnimation(String animationName){
if(animationName == null){
return false;
}
for(ActorAnimationMaskEntry mask : animationQueue){
if(mask.getAnimationName().equals(animationName)){
return true;
}
}
return false;
}
/**
* Checks if an animation is being played on any meshes
* @param animationData The animation data
* @return true if the animation is being played on any meshes, false if the provided animation name is null or the animation is not being played on any meshes
*/
public boolean isPlayingAnimation(TreeDataAnimation animationData){
if(animationData == null){
return false;
}
for(ActorAnimationMaskEntry mask : animationQueue){
if(animationData.getNameFirstPerson() != null && mask.getAnimationName().contains(animationData.getNameFirstPerson())){
return true;
}
if(animationData.getNameThirdPerson() != null && mask.getAnimationName().contains(animationData.getNameThirdPerson())){
return true;
}
}
return false;
}
/**
* Checks if the actor is playing an animation
* @return true if it is playing an animation, false otherwise
*/
public boolean isPlayingAnimation(){
return animationQueue.size() > 0;
}
/**
* Stops playing an animation on the actor
* @param animationName The name of the animation
*/
public void stopAnimation(String animationName){
List<ActorAnimationMaskEntry> toRemove = new LinkedList<ActorAnimationMaskEntry>();
for(ActorAnimationMaskEntry mask : animationQueue){
if(mask.getAnimationName().contains(animationName)){
toRemove.add(mask);
}
}
for(ActorAnimationMaskEntry mask : toRemove){
animationQueue.remove(mask);
}
}
/**
* Plays an animation provided as a string with a given priority
* @param animationName The name of the animation
* @param priority The priority of the animation
*/
public void playAnimation(String animationName, int priority){
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null && model.getAnimation(animationName) != null){
double length = model.getAnimation(animationName).duration;
ActorAnimationMaskEntry animMask = new ActorAnimationMaskEntry(priority, animationName, 0, length);
for(Bone bone : model.getBones()){
animMask.addBone(bone.boneID);
}
toRemoveMasks.clear();
for(ActorAnimationMaskEntry currentMask : animationQueue){
if(currentMask.getPriority() == animMask.getPriority()){
toRemoveMasks.add(currentMask);
break;
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
animationQueue.add(animMask);
}
}
/**
* Plays animation data
* @param animation The animation data
* @param isThirdPerson true if is third person, false if is first person
*/
public void playAnimation(TreeDataAnimation animation, boolean isThirdPerson){
//Get the animation's name
String animationName = "";
if(isThirdPerson){
animationName = animation.getNameThirdPerson();
} else {
animationName = animation.getNameFirstPerson();
}
//Get the animation's priority
int priority = AnimationPriorities.getValue(AnimationPriorities.DEFAULT);
if(animation.getPriority() != null){
priority = animation.getPriority();
}
if(animation.getPriorityCategory() != null){
priority = AnimationPriorities.getValue(animation.getPriorityCategory());
}
//Gets the mask
List<String> boneMask = null;
if(animation.getBoneGroups() != null && this.boneGroups != null){
boneMask = new LinkedList<String>();
for(String boneGroupName : animation.getBoneGroups()){
BoneGroup group = null;
for(BoneGroup currentGroup : this.boneGroups){
if(currentGroup.getId().equals(boneGroupName)){
group = currentGroup;
break;
}
}
if(group != null && isThirdPerson == true && group.getBoneNamesThirdPerson() != null && group.getBoneNamesThirdPerson().size() > 0){
boneMask.addAll(group.getBoneNamesThirdPerson());
}
if(group != null && isThirdPerson == false && group.getBoneNamesFirstPerson() != null && group.getBoneNamesFirstPerson().size() > 0){
boneMask.addAll(group.getBoneNamesFirstPerson());
}
}
} else if(animation.getBoneGroups() != null && this.boneGroups == null){
LoggerInterface.loggerRenderer.WARNING(
"Trying to play animation on Actor that uses bone groups, but the Actor's bone group isn't defined!\n" +
"Model path: " + baseModelPath + "\n" +
"Animation name: " + animationName + "\n"
);
} else if(animation.getBoneGroups() == null){
Model model = Globals.assetManager.fetchModel(this.baseModelPath);
if(model != null){
boneMask = new LinkedList<String>();
for(Bone bone : model.getBones()){
boneMask.add(bone.boneID);
}
}
}
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null && model.getAnimation(animationName) != null){
//get data from the actual animation in the model
double length = model.getAnimation(animationName).duration;
//construct the animation mask
ActorAnimationMaskEntry animMask;
if(boneMask == null){
animMask = new ActorAnimationMaskEntry(
priority,
animationName,
length
);
} else {
animMask = new ActorAnimationMaskEntry(
priority,
animationName,
length,
boneMask
);
}
//if a mask wasn't defined, apply this mask to all animations
if(boneMask == null){
for(Bone bone : model.getBones()){
animMask.addBone(bone.boneID);
}
}
//clear existing masks that are lower priority
toRemoveMasks.clear();
for(ActorAnimationMaskEntry currentMask : animationQueue){
if(currentMask.getPriority() == animMask.getPriority()){
toRemoveMasks.add(currentMask);
break;
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
animationQueue.add(animMask);
}
}
/**
* Interrupts an animation, thereby causing it to stop playing
* @param animation The animation to interrupt
*/
public void interruptAnimation(TreeDataAnimation animation, boolean isThirdPerson){
//Get the animation's name
String animationName = "";
if(isThirdPerson){
animationName = animation.getNameThirdPerson();
} else {
animationName = animation.getNameFirstPerson();
}
//Get the animation's priority
int priority = AnimationPriorities.getValue(AnimationPriorities.DEFAULT);
if(animation.getPriority() != null){
priority = animation.getPriority();
}
if(animation.getPriorityCategory() != null){
priority = AnimationPriorities.getValue(animation.getPriorityCategory());
}
toRemoveMasks.clear();
for(ActorAnimationMaskEntry mask : this.animationQueue){
if(mask.getAnimationName() == animationName && mask.getPriority() == priority){
toRemoveMasks.add(mask);
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
}
public void playAnimationWithMask(String animationName, int priority, List<String> boneMask){
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null){
double length = model.getAnimation(animationName).duration;
ActorAnimationMaskEntry animMask = new ActorAnimationMaskEntry(priority, animationName, 0, length, boneMask);
toRemoveMasks.clear();
for(ActorAnimationMaskEntry currentMask : animationQueue){
if(currentMask.getPriority() == animMask.getPriority()){
toRemoveMasks.add(currentMask);
break;
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
animationQueue.add(animMask);
}
}
/**
* Gets the animation queue
* @return The animation queue
*/
public Set<ActorAnimationMaskEntry> getAnimationQueue(){
return animationQueue;
}
/**
* Applies the animation masks in this actor to the provided model
* @param model The model
*/
private void applyAnimationMasks(Model model){
List<String> bonesUsed = new LinkedList<String>();
List<String> currentAnimationMask = new LinkedList<String>();
for(ActorAnimationMaskEntry mask : animationQueue){
currentAnimationMask.clear();
for(String currentBone : mask.getBones()){
if(!bonesUsed.contains(currentBone)){
bonesUsed.add(currentBone);
currentAnimationMask.add(currentBone);
}
}
model.applyAnimationMask(mask.getAnimationName(), mask.getTime(), currentAnimationMask);
}
}
/**
* Calculates the node transforms for the actor
* @param model The model that backs the actor
*/
private void calculateNodeTransforms(Model model){
model.updateNodeTransform(boneRotators,staticMorph);
for(Bone bone : model.getBones()){
//store position
Matrix4d betweenMat = new Matrix4d(bone.getMOffset()).invert();
Vector4d result = betweenMat.transform(new Vector4d(0,0,0,1));
betweenMat.set(bone.getFinalTransform());
result = betweenMat.transform(result);
this.bonePositionMap.put(bone.boneID,new Vector3d(result.x,result.y,result.z));
//store rotation
Quaterniond rotation = new Matrix4d(bone.getFinalTransform()).getNormalizedRotation(new Quaterniond());
this.boneRotationMap.put(bone.boneID,rotation);
}
}
public void setAnimationScalar(float animationScalar) {
this.animationScalar = animationScalar;
}
/**
* Gets the animation mask for a given animation
* @param animationName The animation's name
* @return The animation mask if the actor is playing the animation, null otherwise
*/
public ActorAnimationMaskEntry getAnimationMask(String animationName){
for(ActorAnimationMaskEntry mask : this.getAnimationQueue()){
if(mask.getAnimationName().equals(animationName)){
return mask;
} else if(mask.getAnimationName().equalsIgnoreCase(animationName)){
LoggerInterface.loggerEngine.WARNING("Animation mask failed to find, but there is an animation with a very similar name! " + animationName + " vs " + mask.getAnimationName());
}
}
return null;
this.animationData = new ActorAnimationData(this);
}
/**
@ -594,7 +189,7 @@ public class Actor {
//frustum cull then draw
if(Actor.isWithinFrustumBox(renderPipelineState,model,frustumCull)){
this.applyAnimationMasks(model);
this.animationData.applyAnimationMasks(model);
meshMask.processMeshMaskQueue();
model.setMeshMask(meshMask);
model.setTextureMask(textureMap);
@ -603,7 +198,7 @@ public class Actor {
model.getShaderMask().put(shaderMask.getMeshName(),shaderMask);
}
}
this.calculateNodeTransforms(model);
this.animationData.calculateNodeTransforms(model);
//apply uniform overrides
if(this.uniformMap.getMeshes() != null && this.uniformMap.getMeshes().size() > 0){
for(String meshName : this.uniformMap.getMeshes()){
@ -635,12 +230,12 @@ public class Actor {
this.lodLevel == Actor.LOD_LEVEL_STATIC ||
//actor doesn't have anything complicated render-wise (animations, custom textures, etc)
(
this.animationQueue.size() == 0 &&
this.animationData.isPlayingAnimation() &&
this.meshMask.getBlockedMeshes().size() == 0 &&
this.textureMap == null &&
this.shaderMasks.size() == 0 &&
this.uniformMap.isEmpty() &&
this.staticMorph == null
this.animationData.getStaticMorph() == null
)
;
}
@ -657,12 +252,12 @@ public class Actor {
public String getStatisDrawStatus(){
String rVal = "";
rVal = rVal + this.isStaticDrawCall() + "\n";
rVal = rVal + (this.animationQueue.size() == 0) + "\n";
rVal = rVal + this.animationData.isPlayingAnimation() + "\n";
rVal = rVal + (this.meshMask.getBlockedMeshes().size() == 0) + "\n";
rVal = rVal + (this.textureMap == null) + "\n";
rVal = rVal + (this.shaderMasks.size() == 0) + "\n";
rVal = rVal + this.uniformMap.isEmpty() + "\n";
rVal = rVal + (this.staticMorph == null) + "\n";
rVal = rVal + (this.animationData.getStaticMorph() == null) + "\n";
return rVal;
}
@ -697,130 +292,6 @@ public class Actor {
public String getBaseModelPath(){
return baseModelPath;
}
/**
* Gets the position of a bone in local space
* @param boneName The name of the bone
* @return The vector3d containing the position of the bone, or a vector of (0,0,0) if the model lookup fails
* //TODO: refactor to make failure more transparent (both for model not existing and bone not existing)
*/
public Vector3d getBonePosition(String boneName){
if(bonePositionMap.containsKey(boneName)){
return bonePositionMap.get(boneName);
}
Vector3d rVal = new Vector3d();
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null){
this.applyAnimationMasks(model);
this.calculateNodeTransforms(model);
Bone currentBone = model.getBoneMap().get(boneName);
if(currentBone != null){
Matrix4d betweenMat = new Matrix4d(currentBone.getMOffset()).invert();
Vector4d result = betweenMat.transform(new Vector4d(rVal.x,rVal.y,rVal.z,1));
betweenMat.set(currentBone.getFinalTransform());
result = betweenMat.transform(result);
rVal.x = (float)result.x;
rVal.y = (float)result.y;
rVal.z = (float)result.z;
} else {
String message = "Trying to get position of bone that does not exist on model!\n" +
"boneName: " + boneName;
throw new Error(message);
}
}
if(!Double.isFinite(rVal.x)){
throw new Error("Bone position that is not finite!");
}
return rVal;
}
/**
* Gets the rotation of a bone in local space
* @param boneName The name of the bone
* @return The Quaterniond containing the rotation of the bone, or an identity Quaterniond if the lookup fails
*/
public Quaterniond getBoneRotation(String boneName){
if(boneRotationMap.containsKey(boneName)){
return boneRotationMap.get(boneName);
}
Quaterniond rVal = new Quaterniond();
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null){
this.applyAnimationMasks(model);
this.calculateNodeTransforms(model);
Bone currentBone = model.getBoneMap().get(boneName);
if(currentBone != null){
Quaterniond rotation = new Matrix4d(currentBone.getFinalTransform()).getNormalizedRotation(new Quaterniond());
rVal.set(rotation);
} else {
String message = "Trying to get rotation of bone that does not exist on model!\n" +
"boneName: " + boneName;
throw new IllegalArgumentException(message);
}
}
if(!Double.isFinite(rVal.x)){
throw new IllegalStateException("Bone rotation that is not finite!");
}
return rVal;
}
public Matrix4d getBoneTransform(String boneName){
Matrix4d rVal = new Matrix4d();
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null){
applyAnimationMasks(model);
calculateNodeTransforms(model);
Bone currentBone = model.getBoneMap().get(boneName);
if(currentBone != null){
rVal.set(currentBone.getFinalTransform());
} else {
throw new IllegalArgumentException("Trying to get rotation of bone that does not exist on model!");
}
}
if(!Double.isFinite(rVal.m00())){
throw new IllegalStateException("Bone rotation that is not finite!");
}
return rVal;
}
/**
* Gets the list of all bones
* @return the list of all bones
*/
public List<Bone> getBoneValues(){
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null){
applyAnimationMasks(model);
calculateNodeTransforms(model);
return model.getBones();
}
return null;
}
/**
* Gets the value of a single bone
* @return the bone
*/
public Bone getBone(String boneName){
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null){
return model.getBoneMap().get(boneName);
}
return null;
}
/**
* Checks if the actor contains a bone
* @param boneName The name of the bone
* @return true if it exists, false otherwise
*/
public boolean containsBone(String boneName){
Model model = Globals.assetManager.fetchModel(baseModelPath);
if(model != null){
return model.getBoneMap().containsKey(boneName);
}
return false;
}
/**
* Checks if the model is loaded for this actor
@ -872,25 +343,6 @@ public class Actor {
textureMap.put(textureMask.getMeshName(),textureMask);
}
/**
* Adds a rotator to a bone on this actor
* @param bone The name of the bone
* @param rotator The rotator
*/
public void addBoneRotator(String bone, ActorBoneRotator rotator){
boneRotators.put(bone, rotator);
}
/**
* Gets the rotator to apply to a bone
* @param bone The name of the bone
* @return The rotator to apply to that bone if it exists, null otherwise
*/
public ActorBoneRotator getBoneRotator(String bone){
return boneRotators.get(bone);
}
/**
* Sets the value of a uniform on a given mesh within this actor
* @param meshName The name of the mesh
@ -900,22 +352,6 @@ public class Actor {
public void setUniformOnMesh(String meshName, String uniformName, Object value){
this.uniformMap.setUniform(meshName, uniformName, value);
}
/**
* Sets the static morph for this actor
* @param staticMorph The static morph
*/
public void setActorStaticMorph(ActorStaticMorph staticMorph){
this.staticMorph = staticMorph;
}
/**
* Gets the static morph for this actor
* @return The static morph for this actor
*/
public ActorStaticMorph getStaticMorph(){
return this.staticMorph;
}
/**
* Sets whether this actor should frustum cull or not
@ -933,31 +369,6 @@ public class Actor {
return frustumCull;
}
/**
* Sets the bone groups in the actor
* @param boneGroups The bone groups
*/
public void setBoneGroups(List<BoneGroup> boneGroups){
this.boneGroups = boneGroups;
}
/**
* Sets the number of freeze frames for a given animation path if the animation is being played
* @param animationPath The path to the animation
* @param numFrames The number of frames to freeze for
*/
public void setFreezeFrames(String animationPath, int numFrames){
if(numFrames < 1){
throw new Error("Num frames less than 1 !" + numFrames);
}
for(ActorAnimationMaskEntry mask : this.animationQueue){
if(mask.getAnimationName().contains(animationPath)){
mask.setFreezeFrames(numFrames);
break;
}
}
}
/**
@ -1047,5 +458,13 @@ public class Actor {
this.lodLevel = lodLevel;
}
/**
* Gets the animation data for the actor
* @return
*/
public ActorAnimationData getAnimationData(){
return this.animationData;
}
}

View File

@ -37,7 +37,7 @@ public class ActorUtils {
*/
public static ActorStaticMorph getStaticMorph(Entity actorEntity){
Actor entityActor = EntityUtils.getActor(actorEntity);
return entityActor.getStaticMorph();
return entityActor.getAnimationData().getStaticMorph();
}
/**

View File

@ -0,0 +1,642 @@
package electrosphere.renderer.actor.mask;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector4d;
import electrosphere.data.entity.common.treedata.TreeDataAnimation;
import electrosphere.data.entity.creature.bonegroups.BoneGroup;
import electrosphere.engine.Globals;
import electrosphere.entity.state.AnimationPriorities;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.ActorBoneRotator;
import electrosphere.renderer.actor.ActorStaticMorph;
import electrosphere.renderer.model.Bone;
import electrosphere.renderer.model.Model;
/**
* The data about animations for a given actor
*/
public class ActorAnimationData {
/**
* Returned when the current time is requested of an animation that the actor is not playing
*/
public static final int INVALID_ANIMATION = -1;
/**
* The parent actor
*/
private final Actor parent;
/**
* scales the time that animations are played at
*/
private float animationScalar = 1.0f;
/**
* The stack of animations being applied to a given actor
*/
private Set<ActorAnimationMaskEntry> animationQueue = new TreeSet<ActorAnimationMaskEntry>();
/**
* Used for caching animation masks that should be removed
*/
private List<ActorAnimationMaskEntry> toRemoveMasks = new LinkedList<ActorAnimationMaskEntry>();
/**
* The list of bone groups
*/
private List<BoneGroup> boneGroups;
/**
* static morph for this specific actor
*/
private ActorStaticMorph staticMorph;
/**
* bone rotators
*/
private Map<String,ActorBoneRotator> boneRotators = new HashMap<String,ActorBoneRotator>();
//
//
// DATA CACHING
//
//
/**
* Stores the positions of bones as they are updated
*/
private Map<String,Vector3d> bonePositionMap = new HashMap<String,Vector3d>();
/**
* Stores the rotations of bones as they are updated
*/
private Map<String,Quaterniond> boneRotationMap = new HashMap<String,Quaterniond>();
/**
* Constructor
* @param parent The parent actor
*/
public ActorAnimationData(Actor parent){
this.parent = parent;
}
/**
* Increments the animation time of the actor
* @param deltaTime The amount of time to increment by
*/
public void incrementAnimationTime(double deltaTime){
toRemoveMasks.clear();
for(ActorAnimationMaskEntry mask : animationQueue){
if(mask.getFreezeFrames() > 0){
mask.setFreezeFrames(mask.getFreezeFrames() - 1);
} else {
mask.setTime(mask.getTime() + deltaTime * animationScalar);
if(mask.getTime() > mask.getDuration()){
toRemoveMasks.add(mask);
}
}
}
for(ActorAnimationMaskEntry mask : toRemoveMasks){
animationQueue.remove(mask);
}
}
/**
* Gets the current time of the given animation that is being played on this actor
* @param animation The animation's name
* @return The time into the animation, -1 if the animation is not being played
*/
public double getAnimationTime(String animation){
ActorAnimationMaskEntry mask = this.getAnimationMask(animation);
if(mask != null){
return mask.getTime();
}
return INVALID_ANIMATION;
}
/**
* Checks if an animation is being played on any meshes
* @param animationName The animation name
* @return true if the animation is being played on any meshes, false if the provided animation name is null or the animation is not being played on any meshes
*/
public boolean isPlayingAnimation(String animationName){
if(animationName == null){
return false;
}
for(ActorAnimationMaskEntry mask : animationQueue){
if(mask.getAnimationName().equals(animationName)){
return true;
}
}
return false;
}
/**
* Checks if an animation is being played on any meshes
* @param animationData The animation data
* @return true if the animation is being played on any meshes, false if the provided animation name is null or the animation is not being played on any meshes
*/
public boolean isPlayingAnimation(TreeDataAnimation animationData){
if(animationData == null){
return false;
}
for(ActorAnimationMaskEntry mask : animationQueue){
if(animationData.getNameFirstPerson() != null && mask.getAnimationName().contains(animationData.getNameFirstPerson())){
return true;
}
if(animationData.getNameThirdPerson() != null && mask.getAnimationName().contains(animationData.getNameThirdPerson())){
return true;
}
}
return false;
}
/**
* Checks if the actor is playing an animation
* @return true if it is playing an animation, false otherwise
*/
public boolean isPlayingAnimation(){
return animationQueue.size() > 0;
}
/**
* Stops playing an animation on the actor
* @param animationName The name of the animation
*/
public void stopAnimation(String animationName){
List<ActorAnimationMaskEntry> toRemove = new LinkedList<ActorAnimationMaskEntry>();
for(ActorAnimationMaskEntry mask : animationQueue){
if(mask.getAnimationName().contains(animationName)){
toRemove.add(mask);
}
}
for(ActorAnimationMaskEntry mask : toRemove){
animationQueue.remove(mask);
}
}
/**
* Plays an animation provided as a string with a given priority
* @param animationName The name of the animation
* @param priority The priority of the animation
*/
public void playAnimation(String animationName, int priority){
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null && model.getAnimation(animationName) != null){
double length = model.getAnimation(animationName).duration;
ActorAnimationMaskEntry animMask = new ActorAnimationMaskEntry(priority, animationName, 0, length);
for(Bone bone : model.getBones()){
animMask.addBone(bone.boneID);
}
toRemoveMasks.clear();
for(ActorAnimationMaskEntry currentMask : animationQueue){
if(currentMask.getPriority() == animMask.getPriority()){
toRemoveMasks.add(currentMask);
break;
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
animationQueue.add(animMask);
}
}
/**
* Plays animation data
* @param animation The animation data
* @param isThirdPerson true if is third person, false if is first person
*/
public void playAnimation(TreeDataAnimation animation, boolean isThirdPerson){
//Get the animation's name
String animationName = "";
if(isThirdPerson){
animationName = animation.getNameThirdPerson();
} else {
animationName = animation.getNameFirstPerson();
}
//Get the animation's priority
int priority = AnimationPriorities.getValue(AnimationPriorities.DEFAULT);
if(animation.getPriority() != null){
priority = animation.getPriority();
}
if(animation.getPriorityCategory() != null){
priority = AnimationPriorities.getValue(animation.getPriorityCategory());
}
//Gets the mask
List<String> boneMask = null;
if(animation.getBoneGroups() != null && this.boneGroups != null){
boneMask = new LinkedList<String>();
for(String boneGroupName : animation.getBoneGroups()){
BoneGroup group = null;
for(BoneGroup currentGroup : this.boneGroups){
if(currentGroup.getId().equals(boneGroupName)){
group = currentGroup;
break;
}
}
if(group != null && isThirdPerson == true && group.getBoneNamesThirdPerson() != null && group.getBoneNamesThirdPerson().size() > 0){
boneMask.addAll(group.getBoneNamesThirdPerson());
}
if(group != null && isThirdPerson == false && group.getBoneNamesFirstPerson() != null && group.getBoneNamesFirstPerson().size() > 0){
boneMask.addAll(group.getBoneNamesFirstPerson());
}
}
} else if(animation.getBoneGroups() != null && this.boneGroups == null){
LoggerInterface.loggerRenderer.WARNING(
"Trying to play animation on Actor that uses bone groups, but the Actor's bone group isn't defined!\n" +
"Model path: " + parent.getBaseModelPath() + "\n" +
"Animation name: " + animationName + "\n"
);
} else if(animation.getBoneGroups() == null){
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
boneMask = new LinkedList<String>();
for(Bone bone : model.getBones()){
boneMask.add(bone.boneID);
}
}
}
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null && model.getAnimation(animationName) != null){
//get data from the actual animation in the model
double length = model.getAnimation(animationName).duration;
//construct the animation mask
ActorAnimationMaskEntry animMask;
if(boneMask == null){
animMask = new ActorAnimationMaskEntry(
priority,
animationName,
length
);
} else {
animMask = new ActorAnimationMaskEntry(
priority,
animationName,
length,
boneMask
);
}
//if a mask wasn't defined, apply this mask to all animations
if(boneMask == null){
for(Bone bone : model.getBones()){
animMask.addBone(bone.boneID);
}
}
//clear existing masks that are lower priority
toRemoveMasks.clear();
for(ActorAnimationMaskEntry currentMask : animationQueue){
if(currentMask.getPriority() == animMask.getPriority()){
toRemoveMasks.add(currentMask);
break;
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
animationQueue.add(animMask);
}
}
/**
* Interrupts an animation, thereby causing it to stop playing
* @param animation The animation to interrupt
*/
public void interruptAnimation(TreeDataAnimation animation, boolean isThirdPerson){
//Get the animation's name
String animationName = "";
if(isThirdPerson){
animationName = animation.getNameThirdPerson();
} else {
animationName = animation.getNameFirstPerson();
}
//Get the animation's priority
int priority = AnimationPriorities.getValue(AnimationPriorities.DEFAULT);
if(animation.getPriority() != null){
priority = animation.getPriority();
}
if(animation.getPriorityCategory() != null){
priority = AnimationPriorities.getValue(animation.getPriorityCategory());
}
toRemoveMasks.clear();
for(ActorAnimationMaskEntry mask : this.animationQueue){
if(mask.getAnimationName() == animationName && mask.getPriority() == priority){
toRemoveMasks.add(mask);
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
}
/**
* Plays an animation on a specified set of bones
* @param animationName The name of the animation
* @param priority The priority of the animation
* @param boneMask The set of bones to play the animation on
*/
public void playAnimationWithMask(String animationName, int priority, List<String> boneMask){
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
double length = model.getAnimation(animationName).duration;
ActorAnimationMaskEntry animMask = new ActorAnimationMaskEntry(priority, animationName, 0, length, boneMask);
toRemoveMasks.clear();
for(ActorAnimationMaskEntry currentMask : animationQueue){
if(currentMask.getPriority() == animMask.getPriority()){
toRemoveMasks.add(currentMask);
break;
}
}
for(ActorAnimationMaskEntry currentMask : toRemoveMasks){
animationQueue.remove(currentMask);
}
animationQueue.add(animMask);
}
}
/**
* Gets the animation queue
* @return The animation queue
*/
public Set<ActorAnimationMaskEntry> getAnimationQueue(){
return animationQueue;
}
/**
* Applies the animation masks in this actor to the provided model
* @param model The model
*/
public void applyAnimationMasks(Model model){
List<String> bonesUsed = new LinkedList<String>();
List<String> currentAnimationMask = new LinkedList<String>();
for(ActorAnimationMaskEntry mask : animationQueue){
currentAnimationMask.clear();
for(String currentBone : mask.getBones()){
if(!bonesUsed.contains(currentBone)){
bonesUsed.add(currentBone);
currentAnimationMask.add(currentBone);
}
}
model.applyAnimationMask(mask.getAnimationName(), mask.getTime(), currentAnimationMask);
}
}
/**
* Calculates the node transforms for the actor
* @param model The model that backs the actor
*/
public void calculateNodeTransforms(Model model){
model.updateNodeTransform(boneRotators,staticMorph);
for(Bone bone : model.getBones()){
//store position
Matrix4d betweenMat = new Matrix4d(bone.getMOffset()).invert();
Vector4d result = betweenMat.transform(new Vector4d(0,0,0,1));
betweenMat.set(bone.getFinalTransform());
result = betweenMat.transform(result);
this.bonePositionMap.put(bone.boneID,new Vector3d(result.x,result.y,result.z));
//store rotation
Quaterniond rotation = new Matrix4d(bone.getFinalTransform()).getNormalizedRotation(new Quaterniond());
this.boneRotationMap.put(bone.boneID,rotation);
}
}
public void setAnimationScalar(float animationScalar) {
this.animationScalar = animationScalar;
}
/**
* Gets the animation mask for a given animation
* @param animationName The animation's name
* @return The animation mask if the actor is playing the animation, null otherwise
*/
public ActorAnimationMaskEntry getAnimationMask(String animationName){
for(ActorAnimationMaskEntry mask : this.getAnimationQueue()){
if(mask.getAnimationName().equals(animationName)){
return mask;
} else if(mask.getAnimationName().equalsIgnoreCase(animationName)){
LoggerInterface.loggerEngine.WARNING("Animation mask failed to find, but there is an animation with a very similar name! " + animationName + " vs " + mask.getAnimationName());
}
}
return null;
}
/**
* Adds a rotator to a bone on this actor
* @param bone The name of the bone
* @param rotator The rotator
*/
public void addBoneRotator(String bone, ActorBoneRotator rotator){
boneRotators.put(bone, rotator);
}
/**
* Gets the rotator to apply to a bone
* @param bone The name of the bone
* @return The rotator to apply to that bone if it exists, null otherwise
*/
public ActorBoneRotator getBoneRotator(String bone){
return boneRotators.get(bone);
}
/**
* Sets the static morph for this actor
* @param staticMorph The static morph
*/
public void setActorStaticMorph(ActorStaticMorph staticMorph){
this.staticMorph = staticMorph;
}
/**
* Gets the static morph for this actor
* @return The static morph for this actor
*/
public ActorStaticMorph getStaticMorph(){
return this.staticMorph;
}
/**
* Sets the bone groups in the actor
* @param boneGroups The bone groups
*/
public void setBoneGroups(List<BoneGroup> boneGroups){
this.boneGroups = boneGroups;
}
/**
* Sets the number of freeze frames for a given animation path if the animation is being played
* @param animationPath The path to the animation
* @param numFrames The number of frames to freeze for
*/
public void setFreezeFrames(String animationPath, int numFrames){
if(numFrames < 1){
throw new Error("Num frames less than 1 !" + numFrames);
}
for(ActorAnimationMaskEntry mask : this.animationQueue){
if(mask.getAnimationName().contains(animationPath)){
mask.setFreezeFrames(numFrames);
break;
}
}
}
/**
* Gets the position of a bone in local space
* @param boneName The name of the bone
* @return The vector3d containing the position of the bone, or a vector of (0,0,0) if the model lookup fails
* //TODO: refactor to make failure more transparent (both for model not existing and bone not existing)
*/
public Vector3d getBonePosition(String boneName){
if(bonePositionMap.containsKey(boneName)){
return bonePositionMap.get(boneName);
}
Vector3d rVal = new Vector3d();
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
this.applyAnimationMasks(model);
this.calculateNodeTransforms(model);
Bone currentBone = model.getBoneMap().get(boneName);
if(currentBone != null){
Matrix4d betweenMat = new Matrix4d(currentBone.getMOffset()).invert();
Vector4d result = betweenMat.transform(new Vector4d(rVal.x,rVal.y,rVal.z,1));
betweenMat.set(currentBone.getFinalTransform());
result = betweenMat.transform(result);
rVal.x = (float)result.x;
rVal.y = (float)result.y;
rVal.z = (float)result.z;
} else {
String message = "Trying to get position of bone that does not exist on model!\n" +
"boneName: " + boneName;
throw new Error(message);
}
}
if(!Double.isFinite(rVal.x)){
throw new Error("Bone position that is not finite!");
}
return rVal;
}
/**
* Gets the rotation of a bone in local space
* @param boneName The name of the bone
* @return The Quaterniond containing the rotation of the bone, or an identity Quaterniond if the lookup fails
*/
public Quaterniond getBoneRotation(String boneName){
if(boneRotationMap.containsKey(boneName)){
return boneRotationMap.get(boneName);
}
Quaterniond rVal = new Quaterniond();
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
this.applyAnimationMasks(model);
this.calculateNodeTransforms(model);
Bone currentBone = model.getBoneMap().get(boneName);
if(currentBone != null){
Quaterniond rotation = new Matrix4d(currentBone.getFinalTransform()).getNormalizedRotation(new Quaterniond());
rVal.set(rotation);
} else {
String message = "Trying to get rotation of bone that does not exist on model!\n" +
"boneName: " + boneName;
throw new IllegalArgumentException(message);
}
}
if(!Double.isFinite(rVal.x)){
throw new IllegalStateException("Bone rotation that is not finite!");
}
return rVal;
}
/**
* Gets the bone transform for a bone on this actor
* @param boneName The name of the bone
* @return The transform
*/
public Matrix4d getBoneTransform(String boneName){
Matrix4d rVal = new Matrix4d();
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
applyAnimationMasks(model);
calculateNodeTransforms(model);
Bone currentBone = model.getBoneMap().get(boneName);
if(currentBone != null){
rVal.set(currentBone.getFinalTransform());
} else {
throw new IllegalArgumentException("Trying to get rotation of bone that does not exist on model!");
}
}
if(!Double.isFinite(rVal.m00())){
throw new IllegalStateException("Bone rotation that is not finite!");
}
return rVal;
}
/**
* Gets the list of all bones
* @return the list of all bones
*/
public List<Bone> getBoneValues(){
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
applyAnimationMasks(model);
calculateNodeTransforms(model);
return model.getBones();
}
return null;
}
/**
* Gets the value of a single bone
* @return the bone
*/
public Bone getBone(String boneName){
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
return model.getBoneMap().get(boneName);
}
return null;
}
/**
* Checks if the actor contains a bone
* @param boneName The name of the bone
* @return true if it exists, false otherwise
*/
public boolean containsBone(String boneName){
Model model = Globals.assetManager.fetchModel(parent.getBaseModelPath());
if(model != null){
return model.getBoneMap().containsKey(boneName);
}
return false;
}
}

View File

@ -70,7 +70,7 @@ public class DebugBonesPipeline implements RenderPipeline {
Actor targetActor = EntityUtils.getActor(targetEntity);
Model boneModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCYLINDER);
boneModel.getMaterials().get(0).setDiffuse(Globals.assetManager.fetchTexture(AssetDataStrings.TEXTURE_DEFAULT));
for(Bone bone : targetActor.getBoneValues()){
for(Bone bone : targetActor.getAnimationData().getBoneValues()){
Vector3d bonePos = MathBones.getBoneWorldPosition(targetEntity, bone.boneID);
Quaterniond boneRot = MathBones.getBoneWorldRotation(targetEntity, bone.boneID);

View File

@ -226,12 +226,12 @@ public class ActorPanel extends BufferedStandardDrawableContainerElement impleme
Model actorModel = Globals.assetManager.fetchModel(actor.getBaseModelPath());
if(currentAnim != null){
if((!actor.isPlayingAnimation() || !actor.isPlayingAnimation(currentAnim)) &&
if((!actor.getAnimationData().isPlayingAnimation() || !actor.getAnimationData().isPlayingAnimation(currentAnim)) &&
actorModel != null &&
actorModel.getAnimation(currentAnim) != null
){
actor.playAnimation(currentAnim,3);
actor.incrementAnimationTime(0.0001);
actor.getAnimationData().playAnimation(currentAnim,3);
actor.getAnimationData().incrementAnimationTime(0.0001);
}
}
if(!hasOffsetFromBoundingSphere && actorModel != null){

View File

@ -22,7 +22,7 @@ public class MathBones {
*/
public static Vector3d getBoneWorldPosition(Entity actorEntity, String boneName){
Actor actor = EntityUtils.getActor(actorEntity);
Vector3d localPos = new Vector3d(actor.getBonePosition(boneName));
Vector3d localPos = new Vector3d(actor.getAnimationData().getBonePosition(boneName));
//transform bone space
Vector3d position = new Vector3d(localPos);
@ -41,7 +41,7 @@ public class MathBones {
*/
public static Quaterniond getBoneWorldRotation(Entity actorEntity, String boneName){
Actor actor = EntityUtils.getActor(actorEntity);
Quaterniond localRot = actor.getBoneRotation(boneName);
Quaterniond localRot = actor.getAnimationData().getBoneRotation(boneName);
Vector3d facingAngle = CreatureUtils.getFacingVector(actorEntity);
if(facingAngle == null){