Skip to content

Commit

Permalink
Adds the ability to generate UVs automatically during mesh import (o3…
Browse files Browse the repository at this point in the history
…de#15530)

* Adds the ability to generate UVs automatically during mesh import

This adds a new modifier to the available list of modifiers you can
add to a mesh during its import in its import settings.

The new modifier lets you select how to generate UVs for a mesh.
Currently, only 1 generator is supported (Spherical Positional
projection) but all the hard work here is done to support additional
unwrappers in the future by following the pattern.

This ability is crucial for workflows to do with importing robotic
meshes as they often lack UVs, and the path to otherwise get something
reasonable to render is painful

Signed-off-by: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com>
  • Loading branch information
nick-l-o3de committed Apr 11, 2023
1 parent a23b797 commit c50d66b
Show file tree
Hide file tree
Showing 18 changed files with 805 additions and 38 deletions.
5 changes: 5 additions & 0 deletions Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexUVData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ namespace AZ
m_uvs.reserve(size);
}

void MeshVertexUVData::Clear()
{
m_uvs.clear();
}

void MeshVertexUVData::AppendUV(const AZ::Vector2& uv)
{
m_uvs.push_back(uv);
Expand Down
2 changes: 2 additions & 0 deletions Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexUVData.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ namespace AZ
SCENE_DATA_API void ReserveContainerSpace(size_t size);
SCENE_DATA_API void AppendUV(const AZ::Vector2& uv);

SCENE_DATA_API void Clear();

SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override;
protected:
AZStd::vector<AZ::Vector2> m_uvs;
Expand Down
5 changes: 5 additions & 0 deletions Code/Tools/SceneAPI/SceneData/ManifestMetaInfoHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <SceneAPI/SceneData/Rules/SkinMeshAdvancedRule.h>
#include <SceneAPI/SceneData/Rules/SkinRule.h>
#include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
#include <SceneAPI/SceneData/Rules/UVsRule.h>

namespace AZ
{
Expand Down Expand Up @@ -85,6 +86,10 @@ namespace AZ
{
modifiers.push_back(azrtti_typeid<CoordinateSystemRule>());
}
if (existingRules.find(SceneData::UVsRule::TYPEINFO_Uuid()) == existingRules.end())
{
modifiers.push_back(SceneData::UVsRule::TYPEINFO_Uuid());
}
if (existingRules.find(SceneData::TangentsRule::TYPEINFO_Uuid()) == existingRules.end())
{
modifiers.push_back(SceneData::TangentsRule::TYPEINFO_Uuid());
Expand Down
2 changes: 2 additions & 0 deletions Code/Tools/SceneAPI/SceneData/ReflectionRegistrar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <SceneAPI/SceneData/Rules/ScriptProcessorRule.h>
#include <SceneAPI/SceneData/Rules/SkeletonProxyRule.h>
#include <SceneAPI/SceneData/Rules/TangentsRule.h>
#include <SceneAPI/SceneData/Rules/UVsRule.h>
#include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>

#include <SceneAPI/SceneData/ManifestBase/SceneNodeSelectionList.h>
Expand Down Expand Up @@ -74,6 +75,7 @@ namespace AZ
SceneData::SkeletonProxyRule::Reflect(context);
SceneData::SkinMeshAdvancedRule::Reflect(context);
SceneData::TangentsRule::Reflect(context);
SceneData::UVsRule::Reflect(context);
SceneData::CoordinateSystemRule::Reflect(context);

// Utility
Expand Down
129 changes: 129 additions & 0 deletions Code/Tools/SceneAPI/SceneData/Rules/UVsRule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* 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 <SceneAPI/SceneData/Rules/UVsRule.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <AzCore/Settings/SettingsRegistry.h>

namespace AZ
{
namespace SceneAPI
{
namespace SceneData
{
static constexpr AZStd::string_view DefaultUVsGenerationMethodKeyIfNoRulePresent{
"/O3DE/SceneAPI/UVsGenerateComponent/DefaultGenerationMethodIfNoRulePresent"
};

static constexpr AZStd::string_view DefaultUVsGenerationMethodKeyWhenAddingNewRules{
"/O3DE/SceneAPI/UVsGenerateComponent/DefaultGenerationMethodWhenRuleIsPresent"
};


UVsRule::UVsRule()
: DataTypes::IRule()
{
m_generationMethod = GetDefaultGenerationMethodWhenAddingNewRule();
}

AZ::SceneAPI::DataTypes::UVsGenerationMethod GetGenerationMethodFromRegistry(
AZStd::string_view regKey, AZ::SceneAPI::DataTypes::UVsGenerationMethod defaultValue)
{
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
AZStd::string stringFromRegistry;
if (settingsRegistry->Get(stringFromRegistry, regKey))
{
const bool isCaseSensitive = false;
if (AZ::StringFunc::Equal(stringFromRegistry, "LeaveSceneDataAsIs", isCaseSensitive))
{
return AZ::SceneAPI::DataTypes::UVsGenerationMethod::LeaveSceneDataAsIs;
}
else if (AZ::StringFunc::Equal(stringFromRegistry, "SphericalProjection", isCaseSensitive))
{
return AZ::SceneAPI::DataTypes::UVsGenerationMethod::SphericalProjection;
}
else
{
AZ_Warning(
AZ::SceneAPI::Utilities::WarningWindow,
false,
"'%s' is not a valid default UV generation method. Check the value of " AZ_STRING_FORMAT " in your "
"settings registry, and change "
"it to 'LeaveSceneDataAsIs' or 'SphericalProjection'",
stringFromRegistry.c_str(),
AZ_STRING_ARG(regKey));
}
}
}
return defaultValue;
}

//! return the default method for when a new rule is explicitly created by script or user
AZ::SceneAPI::DataTypes::UVsGenerationMethod UVsRule::GetDefaultGenerationMethodWhenAddingNewRule()
{
// When someone goes to the effort of actually adding a new rule, make the default actually do something
return GetGenerationMethodFromRegistry(DefaultUVsGenerationMethodKeyWhenAddingNewRules,
AZ::SceneAPI::DataTypes::UVsGenerationMethod::SphericalProjection);
}

AZ::SceneAPI::DataTypes::UVsGenerationMethod UVsRule::GetDefaultGenerationMethodWithNoRule()
{
// when there is no rule on the mesh, do nothing by default
return GetGenerationMethodFromRegistry(DefaultUVsGenerationMethodKeyIfNoRulePresent,
AZ::SceneAPI::DataTypes::UVsGenerationMethod::LeaveSceneDataAsIs);
}

AZ::SceneAPI::DataTypes::UVsGenerationMethod UVsRule::GetGenerationMethod() const
{
return m_generationMethod;
}

bool UVsRule::GetReplaceExisting() const
{
return m_replaceExisting;
}

void UVsRule::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (!serializeContext)
{
return;
}

serializeContext->Class<UVsRule, DataTypes::IRule>()
->Version(1)
->Field("generationMethod", &UVsRule::m_generationMethod)
->Field("replaceExisting", &UVsRule::m_replaceExisting);

AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<UVsRule>("UVs", "Specify how UVs are imported or generated.")
->ClassElement(Edit::ClassElements::EditorData, "")
->Attribute("AutoExpand", true)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->DataElement(
AZ::Edit::UIHandlers::ComboBox,
&AZ::SceneAPI::SceneData::UVsRule::m_generationMethod,
"Generation Method",
"Specify the UVs generation method when UVs are generated.")
->EnumAttribute(AZ::SceneAPI::DataTypes::UVsGenerationMethod::LeaveSceneDataAsIs, "Do not generate UVs")
->EnumAttribute(AZ::SceneAPI::DataTypes::UVsGenerationMethod::SphericalProjection, "Spherical Projection")
->DataElement(0, &AZ::SceneAPI::SceneData::UVsRule::m_replaceExisting,
"Replace existing UVs",
"If true, will replace UVs in the source scene even if present in the incoming data.")
;
}
}
} // SceneData
} // SceneAPI
} // AZ
73 changes: 73 additions & 0 deletions Code/Tools/SceneAPI/SceneData/Rules/UVsRule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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
*
*/

