/* * 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 "StdAfx.h" #include <Editor/EditorBlastMeshDataComponent.h> #include <Editor/EditorBlastSliceAssetHandler.h> #include <AzCore/Component/Entity.h> #include <AzCore/IO/FileIO.h> #include <AzCore/IO/GenericStreams.h> #include <AzCore/Serialization/ObjectStream.h> #include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/Utils.h> #include <AzCore/Slice/SliceComponent.h> #include <AzCore/StringFunc/StringFunc.h> #include <AzFramework/StringFunc/StringFunc.h> #include <SceneAPI/SceneCore/Containers/RuleContainer.h> #include <SceneAPI/SceneCore/Containers/SceneManifest.h> #include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h> #include <SceneAPI/SceneData/Groups/MeshGroup.h> #include <SceneAPI/SceneData/ManifestBase/SceneNodeSelectionList.h> #include <SceneAPI/SceneData/Rules/MaterialRule.h> #include <GFxFramework/MaterialIO/Material.h> namespace Blast { // BlastSliceAssetStorageComponent void BlastSliceAssetStorageComponent::Reflect(AZ::ReflectContext* context) { using namespace AZ::Edit; if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context)) { serialize->Class<BlastSliceAssetStorageComponent, AzToolsFramework::Components::EditorComponentBase>() ->Version(2) ->Field("Mesh Data", &BlastSliceAssetStorageComponent::m_meshAssetIdList) ->Field("Mesh Path List", &BlastSliceAssetStorageComponent::m_meshAssetPathList); if (AZ::EditContext* ec = serialize->GetEditContext()) { ec->Class<BlastSliceAssetStorageComponent>( "Blast Slice Storage Component", "Used process blast slice data") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "Physics") ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/Box.png") ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/Box.png") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AddableByUser, false) ->DataElement( AZ::Edit::UIHandlers::Default, &BlastSliceAssetStorageComponent::m_meshAssetIdList, "Mesh Data", "Slice data to fill out the mesh list") ->DataElement( AZ::Edit::UIHandlers::Default, &BlastSliceAssetStorageComponent::m_meshAssetPathList, "Mesh Paths", "The mesh path list"); } } if (AZ::BehaviorContext* behavior = azrtti_cast<AZ::BehaviorContext*>(context)) { behavior->Class<BlastSliceAssetStorageComponent>("BlastSliceAssetStorageComponent") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "blast") ->Method("GenerateAssetInfo", &BlastSliceAssetStorageComponent::GenerateAssetInfo) ->Method("WriteMaterialFile", &BlastSliceAssetStorageComponent::WriteMaterialFile); } } bool BlastSliceAssetStorageComponent::GenerateAssetInfo( const AZStd::vector<AZStd::string>& chunkNames, AZStd::string_view blastFilename, AZStd::string_view assetinfoFilename) { AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult( serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); if (serializeContext == nullptr) { return false; } using namespace AZ::SceneAPI::Containers; using namespace AZ::SceneAPI::SceneData; AZStd::string filename; AZ::StringFunc::Path::Split(blastFilename.data(), nullptr, nullptr, &filename, nullptr); AZStd::any sceneManifestPointer(serializeContext->CreateAny(azrtti_typeid<SceneManifest>())); SceneManifest* sceneManifest = AZStd::any_cast<SceneManifest>(&sceneManifestPointer); AZStd::vector<AZStd::any> meshGroupData; meshGroupData.reserve(chunkNames.size()); AZStd::vector<AZStd::any> materialRuleData; materialRuleData.reserve(chunkNames.size()); for (const AZStd::string& chunkName : chunkNames) { meshGroupData.emplace_back(serializeContext->CreateAny(azrtti_typeid<MeshGroup>())); AZStd::any& meshGroupPointer = meshGroupData.back(); MeshGroup* meshGroup = AZStd::any_cast<MeshGroup>(&meshGroupPointer); // make selection list meshGroup->GetSceneNodeSelectionList().RemoveSelectedNode("RootNode"); for (const AZStd::string& node : chunkNames) { meshGroup->GetSceneNodeSelectionList().RemoveSelectedNode( AZStd::string::format("RootNode.%s", node.c_str())); } meshGroup->GetSceneNodeSelectionList().AddSelectedNode( AZStd::string::format("RootNode.%s", chunkName.c_str())); // create a default material for the mesh group materialRuleData.emplace_back(serializeContext->CreateAny(azrtti_typeid<MaterialRule>())); AZStd::any& materialRulePointer = materialRuleData.back(); MaterialRule* materialRule = AZStd::any_cast<MaterialRule>(&materialRulePointer); // override the deleter since the AZStd::any will clean up later on AZStd::shared_ptr<MaterialRule> materialRuleEntry = AZStd::shared_ptr<MaterialRule>( materialRule, [](auto) { }); meshGroup->GetRuleContainer().AddRule(materialRuleEntry); // construct the asset name for the chunk's mesh group AZStd::string meshGroupName(filename); meshGroupName.append("-"); meshGroupName.append(chunkName); meshGroup->OverrideId(AZ::Uuid::CreateName(meshGroupName.c_str())); meshGroup->SetName(AZStd::move(meshGroupName)); // override the deleter since the AZStd::any will clean up later on AZStd::shared_ptr<MeshGroup> meshGroupEntry = AZStd::shared_ptr<MeshGroup>( meshGroup, [](auto) { }); sceneManifest->AddEntry(AZStd::move(meshGroupEntry)); } return sceneManifest->SaveToFile(assetinfoFilename.data()); } bool BlastSliceAssetStorageComponent::WriteMaterialFile( AZStd::string_view materialGroupName, const AZStd::vector<AZStd::string>& materialNames, AZStd::string_view materialFilename) { AZStd::vector<AZStd::shared_ptr<IMaterial>> materialList; materialList.reserve(materialNames.size()); AZ::GFxFramework::MaterialGroup group; for (const auto& texture : materialNames) { auto mat = AZStd::make_shared<AZ::GFxFramework::Material>(); mat->SetName(texture); mat->SetTexture(AZ::GFxFramework::TextureMapType::Diffuse, "EngineAssets/Textures/white.dds"); group.AddMaterial(mat); } group.SetMtlName(materialGroupName); return group.WriteMtlFile(materialFilename.data()); } // // EditorBlastSliceAssetHandler // EditorBlastSliceAssetHandler::~EditorBlastSliceAssetHandler() { Unregister(); } AZ::Data::AssetPtr EditorBlastSliceAssetHandler::CreateAsset( const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) { if (type != GetAssetType()) { AZ_Error("Blast", type == GetAssetType(), "Invalid asset type! We only handle 'BlastAsset'"); return {}; } if (!CanHandleAsset(id)) { return nullptr; } return aznew BlastSliceAsset; } bool EditorBlastSliceAssetHandler::LoadAssetData( const AZ::Data::Asset<AZ::Data::AssetData>& asset, AZ::IO::GenericStream* stream, const AZ::Data::AssetFilterCB& assetLoadFilterCB) { BlastSliceAsset* blastSliceAssetData = asset.GetAs<BlastSliceAsset>(); AZ_Error( "blast", blastSliceAssetData, "This should be a BlastSliceAsset type, as this is the only type we process!"); AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult( serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); if (blastSliceAssetData && serializeContext) { AZ::ObjectStream::FilterDescriptor filter(assetLoadFilterCB); AZStd::unique_ptr<AZ::Entity> baseEntity( AZ::Utils::LoadObjectFromStream<AZ::Entity>(*stream, serializeContext, filter)); AZ_Error("Blast", baseEntity, "Could not load slice root entity {asset id}"); if (!baseEntity) { return false; } auto&& sliceComponent = baseEntity->FindComponent<AZ::SliceComponent>(); AZ_Error("Blast", sliceComponent, "blast_slice entity missing SliceComponent!"); if (sliceComponent == nullptr) { return false; } AZStd::vector<AZ::Entity*> enityList; sliceComponent->GetEntities(enityList); for (auto&& entity : enityList) { // the base element type to store Blast mesh data is the BlastSliceAssetStorageComponent auto&& blastSliceAssetStorage = entity->FindComponent<BlastSliceAssetStorageComponent>(); if (blastSliceAssetStorage) { if (blastSliceAssetStorage->GetMeshData().empty() == false) { blastSliceAssetData->SetMeshIdList(blastSliceAssetStorage->GetMeshData()); return true; } else if (blastSliceAssetStorage->GetMeshPathList().empty() == false) { AZStd::vector<AZ::Data::AssetId> meshAssetIdList; meshAssetIdList.reserve(blastSliceAssetStorage->GetMeshPathList().size()); for (auto&& assetPath : blastSliceAssetStorage->GetMeshPathList()) { AZ::Data::AssetId meshAssetId; AZ::Data::AssetCatalogRequestBus::BroadcastResult( meshAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, assetPath.c_str(), AZ::Data::s_invalidAssetType, false); if (meshAssetId.IsValid()) { meshAssetIdList.emplace_back(meshAssetId); } } blastSliceAssetData->SetMeshIdList(meshAssetIdList); return true; } } // back up logic to load blast data for the EditorBlastMeshDataComponent auto&& meshDataComponent = entity->FindComponent<EditorBlastMeshDataComponent>(); if (meshDataComponent) { auto&& innerBlastSliceAsset = meshDataComponent->GetBlastSliceAsset(); if (innerBlastSliceAsset.IsReady()) { blastSliceAssetData->SetMeshIdList(innerBlastSliceAsset.Get()->GetMeshIdList()); blastSliceAssetData->SetMaterialId(innerBlastSliceAsset.Get()->GetMaterialId()); return true; } else { auto&& meshDataList = meshDataComponent->GetMeshAssets(); AZStd::vector<AZ::Data::AssetId> meshAssetIdList; meshAssetIdList.reserve(meshDataList.size()); for (auto&& meshData : meshDataList) { LmbrCentral::MeshAsset* meshAsset = meshData.Get(); if (meshAsset) { meshAssetIdList.push_back(meshAsset->GetId()); } } blastSliceAssetData->SetMeshIdList(meshAssetIdList); return true; } } } AZ_Error( "Blast", false, "blast_slice assetId:%s missing EditorBlastMeshDataComponent!", asset->GetId().ToString<AZStd::string>().c_str()); } return false; } bool EditorBlastSliceAssetHandler::LoadAssetData( const AZ::Data::Asset<AZ::Data::AssetData>& asset, const char* assetPath, const AZ::Data::AssetFilterCB& assetLoadFilterCB) { AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); if (fileIO) { AZ::IO::FileIOStream stream(assetPath, AZ::IO::OpenMode::ModeRead); if (stream.IsOpen()) { return LoadAssetData(asset, &stream, assetLoadFilterCB); } } return false; } void EditorBlastSliceAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr) { delete ptr; } void EditorBlastSliceAssetHandler::GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) { assetTypes.push_back(azrtti_typeid<BlastSliceAsset>()); } void EditorBlastSliceAssetHandler::Register() { AZ_Assert(AZ::Data::AssetManager::IsReady(), "Asset manager isn't ready!"); AZ::Data::AssetManager::Instance().RegisterHandler(this, azrtti_typeid<BlastSliceAsset>()); AZ::AssetTypeInfoBus::Handler::BusConnect(azrtti_typeid<BlastSliceAsset>()); } void EditorBlastSliceAssetHandler::Unregister() { AZ::AssetTypeInfoBus::Handler::BusDisconnect(azrtti_typeid<BlastSliceAsset>()); if (AZ::Data::AssetManager::IsReady()) { AZ::Data::AssetManager::Instance().UnregisterHandler(this); } } AZ::Data::AssetType EditorBlastSliceAssetHandler::GetAssetType() const { return azrtti_typeid<BlastSliceAsset>(); } const char* EditorBlastSliceAssetHandler::GetAssetTypeDisplayName() const { return "Blast Slice Asset"; } const char* EditorBlastSliceAssetHandler::GetGroup() const { return "Blast"; } const char* EditorBlastSliceAssetHandler::GetBrowserIcon() const { return "Editor/Icons/Components/Box.png"; } void EditorBlastSliceAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) { extensions.push_back("blast_slice"); } } // namespace Blast