diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.cpp new file mode 100644 index 0000000000..6077a66682 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.cpp @@ -0,0 +1,100 @@ +/* + * 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 "ByteStreamSerializer.h" + +#include +#include +#include +#include + +namespace AZ +{ + namespace ByteSerializerInternal + { + static JsonSerializationResult::Result Load(void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context) + { + using JsonSerializationResult::Outcomes; + using JsonSerializationResult::Tasks; + + AZ_Assert(outputValue, "Expected a valid pointer to load from json value."); + + switch (inputValue.GetType()) + { + case rapidjson::kStringType: { + JsonByteStream buffer; + if (AZ::StringFunc::Base64::Decode(buffer, inputValue.GetString(), inputValue.GetStringLength())) + { + JsonByteStream* valAsByteStream = static_cast(outputValue); + *valAsByteStream = AZStd::move(buffer); + return context.Report(Tasks::ReadField, Outcomes::Success, "Successfully read ByteStream."); + } + return context.Report(Tasks::ReadField, Outcomes::Invalid, "Decode of Base64 encoded ByteStream failed."); + } + case rapidjson::kArrayType: + case rapidjson::kObjectType: + case rapidjson::kNullType: + case rapidjson::kFalseType: + case rapidjson::kTrueType: + case rapidjson::kNumberType: + return context.Report( + Tasks::ReadField, Outcomes::Unsupported, + "Unsupported type. ByteStream values cannot be read from arrays, objects, nulls, booleans or numbers."); + default: + return context.Report(Tasks::ReadField, Outcomes::Unknown, "Unknown json type encountered for ByteStream value."); + } + } + + static JsonSerializationResult::Result StoreWithDefault( + rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, JsonSerializerContext& context) + { + using JsonSerializationResult::Outcomes; + using JsonSerializationResult::Tasks; + + const JsonByteStream& valAsByteStream = *static_cast(inputValue); + if (context.ShouldKeepDefaults() || !defaultValue || (valAsByteStream != *static_cast(defaultValue))) + { + const auto base64ByteStream = AZ::StringFunc::Base64::Encode(valAsByteStream.data(), valAsByteStream.size()); + outputValue.SetString(base64ByteStream.c_str(), base64ByteStream.size(), context.GetJsonAllocator()); + return context.Report(Tasks::WriteValue, Outcomes::Success, "ByteStream successfully stored."); + } + + return context.Report(Tasks::WriteValue, Outcomes::DefaultsUsed, "Default ByteStream used."); + } + } // namespace ByteSerializerInternal + + AZ_CLASS_ALLOCATOR_IMPL(JsonByteStreamSerializer, SystemAllocator, 0); + + JsonSerializationResult::Result JsonByteStreamSerializer::Load( + void* outputValue, [[maybe_unused]] const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) + { + AZ_Assert( + azrtti_typeid() == outputValueTypeId, + "Unable to deserialize AZStd::vector> to json because the provided type is %s", + outputValueTypeId.ToString().c_str()); + + return ByteSerializerInternal::Load(outputValue, inputValue, context); + } + + JsonSerializationResult::Result JsonByteStreamSerializer::Store( + rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, [[maybe_unused]] const Uuid& valueTypeId, + JsonSerializerContext& context) + { + AZ_Assert( + azrtti_typeid() == valueTypeId, + "Unable to serialize AZStd::vector to json because the provided type is %s", + valueTypeId.ToString().c_str()); + + return ByteSerializerInternal::StoreWithDefault(outputValue, inputValue, defaultValue, context); + } +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.h new file mode 100644 index 0000000000..4f01e28319 --- /dev/null +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/ByteStreamSerializer.h @@ -0,0 +1,38 @@ +/* + * 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. + * + */ + +#pragma once + +#include +#include + +namespace AZ +{ + using JsonByteStream = AZStd::vector; //!< Alias for AZStd::vector. + + //! Serialize a stream of bytes (usually binary data) as a json string value. + //! @note Related to GenericClassByteStream (part of SerializeGenericTypeInfo> - see AZStdContainers.inl for more + //! details). + class JsonByteStreamSerializer : public BaseJsonSerializer + { + public: + AZ_RTTI(JsonByteStreamSerializer, "{30F0EA5A-CD13-4BA7-BAE1-D50D851CAC45}", BaseJsonSerializer); + AZ_CLASS_ALLOCATOR_DECL; + + JsonSerializationResult::Result Load( + void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue, + JsonDeserializerContext& context) override; + JsonSerializationResult::Result Store( + rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, + JsonSerializerContext& context) override; + }; +} // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp index e33466d8dc..6e3c8cec60 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,8 @@ namespace AZ jsonContext->Serializer()->HandlesType(); jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer()->HandlesType(); + jsonContext->Serializer() ->HandlesType() ->HandlesType() diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake index 97b86f2432..e100b240c2 100644 --- a/Code/Framework/AzCore/AzCore/azcore_files.cmake +++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake @@ -505,6 +505,8 @@ set(FILES Serialization/Json/BasicContainerSerializer.cpp Serialization/Json/BoolSerializer.h Serialization/Json/BoolSerializer.cpp + Serialization/Json/ByteStreamSerializer.h + Serialization/Json/ByteStreamSerializer.cpp Serialization/Json/CastingHelpers.h Serialization/Json/DoubleSerializer.h Serialization/Json/DoubleSerializer.cpp diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/ByteStreamSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/ByteStreamSerializerTests.cpp new file mode 100644 index 0000000000..110466d1a0 --- /dev/null +++ b/Code/Framework/AzCore/Tests/Serialization/Json/ByteStreamSerializerTests.cpp @@ -0,0 +1,59 @@ +/* + * 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 +#include +#include + +namespace JsonSerializationTests +{ + class ByteStreamSerializerTestDescription : public JsonSerializerConformityTestDescriptor + { + public: + AZStd::shared_ptr CreateSerializer() override + { + return AZStd::make_shared(); + } + + AZStd::shared_ptr CreateDefaultInstance() override + { + return AZStd::make_shared(); + } + + AZStd::shared_ptr CreateFullySetInstance() override + { + // create a JsonByteStream (AZStd::vector) with ten 'a's + return AZStd::make_shared(10, 'a'); + } + + AZStd::string_view GetJsonForFullySetInstance() override + { + // Base64 encoded version of 'aaaaaaaaaa' (see CreateFullySetInstance) + return R"("YWFhYWFhYWFhYQ==")"; + } + + void ConfigureFeatures(JsonSerializerConformityTestDescriptorFeatures& features) override + { + features.EnableJsonType(rapidjson::kStringType); + features.m_supportsPartialInitialization = false; + features.m_supportsInjection = false; + } + + bool AreEqual(const AZ::JsonByteStream& lhs, const AZ::JsonByteStream& rhs) override + { + return lhs == rhs; + } + }; + + using ByteStreamConformityTestTypes = ::testing::Types; + INSTANTIATE_TYPED_TEST_CASE_P(JsonByteStreamSerialzier, JsonSerializerConformityTests, ByteStreamConformityTestTypes); +} // namespace JsonSerializationTests diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index c51cf46a37..2129761bfe 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -98,6 +98,7 @@ set(FILES Serialization/Json/BaseJsonSerializerTests.cpp Serialization/Json/BasicContainerSerializerTests.cpp Serialization/Json/BoolSerializerTests.cpp + Serialization/Json/ByteStreamSerializerTests.cpp Serialization/Json/ColorSerializerTests.cpp Serialization/Json/DoubleSerializerTests.cpp Serialization/Json/IntSerializerTests.cpp diff --git a/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h b/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h index f47c5c6b0e..8e74c09f52 100644 --- a/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h +++ b/Gems/WhiteBox/Code/Include/WhiteBox/WhiteBoxToolApi.h @@ -102,6 +102,9 @@ namespace WhiteBox //! Alias for a collection of faces. using Faces = AZStd::vector; + //! Underlying representation of the White Box mesh (serialized halfedge data). + using WhiteBoxMeshStream = AZStd::vector; + //! Represents the vertex handles to be used to form a new face. struct FaceVertHandles { @@ -726,19 +729,33 @@ namespace WhiteBox /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Serialization + //! The result of attempting to deserialize a white box mesh from a white box mesh stream. + enum class ReadResult + { + Full, //!< The white box mesh stream was full and was read into white box mesh (it is now initialized). + Empty, //!< The white box mesh stream was empty so no white box mesh was loaded. + Error //!< An error occurred while trying to deserialize white box mesh stream. + }; + //! Take an input stream of bytes and create a white box mesh from the deserialized data. - //! @return Will return false if any error was encountered during deserialization, true otherwise. + //! @return Will return ReadResult::Full if the white box mesh stream was filled with data and + //! the white box mesh was initialized, ReadResult::Empty if white box mesh stream did not contain + //! any data (white box mesh will be left empty) or ReadResult::Error if any error was encountered + //! during deserialization. //! @note A white box mesh must have been created first. - bool ReadMesh(WhiteBoxMesh& whiteBox, const AZStd::vector& input); + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, const WhiteBoxMeshStream& input); //! Take an input stream and create a white box mesh from the deserialized data. - //! @return Will return false if any error was encountered during deserialization, true otherwise. + //! @return Will return ReadResult::Full if the white box mesh stream was filled with data and + //! the white box mesh was initialized, ReadResult::Empty if white box mesh stream did not contain + //! any data (white box mesh will be left empty) or ReadResult::Error if any error was encountered + //! during deserialization. //! @note The input stream must not skip white space characters (std::noskipws must be set on the stream). - bool ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input); + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input); //! Take a white box mesh and write it out to a stream of bytes. //! @return Will return false if any error was encountered during serialization, true otherwise. - bool WriteMesh(const WhiteBoxMesh& whiteBox, AZStd::vector& output); + bool WriteMesh(const WhiteBoxMesh& whiteBox, WhiteBoxMeshStream& output); //! Clones the white box mesh object into a new mesh. //! @return Will return null if any error was encountered during serialization, otherwise the cloned mesh. diff --git a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp index 69d7c03fce..cd308aef0f 100644 --- a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp +++ b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.cpp @@ -86,7 +86,7 @@ namespace WhiteBox { success = assetHandler->SaveAssetData(meshAsset, &fileStream); AZ_Printf( - "EditorWhiteBoxComponent", "Save %s. Location: %s", success ? "succeeded" : "failed", + "EditorWhiteBoxMeshAsset", "Save %s. Location: %s", success ? "succeeded" : "failed", absoluteFilePath.c_str()); } } @@ -229,7 +229,15 @@ namespace WhiteBox { if (asset == m_meshAsset) { - AZ_Warning("EditorWhiteBoxComponent", false, "OnAssetError: %s", asset.GetHint().c_str()); + AZ_Warning("EditorWhiteBoxMeshAsset", false, "OnAssetError: %s", asset.GetHint().c_str()); + } + } + + void EditorWhiteBoxMeshAsset::OnAssetReloadError(AZ::Data::Asset asset) + { + if (asset == m_meshAsset) + { + AZ_Warning("EditorWhiteBoxMeshAsset", false, "OnAssetReloadError: %s", asset.GetHint().c_str()); } } diff --git a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h index 2001808668..caa56dce1a 100644 --- a/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h +++ b/Gems/WhiteBox/Code/Source/Asset/EditorWhiteBoxMeshAsset.h @@ -82,6 +82,7 @@ namespace WhiteBox void OnAssetReady(AZ::Data::Asset asset) override; void OnAssetReloaded(AZ::Data::Asset asset) override; void OnAssetError(AZ::Data::Asset asset) override; + void OnAssetReloadError(AZ::Data::Asset asset) override; // WhiteBoxMeshAssetNotificationBus ... void OnWhiteBoxMeshAssetModified(AZ::Data::Asset asset) override; diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h index f5915d033b..d25af2fed2 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAsset.h @@ -51,12 +51,12 @@ namespace WhiteBox return AZStd::move(m_mesh); } - void SetWhiteBoxData(AZStd::vector whiteBoxData) + void SetWhiteBoxData(Api::WhiteBoxMeshStream whiteBoxData) { m_whiteBoxData = AZStd::move(whiteBoxData); } - const AZStd::vector& GetWhiteBoxData() const + const Api::WhiteBoxMeshStream& GetWhiteBoxData() const { return m_whiteBoxData; } @@ -73,7 +73,7 @@ namespace WhiteBox } Api::WhiteBoxMeshPtr m_mesh; - AZStd::vector m_whiteBoxData; //! Data used for creating undo commands. + Api::WhiteBoxMeshStream m_whiteBoxData; //! Data used for creating undo commands. }; } // namespace Pipeline } // namespace WhiteBox diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp index a64c552bb6..3ed22a3d1b 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetHandler.cpp @@ -112,16 +112,21 @@ namespace WhiteBox const auto size = stream->GetLength(); - AZStd::vector whiteBoxData(size); + Api::WhiteBoxMeshStream whiteBoxData; + whiteBoxData.resize(size); + stream->Read(size, whiteBoxData.data()); auto whiteBoxMesh = WhiteBox::Api::CreateWhiteBoxMesh(); - const bool success = WhiteBox::Api::ReadMesh(*whiteBoxMesh, whiteBoxData); + const auto result = WhiteBox::Api::ReadMesh(*whiteBoxMesh, whiteBoxData); + // if result is not 'Full', then whiteBoxMeshAsset could be empty which is most likely an error + // as no data was loaded from the asset, or it was not correctly read in stream->Read(..) + const auto success = result == Api::ReadResult::Full; if (success) { whiteBoxMeshAsset->SetMesh(AZStd::move(whiteBoxMesh)); - whiteBoxMeshAsset->SetWhiteBoxData(whiteBoxData); + whiteBoxMeshAsset->SetWhiteBoxData(AZStd::move(whiteBoxData)); } return success ? AZ::Data::AssetHandler::LoadResult::LoadComplete diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp index ef11b8d47d..19cbc802dd 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.cpp @@ -29,12 +29,12 @@ namespace WhiteBox m_asset = asset; } - void WhiteBoxMeshAssetUndoCommand::SetUndoState(const AZStd::vector& undoState) + void WhiteBoxMeshAssetUndoCommand::SetUndoState(const Api::WhiteBoxMeshStream& undoState) { m_undoState = undoState; } - void WhiteBoxMeshAssetUndoCommand::SetRedoState(const AZStd::vector& redoState) + void WhiteBoxMeshAssetUndoCommand::SetRedoState(const Api::WhiteBoxMeshStream& redoState) { m_redoState = redoState; } diff --git a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h index eb657c5a85..3f6258c1b4 100644 --- a/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h +++ b/Gems/WhiteBox/Code/Source/Asset/WhiteBoxMeshAssetUndoCommand.h @@ -34,8 +34,8 @@ namespace WhiteBox ~WhiteBoxMeshAssetUndoCommand() override = default; void SetAsset(AZ::Data::Asset asset); - void SetUndoState(const AZStd::vector& undoState); - void SetRedoState(const AZStd::vector& redoState); + void SetUndoState(const Api::WhiteBoxMeshStream& undoState); + void SetRedoState(const Api::WhiteBoxMeshStream& redoState); // AzToolsFramework::UndoSystem::URSequencePoint ... void Undo() override; @@ -44,7 +44,7 @@ namespace WhiteBox protected: AZ::Data::Asset m_asset; - AZStd::vector m_undoState; - AZStd::vector m_redoState; + Api::WhiteBoxMeshStream m_undoState; + Api::WhiteBoxMeshStream m_redoState; }; } // namespace WhiteBox diff --git a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp index 49e713810e..5803f066ae 100644 --- a/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp +++ b/Gems/WhiteBox/Code/Source/Core/WhiteBoxToolApi.cpp @@ -3379,7 +3379,7 @@ namespace WhiteBox CalculatePlanarUVs(whiteBox); } - bool WriteMesh(const WhiteBoxMesh& whiteBox, AZStd::vector& output) + bool WriteMesh(const WhiteBoxMesh& whiteBox, WhiteBoxMeshStream& output) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -3403,10 +3403,15 @@ namespace WhiteBox return false; } - bool ReadMesh(WhiteBoxMesh& whiteBox, const AZStd::vector& input) + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, const WhiteBoxMeshStream& input) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + if (input.empty()) + { + return ReadResult::Empty; + } + std::string inputStr; inputStr.reserve(input.size()); AZStd::copy(input.cbegin(), input.cend(), AZStd::back_inserter(inputStr)); @@ -3418,33 +3423,33 @@ namespace WhiteBox return ReadMesh(whiteBox, whiteBoxStream); } - bool ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input) + ReadResult ReadMesh(WhiteBoxMesh& whiteBox, std::istream& input) { const auto skipws = input.flags() & std::ios_base::skipws; AZ_Assert(skipws == 0, "Input stream must not skip white space characters"); if (skipws != 0) { - return false; + return ReadResult::Error; } AZStd::lock_guard lg(g_omSerializationLock); OpenMesh::IO::Options options{OpenMesh::IO::Options::FaceTexCoord | OpenMesh::IO::Options::FaceNormal}; - return OpenMesh::IO::read_mesh(whiteBox.mesh, input, ".om", options); + return OpenMesh::IO::read_mesh(whiteBox.mesh, input, ".om", options) ? ReadResult::Full : ReadResult::Error; } WhiteBoxMeshPtr CloneMesh(const WhiteBoxMesh& whiteBox) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - AZStd::vector clonedData; + WhiteBoxMeshStream clonedData; if (!WriteMesh(whiteBox, clonedData)) { return nullptr; } WhiteBoxMeshPtr newMesh = CreateWhiteBoxMesh(); - if (!ReadMesh(*newMesh, clonedData)) + if (ReadMesh(*newMesh, clonedData) != ReadResult::Full) { return nullptr; } @@ -3461,7 +3466,7 @@ namespace WhiteBox bool SaveToWbm(const WhiteBoxMesh& whiteBox, AZ::IO::GenericStream& stream) { - AZStd::vector buffer; + WhiteBoxMeshStream buffer; const bool success = WhiteBox::Api::WriteMesh(whiteBox, buffer); const auto bytesWritten = stream.Write(buffer.size(), buffer.data()); diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp index 61012b8577..e88b4ce686 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.cpp @@ -348,14 +348,14 @@ namespace WhiteBox else { // attempt to load the mesh - if (Api::ReadMesh(*m_whiteBox, m_whiteBoxData)) + const auto result = Api::ReadMesh(*m_whiteBox, m_whiteBoxData); + AZ_Error("EditorWhiteBoxComponent", result != WhiteBox::Api::ReadResult::Error, "Error deserializing white box mesh stream"); + + // if the read was successful but the byte stream is empty + // (there was nothing to load), create a default mesh + if (result == Api::ReadResult::Empty) { - // if the read was successful but the byte stream is empty - // (there was nothing to load), create a default mesh - if (m_whiteBoxData.empty()) - { - Api::InitializeAsUnitCube(*m_whiteBox); - } + Api::InitializeAsUnitCube(*m_whiteBox); } } } diff --git a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h index 5d7ac3b303..02b50a9407 100644 --- a/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h +++ b/Gems/WhiteBox/Code/Source/EditorWhiteBoxComponent.h @@ -124,7 +124,7 @@ namespace WhiteBox AZStd::optional> m_renderMesh; //!< The render mesh to use for the White Box mesh data. AZ::Transform m_worldFromLocal = AZ::Transform::CreateIdentity(); //!< Cached world transform of Entity. - AZStd::vector m_whiteBoxData; //!< Serialized White Box mesh data. + Api::WhiteBoxMeshStream m_whiteBoxData; //!< Serialized White Box mesh data. //! Holds a reference to an optional WhiteBoxMeshAsset and manages the lifecycle of adding/removing an asset. EditorWhiteBoxMeshAsset* m_editorMeshAsset = nullptr; AZStd::optional m_worldAabb; //!< Cached world aabb (used for selection/view determination). diff --git a/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp b/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp index 1d55539720..1135a915ec 100644 --- a/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp +++ b/Gems/WhiteBox/Code/Tests/WhiteBoxTest.cpp @@ -470,6 +470,7 @@ namespace UnitTest TEST_F(WhiteBoxTestFixture, MeshNotDeserializedWithSkipWhiteSpaceStream) { namespace Api = WhiteBox::Api; + using testing::Eq; Api::InitializeAsUnitCube(*m_whiteBox); AZStd::vector serializedWhiteBox; @@ -485,7 +486,7 @@ namespace UnitTest // note: std::stringstream will default to skip white space characters AZ_TEST_START_TRACE_SUPPRESSION; - EXPECT_FALSE(Api::ReadMesh(*m_whiteBox, whiteBoxStream)); + EXPECT_THAT(Api::ReadMesh(*m_whiteBox, whiteBoxStream), Eq(Api::ReadResult::Error)); AZ_TEST_STOP_TRACE_SUPPRESSION(1); }