#pragma once

#include <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
#include <SceneAPI/SceneData/SceneDataConfiguration.h>

namespace AZ
{
class ReflectContext;

namespace SceneAPI
{
namespace Containers
{
class Scene;
}

namespace DataTypes
{
class IMeshVertexUVData;
enum class UVsGenerationMethod
{
LeaveSceneDataAsIs = 0, //! don't do anything to the scene
SphericalProjection = 1 //! generate UVs using simple spherical positional projection
};
}

namespace SceneData
{
//! The UVsRule class contains the settings for one particular instance of the "Generate UVs" modifier
//! on one particular mesh group in the scene.
class SCENE_DATA_CLASS UVsRule
: public DataTypes::IRule
{
public:
AZ_RTTI(UVsRule, "{79FB186C-E9B2-4569-9172-84B85DF81DB9}", DataTypes::IRule);
AZ_CLASS_ALLOCATOR(UVsRule, AZ::SystemAllocator)

SCENE_DATA_API UVsRule();
SCENE_DATA_API ~UVsRule() override = default;

SCENE_DATA_API AZ::SceneAPI::DataTypes::UVsGenerationMethod GetGenerationMethod() const;
SCENE_DATA_API bool GetReplaceExisting() const;

static void Reflect(ReflectContext* context);

// it can be useful to have a different default for when there is no rule ("do nothing" for example)
// versus if the user actually clicks a button or something to cause a rule to exist now, ie, actually do something
// useful.

//! Return the default method for UV Generation when a Generate UVs rule is attached as a modifier to a mesh group.
SCENE_DATA_API static AZ::SceneAPI::DataTypes::UVsGenerationMethod GetDefaultGenerationMethodWhenAddingNewRule();

//! Return the default method for when there is no Generate UVs rule attached to the mesh group.
//! this should probably be left as "do nothing" unless you want to auto-generate UVs for everything without UVs
SCENE_DATA_API static AZ::SceneAPI::DataTypes::UVsGenerationMethod GetDefaultGenerationMethodWithNoRule();

protected:
AZ::SceneAPI::DataTypes::UVsGenerationMethod m_generationMethod = AZ::SceneAPI::DataTypes::UVsGenerationMethod::LeaveSceneDataAsIs;
bool m_replaceExisting = false;

};
} // SceneData
} // SceneAPI
} // AZ
2 changes: 2 additions & 0 deletions Code/Tools/SceneAPI/SceneData/SceneData_files.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ set(FILES
Rules/SkinRule.cpp
Rules/TangentsRule.h
Rules/TangentsRule.cpp
Rules/UVsRule.h
Rules/UVsRule.cpp
Rules/UnmodifiableRule.h
Rules/UnmodifiableRule.cpp
GraphData/CustomPropertyData.h
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ namespace AZ::SceneGenerationComponents
BindToCall(&TangentPreExportComponent::Register);
}

uint8_t TangentPreExportComponent::GetPriority() const
{
return AZ::SceneAPI::Events::CallProcessor::ProcessingPriority::LateProcessing;
}


void TangentPreExportComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ namespace AZ::SceneGenerationComponents

static void Reflect(AZ::ReflectContext* context);

// bumps Tangent export to later on in the generation phase, so that it can generate tangents after other rules have
// generated things like normals and UVs.

uint8_t GetPriority() const override;

AZ::SceneAPI::Events::ProcessingResult Register(AZ::SceneAPI::Events::GenerateAdditionEventContext& context);
};
} // namespace AZ::SceneGenerationComponents
Loading

0 comments on commit c50d66b

Please sign in to comment.