Node* GameApplication::add_object(Node* pParentNode, const String& nodeName,enObjectType type,float x,float y,float z,const char* modelUrl,const char* material) { ResourceCache* cache = GetSubsystem<ResourceCache>(); Node* pNode = pParentNode->CreateChild(nodeName); pNode->SetPosition(Vector3(x, y, z)); if(type == enObjectType_StaticModel) { StaticModel* pModel = pNode->CreateComponent<StaticModel>(); pModel->SetModel(cache->GetResource<Model>(modelUrl)); if(material != NULL) pModel->SetMaterial(0,cache->GetResource<Material>(material)); pModel->SetCastShadows(true); } else { AnimatedModel* pAniModel = pNode->CreateComponent<AnimatedModel>(); pAniModel->SetModel(cache->GetResource<Model>(modelUrl)); if(material != NULL) pAniModel->SetMaterial(0,cache->GetResource<Material>(material)); pAniModel->SetCastShadows(true); } return pNode; }
bool AnimationController::FadeOthers(const String& name, float targetWeight, float fadeTime) { unsigned index; AnimationState* state; FindAnimation(name, index, state); if (index == M_MAX_UNSIGNED || !state) return false; AnimatedModel* model = GetComponent<AnimatedModel>(); unsigned char layer = state->GetLayer(); bool needUpdate = false; for (unsigned i = 0; i < animations_.Size(); ++i) { if (i != index) { AnimationControl& control = animations_[i]; AnimationState* otherState = model->GetAnimationState(control.hash_); if (otherState && otherState->GetLayer() == layer) { control.targetWeight_ = Clamp(targetWeight, 0.0f, 1.0f); control.fadeTime_ = fadeTime; needUpdate = true; } } } if (needUpdate) MarkNetworkUpdate(); return true; }
void RagDoll::Activate() { //turn the whole thing on //pawn_->GetNode()->RemoveComponent<RigidBody>(); //pawn_->GetNode()->RemoveComponent<CollisionShape>(); //pawn_->GetNode()->RemoveComponent(pawn_->GetBody());//remove the main components //pawn_->GetNode()->RemoveComponent(pawn_->GetShape());//remove the main components //if(node_->HasComponent<RigidBody>()) //{ //GetSubsystem<DebugHud>()->SetAppStats("state:", name_ ); node_->RemoveComponent<RigidBody>(); node_->RemoveComponent<CollisionShape>(); //} AnimatedModel* model = node_->GetComponent<AnimatedModel>(); Skeleton& skeleton = model->GetSkeleton(); for (unsigned i = 0; i < skeleton.GetNumBones(); ++i) skeleton.GetBone(i)->animated_ = false; for (unsigned i = 0; i < boneNode_.Size(); ++i) { //URHO3D_LOGINFO(String(i)); RigidBody* rb = boneNode_[i]->GetComponent<RigidBody>(); rb->SetTrigger(false); rb->SetMass(1.0f); } }
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 (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(); }
bool AnimationController::SetStartBone(const String& name, const String& startBoneName) { AnimationState* state = FindAnimationState(name); if (!state) return false; AnimatedModel* model = GetComponent<AnimatedModel>(); Bone* bone = model->GetSkeleton().GetBone(startBoneName); state->SetStartBone(bone); MarkNetworkUpdate(); return true; }
void StaticModel::SetModel(Model* model) { if (model == model_) return; // If script erroneously calls StaticModel::SetModel on an AnimatedModel, warn and redirect if (GetType() == AnimatedModel::GetTypeStatic()) { ATOMIC_LOGWARNING("StaticModel::SetModel() called on AnimatedModel. Redirecting to AnimatedModel::SetModel()"); AnimatedModel* animatedModel = static_cast<AnimatedModel*>(this); animatedModel->SetModel(model); return; } // Unsubscribe from the reload event of previous model (if any), then subscribe to the new if (model_) UnsubscribeFromEvent(model_, E_RELOADFINISHED); model_ = model; if (model) { SubscribeToEvent(model, E_RELOADFINISHED, ATOMIC_HANDLER(StaticModel, HandleModelReloadFinished)); // Copy the subgeometry & LOD level structure SetNumGeometries(model->GetNumGeometries()); const Vector<Vector<SharedPtr<Geometry> > >& geometries = model->GetGeometries(); const PODVector<Vector3>& geometryCenters = model->GetGeometryCenters(); const Matrix3x4* worldTransform = node_ ? &node_->GetWorldTransform() : (const Matrix3x4*)0; for (unsigned i = 0; i < geometries.Size(); ++i) { batches_[i].worldTransform_ = worldTransform; geometries_[i] = geometries[i]; geometryData_[i].center_ = geometryCenters[i]; // ATOMIC BEGIN geometryData_[i].enabled_ = true; geometryData_[i].batchGeometry_ = 0; // ATOMIC END } SetBoundingBox(model->GetBoundingBox()); ResetLodLevels(); } else { SetNumGeometries(0); SetBoundingBox(BoundingBox()); } MarkNetworkUpdate(); }
bool ModelImporter::ImportAnimation(const String& filename, const String& name, float startTime, float endTime) { SharedPtr<OpenAssetImporter> importer(new OpenAssetImporter(context_)); //importer->SetVerboseLog(true); importer->SetScale(scale_); importer->SetExportAnimations(true); importer->SetStartTime(startTime); importer->SetEndTime(endTime); if (importer->Load(filename)) { importer->ExportModel(asset_->GetCachePath(), name, true); const Vector<OpenAssetImporter::AnimationInfo>& infos = importer->GetAnimationInfos(); for (unsigned i = 0; i < infos.Size(); i++) { const OpenAssetImporter::AnimationInfo& info = infos.At(i); String pathName, fileName, extension; SplitPath(info.cacheFilename_, pathName, fileName, extension); ResourceCache* cache = GetSubsystem<ResourceCache>(); AnimatedModel* animatedModel = importNode_->GetComponent<AnimatedModel>(); if (animatedModel) { Model* model = animatedModel->GetModel(); if (model) { SharedPtr<Animation> animation = cache->GetTempResource<Animation>(fileName + extension); if (animation) model->AddAnimationResource(animation); } } LOGINFOF("Import Info: %s : %s", info.name_.CString(), fileName.CString()); } return true; } return false; }
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; }
AnimationState* AnimationController::GetAnimationState(StringHash nameHash) const { // Model mode AnimatedModel* model = GetComponent<AnimatedModel>(); if (model) return model->GetAnimationState(nameHash); // Node hierarchy mode for (Vector<SharedPtr<AnimationState> >::ConstIterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i) { Animation* animation = (*i)->GetAnimation(); if (animation->GetNameHash() == nameHash || animation->GetAnimationNameHash() == nameHash) return *i; } return 0; }
void Mover::Update(float timeStep) { node_->Translate(Vector3::FORWARD * moveSpeed_ * timeStep); // If in risk of going outside the plane, rotate the model right Vector3 pos = node_->GetPosition(); if (pos.x_ < bounds_.min_.x_ || pos.x_ > bounds_.max_.x_ || pos.z_ < bounds_.min_.z_ || pos.z_ > bounds_.max_.z_) node_->Yaw(rotationSpeed_ * timeStep); // Get the model's first (only) animation state and advance its time. Note the convenience accessor to other components // in the same scene node AnimatedModel* model = GetComponent<AnimatedModel>(); if (model->GetNumAnimationStates()) { AnimationState* state = model->GetAnimationStates()[0]; state->AddTime(timeStep); } }
void CharacterDemo::CreateCharacter() { ResourceCache* cache = GetSubsystem<ResourceCache>(); Node* objectNode = scene_->CreateChild("Jack"); objectNode->SetPosition(Vector3(0.0f, 1.0f, 0.0f)); // spin node Node* adjustNode = objectNode->CreateChild("AdjNode"); adjustNode->SetRotation( Quaternion(180, Vector3(0,1,0) ) ); // Create the rendering component + animation controller AnimatedModel* object = adjustNode->CreateComponent<AnimatedModel>(); object->SetModel(cache->GetResource<Model>("Models/Mutant/Mutant.mdl")); object->SetMaterial(cache->GetResource<Material>("Models/Mutant/Materials/mutant_M.xml")); object->SetCastShadows(true); adjustNode->CreateComponent<AnimationController>(); // Set the head bone for manual control object->GetSkeleton().GetBone("Mutant:Head")->animated_ = false; // Create rigidbody, and set non-zero mass so that the body becomes dynamic RigidBody* body = objectNode->CreateComponent<RigidBody>(); body->SetCollisionLayer(1); body->SetMass(1.0f); // Set zero angular factor so that physics doesn't turn the character on its own. // Instead we will control the character yaw manually body->SetAngularFactor(Vector3::ZERO); // Set the rigidbody to signal collision also when in rest, so that we get ground collisions properly body->SetCollisionEventMode(COLLISION_ALWAYS); // Set a capsule shape for collision CollisionShape* shape = objectNode->CreateComponent<CollisionShape>(); shape->SetCapsule(0.7f, 1.8f, Vector3(0.0f, 0.9f, 0.0f)); // Create the character logic component, which takes care of steering the rigidbody // Remember it so that we can set the controls. Use a WeakPtr because the scene hierarchy already owns it // and keeps it alive as long as it's not removed from the hierarchy character_ = objectNode->CreateComponent<Character>(); }
void AnimationController::StopLayer(unsigned char layer, float fadeOutTime) { AnimatedModel* model = GetComponent<AnimatedModel>(); if (!model) return; bool needUpdate = false; for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End(); ++i) { AnimationState* state = model->GetAnimationState(i->hash_); if (state && state->GetLayer() == layer) { i->targetWeight_ = 0.0f; i->fadeTime_ = fadeOutTime; needUpdate = true; } } if (needUpdate) MarkNetworkUpdate(); }
void Mover::HandleSceneUpdate(StringHash eventType, VariantMap& eventData) { // Get the timestep from the update event using namespace SceneUpdate; float timeStep = eventData[P_TIMESTEP].GetFloat(); node_->TranslateRelative(Vector3::FORWARD * moveSpeed_ * timeStep); // If in risk of going outside the plane, rotate the model right Vector3 pos = node_->GetPosition(); if (pos.x_ < bounds_.min_.x_ || pos.x_ > bounds_.max_.x_ || pos.z_ < bounds_.min_.z_ || pos.z_ > bounds_.max_.z_) node_->Yaw(rotationSpeed_ * timeStep); // Get the model's first (only) animation state and advance its time. Note the convenience accessor to other components // in the same scene node AnimatedModel* model = GetComponent<AnimatedModel>(); if (model->GetNumAnimationStates()) { AnimationState* state = model->GetAnimationStates()[0]; state->AddTime(timeStep); } }
void AnimationController::RemoveAnimationState(AnimationState* state) { if (!state) return; // Model mode AnimatedModel* model = GetComponent<AnimatedModel>(); if (model) { model->RemoveAnimationState(state); return; } // Node hierarchy mode for (Vector<SharedPtr<AnimationState> >::Iterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i) { if ((*i) == state) { nodeAnimationStates_.Erase(i); return; } } }
void AnimationController::FindAnimation(const String& name, unsigned& index, AnimationState*& state) const { AnimatedModel* model = GetComponent<AnimatedModel>(); StringHash nameHash(name); // Find the AnimationState state = model ? model->GetAnimationState(nameHash) : 0; if (state) { // Either a resource name or animation name may be specified. We store resource names, so correct the hash if necessary nameHash = state->GetAnimation()->GetNameHash(); } // Find the internal control structure index = M_MAX_UNSIGNED; for (unsigned i = 0; i < animations_.Size(); ++i) { if (animations_[i].hash_ == nameHash) { index = i; break; } } }
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; }
bool ModelImporter::Import() { String ext = asset_->GetExtension(); String modelAssetFilename = asset_->GetPath(); importNode_ = new Node(context_); if (ext == ".mdl") { FileSystem* fs = GetSubsystem<FileSystem>(); ResourceCache* cache = GetSubsystem<ResourceCache>(); // mdl files are native file format that doesn't need to be converted // doesn't allow scale, animations legacy primarily for ToonTown if (!fs->Copy(asset_->GetPath(), asset_->GetCachePath() + ".mdl")) { importNode_= 0; return false; } Model* mdl = cache->GetResource<Model>( asset_->GetCachePath() + ".mdl"); if (!mdl) { importNode_= 0; return false; } // Force a reload, though file watchers will catch this delayed and load again cache->ReloadResource(mdl); importNode_->CreateComponent<StaticModel>()->SetModel(mdl); } else { // skip external animations, they will be brought in when importing their // corresponding model if (!modelAssetFilename.Contains("@")) { ImportModel(); if (importAnimations_) { ImportAnimations(); } AnimatedModel* animatedModel = importNode_->GetComponent<AnimatedModel>(); if (animatedModel) { Model* model = animatedModel->GetModel(); if (model && model->GetAnimationCount()) { // resave with animation info File mdlFile(context_); if (!mdlFile.Open(asset_->GetCachePath() + ".mdl", FILE_WRITE)) { ErrorExit("Could not open output file " + asset_->GetCachePath() + ".mdl"); return false; } model->Save(mdlFile); } } } } File outFile(context_); if (!outFile.Open(asset_->GetCachePath(), FILE_WRITE)) ErrorExit("Could not open output file " + asset_->GetCachePath()); importNode_->SaveXML(outFile); importNode_ = 0; return true; }
bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blendWeights, const unsigned char* blendIndices, unsigned char* newBlendIndices) { AnimatedModel* animatedModel = dynamic_cast<AnimatedModel*>(target); if (!animatedModel) return false; // Check whether target is using global or per-geometry skinning const Vector<PODVector<Matrix3x4> >& geometrySkinMatrices = animatedModel->GetGeometrySkinMatrices(); const Vector<PODVector<unsigned> >& geometryBoneMappings = animatedModel->GetGeometryBoneMappings(); for (unsigned i = 0; i < 4; ++i) { if (blendWeights[i] > 0.0f) { Bone* bone = 0; if (geometrySkinMatrices.Empty()) bone = animatedModel->GetSkeleton().GetBone(blendIndices[i]); else if (blendIndices[i] < geometryBoneMappings[batchIndex].Size()) bone = animatedModel->GetSkeleton().GetBone(geometryBoneMappings[batchIndex][blendIndices[i]]); if (!bone) { URHO3D_LOGWARNING("Out of range bone index for skinned decal"); return false; } bool found = false; unsigned index; for (index = 0; index < bones_.Size(); ++index) { if (bones_[index].node_ == bone->node_) { // Check also that the offset matrix matches, in case we for example have a separate attachment AnimatedModel // with a different bind pose if (bones_[index].offsetMatrix_.Equals(bone->offsetMatrix_)) { found = true; break; } } } if (!found) { if (bones_.Size() >= Graphics::GetMaxBones()) { URHO3D_LOGWARNING("Maximum skinned decal bone count reached"); return false; } else { // Copy the bone from the model to the decal index = bones_.Size(); bones_.Resize(bones_.Size() + 1); bones_[index] = *bone; skinMatrices_.Resize(skinMatrices_.Size() + 1); skinningDirty_ = true; // Start listening to bone transform changes to update skinning bone->node_->AddListener(this); } } newBlendIndices[i] = (unsigned char)index; } else newBlendIndices[i] = 0; } // Update amount of shader data in the decal batch UpdateBatch(); return true; }
bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Quaternion& worldRotation, float size, float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive, float normalCutoff, unsigned subGeometry) { URHO3D_PROFILE(AddDecal); // Do not add decals in headless mode if (!node_ || !GetSubsystem<Graphics>()) return false; if (!target || !target->GetNode()) { URHO3D_LOGERROR("Null target drawable for decal"); return false; } // Check for animated target and switch into skinned/static mode if necessary AnimatedModel* animatedModel = dynamic_cast<AnimatedModel*>(target); if ((animatedModel && !skinned_) || (!animatedModel && skinned_)) { RemoveAllDecals(); skinned_ = animatedModel != 0; bufferDirty_ = true; } // Center the decal frustum on the world position Vector3 adjustedWorldPosition = worldPosition - 0.5f * depth * (worldRotation * Vector3::FORWARD); /// \todo target transform is not right if adding a decal to StaticModelGroup Matrix3x4 targetTransform = target->GetNode()->GetWorldTransform().Inverse(); // For an animated model, adjust the decal position back to the bind pose // To do this, need to find the bone the decal is colliding with if (animatedModel) { Skeleton& skeleton = animatedModel->GetSkeleton(); unsigned numBones = skeleton.GetNumBones(); Bone* bestBone = 0; float bestSize = 0.0f; for (unsigned i = 0; i < numBones; ++i) { Bone* bone = skeleton.GetBone(i); if (!bone->node_ || !bone->collisionMask_) continue; // Represent the decal as a sphere, try to find the biggest colliding bone Sphere decalSphere (bone->node_->GetWorldTransform().Inverse() * worldPosition, 0.5f * size / bone->node_->GetWorldScale().Length()); if (bone->collisionMask_ & BONECOLLISION_BOX) { float size = bone->boundingBox_.HalfSize().Length(); if (bone->boundingBox_.IsInside(decalSphere) && size > bestSize) { bestBone = bone; bestSize = size; } } else if (bone->collisionMask_ & BONECOLLISION_SPHERE) { Sphere boneSphere(Vector3::ZERO, bone->radius_); float size = bone->radius_; if (boneSphere.IsInside(decalSphere) && size > bestSize) { bestBone = bone; bestSize = size; } } } if (bestBone) targetTransform = (bestBone->node_->GetWorldTransform() * bestBone->offsetMatrix_).Inverse(); } // Build the decal frustum Frustum decalFrustum; Matrix3x4 frustumTransform = targetTransform * Matrix3x4(adjustedWorldPosition, worldRotation, 1.0f); decalFrustum.DefineOrtho(size, aspectRatio, 1.0, 0.0f, depth, frustumTransform); Vector3 decalNormal = (targetTransform * Vector4(worldRotation * Vector3::BACK, 0.0f)).Normalized(); decals_.Resize(decals_.Size() + 1); Decal& newDecal = decals_.Back(); newDecal.timeToLive_ = timeToLive; Vector<PODVector<DecalVertex> > faces; PODVector<DecalVertex> tempFace; unsigned numBatches = target->GetBatches().Size(); // Use either a specified subgeometry in the target, or all if (subGeometry < numBatches) GetFaces(faces, target, subGeometry, decalFrustum, decalNormal, normalCutoff); else { for (unsigned i = 0; i < numBatches; ++i) GetFaces(faces, target, i, decalFrustum, decalNormal, normalCutoff); } // Clip the acquired faces against all frustum planes for (unsigned i = 0; i < NUM_FRUSTUM_PLANES; ++i) { for (unsigned j = 0; j < faces.Size(); ++j) { PODVector<DecalVertex>& face = faces[j]; if (face.Empty()) continue; ClipPolygon(tempFace, face, decalFrustum.planes_[i], skinned_); face = tempFace; } } // Now triangulate the resulting faces into decal vertices for (unsigned i = 0; i < faces.Size(); ++i) { PODVector<DecalVertex>& face = faces[i]; if (face.Size() < 3) continue; for (unsigned j = 2; j < face.Size(); ++j) { newDecal.AddVertex(face[0]); newDecal.AddVertex(face[j - 1]); newDecal.AddVertex(face[j]); } } // Check if resulted in no triangles if (newDecal.vertices_.Empty()) { decals_.Pop(); return true; } if (newDecal.vertices_.Size() > maxVertices_) { URHO3D_LOGWARNING("Can not add decal, vertex count " + String(newDecal.vertices_.Size()) + " exceeds maximum " + String(maxVertices_)); decals_.Pop(); return false; } if (newDecal.indices_.Size() > maxIndices_) { URHO3D_LOGWARNING("Can not add decal, index count " + String(newDecal.indices_.Size()) + " exceeds maximum " + String(maxIndices_)); decals_.Pop(); return false; } // Calculate UVs Matrix4 projection(Matrix4::ZERO); projection.m11_ = (1.0f / (size * 0.5f)); projection.m00_ = projection.m11_ / aspectRatio; projection.m22_ = 1.0f / depth; projection.m33_ = 1.0f; CalculateUVs(newDecal, frustumTransform.Inverse(), projection, topLeftUV, bottomRightUV); // Transform vertices to this node's local space and generate tangents Matrix3x4 decalTransform = node_->GetWorldTransform().Inverse() * target->GetNode()->GetWorldTransform(); TransformVertices(newDecal, skinned_ ? Matrix3x4::IDENTITY : decalTransform); GenerateTangents(&newDecal.vertices_[0], sizeof(DecalVertex), &newDecal.indices_[0], sizeof(unsigned short), 0, newDecal.indices_.Size(), offsetof(DecalVertex, normal_), offsetof(DecalVertex, texCoord_), offsetof(DecalVertex, tangent_)); newDecal.CalculateBoundingBox(); numVertices_ += newDecal.vertices_.Size(); numIndices_ += newDecal.indices_.Size(); // Remove oldest decals if total vertices exceeded while (decals_.Size() && (numVertices_ > maxVertices_ || numIndices_ > maxIndices_)) RemoveDecals(1); URHO3D_LOGDEBUG("Added decal with " + String(newDecal.vertices_.Size()) + " vertices"); // If new decal is time limited, subscribe to scene post-update if (newDecal.timeToLive_ > 0.0f && !subscribed_) UpdateEventSubscription(false); MarkDecalsDirty(); return true; }
void Urho3DTemplate::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, 200 world unitys, fade shadows at 80% of maximum shadow distance light->SetShadowCascade(CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f)); //Create some mushrooms const unsigned NUM_MUSHROOMS = 100; for (unsigned i = 0; i < NUM_MUSHROOMS; ++i) CreateMushroom(Vector3(Random(90.0f) - 45.0f, 0.0f, Random(90.0f) - 45.0f)); //Create randomly sized boxes. If boxes are big enough make them occluders const unsigned NUM_BOXES = 20; for (unsigned i = 0; i <NUM_BOXES; ++i) { Node* boxNode = scene_->CreateChild("Box"); float size = 1.0f + Random(10.0f); boxNode->SetPosition(Vector3(Random(80.0f) - 40.0f, size * 0.5f, Random(80.0f) - 40.0f)); boxNode->SetScale(size); StaticModel* boxObject = boxNode->CreateComponent<StaticModel>(); boxObject->SetModel(cache->GetResource<Model>("Models/Box.mdl")); boxObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml")); boxObject->SetCastShadows(true); if (size >= 3.0f) boxObject->SetOccluder(true); } //Create Jack node that will follow the path jackNode_ = scene_->CreateChild("Jack"); jackNode_->SetPosition(Vector3(-5.0f, 0.0f, 20.0f)); AnimatedModel* modelObject = jackNode_->CreateComponent<AnimatedModel>(); modelObject->SetModel(cache->GetResource<Model>("Model/Jack.mdl")); modelObject->SetMaterial(cache->GetResource<Material>("Materials/Jack.xml")); modelObject->SetCastShadows(true); //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 Ragdolls::CreateScene() { ResourceCache* cache = GetContext()->m_ResourceCache.get(); scene_ = new Scene(GetContext()); // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000) // Create a physics simulation world with default parameters, which will update at 60fps. Like the Octree must // exist before creating drawable components, the PhysicsWorld must exist before creating physics components. // Finally, create a DebugRenderer component so that we can draw physics debug geometry scene_->CreateComponent<Octree>(); scene_->CreateComponent<PhysicsWorld>(); scene_->CreateComponent<DebugRenderer>(); // 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 a floor object, 500 x 500 world units. Adjust position so that the ground is at zero Y Node* floorNode = scene_->CreateChild("Floor"); floorNode->SetPosition(Vector3(0.0f, -0.5f, 0.0f)); floorNode->SetScale(Vector3(500.0f, 1.0f, 500.0f)); StaticModel* floorObject = floorNode->CreateComponent<StaticModel>(); floorObject->SetModel(cache->GetResource<Model>("Models/Box.mdl")); floorObject->SetMaterial(cache->GetResource<Material>("Materials/StoneTiled.xml")); // Make the floor physical by adding RigidBody and CollisionShape components RigidBody* body = floorNode->CreateComponent<RigidBody>(); // We will be spawning spherical objects in this sample. The ground also needs non-zero rolling friction so that // the spheres will eventually come to rest body->SetRollingFriction(0.15f); CollisionShape* shape = floorNode->CreateComponent<CollisionShape>(); // Set a box shape of size 1 x 1 x 1 for collision. The shape will be scaled with the scene node scale, so the // rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.) shape->SetBox(Vector3::ONE); } // Create animated models for (int z = -1; z <= 1; ++z) { for (int x = -4; x <= 4; ++x) { Node* modelNode = scene_->CreateChild("Jack"); modelNode->SetPosition(Vector3(x * 5.0f, 0.0f, z * 5.0f)); modelNode->SetRotation(Quaternion(0.0f, 180.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); // Set the model to also update when invisible to avoid staying invisible when the model should come into // view, but does not as the bounding box is not updated modelObject->SetUpdateInvisible(true); // Create a rigid body and a collision shape. These will act as a trigger for transforming the // model into a ragdoll when hit by a moving object RigidBody* body = modelNode->CreateComponent<RigidBody>(); // The Trigger mode makes the rigid body only detect collisions, but impart no forces on the // colliding objects body->SetTrigger(true); CollisionShape* shape = modelNode->CreateComponent<CollisionShape>(); // Create the capsule shape with an offset so that it is correctly aligned with the model, which // has its origin at the feet shape->SetCapsule(0.7f, 2.0f, Vector3(0.0f, 1.0f, 0.0f)); // Create a custom component that reacts to collisions and creates the ragdoll modelNode->CreateComponent<CreateRagdoll>(); } } // Create the camera. Limit far clip distance to match the fog. Note: now we actually create the camera node outside // the scene, because we want it to be unaffected by scene load / save cameraNode_ = new Node(GetContext()); Camera* camera = cameraNode_->CreateComponent<Camera>(); camera->setFarClipDistance(300.0f); // Set an initial position for the camera scene node above the floor cameraNode_->SetPosition(Vector3(0.0f, 3.0f, -20.0f)); }
void CreateRagdoll::HandleNodeCollision(StringHash eventType, VariantMap& eventData) { using namespace NodeCollision; // Get the other colliding body, make sure it is moving (has nonzero mass) RigidBody* otherBody = static_cast<RigidBody*>(eventData[P_OTHERBODY].GetPtr()); if (otherBody->GetMass() > 0.0f) { // We do not need the physics components in the AnimatedModel's root scene node anymore node_->RemoveComponent<RigidBody>(); node_->RemoveComponent<CollisionShape>(); // Create RigidBody & CollisionShape components to bones CreateRagdollBone("Bip01_Pelvis", SHAPE_BOX, Vector3(0.3f, 0.2f, 0.25f), Vector3(0.0f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f)); CreateRagdollBone("Bip01_Spine1", SHAPE_BOX, Vector3(0.35f, 0.2f, 0.3f), Vector3(0.15f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f)); CreateRagdollBone("Bip01_L_Thigh", SHAPE_CAPSULE, Vector3(0.175f, 0.45f, 0.175f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_Thigh", SHAPE_CAPSULE, Vector3(0.175f, 0.45f, 0.175f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_L_Calf", SHAPE_CAPSULE, Vector3(0.15f, 0.55f, 0.15f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_Calf", SHAPE_CAPSULE, Vector3(0.15f, 0.55f, 0.15f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_Head", SHAPE_BOX, Vector3(0.2f, 0.2f, 0.2f), Vector3(0.1f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f)); CreateRagdollBone("Bip01_L_UpperArm", SHAPE_CAPSULE, Vector3(0.15f, 0.35f, 0.15f), Vector3(0.1f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_UpperArm", SHAPE_CAPSULE, Vector3(0.15f, 0.35f, 0.15f), Vector3(0.1f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_L_Forearm", SHAPE_CAPSULE, Vector3(0.125f, 0.4f, 0.125f), Vector3(0.2f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_Forearm", SHAPE_CAPSULE, Vector3(0.125f, 0.4f, 0.125f), Vector3(0.2f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); // Create Constraints between bones CreateRagdollConstraint("Bip01_L_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3::BACK, Vector3::FORWARD, Vector2(45.0f, 45.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_R_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3::BACK, Vector3::FORWARD, Vector2(45.0f, 45.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_L_Calf", "Bip01_L_Thigh", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_R_Calf", "Bip01_R_Thigh", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_Spine1", "Bip01_Pelvis", CONSTRAINT_HINGE, Vector3::FORWARD, Vector3::FORWARD, Vector2(45.0f, 0.0f), Vector2(-10.0f, 0.0f)); CreateRagdollConstraint("Bip01_Head", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3::LEFT, Vector3::LEFT, Vector2(0.0f, 30.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_L_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3::DOWN, Vector3::UP, Vector2(45.0f, 45.0f), Vector2::ZERO, false); CreateRagdollConstraint("Bip01_R_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3::DOWN, Vector3::UP, Vector2(45.0f, 45.0f), Vector2::ZERO, false); CreateRagdollConstraint("Bip01_L_Forearm", "Bip01_L_UpperArm", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_R_Forearm", "Bip01_R_UpperArm", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); // Disable keyframe animation from all bones so that they will not interfere with the ragdoll AnimatedModel* model = GetComponent<AnimatedModel>(); Skeleton& skeleton = model->GetSkeleton(); for (unsigned i = 0; i < skeleton.GetNumBones(); ++i) skeleton.GetBone(i)->animated_ = false; // Finally remove self from the scene node. Note that this must be the last operation performed in the function Remove(); } }
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::Update(float timeStep) { AnimatedModel* model = GetComponent<AnimatedModel>(); if (!model) return; // Loop through animations for (Vector<AnimationControl>::Iterator i = animations_.Begin(); i != animations_.End();) { bool remove = false; AnimationState* state = model->GetAnimationState(i->hash_); if (!state) remove = true; else { // Advance the animation if (i->speed_ != 0.0f) state->AddTime(i->speed_ * timeStep); float targetWeight = i->targetWeight_; float fadeTime = i->fadeTime_; // If non-looped animation at the end, activate autofade as applicable if (!state->IsLooped() && state->GetTime() >= state->GetLength() && i->autoFadeTime_ > 0.0f) { targetWeight = 0.0f; fadeTime = i->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)) remove = true; } // Decrement the command time-to-live values if (i->setTimeTtl_ > 0.0f) i->setTimeTtl_ = Max(i->setTimeTtl_ - timeStep, 0.0f); if (i->setWeightTtl_ > 0.0f) i->setWeightTtl_ = Max(i->setWeightTtl_ - timeStep, 0.0f); if (remove) { if (state) model->RemoveAnimationState(state); i = animations_.Erase(i); MarkNetworkUpdate(); } else ++i; } }
AnimationState* AnimationController::FindAnimationState(const String& name) const { AnimatedModel* model = GetComponent<AnimatedModel>(); return model ? model->GetAnimationState(StringHash(name)) : 0; }
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) { 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); 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; } } }
void RibbonTrailDemo::CreateScene() { ResourceCache* cache = GetSubsystem<ResourceCache>(); scene_ = new Scene(context_); // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000) scene_->CreateComponent<Octree>(); // 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 directional light to the world. Node* lightNode = scene_->CreateChild("DirectionalLight"); lightNode->SetDirection(Vector3(0.6f, -1.0f, 0.8f)); // The direction vector does not need to be normalized Light* light = lightNode->CreateComponent<Light>(); light->SetLightType(LIGHT_DIRECTIONAL); light->SetCastShadows(true); light->SetShadowBias(BiasParameters(0.00005f, 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 first box for face camera trail demo with 1 column. boxNode1_ = scene_->CreateChild("Box1"); StaticModel* box1 = boxNode1_->CreateComponent<StaticModel>(); box1->SetModel(cache->GetResource<Model>("Models/Box.mdl")); box1->SetCastShadows(true); RibbonTrail* boxTrail1 = boxNode1_->CreateComponent<RibbonTrail>(); boxTrail1->SetMaterial(cache->GetResource<Material>("Materials/RibbonTrail.xml")); boxTrail1->SetStartColor(Color(1.0f, 0.5f, 0.0f, 1.0f)); boxTrail1->SetEndColor(Color(1.0f, 1.0f, 0.0f, 0.0f)); boxTrail1->SetWidth(0.5f); boxTrail1->SetUpdateInvisible(true); // Create second box for face camera trail demo with 4 column. // This will produce less distortion than first trail. boxNode2_ = scene_->CreateChild("Box2"); StaticModel* box2 = boxNode2_->CreateComponent<StaticModel>(); box2->SetModel(cache->GetResource<Model>("Models/Box.mdl")); box2->SetCastShadows(true); RibbonTrail* boxTrail2 = boxNode2_->CreateComponent<RibbonTrail>(); boxTrail2->SetMaterial(cache->GetResource<Material>("Materials/RibbonTrail.xml")); boxTrail2->SetStartColor(Color(1.0f, 0.5f, 0.0f, 1.0f)); boxTrail2->SetEndColor(Color(1.0f, 1.0f, 0.0f, 0.0f)); boxTrail2->SetWidth(0.5f); boxTrail2->SetTailColumn(4); boxTrail2->SetUpdateInvisible(true); // Load ninja animated model for bone trail demo. Node* ninjaNode = scene_->CreateChild("Ninja"); ninjaNode->SetPosition(Vector3(5.0f, 0.0f, 0.0f)); ninjaNode->SetRotation(Quaternion(0.0f, 180.0f, 0.0f)); AnimatedModel* ninja = ninjaNode->CreateComponent<AnimatedModel>(); ninja->SetModel(cache->GetResource<Model>("Models/NinjaSnowWar/Ninja.mdl")); ninja->SetMaterial(cache->GetResource<Material>("Materials/NinjaSnowWar/Ninja.xml")); ninja->SetCastShadows(true); // Create animation controller and play attack animation. ninjaAnimCtrl_ = ninjaNode->CreateComponent<AnimationController>(); ninjaAnimCtrl_->PlayExclusive("Models/NinjaSnowWar/Ninja_Attack3.ani", 0, true, 0.0f); // Add ribbon trail to tip of sword. Node* swordTip = ninjaNode->GetChild("Joint29", true); swordTrail_ = swordTip->CreateComponent<RibbonTrail>(); // Set sword trail type to bone and set other parameters. swordTrail_->SetTrailType(TT_BONE); swordTrail_->SetMaterial(cache->GetResource<Material>("Materials/SlashTrail.xml")); swordTrail_->SetLifetime(0.22f); swordTrail_->SetStartColor(Color(1.0f, 1.0f, 1.0f, 0.75f)); swordTrail_->SetEndColor(Color(0.2f, 0.5f, 1.0f, 0.0f)); swordTrail_->SetTailColumn(4); swordTrail_->SetUpdateInvisible(true); // Add floating text for info. Node* boxTextNode1 = scene_->CreateChild("BoxText1"); boxTextNode1->SetPosition(Vector3(-1.0f, 2.0f, 0.0f)); Text3D* boxText1 = boxTextNode1->CreateComponent<Text3D>(); boxText1->SetText(String("Face Camera Trail (4 Column)")); boxText1->SetFont(cache->GetResource<Font>("Fonts/BlueHighway.sdf"), 24); Node* boxTextNode2 = scene_->CreateChild("BoxText2"); boxTextNode2->SetPosition(Vector3(-6.0f, 2.0f, 0.0f)); Text3D* boxText2 = boxTextNode2->CreateComponent<Text3D>(); boxText2->SetText(String("Face Camera Trail (1 Column)")); boxText2->SetFont(cache->GetResource<Font>("Fonts/BlueHighway.sdf"), 24); Node* ninjaTextNode2 = scene_->CreateChild("NinjaText"); ninjaTextNode2->SetPosition(Vector3(4.0f, 2.5f, 0.0f)); Text3D* ninjaText = ninjaTextNode2->CreateComponent<Text3D>(); ninjaText->SetText(String("Bone Trail (4 Column)")); ninjaText->SetFont(cache->GetResource<Font>("Fonts/BlueHighway.sdf"), 24); // Create the camera. cameraNode_ = scene_->CreateChild("Camera"); cameraNode_->CreateComponent<Camera>(); // Set an initial position for the camera scene node above the plane cameraNode_->SetPosition(Vector3(0.0f, 2.0f, -14.0f)); }
void Navigation::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 some mushrooms const unsigned NUM_MUSHROOMS = 100; for (unsigned i = 0; i < NUM_MUSHROOMS; ++i) CreateMushroom(Vector3(Random(90.0f) - 45.0f, 0.0f, Random(90.0f) - 45.0f)); // Create randomly sized boxes. If boxes are big enough, make them occluders const unsigned NUM_BOXES = 20; for (unsigned i = 0; i < NUM_BOXES; ++i) { Node* boxNode = scene_->CreateChild("Box"); float size = 1.0f + Random(10.0f); boxNode->SetPosition(Vector3(Random(80.0f) - 40.0f, size * 0.5f, Random(80.0f) - 40.0f)); boxNode->SetScale(size); StaticModel* boxObject = boxNode->CreateComponent<StaticModel>(); boxObject->SetModel(cache->GetResource<Model>("Models/Box.mdl")); boxObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml")); boxObject->SetCastShadows(true); if (size >= 3.0f) boxObject->SetOccluder(true); } // Create Jack node that will follow the path jackNode_ = scene_->CreateChild("Jack"); jackNode_->SetPosition(Vector3(-5.0f, 0.0f, 20.0f)); AnimatedModel* modelObject = jackNode_->CreateComponent<AnimatedModel>(); modelObject->SetModel(cache->GetResource<Model>("Models/Jack.mdl")); modelObject->SetMaterial(cache->GetResource<Material>("Materials/Jack.xml")); modelObject->SetCastShadows(true); // Create a NavigationMesh component to the scene root NavigationMesh* navMesh = scene_->CreateComponent<NavigationMesh>(); // Create a Navigable component to the scene root. This tags all of the geometry in the scene as being part of the // navigation mesh. By default this is recursive, but the recursion could be turned off from Navigable scene_->CreateComponent<Navigable>(); // Add padding to the navigation mesh in Y-direction so that we can add objects on top of the tallest boxes // in the scene and still update the mesh correctly navMesh->SetPadding(Vector3(0.0f, 10.0f, 0.0f)); // Now build the navigation geometry. This will take some time. Note that the navigation mesh will prefer to use // physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example) // it will use renderable geometry instead navMesh->Build(); // 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 and looking down cameraNode_->SetPosition(Vector3(0.0f, 50.0f, 0.0f)); pitch_ = 80.0f; cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f)); }