AnimationState* AnimationController::AddAnimationState(Animation* animation) { if (!animation) return 0; // Model mode AnimatedModel* model = GetComponent<AnimatedModel>(); if (model) return model->AddAnimationState(animation); // Node hierarchy mode SharedPtr<AnimationState> newState(new AnimationState(node_, animation)); nodeAnimationStates_.Push(newState); return newState; }
bool AnimationController::Play(const String& name, unsigned char layer, bool looped, float fadeInTime) { AnimatedModel* model = GetComponent<AnimatedModel>(); if (!model) return false; // Check if already exists unsigned index; AnimationState* state; FindAnimation(name, index, state); if (!state) { Animation* newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(name); state = model->AddAnimationState(newAnimation); if (!state) return false; } if (index == M_MAX_UNSIGNED) { AnimationControl newControl; Animation* animation = state->GetAnimation(); newControl.hash_ = animation->GetNameHash(); animations_.Push(newControl); index = animations_.Size() - 1; } state->SetLayer(layer); state->SetLooped(looped); animations_[index].targetWeight_ = 1.0f; animations_[index].fadeTime_ = fadeInTime; MarkNetworkUpdate(); return true; }
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("Directional light"); 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.0001f, 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); // Enable full blending weight and looping state->SetWeight(1.0f); state->SetLooped(true); // 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) { // To apply animations, the model must exist first (practically, have been added before AnimationController) AnimatedModel* model = GetComponent<AnimatedModel>(); if (!model) return; MemoryBuffer buf(value); // Check which animations we need to remove HashSet<StringHash> processedAnimations; unsigned numAnimations = buf.ReadVLE(); while (numAnimations--) { StringHash animHash = buf.ReadStringHash(); processedAnimations.Insert(animHash); // Check if the animation state exists. If not, add new AnimationState* state = model->GetAnimationState(animHash); if (!state) { Animation* newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(animHash); state = model->AddAnimationState(newAnimation); if (!state) { 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.hash_ = animHash; animations_.Push(newControl); } unsigned char ctrl = buf.ReadUByte(); state->SetLayer(buf.ReadUByte()); state->SetLooped((ctrl & CTRL_LOOPED) != 0); 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) state->SetStartBone(model->GetSkeleton().GetBone(buf.ReadStringHash())); 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; 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; } } }