//------------------------------------------------------------------------------------- void EntitySimple::playAnimation(Ogre::String name) { if(modelName_ == "ogrehead.mesh") return; if(name != "Die" && mState == 1) name = "Die"; AnimationState* astate = NULL; bool loopplay = true; if(name == "Idle") { srand((unsigned)time(NULL)); if(mState == 3) astate = mAnims[ANIM_BATTLEIDLE1 + (rand() % 1)]; else astate = mAnims[ANIM_IDLE_1 + (rand() % 1)]; assert(mState != 1); } else if(name == "Die") { if(mLastAnimName == name) return; srand((unsigned)time(NULL)); astate = mAnims[ANIM_DIE1 + (rand() % 1)]; loopplay = false; } else if(name == "Attack") { srand((unsigned)time(NULL)); int attackID = ANIM_ATTACK1 + (rand() % 4); astate = mAnims[attackID]; } else { if(mLastAnimName == name) return; for (int i = 0; i < SIMPLE_NUM_ANIMS; i++) { if(name == animNames[i]) { astate = mAnims[i]; break; } } } if(astate == NULL) return; if(mLastAnims) mLastAnims->setEnabled(false); mLastAnimName = name; astate->setLoop(loopplay); astate->setEnabled(true); mLastAnims = astate; }
void AnimationController::Update(float timeStep) { // Loop through animations for (unsigned i = 0; i < animations_.Size();) { AnimationControl& ctrl = animations_[i]; AnimationState* state = GetAnimationState(ctrl.hash_); bool remove = false; if (!state) remove = true; else { // Advance the animation if (ctrl.speed_ != 0.0f) state->AddTime(ctrl.speed_ * timeStep); float targetWeight = ctrl.targetWeight_; float fadeTime = ctrl.fadeTime_; // If non-looped animation at the end, activate autofade as applicable if (!state->IsLooped() && state->GetTime() >= state->GetLength() && ctrl.autoFadeTime_ > 0.0f) { targetWeight = 0.0f; fadeTime = ctrl.autoFadeTime_; } // Process weight fade float currentWeight = state->GetWeight(); if (currentWeight != targetWeight) { if (fadeTime > 0.0f) { float weightDelta = 1.0f / fadeTime * timeStep; if (currentWeight < targetWeight) currentWeight = Min(currentWeight + weightDelta, targetWeight); else if (currentWeight > targetWeight) currentWeight = Max(currentWeight - weightDelta, targetWeight); state->SetWeight(currentWeight); } else state->SetWeight(targetWeight); } // Remove if weight zero and target weight zero if (state->GetWeight() == 0.0f && (targetWeight == 0.0f || fadeTime == 0.0f) && ctrl.removeOnCompletion_) remove = true; } // Decrement the command time-to-live values if (ctrl.setTimeTtl_ > 0.0f) ctrl.setTimeTtl_ = Max(ctrl.setTimeTtl_ - timeStep, 0.0f); if (ctrl.setWeightTtl_ > 0.0f) ctrl.setWeightTtl_ = Max(ctrl.setWeightTtl_ - timeStep, 0.0f); if (remove) { if (state) RemoveAnimationState(state); animations_.Erase(i); MarkNetworkUpdate(); } else ++i; } // Node hierarchy animations need to be applied manually for (Vector<SharedPtr<AnimationState> >::Iterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i) (*i)->Apply(); }
void SkeletalAnimation::CreateScene() { ResourceCache* cache = GetSubsystem<ResourceCache>(); scene_ = new Scene(context_); // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000) // Also create a DebugRenderer component so that we can draw debug geometry scene_->CreateComponent<Octree>(); scene_->CreateComponent<DebugRenderer>(); // Create scene node & StaticModel component for showing a static plane Node* planeNode = scene_->CreateChild("Plane"); planeNode->SetScale(Vector3(100.0f, 1.0f, 100.0f)); StaticModel* planeObject = planeNode->CreateComponent<StaticModel>(); planeObject->SetModel(cache->GetResource<Model>("Models/Plane.mdl")); planeObject->SetMaterial(cache->GetResource<Material>("Materials/StoneTiled.xml")); // Create a Zone component for ambient lighting & fog control Node* zoneNode = scene_->CreateChild("Zone"); Zone* zone = zoneNode->CreateComponent<Zone>(); zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f)); zone->SetAmbientColor(Color(0.15f, 0.15f, 0.15f)); zone->SetFogColor(Color(0.5f, 0.5f, 0.7f)); zone->SetFogStart(100.0f); zone->SetFogEnd(300.0f); // Create a directional light to the world. Enable cascaded shadows on it Node* lightNode = scene_->CreateChild("DirectionalLight"); lightNode->SetDirection(Vector3(0.6f, -1.0f, 0.8f)); Light* light = lightNode->CreateComponent<Light>(); light->SetLightType(LIGHT_DIRECTIONAL); light->SetCastShadows(true); light->SetShadowBias(BiasParameters(0.00025f, 0.5f)); // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance light->SetShadowCascade(CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f)); // Create animated models const unsigned NUM_MODELS = 100; const float MODEL_MOVE_SPEED = 2.0f; const float MODEL_ROTATE_SPEED = 100.0f; const BoundingBox bounds(Vector3(-47.0f, 0.0f, -47.0f), Vector3(47.0f, 0.0f, 47.0f)); for (unsigned i = 0; i < NUM_MODELS; ++i) { Node* modelNode = scene_->CreateChild("Jack"); modelNode->SetPosition(Vector3(Random(90.0f) - 45.0f, 0.0f, Random(90.0f) - 45.0f)); modelNode->SetRotation(Quaternion(0.0f, Random(360.0f), 0.0f)); AnimatedModel* modelObject = modelNode->CreateComponent<AnimatedModel>(); modelObject->SetModel(cache->GetResource<Model>("Models/Jack.mdl")); modelObject->SetMaterial(cache->GetResource<Material>("Materials/Jack.xml")); modelObject->SetCastShadows(true); // Create an AnimationState for a walk animation. Its time position will need to be manually updated to advance the // animation, The alternative would be to use an AnimationController component which updates the animation automatically, // but we need to update the model's position manually in any case Animation* walkAnimation = cache->GetResource<Animation>("Models/Jack_Walk.ani"); AnimationState* state = modelObject->AddAnimationState(walkAnimation); // The state would fail to create (return null) if the animation was not found if (state) { // Enable full blending weight and looping state->SetWeight(1.0f); state->SetLooped(true); state->SetTime(Random(walkAnimation->GetLength())); } // Create our custom Mover component that will move & animate the model during each frame's update Mover* mover = modelNode->CreateComponent<Mover>(); mover->SetParameters(MODEL_MOVE_SPEED, MODEL_ROTATE_SPEED, bounds); } // Create the camera. Limit far clip distance to match the fog cameraNode_ = scene_->CreateChild("Camera"); Camera* camera = cameraNode_->CreateComponent<Camera>(); camera->SetFarClip(300.0f); // Set an initial position for the camera scene node above the plane cameraNode_->SetPosition(Vector3(0.0f, 5.0f, 0.0f)); }
void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& value) { MemoryBuffer buf(value); AnimatedModel* model = GetComponent<AnimatedModel>(); // Check which animations we need to remove HashSet<StringHash> processedAnimations; unsigned numAnimations = buf.ReadVLE(); while (numAnimations--) { String animName = buf.ReadString(); StringHash animHash(animName); processedAnimations.Insert(animHash); // Check if the animation state exists. If not, add new AnimationState* state = GetAnimationState(animHash); if (!state) { Animation* newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(animName); state = AddAnimationState(newAnimation); if (!state) { URHO3D_LOGERROR("Animation update applying aborted due to unknown animation"); return; } } // Check if the internal control structure exists. If not, add new unsigned index; for (index = 0; index < animations_.Size(); ++index) { if (animations_[index].hash_ == animHash) break; } if (index == animations_.Size()) { AnimationControl newControl; newControl.name_ = animName; newControl.hash_ = animHash; animations_.Push(newControl); } unsigned char ctrl = buf.ReadUByte(); state->SetLayer(buf.ReadUByte()); state->SetLooped((ctrl & CTRL_LOOPED) != 0); state->SetBlendMode((ctrl & CTRL_ADDITIVE) != 0 ? ABM_ADDITIVE : ABM_LERP); animations_[index].speed_ = (float)buf.ReadShort() / 2048.0f; // 11 bits of decimal precision, max. 16x playback speed animations_[index].targetWeight_ = (float)buf.ReadUByte() / 255.0f; // 8 bits of decimal precision animations_[index].fadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade if (ctrl & CTRL_STARTBONE) { StringHash boneHash = buf.ReadStringHash(); if (model) state->SetStartBone(model->GetSkeleton().GetBone(boneHash)); } else state->SetStartBone(0); if (ctrl & CTRL_AUTOFADE) animations_[index].autoFadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade else animations_[index].autoFadeTime_ = 0.0f; animations_[index].removeOnCompletion_ = (ctrl & CTRL_REMOVEONCOMPLETION) != 0; if (ctrl & CTRL_SETTIME) { unsigned char setTimeRev = buf.ReadUByte(); unsigned short setTime = buf.ReadUShort(); // Apply set time command only if revision differs if (setTimeRev != animations_[index].setTimeRev_) { state->SetTime(((float)setTime / 65535.0f) * state->GetLength()); animations_[index].setTimeRev_ = setTimeRev; } } if (ctrl & CTRL_SETWEIGHT) { unsigned char setWeightRev = buf.ReadUByte(); unsigned char setWeight = buf.ReadUByte(); // Apply set weight command only if revision differs if (setWeightRev != animations_[index].setWeightRev_) { state->SetWeight((float)setWeight / 255.0f); animations_[index].setWeightRev_ = setWeightRev; } } } // Set any extra animations to fade out for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i) { if (!processedAnimations.Contains(i->hash_)) { i->targetWeight_ = 0.0f; i->fadeTime_ = EXTRA_ANIM_FADEOUT_TIME; } } }
const PODVector<unsigned char>& AnimationController::GetNetAnimationsAttr() const { attrBuffer_.Clear(); AnimatedModel* model = GetComponent<AnimatedModel>(); unsigned validAnimations = 0; for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i) { if (GetAnimationState(i->hash_)) ++validAnimations; } attrBuffer_.WriteVLE(validAnimations); for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i) { AnimationState* state = GetAnimationState(i->hash_); if (!state) continue; unsigned char ctrl = 0; Bone* startBone = state->GetStartBone(); if (state->IsLooped()) ctrl |= CTRL_LOOPED; if (state->GetBlendMode() == ABM_ADDITIVE) ctrl |= CTRL_ADDITIVE; if (startBone && model && startBone != model->GetSkeleton().GetRootBone()) ctrl |= CTRL_STARTBONE; if (i->autoFadeTime_ > 0.0f) ctrl |= CTRL_AUTOFADE; if (i->removeOnCompletion_) ctrl |= CTRL_REMOVEONCOMPLETION; if (i->setTimeTtl_ > 0.0f) ctrl |= CTRL_SETTIME; if (i->setWeightTtl_ > 0.0f) ctrl |= CTRL_SETWEIGHT; attrBuffer_.WriteString(i->name_); attrBuffer_.WriteUByte(ctrl); attrBuffer_.WriteUByte(state->GetLayer()); attrBuffer_.WriteShort((short)Clamp(i->speed_ * 2048.0f, -32767.0f, 32767.0f)); attrBuffer_.WriteUByte((unsigned char)(i->targetWeight_ * 255.0f)); attrBuffer_.WriteUByte((unsigned char)Clamp(i->fadeTime_ * 64.0f, 0.0f, 255.0f)); if (ctrl & CTRL_STARTBONE) attrBuffer_.WriteStringHash(startBone->nameHash_); if (ctrl & CTRL_AUTOFADE) attrBuffer_.WriteUByte((unsigned char)Clamp(i->autoFadeTime_ * 64.0f, 0.0f, 255.0f)); if (ctrl & CTRL_SETTIME) { attrBuffer_.WriteUByte(i->setTimeRev_); attrBuffer_.WriteUShort(i->setTime_); } if (ctrl & CTRL_SETWEIGHT) { attrBuffer_.WriteUByte(i->setWeightRev_); attrBuffer_.WriteUByte(i->setWeight_); } } return attrBuffer_.GetBuffer(); }
float AnimationController::GetLength(const String& name) const { AnimationState* state = GetAnimationState(name); return state ? state->GetLength() : 0.0f; }
AnimationBlendMode AnimationController::GetBlendMode(const String& name) const { AnimationState* state = GetAnimationState(name); return state ? state->GetBlendMode() : ABM_LERP; }
bool AnimationController::IsLooped(const String& name) const { AnimationState* state = GetAnimationState(name); return state ? state->IsLooped() : false; }
Bone* AnimationController::GetStartBone(const String& name) const { AnimationState* state = GetAnimationState(name); return state ? state->GetStartBone() : 0; }
unsigned char AnimationController::GetLayer(const String& name) const { AnimationState* state = GetAnimationState(name); return (unsigned char)(state ? state->GetLayer() : 0); }