Merge branch 'development' of https://github.com/o3de/o3de into carlitosan-sc-bug-fix

This commit is contained in:
carlitosan
2021-12-09 13:18:45 -08:00
15 changed files with 535 additions and 247 deletions
@@ -14,30 +14,44 @@ from scene_api.scene_data import SceneGraphName
def log_exception_traceback():
"""
Outputs an exception stacktrace.
"""
"""Outputs an exception stacktrace."""
data = traceback.format_exc()
logger = logging.getLogger('python')
logger.error(data)
def sanitize_name_for_disk(name: str):
"""
Removes illegal filename characters from a string.
def sanitize_name_for_disk(name: str) -> str:
"""Removes illegal filename characters from a string.
Parameters
----------
name :
String to clean.
Returns
-------
str
Name with illegal characters removed.
:param name: String to clean.
:return: Name with illegal characters removed.
"""
return "".join(char for char in name if char not in "|<>:\"/?*\\")
def get_mesh_node_names(scene_graph: sceneData.SceneGraph) -> Tuple[List[SceneGraphName], List[str]]:
"""
Returns a tuple of all the mesh nodes as well as all the node paths
"""Returns a tuple of all the mesh nodes as well as all the node paths
Parameters
----------
scene_graph :
Scene graph to search
Returns
-------
Tuple[List[SceneGraphName], List[str]]
Tuple of [Mesh Nodes, All Node Paths]
:param scene_graph: Scene graph to search
:return: Tuple of [Mesh Nodes, All Node Paths]
"""
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
@@ -8,7 +8,7 @@
import azlmbr.bus
import azlmbr.math
from scene_api.scene_data import PrimitiveShape, DecompositionMode
from scene_api.scene_data import PrimitiveShape, DecompositionMode, ColorChannel, TangentSpaceSource, TangentSpaceMethod
from scene_helpers import *
@@ -71,6 +71,7 @@ def add_physx_meshes(scene_manifest: sceneData.SceneManifest, source_file_name:
triangle = scene_manifest.add_physx_triangle_mesh_group(source_file_name + "_triangle", False, True, True, True, True, True)
scene_manifest.physx_mesh_group_add_selected_unselected_nodes(triangle, [first_mesh], all_except_first_mesh)
def update_manifest(scene):
import uuid, os
import azlmbr.scene.graph
@@ -114,10 +115,11 @@ def update_manifest(scene):
if node != mesh_path:
scene_manifest.mesh_group_unselect_node(mesh_group, node)
scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", 1, "Col0", 2, "Col0", 2, 3)
scene_manifest.mesh_group_add_cloth_rule(mesh_group, mesh_path, "Col0", ColorChannel.GREEN, "Col0",
ColorChannel.BLUE, "Col0", ColorChannel.BLUE, ColorChannel.ALPHA)
scene_manifest.mesh_group_add_advanced_mesh_rule(mesh_group, True, False, True, "Col0")
scene_manifest.mesh_group_add_skin_rule(mesh_group, 3, 0.002)
scene_manifest.mesh_group_add_tangent_rule(mesh_group, 1, 0)
scene_manifest.mesh_group_add_tangent_rule(mesh_group, TangentSpaceSource.MIKKT_GENERATION, TangentSpaceMethod.TSPACE_BASIC)
# Create an editor entity
entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name)
@@ -150,7 +150,7 @@ namespace AzToolsFramework
return result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success;
}
// some assets may come in from the JSON serialzier with no AssetID, but have an asset hint
// some assets may come in from the JSON serializer with no AssetID, but have an asset hint
// this attempts to fix up the assets using the assetHint field
void FixUpInvalidAssets(AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
@@ -2504,6 +2504,29 @@ namespace AzToolsFramework
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
// do not create manipulators for the container entity of the focused prefab.
if (auto prefabFocusPublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabFocusPublicInterface>::Get())
{
AzFramework::EntityContextId editorEntityContextId = GetEntityContextId();
if (AZ::EntityId focusRoot = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
focusRoot.IsValid())
{
m_selectedEntityIds.erase(focusRoot);
}
}
// do not create manipulators for any entities marked as read only
if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get())
{
AZStd::erase_if(
m_selectedEntityIds,
[readOnlyEntityPublicInterface](auto entityId)
{
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
}
);
}
// note: create/destroy pattern to be addressed
DestroyManipulators(m_entityIdManipulators);
CreateEntityIdManipulators();
@@ -3635,29 +3658,6 @@ namespace AzToolsFramework
m_selectedEntityIds.clear();
m_selectedEntityIds.reserve(selectedEntityIds.size());
AZStd::copy(selectedEntityIds.begin(), selectedEntityIds.end(), AZStd::inserter(m_selectedEntityIds, m_selectedEntityIds.end()));
// Do not create manipulators for the container entity of the focused prefab.
if (auto prefabFocusPublicInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabFocusPublicInterface>::Get())
{
AzFramework::EntityContextId editorEntityContextId = GetEntityContextId();
if (AZ::EntityId focusRoot = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
focusRoot.IsValid())
{
m_selectedEntityIds.erase(focusRoot);
}
}
// Do not create manipulators for any entities marked as read only
if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get())
{
AZStd::erase_if(
m_selectedEntityIds,
[readOnlyEntityPublicInterface](auto entityId)
{
return readOnlyEntityPublicInterface->IsReadOnly(entityId);
}
);
}
}
void EditorTransformComponentSelection::OnTransformChanged(
@@ -176,7 +176,6 @@ namespace UnitTest
TEST_F(PrefabFixupTest, Test_LoadInstanceFromPrefabDom_Overload3)
{
Instance instance;
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> referencedAssets;
Instance::EntityList entityList;
(PrefabDomUtils::LoadInstanceFromPrefabDom(instance, entityList, m_prefabDom));
@@ -74,7 +74,7 @@ struct TileLightData
bool Light_IsInsideBin(uint package, uint bin)
{
return (package & (1 << bin)) != 0;
return (package & (1u << bin)) != 0;
}
uint PackLightIndexWithBinMask(uint ind, uint bins)
@@ -130,7 +130,7 @@ uint NVLC_GetBin(const float viewZ, const TileLightData data)
const float zFarCoordSystemAdjusted = data.zFar * RH_COORD_SYSTEM_REVERSE;
float f = saturate( (abs(viewZCoordSystemAdjusted) - zNearCoordSystemAdjusted) / (zFarCoordSystemAdjusted - zNearCoordSystemAdjusted) );
float bin = min(f, 0.999999) * float(1 << data.logMaxBins);
float bin = min(f, 0.999999) * float(1u << data.logMaxBins);
return uint(bin);
}
@@ -54,6 +54,8 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface)
localPos = mul(decalRot, localPos);
float3 decalUVW = localPos * rcp(decal.m_halfSize);
[branch]
if(decalUVW.x >= -1.0f && decalUVW.x <= 1.0f &&
decalUVW.y >= -1.0f && decalUVW.y <= 1.0f &&
decalUVW.z >= -1.0f && decalUVW.z <= 1.0f)
@@ -72,6 +74,7 @@ void ApplyDecal(uint currDecalIndex, inout Surface surface)
float2 normalMap = 0;
// Each texture array handles a size permutation.
// e.g. it could be that tex array 0 handles 256x256 and tex array 1 handles 512x64, etc.
[branch]
switch(textureArrayIndex)
{
case 0:
@@ -7,13 +7,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
import typing
import json
import azlmbr.scene as sceneApi
from enum import Enum, IntEnum
from enum import IntEnum
# Wraps the AZ.SceneAPI.Containers.SceneGraph.NodeIndex internal class
class SceneGraphNodeIndex:
def __init__(self, sceneGraphNodeIndex) -> None:
self.nodeIndex = sceneGraphNodeIndex
def __init__(self, scene_graph_node_index) -> None:
self.nodeIndex = scene_graph_node_index
def as_number(self):
return self.nodeIndex.AsNumber()
@@ -29,9 +29,9 @@ class SceneGraphNodeIndex:
# Wraps AZ.SceneAPI.Containers.SceneGraph.Name internal class
class SceneGraphName():
def __init__(self, sceneGraphName) -> None:
self.name = sceneGraphName
class SceneGraphName:
def __init__(self, scene_graph_name) -> None:
self.name = scene_graph_name
def get_path(self) -> str:
return self.name.GetPath()
@@ -41,9 +41,9 @@ class SceneGraphName():
# Wraps AZ.SceneAPI.Containers.SceneGraph class
class SceneGraph():
def __init__(self, sceneGraphInstance) -> None:
self.sceneGraph = sceneGraphInstance
class SceneGraph:
def __init__(self, scene_graph_instance) -> None:
self.sceneGraph = scene_graph_instance
@classmethod
def is_valid_name(cls, name):
@@ -96,53 +96,161 @@ class SceneGraph():
return self.sceneGraph.GetNodeContent(node)
class ColorChannel(IntEnum):
RED = 0
""" Red color channel """
GREEN = 1
""" Green color channel """
BLUE = 2
""" Blue color channel """
ALPHA = 3
""" Alpha color channel """
class TangentSpaceSource(IntEnum):
SCENE = 0
""" Extract the tangents and bitangents directly from the source scene file. """
MIKKT_GENERATION = 1
""" Use MikkT algorithm to generate tangents """
class TangentSpaceMethod(IntEnum):
TSPACE = 0
""" Generates the tangents and bitangents with their true magnitudes which can be used for relief mapping effects.
It calculates the 'real' bitangent which may not be perpendicular to the tangent.
However, both, the tangent and bitangent are perpendicular to the vertex normal.
"""
TSPACE_BASIC = 1
""" Calculates unit vector tangents and bitangents at pixel/vertex level which are sufficient for basic normal mapping. """
class PrimitiveShape(IntEnum):
BEST_FIT = 0
""" The algorithm will determine which of the shapes fits best. """
SPHERE = 1
""" Sphere shape """
BOX = 2
""" Box shape """
CAPSULE = 3
""" Capsule shape """
class DecompositionMode(IntEnum):
VOXEL = 0
""" Voxel-based approximate convex decomposition """
TETRAHEDRON = 1
""" Tetrahedron-based approximate convex decomposition """
# Contains a dictionary to contain and export AZ.SceneAPI.Containers.SceneManifest
class SceneManifest():
class SceneManifest:
def __init__(self):
self.manifest = {'values': []}
def add_mesh_group(self, name: str) -> dict:
meshGroup = {}
meshGroup['$type'] = '{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup'
meshGroup['name'] = name
meshGroup['nodeSelectionList'] = {'selectedNodes': [], 'unselectedNodes': []}
meshGroup['rules'] = {'rules': [{'$type': 'MaterialRule'}]}
self.manifest['values'].append(meshGroup)
return meshGroup
"""Adds a Mesh Group to the scene manifest.
Parameters
----------
name :
Name of the mesh group. This will become a file on disk and be usable as a Mesh in the editor.
Returns
-------
dict
Newly created mesh group.
"""
mesh_group = {
'$type': '{07B356B7-3635-40B5-878A-FAC4EFD5AD86} MeshGroup',
'name': name,
'nodeSelectionList': {'selectedNodes': [], 'unselectedNodes': []},
'rules': {'rules': [{'$type': 'MaterialRule'}]}
}
self.manifest['values'].append(mesh_group)
return mesh_group
def add_prefab_group(self, name: str, id: str, json: dict) -> dict:
prefabGroup = {}
prefabGroup['$type'] = '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup'
prefabGroup['name'] = name
prefabGroup['id'] = id
prefabGroup['prefabDomData'] = json
self.manifest['values'].append(prefabGroup)
return prefabGroup
"""Adds a Prefab Group to the scene manifest. This will become a file on disk and be usable as a ProceduralPrefab in the editor.
Parameters
----------
name :
Name of the prefab.
id :
Unique ID for this prefab group.
json :
The prefab template data.
Returns
-------
dict
The newly created Prefab group
"""
prefab_group = {
'$type': '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup',
'name': name,
'id': id,
'prefabDomData': json
}
self.manifest['values'].append(prefab_group)
return prefab_group
def mesh_group_select_node(self, mesh_group: dict, node_name: str) -> None:
"""Adds a node as a selected node.
Parameters
----------
mesh_group :
Mesh group to apply the selection to.
node_name :
Path of the node.
"""
mesh_group['nodeSelectionList']['selectedNodes'].append(node_name)
def mesh_group_unselect_node(self, mesh_group: dict, node_name: str) -> None:
"""Adds a node as an unselected node.
Parameters
----------
mesh_group :
Mesh group to apply the selection to.
node_name :
Path of the node.
"""
mesh_group['nodeSelectionList']['unselectedNodes'].append(node_name)
def mesh_group_add_advanced_coordinate_system(self, mesh_group: dict, origin_node_name: str, translation: object,
rotation: object, scale: float) -> None:
def mesh_group_add_advanced_coordinate_system(self, mesh_group: dict,
origin_node_name: str = '',
translation: typing.Optional[object] = None,
rotation: typing.Optional[object] = None,
scale: float = 1.0) -> None:
"""Adds an Advanced Coordinate System rule which modifies the target coordinate system,
applying a transformation to all data (transforms and vertex data if it exists).
Parameters
----------
mesh_group :
Mesh group to add the Advanced Coordinate System rule to.
origin_node_name :
Path of the node to use as the origin.
translation :
Moves the group along the given vector.
rotation :
Sets the orientation offset of the processed mesh in degrees. Rotates the group after translation.
scale :
Sets the scale offset of the processed mesh.
"""
origin_rule = {
'$type': 'CoordinateSystemRule',
'useAdvancedData': True,
'originNodeName': '' if origin_node_name is None else origin_node_name
'originNodeName': self.__default_or_value(origin_node_name, '')
}
if translation is not None:
origin_rule['translation'] = translation
@@ -153,31 +261,57 @@ class SceneManifest():
mesh_group['rules']['rules'].append(origin_rule)
def mesh_group_add_comment(self, mesh_group: dict, comment: str) -> None:
commentRule = {
"""Adds a Comment rule.
Parameters
----------
mesh_group :
Mesh group to add the comment rule to.
comment :
Text for the comment rule.
"""
comment_rule = {
'$type': 'CommentRule',
'comment': comment
}
mesh_group['rules']['rules'].append(commentRule)
mesh_group['rules']['rules'].append(comment_rule)
def __default_or_value(self, val, default):
return default if val is None else val
def mesh_group_add_cloth_rule(self, mesh_group: dict, cloth_node_name: str,
inverse_masses_stream_name: str, inverse_masses_channel: int,
motion_constraints_stream_name: str, motion_constraints_channel: int,
backstop_stream_name: str, backstop_offset_channel: int,
backstop_radius_channel: int) -> None:
"""
Adds a Cloth rule. 0 = Red, 1 = Green, 2 = Blue, 3 = Alpha
:param mesh_group: Mesh Group to add the cloth rule to
:param cloth_node_name: Name of the node that the rule applies to
:param inverse_masses_stream_name: Name of the color stream to use for inverse masses
:param inverse_masses_channel: Color channel (index) for inverse masses
:param motion_constraints_stream_name: Name of the color stream to use for motion constraints
:param motion_constraints_channel: Color channel (index) for motion constraints
:param backstop_stream_name: Name of the color stream to use for backstop
:param backstop_offset_channel: Color channel (index) for backstop offset value
:param backstop_radius_channel: Color chnanel (index) for backstop radius value
def mesh_group_add_cloth_rule(self, mesh_group: dict,
cloth_node_name: str,
inverse_masses_stream_name: typing.Optional[str],
inverse_masses_channel: typing.Optional[ColorChannel],
motion_constraints_stream_name: typing.Optional[str],
motion_constraints_channel: typing.Optional[ColorChannel],
backstop_stream_name: typing.Optional[str],
backstop_offset_channel: typing.Optional[ColorChannel],
backstop_radius_channel: typing.Optional[ColorChannel]) -> None:
"""Adds a Cloth rule.
Parameters
----------
mesh_group :
Mesh Group to add the cloth rule to
cloth_node_name :
Name of the node that the rule applies to
inverse_masses_stream_name :
Name of the color stream to use for inverse masses
inverse_masses_channel :
Color channel (index) for inverse masses
motion_constraints_stream_name :
Name of the color stream to use for motion constraints
motion_constraints_channel :
Color channel (index) for motion constraints
backstop_stream_name :
Name of the color stream to use for backstop
backstop_offset_channel :
Color channel (index) for backstop offset value
backstop_radius_channel :
Color channel (index) for backstop radius value
"""
cloth_rule = {
'$type': 'ClothRule',
@@ -186,22 +320,31 @@ class SceneManifest():
}
if inverse_masses_channel is not None:
cloth_rule['inverseMassesChannel'] = inverse_masses_channel
cloth_rule['inverseMassesChannel'] = int(inverse_masses_channel)
cloth_rule['motionConstraintsStreamName'] = self.__default_or_value(motion_constraints_stream_name, 'Default: 1.0')
if motion_constraints_channel is not None:
cloth_rule['motionConstraintsChannel'] = motion_constraints_channel
cloth_rule['motionConstraintsChannel'] = int(motion_constraints_channel)
cloth_rule['backstopStreamName'] = self.__default_or_value(backstop_stream_name, 'None')
if backstop_offset_channel is not None:
cloth_rule['backstopOffsetChannel'] = backstop_offset_channel
cloth_rule['backstopOffsetChannel'] = int(backstop_offset_channel)
if backstop_radius_channel is not None:
cloth_rule['backstopRadiusChannel'] = backstop_radius_channel
cloth_rule['backstopRadiusChannel'] = int(backstop_radius_channel)
mesh_group['rules']['rules'].append(cloth_rule)
def mesh_group_add_lod_rule(self, mesh_group: dict) -> dict:
"""
Adds an LOD rule
:param mesh_group: Mesh Group to add the rule to
:return: LOD rule
"""Adds an LOD rule.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
Returns
-------
dict
LOD rule.
"""
lod_rule = {
'$type': '{6E796AC8-1484-4909-860A-6D3F22A7346F} LodRule',
@@ -212,47 +355,76 @@ class SceneManifest():
return lod_rule
def lod_rule_add_lod(self, lod_rule: dict) -> dict:
"""
Adds an LOD level to the LOD rule. Nodes are added in order. The first node added represents LOD1, 2nd LOD2, etc
:param lod_rule: LOD rule to add the LOD level to
:return: LOD level
"""Adds an LOD level to the LOD rule. Nodes are added in order. The first node added represents LOD1, 2nd LOD2, etc.
Parameters
----------
lod_rule :
LOD rule to add the LOD level to.
Returns
-------
dict
LOD level.
"""
lod = {'selectedNodes': [], 'unselectedNodes': []}
lod_rule['nodeSelectionList'].append(lod)
return lod
def lod_select_node(self, lod: dict, selected_node: str) -> None:
"""
Adds a node as a selected node
:param lod: LOD level to add the node to
:param selected_node: Path of the node
"""Adds a node as a selected node.
Parameters
----------
lod :
LOD level to add the node to.
selected_node :
Path of the node.
"""
lod['selectedNodes'].append(selected_node)
def lod_unselect_node(self, lod: dict, unselected_node: str) -> None:
"""
Adds a node as an unselected node
:param lod: LOD rule to add the node to
:param unselected_node: Path of the node
"""Adds a node as an unselected node.
Parameters
----------
lod :
LOD rule to add the node to.
unselected_node :
Path of the node.
"""
lod['unselectedNodes'].append(unselected_node)
def mesh_group_add_advanced_mesh_rule(self, mesh_group: dict, use_32bit_vertices: bool, merge_meshes: bool,
use_custom_normals: bool,
vertex_color_stream: str) -> None:
"""
Adds an Advanced Mesh rule
:param mesh_group: Mesh Group to add the rule to
:param use_32bit_vertices: False = 16bit vertex position precision. True = 32bit vertex position precision
:param merge_meshes: Merge all meshes into a single mesh
:param use_custom_normals: True = use normals from DCC tool. False = average normals
:param vertex_color_stream: Color stream name to use for Vertex Coloring
def mesh_group_add_advanced_mesh_rule(self, mesh_group: dict,
use_32bit_vertices: bool = False,
merge_meshes: bool = True,
use_custom_normals: bool = True,
vertex_color_stream: typing.Optional[str] = None) -> None:
"""Adds an Advanced Mesh rule.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
use_32bit_vertices :
False = 16bit vertex position precision. True = 32bit vertex position precision.
merge_meshes :
Merge all meshes into a single mesh.
use_custom_normals :
True = use normals from DCC tool. False = average normals.
vertex_color_stream :
Color stream name to use for Vertex Coloring.
"""
rule = {
'$type': 'StaticMeshAdvancedRule',
'use32bitVertices': self.__default_or_value(use_32bit_vertices, False),
'mergeMeshes': self.__default_or_value(merge_meshes, True),
'useCustomNormals': self.__default_or_value(use_custom_normals, True)
'use32bitVertices': use_32bit_vertices,
'mergeMeshes': merge_meshes,
'useCustomNormals': use_custom_normals
}
if vertex_color_stream is not None:
@@ -260,37 +432,51 @@ class SceneManifest():
mesh_group['rules']['rules'].append(rule)
def mesh_group_add_skin_rule(self, mesh_group: dict, max_weights_per_vertex: int, weight_threshold: float) -> None:
"""
Adds a Skin rule
:param mesh_group: Mesh Group to add the rule to
:param max_weights_per_vertex: Max number of joints that can influence a vertex
:param weight_threshold: Weight values below this value will be treated as 0
def mesh_group_add_skin_rule(self, mesh_group: dict, max_weights_per_vertex: int = 4, weight_threshold: float = 0.001) -> None:
"""Adds a Skin rule.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
max_weights_per_vertex :
Max number of joints that can influence a vertex.
weight_threshold :
Weight values below this value will be treated as 0.
"""
rule = {
'$type': 'SkinRule',
'maxWeightsPerVertex': self.__default_or_value(max_weights_per_vertex, 4),
'weightThreshold': self.__default_or_value(weight_threshold, 0.001)
'maxWeightsPerVertex': max_weights_per_vertex,
'weightThreshold': weight_threshold
}
mesh_group['rules']['rules'].append(rule)
def mesh_group_add_tangent_rule(self, mesh_group: dict, tangent_space: int, tspace_method: int) -> None:
"""
Adds a Tangent rule to control tangent space generation
:param mesh_group: Mesh Group to add the rule to
:param tangent_space: Tangent space source. 0 = Scene, 1 = MikkT Tangent Generation
:param tspace_method: MikkT Generation method. 0 = TSpace, 1 = TSpaceBasic
def mesh_group_add_tangent_rule(self, mesh_group: dict,
tangent_space: TangentSpaceSource = TangentSpaceSource.SCENE,
tspace_method: TangentSpaceMethod = TangentSpaceMethod.TSPACE) -> None:
"""Adds a Tangent rule to control tangent space generation.
Parameters
----------
mesh_group :
Mesh Group to add the rule to.
tangent_space :
Tangent space source. 0 = Scene, 1 = MikkT Tangent Generation.
tspace_method :
MikkT Generation method. 0 = TSpace, 1 = TSpaceBasic.
"""
rule = {
'$type': 'TangentsRule',
'tangentSpace': self.__default_or_value(tangent_space, 1),
'tSpaceMethod': self.__default_or_value(tspace_method, 0)
'tangentSpace': int(tangent_space),
'tSpaceMethod': int(tspace_method)
}
mesh_group['rules']['rules'].append(rule)
def __add_physx_base_mesh_group(self, name: str, physics_material: typing.Optional[str]) -> dict:
def __add_physx_base_mesh_group(self, name: str, physics_material: typing.Optional[str] = None) -> dict:
import azlmbr.math
group = {
'$type': '{5B03C8E6-8CEE-4DA0-A7FA-CD88689DD45B} MeshGroup',
@@ -314,7 +500,9 @@ class SceneManifest():
return group
def add_physx_triangle_mesh_group(self, name: str, merge_meshes: bool = True, weld_vertices: bool = False,
def add_physx_triangle_mesh_group(self, name: str,
merge_meshes: bool = True,
weld_vertices: bool = False,
disable_clean_mesh: bool = False,
force_32bit_indices: bool = False,
suppress_triangle_mesh_remap_table: bool = False,
@@ -322,26 +510,42 @@ class SceneManifest():
mesh_weld_tolerance: float = 0.0,
num_tris_per_leaf: int = 4,
physics_material: typing.Optional[str] = None) -> dict:
"""
Adds a Triangle type PhysX Mesh Group to the scene.
"""Adds a Triangle type PhysX Mesh Group to the scene.
Parameters
----------
name :
Name of the mesh group.
merge_meshes :
When true, all selected nodes will be merged into a single collision mesh.
weld_vertices :
When true, mesh welding is performed. Clean mesh must be enabled.
disable_clean_mesh :
When true, mesh cleaning is disabled. This makes cooking faster.
force_32bit_indices :
When true, 32-bit indices will always be created regardless of triangle count.
suppress_triangle_mesh_remap_table :
When true, the face remap table is not created.
This saves a significant amount of memory, but the SDK will not be able to provide the remap
information for internal mesh triangles returned by collisions, sweeps or raycasts hits.
build_triangle_adjacencies :
When true, the triangle adjacency information is created.
mesh_weld_tolerance :
If mesh welding is enabled, this controls the distance at
which vertices are welded. If mesh welding is not enabled, this value defines the
acceptance distance for mesh validation. Provided no two vertices are within this
distance, the mesh is considered to be clean. If not, a warning will be emitted.
num_tris_per_leaf :
Mesh cooking hint for max triangles per leaf limit. Fewer triangles per leaf
produces larger meshes with better runtime performance and worse cooking performance.
physics_material :
Configure which physics material to use.
Returns
-------
dict
The newly created mesh group.
:param name: Name of the mesh group.
:param merge_meshes: When true, all selected nodes will be merged into a single collision mesh.
:param weld_vertices: When true, mesh welding is performed. Clean mesh must be enabled.
:param disable_clean_mesh: When true, mesh cleaning is disabled. This makes cooking faster.
:param force_32bit_indices: When true, 32-bit indices will always be created regardless of triangle count.
:param suppress_triangle_mesh_remap_table: When true, the face remap table is not created.
This saves a significant amount of memory, but the SDK will not be able to provide the remap
information for internal mesh triangles returned by collisions, sweeps or raycasts hits.
:param build_triangle_adjacencies: When true, the triangle adjacency information is created.
:param mesh_weld_tolerance: If mesh welding is enabled, this controls the distance at
which vertices are welded. If mesh welding is not enabled, this value defines the
acceptance distance for mesh validation. Provided no two vertices are within this
distance, the mesh is considered to be clean. If not, a warning will be emitted.
:param num_tris_per_leaf: Mesh cooking hint for max triangles per leaf limit. Fewer triangles per leaf
produces larger meshes with better runtime performance and worse cooking performance.
:param physics_material: Configure which physics material to use.
:return: The newly created mesh group.
"""
group = self.__add_physx_base_mesh_group(name, physics_material)
group["export method"] = 0
@@ -367,32 +571,49 @@ class SceneManifest():
gauss_map_limit: int = 32,
build_gpu_data: bool = False,
physics_material: typing.Optional[str] = None) -> dict:
"""
Adds a Convex type PhysX Mesh Group to the scene.
"""Adds a Convex type PhysX Mesh Group to the scene.
Parameters
----------
name :
Name of the mesh group.
area_test_epsilon :
If the area of a triangle of the hull is below this value, the triangle will be
rejected. This test is done only if Check Zero Area Triangles is used.
plane_tolerance :
The value is used during hull construction. When a new point is about to be added
to the hull it gets dropped when the point is closer to the hull than the planeTolerance.
use_16bit_indices :
Denotes the use of 16-bit vertex indices in Convex triangles or polygons.
check_zero_area_triangles :
Checks and removes almost zero-area triangles during convex hull computation.
The rejected area size is specified in Area Test Epsilon.
quantize_input :
Quantizes the input vertices using the k-means clustering.
use_plane_shifting :
Enables plane shifting vertex limit algorithm. Plane shifting is an alternative
algorithm for the case when the computed hull has more vertices than the specified vertex
limit.
shift_vertices :
Convex hull input vertices are shifted to be around origin to provide better
computation stability
gauss_map_limit :
Vertex limit beyond which additional acceleration structures are computed for each
convex mesh. Increase that limit to reduce memory usage. Computing the extra structures
all the time does not guarantee optimal performance.
build_gpu_data :
When true, additional information required for GPU-accelerated rigid body
simulation is created. This can increase memory usage and cooking times for convex meshes
and triangle meshes. Convex hulls are created with respect to GPU simulation limitations.
Vertex limit is set to 64 and vertex limit per face is internally set to 32.
physics_material :
Configure which physics material to use.
Returns
-------
dict
The newly created mesh group.
:param name: Name of the mesh group.
:param area_test_epsilon: If the area of a triangle of the hull is below this value, the triangle will be
rejected. This test is done only if Check Zero Area Triangles is used.
:param plane_tolerance: The value is used during hull construction. When a new point is about to be added
to the hull it gets dropped when the point is closer to the hull than the planeTolerance.
:param use_16bit_indices: Denotes the use of 16-bit vertex indices in Convex triangles or polygons.
:param check_zero_area_triangles: Checks and removes almost zero-area triangles during convex hull computation.
The rejected area size is specified in Area Test Epsilon.
:param quantize_input: Quantizes the input vertices using the k-means clustering.
:param use_plane_shifting: Enables plane shifting vertex limit algorithm. Plane shifting is an alternative
algorithm for the case when the computed hull has more vertices than the specified vertex
limit.
:param shift_vertices: Convex hull input vertices are shifted to be around origin to provide better
computation stability
:param gauss_map_limit: Vertex limit beyond which additional acceleration structures are computed for each
convex mesh. Increase that limit to reduce memory usage. Computing the extra structures
all the time does not guarantee optimal performance.
:param build_gpu_data: When true, additional information required for GPU-accelerated rigid body
simulation is created. This can increase memory usage and cooking times for convex meshes
and triangle meshes. Convex hulls are created with respect to GPU simulation limitations.
Vertex limit is set to 64 and vertex limit per face is internally set to 32.
:param physics_material: Configure which physics material to use.
:return: The newly created mesh group.
"""
group = self.__add_physx_base_mesh_group(name, physics_material)
group["export method"] = 1
@@ -414,17 +635,27 @@ class SceneManifest():
primitive_shape_target: PrimitiveShape = PrimitiveShape.BEST_FIT,
volume_term_coefficient: float = 0.0,
physics_material: typing.Optional[str] = None) -> dict:
"""
Adds a Primitive Shape type PhysX Mesh Group to the scene
"""Adds a Primitive Shape type PhysX Mesh Group to the scene
Parameters
----------
name :
Name of the mesh group.
primitive_shape_target :
The shape that should be fitted to this mesh. If BEST_FIT is selected, the
algorithm will determine which of the shapes fits best.
volume_term_coefficient :
This parameter controls how aggressively the primitive fitting algorithm will try
to minimize the volume of the fitted primitive. A value of 0 (no volume minimization) is
recommended for most meshes, especially those with moderate to high vertex counts.
physics_material :
Configure which physics material to use.
Returns
-------
dict
The newly created mesh group.
:param name: Name of the mesh group.
:param primitive_shape_target: The shape that should be fitted to this mesh. If BEST_FIT is selected, the
algorithm will determine which of the shapes fits best.
:param volume_term_coefficient: This parameter controls how aggressively the primitive fitting algorithm will try
to minimize the volume of the fitted primitive. A value of 0 (no volume minimization) is
recommended for most meshes, especially those with moderate to high vertex counts.
:param physics_material: Configure which physics material to use.
:return: The newly created mesh group.
"""
group = self.__add_physx_base_mesh_group(name, physics_material)
group["export method"] = 2
@@ -447,26 +678,40 @@ class SceneManifest():
convex_hull_downsampling: int = 4,
pca: bool = False,
project_hull_vertices: bool = True) -> None:
"""
Enables and configures mesh decomposition for a PhysX Mesh Group.
"""Enables and configures mesh decomposition for a PhysX Mesh Group.
Only valid for convex or primitive mesh types.
:param mesh_group: Mesh group to configure decomposition for.
:param max_convex_hulls: Controls the maximum number of hulls to generate.
:param max_num_vertices_per_convex_hull: Controls the maximum number of triangles per convex hull.
:param concavity: Maximum concavity of each approximate convex hull.
:param resolution: Maximum number of voxels generated during the voxelization stage.
:param mode: Select voxel-based approximate convex decomposition or tetrahedron-based
approximate convex decomposition.
:param alpha: Controls the bias toward clipping along symmetry planes.
:param beta: Controls the bias toward clipping along revolution axes.
:param min_volume_per_convex_hull: Controls the adaptive sampling of the generated convex hulls.
:param plane_downsampling: Controls the granularity of the search for the best clipping plane.
:param convex_hull_downsampling: Controls the precision of the convex hull generation process
during the clipping plane selection stage.
:param pca: Enable or disable normalizing the mesh before applying the convex decomposition.
:param project_hull_vertices: Project the output convex hull vertices onto the original source mesh to increase
the floating point accuracy of the results.
Parameters
----------
mesh_group :
Mesh group to configure decomposition for.
max_convex_hulls :
Controls the maximum number of hulls to generate.
max_num_vertices_per_convex_hull :
Controls the maximum number of triangles per convex hull.
concavity :
Maximum concavity of each approximate convex hull.
resolution :
Maximum number of voxels generated during the voxelization stage.
mode :
Select voxel-based approximate convex decomposition or tetrahedron-based
approximate convex decomposition.
alpha :
Controls the bias toward clipping along symmetry planes.
beta :
Controls the bias toward clipping along revolution axes.
min_volume_per_convex_hull :
Controls the adaptive sampling of the generated convex hulls.
plane_downsampling :
Controls the granularity of the search for the best clipping plane.
convex_hull_downsampling :
Controls the precision of the convex hull generation process
during the clipping plane selection stage.
pca :
Enable or disable normalizing the mesh before applying the convex decomposition.
project_hull_vertices :
Project the output convex hull vertices onto the original source mesh to increase
the floating point accuracy of the results.
"""
mesh_group['DecomposeMeshes'] = True
mesh_group['ConvexDecompositionParams'] = {
@@ -485,41 +730,54 @@ class SceneManifest():
}
def physx_mesh_group_add_selected_node(self, mesh_group: dict, node: str) -> None:
"""
Adds a node to the selected nodes list
"""Adds a node to the selected nodes list
:param mesh_group: Mesh group to add to.
:param node: Node path to add.
Parameters
----------
mesh_group :
Mesh group to add to.
node :
Node path to add.
"""
mesh_group['NodeSelectionList']['selectedNodes'].append(node)
def physx_mesh_group_add_unselected_node(self, mesh_group: dict, node: str) -> None:
"""
Adds a node to the unselected nodes list
"""Adds a node to the unselected nodes list
:param mesh_group: Mesh group to add to.
:param node: Node path to add.
Parameters
----------
mesh_group :
Mesh group to add to.
node :
Node path to add.
"""
mesh_group['NodeSelectionList']['unselectedNodes'].append(node)
def physx_mesh_group_add_selected_unselected_nodes(self, mesh_group: dict, selected: typing.List[str],
unselected: typing.List[str]) -> None:
"""
Adds a set of nodes to the selected/unselected node lists
"""Adds a set of nodes to the selected/unselected node lists
:param mesh_group: Mesh group to add to.
:param selected: List of node paths to add to the selected list.
:param unselected: List of node paths to add to the unselected list.
Parameters
----------
mesh_group :
Mesh group to add to.
selected :
List of node paths to add to the selected list.
unselected :
List of node paths to add to the unselected list.
"""
mesh_group['NodeSelectionList']['selectedNodes'].extend(selected)
mesh_group['NodeSelectionList']['unselectedNodes'].extend(unselected)
def physx_mesh_group_add_comment(self, mesh_group: dict, comment: str) -> None:
"""
Adds a comment rule
"""Adds a comment rule
:param mesh_group: Mesh group to add the rule to.
:param comment: Comment string.
Parameters
----------
mesh_group :
Mesh group to add the rule to.
comment :
Comment string.
"""
rule = {
"$type": "CommentRule",
@@ -433,16 +433,6 @@ namespace ScriptCanvasEditor
AzToolsFramework::ToolsApplicationNotificationBus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree_NewContent);
}
void EditorScriptCanvasComponent::OnStartPlayInEditor()
{
ScriptCanvas::Execution::PerformanceStatisticsEBus::Broadcast(&ScriptCanvas::Execution::PerformanceStatisticsBus::ClearSnaphotStatistics);
}
void EditorScriptCanvasComponent::OnStopPlayInEditor()
{
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::GarbageCollect);
}
void EditorScriptCanvasComponent::SetAssetId(const SourceHandle& assetId)
{
if (m_sourceHandle.Describe() != assetId.Describe())
@@ -93,9 +93,7 @@ namespace ScriptCanvasEditor
//=====================================================================
// EditorEntityContextNotificationBus
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
protected:
enum class SourceChangeDescription : AZ::u8
@@ -34,6 +34,7 @@
#include <ScriptCanvas/Core/Datum.h>
#include <ScriptCanvas/Data/DataRegistry.h>
#include <ScriptCanvas/Libraries/Libraries.h>
#include <ScriptCanvas/PerformanceStatisticsBus.h>
#include <ScriptCanvas/Variable/VariableCore.h>
#include <ScriptCanvas/View/EditCtrls/GenericLineEditCtrl.h>
@@ -338,10 +339,20 @@ namespace ScriptCanvasEditor
}
};
openers.push_back({ "O3DE_ScriptCanvasEditor", "Open In Script Canvas Editor...", QIcon(), scriptCanvasEditorCallback });
openers.push_back({ "O3DE_ScriptCanvasEditor", "Open In Script Canvas Editor...", QIcon(ScriptCanvasAssetDescription().GetIconPathImpl()), scriptCanvasEditorCallback });
}
}
void SystemComponent::OnStartPlayInEditor()
{
ScriptCanvas::Execution::PerformanceStatisticsEBus::Broadcast(&ScriptCanvas::Execution::PerformanceStatisticsBus::ClearSnaphotStatistics);
}
void SystemComponent::OnStopPlayInEditor()
{
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::GarbageCollect);
}
void SystemComponent::OnUserSettingsActivated()
{
PopulateEditorCreatableTypes();
@@ -23,6 +23,7 @@
#include <Editor/View/Windows/Tools/UpgradeTool/Model.h>
#include <ScriptCanvas/Bus/ScriptCanvasBus.h>
#include <ScriptCanvas/Bus/ScriptCanvasExecutionBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
namespace ScriptCanvasEditor
{
@@ -36,6 +37,8 @@ namespace ScriptCanvasEditor
, private AZ::Data::AssetBus::MultiHandler
, private AzToolsFramework::AssetSeedManagerRequests::Bus::Handler
, private AzToolsFramework::EditorContextMenuBus::Handler
, private AzToolsFramework::EditorEntityContextNotificationBus::Handler
{
public:
AZ_COMPONENT(SystemComponent, "{1DE7A120-4371-4009-82B5-8140CB1D7B31}");
@@ -97,7 +100,12 @@ namespace ScriptCanvasEditor
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
protected:
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
private:
SystemComponent(const SystemComponent&) = delete;
@@ -84,6 +84,7 @@ namespace ScriptCanvas
}
}
return true;
}
@@ -503,20 +503,21 @@ namespace ScriptCanvas
void InitializeInterpretedStatics(RuntimeData& runtimeData)
{
if (!runtimeData.m_areStaticsInitialized)
AZ_Error("ScriptCanvas", !runtimeData.m_areStaticsInitialized, "ScriptCanvas runtime data already initalized");
{
runtimeData.m_areStaticsInitialized = true;
for (auto& dependency : runtimeData.m_requiredAssets)
{
InitializeInterpretedStatics(dependency.Get()->GetData());
if (!dependency.Get()->GetData().m_areStaticsInitialized)
{
InitializeInterpretedStatics(dependency.Get()->GetData());
}
}
#if defined(AZ_PROFILE_BUILD) || defined(AZ_DEBUG_BUILD)
Execution::InitializeFromLuaStackFunctions(const_cast<Grammar::DebugSymbolMap&>(runtimeData.m_debugMap));
#endif
AZ_WarningOnce("ScriptCanvas", !runtimeData.m_areStaticsInitialized, "ScriptCanvas runtime data already initalized");
if (runtimeData.RequiresStaticInitialization())
{
AZ::ScriptLoadResult result{};
@@ -49,7 +49,10 @@ namespace ScriptCanvas
, config.asset.GetId().ToString<AZStd::string>().data());
#endif
Execution::InitializeInterpretedStatics(runtimeAsset->GetData());
if (!runtimeAsset->GetData().m_areStaticsInitialized)
{
Execution::InitializeInterpretedStatics(runtimeAsset->GetData());
}
}
void ExecutionStateInterpreted::ClearLuaRegistryIndex()