// ---------------------------------------------------------------------------- void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData) { using namespace NodeAdded; if (solver_->tree == nullptr) return; auto* node = static_cast<Node*>(eventData[P_NODE].GetPtr()); PODVector<IKEffector*> effectors; node->GetComponents<IKEffector>(effectors, true); for (PODVector<IKEffector*>::ConstIterator it = effectors.Begin(); it != effectors.End(); ++it) { if (ComponentIsInOurSubtree(*it) == false) continue; BuildTreeToEffector(*it); effectorList_.Push(*it); } PODVector<IKConstraint*> constraints; node->GetComponents<IKConstraint>(constraints, true); for (PODVector<IKConstraint*>::ConstIterator it = constraints.Begin(); it != constraints.End(); ++it) { if (ComponentIsInOurSubtree(*it) == false) continue; constraintList_.Push(*it); } }
void BatchQueue::SortFrontToBack2Pass(PODVector<Batch*>& batches) { // Mobile devices likely use a tiled deferred approach, with which front-to-back sorting is irrelevant. The 2-pass // method is also time consuming, so just sort with state having priority #ifdef GL_ES_VERSION_2_0 Sort(batches.Begin(), batches.End(), CompareBatchesState); #else // For desktop, first sort by distance and remap shader/material/geometry IDs in the sort key Sort(batches.Begin(), batches.End(), CompareBatchesFrontToBack); unsigned freeShaderID = 0; unsigned short freeMaterialID = 0; unsigned short freeGeometryID = 0; for (PODVector<Batch*>::Iterator i = batches.Begin(); i != batches.End(); ++i) { Batch* batch = *i; unsigned shaderID = (batch->sortKey_ >> 32); HashMap<unsigned, unsigned>::ConstIterator j = shaderRemapping_.Find(shaderID); if (j != shaderRemapping_.End()) shaderID = j->second_; else { shaderID = shaderRemapping_[shaderID] = freeShaderID | (shaderID & 0xc0000000); ++freeShaderID; } unsigned short materialID = (unsigned short)(batch->sortKey_ & 0xffff0000); HashMap<unsigned short, unsigned short>::ConstIterator k = materialRemapping_.Find(materialID); if (k != materialRemapping_.End()) materialID = k->second_; else { materialID = materialRemapping_[materialID] = freeMaterialID; ++freeMaterialID; } unsigned short geometryID = (unsigned short)(batch->sortKey_ & 0xffff); HashMap<unsigned short, unsigned short>::ConstIterator l = geometryRemapping_.Find(geometryID); if (l != geometryRemapping_.End()) geometryID = l->second_; else { geometryID = geometryRemapping_[geometryID] = freeGeometryID; ++freeGeometryID; } batch->sortKey_ = (((unsigned long long)shaderID) << 32) || (((unsigned long long)materialID) << 16) | geometryID; } shaderRemapping_.Clear(); materialRemapping_.Clear(); geometryRemapping_.Clear(); // Finally sort again with the rewritten ID's Sort(batches.Begin(), batches.End(), CompareBatchesState); #endif }
void Zone::ClearDrawablesZone() { if (octant_ && lastWorldBoundingBox_.defined_) { PODVector<Drawable*> result; BoxOctreeQuery query(result, lastWorldBoundingBox_, DRAWABLE_GEOMETRY | DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); for (PODVector<Drawable*>::Iterator i = result.Begin(); i != result.End(); ++i) { Drawable* drawable = *i; unsigned drawableFlags = drawable->GetDrawableFlags(); if (drawableFlags & DRAWABLE_GEOMETRY) drawable->SetZone(0); else if (drawableFlags & DRAWABLE_ZONE) { Zone* zone = static_cast<Zone*>(drawable); zone->lastAmbientStartZone_.Reset(); zone->lastAmbientEndZone_.Reset(); } } } lastWorldBoundingBox_ = GetWorldBoundingBox(); lastAmbientStartZone_.Reset(); lastAmbientEndZone_.Reset(); }
void VertexDeclaration::Create(Graphics* graphics, const PODVector<VertexDeclarationElement>& elements) { SharedArrayPtr<D3DVERTEXELEMENT9> elementArray(new D3DVERTEXELEMENT9[elements.Size() + 1]); D3DVERTEXELEMENT9* dest = elementArray; for (Vector<VertexDeclarationElement>::ConstIterator i = elements.Begin(); i != elements.End(); ++i) { dest->Stream = (WORD)i->streamIndex_; dest->Offset = (WORD)i->offset_; dest->Type = d3dElementType[i->type_]; dest->Method = D3DDECLMETHOD_DEFAULT; dest->Usage = d3dElementUsage[i->semantic_]; dest->UsageIndex = i->index_; dest++; } dest->Stream = 0xff; dest->Offset = 0; dest->Type = D3DDECLTYPE_UNUSED; dest->Method = 0; dest->Usage = 0; dest->UsageIndex = 0; IDirect3DDevice9* device = graphics->GetImpl()->GetDevice(); HRESULT hr = device->CreateVertexDeclaration(elementArray, &declaration_); if (FAILED(hr)) { URHO3D_SAFE_RELEASE(declaration_); URHO3D_LOGD3DERROR("Failed to create vertex declaration", hr); } }
void PhysicsWorld::Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask) { ATOMIC_PROFILE(PhysicsRaycast); if (maxDistance >= M_INFINITY) ATOMIC_LOGWARNING("Infinite maxDistance in physics raycast is not supported"); btCollisionWorld::AllHitsRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_)); rayCallback.m_collisionFilterGroup = (short)0xffff; rayCallback.m_collisionFilterMask = (short)collisionMask; world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback); for (int i = 0; i < rayCallback.m_collisionObjects.size(); ++i) { PhysicsRaycastResult newResult; newResult.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObjects[i]->getUserPointer()); newResult.position_ = ToVector3(rayCallback.m_hitPointWorld[i]); newResult.normal_ = ToVector3(rayCallback.m_hitNormalWorld[i]); newResult.distance_ = (newResult.position_ - ray.origin_).Length(); newResult.hitFraction_ = rayCallback.m_closestHitFraction; result.Push(newResult); } Sort(result.Begin(), result.End(), CompareRaycastResults); }
// ---------------------------------------------------------------------------- void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData) { using namespace NodeRemoved; if (solver_->tree == NULL) return; Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr()); // Remove cached IKEffectors from our list PODVector<Node*> nodes; node->GetChildrenWithComponent<IKEffector>(nodes, true); for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it) { IKEffector* effector = (*it)->GetComponent<IKEffector>(); effector->SetIKEffector(NULL); effectorList_.RemoveSwap(effector); } // Special case, if the node being destroyed is the root node, destroy the // solver's tree instead of destroying the single node. Calling // ik_node_destroy() on the solver's root node will cause segfaults. ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID()); if (ikNode != NULL) { if (ikNode == solver_->tree) ik_solver_destroy_tree(solver_); else ik_node_destroy(ikNode); MarkSolverTreeDirty(); } }
void VertexDeclaration::Create(Graphics* graphics, const PODVector<VertexDeclarationElement>& elements) { SharedArrayPtr<D3DVERTEXELEMENT9> elementArray(new D3DVERTEXELEMENT9[elements.Size() + 1]); D3DVERTEXELEMENT9* dest = elementArray; for (Vector<VertexDeclarationElement>::ConstIterator i = elements.Begin(); i != elements.End(); ++i) { dest->Stream = i->stream_; dest->Offset = i->offset_; dest->Type = d3dElementType[i->element_]; dest->Method = D3DDECLMETHOD_DEFAULT; dest->Usage = d3dElementUsage[i->element_]; dest->UsageIndex = d3dElementUsageIndex[i->element_]; dest++; } dest->Stream = 0xff; dest->Offset = 0; dest->Type = D3DDECLTYPE_UNUSED; dest->Method = 0; dest->Usage = 0; dest->UsageIndex = 0; IDirect3DDevice9* device = graphics->GetImpl()->GetDevice(); if (!device) return; device->CreateVertexDeclaration(elementArray, &declaration_); }
void VertexBuffer::UpdateOffsets(PODVector<VertexElement>& elements) { unsigned elementOffset = 0; for (PODVector<VertexElement>::Iterator i = elements.Begin(); i != elements.End(); ++i) { i->offset_ = elementOffset; elementOffset += ELEMENT_TYPESIZES[i->type_]; } }
void Renderer2D::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { unsigned resultSize = results.Size(); for (unsigned i = 0; i < drawables_.Size(); ++i) { if (drawables_[i]->GetViewMask() & query.viewMask_) drawables_[i]->ProcessRayQuery(query, results); } if (results.Size() != resultSize) Sort(results.Begin() + resultSize, results.End(), CompareRayQueryResults); }
// ---------------------------------------------------------------------------- void IKSolver::HandleNodeRemoved(StringHash eventType, VariantMap& eventData) { using namespace NodeRemoved; if (solver_->tree == nullptr) return; auto* node = static_cast<Node*>(eventData[P_NODE].GetPtr()); // Remove cached IKEffectors from our list PODVector<IKEffector*> effectors; node->GetComponents<IKEffector>(effectors, true); for (PODVector<IKEffector*>::ConstIterator it = effectors.Begin(); it != effectors.End(); ++it) { (*it)->SetIKEffectorNode(nullptr); effectorList_.RemoveSwap(*it); } PODVector<IKConstraint*> constraints; node->GetComponents<IKConstraint>(constraints, true); for (PODVector<IKConstraint*>::ConstIterator it = constraints.Begin(); it != constraints.End(); ++it) { constraintList_.RemoveSwap(*it); } // Special case, if the node being destroyed is the root node, destroy the // solver's tree instead of destroying the single node. Calling // ik_node_destroy() on the solver's root node will cause segfaults. ik_node_t* ikNode = ik_node_find_child(solver_->tree, node->GetID()); if (ikNode != nullptr) { if (ikNode == solver_->tree) ik_solver_destroy_tree(solver_); else ik_node_destroy(ikNode); MarkChainsNeedUpdating(); } }
// ---------------------------------------------------------------------------- void IKSolver::RebuildTree() { assert(node_ != NULL); ik_node_t* ikRoot = CreateIKNode(node_); ik_solver_set_tree(solver_, ikRoot); PODVector<Node*> effectorNodes; node_->GetChildrenWithComponent<IKEffector>(effectorNodes, true); for (PODVector<Node*>::ConstIterator it = effectorNodes.Begin(); it != effectorNodes.End(); ++it) { BuildTreeToEffector(*it); } }
void RigidBody::ReleaseBody() { if (body_) { // Release all constraints which refer to this body // Make a copy for iteration PODVector<Constraint*> constraints = constraints_; for (PODVector<Constraint*>::Iterator i = constraints.Begin(); i != constraints.End(); ++i) (*i)->ReleaseConstraint(); RemoveBodyFromWorld(); body_.Reset(); } }
void RigidBody2D::OnNodeSet(Node* node) { if (node) { node->AddListener(this); PODVector<CollisionShape2D*> shapes; node_->GetDerivedComponents<CollisionShape2D>(shapes); for (PODVector<CollisionShape2D*>::Iterator i = shapes.Begin(); i != shapes.End(); ++i) { (*i)->CreateFixture(); AddCollisionShape2D(*i); } } }
// ---------------------------------------------------------------------------- void GravityManager::AddGravityVectorsRecursively(Node* node) { // Recursively retrieve all nodes that have a gravity probe component and // add them to our internal list of gravity probe nodes. Note that it // should not be possible for there to be duplicates; scene graphs can't // have loops. PODVector<Node*> gravityVectorNodesToAdd; node->GetChildrenWithComponent<GravityVector>(gravityVectorNodesToAdd, true); // Don't forget to check this node's components as well if(node->GetComponent<GravityVector>()) gravityVectorNodesToAdd.Push(node); PODVector<Node*>::Iterator it = gravityVectorNodesToAdd.Begin(); for(; it != gravityVectorNodesToAdd.End(); ++it) gravityVectors_.Push((*it)->GetComponent<GravityVector>()); }
// ---------------------------------------------------------------------------- void IKSolver::HandleNodeAdded(StringHash eventType, VariantMap& eventData) { using namespace NodeAdded; if (solver_->tree == NULL) return; Node* node = static_cast<Node*>(eventData[P_NODE].GetPtr()); PODVector<Node*> nodes; node->GetChildrenWithComponent<IKEffector>(nodes, true); for (PODVector<Node*>::ConstIterator it = nodes.Begin(); it != nodes.End(); ++it) { BuildTreeToEffector(*it); effectorList_.Push((*it)->GetComponent<IKEffector>()); } }
// ---------------------------------------------------------------------------- void GravityManager::RemoveGravityVectorsRecursively(Node* node) { // Recursively retrieve all nodes that have a gravity probe component PODVector<Node*> gravityVectorNodesToRemove; node->GetChildrenWithComponent<GravityVector>(gravityVectorNodesToRemove, true); // Don't forget to check this node's components as well if(node->GetComponent<GravityVector>()) gravityVectorNodesToRemove.Push(node); // search for found components and remove them from our internal list PODVector<Node*>::ConstIterator it = gravityVectorNodesToRemove.Begin(); for(; it != gravityVectorNodesToRemove.End(); ++it) { PODVector<GravityVector*>::Iterator gravityNode = gravityVectors_.Find((*it)->GetComponent<GravityVector>()); if(gravityNode != gravityVectors_.End()) gravityVectors_.Erase(gravityNode); } }
void Menu::ShowPopup(bool enable) { if (!popup_) return; if (enable) { OnShowPopup(); popup_->SetVar(VAR_ORIGIN, this); static_cast<Window*>(popup_.Get())->SetModal(true); popup_->SetPosition(GetScreenPosition() + popupOffset_); popup_->SetVisible(true); // BringToFront() is unreliable in this case as it takes into account only input-enabled elements. // Rather just force priority to max popup_->SetPriority(M_MAX_INT); } else { OnHidePopup(); // If the popup has child menus, hide their popups as well PODVector<UIElement*> children; popup_->GetChildren(children, true); for (PODVector<UIElement*>::ConstIterator i = children.Begin(); i != children.End(); ++i) { Menu* menu = dynamic_cast<Menu*>(*i); if (menu) menu->ShowPopup(false); } static_cast<Window*>(popup_.Get())->SetModal(false); const_cast<VariantMap&>(popup_->GetVars()).Erase(VAR_ORIGIN); popup_->SetVisible(false); popup_->Remove(); } SetVar(VAR_SHOW_POPUP, enable); showPopup_ = enable; selected_ = enable; }
PODVector<CrowdAgent*> CrowdManager::GetAgents(Node* node, bool inCrowdFilter) const { if (!node) node = GetScene(); PODVector<CrowdAgent*> agents; node->GetComponents<CrowdAgent>(agents, true); if (inCrowdFilter) { PODVector<CrowdAgent*>::Iterator i = agents.Begin(); while (i != agents.End()) { if ((*i)->IsInCrowd()) ++i; else i = agents.Erase(i); } } return agents; }
void Zone::OnMarkedDirty(Node* node) { // Due to the octree query and weak pointer manipulation, is not safe from worker threads Scene* scene = GetScene(); if (scene && scene->IsThreadedUpdate()) { scene->DelayedMarkedDirty(this); return; } Drawable::OnMarkedDirty(node); // When marked dirty, clear the cached zone from all drawables inside the zone bounding box, // and mark gradient dirty in all neighbor zones if (octant_ && lastWorldBoundingBox_.defined_) { PODVector<Drawable*> result; BoxOctreeQuery query(result, lastWorldBoundingBox_, DRAWABLE_GEOMETRY | DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); for (PODVector<Drawable*>::Iterator i = result.Begin(); i != result.End(); ++i) { Drawable* drawable = *i; unsigned drawableFlags = drawable->GetDrawableFlags(); if (drawableFlags & DRAWABLE_GEOMETRY) { if (drawable->GetZone() == this) drawable->SetZone(0); } else if (drawableFlags & DRAWABLE_ZONE) { Zone* zone = static_cast<Zone*>(drawable); zone->lastAmbientStartZone_.Reset(); zone->lastAmbientEndZone_.Reset(); } } } lastWorldBoundingBox_ = GetWorldBoundingBox(); lastAmbientStartZone_.Reset(); lastAmbientEndZone_.Reset(); inverseWorldDirty_ = true; }
void Menu::ShowPopup(bool enable) { if (!popup_) return; if (enable) { // Find the UI root element for showing the popup UIElement* root = GetRoot(); if (!root) return; OnShowPopup(); if (popup_->GetParent() != root) root->AddChild(popup_); popup_->SetPosition(GetScreenPosition() + popupOffset_); popup_->SetVisible(true); popup_->SetVar(originHash, (void*)this); popup_->BringToFront(); } else { // If the popup has child menus, hide their popups as well PODVector<UIElement*> children; popup_->GetChildren(children, true); for (PODVector<UIElement*>::ConstIterator i = children.Begin(); i != children.End(); ++i) { Menu* menu = dynamic_cast<Menu*>(*i); if (menu) menu->ShowPopup(false); } popup_->SetVar(originHash, Variant::EMPTY); popup_->SetVisible(false); popup_->Remove(); } showPopup_ = enable; selected_ = enable; }
void Menu::ShowPopup(bool enable) { if (!popup_) return; if (enable) { OnShowPopup(); popup_->SetVar(VAR_ORIGIN, (void*)this); static_cast<Window*>(popup_.Get())->SetModal(true); popup_->SetPosition(GetScreenPosition() + popupOffset_); popup_->SetVisible(true); popup_->BringToFront(); } else { // If the popup has child menus, hide their popups as well PODVector<UIElement*> children; popup_->GetChildren(children, true); for (PODVector<UIElement*>::ConstIterator i = children.Begin(); i != children.End(); ++i) { Menu* menu = dynamic_cast<Menu*>(*i); if (menu) menu->ShowPopup(false); } static_cast<Window*>(popup_.Get())->SetModal(false); const_cast<VariantMap&>(popup_->GetVars()).Erase(VAR_ORIGIN); popup_->SetVisible(false); popup_->Remove(); } SetVar(VAR_SHOW_POPUP, enable); showPopup_ = enable; selected_ = enable; }
void UIDrag::HandleUpdate(StringHash eventType, VariantMap& eventData) { auto* ui = GetSubsystem<UI>(); UIElement* root = ui->GetRoot(); auto* input = GetSubsystem<Input>(); unsigned n = input->GetNumTouches(); for (unsigned i = 0; i < n; i++) { Text* t = (Text*)root->GetChild("Touch " + String(i)); TouchState* ts = input->GetTouch(i); t->SetText("Touch " + String(ts->touchID_)); IntVector2 pos = ts->position_; pos.y_ -= 30; t->SetPosition(pos); t->SetVisible(true); } for (unsigned i = n; i < 10; i++) { Text* t = (Text*)root->GetChild("Touch " + String(i)); t->SetVisible(false); } if (input->GetKeyPress(KEY_SPACE)) { PODVector<UIElement*> elements; root->GetChildrenWithTag(elements, "SomeTag"); for (PODVector<UIElement*>::ConstIterator i = elements.Begin(); i != elements.End(); ++i) { UIElement* element = *i; element->SetVisible(!element->IsVisible()); } } }
void Terrain::CreateGeometry() { recreateTerrain_ = false; if (!node_) return; PROFILE(CreateTerrainGeometry); unsigned prevNumPatches = patches_.Size(); // Determine number of LOD levels unsigned lodSize = patchSize_; numLodLevels_ = 1; while (lodSize > MIN_PATCH_SIZE && numLodLevels_ < MAX_LOD_LEVELS) { lodSize >>= 1; ++numLodLevels_; } // Determine total terrain size patchWorldSize_ = Vector2(spacing_.x_ * (float)patchSize_, spacing_.z_ * (float)patchSize_); if (heightMap_) { numPatches_ = IntVector2((heightMap_->GetWidth() - 1) / patchSize_, (heightMap_->GetHeight() - 1) / patchSize_); numVertices_ = IntVector2(numPatches_.x_ * patchSize_ + 1, numPatches_.y_ * patchSize_ + 1); patchWorldOrigin_ = Vector2(-0.5f * (float)numPatches_.x_ * patchWorldSize_.x_, -0.5f * (float)numPatches_.y_ * patchWorldSize_.y_); heightData_ = new float[numVertices_.x_ * numVertices_.y_]; } else { numPatches_ = IntVector2::ZERO; numVertices_ = IntVector2::ZERO; patchWorldOrigin_ = Vector2::ZERO; heightData_.Reset(); } // Remove old patch nodes which are not needed PODVector<Node*> oldPatchNodes; node_->GetChildrenWithComponent<TerrainPatch>(oldPatchNodes); for (PODVector<Node*>::Iterator i = oldPatchNodes.Begin(); i != oldPatchNodes.End(); ++i) { bool nodeOk = false; Vector<String> coords = (*i)->GetName().Substring(6).Split('_'); if (coords.Size() == 2) { int x = ToInt(coords[0]); int z = ToInt(coords[1]); if (x < numPatches_.x_ && z < numPatches_.y_) nodeOk = true; } if (!nodeOk) node_->RemoveChild(*i); } patches_.Clear(); if (heightMap_) { // Copy heightmap data const unsigned char* src = heightMap_->GetData(); float* dest = heightData_; unsigned imgComps = heightMap_->GetComponents(); unsigned imgRow = heightMap_->GetWidth() * imgComps; if (imgComps == 1) { for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) *dest++ = (float)src[imgRow * (numVertices_.y_ - 1 - z) + x] * spacing_.y_; } } else { // If more than 1 component, use the green channel for more accuracy for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) *dest++ = ((float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x] + (float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x + 1] / 256.0f) * spacing_.y_; } } if (smoothing_) SmoothHeightMap(); patches_.Reserve(numPatches_.x_ * numPatches_.y_); bool enabled = IsEnabledEffective(); // Create patches and set node transforms for (int z = 0; z < numPatches_.y_; ++z) { for (int x = 0; x < numPatches_.x_; ++x) { String nodeName = "Patch_" + String(x) + "_" + String(z); Node* patchNode = node_->GetChild(nodeName); if (!patchNode) { // Create the patch scene node as local and temporary so that it is not unnecessarily serialized to either // file or replicated over the network patchNode = node_->CreateChild(nodeName, LOCAL); patchNode->SetTemporary(true); } patchNode->SetPosition(Vector3(patchWorldOrigin_.x_ + (float)x * patchWorldSize_.x_, 0.0f, patchWorldOrigin_.y_ + (float)z * patchWorldSize_.y_)); TerrainPatch* patch = patchNode->GetOrCreateComponent<TerrainPatch>(); patch->SetOwner(this); patch->SetCoordinates(IntVector2(x, z)); // Copy initial drawable parameters patch->SetEnabled(enabled); patch->SetMaterial(material_); patch->SetDrawDistance(drawDistance_); patch->SetShadowDistance(shadowDistance_); patch->SetLodBias(lodBias_); patch->SetViewMask(viewMask_); patch->SetLightMask(lightMask_); patch->SetShadowMask(shadowMask_); patch->SetZoneMask(zoneMask_); patch->SetMaxLights(maxLights_); patch->SetCastShadows(castShadows_); patch->SetOccluder(occluder_); patch->SetOccludee(occludee_); patches_.Push(WeakPtr<TerrainPatch>(patch)); } } // Create the shared index data CreateIndexData(); // Create vertex data for patches for (Vector<WeakPtr<TerrainPatch> >::Iterator i = patches_.Begin(); i != patches_.End(); ++i) { CreatePatchGeometry(*i); CalculateLodErrors(*i); SetNeighbors(*i); } } // Send event only if new geometry was generated, or the old was cleared if (patches_.Size() || prevNumPatches) { using namespace TerrainCreated; VariantMap& eventData = GetEventDataMap(); eventData[P_NODE] = node_; node_->SendEvent(E_TERRAINCREATED, eventData); } }
void ListView::SetSelections(const PODVector<unsigned>& indices) { // Make a weak pointer to self to check for destruction as a response to events WeakPtr<ListView> self(this); unsigned numItems = GetNumItems(); // Remove first items that should no longer be selected for (PODVector<unsigned>::Iterator i = selections_.Begin(); i != selections_.End();) { unsigned index = *i; if (!indices.Contains(index)) { i = selections_.Erase(i); using namespace ItemSelected; VariantMap& eventData = GetEventDataMap(); eventData[P_ELEMENT] = this; eventData[P_SELECTION] = index; SendEvent(E_ITEMDESELECTED, eventData); if (self.Expired()) return; } else ++i; } bool added = false; // Then add missing items for (PODVector<unsigned>::ConstIterator i = indices.Begin(); i != indices.End(); ++i) { unsigned index = *i; if (index < numItems) { // In singleselect mode, resend the event even for the same selection bool duplicate = selections_.Contains(index); if (!duplicate || !multiselect_) { if (!duplicate) { selections_.Push(index); added = true; } using namespace ItemSelected; VariantMap& eventData = GetEventDataMap(); eventData[P_ELEMENT] = this; eventData[P_SELECTION] = *i; SendEvent(E_ITEMSELECTED, eventData); if (self.Expired()) return; } } // If no multiselect enabled, allow setting only one item if (!multiselect_) break; } // Re-sort selections if necessary if (added) Sort(selections_.Begin(), selections_.End()); UpdateSelectionEffect(); SendEvent(E_SELECTIONCHANGED); }
void Terrain::CreateGeometry() { recreateTerrain_ = false; if (!node_) return; URHO3D_PROFILE(CreateTerrainGeometry); unsigned prevNumPatches = patches_.Size(); // Determine number of LOD levels unsigned lodSize = (unsigned)patchSize_; numLodLevels_ = 1; while (lodSize > MIN_PATCH_SIZE && numLodLevels_ < maxLodLevels_) { lodSize >>= 1; ++numLodLevels_; } // Determine total terrain size patchWorldSize_ = Vector2(spacing_.x_ * (float)patchSize_, spacing_.z_ * (float)patchSize_); bool updateAll = false; if (heightMap_) { numPatches_ = IntVector2((heightMap_->GetWidth() - 1) / patchSize_, (heightMap_->GetHeight() - 1) / patchSize_); numVertices_ = IntVector2(numPatches_.x_ * patchSize_ + 1, numPatches_.y_ * patchSize_ + 1); patchWorldOrigin_ = Vector2(-0.5f * (float)numPatches_.x_ * patchWorldSize_.x_, -0.5f * (float)numPatches_.y_ * patchWorldSize_.y_); if (numVertices_ != lastNumVertices_ || lastSpacing_ != spacing_ || patchSize_ != lastPatchSize_) updateAll = true; unsigned newDataSize = (unsigned)(numVertices_.x_ * numVertices_.y_); // Create new height data if terrain size changed if (!heightData_ || updateAll) heightData_ = new float[newDataSize]; // Ensure that the source (unsmoothed) data exists if smoothing is active if (smoothing_ && (!sourceHeightData_ || updateAll)) { sourceHeightData_ = new float[newDataSize]; updateAll = true; } else if (!smoothing_) sourceHeightData_.Reset(); } else { numPatches_ = IntVector2::ZERO; numVertices_ = IntVector2::ZERO; patchWorldOrigin_ = Vector2::ZERO; heightData_.Reset(); sourceHeightData_.Reset(); } lastNumVertices_ = numVertices_; lastPatchSize_ = patchSize_; lastSpacing_ = spacing_; // Remove old patch nodes which are not needed if (updateAll) { URHO3D_PROFILE(RemoveOldPatches); PODVector<Node*> oldPatchNodes; node_->GetChildrenWithComponent<TerrainPatch>(oldPatchNodes); for (PODVector<Node*>::Iterator i = oldPatchNodes.Begin(); i != oldPatchNodes.End(); ++i) { bool nodeOk = false; Vector<String> coords = (*i)->GetName().Substring(6).Split('_'); if (coords.Size() == 2) { int x = ToInt(coords[0]); int z = ToInt(coords[1]); if (x < numPatches_.x_ && z < numPatches_.y_) nodeOk = true; } if (!nodeOk) node_->RemoveChild(*i); } } // Keep track of which patches actually need an update PODVector<bool> dirtyPatches((unsigned)(numPatches_.x_ * numPatches_.y_)); for (unsigned i = 0; i < dirtyPatches.Size(); ++i) dirtyPatches[i] = updateAll; patches_.Clear(); if (heightMap_) { // Copy heightmap data const unsigned char* src = heightMap_->GetData(); float* dest = smoothing_ ? sourceHeightData_ : heightData_; unsigned imgComps = heightMap_->GetComponents(); unsigned imgRow = heightMap_->GetWidth() * imgComps; IntRect updateRegion(-1, -1, -1, -1); if (imgComps == 1) { URHO3D_PROFILE(CopyHeightData); for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) { float newHeight = (float)src[imgRow * (numVertices_.y_ - 1 - z) + x] * spacing_.y_; if (updateAll) *dest = newHeight; else { if (*dest != newHeight) { *dest = newHeight; GrowUpdateRegion(updateRegion, x, z); } } ++dest; } } } else { URHO3D_PROFILE(CopyHeightData); // If more than 1 component, use the green channel for more accuracy for (int z = 0; z < numVertices_.y_; ++z) { for (int x = 0; x < numVertices_.x_; ++x) { float newHeight = ((float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x] + (float)src[imgRow * (numVertices_.y_ - 1 - z) + imgComps * x + 1] / 256.0f) * spacing_.y_; if (updateAll) *dest = newHeight; else { if (*dest != newHeight) { *dest = newHeight; GrowUpdateRegion(updateRegion, x, z); } } ++dest; } } } // If updating a region of the heightmap, check which patches change if (!updateAll) { int lodExpand = 1 << (numLodLevels_ - 1); // Expand the right & bottom 1 pixel more, as patches share vertices at the edge updateRegion.left_ -= lodExpand; updateRegion.right_ += lodExpand + 1; updateRegion.top_ -= lodExpand; updateRegion.bottom_ += lodExpand + 1; int sX = Max(updateRegion.left_ / patchSize_, 0); int eX = Min(updateRegion.right_ / patchSize_, numPatches_.x_ - 1); int sY = Max(updateRegion.top_ / patchSize_, 0); int eY = Min(updateRegion.bottom_ / patchSize_, numPatches_.y_ - 1); for (int y = sY; y <= eY; ++y) { for (int x = sX; x <= eX; ++x) dirtyPatches[y * numPatches_.x_ + x] = true; } } patches_.Reserve((unsigned)(numPatches_.x_ * numPatches_.y_)); bool enabled = IsEnabledEffective(); { URHO3D_PROFILE(CreatePatches); // Create patches and set node transforms for (int z = 0; z < numPatches_.y_; ++z) { for (int x = 0; x < numPatches_.x_; ++x) { String nodeName = "Patch_" + String(x) + "_" + String(z); Node* patchNode = node_->GetChild(nodeName); if (!patchNode) { // Create the patch scene node as local and temporary so that it is not unnecessarily serialized to either // file or replicated over the network patchNode = node_->CreateChild(nodeName, LOCAL); patchNode->SetTemporary(true); } patchNode->SetPosition(Vector3(patchWorldOrigin_.x_ + (float)x * patchWorldSize_.x_, 0.0f, patchWorldOrigin_.y_ + (float)z * patchWorldSize_.y_)); TerrainPatch* patch = patchNode->GetComponent<TerrainPatch>(); if (!patch) { patch = patchNode->CreateComponent<TerrainPatch>(); patch->SetOwner(this); patch->SetCoordinates(IntVector2(x, z)); // Copy initial drawable parameters patch->SetEnabled(enabled); patch->SetMaterial(material_); patch->SetDrawDistance(drawDistance_); patch->SetShadowDistance(shadowDistance_); patch->SetLodBias(lodBias_); patch->SetViewMask(viewMask_); patch->SetLightMask(lightMask_); patch->SetShadowMask(shadowMask_); patch->SetZoneMask(zoneMask_); patch->SetMaxLights(maxLights_); patch->SetCastShadows(castShadows_); patch->SetOccluder(occluder_); patch->SetOccludee(occludee_); } patches_.Push(WeakPtr<TerrainPatch>(patch)); } } } // Create the shared index data if (updateAll) CreateIndexData(); // Create vertex data for patches. First update smoothing to ensure normals are calculated correctly across patch borders if (smoothing_) { URHO3D_PROFILE(UpdateSmoothing); for (unsigned i = 0; i < patches_.Size(); ++i) { if (dirtyPatches[i]) { TerrainPatch* patch = patches_[i]; const IntVector2& coords = patch->GetCoordinates(); int startX = coords.x_ * patchSize_; int endX = startX + patchSize_; int startZ = coords.y_ * patchSize_; int endZ = startZ + patchSize_; for (int z = startZ; z <= endZ; ++z) { for (int x = startX; x <= endX; ++x) { float smoothedHeight = ( GetSourceHeight(x - 1, z - 1) + GetSourceHeight(x, z - 1) * 2.0f + GetSourceHeight(x + 1, z - 1) + GetSourceHeight(x - 1, z) * 2.0f + GetSourceHeight(x, z) * 4.0f + GetSourceHeight(x + 1, z) * 2.0f + GetSourceHeight(x - 1, z + 1) + GetSourceHeight(x, z + 1) * 2.0f + GetSourceHeight(x + 1, z + 1) ) / 16.0f; heightData_[z * numVertices_.x_ + x] = smoothedHeight; } } } } } for (unsigned i = 0; i < patches_.Size(); ++i) { TerrainPatch* patch = patches_[i]; if (dirtyPatches[i]) { CreatePatchGeometry(patch); CalculateLodErrors(patch); } SetPatchNeighbors(patch); } } // Send event only if new geometry was generated, or the old was cleared if (patches_.Size() || prevNumPatches) { using namespace TerrainCreated; VariantMap& eventData = GetEventDataMap(); eventData[P_NODE] = node_; node_->SendEvent(E_TERRAINCREATED, eventData); } }
void RigidBody::AddBodyToWorld() { if (!physicsWorld_) return; URHO3D_PROFILE(AddBodyToWorld); if (mass_ < 0.0f) mass_ = 0.0f; if (body_) RemoveBodyFromWorld(); else { // Correct inertia will be calculated below btVector3 localInertia(0.0f, 0.0f, 0.0f); body_ = new btRigidBody(mass_, this, shiftedCompoundShape_, localInertia); body_->setUserPointer(this); // Check for existence of the SmoothedTransform component, which should be created by now in network client mode. // If it exists, subscribe to its change events smoothedTransform_ = GetComponent<SmoothedTransform>(); if (smoothedTransform_) { SubscribeToEvent(smoothedTransform_, E_TARGETPOSITION, URHO3D_HANDLER(RigidBody, HandleTargetPosition)); SubscribeToEvent(smoothedTransform_, E_TARGETROTATION, URHO3D_HANDLER(RigidBody, HandleTargetRotation)); } // Check if CollisionShapes already exist in the node and add them to the compound shape. // Do not update mass yet, but do it once all shapes have been added PODVector<CollisionShape*> shapes; node_->GetComponents<CollisionShape>(shapes); for (PODVector<CollisionShape*>::Iterator i = shapes.Begin(); i != shapes.End(); ++i) (*i)->NotifyRigidBody(false); // Check if this node contains Constraint components that were waiting for the rigid body to be created, and signal them // to create themselves now PODVector<Constraint*> constraints; node_->GetComponents<Constraint>(constraints); for (PODVector<Constraint*>::Iterator i = constraints.Begin(); i != constraints.End(); ++i) (*i)->CreateConstraint(); } UpdateMass(); UpdateGravity(); int flags = body_->getCollisionFlags(); if (trigger_) flags |= btCollisionObject::CF_NO_CONTACT_RESPONSE; else flags &= ~btCollisionObject::CF_NO_CONTACT_RESPONSE; if (kinematic_) flags |= btCollisionObject::CF_KINEMATIC_OBJECT; else flags &= ~btCollisionObject::CF_KINEMATIC_OBJECT; body_->setCollisionFlags(flags); body_->forceActivationState(kinematic_ ? DISABLE_DEACTIVATION : ISLAND_SLEEPING); if (!IsEnabledEffective()) return; btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld(); world->addRigidBody(body_, (short)collisionLayer_, (short)collisionMask_); inWorld_ = true; readdBody_ = false; if (mass_ > 0.0f) Activate(); else { SetLinearVelocity(Vector3::ZERO); SetAngularVelocity(Vector3::ZERO); } }
void Zone::UpdateAmbientGradient() { // In case no neighbor zones are found, reset ambient start/end with own ambient color ambientStartColor_ = ambientColor_; ambientEndColor_ = ambientColor_; lastAmbientStartZone_ = this; lastAmbientEndZone_ = this; if (octant_) { const Matrix3x4& worldTransform = node_->GetWorldTransform(); Vector3 center = boundingBox_.Center(); Vector3 minZPosition = worldTransform * Vector3(center.x_, center.y_, boundingBox_.min_.z_); Vector3 maxZPosition = worldTransform * Vector3(center.x_, center.y_, boundingBox_.max_.z_); PODVector<Zone*> result; { PointOctreeQuery query(reinterpret_cast<PODVector<Drawable*>&>(result), minZPosition, DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); } // Gradient start position: get the highest priority zone that is not this zone int bestPriority = M_MIN_INT; Zone* bestZone = 0; for (PODVector<Zone*>::ConstIterator i = result.Begin(); i != result.End(); ++i) { Zone* zone = *i; int priority = zone->GetPriority(); if (priority > bestPriority && zone != this && zone->IsInside(minZPosition)) { bestZone = zone; bestPriority = priority; } } if (bestZone) { ambientStartColor_ = bestZone->GetAmbientColor(); lastAmbientStartZone_ = bestZone; } // Do the same for gradient end position { PointOctreeQuery query(reinterpret_cast<PODVector<Drawable*>&>(result), maxZPosition, DRAWABLE_ZONE); octant_->GetRoot()->GetDrawables(query); } bestPriority = M_MIN_INT; bestZone = 0; for (PODVector<Zone*>::ConstIterator i = result.Begin(); i != result.End(); ++i) { Zone* zone = *i; int priority = zone->GetPriority(); if (priority > bestPriority && zone != this && zone->IsInside(maxZPosition)) { bestZone = zone; bestPriority = priority; } } if (bestZone) { ambientEndColor_ = bestZone->GetAmbientColor(); lastAmbientEndZone_ = bestZone; } } }
void AnimatedModel::FinalizeBoneBoundingBoxes() { Vector<Bone>& bones = skeleton_.GetModifiableBones(); PODVector<AnimatedModel*> models; GetComponents<AnimatedModel>(models); if (models.Size() > 1) { // Reset first to the model resource's original bone bounding information if available (should be) if (model_) { const Vector<Bone>& modelBones = model_->GetSkeleton().GetBones(); for (unsigned i = 0; i < bones.Size() && i < modelBones.Size(); ++i) { bones[i].collisionMask_ = modelBones[i].collisionMask_; bones[i].radius_ = modelBones[i].radius_; bones[i].boundingBox_ = modelBones[i].boundingBox_; } } // Get matching bones from all non-master models and merge their bone bounding information // to prevent culling errors (master model may not have geometry in all bones, or the bounds are smaller) for (PODVector<AnimatedModel*>::Iterator i = models.Begin(); i != models.End(); ++i) { if ((*i) == this) continue; Skeleton& otherSkeleton = (*i)->GetSkeleton(); for (Vector<Bone>::Iterator j = bones.Begin(); j != bones.End(); ++j) { Bone* otherBone = otherSkeleton.GetBone(j->nameHash_); if (otherBone) { if (otherBone->collisionMask_ & BONECOLLISION_SPHERE) { j->collisionMask_ |= BONECOLLISION_SPHERE; j->radius_ = Max(j->radius_, otherBone->radius_); } if (otherBone->collisionMask_ & BONECOLLISION_BOX) { j->collisionMask_ |= BONECOLLISION_BOX; if (j->boundingBox_.Defined()) j->boundingBox_.Merge(otherBone->boundingBox_); else j->boundingBox_.Define(otherBone->boundingBox_); } } } } } // Remove collision information from dummy bones that do not affect skinning, to prevent them from being merged // to the bounding box and making it artificially large for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i) { if (i->collisionMask_ & BONECOLLISION_BOX && i->boundingBox_.Size().Length() < M_EPSILON) i->collisionMask_ &= ~BONECOLLISION_BOX; if (i->collisionMask_ & BONECOLLISION_SPHERE && i->radius_ < M_EPSILON) i->collisionMask_ &= ~BONECOLLISION_SPHERE; } }