/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "Vegetation_precompiled.h" #include "BlockerComponent.h" #include #include #include #include #include #include #include #include #include #include namespace Vegetation { namespace BlockerUtil { static bool UpdateVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { if (classElement.GetVersion() < 1) { classElement.RemoveElementByName(AZ_CRC("UseRelativeUVW", 0x97a6718e)); } return true; } } void BlockerConfig::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { serialize->Class() ->Version(1, &BlockerUtil::UpdateVersion) ->Field("InheritBehavior", &BlockerConfig::m_inheritBehavior) ; AZ::EditContext* edit = serialize->GetEditContext(); if (edit) { edit->Class( "Vegetation Layer Blocker", "Vegetation blocker") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(0, &BlockerConfig::m_inheritBehavior, "Inherit Behavior", "Allow shapes, modifiers, filters of a parent to affect this area.") ; } } if (auto behaviorContext = azrtti_cast(context)) { behaviorContext->Class() ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview) ->Attribute(AZ::Script::Attributes::Category, "Vegetation") ->Constructor() ->Property("inheritBehavior", BehaviorValueProperty(&BlockerConfig::m_inheritBehavior)) ; } } void BlockerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) { AreaComponentBase::GetProvidedServices(services); } void BlockerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) { AreaComponentBase::GetIncompatibleServices(services); services.push_back(AZ_CRC("VegetationModifierService", 0xc551fca6)); } void BlockerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services) { services.push_back(AZ_CRC("ShapeService", 0xe86aa5fe)); } void BlockerComponent::Reflect(AZ::ReflectContext* context) { BlockerConfig::Reflect(context); AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { serialize->Class() ->Version(0) ->Field("Configuration", &BlockerComponent::m_configuration) ; } if (auto behaviorContext = azrtti_cast(context)) { behaviorContext->Constant("BlockerComponentTypeId", BehaviorConstant(BlockerComponentTypeId)); behaviorContext->Class()->RequestBus("BlockerRequestBus"); behaviorContext->EBus("BlockerRequestBus") ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview) ->Attribute(AZ::Script::Attributes::Category, "Vegetation") ->Event("GetAreaPriority", &BlockerRequestBus::Events::GetAreaPriority) ->Event("SetAreaPriority", &BlockerRequestBus::Events::SetAreaPriority) ->VirtualProperty("AreaPriority", "GetAreaPriority", "SetAreaPriority") ->Event("GetAreaLayer", &BlockerRequestBus::Events::GetAreaLayer) ->Event("SetAreaLayer", &BlockerRequestBus::Events::SetAreaLayer) ->VirtualProperty("AreaLayer", "GetAreaLayer", "SetAreaLayer") ->Event("GetAreaProductCount", &BlockerRequestBus::Events::GetAreaProductCount) ->Event("GetInheritBehavior", &BlockerRequestBus::Events::GetInheritBehavior) ->Event("SetInheritBehavior", &BlockerRequestBus::Events::SetInheritBehavior) ->VirtualProperty("InheritBehavior", "GetInheritBehavior", "SetInheritBehavior") ; } } BlockerComponent::BlockerComponent(const BlockerConfig& configuration) : AreaComponentBase(configuration) , m_configuration(configuration) { } void BlockerComponent::Activate() { UpdateShapeParams(); BlockerRequestBus::Handler::BusConnect(GetEntityId()); AreaComponentBase::Activate(); //must activate base last to connect AreaRequestBus once everything else is setup #if VEG_BLOCKER_ENABLE_CACHING AZStd::lock_guard cacheLock(m_cacheMutex); m_claimCacheMapping.clear(); #endif } void BlockerComponent::Deactivate() { AreaComponentBase::Deactivate(); //must deactivate base first to ensure AreaRequestBus disconnect waits for other threads BlockerRequestBus::Handler::BusDisconnect(); #if VEG_BLOCKER_ENABLE_CACHING AZStd::lock_guard cacheLock(m_cacheMutex); m_claimCacheMapping.clear(); #endif } void BlockerComponent::OnCompositionChanged() { UpdateShapeParams(); AreaComponentBase::OnCompositionChanged(); #if VEG_BLOCKER_ENABLE_CACHING AZStd::lock_guard cacheLock(m_cacheMutex); m_claimCacheMapping.clear(); #endif } bool BlockerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) { AreaComponentBase::ReadInConfig(baseConfig); if (auto config = azrtti_cast(baseConfig)) { m_configuration = *config; return true; } return false; } bool BlockerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const { AreaComponentBase::WriteOutConfig(outBaseConfig); if (auto config = azrtti_cast(outBaseConfig)) { *config = m_configuration; return true; } return false; } void BlockerComponent::UpdateShapeParams() { AZStd::lock_guard shapeLock(m_shapeMutex); GradientSignal::GetObbParamsFromShape(GetEntityId(), m_shapeBounds, m_shapeTransformInverse); } bool BlockerComponent::PrepareToClaim(EntityIdStack& stackIds) { UpdateShapeParams(); return true; } bool BlockerComponent::ClaimPosition(EntityIdStack& processedIds, const ClaimPoint& point, InstanceData& instanceData) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity); #if VEG_BLOCKER_ENABLE_CACHING { AZStd::lock_guard cacheLock(m_cacheMutex); auto claimItr = m_claimCacheMapping.find(point.m_handle); if (claimItr != m_claimCacheMapping.end()) { return claimItr->second; } } #endif // test shape bus as first pass to claim the point bool isInsideShape = true; for (const auto& id : processedIds) { LmbrCentral::ShapeComponentRequestsBus::EventResult(isInsideShape, id, &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, point.m_position); if (!isInsideShape) { #if VEG_BLOCKER_ENABLE_CACHING AZStd::lock_guard cacheLock(m_cacheMutex); m_claimCacheMapping[point.m_handle] = false; #endif return false; } } //generate details for a single vegetation instance instanceData.m_position = point.m_position; instanceData.m_normal = point.m_normal; instanceData.m_masks = point.m_masks; //determine if an instance can be created using the generated details for (const auto& id : processedIds) { bool accepted = true; FilterRequestBus::EnumerateHandlersId(id, [this, &instanceData, &accepted](FilterRequestBus::Events* handler) { accepted = handler->Evaluate(instanceData); return accepted; }); if (!accepted) { #if VEG_BLOCKER_ENABLE_CACHING AZStd::lock_guard cacheLock(m_cacheMutex); m_claimCacheMapping[point.m_handle] = false; #endif return false; } } #if VEG_BLOCKER_ENABLE_CACHING AZStd::lock_guard cacheLock(m_cacheMutex); m_claimCacheMapping[point.m_handle] = true; #endif return true; } void BlockerComponent::ClaimPositions(EntityIdStack& stackIds, ClaimContext& context) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity); //adding entity id to the stack of entity ids affecting vegetation EntityIdStack emptyIds; //when the inherit flag is disabled, as opposed to always inheriting, the stack must be cleared but preserved so redirecting to an empty stack to avoid copying EntityIdStack& processedIds = m_configuration.m_inheritBehavior ? stackIds : emptyIds; //adding current entity id to be processed uniformly EntityIdStackPusher stackPusher(processedIds, GetEntityId()); InstanceData instanceData; instanceData.m_id = GetEntityId(); instanceData.m_changeIndex = GetChangeIndex(); size_t numAvailablePoints = context.m_availablePoints.size(); for (size_t pointIndex = 0; pointIndex < numAvailablePoints; ) { ClaimPoint& point = context.m_availablePoints[pointIndex]; if (ClaimPosition(processedIds, point, instanceData)) { context.m_createdCallback(point, instanceData); //Swap an available point from the end of the list AZStd::swap(point, context.m_availablePoints.at(numAvailablePoints - 1)); --numAvailablePoints; continue; } ++pointIndex; } //resize to remove all used points context.m_availablePoints.resize(numAvailablePoints); } void BlockerComponent::UnclaimPosition(const ClaimHandle handle) { } AZ::Aabb BlockerComponent::GetEncompassingAabb() const { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity); AZ::Aabb bounds = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::EventResult(bounds, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); return bounds; } AZ::u32 BlockerComponent::GetProductCount() const { return 0; } AZ::u32 BlockerComponent::GetAreaPriority() const { return m_configuration.m_priority; } void BlockerComponent::SetAreaPriority(AZ::u32 priority) { m_configuration.m_priority = priority; LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged); } AZ::u32 BlockerComponent::GetAreaLayer() const { return m_configuration.m_layer; } void BlockerComponent::SetAreaLayer(AZ::u32 layer) { m_configuration.m_layer = layer; LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged); } AZ::u32 BlockerComponent::GetAreaProductCount() const { return GetProductCount(); } bool BlockerComponent::GetInheritBehavior() const { return m_configuration.m_inheritBehavior; } void BlockerComponent::SetInheritBehavior(bool value) { m_configuration.m_inheritBehavior = value; LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged); } }