Skip to content

Commit

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

* 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.

I tried adding tests, but the framework is not useful right now.

Signed-off-by: Nicholas Lawson <70027408+lawsonamzn@users.noreply.github.com>
  • Loading branch information
nick-l-o3de authored Apr 7, 2023
1 parent d677f82 commit 6c25f9f
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 @@ -29,6 +29,7 @@
#include <SceneAPI/SceneData/Rules/SkinRule.h>
#include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
#include <SceneAPI/SceneData/Rules/TagRule.h>
#include <SceneAPI/SceneData/Rules/UVsRule.h>

namespace AZ
{
Expand Down Expand Up @@ -86,6 +87,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/Rules/TagRule.h>

Expand Down Expand Up @@ -75,6 +76,7 @@ namespace AZ
SceneData::SkeletonProxyRule::Reflect(context);
SceneData::SkinMeshAdvancedRule::Reflect(context);
SceneData::TangentsRule::Reflect(context);
SceneData::UVsRule::Reflect(context);
SceneData::CoordinateSystemRule::Reflect(context);
SceneData::TagRule::Reflect(context);

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 @@ -69,6 +69,8 @@ set(FILES
Rules/TagRule.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 6c25f9f

Please sign in to comment.