/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include #include #include #include namespace PhysX { namespace Pipeline { const char* HeightFieldAssetHandler::s_assetFileExtension = "pxheightfield"; HeightFieldAssetHandler::HeightFieldAssetHandler() { Register(); } HeightFieldAssetHandler::~HeightFieldAssetHandler() { Unregister(); } void HeightFieldAssetHandler::Register() { bool assetManagerReady = AZ::Data::AssetManager::IsReady(); AZ_Error("PhysX HeightField Asset", assetManagerReady, "Asset manager isn't ready."); if (assetManagerReady) { AZ::Data::AssetManager::Instance().RegisterHandler(this, AZ::AzTypeInfo::Uuid()); } AZ::AssetTypeInfoBus::Handler::BusConnect(AZ::AzTypeInfo::Uuid()); } void HeightFieldAssetHandler::Unregister() { AZ::AssetTypeInfoBus::Handler::BusDisconnect(); if (AZ::Data::AssetManager::IsReady()) { AZ::Data::AssetManager::Instance().UnregisterHandler(this); } } // AZ::AssetTypeInfoBus AZ::Data::AssetType HeightFieldAssetHandler::GetAssetType() const { return AZ::AzTypeInfo::Uuid(); } void HeightFieldAssetHandler::GetAssetTypeExtensions(AZStd::vector& extensions) { extensions.push_back(HeightFieldAssetHandler::s_assetFileExtension); } const char* HeightFieldAssetHandler::GetAssetTypeDisplayName() const { return "PhysX HeightField Mesh"; } const char* HeightFieldAssetHandler::GetBrowserIcon() const { return "Icons/Components/ColliderMesh.svg"; } const char* HeightFieldAssetHandler::GetGroup() const { return "Physics"; } AZ::Uuid HeightFieldAssetHandler::GetComponentTypeId() const { return PhysX::EditorTerrainComponentTypeId; } // AZ::Data::AssetHandler AZ::Data::AssetPtr HeightFieldAssetHandler::CreateAsset([[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) { if (type == AZ::AzTypeInfo::Uuid()) { return aznew HeightFieldAsset(); } AZ_Error("PhysX HeightField Asset", false, "This handler deals only with PhysXHeightFieldAsset type."); return nullptr; } AZ::Data::AssetHandler::LoadResult HeightFieldAssetHandler::LoadAssetData( const AZ::Data::Asset& asset, AZStd::shared_ptr stream, [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) { AZ_PROFILE_FUNCTION(Physics); HeightFieldAsset* physXHeightFieldAsset = asset.GetAs(); if (!physXHeightFieldAsset) { AZ_Error("PhysX HeightField Asset", false, "This should be a PhysX HeightField Asset, as this is the only type we process."); return AZ::Data::AssetHandler::LoadResult::Error; } // Wrap az stream behind physx interface PhysX::AssetDataStreamWrapper readerStream(stream); // Read the file header HeightFieldAssetHeader header; readerStream.read(&header, sizeof(header)); // Parse the asset versions if (header.m_assetVersion >= 1) { if (header.m_assetDataSize > 0) { // Version 1 doesn't have min/max heights, so only read this data for versions 2+. if (header.m_assetVersion >= 2) { readerStream.read(&physXHeightFieldAsset->m_minHeight, sizeof(float)); readerStream.read(&physXHeightFieldAsset->m_maxHeight, sizeof(float)); } else { // In versions 0 & 1, the data is cooked assuming the data starts at origin (min height = 0) // and has a max height of 1024.0f. const float v1HardCodedMaxHeight = 1024.0f; physXHeightFieldAsset->m_minHeight = 0.0f; physXHeightFieldAsset->m_maxHeight = v1HardCodedMaxHeight; } // Create heightfield from cooked file physx::PxPhysics& physx = PxGetPhysics(); physXHeightFieldAsset->SetHeightField(physx.createHeightField(readerStream)); AZ_Error("PhysX HeightField Asset", physXHeightFieldAsset->m_heightField != nullptr, "Failed to construct PhysX mesh from the cooked data. Possible data corruption."); return (physXHeightFieldAsset->m_heightField != nullptr) ? AZ::Data::AssetHandler::LoadResult::LoadComplete : AZ::Data::AssetHandler::LoadResult::Error; } else { AZ_Warning("HeightFieldAssetHandler", false, "Empty heightfield file. Try resaving your level"); } } else { AZ_Warning("HeightFieldAssetHandler", false, "Unsupported asset version"); } return AZ::Data::AssetHandler::LoadResult::Error; } bool HeightFieldAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) { AZ_PROFILE_FUNCTION(Physics); HeightFieldAsset* physXHeightFieldAsset = asset.GetAs(); if (!physXHeightFieldAsset) { AZ_Error("PhysX HeightField Asset", false, "This should be a PhysX HeightField Asset. HeightFieldAssetHandler doesn't handle any other asset type."); return false; } physx::PxHeightField* heightField = physXHeightFieldAsset->GetHeightField(); if (!heightField) { AZ_Warning("PhysX HeightField Asset", false, "There is no heightfield to save."); return false; } HeightFieldAssetHeader header; if (header.m_assetVersion == 2) { physx::PxCooking* cooking = nullptr; SystemRequestsBus::BroadcastResult(cooking, &SystemRequests::GetCooking); // Read samples from heightfield AZStd::vector samples; samples.resize(heightField->getNbColumns() * heightField->getNbRows()); heightField->saveCells(samples.data(), (physx::PxU32)samples.size() * heightField->getSampleStride()); // Read description from heightfield physx::PxHeightFieldDesc heightFieldDesc; heightFieldDesc.format = heightField->getFormat(); heightFieldDesc.nbColumns = heightField->getNbColumns(); heightFieldDesc.nbRows = heightField->getNbRows(); heightFieldDesc.samples.data = samples.data(); heightFieldDesc.samples.stride = heightField->getSampleStride(); // Cook description to file physx::PxDefaultMemoryOutputStream writer; bool success = cooking->cookHeightField(heightFieldDesc, writer); header.m_assetDataSize = writer.getSize() + 2 * sizeof(float); PhysX::StreamWrapper writerStream(stream); writerStream.write(&header, sizeof(header)); writerStream.write(&physXHeightFieldAsset->m_minHeight, sizeof(physXHeightFieldAsset->m_minHeight)); writerStream.write(&physXHeightFieldAsset->m_maxHeight, sizeof(physXHeightFieldAsset->m_maxHeight)); writerStream.write(writer.getData(), writer.getSize()); return success; } else { AZ_Warning("HeightFieldAssetHandler", false, "Unsupported asset version"); } return false; } void HeightFieldAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr) { delete ptr; } void HeightFieldAssetHandler::GetHandledAssetTypes(AZStd::vector& assetTypes) { assetTypes.push_back(AZ::AzTypeInfo::Uuid()); } } //namespace Pipeline } // namespace PhysX