You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
9.2 KiB
C++
236 lines
9.2 KiB
C++
/*
|
|
* 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 <Pipeline/HeightFieldAssetHandler.h>
|
|
#include <Pipeline/StreamWrapper.h>
|
|
#include <AzCore/IO/GenericStreams.h>
|
|
#include <AzCore/IO/FileIO.h>
|
|
#include <PhysX/HeightFieldAsset.h>
|
|
#include <PhysX/SystemComponentBus.h>
|
|
#include <PhysX/ComponentTypeIds.h>
|
|
#include <Source/Pipeline/HeightFieldAssetHandler.h>
|
|
#include <PxPhysicsAPI.h>
|
|
|
|
#include <extensions/PxSerialization.h>
|
|
#include <extensions/PxDefaultStreams.h>
|
|
|
|
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<HeightFieldAsset>::Uuid());
|
|
}
|
|
|
|
AZ::AssetTypeInfoBus::Handler::BusConnect(AZ::AzTypeInfo<HeightFieldAsset>::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<HeightFieldAsset>::Uuid();
|
|
}
|
|
|
|
void HeightFieldAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& 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<HeightFieldAsset>::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<AZ::Data::AssetData>& asset,
|
|
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
|
|
[[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB)
|
|
{
|
|
AZ_PROFILE_FUNCTION(Physics);
|
|
|
|
HeightFieldAsset* physXHeightFieldAsset = asset.GetAs<HeightFieldAsset>();
|
|
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<AZ::Data::AssetData>& asset, AZ::IO::GenericStream* stream)
|
|
{
|
|
AZ_PROFILE_FUNCTION(Physics);
|
|
|
|
HeightFieldAsset* physXHeightFieldAsset = asset.GetAs<HeightFieldAsset>();
|
|
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<physx::PxHeightFieldSample> 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<AZ::Data::AssetType>& assetTypes)
|
|
{
|
|
assetTypes.push_back(AZ::AzTypeInfo<HeightFieldAsset>::Uuid());
|
|
}
|
|
} //namespace Pipeline
|
|
} // namespace PhysX
|