Merge branch 'development' into Atom/dmcdiar/ATOM-5702

monroegm-disable-blank-issue-2
Doug McDiarmid 5 years ago
commit 72e5ab4437

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<style type="text/css">
.st0{filter:url(#filter-1);}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
</style>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0">
</feColorMatrix>
</filter>
<g id="Working-Design-Page-v2">
<g id="_x32_A---Normal-Mode---Pre-Edit-Mode" transform="translate(-343.000000, -156.000000)">
<g id="Tool-Bar-Copy-2" transform="translate(336.000000, 140.000000)">
<g id="Icons-_x2F_-Toolbar-_x2F_-Move" transform="translate(5.000000, 14.000000)" class="st0">
<g>
<g id="Group_1_" transform="translate(2.000000, 2.000000)">
<path id="Combined-Shape_1_" class="st1" d="M11,13.9v2l3.2,0L10,20l-4.1-4.1l3.2,0v-2H11z M15.9,5.9L20,10l-4.1,4.1l0-3.2h-2
V9h2L15.9,5.9z M4.1,5.9l0,3.2h2v2h-2l0,3.2L0,10L4.1,5.9z M10,0l4.1,4.1l-3.2,0v2H9v-2l-3.2,0L10,0z"/>
<rect id="Rectangle_1_" x="7" y="7" class="st1" width="6" height="6"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="17px" viewBox="0 0 20 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Toolbar / Rotate</title>
<defs>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"></feColorMatrix>
</filter>
</defs>
<g id="Working-Design-Page" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="4A" transform="translate(-343.000000, -190.000000)">
<g id="Tool-Bar-Copy-2" transform="translate(336.000000, 140.000000)">
<g id="Icons-/-Toolbar-/-Rotate" transform="translate(5.000000, 45.000000)" filter="url(#filter-1)">
<g>
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<path d="M14,12.7573593 L14,21.2426407 L9.75735931,17 L14,12.7573593 Z M12,5 C17.5228475,5 22,7.3608081 22,12 C22,16.0635474 18.5649911,18.3790487 14.0010174,18.8911124 L14.0009061,16.4168268 C17.3303835,15.9561668 19.5351812,14.3225002 19.5351812,12 C19.5351812,9.23857625 16.418278,8.0041333 12,8.0041333 C7.581722,8.0041333 4.51608017,9.23857625 4.51608017,12 C4.51608017,13.8140495 5.1333787,15.673982 7.29370866,16.4168268 C7.95862159,16.7906273 8.29107806,17.0452093 8.29107806,17.180573 C8.29107806,17.3159366 7.86071871,17.2557456 7,17 C4.90930531,16.4680156 4.01338412,15.8858396 3.24544096,14.8949777 C2.71607427,14.2119464 2,13.4055574 2,12 C2,7.3608081 6.4771525,5 12,5 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Toolbar / Scale</title>
<defs>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"></feColorMatrix>
</filter>
</defs>
<g id="Working-Design-Page" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="4A" transform="translate(-344.000000, -219.000000)">
<g id="Tool-Bar-Copy-2" transform="translate(336.000000, 140.000000)">
<g id="Icons-/-Toolbar-/-Scale" transform="translate(5.000000, 76.000000)" filter="url(#filter-1)">
<g>
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<path d="M21,3 L20.999,4.399 L21,4.40005893 L20.999,4.401 L21,9 L18.7,6.701 L14.4000589,11 L13,9.59994107 L17.299,5.3 L15,3 L21,3 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
<path d="M3,21 L3,15 L5.356,17.356 L9.71109794,13 L11,14.2889021 L6.644,18.644 L9,21 L3,21 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<style type="text/css">
.st0{filter:url(#filter-1);}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
</style>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0">
</feColorMatrix>
</filter>
<g id="Working-Design-Page-v2">
<g id="_x32_A---Normal-Mode---Pre-Edit-Mode" transform="translate(-343.000000, -156.000000)">
<g id="Tool-Bar-Copy-2" transform="translate(336.000000, 140.000000)">
<g id="Icons-_x2F_-Toolbar-_x2F_-Move" transform="translate(5.000000, 14.000000)" class="st0">
<g>
<g id="Group_1_" transform="translate(2.000000, 2.000000)">
<path id="Combined-Shape_1_" class="st1" d="M11,13.9v2l3.2,0L10,20l-4.1-4.1l3.2,0v-2H11z M15.9,5.9L20,10l-4.1,4.1l0-3.2h-2
V9h2L15.9,5.9z M4.1,5.9l0,3.2h2v2h-2l0,3.2L0,10L4.1,5.9z M10,0l4.1,4.1l-3.2,0v2H9v-2l-3.2,0L10,0z"/>
<rect id="Rectangle_1_" x="7" y="7" class="st1" width="6" height="6"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="17px" viewBox="0 0 20 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Toolbar / Rotate</title>
<defs>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"></feColorMatrix>
</filter>
</defs>
<g id="Working-Design-Page" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="4A" transform="translate(-343.000000, -190.000000)">
<g id="Tool-Bar-Copy-2" transform="translate(336.000000, 140.000000)">
<g id="Icons-/-Toolbar-/-Rotate" transform="translate(5.000000, 45.000000)" filter="url(#filter-1)">
<g>
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<path d="M14,12.7573593 L14,21.2426407 L9.75735931,17 L14,12.7573593 Z M12,5 C17.5228475,5 22,7.3608081 22,12 C22,16.0635474 18.5649911,18.3790487 14.0010174,18.8911124 L14.0009061,16.4168268 C17.3303835,15.9561668 19.5351812,14.3225002 19.5351812,12 C19.5351812,9.23857625 16.418278,8.0041333 12,8.0041333 C7.581722,8.0041333 4.51608017,9.23857625 4.51608017,12 C4.51608017,13.8140495 5.1333787,15.673982 7.29370866,16.4168268 C7.95862159,16.7906273 8.29107806,17.0452093 8.29107806,17.180573 C8.29107806,17.3159366 7.86071871,17.2557456 7,17 C4.90930531,16.4680156 4.01338412,15.8858396 3.24544096,14.8949777 C2.71607427,14.2119464 2,13.4055574 2,12 C2,7.3608081 6.4771525,5 12,5 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icons / Toolbar / Scale</title>
<defs>
<filter id="filter-1">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 0 1.000000 0 0 0 1.000000 0"></feColorMatrix>
</filter>
</defs>
<g id="Working-Design-Page" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="4A" transform="translate(-344.000000, -219.000000)">
<g id="Tool-Bar-Copy-2" transform="translate(336.000000, 140.000000)">
<g id="Icons-/-Toolbar-/-Scale" transform="translate(5.000000, 76.000000)" filter="url(#filter-1)">
<g>
<rect id="Icon-Background" x="0" y="0" width="24" height="24"></rect>
<path d="M21,3 L20.999,4.399 L21,4.40005893 L20.999,4.401 L21,9 L18.7,6.701 L14.4000589,11 L13,9.59994107 L17.299,5.3 L15,3 L21,3 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
<path d="M3,21 L3,15 L5.356,17.356 L9.71109794,13 L11,14.2889021 L6.644,18.644 L9,21 L3,21 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

@ -10,7 +10,7 @@
#
if(NOT PROJECT_NAME)
cmake_minimum_required(VERSION 3.19)
cmake_minimum_required(VERSION 3.20)
project(AutomatedTesting
LANGUAGES C CXX
VERSION 1.0.0.0

@ -49,7 +49,7 @@ class TestViewMenuOptions(EditorTestHelper):
view_menu_options = [
("Center on Selection",),
("Show Quick Access Bar",),
("Viewport", "Wireframe"),
("Viewport", "Configure Layout"),
("Viewport", "Go to Position"),
("Viewport", "Center on Selection"),
("Viewport", "Go to Location"),

@ -89,7 +89,7 @@ class TestMenus(object):
expected_lines = [
"Center on Selection Action triggered",
"Show Quick Access Bar Action triggered",
"Wireframe Action triggered",
"Configure Layout Action triggered",
"Go to Position Action triggered",
"Center on Selection Action triggered",
"Go to Location Action triggered",

@ -56,6 +56,7 @@ def C18243584_Joints_HingeSoftLimitsConstrained():
"""
import os
import sys
import math
import ImportPathHelper as imports
@ -93,22 +94,51 @@ def C18243584_Joints_HingeSoftLimitsConstrained():
Report.info_vector3(lead.position, "lead initial position:")
Report.info_vector3(follower.position, "follower initial position:")
leadInitialPosition = lead.position
followerInitialPosition = follower.position
# 4) Wait for several seconds
general.idle_wait(4.0) # wait for lead and follower to move
# 4) Wait for the follower to move above the lead or Timeout
normalizedStartPos = JointsHelper.getRelativeVector(lead.position, follower.position)
normalizedStartPos = normalizedStartPos.GetNormalizedSafe()
class WaitCondition:
TARGET_ANGLE = math.radians(45)
TARGET_MAX_ANGLE = math.radians(180)
angleAchieved = 0.0
followerMovedAbove45Deg = False #this is expected to be true to pass the test
followerMovedAbove180Deg = True #this is expected to be false to pass the test
def checkConditionMet(self):
#calculate the current follower-lead vector
normalVec = JointsHelper.getRelativeVector(lead.position, follower.position)
normalVec = normalVec.GetNormalizedSafe()
#dot product + acos to get the angle
currentAngle = math.acos(normalizedStartPos.Dot(normalVec))
#if the angle is now less then last time, it is no longer rising, so end the test.
if currentAngle < self.angleAchieved:
return True
self.angleAchieved = currentAngle
self.followerMovedAbove45Deg = currentAngle > self.TARGET_ANGLE
self.followerMovedAbove180Deg = currentAngle > self.TARGET_MAX_ANGLE
return False
def isFollowerPositionCorrect(self):
return self.followerMovedAbove45Deg and not self.followerMovedAbove180Deg
waitCondition = WaitCondition()
MAX_WAIT_TIME = 5.0 #seconds
conditionMet = helper.wait_for_condition(lambda: waitCondition.checkConditionMet(), MAX_WAIT_TIME)
# 5) Check to see if lead and follower behaved as expected
Report.info_vector3(lead.position, "lead position after 1 second:")
Report.info_vector3(follower.position, "follower position after 1 second:")
Report.info_vector3(lead.position, "lead position after test:")
Report.info_vector3(follower.position, "follower position after test:")
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
followerMovedInXOnly = ((follower.position.x > leadInitialPosition.x) > FLOAT_EPSILON and
(follower.position.z - leadInitialPosition.z) > FLOAT_EPSILON)
Report.critical_result(Tests.check_follower_position, followerMovedInXOnly)
Report.critical_result(Tests.check_follower_position, conditionMet and waitCondition.isFollowerPositionCorrect())
# 6) Exit Game Mode
helper.exit_game_mode(Tests.exit_game_mode)

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:16f592487e8973abcf6b696aee6e430924c22daac0d6bb781c9e76153cd932f7
size 8885
oid sha256:352a64f523b3246000393309fa7f14955fe554e0b792a4177349f0f2db8a2b62
size 5901

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4d5c38cf9b97ae28c391916e6637aebf767d5ac009df61e730396fe8b116f3e5
size 6913
oid sha256:31bd1feb92c3bb8a5c5df4638927a9a80e879329965732c4c32f673c236a8b0a
size 6021

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a6f26f1c1c037fa0b848ac9b3d9a76e476b4853d770f1a77c01714286733b567
size 6921
oid sha256:063779c1e80ce22319cb82ff0d7635d3dcbb043b789c3830cc405ffa36d3c0ef
size 5876

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4ffd3c4ee04fa8a414995c39c7ca79246e3d9b0ceee1ad1b85d61a5298f71495
size 6940
oid sha256:5bd3a952841aa924a4869c74fad7ed397667a87948270f0ce35f314e6cf2e14a
size 5927

@ -9,17 +9,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
# Cmake version 3.19 is the minimum version needed for all of Open 3D Engine's supported platforms
cmake_minimum_required(VERSION 3.19)
# CMP0111 introduced in 3.19 has a bug that produces the policy to warn every time there is an
# INTERFACE IMPORTED library. We use this type of libraries for handling 3rdParty. The rest of
# the documentation states that INTERFACE IMPORTED libraries do not require to set locations, but
# the policy still warns about it. Issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21470
# The issue was fixed in 3.19.1 so we just disable the policy for 3.19
if(CMAKE_VERSION VERSION_EQUAL 3.19)
cmake_policy(SET CMP0111 OLD)
endif()
# Cmake version 3.20 is the minimum version needed for all of Open 3D Engine's supported platforms
cmake_minimum_required(VERSION 3.20)
include(cmake/LySet.cmake)
include(cmake/Version.cmake)
@ -114,24 +105,25 @@ endforeach()
# Post-processing
################################################################################
# The following steps have to be done after all targets are registered:
# Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic
# builds until after all the targets are known
ly_delayed_generate_static_modules_inl()
# 1. Add any dependencies registered via ly_enable_gems
ly_enable_gems_delayed()
# 2. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls
# 2. Defer generation of the StaticModules.inl file which is needed to create the AZ::Module derived class in monolithic
# builds until after all the targets are known and all the gems are enabled
ly_delayed_generate_static_modules_inl()
# 3. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls
# to provide applications with the filenames of gem modules to load
# This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIES as MANUALLY_ADDED_DEPENDENCIES
# if the build dependency is a MODULE_LIBRARY. That would cause a false load dependency to be generated
ly_delayed_generate_settings_registry()
# 3. link targets where the dependency was yet not declared, we need to have the declaration so we do different
# 4. link targets where the dependency was yet not declared, we need to have the declaration so we do different
# linking logic depending on the type of target
ly_delayed_target_link_libraries()
# 4. generate a registry file for unit testing for platforms that support unit testing
# 5. generate a registry file for unit testing for platforms that support unit testing
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_delayed_generate_unit_test_module_registry()
endif()

@ -1,79 +0,0 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Marsenne Twister PRNG. See MT.h for more info.
#include "MTPseudoRandom.h"
// non-inline function definitions and static member definitions cannot
// reside in header file because of the risk of multiple declarations
void CMTRand_int32::gen_state() // generate new m_nState vector
{
for (int i = 0; i < (n - m); ++i)
{
m_nState[i] = m_nState[i + m] ^ twiddle(m_nState[i], m_nState[i + 1]);
}
for (int i = n - m; i < (n - 1); ++i)
{
m_nState[i] = m_nState[i + m - n] ^ twiddle(m_nState[i], m_nState[i + 1]);
}
m_nState[n - 1] = m_nState[m - 1] ^ twiddle(m_nState[n - 1], m_nState[0]);
p = 0; // reset position
}
void CMTRand_int32::seed(uint32 s) // init by 32 bit seed
{ //if (s == 0)
//m_nRandom = 1;
for (int i = 0; i < n; ++i)
{
m_nState[i] = 0x0UL;
}
m_nState[0] = s;
for (int i = 1; i < n; ++i)
{
m_nState[i] = 1812433253UL * (m_nState[i - 1] ^ (m_nState[i - 1] >> 30)) + i;
// see Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier
// in the previous versions, MSBs of the seed affect only MSBs of the array m_nState
// 2002/01/09 modified by Makoto Matsumoto
}
p = n; // force gen_state() to be called for next random number
}
void CMTRand_int32::seed(const uint32* array, int size) // init by array
{
seed(19650218UL);
int i = 1, j = 0;
for (int k = ((n > size) ? n : size); k; --k)
{
m_nState[i] = (m_nState[i] ^ ((m_nState[i - 1] ^ (m_nState[i - 1] >> 30)) * 1664525UL))
+ array[j] + j; // non linear
++j;
j %= size;
if ((++i) == n)
{
m_nState[0] = m_nState[n - 1];
i = 1;
}
}
for (int k = n - 1; k; --k)
{
PREFAST_SUPPRESS_WARNING(6385) PREFAST_SUPPRESS_WARNING(6386) m_nState[i] = (m_nState[i] ^ ((m_nState[i - 1] ^ (m_nState[i - 1] >> 30)) * 1566083941UL)) - i;
if ((++i) == n)
{
m_nState[0] = m_nState[n - 1];
i = 1;
}
}
m_nState[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array
p = n; // force gen_state() to be called for next random number
}

@ -1,166 +0,0 @@
// mtrand.h
// C++ include file for MT19937, with initialization improved 2002/1/26.
// Coded by Takuji Nishimura and Makoto Matsumoto.
// Ported to C++ by Jasper Bedaux 2003/1/1 (see http://www.bedaux.net/mtrand/).
// The generators returning floating point numbers are based on
// a version by Isaku Wada, 2002/01/09
// Static shared data converted to per-instance, 2008-11-13 by JSP.
//
// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. The names of its contributors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Any feedback is very welcome.
// http://www.math.keio.ac.jp/matumoto/emt.html
// email: matumoto@math.keio.ac.jp
//
// Feedback about the C++ port should be sent to Jasper Bedaux,
// see http://www.bedaux.net/mtrand/ for e-mail address and info.
//-------------------------------------------------------------------------
// History:
// - 28:7:2005: File created and minor changes by Marco Corbetta
//
//*************************************************************************/
// Modifications copyright Amazon.com, Inc. or its affiliates
#ifndef CRYINCLUDE_CRYCOMMON_MTPSEUDORANDOM_H
#define CRYINCLUDE_CRYCOMMON_MTPSEUDORANDOM_H
#pragma once
#include <CryRandomInternal.h>
//////////////////////////////////////////////////////////////////////////
class CMTRand_int32
{
// Mersenne Twister random number generator
public:
// default constructor
CMTRand_int32() { seed(5489UL); }
// constructor with 32 bit int as seed
CMTRand_int32(uint32 seed_value) { seed(seed_value); }
// constructor with array of 32 bit integers as seed
CMTRand_int32(const uint32* array, int size) { seed(array, size); }
// seeds with 32 bit integer
void seed(uint32 seed_value);
// seeds with array
void seed(const uint32*, int size);
// overloaded operator() to make this a generator (functor)
//uint32 operator()() { return rand_int32(); }
~CMTRand_int32() {}
// Functions with PascalCase names were added for
// interchangeability with CRndGen (see LCGRandom.h).
void Seed(uint32 seed_value)
{
seed(seed_value);
}
uint32 GenerateUint32()
{
return rand_int32();
}
uint64 GenerateUint64()
{
const uint32 a = GenerateUint32();
const uint32 b = GenerateUint32();
return ((uint64)b << 32) | (uint64)a;
}
float GenerateFloat()
{
return (float)GenerateUint32() * (1.0f / 4294967295.0f);
}
// Ranged function returns random value within the *inclusive* range
// between minValue and maxValue.
// Any orderings work correctly: minValue <= maxValue and
// minValue >= minValue.
template <class T>
T GetRandom(const T minValue, const T maxValue)
{
return CryRandom_Internal::BoundedRandom<CMTRand_int32, T>::Get(*this, minValue, maxValue);
}
// Vector (Vec2, Vec3, Vec4) ranged function returns vector with
// every component within the *inclusive* ranges between minValue.component
// and maxValue.component.
// All orderings work correctly: minValue.component <= maxValue.component and
// minValue.component >= maxValue.component.
template <class T>
T GetRandomComponentwise(const T& minValue, const T& maxValue)
{
return CryRandom_Internal::BoundedRandomComponentwise<CMTRand_int32, T>::Get(*this, minValue, maxValue);
}
// The function returns a random unit vector (Vec2, Vec3, Vec4).
template <class T>
T GetRandomUnitVector()
{
return CryRandom_Internal::GetRandomUnitVector<CMTRand_int32, T>(*this);
}
protected: // used by derived classes, otherwise not accessible; use the ()-operator
// generates 32 bit random int
uint32 rand_int32()
{
if (p >= n) gen_state(); // new m_nState vector needed
// gen_state() is split off to be non-inline, because it is only called once
// in every 624 calls and otherwise irand() would become too big to get inlined
uint32 x = m_nState[p++];
x ^= (x >> 11);
x ^= (x << 7) & 0x9D2C5680UL;
x ^= (x << 15) & 0xEFC60000UL;
return x ^ (x >> 18);
}
private:
static const int n = 624, m = 397; // compile time constants
// the variables below are static (no duplicates can exist)
uint32 m_nState[n+1]; // m_nState vector array
int p; // position in m_nState array
// private functions used to generate the pseudo random numbers
uint32 twiddle(uint32 u, uint32 v)
{
return (((u & 0x80000000UL) | (v & 0x7FFFFFFFUL)) >> 1)
^ ((v & 1UL) ? 0x9908B0DFUL : 0x0UL);
}
void gen_state(); // generate new m_nState
// make copy constructor and assignment operator unavailable, they don't make sense
CMTRand_int32(const CMTRand_int32&); // copy constructor not defined
void operator=(const CMTRand_int32&); // assignment operator not defined
};
#endif // CRYINCLUDE_CRYCOMMON_MTPSEUDORANDOM_H

@ -17,7 +17,6 @@
#include "BaseTypes.h"
#include "LCGRandom.h"
#include "MTPseudoRandom.h"
namespace CryRandom_Internal
{

@ -69,7 +69,6 @@ set(FILES
CryRandomInternal.h
Random.h
LCGRandom.h
MTPseudoRandom.cpp
CryTypeInfo.cpp
BaseTypes.h
CompileTimeAssert.h
@ -102,7 +101,6 @@ set(FILES
LegacyAllocator.h
MetaUtils.h
MiniQueue.h
MTPseudoRandom.h
MultiThread.h
MultiThread_Containers.h
NullAudioSystem.h

@ -321,7 +321,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
{
if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName))
{
// level folder contain pak files like 'level.pak'
// level folder contain pak files like 'level.pak'
// which we only want to load during level loading.
continue;
}
@ -352,7 +352,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
PopulateLevels(search, folder, pPak, modFolder, false);
// Load levels outside of the bundles to maintain backward compatibility.
PopulateLevels(search, folder, pPak, modFolder, true);
}
void CLevelSystem::PopulateLevels(
@ -974,7 +974,7 @@ void CLevelSystem::UnloadLevel()
m_lastLevelName.clear();
SAFE_RELEASE(m_pCurrentLevel);
// Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed).
// Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event).
EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect);

@ -9,8 +9,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
cmake_minimum_required(VERSION 3.0)
ly_add_target(
NAME AzAutoGen HEADERONLY
NAMESPACE AZ

@ -292,8 +292,13 @@ namespace AZ
const typename VecType::FloatType cmp2 = VecType::AndNot(cmp0, cmp1);
// -1/x
// this step is calculated for all values of x, but only used if x > Sqrt(2) + 1
// in order to avoid a division by zero, detect if xabs is zero here and replace it with an arbitrary value
// if xabs does equal zero, the value here doesn't matter because the result will be thrown away
typename VecType::FloatType xabsSafe =
VecType::Add(xabs, VecType::And(VecType::CmpEq(xabs, VecType::ZeroFloat()), FastLoadConstant<VecType>(Simd::g_vec1111)));
const typename VecType::FloatType y0 = VecType::And(cmp0, FastLoadConstant<VecType>(Simd::g_HalfPi));
typename VecType::FloatType x0 = VecType::Div(FastLoadConstant<VecType>(Simd::g_vec1111), xabs);
typename VecType::FloatType x0 = VecType::Div(FastLoadConstant<VecType>(Simd::g_vec1111), xabsSafe);
x0 = VecType::Xor(x0, VecType::CastToFloat(FastLoadConstant<VecType>(Simd::g_negateMask)));
const typename VecType::FloatType y1 = VecType::And(cmp2, FastLoadConstant<VecType>(Simd::g_QuarterPi));
@ -368,8 +373,12 @@ namespace AZ
typename VecType::FloatType offset = VecType::And(x_lt_0, offset1);
// the result of this part of the computation is thrown away if x equals 0,
// but if x does equal 0, it will cause a division by zero
// so replace zero by an arbitrary value here in that case
typename VecType::FloatType xSafe = VecType::Add(x, VecType::And(x_eq_0, FastLoadConstant<VecType>(Simd::g_vec1111)));
const typename VecType::FloatType atan_mask = VecType::Not(VecType::Or(x_eq_0, y_eq_0));
const typename VecType::FloatType atan_arg = VecType::Div(y, x);
const typename VecType::FloatType atan_arg = VecType::Div(y, xSafe);
typename VecType::FloatType atan_result = VecType::Atan(atan_arg);
atan_result = VecType::Add(atan_result, offset);
atan_result = VecType::AndNot(pio2_mask, atan_result);

@ -471,6 +471,7 @@ namespace AZ
AZ_MATH_INLINE Vec2::FloatType Vec2::Reciprocal(FloatArgType value)
{
value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f);
return Sse::Reciprocal(value);
}
@ -513,6 +514,7 @@ namespace AZ
AZ_MATH_INLINE Vec2::FloatType Vec2::SqrtInv(FloatArgType value)
{
value = Sse::ReplaceFourth(Sse::ReplaceThird(value, 1.0f), 1.0f);
return Sse::SqrtInv(value);
}

@ -507,6 +507,7 @@ namespace AZ
AZ_MATH_INLINE Vec3::FloatType Vec3::Reciprocal(FloatArgType value)
{
value = Sse::ReplaceFourth(value, 1.0f);
return Sse::Reciprocal(value);
}
@ -549,6 +550,7 @@ namespace AZ
AZ_MATH_INLINE Vec3::FloatType Vec3::SqrtInv(FloatArgType value)
{
value = Sse::ReplaceFourth(value, 1.0f);
return Sse::SqrtInv(value);
}

@ -175,4 +175,11 @@ namespace AZ::Utils
path /= ".o3de";
return path.Native();
}
AZ::IO::FixedMaxPathString GetO3deLogsDirectory()
{
AZ::IO::FixedMaxPath path = GetO3deManifestDirectory();
path /= "Logs";
return path.Native();
}
}

@ -97,6 +97,9 @@ namespace AZ
//! Retrieves the full path where the manifest file lives, i.e. "<userhome>/.o3de/o3de_manifest.json"
AZ::IO::FixedMaxPathString GetEngineManifestPath();
//! Retrieves the full directory to the O3DE logs directory, i.e. "<userhome>/.o3de/Logs"
AZ::IO::FixedMaxPathString GetO3deLogsDirectory();
//! Retrieves the App root path to use on the current platform
//! If the optional is not engaged the AppRootPath should be calculated based
//! on the location of the bootstrap.cfg file

@ -26,6 +26,7 @@ namespace AZStd
using std::exp2;
using std::floor;
using std::fmod;
using std::pow;
using std::round;
using std::sin;
using std::sqrt;

@ -1555,7 +1555,9 @@ namespace UnitTest
}
}
TEST_F(Components, EntityIdGeneration)
// Temporary disabled. This will be re-enabled in the short term upon completion of SPEC-7384 and
// fixed in the long term upon completion of SPEC-4849
TEST_F(Components, DISABLED_EntityIdGeneration)
{
// Generate 1 million ids across 100 threads, and ensure that none collide
AZStd::concurrent_unordered_set<AZ::EntityId> entityIds;

@ -0,0 +1,36 @@
/*
* 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 <AzFramework/Physics/Common/PhysicsJoint.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/PhysicsScene.h>
#include <AzFramework/Physics/PhysicsSystem.h>
namespace AzPhysics
{
AZ_CLASS_ALLOCATOR_IMPL(Joint, AZ::SystemAllocator, 0);
void Joint::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azdynamic_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AzPhysics::Joint>()
->Version(1)
->Field("SceneOwner", &Joint::m_sceneOwner)
->Field("JointHandle", &Joint::m_jointHandle)
;
}
}
}

@ -0,0 +1,143 @@
/*
* 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 <AzCore/Math/Aabb.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Physics/Common/PhysicsSimulatedBodyEvents.h>
#include <AzFramework/Physics/Common/PhysicsSceneQueries.h>
#include <AzFramework/Physics/Common/PhysicsTypes.h>
namespace AZ
{
class ReflectContext;
}
namespace AzPhysics
{
struct JointConfiguration;
//! Base class for all Joints in Physics.
struct Joint
{
AZ_CLASS_ALLOCATOR_DECL;
AZ_RTTI(AzPhysics::Joint, "{1EEC9382-3434-4866-9B18-E93F151A6F59}");
static void Reflect(AZ::ReflectContext* context);
virtual ~Joint() = default;
//! The current Scene the joint is contained.
SceneHandle m_sceneOwner = AzPhysics::InvalidSceneHandle;
//! The handle to this joint.
JointHandle m_jointHandle = AzPhysics::InvalidJointHandle;
//! Helper functions for setting user data.
//! @param userData Can be a pointer to any type as internally will be cast to a void*. Object lifetime not managed by the Joint.
template<typename T>
void SetUserData(T* userData)
{
m_customUserData = static_cast<void*>(userData);
}
//! Helper functions for getting the set user data.
//! @return Will return a void* to the user data set.
void* GetUserData()
{
return m_customUserData;
}
virtual AZ::Crc32 GetNativeType() const = 0;
virtual void* GetNativePointer() const = 0;
virtual AzPhysics::SimulatedBodyHandle GetParentBodyHandle() const = 0;
virtual AzPhysics::SimulatedBodyHandle GetChildBodyHandle() const = 0;
virtual void SetParentBody(AzPhysics::SimulatedBodyHandle parentBody) = 0;
virtual void SetChildBody(AzPhysics::SimulatedBodyHandle childBody) = 0;
virtual void GenerateJointLimitVisualizationData(
[[ maybe_unused ]] float scale,
[[ maybe_unused ]] AZ::u32 angularSubdivisions,
[[ maybe_unused ]] AZ::u32 radialSubdivisions,
[[ maybe_unused ]] AZStd::vector<AZ::Vector3>& vertexBufferOut,
[[ maybe_unused ]] AZStd::vector<AZ::u32>& indexBufferOut,
[[ maybe_unused ]] AZStd::vector<AZ::Vector3>& lineBufferOut,
[[ maybe_unused ]] AZStd::vector<bool>& lineValidityBufferOut) { }
private:
void* m_customUserData = nullptr;
};
//! Alias for a list of non owning weak pointers to Joint objects.
using JointList = AZStd::vector<Joint*>;
//! Interface to access Joint utilities and helper functions
class JointHelpersInterface
{
public:
AZ_RTTI(AzPhysics::JointHelpersInterface, "{A511C64D-C8A5-4E8F-9C69-8DC5EFAD0C4C}");
JointHelpersInterface() = default;
virtual ~JointHelpersInterface() = default;
AZ_DISABLE_COPY_MOVE(JointHelpersInterface);
//! Returns a list of supported Joint types
virtual const AZStd::vector<AZ::TypeId> GetSupportedJointTypeIds() const = 0;
//! Returns a TypeID if the request joint type is supported.
//! If the Physics backend supports this joint type JointHelpersInterface::GetSupportedJointTypeId will return a AZ::TypeId.
virtual AZStd::optional<const AZ::TypeId> GetSupportedJointTypeId(JointType typeEnum) const = 0;
//! Computes parameters such as joint limit local rotations to give the desired initial joint limit orientation.
//! @param jointLimitTypeId The type ID used to identify the particular kind of joint limit configuration to be created.
//! @param parentWorldRotation The rotation in world space of the parent world body associated with the joint.
//! @param childWorldRotation The rotation in world space of the child world body associated with the joint.
//! @param axis Axis used to define the centre for limiting angular degrees of freedom.
//! @param exampleLocalRotations A vector (which may be empty) containing example valid rotations in the local space
//! of the child world body relative to the parent world body, which may optionally be used to help estimate the extents
//! of the joint limit.
virtual AZStd::unique_ptr<JointConfiguration> ComputeInitialJointLimitConfiguration(
const AZ::TypeId& jointLimitTypeId,
const AZ::Quaternion& parentWorldRotation,
const AZ::Quaternion& childWorldRotation,
const AZ::Vector3& axis,
const AZStd::vector<AZ::Quaternion>& exampleLocalRotations) = 0;
/// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions.
/// @param configuration The joint configuration to generate visualization data for.
/// @param parentRotation The rotation of the joint's parent body (in the same frame as childRotation).
/// @param childRotation The rotation of the joint's child body (in the same frame as parentRotation).
/// @param scale Scale factor for the output display data.
/// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation).
/// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation).
/// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed.
/// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed.
/// @param[out] lineBufferOut Used to define lines to be displayed.
/// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit.
virtual void GenerateJointLimitVisualizationData(
const JointConfiguration& configuration,
const AZ::Quaternion& parentRotation,
const AZ::Quaternion& childRotation,
float scale,
AZ::u32 angularSubdivisions,
AZ::u32 radialSubdivisions,
AZStd::vector<AZ::Vector3>& vertexBufferOut,
AZStd::vector<AZ::u32>& indexBufferOut,
AZStd::vector<AZ::Vector3>& lineBufferOut,
AZStd::vector<bool>& lineValidityBufferOut) = 0;
};
}

@ -51,8 +51,10 @@ namespace AzPhysics
using SceneIndex = AZ::s8;
using SimulatedBodyIndex = AZ::s32;
using JointIndex = AZ::s32;
static_assert(std::is_signed<SceneIndex>::value
&& std::is_signed<SimulatedBodyIndex>::value, "SceneIndex and SimulatedBodyIndex must be signed integers.");
&& std::is_signed<SimulatedBodyIndex>::value
&& std::is_signed<JointIndex>::value, "SceneIndex, SimulatedBodyIndex and JointIndex must be signed integers.");
//! A handle to a Scene within the physics simulation.
@ -69,12 +71,27 @@ namespace AzPhysics
static constexpr SimulatedBodyHandle InvalidSimulatedBodyHandle = { AZ::Crc32(), -1 };
using SimulatedBodyHandleList = AZStd::vector<SimulatedBodyHandle>;
//! A handle to a Joint within a physics scene.
//! A JointHandle is a tuple of a Crc of the scene's name and the index in the Joint list.
using JointHandle = AZStd::tuple<AZ::Crc32, JointIndex>;
static constexpr JointHandle InvalidJointHandle = { AZ::Crc32(), -1 };
//! Helper used for pairing the ShapeConfiguration and ColliderConfiguration together which is used when creating a Simulated Body.
using ShapeColliderPair = AZStd::pair<
AZStd::shared_ptr<Physics::ColliderConfiguration>,
AZStd::shared_ptr<Physics::ShapeConfiguration>>;
using ShapeColliderPairList = AZStd::vector<ShapeColliderPair>;
//! Joint types are used to request for AZ::TypeId with the JointHelpersInterface::GetSupportedJointTypeId.
//! If the Physics backend supports this joint type JointHelpersInterface::GetSupportedJointTypeId will return a AZ::TypeId.
enum class JointType
{
D6Joint,
FixedJoint,
BallJoint,
HingeJoint
};
//! Flags used to specifying which properties of a body to compute.
enum class MassComputeFlags : AZ::u8
{

@ -0,0 +1,37 @@
/*
* 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 <AzFramework/Physics/Configuration/JointConfiguration.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzPhysics
{
AZ_CLASS_ALLOCATOR_IMPL(JointConfiguration, AZ::SystemAllocator, 0);
void JointConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<JointConfiguration>()
->Version(1)
->Field("Name", &JointConfiguration::m_debugName)
->Field("ParentLocalRotation", &JointConfiguration::m_parentLocalRotation)
->Field("ParentLocalPosition", &JointConfiguration::m_parentLocalPosition)
->Field("ChildLocalRotation", &JointConfiguration::m_childLocalRotation)
->Field("ChildLocalPosition", &JointConfiguration::m_childLocalPosition)
->Field("StartSimulationEnabled", &JointConfiguration::m_startSimulationEnabled)
;
}
}
}

@ -0,0 +1,51 @@
/*
* 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 <AzCore/Component/EntityId.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/vector.h>
namespace AZ
{
class ReflectContext;
}
namespace AzPhysics
{
//! Base Class of all Physics Joints that will be simulated.
struct JointConfiguration
{
AZ_CLASS_ALLOCATOR_DECL;
AZ_RTTI(AzPhysics::JointConfiguration, "{DF91D39A-4901-48C4-9159-93FD2ACA5252}");
static void Reflect(AZ::ReflectContext* context);
JointConfiguration() = default;
virtual ~JointConfiguration() = default;
// Entity/object association.
void* m_customUserData = nullptr;
// Basic initial settings.
AZ::Quaternion m_parentLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Parent joint frame relative to parent body.
AZ::Vector3 m_parentLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to parent body.
AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body.
AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body.
bool m_startSimulationEnabled = true;
// For debugging/tracking purposes only.
AZStd::string m_debugName;
};
}

@ -1,56 +0,0 @@
/*
* 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 <AzFramework/Physics/Joint.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
namespace Physics
{
const char* JointLimitConfiguration::GetTypeName()
{
return "Base Joint";
}
void JointLimitConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<JointLimitConfiguration>()
->Version(1)
->Field("ParentLocalRotation", &JointLimitConfiguration::m_parentLocalRotation)
->Field("ParentLocalPosition", &JointLimitConfiguration::m_parentLocalPosition)
->Field("ChildLocalRotation", &JointLimitConfiguration::m_childLocalRotation)
->Field("ChildLocalPosition", &JointLimitConfiguration::m_childLocalPosition)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<JointLimitConfiguration>(
"Joint Configuration", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_parentLocalRotation,
"Parent local rotation", "The rotation of the parent joint frame relative to the parent body")
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_parentLocalPosition,
"Parent local position", "The position of the joint in the frame of the parent body")
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_childLocalRotation,
"Child local rotation", "The rotation of the child joint frame relative to the child body")
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_childLocalPosition,
"Child local position", "The position of the joint in the frame of the child body")
;
}
}
}
} // namespace Physics

@ -1,75 +0,0 @@
/*
* 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 <AzCore/Math/Transform.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzPhysics
{
struct SimulatedBody;
}
namespace Physics
{
class JointLimitConfiguration
{
public:
AZ_CLASS_ALLOCATOR(JointLimitConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(JointLimitConfiguration, "{C9B70C4D-22D7-45AB-9B0A-30A4ED5E42DB}");
static void Reflect(AZ::ReflectContext* context);
JointLimitConfiguration() = default;
JointLimitConfiguration(const JointLimitConfiguration&) = default;
virtual ~JointLimitConfiguration() = default;
virtual const char* GetTypeName();
AZ::Quaternion m_parentLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Parent joint frame relative to parent body.
AZ::Vector3 m_parentLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to parent body.
AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body.
AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body.
};
class Joint
{
public:
AZ_CLASS_ALLOCATOR(Joint, AZ::SystemAllocator, 0);
AZ_RTTI(Joint, "{405F517C-E986-4ACB-9606-D5D080DDE987}");
virtual AzPhysics::SimulatedBody* GetParentBody() const = 0;
virtual AzPhysics::SimulatedBody* GetChildBody() const = 0;
virtual void SetParentBody(AzPhysics::SimulatedBody* parentBody) = 0;
virtual void SetChildBody(AzPhysics::SimulatedBody* childBody) = 0;
virtual const AZStd::string& GetName() const = 0;
virtual void SetName(const AZStd::string& name) = 0;
virtual const AZ::Crc32 GetNativeType() const = 0;
virtual void* GetNativePointer() = 0;
/// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions.
/// @param scale Scale factor for the output display data.
/// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation).
/// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation).
/// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed.
/// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed.
/// @param[out] lineBufferOut Used to define lines to be displayed.
/// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit.
virtual void GenerateJointLimitVisualizationData(
float scale,
AZ::u32 angularSubdivisions,
AZ::u32 radialSubdivisions,
AZStd::vector<AZ::Vector3>& vertexBufferOut,
AZStd::vector<AZ::u32>& indexBufferOut,
AZStd::vector<AZ::Vector3>& lineBufferOut,
AZStd::vector<bool>& lineValidityBufferOut) = 0;
};
} // namespace Physics

@ -16,6 +16,8 @@
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
#include <AzFramework/Physics/Common/PhysicsSceneQueries.h>
#include <AzFramework/Physics/Common/PhysicsTypes.h>
#include <AzFramework/Physics/Common/PhysicsJoint.h>
#include <AzFramework/Physics/Configuration/JointConfiguration.h>
#include <AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h>
namespace AzPhysics
@ -103,6 +105,26 @@ namespace AzPhysics
virtual void EnableSimulationOfBody(SceneHandle sceneHandle, SimulatedBodyHandle bodyHandle) = 0;
virtual void DisableSimulationOfBody(SceneHandle sceneHandle, SimulatedBodyHandle bodyHandle) = 0;
//! Add a joint to the Scene.
//! @param sceneHandle A handle to the scene to add / remove the joint.
//! @param jointConfig The config of the joint.
//! @param parentBody The parent body of the joint.
//! @param childBody The child body of the joint
//! @return Returns a handle to the created joint. Will return AzPhyiscs::InvalidJointHandle if it fails.
virtual JointHandle AddJoint(SceneHandle sceneHandle, const JointConfiguration* jointConfig,
SimulatedBodyHandle parentBody, SimulatedBodyHandle childBody) = 0;
//! Get the Raw pointer to the requested joint.
//! @param sceneHandle A handle to the scene to get the simulated bodies from.
//! @param jointHandle A handle to the joint to retrieve the raw pointer.
//! @return A raw pointer to the Joint body. If the either handle is invalid this will return null.
virtual Joint* GetJointFromHandle(SceneHandle sceneHandle, JointHandle jointHandle) = 0;
//! Remove a joint from the Scene.
//! @param sceneHandle A handle to the scene to add / remove the joint.
//! @param jointHandle A handle to the joint being removed.
virtual void RemoveJoint(SceneHandle sceneHandle, JointHandle jointHandle) = 0;
//! Make a blocking query into the scene.
//! @param sceneHandle A handle to the scene to make the scene query with.
//! @param request The request to make. Should be one of RayCastRequest || ShapeCastRequest || OverlapRequest
@ -299,6 +321,23 @@ namespace AzPhysics
virtual void EnableSimulationOfBody(SimulatedBodyHandle bodyHandle) = 0;
virtual void DisableSimulationOfBody(SimulatedBodyHandle bodyHandle) = 0;
//! Add a joint to the Scene.
//! @param jointConfig The config of the joint.
//! @param parentBody The parent body of the joint.
//! @param childBody The child body of the joint
//! @return Returns a handle to the created joint. Will return AzPhyiscs::InvalidJointHandle if it fails.
virtual JointHandle AddJoint(const JointConfiguration* jointConfig,
SimulatedBodyHandle parentBody, SimulatedBodyHandle childBody) = 0;
//! Get the Raw pointer to the requested joint.
//! @param jointHandle A handle to the joint to retrieve the raw pointer.
//! @return A raw pointer to the Joint body. If the either handle is invalid this will return null.
virtual Joint* GetJointFromHandle(JointHandle jointHandle) = 0;
//! Remove a joint from the Scene.
//! @param jointHandle A handle to the joint being removed.
virtual void RemoveJoint(JointHandle jointHandle) = 0;
//! Make a blocking query into the scene.
//! @param request The request to make. Should be one of RayCastRequest || ShapeCastRequest || OverlapRequest
//! @return Returns a structure that contains a list of Hits. Depending on flags set in the request, this may only contain 1 result.

@ -36,8 +36,8 @@ namespace Physics
if (serializeContext)
{
serializeContext->Class<RagdollNodeConfiguration, RigidBodyConfiguration>()
->Version(4, &ClassConverters::RagdollNodeConfigConverter)
->Field("JointLimit", &RagdollNodeConfiguration::m_jointLimit)
->Version(5, &ClassConverters::RagdollNodeConfigConverter)
->Field("JointConfig", &RagdollNodeConfiguration::m_jointConfig)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();

@ -17,10 +17,11 @@
#include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/SimulatedBodies/RigidBody.h>
#include <AzFramework/Physics/RagdollPhysicsBus.h>
#include <AzFramework/Physics/Joint.h>
#include <AzFramework/Physics/Common/PhysicsSimulatedBody.h>
#include <AzFramework/Physics/Common/PhysicsJoint.h>
#include <AzFramework/Physics/Configuration/RigidBodyConfiguration.h>
#include <AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h>
#include <AzFramework/Physics/Configuration/JointConfiguration.h>
namespace Physics
{
@ -37,7 +38,7 @@ namespace Physics
RagdollNodeConfiguration();
RagdollNodeConfiguration(const RagdollNodeConfiguration& settings) = default;
AZStd::shared_ptr<JointLimitConfiguration> m_jointLimit;
AZStd::shared_ptr<AzPhysics::JointConfiguration> m_jointConfig;
};
class RagdollConfiguration
@ -73,7 +74,7 @@ namespace Physics
virtual AzPhysics::RigidBody& GetRigidBody() = 0;
virtual ~RagdollNode() = default;
virtual const AZStd::shared_ptr<Physics::Joint>& GetJoint() const = 0;
virtual AzPhysics::Joint* GetJoint() = 0;
virtual bool IsSimulating() const = 0;
};

@ -26,23 +26,15 @@ namespace AZ
namespace AzPhysics
{
struct SimulatedBody;
struct RigidBodyConfiguration;
struct RigidBody;
}
namespace Physics
{
class WorldBody;
class Shape;
class Material;
class MaterialSelection;
class MaterialConfiguration;
class ColliderConfiguration;
class ShapeConfiguration;
class JointLimitConfiguration;
class Joint;
class CharacterConfiguration;
class Character;
/// Represents a debug vertex (position & color).
struct DebugDrawVertex
@ -148,51 +140,6 @@ namespace Physics
/// @param nativeMeshObject Pointer to the mesh object.
virtual void ReleaseNativeMeshObject(void* nativeMeshObject) = 0;
//////////////////////////////////////////////////////////////////////////
//// Joints
virtual AZStd::vector<AZ::TypeId> GetSupportedJointTypes() = 0;
virtual AZStd::shared_ptr<JointLimitConfiguration> CreateJointLimitConfiguration(AZ::TypeId jointType) = 0;
virtual AZStd::shared_ptr<Joint> CreateJoint(const AZStd::shared_ptr<JointLimitConfiguration>& configuration,
AzPhysics::SimulatedBody* parentBody, AzPhysics::SimulatedBody* childBody) = 0;
/// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions.
/// @param configuration The joint configuration to generate visualization data for.
/// @param parentRotation The rotation of the joint's parent body (in the same frame as childRotation).
/// @param childRotation The rotation of the joint's child body (in the same frame as parentRotation).
/// @param scale Scale factor for the output display data.
/// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation).
/// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation).
/// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed.
/// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed.
/// @param[out] lineBufferOut Used to define lines to be displayed.
/// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit.
virtual void GenerateJointLimitVisualizationData(
const JointLimitConfiguration& configuration,
const AZ::Quaternion& parentRotation,
const AZ::Quaternion& childRotation,
float scale,
AZ::u32 angularSubdivisions,
AZ::u32 radialSubdivisions,
AZStd::vector<AZ::Vector3>& vertexBufferOut,
AZStd::vector<AZ::u32>& indexBufferOut,
AZStd::vector<AZ::Vector3>& lineBufferOut,
AZStd::vector<bool>& lineValidityBufferOut) = 0;
/// Computes parameters such as joint limit local rotations to give the desired initial joint limit orientation.
/// @param jointLimitTypeId The type ID used to identify the particular kind of joint limit configuration to be created.
/// @param parentWorldRotation The rotation in world space of the parent world body associated with the joint.
/// @param childWorldRotation The rotation in world space of the child world body associated with the joint.
/// @param axis Axis used to define the centre for limiting angular degrees of freedom.
/// @param exampleLocalRotations A vector (which may be empty) containing example valid rotations in the local space
/// of the child world body relative to the parent world body, which may optionally be used to help estimate the extents
/// of the joint limit.
virtual AZStd::unique_ptr<JointLimitConfiguration> ComputeInitialJointLimitConfiguration(
const AZ::TypeId& jointLimitTypeId,
const AZ::Quaternion& parentWorldRotation,
const AZ::Quaternion& childWorldRotation,
const AZ::Vector3& axis,
const AZStd::vector<AZ::Quaternion>& exampleLocalRotations) = 0;
//////////////////////////////////////////////////////////////////////////
//// Cooking

@ -34,6 +34,7 @@
#include <AzFramework/Physics/Configuration/SceneConfiguration.h>
#include <AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h>
#include <AzFramework/Physics/SimulatedBodies/RigidBody.h>
#include <AzFramework/Physics/Common/PhysicsJoint.h>
namespace Physics
{
@ -121,9 +122,9 @@ namespace Physics
DefaultMaterialConfiguration::Reflect(context);
MaterialLibraryAsset::Reflect(context);
MaterialInfoReflectionWrapper::Reflect(context);
JointLimitConfiguration::Reflect(context);
AzPhysics::SimulatedBodyConfiguration::Reflect(context);
AzPhysics::RigidBodyConfiguration::Reflect(context);
AzPhysics::JointConfiguration::Reflect(context);
RagdollNodeConfiguration::Reflect(context);
RagdollConfiguration::Reflect(context);
CharacterColliderNodeConfiguration::Reflect(context);
@ -131,6 +132,7 @@ namespace Physics
AnimationConfiguration::Reflect(context);
CharacterConfiguration::Reflect(context);
AzPhysics::SimulatedBody::Reflect(context);
AzPhysics::Joint::Reflect(context);
ReflectSimulatedBodyComponentRequestsBus(context);
CollisionFilteringRequests::Reflect(context);
AzPhysics::SceneQuery::ReflectSceneQueryObjects(context);

@ -693,11 +693,11 @@ namespace AzFramework
// set the __index so we can read values in case we change the script
// after we export the component
lua_pushliteral(lua, "__index");
lua_pushcclosure(lua, &Internal::Properties__Index, 1);
lua_pushcclosure(lua, &Internal::Properties__Index, 0);
lua_rawset(lua, -3);
lua_pushliteral(lua, "__newindex");
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1);
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0);
lua_rawset(lua, -3);
}
lua_pop(lua, 1); // pop the properties table (or the nil value)
@ -900,11 +900,11 @@ namespace AzFramework
// Ensure that this instance of Properties table has the proper __index and __newIndex metamethods.
lua_newtable(lua); // This new table will become the Properties instance metatable. Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {}
lua_pushliteral(lua, "__index"); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index
lua_pushcclosure(lua, &Internal::Properties__Index, 1); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function
lua_pushcclosure(lua, &Internal::Properties__Index, 0); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {} __index function
lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index}
lua_pushliteral(lua, "__newindex");
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 1);
lua_pushcclosure(lua, &Internal::Properties__NewIndex, 0);
lua_rawset(lua, -3); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {} {__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex}
lua_setmetatable(lua, -2); // Stack: ScriptRootTable PropertiesTable EntityTable "Properties" {Meta{__index=Internal::Properties__Index __newindex=Internal::Properties__NewIndex} }

@ -204,6 +204,8 @@ set(FILES
Physics/Collision/CollisionLayers.cpp
Physics/Collision/CollisionGroups.h
Physics/Collision/CollisionGroups.cpp
Physics/Common/PhysicsJoint.h
Physics/Common/PhysicsJoint.cpp
Physics/Common/PhysicsSceneQueries.h
Physics/Common/PhysicsSceneQueries.cpp
Physics/Common/PhysicsEvents.h
@ -215,6 +217,8 @@ set(FILES
Physics/Common/PhysicsSimulatedBodyEvents.cpp
Physics/Common/PhysicsTypes.h
Physics/Components/SimulatedBodyComponentBus.h
Physics/Configuration/JointConfiguration.h
Physics/Configuration/JointConfiguration.cpp
Physics/Configuration/CollisionConfiguration.h
Physics/Configuration/CollisionConfiguration.cpp
Physics/Configuration/RigidBodyConfiguration.h
@ -259,8 +263,6 @@ set(FILES
Physics/Ragdoll.h
Physics/Utils.h
Physics/Utils.cpp
Physics/Joint.h
Physics/Joint.cpp
Physics/ClassConverters.cpp
Physics/ClassConverters.h
Physics/MaterialBus.h

@ -13,6 +13,7 @@
#pragma once
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>

@ -493,6 +493,29 @@ namespace AzQtComponents
}
}
break;
case CE_MenuItem:
{
const QMenu* menu = qobject_cast<const QMenu*>(widget);
QAction* action = menu->activeAction();
if (action)
{
QMenu* subMenu = action->menu();
if (subMenu)
{
QVariant noHover = subMenu->property("noHover");
if (noHover.isValid() && noHover.toBool())
{
// First draw as standard to get the correct hover background for the complete control.
QProxyStyle::drawControl(element, option, painter, widget);
// Now draw the icon as non-hovered so control behaves as designed.
QStyleOptionMenuItem myOpt = *qstyleoption_cast<const QStyleOptionMenuItem*>(option);
myOpt.state &= ~QStyle::State_Selected;
return QProxyStyle::drawControl(element, &myOpt, painter, widget);
}
}
}
}
break;
}
return QProxyStyle::drawControl(element, option, painter, widget);

@ -370,6 +370,7 @@
<file>img/UI20/toolbar/Redo.svg</file>
<file>img/UI20/toolbar/remove_link.svg</file>
<file>img/UI20/toolbar/Reset_physics_state.svg</file>
<file>img/UI20/toolbar/RestoreMode.svg</file>
<file>img/UI20/toolbar/Save.svg</file>
<file>img/UI20/toolbar/Scale.svg</file>
<file>img/UI20/toolbar/Select.svg</file>
@ -377,9 +378,10 @@
<file>img/UI20/toolbar/Select_terrain.svg</file>
<file>img/UI20/toolbar/Simulate_Physics.svg</file>
<file>img/UI20/toolbar/Simulate_Physics_on_selected_objects.svg</file>
<file>img/UI20/toolbar/SketchMode.svg</file>
<file>img/UI20/toolbar/Terrain.svg</file>
<file>img/UI20/toolbar/Terrain_Texture.svg</file>
<file>img/UI20/toolbar/Translate.svg</file>
<file>img/UI20/toolbar/Rotate.svg</file>
<file>img/UI20/toolbar/undo.svg</file>
<file>img/UI20/toolbar/Unlocked.svg</file>
<file>img/UI20/toolbar/Vertex_snapping.svg</file>

@ -1,4 +1,4 @@
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.0708 0.87087L11.0708 0.87074C10.4286 0.371024 9.61107 0.13517 8.79265 0.213464C7.97422 0.291757 7.21961 0.678004 6.6897 1.28985L5.76756 2.36366C5.63378 2.51944 5.56909 2.72055 5.58771 2.92273C5.60634 3.12492 5.70675 3.31163 5.86686 3.44178C6.02698 3.57194 6.23368 3.63488 6.44149 3.61676C6.6493 3.59864 6.8412 3.50094 6.97498 3.34516L7.89716 2.27122C8.03184 2.11488 8.19736 1.98639 8.38395 1.89334C8.57053 1.8003 8.7744 1.74459 8.98349 1.7295C9.19258 1.71442 9.40266 1.74027 9.60131 1.80552C9.79996 1.87078 9.98315 1.97411 10.1401 2.10942C10.4422 2.38256 10.6244 2.75851 10.6488 3.15904C10.6732 3.55957 10.5379 3.95383 10.2711 4.25978L8.35381 6.49309L8.33616 6.51209C8.23375 6.62767 8.11374 6.72729 7.98031 6.80749C7.67189 6.99364 7.30661 7.06983 6.94686 7.02305C6.58711 6.97626 6.25522 6.8094 6.00789 6.55097C5.86556 6.40288 5.66865 6.31578 5.46039 6.30879C5.25213 6.30179 5.04952 6.37546 4.89703 6.51365C4.74453 6.65183 4.65462 6.84323 4.64701 7.04584C4.6394 7.24845 4.71472 7.44572 4.85645 7.59436C5.21222 7.96578 5.65746 8.24515 6.15198 8.40726C6.6465 8.56937 7.17473 8.60911 7.68898 8.52289C7.84983 8.49539 8.00828 8.45599 8.16298 8.40505C8.70939 8.22568 9.19408 7.90261 9.56335 7.47165L11.4758 5.24457C11.7478 4.92529 11.9523 4.55683 12.0773 4.16038C12.2024 3.76394 12.2457 3.34735 12.2046 2.93457C12.1671 2.53429 12.0475 2.1454 11.8527 1.79092C11.658 1.43644 11.3921 1.12358 11.0708 0.87087Z" fill="white"/>
<path d="M5.40958 9.14055L4.58546 10.1003C4.32342 10.4101 3.94867 10.6097 3.53919 10.6576C3.12972 10.7054 2.71706 10.5977 2.3871 10.357C2.22234 10.2309 2.08525 10.0738 1.98392 9.89526C1.8826 9.71668 1.81911 9.52014 1.79719 9.31727C1.77528 9.1144 1.79539 8.90932 1.85634 8.71414C1.91729 8.51896 2.01784 8.33765 2.15204 8.18093L4.10231 5.90975L4.11666 5.89415C4.21908 5.77861 4.3391 5.67903 4.47254 5.59887C4.75216 5.42921 5.07976 5.34988 5.4085 5.37222C5.73725 5.39457 6.05033 5.51745 6.30299 5.72329C6.3635 5.77246 6.42015 5.82595 6.47246 5.88333C6.54738 5.9658 6.63971 6.03156 6.74316 6.07611C6.84661 6.12066 6.95872 6.14295 7.07184 6.14146C7.18361 6.13992 7.29372 6.11492 7.39465 6.06817C7.49558 6.02143 7.58495 5.95403 7.65666 5.8706L7.66607 5.85958C7.78641 5.72083 7.85132 5.54454 7.8489 5.36301C7.84647 5.18148 7.77687 5.00689 7.65286 4.87124C7.35341 4.54132 6.98422 4.27825 6.57057 4.10003C6.15692 3.92182 5.70858 3.83266 5.25623 3.83866C4.80388 3.84467 4.35821 3.94569 3.94971 4.13482C3.54122 4.32395 3.17954 4.59672 2.88945 4.93446L0.944626 7.19943C0.420065 7.81564 0.163643 8.60683 0.230025 9.40433C0.296406 10.2018 0.68034 10.9426 1.29998 11.4686C1.61272 11.7312 1.97644 11.9301 2.3696 12.0535C2.76277 12.1769 3.17738 12.2223 3.5889 12.187C3.687 12.1793 3.78434 12.1672 3.88093 12.1507C4.62877 12.0232 5.30663 11.6437 5.79562 11.0786L6.617 10.1221C6.75077 9.96628 6.81547 9.76517 6.79684 9.56299C6.77822 9.3608 6.6778 9.17409 6.51769 9.04394C6.35758 8.91378 6.15088 8.85084 5.94306 8.86896C5.73525 8.88708 5.54335 8.98477 5.40957 9.14055H5.40958Z" fill="white"/>
<path d="M11.0708 0.87087L11.0708 0.87074C10.4286 0.371024 9.61107 0.13517 8.79265 0.213464C7.97422 0.291757 7.21961 0.678004 6.6897 1.28985L5.76756 2.36366C5.63378 2.51944 5.56909 2.72055 5.58771 2.92273C5.60634 3.12492 5.70675 3.31163 5.86686 3.44178C6.02698 3.57194 6.23368 3.63488 6.44149 3.61676C6.6493 3.59864 6.8412 3.50094 6.97498 3.34516L7.89716 2.27122C8.03184 2.11488 8.19736 1.98639 8.38395 1.89334C8.57053 1.8003 8.7744 1.74459 8.98349 1.7295C9.19258 1.71442 9.40266 1.74027 9.60131 1.80552C9.79996 1.87078 9.98315 1.97411 10.1401 2.10942C10.4422 2.38256 10.6244 2.75851 10.6488 3.15904C10.6732 3.55957 10.5379 3.95383 10.2711 4.25978L8.35381 6.49309L8.33616 6.51209C8.23375 6.62767 8.11374 6.72729 7.98031 6.80749C7.67189 6.99364 7.30661 7.06983 6.94686 7.02305C6.58711 6.97626 6.25522 6.8094 6.00789 6.55097C5.86556 6.40288 5.66865 6.31578 5.46039 6.30879C5.25213 6.30179 5.04952 6.37546 4.89703 6.51365C4.74453 6.65183 4.65462 6.84323 4.64701 7.04584C4.6394 7.24845 4.71472 7.44572 4.85645 7.59436C5.21222 7.96578 5.65746 8.24515 6.15198 8.40726C6.6465 8.56937 7.17473 8.60911 7.68898 8.52289C7.84983 8.49539 8.00828 8.45599 8.16298 8.40505C8.70939 8.22568 9.19408 7.90261 9.56335 7.47165L11.4758 5.24457C11.7478 4.92529 11.9523 4.55683 12.0773 4.16038C12.2024 3.76394 12.2457 3.34735 12.2046 2.93457C12.1671 2.53429 12.0475 2.1454 11.8527 1.79092C11.658 1.43644 11.3921 1.12358 11.0708 0.87087Z" fill="#3F3F3F"/>
<path d="M5.40958 9.14055L4.58546 10.1003C4.32342 10.4101 3.94867 10.6097 3.53919 10.6576C3.12972 10.7054 2.71706 10.5977 2.3871 10.357C2.22234 10.2309 2.08525 10.0738 1.98392 9.89526C1.8826 9.71668 1.81911 9.52014 1.79719 9.31727C1.77528 9.1144 1.79539 8.90932 1.85634 8.71414C1.91729 8.51896 2.01784 8.33765 2.15204 8.18093L4.10231 5.90975L4.11666 5.89415C4.21908 5.77861 4.3391 5.67903 4.47254 5.59887C4.75216 5.42921 5.07976 5.34988 5.4085 5.37222C5.73725 5.39457 6.05033 5.51745 6.30299 5.72329C6.3635 5.77246 6.42015 5.82595 6.47246 5.88333C6.54738 5.9658 6.63971 6.03156 6.74316 6.07611C6.84661 6.12066 6.95872 6.14295 7.07184 6.14146C7.18361 6.13992 7.29372 6.11492 7.39465 6.06817C7.49558 6.02143 7.58495 5.95403 7.65666 5.8706L7.66607 5.85958C7.78641 5.72083 7.85132 5.54454 7.8489 5.36301C7.84647 5.18148 7.77687 5.00689 7.65286 4.87124C7.35341 4.54132 6.98422 4.27825 6.57057 4.10003C6.15692 3.92182 5.70858 3.83266 5.25623 3.83866C4.80388 3.84467 4.35821 3.94569 3.94971 4.13482C3.54122 4.32395 3.17954 4.59672 2.88945 4.93446L0.944626 7.19943C0.420065 7.81564 0.163643 8.60683 0.230025 9.40433C0.296406 10.2018 0.68034 10.9426 1.29998 11.4686C1.61272 11.7312 1.97644 11.9301 2.3696 12.0535C2.76277 12.1769 3.17738 12.2223 3.5889 12.187C3.687 12.1793 3.78434 12.1672 3.88093 12.1507C4.62877 12.0232 5.30663 11.6437 5.79562 11.0786L6.617 10.1221C6.75077 9.96628 6.81547 9.76517 6.79684 9.56299C6.77822 9.3608 6.6778 9.17409 6.51769 9.04394C6.35758 8.91378 6.15088 8.85084 5.94306 8.86896C5.73525 8.88708 5.54335 8.98477 5.40957 9.14055H5.40958Z" fill="#3F3F3F"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -8,25 +8,27 @@
# 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.
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME})
ly_add_target(
NAME AzTest STATIC
NAMESPACE AZ
FILES_CMAKE
AzTest/aztest_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
${pal_dir}
BUILD_DEPENDENCIES
PUBLIC
3rdParty::googletest::GMock
3rdParty::googletest::GTest
3rdParty::GoogleBenchmark
AZ::AzCore
PLATFORM_INCLUDE_FILES
if(NOT LY_MONOLITHIC_GAME)
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/AzTest/Platform/${PAL_PLATFORM_NAME})
ly_add_target(
NAME AzTest STATIC
NAMESPACE AZ
FILES_CMAKE
AzTest/aztest_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
${pal_dir}
BUILD_DEPENDENCIES
PUBLIC
3rdParty::googletest::GMock
3rdParty::googletest::GTest
3rdParty::GoogleBenchmark
AZ::AzCore
PLATFORM_INCLUDE_FILES
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
)
)
endif()

@ -12,6 +12,7 @@
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzCore/Console/IConsole.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
@ -22,6 +23,9 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <QCollator>
AZ_POP_DISABLE_WARNING
AZ_CVAR(
bool, ed_useNewAssetBrowserTableView, false, nullptr, AZ::ConsoleFunctorFlags::Null,
"Use the new AssetBrowser TableView for searching assets.");
namespace AzToolsFramework
{
namespace AssetBrowser
@ -31,7 +35,11 @@ namespace AzToolsFramework
AssetBrowserFilterModel::AssetBrowserFilterModel(QObject* parent)
: QSortFilterProxyModel(parent)
{
m_showColumn.insert(AssetBrowserModel::m_column);
m_showColumn.insert(aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName));
if (ed_useNewAssetBrowserTableView)
{
m_showColumn.insert(aznumeric_cast<int>(AssetBrowserEntry::Column::Path));
}
m_collator.setNumericMode(true);
AssetBrowserComponentNotificationBus::Handler::BusConnect();
}
@ -128,28 +136,58 @@ namespace AzToolsFramework
auto compFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(m_filter);
if (compFilter)
{
auto& subFilters = compFilter->GetSubFilters();
auto it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool
const auto& subFilters = compFilter->GetSubFilters();
const auto compositeFilterIterator = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [subFilters](FilterConstType filter) -> bool
{
auto assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(filter);
const auto assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(filter);
return !assetTypeFilter.isNull();
});
if (it != subFilters.end())
if (compositeFilterIterator != subFilters.end())
{
m_assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(*it);
m_assetTypeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(*compositeFilterIterator);
}
it = AZStd::find_if(subFilters.begin(), subFilters.end(), [subFilters](FilterConstType filter) -> bool
const auto compStringFilterIter = AZStd::find_if(subFilters.cbegin(), subFilters.cend(), [](FilterConstType filter) -> bool
{
auto stringFilter = qobject_cast<QSharedPointer<const StringFilter> >(filter);
return !stringFilter.isNull();
//The real StringFilter is really a CompositeFilter with just one StringFilter in its subfilter list
//To know if it is actually a StringFilter we have to get that subfilter and check if it is a Stringfilter.
const auto stringCompositeFilter = qobject_cast<QSharedPointer<const CompositeFilter> >(filter);
bool isStringFilter = false;
if (stringCompositeFilter)
{
const auto& stringSubfilters = stringCompositeFilter->GetSubFilters();
auto canBeCasted = [](FilterConstType filt) -> bool
{
auto strFilter = qobject_cast<QSharedPointer<const StringFilter>>(filt);
return !strFilter.isNull();
};
const auto stringSubfliterConstIter = AZStd::find_if(stringSubfilters.cbegin(), stringSubfilters.cend(), canBeCasted);
//A Composite StringFilter will only have just one subfilter and nothing more.
if (stringSubfliterConstIter != stringSubfilters.end() && stringSubfilters.size() == 1)
{
isStringFilter = true;
}
}
return isStringFilter;
});
if (it != subFilters.end())
if (compStringFilterIter != subFilters.end())
{
m_stringFilter = qobject_cast<QSharedPointer<const StringFilter> >(*it);
const auto compStringFilter = qobject_cast<QSharedPointer<const CompositeFilter>>(*compStringFilterIter);
if (!compStringFilter->GetSubFilters().isEmpty() && compStringFilter->GetSubFilters()[0])
{
m_stringFilter = qobject_cast<QSharedPointer<const StringFilter>>(compStringFilter->GetSubFilters()[0]);
}
}
}
invalidateFilter();
Q_EMIT filterChanged();
emit stringFilterPopulated(!m_stringFilter.isNull());
}
void AssetBrowserFilterModel::filterUpdatedSlot()

@ -45,15 +45,15 @@ namespace AzToolsFramework
//asset type filtering
void SetFilter(FilterConstType filter);
void FilterUpdatedSlotImmediate();
const FilterConstType& GetFilter() const { return m_filter; }
//////////////////////////////////////////////////////////////////////////
// AssetBrowserComponentNotificationBus
//////////////////////////////////////////////////////////////////////////
void OnAssetBrowserComponentReady() override;
Q_SIGNALS:
void stringFilterPopulated(bool);
void filterChanged();
//////////////////////////////////////////////////////////////////////////
//QSortFilterProxyModel
protected:
@ -68,7 +68,7 @@ namespace AzToolsFramework
protected:
//set for filtering columns
//if the column is in the set the column is not filtered and is shown
AZStd::fixed_unordered_set<int, 3, static_cast<int>(AssetBrowserEntry::Column::Count)> m_showColumn;
AZStd::fixed_unordered_set<int, 3, aznumeric_cast<int>(AssetBrowserEntry::Column::Count)> m_showColumn;
bool m_alreadyRecomputingFilters = false;
//asset source name match filter
FilterConstType m_filter;

@ -27,8 +27,6 @@ namespace AzToolsFramework
{
namespace AssetBrowser
{
const int AssetBrowserModel::m_column = static_cast<int>(AssetBrowserEntry::Column::DisplayName);
AssetBrowserModel::AssetBrowserModel(QObject* parent)
: QAbstractItemModel(parent)
, m_rootEntry(nullptr)
@ -143,8 +141,9 @@ namespace AzToolsFramework
if (parent.isValid())
{
if ((parent.column() != static_cast<int>(AssetBrowserEntry::Column::DisplayName)) &&
(parent.column() != static_cast<int>(AssetBrowserEntry::Column::Name)))
if ((parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName)) &&
(parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::Name)) &&
(parent.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::Path)))
{
return 0;
}
@ -164,7 +163,7 @@ namespace AzToolsFramework
int AssetBrowserModel::columnCount(const QModelIndex& /*parent*/) const
{
return static_cast<int>(AssetBrowserEntry::Column::Count);
return aznumeric_cast<int>(AssetBrowserEntry::Column::Count);
}
QVariant AssetBrowserModel::data(const QModelIndex& index, int role) const
@ -393,7 +392,7 @@ namespace AzToolsFramework
}
int row = entry->row();
index = createIndex(row, m_column, entry);
index = createIndex(row, aznumeric_cast<int>(AssetBrowserEntry::Column::DisplayName), entry);
return true;
}

@ -91,8 +91,6 @@ namespace AzToolsFramework
static void SourceIndexesToAssetIds(const QModelIndexList& indexes, AZStd::vector<AZ::Data::AssetId>& assetIds);
static void SourceIndexesToAssetDatabaseEntries(const QModelIndexList& indexes, AZStd::vector<AssetBrowserEntry*>& entries);
const static int m_column;
private:
AZStd::shared_ptr<RootAssetBrowserEntry> m_rootEntry;
bool m_loaded;

@ -0,0 +1,136 @@
/*
* 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 <AssetBrowser/AssetBrowserFilterModel.h>
#include <AssetBrowser/AssetBrowserTableModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
namespace AzToolsFramework
{
namespace AssetBrowser
{
AssetBrowserTableModel::AssetBrowserTableModel(QObject* parent /* = nullptr */)
: QSortFilterProxyModel(parent)
{
setDynamicSortFilter(false);
}
void AssetBrowserTableModel::setSourceModel(QAbstractItemModel* sourceModel)
{
m_filterModel = qobject_cast<AssetBrowserFilterModel*>(sourceModel);
AZ_Assert(
m_filterModel,
"Error in AssetBrowserTableModel initialization, class expects source model to be an AssetBrowserFilterModel.");
QSortFilterProxyModel::setSourceModel(sourceModel);
}
QModelIndex AssetBrowserTableModel::mapToSource(const QModelIndex& proxyIndex) const
{
Q_ASSERT(!proxyIndex.isValid() || proxyIndex.model() == this);
if (!proxyIndex.isValid())
{
return QModelIndex();
}
return m_indexMap[proxyIndex.row()];
}
QVariant AssetBrowserTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
return tr(AssetBrowserEntry::m_columnNames[section]);
}
return QSortFilterProxyModel::headerData(section, orientation, role);
}
QVariant AssetBrowserTableModel::data(const QModelIndex& index, int role) const
{
auto sourceIndex = mapToSource(index);
if (!sourceIndex.isValid())
{
return QVariant();
}
AssetBrowserEntry* entry = GetAssetEntry(sourceIndex);
if (entry == nullptr)
{
AZ_Assert(false, "AssetBrowserTableModel - QModelIndex does not reference an AssetEntry. Source model is not valid.");
return QVariant();
}
return sourceIndex.data(role);
}
QModelIndex AssetBrowserTableModel::index(int row, int column, const QModelIndex& parent) const
{
return parent.isValid() ? QModelIndex() : createIndex(row, column, m_indexMap[row].internalPointer());
}
int AssetBrowserTableModel::rowCount(const QModelIndex& parent) const
{
return !parent.isValid() ? m_indexMap.size() : 0;
}
int AssetBrowserTableModel::BuildTableModelMap(
const QAbstractItemModel* model, const QModelIndex& parent /*= QModelIndex()*/, int row /*= 0*/)
{
int rows = model ? model->rowCount(parent) : 0;
for (int i = 0; i < rows; ++i)
{
QModelIndex index = model->index(i, 0, parent);
AssetBrowserEntry* entry = GetAssetEntry(m_filterModel->mapToSource(index));
//We only wanna see the source assets.
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
{
beginInsertRows(parent, row, row);
m_indexMap[row] = index;
endInsertRows();
Q_EMIT dataChanged(index, index);
++row;
}
if (model->hasChildren(index))
{
row = BuildTableModelMap(model, index, row);
}
}
return row;
}
AssetBrowserEntry* AssetBrowserTableModel::GetAssetEntry(QModelIndex index) const
{
if (index.isValid())
{
return static_cast<AssetBrowserEntry*>(index.internalPointer());
}
else
{
AZ_Error("AssetBrowser", false, "Invalid Source Index provided to GetAssetEntry.");
return nullptr;
}
}
void AssetBrowserTableModel::UpdateTableModelMaps()
{
emit layoutAboutToBeChanged();
if (!m_indexMap.isEmpty())
{
beginRemoveRows(m_indexMap.first(), m_indexMap.first().row(), m_indexMap.last().row());
m_indexMap.clear();
endRemoveRows();
}
BuildTableModelMap(sourceModel());
emit layoutChanged();
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/moc_AssetBrowserTableModel.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.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Asset/AssetCommon.h>
#include <QSortFilterProxyModel>
#include <QPointer>
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserFilterModel;
class AssetBrowserEntry;
class AssetBrowserTableModel
: public QSortFilterProxyModel
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(AssetBrowserTableModel, AZ::SystemAllocator, 0);
explicit AssetBrowserTableModel(QObject* parent = nullptr);
////////////////////////////////////////////////////////////////////
// QSortFilterProxyModel
void setSourceModel(QAbstractItemModel* sourceModel) override;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
public Q_SLOTS:
void UpdateTableModelMaps();
protected:
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override;
////////////////////////////////////////////////////////////////////
private:
AssetBrowserEntry* GetAssetEntry(QModelIndex index) const;
int BuildTableModelMap(const QAbstractItemModel* model, const QModelIndex& parent = QModelIndex(), int row = 0);
private:
QPointer<AssetBrowserFilterModel> m_filterModel;
QMap<int, QModelIndex> m_indexMap;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework

@ -46,6 +46,7 @@ namespace AzToolsFramework
const char* AssetBrowserEntry::m_columnNames[] =
{
"Name",
"Path",
"Source ID",
"Fingerprint",
"Guid",
@ -128,6 +129,8 @@ namespace AzToolsFramework
return QString::fromUtf8(m_name.c_str());
case Column::DisplayName:
return m_displayName;
case Column::Path:
return m_displayPath;
default:
return QVariant();
}

@ -68,6 +68,7 @@ namespace AzToolsFramework
enum class Column
{
Name,
Path,
SourceID,
Fingerprint,
Guid,
@ -135,6 +136,7 @@ namespace AzToolsFramework
protected:
AZStd::string m_name;
QString m_displayName;
QString m_displayPath;
AZStd::string m_relativePath;
AZStd::string m_fullPath;
AZStd::vector<AssetBrowserEntry*> m_children;

@ -43,6 +43,7 @@ namespace AzToolsFramework
void FolderAssetBrowserEntry::UpdateChildPaths(AssetBrowserEntry* child) const
{
child->m_relativePath = m_relativePath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
child->m_displayPath = QString::fromUtf8(child->m_relativePath.c_str());
child->m_fullPath = m_fullPath + AZ_CORRECT_DATABASE_SEPARATOR + child->m_name;
AssetBrowserEntry::UpdateChildPaths(child);
}

@ -286,6 +286,9 @@ namespace AzToolsFramework
product->m_assetType = productWithUuidDatabaseEntry.second.m_assetType;
product->m_assetType.ToString(product->m_assetTypeString);
AZ::Data::AssetCatalogRequestBus::BroadcastResult(product->m_relativePath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId);
QString displayPath = QString::fromUtf8(product->m_relativePath.c_str());
displayPath.remove(QString(AZ_CORRECT_DATABASE_SEPARATOR + QString::fromUtf8(product->m_name.c_str())));
product->m_displayPath = displayPath;
EntryCache::GetInstance()->m_productAssetIdMap[assetId] = product;
if (needsAdd)

@ -0,0 +1,178 @@
/*
* 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 <API/EditorAssetSystemAPI.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryCache.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h>
#include <AzToolsFramework/AssetBrowser/Views/EntryDelegate.h>
AZ_PUSH_DISABLE_WARNING(
4244 4251 4800, "-Wunknown-warning-option") // conversion from 'int' to 'float', possible loss of data, needs to have dll-interface to
// be used by clients of class 'QFlags<QPainter::RenderHint>::Int': forcing value to bool
// 'true' or 'false' (performance warning)
#include <QCoreApplication>
#include <QHeaderView>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QPen>
#include <QTimer>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework
{
namespace AssetBrowser
{
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
: QTableView(parent)
, m_delegate(new EntryDelegate(this))
{
setSortingEnabled(true);
setItemDelegate(m_delegate);
verticalHeader()->hide();
setContextMenuPolicy(Qt::CustomContextMenu);
setMouseTracking(true);
setSortingEnabled(false);
setSelectionMode(QAbstractItemView::SingleSelection);
connect(this, &QTableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu);
AssetBrowserViewRequestBus::Handler::BusConnect();
AssetBrowserComponentNotificationBus::Handler::BusConnect();
}
AssetBrowserTableView::~AssetBrowserTableView()
{
AssetBrowserViewRequestBus::Handler::BusDisconnect();
AssetBrowserComponentNotificationBus::Handler::BusDisconnect();
}
void AssetBrowserTableView::setModel(QAbstractItemModel* model)
{
m_tableModel = qobject_cast<AssetBrowserTableModel*>(model);
AZ_Assert(m_tableModel, "Expecting AssetBrowserTableModel");
m_sourceFilterModel = qobject_cast<AssetBrowserFilterModel*>(m_tableModel->sourceModel());
QTableView::setModel(model);
connect(m_tableModel, &AssetBrowserTableModel::layoutChanged, this, &AssetBrowserTableView::layoutChangedSlot);
horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
}
void AssetBrowserTableView::SetName(const QString& name)
{
m_name = name;
bool isAssetBrowserComponentReady = false;
AssetBrowserComponentRequestBus::BroadcastResult(isAssetBrowserComponentReady, &AssetBrowserComponentRequests::AreEntriesReady);
if (isAssetBrowserComponentReady)
{
OnAssetBrowserComponentReady();
}
}
AZStd::vector<AssetBrowserEntry*> AssetBrowserTableView::GetSelectedAssets() const
{
QModelIndexList sourceIndexes;
for (const auto& index : selectedIndexes())
{
if (index.column() == 0)
{
sourceIndexes.push_back(m_sourceFilterModel->mapToSource(m_tableModel->mapToSource(index)));
}
}
AZStd::vector<AssetBrowserEntry*> entries;
AssetBrowserModel::SourceIndexesToAssetDatabaseEntries(sourceIndexes, entries);
return entries;
}
void AssetBrowserTableView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
QTableView::selectionChanged(selected, deselected);
Q_EMIT selectionChangedSignal(selected, deselected);
}
void AssetBrowserTableView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
{
// if selected entry is being removed, clear selection so not to select (and attempt to preview) other entries potentially
// marked for deletion
if (selectionModel() && selectionModel()->selectedIndexes().size() == 1)
{
QModelIndex selectedIndex = selectionModel()->selectedIndexes().first();
QModelIndex parentSelectedIndex = selectedIndex.parent();
if (parentSelectedIndex == parent && selectedIndex.row() >= start && selectedIndex.row() <= end)
{
selectionModel()->clear();
}
}
QTableView::rowsAboutToBeRemoved(parent, start, end);
}
void AssetBrowserTableView::layoutChangedSlot(
[[maybe_unused]] const QList<QPersistentModelIndex>& parents, [[maybe_unused]] QAbstractItemModel::LayoutChangeHint hint)
{
scrollToTop();
}
void AssetBrowserTableView::SelectProduct([[maybe_unused]] AZ::Data::AssetId assetID)
{
}
void AssetBrowserTableView::SelectFileAtPath([[maybe_unused]] const AZStd::string& assetPath)
{
}
void AssetBrowserTableView::ClearFilter()
{
emit ClearStringFilter();
emit ClearTypeFilter();
m_sourceFilterModel->FilterUpdatedSlotImmediate();
}
void AssetBrowserTableView::Update()
{
update();
}
void AssetBrowserTableView::OnAssetBrowserComponentReady()
{
}
void AssetBrowserTableView::OnContextMenu([[maybe_unused]] const QPoint& point)
{
const auto& selectedAssets = GetSelectedAssets();
if (selectedAssets.size() != 1)
{
return;
}
QMenu menu(this);
AssetBrowserInteractionNotificationBus::Broadcast(
&AssetBrowserInteractionNotificationBus::Events::AddContextMenuActions, this, &menu, selectedAssets);
if (!menu.isEmpty())
{
menu.exec(QCursor::pos());
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Views/moc_AssetBrowserTableView.cpp"

@ -0,0 +1,84 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
#include <QModelIndex>
#include <QPointer>
#include <QTableView>
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserEntry;
class AssetBrowserTableModel;
class AssetBrowserFilterModel;
class EntryDelegate;
class AssetBrowserTableView //! Table view that displays the asset browser entries in a list.
: public QTableView
, public AssetBrowserViewRequestBus::Handler
, public AssetBrowserComponentNotificationBus::Handler
{
Q_OBJECT
public:
explicit AssetBrowserTableView(QWidget* parent = nullptr);
~AssetBrowserTableView() override;
void setModel(QAbstractItemModel *model) override;
void SetName(const QString& name);
AZStd::vector<AssetBrowserEntry*> GetSelectedAssets() const;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserViewRequestBus
virtual void SelectProduct(AZ::Data::AssetId assetID) override;
virtual void SelectFileAtPath(const AZStd::string& assetPath) override;
virtual void ClearFilter() override;
virtual void Update() override;
//////////////////////////////////////////////////////////////////////////
// AssetBrowserComponentNotificationBus
void OnAssetBrowserComponentReady() override;
//////////////////////////////////////////////////////////////////////////
Q_SIGNALS:
void selectionChangedSignal(const QItemSelection& selected, const QItemSelection& deselected);
void ClearStringFilter();
void ClearTypeFilter();
protected Q_SLOTS:
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override;
void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override;
void layoutChangedSlot(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(),
QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
private:
QString m_name;
QPointer<AssetBrowserTableModel> m_tableModel = nullptr;
QPointer<AssetBrowserFilterModel> m_sourceFilterModel = nullptr;
EntryDelegate* m_delegate = nullptr;
private Q_SLOTS:
void OnContextMenu(const QPoint& point);
};
} // namespace AssetBrowser
} // namespace AzToolsFramework

@ -53,6 +53,7 @@ namespace AzToolsFramework
setSortingEnabled(true);
setItemDelegate(m_delegate);
header()->hide();
setContextMenuPolicy(Qt::CustomContextMenu);
setMouseTracking(true);
@ -99,8 +100,9 @@ namespace AzToolsFramework
AZStd::vector<AssetBrowserEntry*> AssetBrowserTreeView::GetSelectedAssets() const
{
const QModelIndexList& selectedIndexes = selectionModel()->selectedRows();
QModelIndexList sourceIndexes;
for (const auto& index : selectedIndexes())
for (const auto& index : selectedIndexes)
{
sourceIndexes.push_back(m_assetBrowserSortFilterProxyModel->mapToSource(index));
}
@ -172,6 +174,7 @@ namespace AzToolsFramework
void AssetBrowserTreeView::OnAssetBrowserComponentReady()
{
hideColumn(aznumeric_cast<int>(AssetBrowserEntry::Column::Path));
if (!m_name.isEmpty())
{
auto crc = AZ::Crc32(m_name.toUtf8().data());

@ -74,34 +74,34 @@ namespace AzToolsFramework
QPoint iconTopLeft(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
auto sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey());
QPalette actualPalette(option.palette);
if (sourceEntry)
if (index.column() == aznumeric_cast<int>(AssetBrowserEntry::Column::Name))
{
if (m_showSourceControl)
int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey());
if (sourceEntry)
{
DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey());
if (m_showSourceControl)
{
DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey());
}
// sources with no children should be greyed out.
if (sourceEntry->GetChildCount() == 0)
{
isEnabled = false; // draw in disabled style.
actualPalette.setCurrentColorGroup(QPalette::Disabled);
}
}
// sources with no children should be greyed out.
if (sourceEntry->GetChildCount() == 0)
{
isEnabled = false; // draw in disabled style.
actualPalette.setCurrentColorGroup(QPalette::Disabled);
}
}
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing.
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing.
}
QString displayString = index.column() == aznumeric_cast<int>(AssetBrowserEntry::Column::Name)
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Name)))
: qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Path)));
style->drawItemText(painter,
remainingRect,
option.displayAlignment,
actualPalette,
isEnabled,
entry->GetDisplayName(),
style->drawItemText(
painter, remainingRect, option.displayAlignment, actualPalette, isEnabled,
displayString,
isSelected ? QPalette::HighlightedText : QPalette::Text);
}
}

@ -159,12 +159,23 @@ namespace AzToolsFramework
void PrefabEditorEntityOwnershipService::GetNonPrefabEntities(EntityList& entities)
{
m_rootInstance->GetEntities(entities, false);
m_rootInstance->GetEntities(
[&entities](const AZStd::unique_ptr<AZ::Entity>& entity)
{
entities.emplace_back(entity.get());
return true;
});
}
bool PrefabEditorEntityOwnershipService::GetAllEntities(EntityList& entities)
{
m_rootInstance->GetEntities(entities, true);
m_rootInstance->GetAllEntitiesInHierarchy(
[&entities](const AZStd::unique_ptr<AZ::Entity>& entity)
{
entities.emplace_back(entity.get());
return true;
});
return true;
}
@ -252,13 +263,20 @@ namespace AzToolsFramework
}
AZStd::string out;
if (m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out))
if (!m_loaderInterface->SaveTemplateToString(m_rootInstance->GetTemplateId(), out))
{
const size_t bytesToWrite = out.size();
const size_t bytesWritten = stream.Write(bytesToWrite, out.data());
return bytesWritten == bytesToWrite;
return false;
}
return false;
const size_t bytesToWrite = out.size();
const size_t bytesWritten = stream.Write(bytesToWrite, out.data());
if(bytesWritten != bytesToWrite)
{
return false;
}
m_prefabSystemComponent->SetTemplateDirtyFlag(templateId, false);
return true;
}
void PrefabEditorEntityOwnershipService::CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename)
@ -544,7 +562,7 @@ namespace AzToolsFramework
return;
}
m_rootInstance->GetNestedEntities([this](AZStd::unique_ptr<AZ::Entity>& entity)
m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr<AZ::Entity>& entity)
{
AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor.");
if (entity->GetState() == AZ::Entity::State::Active)

@ -14,6 +14,7 @@
#include <AzCore/Console/Console.h>
#include <AzCore/Math/Internal/VectorConversions.inl>
#include <AzCore/std/numeric.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzToolsFramework/Maths/TransformUtils.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
@ -95,16 +96,34 @@ namespace AzToolsFramework
return axis * snapAdjustment.m_nextSnapDistance;
}
AZ::Vector3 CalculateSnappedOffset(
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size)
{
return AZStd::accumulate(
snapAxes, snapAxes + snapAxesCount, AZ::Vector3::CreateZero(),
[&unsnappedPosition, size](AZ::Vector3 acc, const AZ::Vector3& snapAxis)
{
acc += CalculateSnappedOffset(unsnappedPosition, snapAxis, size);
return acc;
});
}
AZ::Vector3 CalculateSnappedPosition(
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, const size_t snapAxesCount, const float size)
{
return unsnappedPosition + CalculateSnappedOffset(unsnappedPosition, snapAxes, snapAxesCount, size);
}
AZ::Vector3 CalculateSnappedTerrainPosition(
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float gridSize)
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, const int viewportId, const float size)
{
const AZ::Transform localFromWorld = worldFromLocal.GetInverse();
const AZ::Vector3 localSurfacePosition = localFromWorld.TransformPoint(worldSurfacePosition);
// snap in xy plane
AZ::Vector3 localSnappedSurfacePosition = localSurfacePosition +
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), gridSize) +
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), gridSize);
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisX(), size) +
CalculateSnappedOffset(localSurfacePosition, AZ::Vector3::CreateAxisY(), size);
// find terrain height at xy snapped location
float terrainHeight = 0.0f;

@ -12,6 +12,7 @@
#pragma once
#include <AzCore/std/math.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
@ -58,10 +59,18 @@ namespace AzToolsFramework
//! @note A movement of more than half size (in either direction) will cause a snap by size.
AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size);
//! Overload of CalculateSnappedOffset taking multiple axes.
AZ::Vector3 CalculateSnappedOffset(
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size);
//! Return the final snapped position according to size (unsnappedPosition + CalculateSnappedOffset).
AZ::Vector3 CalculateSnappedPosition(
const AZ::Vector3& unsnappedPosition, const AZ::Vector3* snapAxes, size_t snapAxesCount, float size);
//! For a given point on the terrain, calculate the closest xy position snapped to the grid
//! (z position is aligned to terrain height, not snapped to z grid)
AZ::Vector3 CalculateSnappedTerrainPosition(
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float gridSize);
const AZ::Vector3& worldSurfacePosition, const AZ::Transform& worldFromLocal, int viewportId, float size);
//! Wrapper for grid snapping and grid size bus calls.
GridSnapParameters GridSnapSettings(int viewportId);
@ -84,8 +93,8 @@ namespace AzToolsFramework
//! @param exponent Precision to use when rounding.
inline float Round(const float value, const float exponent)
{
const float precision = std::pow(10.0f, exponent);
return roundf(value * precision) / precision;
const float precision = AZStd::pow(10.0f, exponent);
return AZStd::round(value * precision) / precision;
}
//! Round to 3 significant digits (3 digits common usage).
@ -116,7 +125,7 @@ namespace AzToolsFramework
//! when dealing with values far from the origin.
inline AZ::Vector3 NonUniformScaleReciprocal(const AZ::Vector3& nonUniformScale)
{
AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal();
const AZ::Vector3 scaleReciprocal = nonUniformScale.GetReciprocal();
return AZ::Vector3(Round3(scaleReciprocal.GetX()), Round3(scaleReciprocal.GetY()), Round3(scaleReciprocal.GetZ()));
}
} // namespace AzToolsFramework

@ -373,17 +373,25 @@ namespace AzToolsFramework
}
}
void Instance::GetConstNestedEntities(const AZStd::function<bool(const AZ::Entity&)>& callback)
bool Instance::GetEntities_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
{
GetConstEntities(callback);
for (const auto& [instanceAlias, instance] : m_nestedInstances)
for (auto& [entityAlias, entity] : m_entities)
{
instance->GetConstNestedEntities(callback);
if (!entity)
{
continue;
}
if (!callback(entity))
{
return false;
}
}
return true;
}
void Instance::GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback)
bool Instance::GetConstEntities_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const
{
for (const auto& [entityAlias, entity] : m_entities)
{
@ -394,65 +402,91 @@ namespace AzToolsFramework
if (!callback(*entity))
{
break;
return false;
}
}
return true;
}
void Instance::GetNestedEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
bool Instance::GetAllEntitiesInHierarchy_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
{
GetEntities(callback);
for (auto& [instanceAlias, instance] : m_nestedInstances)
if (HasContainerEntity())
{
instance->GetNestedEntities(callback);
if (!callback(m_containerEntity))
{
return false;
}
}
}
void Instance::GetNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>&)>& callback)
{
for (auto& [instanceAlias, instance] : m_nestedInstances)
if (!GetEntities_Impl(callback))
{
callback(instance);
return false;
}
}
void Instance::GetEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
{
for (auto& [entityAlias, entity] : m_entities)
for (auto& [instanceAlias, instance] : m_nestedInstances)
{
if (!callback(entity))
if (!instance->GetAllEntitiesInHierarchy_Impl(callback))
{
break;
return false;
}
}
return true;
}
void Instance::GetEntities(EntityList& entities, bool includeNestedEntities)
bool Instance::GetAllEntitiesInHierarchyConst_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const
{
// Non-recursive traversal of instances
AZStd::vector<Instance*> instancesToTraverse = { this };
while (!instancesToTraverse.empty())
if (HasContainerEntity())
{
Instance* currentInstance = instancesToTraverse.back();
instancesToTraverse.pop_back();
if (includeNestedEntities)
if (!callback(*m_containerEntity))
{
instancesToTraverse.reserve(instancesToTraverse.size() + currentInstance->m_nestedInstances.size());
for (const auto& instanceByAlias : currentInstance->m_nestedInstances)
{
instancesToTraverse.push_back(instanceByAlias.second.get());
}
return false;
}
}
// Size increases by 1 for each instance because we have to count the container entity also.
entities.reserve(entities.size() + currentInstance->m_entities.size() + 1);
entities.push_back(m_containerEntity.get());
for (const auto& entityByAlias : currentInstance->m_entities)
if (!GetConstEntities_Impl(callback))
{
return false;
}
for (const auto& [instanceAlias, instance] : m_nestedInstances)
{
if (!instance->GetAllEntitiesInHierarchyConst_Impl(callback))
{
entities.push_back(entityByAlias.second.get());
return false;
}
}
return true;
}
void Instance::GetEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
{
GetEntities_Impl(callback);
}
void Instance::GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback) const
{
GetConstEntities_Impl(callback);
}
void Instance::GetAllEntitiesInHierarchy(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
{
GetAllEntitiesInHierarchy_Impl(callback);
}
void Instance::GetAllEntitiesInHierarchyConst(const AZStd::function<bool(const AZ::Entity&)>& callback) const
{
GetAllEntitiesInHierarchyConst_Impl(callback);
}
void Instance::GetNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>&)>& callback)
{
for (auto& [instanceAlias, instance] : m_nestedInstances)
{
callback(instance);
}
}
EntityAliasOptionalReference Instance::GetEntityAlias(const AZ::EntityId& id)

@ -121,10 +121,10 @@ namespace AzToolsFramework
/**
* Gets the entities in the Instance DOM. Can recursively trace all nested instances.
*/
void GetConstNestedEntities(const AZStd::function<bool(const AZ::Entity&)>& callback);
void GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback);
void GetNestedEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
void GetEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
void GetConstEntities(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
void GetAllEntitiesInHierarchy(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
void GetAllEntitiesInHierarchyConst(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
void GetNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>&)>& callback);
/**
@ -184,12 +184,6 @@ namespace AzToolsFramework
static InstanceAlias GenerateInstanceAlias();
protected:
/**
* Gets the entities owned by this instance
*/
void GetEntities(EntityList& entities, bool includeNestedEntities = false);
private:
static constexpr const char s_aliasPathSeparator = '/';
@ -197,6 +191,11 @@ namespace AzToolsFramework
void RemoveEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
bool GetEntities_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
bool GetConstEntities_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
bool GetAllEntitiesInHierarchy_Impl(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback);
bool GetAllEntitiesInHierarchyConst_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const;
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
AZStd::unique_ptr<AZ::Entity> DetachEntity(const EntityAlias& entityAlias);

@ -62,25 +62,16 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
}
}
AZStd::vector<AZ::Entity*> EditorInfoRemover::GetEntitiesFromInstance(AZStd::unique_ptr<Instance>& instance)
void EditorInfoRemover::GetEntitiesFromInstance(
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance, EntityList& hierarchyEntities)
{
AZStd::vector<AZ::Entity*> result;
instance->GetNestedEntities(
[&result](const AZStd::unique_ptr<AZ::Entity>& entity)
instance->GetAllEntitiesInHierarchy(
[&hierarchyEntities](const AZStd::unique_ptr<AZ::Entity>& entity)
{
result.emplace_back(entity.get());
hierarchyEntities.emplace_back(entity.get());
return true;
}
);
if (instance->HasContainerEntity())
{
auto containerEntityReference = instance->GetContainerEntity();
result.emplace_back(&containerEntityReference->get());
}
return result;
}
void EditorInfoRemover::SetEditorOnlyEntityHandlerFromCandidates(const EntityList& entities)
@ -543,7 +534,9 @@ exportComponent, prefabProcessorContext);
}
// grab all nested entities from the Instance as source entities.
EntityList sourceEntities = GetEntitiesFromInstance(instance);
EntityList sourceEntities;
GetEntitiesFromInstance(instance, sourceEntities);
EntityList exportEntities;
// prepare for validation of component requirements.
@ -616,7 +609,7 @@ exportComponent, prefabProcessorContext);
);
// replace entities of instance with exported ones.
instance->GetNestedEntities(
instance->GetAllEntitiesInHierarchy(
[&exportEntitiesMap](AZStd::unique_ptr<AZ::Entity>& entity)
{
auto entityId = entity->GetId();
@ -625,14 +618,6 @@ exportComponent, prefabProcessorContext);
}
);
if (instance->HasContainerEntity())
{
if (auto found = exportEntitiesMap.find(instance->GetContainerEntityId()); found != exportEntitiesMap.end())
{
instance->SetContainerEntity(*found->second);
}
}
// save the final result in the target Prefab DOM.
PrefabDom filteredPrefab;
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*instance, filteredPrefab))

@ -55,8 +55,8 @@ namespace AzToolsFramework::Prefab::PrefabConversionUtils
protected:
using EntityList = AZStd::vector<AZ::Entity*>;
static EntityList GetEntitiesFromInstance(
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance);
static void GetEntitiesFromInstance(
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance, EntityList& hierarchyEntities);
static bool ReadComponentAttribute(
AZ::Component* component,

@ -58,8 +58,17 @@ namespace AzToolsFramework
if (!path.empty())
{
infoString =
QObject::tr("<span style=\"font-style: italic; font-weight: 400;\">(%1)</span>").arg(path.Filename().Native().data());
QString saveFlag = "";
auto dirtyOutcome = m_prefabPublicInterface->HasUnsavedChanges(path);
if (dirtyOutcome.IsSuccess() && dirtyOutcome.GetValue() == true)
{
saveFlag = "*";
}
infoString = QObject::tr("<span style=\"font-style: italic; font-weight: 400;\">(%1%2)</span>")
.arg(path.Filename().Native().data())
.arg(saveFlag);
}
return infoString;

@ -28,6 +28,7 @@
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <QApplication>
#include <QFileDialog>
@ -588,15 +589,6 @@ namespace AzToolsFramework
bool PrefabIntegrationManager::QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath)
{
QWidget* mainWindow = nullptr;
EditorRequests::Bus::BroadcastResult(mainWindow, &EditorRequests::Bus::Events::GetMainWindow);
if (mainWindow == nullptr)
{
AZ_Assert(false, "Prefab - Could not detect Editor main window to generate the asset picker.");
return false;
}
AssetSelectionModel selection;
// Note, stringfilter will match every source file CONTAINING ".prefab".
@ -624,7 +616,7 @@ namespace AzToolsFramework
selection.SetDisplayFilter(compositeFilterPtr);
selection.SetSelectionFilter(compositeFilterPtr);
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, mainWindow);
AssetBrowserComponentRequestBus::Broadcast(&AssetBrowserComponentRequests::PickAssets, selection, AzToolsFramework::GetActiveWindow());
if (!selection.IsValid())
{
@ -983,12 +975,7 @@ namespace AzToolsFramework
includedEntities.c_str(),
referencedEntities.c_str());
QWidget* mainWindow = nullptr;
AzToolsFramework::EditorRequests::Bus::BroadcastResult(
mainWindow,
&AzToolsFramework::EditorRequests::Bus::Events::GetMainWindow);
QMessageBox msgBox(mainWindow);
QMessageBox msgBox(AzToolsFramework::GetActiveWindow());
msgBox.setWindowTitle("External Entity References");
msgBox.setText("The prefab contains references to external entities that are not selected.");
msgBox.setInformativeText("You can move the referenced entities into this prefab or retain the external references.");

@ -210,6 +210,15 @@ namespace AzToolsFramework
//! Type to inherit to implement ViewportInteractionRequests.
using ViewportInteractionRequestBus = AZ::EBus<ViewportInteractionRequests, ViewportEBusTraits>;
//! An interface to notify when changes to viewport settings have happened.
class ViewportSettingNotifications
{
public:
virtual void OnGridSnappingChanged(bool enabled) = 0;
};
using ViewportSettingsNotificationBus = AZ::EBus<ViewportSettingNotifications, ViewportEBusTraits>;
//! Requests to freeze the Viewport Input
//! Added to prevent a bug with the legacy CryEngine Viewport code that would
//! keep doing raycast tests even when no level is loaded, causing a crash.

@ -127,6 +127,7 @@ namespace AzToolsFramework
static const char* const s_dittoTranslationIndividualUndoRedoDesc = "Ditto translation individual";
static const char* const s_dittoScaleIndividualWorldUndoRedoDesc = "Ditto scale individual world";
static const char* const s_dittoScaleIndividualLocalUndoRedoDesc = "Ditto scale individual local";
static const char* const s_snapToWorldGridUndoRedoDesc = "Snap to world grid";
static const char* const s_showAllEntitiesUndoRedoDesc = s_showAllTitle;
static const char* const s_lockSelectionUndoRedoDesc = s_lockSelectionTitle;
static const char* const s_hideSelectionUndoRedoDesc = s_hideSelectionTitle;
@ -142,6 +143,7 @@ namespace AzToolsFramework
static const char* const SpaceClusterWorldTooltip = "Toggle world space lock";
static const char* const SpaceClusterParentTooltip = "Toggle parent space lock";
static const char* const SpaceClusterLocalTooltip = "Toggle local space lock";
static const char* const SnappingClusterSnapToWorldTooltip = "Snap selected entities to the world space grid";
static const AZ::Color s_fadedXAxisColor = AZ::Color(AZ::u8(200), AZ::u8(127), AZ::u8(127), AZ::u8(255));
static const AZ::Color s_fadedYAxisColor = AZ::Color(AZ::u8(127), AZ::u8(190), AZ::u8(127), AZ::u8(255));
@ -150,8 +152,6 @@ namespace AzToolsFramework
static const AZ::Color s_pickedOrientationColor = AZ::Color(0.0f, 1.0f, 0.0f, 1.0f);
static const AZ::Color s_selectedEntityAabbColor = AZ::Color(0.6f, 0.6f, 0.6f, 0.4f);
static const int s_defaultViewportId = 0;
static const float s_pivotSize = 0.075f; // the size of the pivot (box) to render when selected
// data passed to manipulators when processing mouse interactions
@ -489,6 +489,16 @@ namespace AzToolsFramework
return buttonId;
}
void SnappingCluster::TrySetVisible(const bool visible)
{
bool snapping = false;
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
snapping, ViewportUi::DefaultViewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSnappingEnabled);
// show snapping viewport ui only if there are entities selected and snapping is enabled
SetViewportUiClusterVisible(m_clusterId, visible && snapping);
}
// return either center or entity pivot
static AZ::Vector3 CalculatePivotTranslation(const AZ::EntityId entityId, const EditorTransformComponentSelectionRequests::Pivot pivot)
{
@ -503,7 +513,8 @@ namespace AzToolsFramework
void EditorTransformComponentSelection::SetAllViewportUiVisible(const bool visible)
{
SetViewportUiClusterVisible(m_transformModeClusterId, visible);
SetViewportUiClusterVisible(m_spaceCluster.m_spaceClusterId, visible);
SetViewportUiClusterVisible(m_spaceCluster.m_clusterId, visible);
SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, visible);
m_viewportUiVisible = visible;
}
@ -524,8 +535,8 @@ namespace AzToolsFramework
};
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton,
m_spaceCluster.m_spaceClusterId, buttonIdFromFrameFn(referenceFrame));
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterActiveButton, m_spaceCluster.m_clusterId,
buttonIdFromFrameFn(referenceFrame));
}
namespace ETCS
@ -1034,9 +1045,12 @@ namespace AzToolsFramework
EditorEntityLockComponentNotificationBus::Router::BusRouterConnect();
EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId);
EditorContextMenuBus::Handler::BusConnect();
ViewportInteraction::ViewportSettingsNotificationBus::Handler::BusConnect(ViewportUi::DefaultViewportId);
CreateTransformModeSelectionCluster();
CreateSpaceSelectionCluster();
CreateSnappingCluster();
RegisterActions();
SetupBoxSelect();
RefreshSelectedEntityIdsAndRegenerateManipulators();
@ -1048,12 +1062,14 @@ namespace AzToolsFramework
DestroyManipulators(m_entityIdManipulators);
DestroyCluster(m_transformModeClusterId);
DestroyCluster(m_spaceCluster.m_spaceClusterId);
DestroyCluster(m_spaceCluster.m_clusterId);
DestroyCluster(m_snappingCluster.m_clusterId);
UnregisterActions();
m_pivotOverrideFrame.Reset();
ViewportInteraction::ViewportSettingsNotificationBus::Handler::BusDisconnect();
EditorContextMenuBus::Handler::BusConnect();
EditorManipulatorCommandUndoRedoRequestBus::Handler::BusDisconnect();
EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect();
@ -2473,7 +2489,7 @@ namespace AzToolsFramework
// create and register the buttons (strings correspond to icons even if the values appear different)
m_translateButtonId = RegisterClusterButton(m_transformModeClusterId, "Move");
m_rotateButtonId = RegisterClusterButton(m_transformModeClusterId, "Translate");
m_rotateButtonId = RegisterClusterButton(m_transformModeClusterId, "Rotate");
m_scaleButtonId = RegisterClusterButton(m_transformModeClusterId, "Scale");
// set button tooltips
@ -2513,28 +2529,64 @@ namespace AzToolsFramework
m_transformModeSelectionHandler);
}
void EditorTransformComponentSelection::CreateSnappingCluster()
{
// create the cluster for switching spaces/reference frames
ViewportUi::ViewportUiRequestBus::EventResult(
m_snappingCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
ViewportUi::Alignment::TopRight);
m_snappingCluster.m_snapToWorldButtonId = RegisterClusterButton(m_snappingCluster.m_clusterId, "Grid");
// set button tooltips
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
m_snappingCluster.m_clusterId, m_snappingCluster.m_snapToWorldButtonId, SnappingClusterSnapToWorldTooltip);
const auto onButtonClicked = [this](const ViewportUi::ButtonId buttonId)
{
if (buttonId == m_snappingCluster.m_snapToWorldButtonId)
{
float gridSize = 1.0f;
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
gridSize, ViewportUi::DefaultViewportId, &ViewportInteraction::ViewportInteractionRequestBus::Events::GridSize);
SnapSelectedEntitiesToWorldGrid(gridSize);
}
};
m_snappingCluster.m_snappingHandler = AZ::Event<ViewportUi::ButtonId>::Handler(onButtonClicked);
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler,
m_snappingCluster.m_clusterId, m_snappingCluster.m_snappingHandler);
// hide initially
SetViewportUiClusterVisible(m_snappingCluster.m_clusterId, false);
}
void EditorTransformComponentSelection::CreateSpaceSelectionCluster()
{
// create the cluster for switching spaces/reference frames
ViewportUi::ViewportUiRequestBus::EventResult(
m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
m_spaceCluster.m_clusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
ViewportUi::Alignment::TopRight);
// create and register the buttons (strings correspond to icons even if the values appear different)
m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "World");
m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Parent");
m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_spaceClusterId, "Local");
m_spaceCluster.m_worldButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "World");
m_spaceCluster.m_parentButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Parent");
m_spaceCluster.m_localButtonId = RegisterClusterButton(m_spaceCluster.m_clusterId, "Local");
// set button tooltips
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip);
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId,
m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip);
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip);
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId,
m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip);
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip);
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip, m_spaceCluster.m_clusterId,
m_spaceCluster.m_localButtonId, SpaceClusterLocalTooltip);
auto onButtonClicked = [this](ViewportUi::ButtonId buttonId)
{
@ -2576,14 +2628,31 @@ namespace AzToolsFramework
}
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonLocked,
m_spaceCluster.m_spaceClusterId, buttonId, m_spaceCluster.m_spaceLock.has_value());
m_spaceCluster.m_clusterId, buttonId, m_spaceCluster.m_spaceLock.has_value());
};
m_spaceCluster.m_spaceSelectionHandler = AZ::Event<ViewportUi::ButtonId>::Handler(onButtonClicked);
m_spaceCluster.m_spaceHandler = AZ::Event<ViewportUi::ButtonId>::Handler(onButtonClicked);
ViewportUi::ViewportUiRequestBus::Event(
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::RegisterClusterEventHandler,
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_spaceSelectionHandler);
m_spaceCluster.m_clusterId, m_spaceCluster.m_spaceHandler);
}
void EditorTransformComponentSelection::SnapSelectedEntitiesToWorldGrid(const float gridSize)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
const AZStd::array snapAxes = { AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ() };
ScopedUndoBatch undoBatch(s_snapToWorldGridUndoRedoDesc);
for (const AZ::EntityId& entityId : m_selectedEntityIds)
{
ScopedUndoBatch::MarkEntityDirty(entityId);
SetEntityWorldTranslation(
entityId, CalculateSnappedPosition(GetWorldTranslation(entityId), snapAxes.data(), snapAxes.size(), gridSize));
}
RefreshManipulators(RefreshType::Translation);
}
EditorTransformComponentSelectionRequests::Mode EditorTransformComponentSelection::GetTransformMode()
@ -3145,15 +3214,16 @@ namespace AzToolsFramework
return "Transform Component";
}
void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags)
void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(
QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags)
{
QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick));
QObject::connect(
action, &QAction::triggered, action,
[this]()
{
ToggleCenterPivotSelection();
});
QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick));
QObject::connect(
action, &QAction::triggered, action,
[this]()
{
ToggleCenterPivotSelection();
});
}
void EditorTransformComponentSelection::BeforeEntitySelectionChanged()
@ -3175,7 +3245,7 @@ namespace AzToolsFramework
}
void EditorTransformComponentSelection::AfterEntitySelectionChanged(
const EntityIdList& /*newlySelectedEntities*/, const EntityIdList& /*newlyDeselectedEntities*/)
[[maybe_unused]] const EntityIdList& newlySelectedEntities, [[maybe_unused]] const EntityIdList& newlyDeselectedEntities)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@ -3195,6 +3265,8 @@ namespace AzToolsFramework
m_didSetSelectedEntities = false;
}
m_snappingCluster.TrySetVisible(m_viewportUiVisible && !m_selectedEntityIds.empty());
RegenerateManipulators();
}
@ -3657,6 +3729,11 @@ namespace AzToolsFramework
SetAllViewportUiVisible(true);
}
void EditorTransformComponentSelection::OnGridSnappingChanged([[maybe_unused]] const bool enabled)
{
m_snappingCluster.TrySetVisible(m_viewportUiVisible && !m_selectedEntityIds.empty());
}
namespace ETCS
{
// little raii wrapper to switch a value from true to false and back

@ -115,12 +115,28 @@ namespace AzToolsFramework
SpaceCluster(const SpaceCluster&) = delete;
SpaceCluster& operator=(const SpaceCluster&) = delete;
ViewportUi::ClusterId m_spaceClusterId; //!< The id identifying the reference space cluster.
ViewportUi::ClusterId m_clusterId; //!< The id identifying the reference space cluster.
ViewportUi::ButtonId m_localButtonId; //!< Local reference space button id.
ViewportUi::ButtonId m_parentButtonId; //!< Parent reference space button id.
ViewportUi::ButtonId m_worldButtonId; //!< World reference space button id.
AZ::Event<ViewportUi::ButtonId>::Handler m_spaceSelectionHandler; //!< Callback for when a space cluster button is pressed.
AZStd::optional<ReferenceFrame> m_spaceLock; //!< Locked reference frame to use if set.
AZ::Event<ViewportUi::ButtonId>::Handler m_spaceHandler; //!< Callback for when a space cluster button is pressed.
};
//! Grouping of viewport ui related state for aligning transforms to a grid.
struct SnappingCluster
{
SnappingCluster() = default;
// disable copying and moving (implicit)
SnappingCluster(const SnappingCluster&) = delete;
SnappingCluster& operator=(const SnappingCluster&) = delete;
//! Attempt to show the snapping cluster (will only succeed if snapping is enabled).
void TrySetVisible(bool visible);
ViewportUi::ClusterId m_clusterId; //!< The cluster id for all snapping buttons.
ViewportUi::ButtonId m_snapToWorldButtonId; //!< The button id for snapping all axes to the world.
AZ::Event<ViewportUi::ButtonId>::Handler m_snappingHandler; //!< Callback for when a snapping cluster button is pressed.
};
//! Entity selection/interaction handling.
@ -138,6 +154,7 @@ namespace AzToolsFramework
, private EditorEntityLockComponentNotificationBus::Router
, private EditorManipulatorCommandUndoRedoRequestBus::Handler
, private AZ::TransformNotificationBus::MultiHandler
, private ViewportInteraction::ViewportSettingsNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR_DECL
@ -180,6 +197,7 @@ namespace AzToolsFramework
void CreateTransformModeSelectionCluster();
void CreateSpaceSelectionCluster();
void CreateSnappingCluster();
void ClearManipulatorTranslationOverride();
void ClearManipulatorOrientationOverride();
@ -228,14 +246,15 @@ namespace AzToolsFramework
AZStd::optional<AZ::Transform> GetManipulatorTransform() override;
void OverrideManipulatorOrientation(const AZ::Quaternion& orientation) override;
void OverrideManipulatorTranslation(const AZ::Vector3& translation) override;
void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation);
void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation);
void ResetTranslationForSelectedEntitiesLocal();
void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation);
void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation);
void ResetOrientationForSelectedEntitiesLocal();
void CopyScaleToSelectedEntitiesIndividualLocal(float scale);
void CopyScaleToSelectedEntitiesIndividualWorld(float scale);
void CopyTranslationToSelectedEntitiesIndividual(const AZ::Vector3& translation) override;
void CopyTranslationToSelectedEntitiesGroup(const AZ::Vector3& translation) override;
void ResetTranslationForSelectedEntitiesLocal() override;
void CopyOrientationToSelectedEntitiesIndividual(const AZ::Quaternion& orientation) override;
void CopyOrientationToSelectedEntitiesGroup(const AZ::Quaternion& orientation) override;
void ResetOrientationForSelectedEntitiesLocal() override;
void CopyScaleToSelectedEntitiesIndividualLocal(float scale) override;
void CopyScaleToSelectedEntitiesIndividualWorld(float scale) override;
void SnapSelectedEntitiesToWorldGrid(float gridSize) override;
// EditorManipulatorCommandUndoRedoRequestBus ...
void UndoRedoEntityManipulatorCommand(
@ -274,6 +293,9 @@ namespace AzToolsFramework
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
// ViewportSettingsNotificationBus overrides ...
void OnGridSnappingChanged(bool enabled) override;
// Helpers to safely interact with the TransformBus (requests).
void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation);
void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation);
@ -320,6 +342,7 @@ namespace AzToolsFramework
AzFramework::ClickDetector m_clickDetector; //!< Detect different types of mouse click.
AzFramework::CursorState m_cursorState; //!< Track the mouse position and delta movement each frame.
SpaceCluster m_spaceCluster; //!< Related viewport ui state for controlling the current reference space.
SnappingCluster m_snappingCluster; //!< Related viewport ui state for aligning positions to a grid or reference frame.
bool m_viewportUiVisible = true; //!< Used to hide/show the viewport ui elements.
};

@ -104,6 +104,9 @@ namespace AzToolsFramework
//! Copy scale to to each individual entity in world (absolute) space.
virtual void CopyScaleToSelectedEntitiesIndividualWorld(float scale) = 0;
//! Snap selected entities to be aligned with the world space grid.
virtual void SnapSelectedEntitiesToWorldGrid(float gridSize) = 0;
protected:
~EditorTransformComponentSelectionRequests() = default;
};

@ -24,8 +24,6 @@
namespace AzToolsFramework::ViewportUi::Internal
{
// margin for the Viewport UI Overlay in pixels
const static int ViewportUiOverlayMargin = 5;
const static int HighlightBorderSize = 5;
const static int TopHighlightBorderSize = 25;
const static char* HighlightBorderColor = "#44B2F8";
@ -281,7 +279,7 @@ namespace AzToolsFramework::ViewportUi::Internal
void ViewportUiDisplay::HideViewportUiElement(ViewportUiElementId elementId)
{
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId);
element.m_widget && UiDisplayEnabled())
element.m_widget)
{
element.m_widget->setVisible(false);
}
@ -387,7 +385,6 @@ namespace AzToolsFramework::ViewportUi::Internal
m_fullScreenLayout.setSpacing(0);
m_fullScreenLayout.setContentsMargins(0, 0, 0, 0);
m_fullScreenLayout.addLayout(&m_uiOverlayLayout, 0, 0, 1, 1);
m_uiOverlayLayout.setMargin(ViewportUiOverlayMargin);
// format the label which will appear on top of the highlight border
AZStd::string styleSheet = AZStd::string::format(

@ -25,13 +25,15 @@ namespace AzToolsFramework::ViewportUi::Internal
: QGridLayout(parent)
{
// set margins and spacing for internal contents
setContentsMargins(0, 0, 0, 0);
setContentsMargins(
ViewportUiOverlayMargin, ViewportUiOverlayMargin + ViewportUiOverlayTopMarginPadding, ViewportUiOverlayMargin,
ViewportUiOverlayMargin);
setSpacing(ViewportUiDisplayLayoutSpacing);
// create a 3x2 map of sub layouts which will stack widgets according to their mapped alignment
m_internalLayouts = AZStd::unordered_map<Qt::Alignment, QBoxLayout*> {
CreateSubLayout(new QVBoxLayout(), 0, 0, Qt::AlignTop | Qt::AlignLeft),
CreateSubLayout(new QHBoxLayout(), 1, 0, Qt::AlignBottom | Qt::AlignLeft),
CreateSubLayout(new QVBoxLayout(), 1, 0, Qt::AlignBottom | Qt::AlignLeft),
CreateSubLayout(new QVBoxLayout(), 0, 1, Qt::AlignTop),
CreateSubLayout(new QHBoxLayout(), 1, 1, Qt::AlignBottom),
CreateSubLayout(new QVBoxLayout(), 0, 2, Qt::AlignTop | Qt::AlignRight),
@ -50,9 +52,42 @@ namespace AzToolsFramework::ViewportUi::Internal
if (auto layoutForAlignment = m_internalLayouts.find(alignment);
layoutForAlignment != m_internalLayouts.end())
{
// place the widget before the invisible spacer
// spacer must be last item in layout to not interfere with positioning
int index = layoutForAlignment->second->count() - 1;
// place the widget before or after the invisible spacer
// depending on the layout alignment
int index = 0;
switch (alignment)
{
case Qt::AlignTop | Qt::AlignLeft:
case Qt::AlignTop:
index = layoutForAlignment->second->count() - 1;
break;
case Qt::AlignBottom | Qt::AlignRight:
case Qt::AlignBottom:
index = layoutForAlignment->second->count();
break;
// TopRight and BottomLeft are special cases
// place the spacer differently according to whether it's a vertical or horizontal layout
case Qt::AlignTop | Qt::AlignRight:
if (QVBoxLayout* vLayout = qobject_cast<QVBoxLayout*>(layoutForAlignment->second))
{
index = layoutForAlignment->second->count() - 1;
}
else if (QHBoxLayout* hLayout = qobject_cast<QHBoxLayout*>(layoutForAlignment->second))
{
index = layoutForAlignment->second->count();
}
break;
case Qt::AlignBottom | Qt::AlignLeft:
if (QVBoxLayout* vLayout = qobject_cast<QVBoxLayout*>(layoutForAlignment->second))
{
index = layoutForAlignment->second->count();
}
else if (QHBoxLayout* hLayout = qobject_cast<QHBoxLayout*>(layoutForAlignment->second))
{
index = layoutForAlignment->second->count() - 1;
}
break;
}
layoutForAlignment->second->insertWidget(index, widget);
}
}

@ -19,6 +19,11 @@
namespace AzToolsFramework::ViewportUi::Internal
{
// margin for the Viewport UI Overlay in pixels
constexpr int ViewportUiOverlayMargin = 5;
// padding to make space for ImGui
constexpr int ViewportUiOverlayTopMarginPadding = 20;
//! QGridLayout implementation that uses a grid of QVBox/QHBoxLayouts internally to stack widgets.
class ViewportUiDisplayLayout : public QGridLayout
{

@ -544,6 +544,8 @@ set(FILES
AssetBrowser/AssetBrowserEntry.h
AssetBrowser/AssetBrowserFilterModel.cpp
AssetBrowser/AssetBrowserFilterModel.h
AssetBrowser/AssetBrowserTableModel.cpp
AssetBrowser/AssetBrowserTableModel.h
AssetBrowser/AssetBrowserModel.cpp
AssetBrowser/AssetBrowserModel.h
AssetBrowser/AssetEntryChange.h
@ -554,6 +556,8 @@ set(FILES
AssetBrowser/EBusFindAssetTypeByName.h
AssetBrowser/Views/AssetBrowserTreeView.cpp
AssetBrowser/Views/AssetBrowserTreeView.h
AssetBrowser/Views/AssetBrowserTableView.cpp
AssetBrowser/Views/AssetBrowserTableView.h
AssetBrowser/Views/EntryDelegate.cpp
AssetBrowser/Views/EntryDelegate.h
AssetBrowser/Views/AssetBrowserFolderWidget.cpp

@ -1,42 +1,42 @@
/*
* 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.
*
*/
* 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 <AzCore/Math/ToString.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ViewportInteraction.h>
#include <AzQtComponents/Components/GlobalEventFilter.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityModel.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzToolsFramework/Viewport/ActionBus.h>
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ViewportInteraction.h>
#include <AzQtComponents/Components/GlobalEventFilter.h>
using namespace AzToolsFramework;
@ -46,12 +46,11 @@ namespace AZ
{
return os << entityId.ToString().c_str();
}
}
} // namespace AZ
namespace UnitTest
{
class EditorEntityVisibilityCacheFixture
: public ToolsApplicationFixture
class EditorEntityVisibilityCacheFixture : public ToolsApplicationFixture
{
public:
void CreateLayerAndEntityHierarchy()
@ -116,8 +115,7 @@ namespace UnitTest
}
// Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection.
class EditorTransformComponentSelectionFixture
: public ToolsApplicationFixture
class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture
{
public:
void SetUpEditorFixtureImpl() override
@ -138,13 +136,11 @@ namespace UnitTest
EntityIdList m_entityIds;
};
void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(
const AZ::Quaternion& orientation)
void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation)
{
for (auto entityId : m_entityIds)
{
AZ::TransformBus::Event(
entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation);
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation);
}
}
@ -152,40 +148,32 @@ namespace UnitTest
{
AZStd::optional<AZ::Transform> manipulatorTransform;
EditorTransformComponentSelectionRequestBus::EventResult(
manipulatorTransform, GetEntityContextId(),
&EditorTransformComponentSelectionRequests::GetManipulatorTransform);
manipulatorTransform, GetEntityContextId(), &EditorTransformComponentSelectionRequests::GetManipulatorTransform);
return manipulatorTransform;
}
void EditorTransformComponentSelectionFixture::RefreshManipulators(
EditorTransformComponentSelectionRequests::RefreshType refreshType)
void EditorTransformComponentSelectionFixture::RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType refreshType)
{
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequests::RefreshManipulators, refreshType);
}
void EditorTransformComponentSelectionFixture::SetTransformMode(
EditorTransformComponentSelectionRequests::Mode transformMode)
void EditorTransformComponentSelectionFixture::SetTransformMode(EditorTransformComponentSelectionRequests::Mode transformMode)
{
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode,
transformMode);
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode, transformMode);
}
void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(
const AZ::Quaternion& orientation)
void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(const AZ::Quaternion& orientation)
{
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation,
orientation);
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation, orientation);
}
void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(
const AZ::Vector3& translation)
void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(const AZ::Vector3& translation)
{
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation,
translation);
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation, translation);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -202,8 +190,7 @@ namespace UnitTest
SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation);
const AZ::Transform manipulatorTransformBefore =
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
// check preconditions - manipulator transform matches parent/world transform (identity)
EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
@ -218,8 +205,7 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Then
const AZ::Transform manipulatorTransformAfter =
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
// check postconditions - manipulator transform matches parent/world transform (identity)
EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
@ -229,8 +215,7 @@ namespace UnitTest
{
// create invalid starting orientation to guarantee correct data is coming from GetLocalRotationQuaternion
AZ::Quaternion entityOrientation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), 90.0f);
AZ::TransformBus::EventResult(
entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
// manipulator orientation matches entity orientation
EXPECT_THAT(entityOrientation, IsClose(manipulatorTransformAfter.GetRotation()));
@ -252,8 +237,7 @@ namespace UnitTest
SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation);
const AZ::Transform manipulatorTransformBefore =
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
// check preconditions - manipulator transform matches manipulator orientation override (not entity transform)
EXPECT_THAT(manipulatorTransformBefore.GetBasisX(), IsClose(AZ::Vector3::CreateAxisY()));
@ -268,8 +252,7 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Then
const AZ::Transform manipulatorTransformAfter =
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
// check postconditions - manipulator transform matches parent/world space (manipulator override was cleared)
EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
@ -278,8 +261,7 @@ namespace UnitTest
for (auto entityId : m_entityIds)
{
AZ::Quaternion entityOrientation;
AZ::TransformBus::EventResult(
entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
// entity transform matches initial (entity transform was not reset, only manipulator was)
EXPECT_THAT(entityOrientation, IsClose(initialEntityOrientation));
@ -301,16 +283,13 @@ namespace UnitTest
AZ::EntityId parentId = CreateDefaultEditorEntity("Parent", &parent);
AZ::EntityId childId = CreateDefaultEditorEntity("Child", &child);
AZ::TransformBus::Event(
childId, &AZ::TransformInterface::SetParent, parentId);
AZ::TransformBus::Event(
parentId, &AZ::TransformInterface::SetParent, grandParentId);
AZ::TransformBus::Event(childId, &AZ::TransformInterface::SetParent, parentId);
AZ::TransformBus::Event(parentId, &AZ::TransformInterface::SetParent, grandParentId);
UnitTest::SliceAssets sliceAssets;
const auto sliceAssetId = UnitTest::SaveAsSlice({ grandParent }, GetApplication(), sliceAssets);
EntityList instantiatedEntities =
UnitTest::InstantiateSlice(sliceAssetId, sliceAssets);
EntityList instantiatedEntities = UnitTest::InstantiateSlice(sliceAssetId, sliceAssets);
const AZ::EntityId entityIdToMove = instantiatedEntities.back()->GetId();
EditorEntityComponentChangeDetector editorEntityChangeDetector(entityIdToMove);
@ -321,8 +300,7 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// When
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(),
&EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual,
GetEntityContextId(), &EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual,
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(90.0f)));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -362,10 +340,9 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Then
AzToolsFramework::EntityIdList selectedEntities;
ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
AzToolsFramework::EntityIdList expectedSelectedEntities = {entity4, entity5, entity6};
AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -396,10 +373,9 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Then
AzToolsFramework::EntityIdList selectedEntities;
ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
AzToolsFramework::EntityIdList expectedSelectedEntities = {m_entity1, entity2, entity3, entity4};
AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entity1, entity2, entity3, entity4 };
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -416,11 +392,9 @@ namespace UnitTest
const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 0.0f));
// calculate the position in screen space of the initial position of the entity
const auto initialPositionScreen =
AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
// calculate the position in screen space of the final position of the entity
const auto finalPositionScreen =
AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState);
const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState);
// select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection)
AzToolsFramework::SelectEntity(m_entity1);
@ -452,10 +426,10 @@ namespace UnitTest
}
// simple widget to listen for a mouse wheel event and then forward it on to the ViewportSelectionRequestBus
class WheelEventWidget
: public QWidget
class WheelEventWidget : public QWidget
{
using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult;
public:
WheelEventWidget(QWidget* parent = nullptr)
: QWidget(parent)
@ -490,8 +464,7 @@ namespace UnitTest
{
EditorTransformComponentSelectionRequests::Mode transformMode;
EditorTransformComponentSelectionRequestBus::EventResult(
transformMode, GetEntityContextId(),
&EditorTransformComponentSelectionRequestBus::Events::GetTransformMode);
transformMode, GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode);
return transformMode;
};
@ -519,6 +492,56 @@ namespace UnitTest
EXPECT_THAT(wheelEventWidget.m_mouseInteractionResult, Eq(vi::MouseInteractionResult::Viewport));
}
TEST_F(EditorTransformComponentSelectionFixture, EntityPositionsCanBeSnappedToGrid)
{
using ::testing::Pointwise;
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
const AZStd::vector<AZ::Vector3> initialUnsnappedPositions = { AZ::Vector3(1.2f, 3.5f, 6.7f), AZ::Vector3(13.2f, 15.6f, 11.4f),
AZ::Vector3(4.2f, 103.2f, 16.6f) };
AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[0]);
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[1]);
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[2]);
AzToolsFramework::SelectEntities(m_entityIds);
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 2.0f);
AZStd::vector<AZ::Vector3> entityPositionsAfterSnap;
AZStd::transform(
m_entityIds.cbegin(), m_entityIds.cend(), AZStd::back_inserter(entityPositionsAfterSnap),
[](const AZ::EntityId& entityId)
{
return GetWorldTranslation(entityId);
});
const AZStd::vector<AZ::Vector3> expectedSnappedPositions = { AZ::Vector3(2.0f, 4.0f, 6.0f), AZ::Vector3(14.0f, 16.0f, 12.0f),
AZ::Vector3(4.0f, 104.0f, 16.0f) };
EXPECT_THAT(entityPositionsAfterSnap, Pointwise(ContainerIsClose(), expectedSnappedPositions));
}
TEST_F(EditorTransformComponentSelectionFixture, ManipulatorStaysAlignedToEntityTranslationAfterSnap)
{
const auto initialUnsnappedPosition = AZ::Vector3(1.2f, 3.5f, 6.7f);
AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPosition);
AzToolsFramework::SelectEntities(m_entityIds);
EditorTransformComponentSelectionRequestBus::Event(
GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 1.0f);
const auto entityPositionAfterSnap = GetWorldTranslation(m_entity1);
const AZ::Vector3 manipulatorPositionAfterSnap =
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()).GetTranslation();
const auto expectedSnappedPosition = AZ::Vector3(1.0f, 4.0f, 7.0f);
EXPECT_THAT(entityPositionAfterSnap, IsClose(expectedSnappedPosition));
EXPECT_THAT(expectedSnappedPosition, IsClose(manipulatorPositionAfterSnap));
}
// struct to contain input reference frame and expected orientation outcome based on
// the reference frame, selection and entity hierarchy
struct ReferenceFrameWithOrientation
@ -541,19 +564,20 @@ namespace UnitTest
class EditorTransformComponentSelectionSingleEntityPivotFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(EditorTransformComponentSelectionSingleEntityPivotFixture, PivotOrientationMatchesReferenceFrameSingleEntity)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculatePivotOrientation;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
AZ::TransformBus::Event(
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateFromQuaternionAndTranslation(
ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -574,20 +598,20 @@ namespace UnitTest
All,
EditorTransformComponentSelectionSingleEntityPivotFixture,
testing::Values(
ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace},
ReferenceFrameWithOrientation{ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity()},
ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()}));
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() },
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
class EditorTransformComponentSelectionSingleEntityWithParentPivotFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionSingleEntityWithParentPivotFixture,
PivotOrientationMatchesReferenceFrameEntityWithParent)
TEST_P(EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, PivotOrientationMatchesReferenceFrameEntityWithParent)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculatePivotOrientation;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
@ -596,8 +620,7 @@ namespace UnitTest
AZ::TransformBus::Event(
parentEntityId, &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateFromQuaternionAndTranslation(
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
AZ::Transform::CreateFromQuaternionAndTranslation(ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
AZ::TransformBus::Event(
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
@ -624,20 +647,20 @@ namespace UnitTest
All,
EditorTransformComponentSelectionSingleEntityWithParentPivotFixture,
testing::Values(
ReferenceFrameWithOrientation{ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace},
ReferenceFrameWithOrientation{ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace},
ReferenceFrameWithOrientation{ReferenceFrame::World, AZ::Quaternion::CreateIdentity()}));
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
class EditorTransformComponentSelectionMultipleEntitiesPivotFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionMultipleEntitiesPivotFixture,
PivotOrientationMatchesReferenceFrameMultipleEntities)
TEST_P(EditorTransformComponentSelectionMultipleEntitiesPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntities)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculatePivotOrientationForEntityIds;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
@ -646,23 +669,18 @@ namespace UnitTest
// setup entities in arbitrary triangle arrangement
AZ::TransformBus::Event(
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
AZ::TransformBus::Event(
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
AZ::TransformBus::Event(
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
// note: EntityIdManipulatorLookup{} is unused during this test
EntityIdManipulatorLookups lookups {
{m_entityIds[0], EntityIdManipulatorLookup{}},
{m_entityIds[1], EntityIdManipulatorLookup{}},
{m_entityIds[2], EntityIdManipulatorLookup{}}
};
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
{ m_entityIds[1], EntityIdManipulatorLookup{} },
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -691,14 +709,16 @@ namespace UnitTest
class EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture,
PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParent)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculatePivotOrientationForEntityIds;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
@ -711,22 +731,18 @@ namespace UnitTest
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
AZ::TransformBus::Event(
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
AZ::TransformBus::Event(
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
// note: EntityIdManipulatorLookup{} is unused during this test
// only select second two entities that are children of m_entityIds[0]
EntityIdManipulatorLookups lookups{
{m_entityIds[1], EntityIdManipulatorLookup{}},
{m_entityIds[2], EntityIdManipulatorLookup{}}
};
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -755,14 +771,16 @@ namespace UnitTest
class EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture,
PivotOrientationMatchesReferenceFrameMultipleEntitiesDifferentParent)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculatePivotOrientationForEntityIds;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
@ -776,22 +794,18 @@ namespace UnitTest
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
AZ::TransformBus::Event(
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
AZ::TransformBus::Event(
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[3]);
// note: EntityIdManipulatorLookup{} is unused during this test
// only select second two entities that are children of different m_entities
EntityIdManipulatorLookups lookups{
{m_entityIds[1], EntityIdManipulatorLookup{}},
{m_entityIds[2], EntityIdManipulatorLookup{}}
};
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -819,30 +833,29 @@ namespace UnitTest
class EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
PivotOrientationMatchesReferenceFrameSingleEntityOptionalOverride)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculateSelectionPivotOrientation;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
AZ::TransformBus::Event(
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateFromQuaternionAndTranslation(
ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// When
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
EntityIdManipulatorLookups lookups{
{m_entityIds[0], EntityIdManipulatorLookup{}}
};
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} } };
// set override frame (orientation only)
OptionalFrame optionalFrame;
@ -870,14 +883,16 @@ namespace UnitTest
class EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture,
PivotOrientationMatchesReferenceFrameMultipleEntitiesOptionalOverride)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculateSelectionPivotOrientation;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
@ -885,23 +900,18 @@ namespace UnitTest
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
AZ::TransformBus::Event(
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
AZ::TransformBus::Event(
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
AZ::TransformBus::Event(
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
// note: EntityIdManipulatorLookup{} is unused during this test
EntityIdManipulatorLookups lookups{
{m_entityIds[0], EntityIdManipulatorLookup{}},
{m_entityIds[1], EntityIdManipulatorLookup{}},
{m_entityIds[2], EntityIdManipulatorLookup{}}
};
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
{ m_entityIds[1], EntityIdManipulatorLookup{} },
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -932,14 +942,16 @@ namespace UnitTest
class EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture,
PivotOrientationMatchesReferenceFrameMultipleEntitiesNoOptionalOverride)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculateSelectionPivotOrientation;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
@ -947,23 +959,18 @@ namespace UnitTest
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
AZ::TransformBus::Event(
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
AZ::TransformBus::Event(
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
AZ::TransformBus::Event(
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
// note: EntityIdManipulatorLookup{} is unused during this test
EntityIdManipulatorLookups lookups{
{m_entityIds[0], EntityIdManipulatorLookup{}},
{m_entityIds[1], EntityIdManipulatorLookup{}},
{m_entityIds[2], EntityIdManipulatorLookup{}}
};
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
{ m_entityIds[1], EntityIdManipulatorLookup{} },
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -992,14 +999,16 @@ namespace UnitTest
class EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture
: public EditorTransformComponentSelectionFixture
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation> {};
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
{
};
TEST_P(
EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture,
PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParentNoOptionalOverride)
{
using ETCS::PivotOrientationResult;
using ETCS::CalculateSelectionPivotOrientation;
using ETCS::PivotOrientationResult;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
@ -1012,21 +1021,17 @@ namespace UnitTest
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
AZ::TransformBus::Event(
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
AZ::TransformBus::Event(
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM,
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
// note: EntityIdManipulatorLookup{} is unused during this test
EntityIdManipulatorLookups lookups{
{m_entityIds[1], EntityIdManipulatorLookup{}},
{m_entityIds[2], EntityIdManipulatorLookup{}}
};
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1174,13 +1179,13 @@ namespace UnitTest
AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId);
// Layer1
// A
// B
// C
// Layer2
// D
// E
// F
// A
// B
// C
// Layer2
// D
// E
// F
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1270,13 +1275,13 @@ namespace UnitTest
AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId);
// Layer1
// A
// B
// C
// Layer2
// D
// E
// F
// A
// B
// C
// Layer2
// D
// E
// F
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1368,8 +1373,7 @@ namespace UnitTest
EXPECT_TRUE(!IsEntityVisible(m_layerId));
bool flagSetVisible = false;
EditorVisibilityRequestBus::EventResult(
flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag);
EditorVisibilityRequestBus::EventResult(flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag);
// even though a layer is set to not be visible, this is recorded by SetLayerChildrenVisibility
// and AreLayerChildrenVisible - the visibility flag will not be modified and remains true
@ -1377,12 +1381,12 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
class EditorEntityInfoRequestActivateTestComponent
: public AzToolsFramework::Components::EditorComponentBase
class EditorEntityInfoRequestActivateTestComponent : public AzToolsFramework::Components::EditorComponentBase
{
public:
AZ_EDITOR_COMPONENT(
EditorEntityInfoRequestActivateTestComponent, "{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}",
EditorEntityInfoRequestActivateTestComponent,
"{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}",
AzToolsFramework::Components::EditorComponentBase);
static void Reflect(AZ::ReflectContext* context);
@ -1391,13 +1395,13 @@ namespace UnitTest
void Activate() override
{
// ensure we can successfully read IsVisible and IsLocked (bus will be connected to in entity Init)
EditorEntityInfoRequestBus::EventResult(
m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible);
EditorEntityInfoRequestBus::EventResult(
m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked);
EditorEntityInfoRequestBus::EventResult(m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible);
EditorEntityInfoRequestBus::EventResult(m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked);
}
void Deactivate() override {}
void Deactivate() override
{
}
bool m_visible = false;
bool m_locked = true;
@ -1407,14 +1411,11 @@ namespace UnitTest
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorEntityInfoRequestActivateTestComponent>()
->Version(0)
;
serializeContext->Class<EditorEntityInfoRequestActivateTestComponent>()->Version(0);
}
}
class EditorEntityModelEntityInfoRequestFixture
: public ToolsApplicationFixture
class EditorEntityModelEntityInfoRequestFixture : public ToolsApplicationFixture
{
public:
void SetUpEditorFixtureImpl() override
@ -1435,8 +1436,7 @@ namespace UnitTest
// This is necessary to prevent a warning in the undo system.
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity,
entity->GetId());
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId());
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1469,8 +1469,7 @@ namespace UnitTest
// This is necessary to prevent a warning in the undo system.
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity,
entity->GetId());
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId());
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

@ -93,7 +93,7 @@ namespace UnitTest
// Retrieve the entity pointer from the component application bus.
AZ::Entity* wheelEntityUnderAxle = nullptr;
axleInstance->GetNestedEntities([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr<AZ::Entity>& entity)
axleInstance->GetAllEntitiesInHierarchy([&wheelEntityUnderAxle, wheelEntityIdUnderAxle](AZStd::unique_ptr<AZ::Entity>& entity)
{
if (entity->GetId() == wheelEntityIdUnderAxle)
{

@ -1,878 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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.
*
*/
/*
* Temporary dynamic tree structure used internally by GridMate.
* To be replaced with a general Vis framework when that becomes available.
*/
#ifndef RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
#define RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Plane.h>
#include <GridMate/Containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
namespace GridMate
{
namespace Internal
{
/**
*
*/
class DynamicTreeAabb : public AZ::Aabb
{
public:
GM_CLASS_ALLOCATOR(DynamicTreeAabb);
AZ_FORCE_INLINE explicit DynamicTreeAabb() {}
AZ_FORCE_INLINE DynamicTreeAabb(const AZ::Aabb& aabb) : AZ::Aabb(aabb) {}
AZ_FORCE_INLINE explicit DynamicTreeAabb(const AZ::Vector3& min,const AZ::Vector3& max) : AZ::Aabb(AZ::Aabb::CreateFromMinMax(min,max)) {}
AZ_FORCE_INLINE static DynamicTreeAabb CreateFromFacePoints(const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c)
{
DynamicTreeAabb vol(a,a);
vol.AddPoint(b);
vol.AddPoint(c);
return vol;
}
AZ_FORCE_INLINE void SignedExpand(const AZ::Vector3& e)
{
AZ::Vector3 zero = AZ::Vector3::CreateZero();
AZ::Vector3 mxE = m_max + e;
AZ::Vector3 miE = m_min + e;
m_max = AZ::Vector3::CreateSelectCmpGreater(e,zero,mxE,m_max );
m_min = AZ::Vector3::CreateSelectCmpGreater(e,zero,m_min,miE);
}
AZ_FORCE_INLINE int Classify(const AZ::Vector3& n,const float o,int s) const
{
AZ::Vector3 pi, px;
switch(s)
{
case (0+0+0): px=m_min;
pi=m_max; break;
case (1+0+0): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());
pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());break;
case (0+2+0): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());
pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());break;
case (1+2+0): px=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());
pi=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());break;
case (0+0+4): px=AZ::Vector3(m_min.GetX(),m_min.GetY(),m_max.GetZ());
pi=AZ::Vector3(m_max.GetX(),m_max.GetY(),m_min.GetZ());break;
case (1+0+4): px=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_max.GetZ());
pi=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_min.GetZ());break;
case (0+2+4): px=AZ::Vector3(m_min.GetX(),m_max.GetY(),m_max.GetZ());
pi=AZ::Vector3(m_max.GetX(),m_min.GetY(),m_min.GetZ());break;
case (1+2+4): px=m_max;
pi=m_min;break;
}
if (n.Dot(px) + o < 0.0f)
{
return -1;
}
if (n.Dot(pi) + o > 0.0f)
{
return 1;
}
return 0;
}
AZ_FORCE_INLINE float ProjectMinimum(const AZ::Vector3& v, unsigned signs) const
{
const AZ::Vector3* b[]={&m_max,&m_min};
const AZ::Vector3 p( b[(signs>>0)&1]->GetX(),b[(signs>>1)&1]->GetY(),b[(signs>>2)&1]->GetZ());
return p.Dot(v);
}
// Move the code here
AZ_FORCE_INLINE friend bool IntersectAabbAabb(const DynamicTreeAabb& a,const DynamicTreeAabb& b);
AZ_FORCE_INLINE friend bool IntersectAabbPoint(const DynamicTreeAabb& a, const AZ::Vector3& b);
AZ_FORCE_INLINE friend bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b);
AZ_FORCE_INLINE friend float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b);
AZ_FORCE_INLINE friend int Select(const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b);
AZ_FORCE_INLINE friend void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r);
AZ_FORCE_INLINE friend bool NotEqual(const DynamicTreeAabb& a, const DynamicTreeAabb& b);
private:
AZ_FORCE_INLINE void AddSpan(const AZ::Vector3& d, float& smi, float& smx) const
{
AZ::Vector3 vecZero = AZ::Vector3::CreateZero();
AZ::Vector3 mxD = m_max*d;
AZ::Vector3 miD = m_min*d;
AZ::Vector3 smiAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,mxD,miD);
AZ::Vector3 smxAdd = AZ::Vector3::CreateSelectCmpGreater(vecZero,d,miD,mxD);
AZ::Vector3 vecOne = AZ::Vector3::CreateOne();
// sum components
smi += smiAdd.Dot(vecOne);
smx += smxAdd.Dot(vecOne);
}
};
//
AZ_FORCE_INLINE bool IntersectAabbAabb(const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
return a.Overlaps(b);
}
AZ_FORCE_INLINE bool IntersectAabbPlane(const DynamicTreeAabb& a, const AZ::Plane& b)
{
//use plane normal to quickly select the nearest corner of the aabb
AZ::Vector3 testPoint = AZ::Vector3::CreateSelectCmpGreater(b.GetNormal(), AZ::Vector3::CreateZero(), a.GetMin(), a.GetMax());
//test if nearest point is inside the plane
return b.GetPointDist(testPoint) <= 0.0f;
}
//
AZ_FORCE_INLINE float Proximity(const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
const AZ::Vector3 d=(a.m_min+a.m_max)-(b.m_min+b.m_max);
// get abs and sum
return d.GetAbs().Dot(AZ::Vector3::CreateOne());
}
//
AZ_FORCE_INLINE int Select( const DynamicTreeAabb& o, const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
return Proximity(o,a) < Proximity(o,b);
}
//
AZ_FORCE_INLINE void Merge(const DynamicTreeAabb& a, const DynamicTreeAabb& b, DynamicTreeAabb& r)
{
r.m_min = AZ::Vector3::CreateSelectCmpGreater(b.m_min,a.m_min,a.m_min,b.m_min);
r.m_max = AZ::Vector3::CreateSelectCmpGreater(a.m_max,b.m_max,a.m_max,b.m_max);
}
//
AZ_FORCE_INLINE bool NotEqual( const DynamicTreeAabb& a, const DynamicTreeAabb& b)
{
return (a.m_min != b.m_min || a.m_max != b.m_max);
}
/* NodeType */
struct DynamicTreeNode
{
GM_CLASS_ALLOCATOR(DynamicTreeNode);
DynamicTreeAabb m_volume;
DynamicTreeNode* m_parent;
AZ_FORCE_INLINE bool IsLeaf() const { return(m_childs[1]==0); }
AZ_FORCE_INLINE bool IsInternal() const { return(!IsLeaf()); }
union
{
DynamicTreeNode* m_childs[2];
void* m_data;
int m_dataAsInt;
};
};
}
/**
* Implementation of dynamic aabb tree, based on the bullet dynamic tree (btDbvt).
*
* The BvDynamicTree class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree).
* This BvDynamicTree is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes.
* Unlike the BvTreeQuantized, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure.
*/
class BvDynamicTree
{
public:
using Ptr = AZStd::intrusive_ptr<BvDynamicTree>;
GM_CLASS_ALLOCATOR(BvDynamicTree);
typedef Internal::DynamicTreeAabb VolumeType;
typedef Internal::DynamicTreeNode NodeType;
typedef vector<NodeType*> NodeArrayType;
typedef vector<const NodeType*> ConstNodeArrayType;
private:
/* Stack element */
struct sStkNN
{
const NodeType* a;
const NodeType* b;
sStkNN() {}
sStkNN(const NodeType* na,const NodeType* nb) : a(na), b(nb) {}
};
struct sStkNP
{
const NodeType* node;
int mask;
sStkNP(const NodeType* n, unsigned m) : node(n), mask(m) {}
};
struct sStkNPS
{
const NodeType* node;
int mask;
float value;
sStkNPS() {}
sStkNPS(const NodeType* n, unsigned m, const float v) : node(n), mask(m), value(v) {}
};
struct sStkCLN
{
const NodeType* node;
NodeType* parent;
sStkCLN(const NodeType* n, NodeType* p) : node(n), parent(p) {}
};
public:
/* ICollideCollector templated collectors should implement this functions or inherit from this class */
struct ICollideCollector
{
void Process(const NodeType*, const NodeType*) {}
void Process(const NodeType*) {}
void Process(const NodeType* n, const float) { Process(n); }
bool Descent(const NodeType*) { return true; }
bool AllLeaves(const NodeType*) { return true; }
};
/* IWriter */
struct IWriter
{
virtual ~IWriter() {}
virtual void Prepare(const NodeType* root,int numnodes) = 0;
virtual void WriteNode(const NodeType*, int index, int parent, int child0, int child1) = 0;
virtual void WriteLeaf(const NodeType*, int index, int parent) = 0;
};
/* IClone */
struct IClone
{
virtual ~IClone() {}
virtual void CloneLeaf(NodeType*) {}
};
// Constants
enum
{
SIMPLE_STACKSIZE = 64,
DOUBLE_STACKSIZE = SIMPLE_STACKSIZE * 2
};
// Methods
BvDynamicTree();
~BvDynamicTree();
NodeType* GetRoot() const { return m_root; }
void Clear();
bool Empty() const { return 0 == m_root; }
int GetNumLeaves() const { return m_leaves; }
void OptimizeBottomUp();
void OptimizeTopDown(int bu_treshold = 128);
void OptimizeIncremental(int passes);
NodeType* Insert(const VolumeType& box,void* data);
void Update(NodeType* leaf, int lookahead=-1);
void Update(NodeType* leaf, VolumeType& volume);
bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity, const float margin);
bool Update(NodeType* leaf, VolumeType& volume, const AZ::Vector3& velocity);
bool Update(NodeType* leaf, VolumeType& volume, const float margin);
void Remove(NodeType* leaf);
void Write(IWriter* iwriter) const;
void Clone(BvDynamicTree& dest, IClone* iclone=0) const;
static int GetMaxDepth(const NodeType* node);
static int CountLeaves(const NodeType* node);
static void ExtractLeaves(const NodeType* node, /*btAlignedObjectArray<const NodeType*>&*/vector<const NodeType*>& leaves);
#if DBVT_ENABLE_BENCHMARK
static void Benchmark();
#else
static void Benchmark(){}
#endif
/**
* Collector should inherit from ICollide
*/
template<class Collector>
static inline void enumNodes( const NodeType* root, Collector& collector)
{
collector.Process(root);
if(root->IsInternal())
{
enumNodes(root->m_childs[0],collector);
enumNodes(root->m_childs[1],collector);
}
}
template<class Collector>
static void enumLeaves( const NodeType* root,Collector& collector)
{
if(root->IsInternal())
{
enumLeaves(root->m_childs[0],collector);
enumLeaves(root->m_childs[1],collector);
}
else
{
collector.Process(root);
}
}
template<class Collector>
void collideTT( const NodeType* root0,const NodeType* root1,Collector& collector) const
{
if(root0&&root1)
{
size_t depth=1;
size_t treshold=DOUBLE_STACKSIZE-4;
vector<sStkNN> stkStack;
stkStack.resize(DOUBLE_STACKSIZE);
stkStack[0]=sStkNN(root0,root1);
do {
sStkNN p=stkStack[--depth];
if(depth>treshold)
{
stkStack.resize(stkStack.size()*2);
treshold=stkStack.size()-4;
}
if(p.a==p.b)
{
if(p.a->IsInternal())
{
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]);
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]);
}
}
else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume))
{
if(p.a->IsInternal())
{
if(p.b->IsInternal())
{
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]);
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]);
}
else
{
stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b);
stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b);
}
}
else
{
if(p.b->IsInternal())
{
stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]);
stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]);
}
else
{
collector.Process(p.a,p.b);
}
}
}
} while(depth);
}
}
template<class Collector>
void collideTTpersistentStack( const NodeType* root0, const NodeType* root1,Collector& collector)
{
if(root0&&root1)
{
size_t depth=1;
size_t treshold=DOUBLE_STACKSIZE-4;
m_stkStack.resize(DOUBLE_STACKSIZE);
m_stkStack[0]=sStkNN(root0,root1);
do
{
sStkNN p=m_stkStack[--depth];
if(depth>treshold)
{
m_stkStack.resize(m_stkStack.size()*2);
treshold=m_stkStack.size()-4;
}
if(p.a==p.b)
{
if(p.a->IsInternal())
{
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.a->m_childs[1]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.a->m_childs[1]);
}
}
else if(IntersectAabbAabb(p.a->m_volume,p.b->m_volume))
{
if(p.a->IsInternal())
{
if(p.b->IsInternal())
{
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b->m_childs[1]);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b->m_childs[1]);
}
else
{
m_stkStack[depth++]=sStkNN(p.a->m_childs[0],p.b);
m_stkStack[depth++]=sStkNN(p.a->m_childs[1],p.b);
}
}
else
{
if(p.b->IsInternal())
{
m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[0]);
m_stkStack[depth++]=sStkNN(p.a,p.b->m_childs[1]);
}
else
{
collector.Process(p.a,p.b);
}
}
}
} while(depth);
}
}
template<class Collector>
void collideTV( const NodeType* root, const VolumeType& volume, Collector& collector) const
{
if(root)
{
// ATTRIBUTE_ALIGNED16(VolumeType) volume(vol);
// btAlignedObjectArray<const NodeType*> stack;
AZStd::fixed_vector<const NodeType*,SIMPLE_STACKSIZE> stack;
//stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(root);
do {
const NodeType* n=stack[stack.size()-1];
stack.pop_back();
if(IntersectAabbAabb(n->m_volume,volume))
{
if(n->IsInternal())
{
stack.push_back(n->m_childs[0]);
stack.push_back(n->m_childs[1]);
}
else
{
collector.Process(n);
}
}
} while(!stack.empty());
}
}
template<class Collector>
void collideTP(const NodeType* root, const AZ::Plane& plane, Collector& collector) const
{
if (root)
{
AZStd::fixed_vector<const NodeType*,SIMPLE_STACKSIZE> stack;
stack.push_back(root);
do
{
const NodeType* n=stack[stack.size()-1];
stack.pop_back();
if (IntersectAabbPlane(n->m_volume, plane))
{
if(n->IsInternal())
{
stack.push_back(n->m_childs[0]);
stack.push_back(n->m_childs[1]);
}
else
{
collector.Process(n);
}
}
} while (!stack.empty());
}
}
///rayTest is a re-entrant ray test, and can be called in parallel as long as the btAlignedAlloc is thread-safe (uses locking etc)
///rayTest is slower than rayTestInternal, because it builds a local stack, using memory allocations, and it recomputes signs/rayDirectionInverses each time
template<class Collector>
static void rayTest( const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, Collector& collector)
{
if(root)
{
AZ::Vector3 ray = rayTo-rayFrom;
AZ::Vector3 rayDir = ray.GetNormalized();
///what about division by zero? --> just set rayDirection[i] to INF/1e30
AZ::Vector3 rayDirectionInverse = AZ::Vector3::CreateSelectCmpEqual(rayDir,AZ::Vector3::CreateZero(),AZ::Vector3(1e30),rayDir.GetReciprocal());
unsigned int signs[3];// = { rayDirectionInverse[0] < 0.0f, rayDirectionInverse[1] < 0.0f, rayDirectionInverse[2] < 0.0f };
signs[0] = rayDirectionInverse.GetX() < 0.0f;
signs[1] = rayDirectionInverse.GetY() < 0.0f;
signs[2] = rayDirectionInverse.GetZ() < 0.0f;
//float lambda_max = rayDir.Dot(ray);
AZ::Vector3 resultNormal;
//btAlignedObjectArray<const NodeType*> stack;
vector<const NodeType*> stack;
int depth=1;
int treshold=DOUBLE_STACKSIZE-2;
stack.resize(DOUBLE_STACKSIZE);
stack[0]=root;
AZ::Vector3 bounds[2];
do {
const NodeType* node=stack[--depth];
bounds[0] = node->m_volume.GetMin();
bounds[1] = node->m_volume.GetMax();
//float tmin = 1.0f;
//float lambda_min = 0.0f;
// todo..
unsigned int result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/0;
#ifdef COMPARE_BTRAY_AABB2
float param = 1.0f;
bool result2 = /*btRayAabb(rayFrom,rayTo,node->volume.GetMin(),node->volume.GetMax(),param,resultNormal)*/0;
AZ_Assert(result1 == result2, "");
#endif //TEST_BTRAY_AABB2
if(result1)
{
if(node->IsInternal())
{
if(depth>treshold)
{
stack.resize(stack.size()*2);
treshold=stack.size()-2;
}
stack[depth++]=node->m_childs[0];
stack[depth++]=node->m_childs[1];
}
else
{
collector.Process(node);
}
}
} while(depth);
}
}
///rayTestInternal is faster than rayTest, because it uses a persistent stack (to reduce dynamic memory allocations to a minimum) and it uses precomputed signs/rayInverseDirections
///rayTestInternal is used by btDbvtBroadphase to accelerate world ray casts
template<class Collector>
void rayTestInternal(const NodeType* root, const AZ::Vector3& rayFrom, const AZ::Vector3& rayTo, const AZ::Vector3& rayDirectionInverse, unsigned int signs[3], const float lambda_max, const AZ::Vector3& aabbMin, const AZ::Vector3& aabbMax, Collector& collector) const
{
(void)rayFrom;(void)rayTo;(void)rayDirectionInverse;(void)signs;(void)lambda_max;
if(root)
{
AZ::Vector3 resultNormal;
int depth=1;
int treshold=DOUBLE_STACKSIZE-2;
vector<const NodeType*> stack;
stack.resize(DOUBLE_STACKSIZE);
stack[0]=root;
AZ::Vector3 bounds[2];
do
{
const NodeType* node=stack[--depth];
bounds[0] = node->m_volume.GetMin()+aabbMin;
bounds[1] = node->m_volume.GetMax()+aabbMax;
//float tmin = 1.0f;
//float lambda_min = 0.0f;
unsigned int result1=false;
// todo...
result1 = /*btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max)*/false;
if(result1)
{
if(node->IsInternal())
{
if(depth>treshold)
{
stack.resize(stack.size()*2);
treshold=stack.size()-2;
}
stack[depth++]=node->m_childs[0];
stack[depth++]=node->m_childs[1];
}
else
{
collector.Process(node);
}
}
} while(depth);
}
}
template<class Collector>
static void collideKDOP(const NodeType* root, const AZ::Vector3* normals, const float* offsets, int count, Collector& collector)
{
(void)root;(void)normals;(void)offsets;(void)count;(void)collector;
/* if(root)
{
const int inside=(1<<count)-1;
btAlignedObjectArray<sStkNP> stack;
int signs[sizeof(unsigned)*8];
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
for(int i=0;i<count;++i)
{
signs[i]= ((normals[i].x()>=0)?1:0)+
((normals[i].y()>=0)?2:0)+
((normals[i].z()>=0)?4:0);
}
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(sStkNP(root,0));
do {
sStkNP se=stack[stack.size()-1];
bool out=false;
stack.pop_back();
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
{
if(0==(se.mask&j))
{
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
switch(side)
{
case -1: out=true;break;
case +1: se.mask|=j;break;
}
}
}
if(!out)
{
if((se.mask!=inside)&&(se.node->isinternal()))
{
stack.push_back(sStkNP(se.node->childs[0],se.mask));
stack.push_back(sStkNP(se.node->childs[1],se.mask));
}
else
{
if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy);
}
}
} while(!stack.empty());
}*/
}
template<class Collector>
static void collideOCL( const NodeType* root, const AZ::Vector3* normals, const float* offsets, const AZ::Vector3& sortaxis, int count, Collector& collector, bool fullsort=true)
{
(void)root;(void)normals;(void)offsets;(void)sortaxis;(void)count;(void)offsets;(void)collector;(void)fullsort;
/* if(root)
{
const unsigned srtsgns=(sortaxis[0]>=0?1:0)+
(sortaxis[1]>=0?2:0)+
(sortaxis[2]>=0?4:0);
const int inside=(1<<count)-1;
btAlignedObjectArray<sStkNPS> stock;
btAlignedObjectArray<int> ifree;
btAlignedObjectArray<int> stack;
int signs[sizeof(unsigned)*8];
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
for(int i=0;i<count;++i)
{
signs[i]= ((normals[i].x()>=0)?1:0)+
((normals[i].y()>=0)?2:0)+
((normals[i].z()>=0)?4:0);
}
stock.reserve(SIMPLE_STACKSIZE);
stack.reserve(SIMPLE_STACKSIZE);
ifree.reserve(SIMPLE_STACKSIZE);
stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns))));
do {
const int id=stack[stack.size()-1];
sStkNPS se=stock[id];
stack.pop_back();ifree.push_back(id);
if(se.mask!=inside)
{
bool out=false;
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
{
if(0==(se.mask&j))
{
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
switch(side)
{
case -1: out=true;break;
case +1: se.mask|=j;break;
}
}
}
if(out) continue;
}
if(policy.Descent(se.node))
{
if(se.node->isinternal())
{
const NodeType* pns[]={ se.node->childs[0],se.node->childs[1]};
sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)),
sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))};
const int q=nes[0].value<nes[1].value?1:0;
int j=stack.size();
if(fsort&&(j>0))
{
// Insert 0
j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size());
stack.push_back(0);
#if DBVT_USE_MEMMOVE
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
#else
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
#endif
stack[j]=allocate(ifree,stock,nes[q]);
// Insert 1
j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size());
stack.push_back(0);
#if DBVT_USE_MEMMOVE
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
#else
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
#endif
stack[j]=allocate(ifree,stock,nes[1-q]);
}
else
{
stack.push_back(allocate(ifree,stock,nes[q]));
stack.push_back(allocate(ifree,stock,nes[1-q]));
}
}
else
{
policy.Process(se.node,se.value);
}
}
} while(stack.size());
}*/
}
template<class Collector>
static void collideTU(const NodeType* root, Collector& collector)
{
(void)root;(void)collector;
/* if(root)
{
btAlignedObjectArray<const NodeType*> stack;
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(root);
do {
const NodeType* n=stack[stack.size()-1];
stack.pop_back();
if(policy.Descent(n))
{
if(n->isinternal())
{ stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); }
else
{ policy.Process(n); }
}
} while(stack.size()>0);
}*/
}
private:
BvDynamicTree(const BvDynamicTree&) {}
// Helpers
//static AZ_FORCE_INLINE int nearest(const int* i,const BvDynamicTree::sStkNPS* a,const float& v,int l,int h)
//{
// int m=0;
// while(l<h)
// {
// m=(l+h)>>1;
// if(a[i[m]].value>=v) l=m+1; else h=m;
// }
// return h;
//}
//static AZ_FORCE_INLINE int allocate( int_fixed_stack_type& ifree, stknps_fixed_stack_type& stock, const sStkNPS& value)
//{
// int i;
// if( !ifree.empty() )
// {
// i=ifree[ifree.size()-1];
// ifree.pop_back();
// stock[i]=value;
// }
// else
// {
// i=stock.size();
// stock.push_back(value);
// }
// return i;
//}
//
AZ_FORCE_INLINE void deletenode( NodeType* node)
{
//btAlignedFree(pdbvt->m_free);
delete m_free;
m_free=node;
}
void recursedeletenode( NodeType* node)
{
if(!node->IsLeaf())
{
recursedeletenode(node->m_childs[0]);
recursedeletenode(node->m_childs[1]);
}
if( node == m_root ) m_root=0;
deletenode(node);
}
AZ_FORCE_INLINE NodeType* createnode( NodeType* parent, void* data)
{
NodeType* node;
if(m_free)
{ node=m_free;m_free=0; }
else
{ node = aznew NodeType(); }
node->m_parent = parent;
node->m_data = data;
node->m_childs[1] = 0;
return node;
}
AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume, void* data)
{
NodeType* node = createnode(parent,data);
node->m_volume=volume;
return node;
}
//
AZ_FORCE_INLINE NodeType* createnode( BvDynamicTree::NodeType* parent, const VolumeType& volume0, const VolumeType& volume1, void* data)
{
NodeType* node = createnode(parent,data);
Merge(volume0,volume1,node->m_volume);
return node;
}
void insertleaf( NodeType* root, NodeType* leaf);
NodeType* removeleaf( NodeType* leaf);
void fetchleaves(NodeType* root,NodeArrayType& leaves,int depth=-1);
void split(const NodeArrayType& leaves,NodeArrayType& left,NodeArrayType& right,const AZ::Vector3& org,const AZ::Vector3& axis);
VolumeType bounds(const NodeArrayType& leaves);
void bottomup( NodeArrayType& leaves );
NodeType* topdown(NodeArrayType& leaves,int bu_treshold);
AZ_FORCE_INLINE NodeType* sort(NodeType* n,NodeType*& r);
NodeType* m_root;
NodeType* m_free;
int m_lkhd;
int m_leaves;
unsigned m_opath;
//btAlignedObjectArray<sStkNN> m_stkStack;
// Profile and choose static or dynamic vector.
typedef AZStd::fixed_vector<sStkNN,DOUBLE_STACKSIZE> stknn_fixed_stack_type;
typedef AZStd::fixed_vector<int,SIMPLE_STACKSIZE> int_fixed_stack_type;
typedef AZStd::fixed_vector<sStkNPS,SIMPLE_STACKSIZE> stknps_fixed_stack_type;
stknn_fixed_stack_type m_stkStack;
};
}
#endif // RR_DYNAMIC_BOUNDING_VOLUME_TREE_H
#pragma once

@ -1,597 +0,0 @@
/*
* 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 <GridMate/Replica/Interest/ProximityInterestHandler.h>
#include <GridMate/Replica/Replica.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <GridMate/Replica/ReplicaMgr.h>
#include <GridMate/Replica/Interest/InterestManager.h>
#include <GridMate/Replica/Interest/BvDynamicTree.h>
// for highly verbose internal debugging
//#define INTERNAL_DEBUG_PROXIMITY
namespace GridMate
{
void ProximityInterestChunk::OnReplicaActivate(const ReplicaContext& rc)
{
m_interestHandler = static_cast<ProximityInterestHandler*>(rc.m_rm->GetUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4)));
AZ_Warning("GridMate", m_interestHandler, "No proximity interest handler in the user context");
if (m_interestHandler)
{
m_interestHandler->OnNewRulesChunk(this, rc.m_peer);
}
}
void ProximityInterestChunk::OnReplicaDeactivate(const ReplicaContext& rc)
{
if (rc.m_peer && m_interestHandler)
{
m_interestHandler->OnDeleteRulesChunk(this, rc.m_peer);
}
}
bool ProximityInterestChunk::AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx)
{
if (IsProxy())
{
auto rulePtr = m_interestHandler->CreateRule(ctx.m_sourcePeer);
rulePtr->Set(bbox);
m_rules.insert(AZStd::make_pair(netId, rulePtr));
}
return true;
}
bool ProximityInterestChunk::RemoveRuleFn(RuleNetworkId netId, const RpcContext&)
{
if (IsProxy())
{
m_rules.erase(netId);
}
return true;
}
bool ProximityInterestChunk::UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&)
{
if (IsProxy())
{
auto it = m_rules.find(netId);
if (it != m_rules.end())
{
it->second->Set(bbox);
}
}
return true;
}
bool ProximityInterestChunk::AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&)
{
ProximityInterestChunk* peerChunk = m_interestHandler->FindRulesChunkByPeerId(peerId);
if (peerChunk)
{
auto it = peerChunk->m_rules.find(netId);
if (it == peerChunk->m_rules.end())
{
auto rulePtr = m_interestHandler->CreateRule(peerId);
peerChunk->m_rules.insert(AZStd::make_pair(netId, rulePtr));
rulePtr->Set(bbox);
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterest
*/
ProximityInterest::ProximityInterest(ProximityInterestHandler* handler)
: m_handler(handler)
, m_bbox(AZ::Aabb::CreateNull())
{
AZ_Assert(m_handler, "Invalid interest handler");
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestRule
*/
void ProximityInterestRule::Set(const AZ::Aabb& bbox)
{
m_bbox = bbox;
m_handler->UpdateRule(this);
}
void ProximityInterestRule::Destroy()
{
m_handler->DestroyRule(this);
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestAttribute
*/
void ProximityInterestAttribute::Set(const AZ::Aabb& bbox)
{
m_bbox = bbox;
m_handler->UpdateAttribute(this);
}
void ProximityInterestAttribute::Destroy()
{
m_handler->DestroyAttribute(this);
}
///////////////////////////////////////////////////////////////////////////
/*
* ProximityInterestHandler
*/
ProximityInterestHandler::ProximityInterestHandler()
: m_im(nullptr)
, m_rm(nullptr)
, m_lastRuleNetId(0)
, m_rulesReplica(nullptr)
{
m_attributeWorld = AZStd::make_unique<SpatialIndex>();
AZ_Assert(m_attributeWorld, "Out of memory");
}
ProximityInterestRule::Ptr ProximityInterestHandler::CreateRule(PeerId peerId)
{
ProximityInterestRule* rulePtr = aznew ProximityInterestRule(this, peerId, GetNewRuleNetId());
if (m_rm && peerId == m_rm->GetLocalPeerId())
{
m_rulesReplica->AddRuleRpc(rulePtr->GetNetworkId(), rulePtr->Get());
}
CreateAndInsertIntoSpatialStructure(rulePtr);
return rulePtr;
}
ProximityInterestAttribute::Ptr ProximityInterestHandler::CreateAttribute(ReplicaId replicaId)
{
auto newAttribute = aznew ProximityInterestAttribute(this, replicaId);
AZ_Assert(newAttribute, "Out of memory");
CreateAndInsertIntoSpatialStructure(newAttribute);
return newAttribute;
}
void ProximityInterestHandler::FreeRule(ProximityInterestRule* rule)
{
//TODO: should be pool-allocated
delete rule;
}
void ProximityInterestHandler::DestroyRule(ProximityInterestRule* rule)
{
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId())
{
m_rulesReplica->RemoveRuleRpc(rule->GetNetworkId());
}
MarkAttributesDirtyInRule(rule);
rule->m_bbox = AZ::Aabb::CreateNull();
m_removedRules.insert(rule);
m_localRules.erase(rule);
}
void ProximityInterestHandler::UpdateRule(ProximityInterestRule* rule)
{
if (m_rm && rule->GetPeerId() == m_rm->GetLocalPeerId())
{
m_rulesReplica->UpdateRuleRpc(rule->GetNetworkId(), rule->Get());
}
m_dirtyRules.insert(rule);
}
void ProximityInterestHandler::FreeAttribute(ProximityInterestAttribute* attrib)
{
delete attrib;
}
void ProximityInterestHandler::DestroyAttribute(ProximityInterestAttribute* attrib)
{
RemoveFromSpatialStructure(attrib);
m_attributes.erase(attrib);
m_removedAttributes.insert(attrib);
}
void ProximityInterestHandler::RemoveFromSpatialStructure(ProximityInterestAttribute* attribute)
{
attribute->m_bbox = AZ::Aabb::CreateNull();
m_attributeWorld->Remove(attribute->GetNode());
attribute->SetNode(nullptr);
}
void ProximityInterestHandler::UpdateAttribute(ProximityInterestAttribute* attrib)
{
auto node = attrib->GetNode();
AZ_Assert(node, "Attribute wasn't created correctly");
node->m_volume = attrib->Get();
m_attributeWorld->Update(node);
m_dirtyAttributes.insert(attrib);
}
void ProximityInterestHandler::OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
{
if (chunk != m_rulesReplica) // non-local
{
m_peerChunks.insert(AZStd::make_pair(peer->GetId(), chunk));
for (auto& rule : m_localRules)
{
chunk->AddRuleForPeerRpc(rule->GetNetworkId(), rule->GetPeerId(), rule->Get());
}
}
}
void ProximityInterestHandler::OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer)
{
(void)chunk;
m_peerChunks.erase(peer->GetId());
}
RuleNetworkId ProximityInterestHandler::GetNewRuleNetId()
{
++m_lastRuleNetId;
if (m_rulesReplica)
{
return m_rulesReplica->GetReplicaId() | (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
}
return (static_cast<AZ::u64>(m_lastRuleNetId) << 32);
}
ProximityInterestChunk* ProximityInterestHandler::FindRulesChunkByPeerId(PeerId peerId)
{
auto it = m_peerChunks.find(peerId);
if (it == m_peerChunks.end())
{
return nullptr;
}
return it->second;
}
const InterestMatchResult& ProximityInterestHandler::GetLastResult()
{
return m_resultCache;
}
ProximityInterestHandler::RuleSet& ProximityInterestHandler::GetAffectedRules()
{
/*
* The expectation that lots of attributes will change frequently,
* so there is no point in trying to optimize cases
* where only a few attributes have changed.
*/
if (m_dirtyAttributes.empty() && !m_dirtyRules.empty())
{
return m_dirtyRules;
}
/*
* Assuming all rules might have been affected.
*
* There is an optimization chance here if the number of rules is large, as in 1,000+ rules.
* To handle such scale we would need another spatial structure for rules.
*/
return m_localRules;
}
void ProximityInterestHandler::GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes)
{
m_attributeWorld->Query(rule->Get(), nodes);
}
void ProximityInterestHandler::ClearDirtyState()
{
m_dirtyAttributes.clear();
m_dirtyRules.clear();
}
void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute)
{
m_attributes.insert(attribute);
SpatialIndex::Node* node = m_attributeWorld->Insert(attribute->Get(), attribute);
attribute->SetNode(node);
}
void ProximityInterestHandler::CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule)
{
m_localRules.insert(rule);
}
void ProximityInterestHandler::UpdateInternal(InterestMatchResult& result)
{
/*
* The goal is to return all dirty attributes that were either dirty because:
* 1) they changed which rules have apply to
* 2) rules have changed and no longer apply to those attributes
* and thus resulted in different peer(s) associated with a given replica.
*/
const RuleSet& rules = GetAffectedRules();
for (auto& dirtyAttribute : m_dirtyAttributes)
{
result.insert(dirtyAttribute->GetReplicaId());
}
/*
* The exectation is to have a lot more attributes than rules.
* The amount of rules should grow linear with amount of peers,
* so it should be OK to iterate through all rules each update.
*/
for (auto& rule : rules)
{
CheckChangesForRule(rule, result);
}
for (auto& removedRule : m_removedRules)
{
FreeRule(removedRule);
}
m_removedRules.clear();
// mark removed attribute as having no peers
for (auto& removedAttribute : m_removedAttributes)
{
result.insert(removedAttribute->GetReplicaId());
FreeAttribute(removedAttribute);
}
m_removedAttributes.clear();
}
void ProximityInterestHandler::CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result)
{
SpatialIndex::NodeCollector collector;
GetAttributesWithinRule(rule, collector);
auto peerId = rule->GetPeerId();
for (ProximityInterestAttribute* attr : collector.GetNodes())
{
AZ_Assert(attr, "bad node?");
auto findIt = result.find(attr->GetReplicaId());
if (findIt != result.end())
{
findIt->second.insert(peerId);
}
else
{
auto resultIt = result.insert(attr->GetReplicaId());
AZ_Assert(resultIt.second, "Successfully inserted");
resultIt.first->second.insert(peerId);
}
}
}
void ProximityInterestHandler::MarkAttributesDirtyInRule(ProximityInterestRule* rule)
{
SpatialIndex::NodeCollector collector;
GetAttributesWithinRule(rule, collector);
for (ProximityInterestAttribute* attr : collector.GetNodes())
{
AZ_Assert(attr, "bad node?");
UpdateAttribute(attr);
}
}
void ProximityInterestHandler::ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after)
{
m_resultCache.clear();
#if defined(INTERNAL_DEBUG_PROXIMITY)
before.PrintMatchResult("before");
after.PrintMatchResult("after");
#endif
/*
* 'after' contains only the stuff that might have changed
*/
for (auto& possiblyDirty : after)
{
ReplicaId repId = possiblyDirty.first;
const InterestPeerSet& peerSet = possiblyDirty.second;
auto foundInBefore = before.find(repId);
if (foundInBefore != before.end())
{
if (!HasSamePeers(foundInBefore->second, peerSet))
{
// was in the last calculation but has a different peer set now
m_resultCache.insert(AZStd::make_pair(repId, peerSet));
}
}
else
{
// since it wasn't present during last calculation
m_resultCache.insert(AZStd::make_pair(repId, peerSet));
}
}
// Mark attributes (replicas) for removal that have not moved but a rule (clients) no longer sees it
for (auto& possiblyDirty : before)
{
ReplicaId repId = possiblyDirty.first;
const auto foundInAfter = after.find(repId);
/*
* If the prior state was a replica A present on peer X: "A{X}", and now A should no longer be present on any peer: "A{}"
* then by the rules of InterestHandlers interacting with InterestManager, we should return in @m_resultCache the following:
*
* A{} - indicating that replica A must be removed all peers.
*
* On the next pass, the prior state would be: "A{}" and the current state would be "A{}" as well. At that point, we have
* already sent the update to remove A from X, so @m_resultCache should no longer mention A at all.
*/
if (foundInAfter == after.end() && !possiblyDirty.second.empty() /* "not A{}" see the above comment */)
{
m_resultCache.insert(AZStd::make_pair(repId, InterestPeerSet()));
}
}
#if defined(INTERNAL_DEBUG_PROXIMITY)
m_resultCache.PrintMatchResult("changes");
#endif
}
bool ProximityInterestHandler::HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another)
{
if (one.size() != another.size())
{
return false;
}
for (auto& peerFromOne : one)
{
if (another.find(peerFromOne) == another.end())
{
return false;
}
}
// Safe to assume it's the same sets since all entries are unique in a peer sets
return true;
}
void ProximityInterestHandler::Update()
{
InterestMatchResult newResult;
UpdateInternal(newResult);
ProduceChanges(m_lastResult, newResult);
m_lastResult = std::move(newResult);
ClearDirtyState();
}
void ProximityInterestHandler::OnRulesHandlerRegistered(InterestManager* manager)
{
AZ_Assert(m_im == nullptr, "Handler is already registered with manager %p (%p)\n", m_im, manager);
AZ_Assert(m_rulesReplica == nullptr, "Rules replica is already created\n");
AZ_TracePrintf("GridMate", "Proximity interest handler is registered\n");
m_im = manager;
m_rm = m_im->GetReplicaManager();
m_rm->RegisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4), this);
auto replica = Replica::CreateReplica("ProximityInterestHandlerRules");
m_rulesReplica = CreateAndAttachReplicaChunk<ProximityInterestChunk>(replica);
m_rm->AddPrimary(replica);
}
void ProximityInterestHandler::OnRulesHandlerUnregistered(InterestManager* manager)
{
(void)manager;
AZ_Assert(m_im == manager, "Handler was not registered with manager %p (%p)\n", manager, m_im);
AZ_TracePrintf("GridMate", "Proximity interest handler is unregistered\n");
m_rulesReplica = nullptr;
m_im = nullptr;
m_rm->UnregisterUserContext(AZ_CRC("ProximityInterestHandler", 0x3a90b3e4));
m_rm = nullptr;
for (auto& chunk : m_peerChunks)
{
chunk.second->m_interestHandler = nullptr;
}
m_peerChunks.clear();
ClearDirtyState();
DestroyAll();
m_resultCache.clear();
}
void ProximityInterestHandler::DestroyAll()
{
for (ProximityInterestRule* rule : m_localRules)
{
FreeRule(rule);
}
m_localRules.clear();
for (ProximityInterestAttribute* attr : m_attributes)
{
FreeAttribute(attr);
}
m_attributes.clear();
for (auto& removedRule : m_removedRules)
{
FreeRule(removedRule);
}
m_removedRules.clear();
for (auto& removedAttribute : m_removedAttributes)
{
FreeAttribute(removedAttribute);
}
m_removedAttributes.clear();
}
///////////////////////////////////////////////////////////////////////////
ProximityInterestHandler::~ProximityInterestHandler()
{
/*
* If a handler was registered with a InterestManager, then InterestManager ought to have called OnRulesHandlerUnregistered
* but this is a safety pre-caution.
*/
DestroyAll();
}
SpatialIndex::SpatialIndex()
{
m_tree.reset(aznew GridMate::BvDynamicTree());
}
void SpatialIndex::Remove(Node* node)
{
m_tree->Remove(node);
}
void SpatialIndex::Update(Node* node)
{
m_tree->Update(node);
}
SpatialIndex::Node* SpatialIndex::Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute)
{
return m_tree->Insert(get, attribute);
}
void SpatialIndex::Query(const AZ::Aabb& shape, NodeCollector& nodes)
{
m_tree->collideTV(m_tree->GetRoot(), shape, nodes);
}
}

@ -1,314 +0,0 @@
/*
* 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.
*
*/
#ifndef GM_REPLICA_PROXIMITYINTERESTHANDLER_H
#define GM_REPLICA_PROXIMITYINTERESTHANDLER_H
#include <GridMate/Replica/RemoteProcedureCall.h>
#include <GridMate/Replica/ReplicaChunk.h>
#include <GridMate/Replica/Interest/RulesHandler.h>
#include <GridMate/Replica/Interest/BvDynamicTree.h>
#include <GridMate/Serialize/UtilityMarshal.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace GridMate
{
class ProximityInterestHandler;
class ProximityInterestAttribute;
/*
* Base interest
*/
class ProximityInterest
{
friend class ProximityInterestHandler;
public:
const AZ::Aabb& Get() const { return m_bbox; }
protected:
explicit ProximityInterest(ProximityInterestHandler* handler);
ProximityInterestHandler* m_handler;
AZ::Aabb m_bbox;
};
///////////////////////////////////////////////////////////////////////////
/*
* Proximity rule
*/
class ProximityInterestRule
: public InterestRule
, public ProximityInterest
{
friend class ProximityInterestHandler;
public:
using Ptr = AZStd::intrusive_ptr<ProximityInterestRule>;
GM_CLASS_ALLOCATOR(ProximityInterestRule);
void Set(const AZ::Aabb& bbox);
private:
// Intrusive ptr
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release() { --m_refCount; if (!m_refCount) Destroy(); }
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
///////////////////////////////////////////////////////////////////////////
ProximityInterestRule(ProximityInterestHandler* handler, PeerId peerId, RuleNetworkId netId)
: InterestRule(peerId, netId)
, ProximityInterest(handler)
{}
void Destroy();
};
///////////////////////////////////////////////////////////////////////////
class SpatialIndex
{
public:
typedef Internal::DynamicTreeNode Node;
class NodeCollector
{
typedef AZStd::vector<ProximityInterestAttribute*> Type;
public:
void Process(const Internal::DynamicTreeNode* node)
{
m_nodes.push_back(reinterpret_cast<ProximityInterestAttribute*>(node->m_data));
}
const Type& GetNodes() const
{
return m_nodes;
}
private:
Type m_nodes;
};
SpatialIndex();
~SpatialIndex() = default;
AZ_FORCE_INLINE void Remove(Node* node);
AZ_FORCE_INLINE void Update(Node* node);
AZ_FORCE_INLINE Node* Insert(const AZ::Aabb& get, ProximityInterestAttribute* attribute);
AZ_FORCE_INLINE void Query(const AZ::Aabb& get, NodeCollector& nodes);
private:
AZStd::unique_ptr<BvDynamicTree> m_tree;
};
/*
* Proximity attribute
*/
class ProximityInterestAttribute
: public InterestAttribute
, public ProximityInterest
{
friend class ProximityInterestHandler;
template<class T> friend class InterestPtr;
public:
using Ptr = AZStd::intrusive_ptr<ProximityInterestAttribute>;
GM_CLASS_ALLOCATOR(ProximityInterestAttribute);
void Set(const AZ::Aabb& bbox);
private:
// Intrusive ptr
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release() { Destroy(); }
AZ_FORCE_INLINE bool IsDeleted() const { return m_refCount == 0; }
///////////////////////////////////////////////////////////////////////////
ProximityInterestAttribute(ProximityInterestHandler* handler, ReplicaId repId)
: InterestAttribute(repId)
, ProximityInterest(handler)
, m_worldNode(nullptr)
{}
void Destroy();
void SetNode(SpatialIndex::Node* node) { m_worldNode = node; }
SpatialIndex::Node* GetNode() const { return m_worldNode; }
SpatialIndex::Node* m_worldNode; ///< non-owning pointer
};
///////////////////////////////////////////////////////////////////////////
class ProximityInterestChunk
: public ReplicaChunk
{
public:
GM_CLASS_ALLOCATOR(ProximityInterestChunk);
// ReplicaChunk
typedef AZStd::intrusive_ptr<ProximityInterestChunk> Ptr;
bool IsReplicaMigratable() override { return false; }
bool IsBroadcast() override { return true; }
static const char* GetChunkName() { return "ProximityInterestChunk"; }
ProximityInterestChunk()
: AddRuleRpc("AddRule")
, RemoveRuleRpc("RemoveRule")
, UpdateRuleRpc("UpdateRule")
, AddRuleForPeerRpc("AddRuleForPeerRpc")
, m_interestHandler(nullptr)
{
}
void OnReplicaActivate(const ReplicaContext& rc) override;
void OnReplicaDeactivate(const ReplicaContext& rc) override;
bool AddRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext& ctx);
bool RemoveRuleFn(RuleNetworkId netId, const RpcContext&);
bool UpdateRuleFn(RuleNetworkId netId, AZ::Aabb bbox, const RpcContext&);
bool AddRuleForPeerFn(RuleNetworkId netId, PeerId peerId, AZ::Aabb bbox, const RpcContext&);
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleFn> AddRuleRpc;
Rpc<RpcArg<RuleNetworkId>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::RemoveRuleFn> RemoveRuleRpc;
Rpc<RpcArg<RuleNetworkId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::UpdateRuleFn> UpdateRuleRpc;
Rpc<RpcArg<RuleNetworkId>, RpcArg<PeerId>, RpcArg<AZ::Aabb>>::BindInterface<ProximityInterestChunk, &ProximityInterestChunk::AddRuleForPeerFn> AddRuleForPeerRpc;
unordered_map<RuleNetworkId, ProximityInterestRule::Ptr> m_rules;
ProximityInterestHandler* m_interestHandler;
};
/*
* Rules handler
*/
class ProximityInterestHandler
: public BaseRulesHandler
{
friend class ProximityInterestRule;
friend class ProximityInterestAttribute;
friend class ProximityInterestChunk;
public:
typedef unordered_set<ProximityInterestAttribute*> AttributeSet;
typedef unordered_set<ProximityInterestRule*> RuleSet;
GM_CLASS_ALLOCATOR(ProximityInterestHandler);
ProximityInterestHandler();
~ProximityInterestHandler();
/*
* Creates new proximity rule and binds it to the peer.
* Note: the lifetime of the created rule is tied to the lifetime of this handler.
*/
ProximityInterestRule::Ptr CreateRule(PeerId peerId);
/*
* Creates new proximity attribute and binds it to the replica.
* Note: the lifetime of the created attribute is tied to the lifetime of this handler.
*/
ProximityInterestAttribute::Ptr CreateAttribute(ReplicaId replicaId);
// Calculates rules and attributes matches
void Update() override;
// Returns last recalculated results
const InterestMatchResult& GetLastResult() override;
// Returns the manager it's bound to
InterestManager* GetManager() override { return m_im; }
// Rules that this handler is aware of
const RuleSet& GetLocalRules() const { return m_localRules; }
private:
// BaseRulesHandler
void OnRulesHandlerRegistered(InterestManager* manager) override;
void OnRulesHandlerUnregistered(InterestManager* manager) override;
void DestroyRule(ProximityInterestRule* rule);
void FreeRule(ProximityInterestRule* rule);
void UpdateRule(ProximityInterestRule* rule);
void DestroyAttribute(ProximityInterestAttribute* attrib);
void FreeAttribute(ProximityInterestAttribute* attrib);
void UpdateAttribute(ProximityInterestAttribute* attrib);
void OnNewRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
void OnDeleteRulesChunk(ProximityInterestChunk* chunk, ReplicaPeer* peer);
RuleNetworkId GetNewRuleNetId();
ProximityInterestChunk* FindRulesChunkByPeerId(PeerId peerId);
void DestroyAll();
InterestManager* m_im;
ReplicaManager* m_rm;
AZ::u32 m_lastRuleNetId;
unordered_map<PeerId, ProximityInterestChunk*> m_peerChunks;
RuleSet m_localRules;
RuleSet m_removedRules;
RuleSet m_dirtyRules;
AttributeSet m_attributes;
AttributeSet m_removedAttributes;
AttributeSet m_dirtyAttributes;
ProximityInterestChunk* m_rulesReplica;
// collection of all known attributes
AZStd::unique_ptr<SpatialIndex> m_attributeWorld;
InterestMatchResult m_resultCache;
///////////////////////////////////////////////////////////////////////////////////////////////////
// internal processing helpers
AZ_FORCE_INLINE RuleSet& GetAffectedRules();
AZ_FORCE_INLINE void GetAttributesWithinRule(ProximityInterestRule* rule, SpatialIndex::NodeCollector& nodes);
AZ_FORCE_INLINE void ClearDirtyState();
AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestAttribute* attribute);
AZ_FORCE_INLINE void RemoveFromSpatialStructure(ProximityInterestAttribute* attribute);
AZ_FORCE_INLINE void CreateAndInsertIntoSpatialStructure(ProximityInterestRule* rule);
void UpdateInternal(InterestMatchResult& result);
void CheckChangesForRule(ProximityInterestRule* rule, InterestMatchResult& result);
void MarkAttributesDirtyInRule(ProximityInterestRule* rule);
static bool HasSamePeers(const InterestPeerSet& one, const InterestPeerSet& another);
void ProduceChanges(const InterestMatchResult& before, const InterestMatchResult& after);
InterestMatchResult m_lastResult;
///////////////////////////////////////////////////////////////////////////////////////////////////
};
///////////////////////////////////////////////////////////////////////////
}
#endif // GM_REPLICA_PROXIMITYINTERESTHANDLER_H

@ -100,14 +100,10 @@ set(FILES
Replica/Tasks/ReplicaPriorityPolicy.h
Replica/Interest/BitmaskInterestHandler.cpp
Replica/Interest/BitmaskInterestHandler.h
Replica/Interest/ProximityInterestHandler.cpp
Replica/Interest/ProximityInterestHandler.h
Replica/Interest/InterestDefs.h
Replica/Interest/InterestManager.cpp
Replica/Interest/InterestManager.h
Replica/Interest/InterestQueryResult.h
Replica/Interest/BvDynamicTree.cpp
Replica/Interest/BvDynamicTree.h
Replica/Interest/RulesHandler.h
Serialize/Buffer.cpp
Serialize/Buffer.h

File diff suppressed because it is too large Load Diff

@ -24,5 +24,4 @@ set(FILES
StreamSocketDriverTests.cpp
CarrierStreamSocketDriverTests.cpp
Carrier.cpp
Interest.cpp
)

@ -46,20 +46,25 @@ extern "C" void CreateStaticModules(AZStd::vector<AZ::Module*>& modulesOut);
# define REMOTE_ASSET_PROCESSOR
#endif
void CVar_OnViewportPosition(const AZ::Vector2& value);
namespace
{
void OnViewportResize(const AZ::Vector2& value);
void CVar_OnViewportResize(const AZ::Vector2& value);
AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate,
AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), CVar_OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate,
"The default size for the launcher viewport, 0 0 means full screen");
void OnViewportResize(const AZ::Vector2& value)
void CVar_OnViewportResize(const AZ::Vector2& value)
{
AzFramework::NativeWindowHandle windowHandle = nullptr;
AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast<int32_t>(value.GetX()), aznumeric_cast<int32_t>(value.GetY()));
AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize);
}
AZ_CVAR(AZ::Vector2, r_viewportPos, AZ::Vector2::CreateZero(), CVar_OnViewportPosition, AZ::ConsoleFunctorFlags::DontReplicate,
"The default position for the launcher viewport, 0 0 means top left corner of your main desktop");
void ExecuteConsoleCommandFile(AzFramework::Application& application)
{

@ -433,3 +433,5 @@ void android_main(android_app* appState)
MAIN_EXIT_FAILURE(appState, GetReturnCodeString(status));
}
}
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}

@ -113,3 +113,5 @@ int main(int argc, char** argv)
return static_cast<int>(status);
}
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}

@ -63,3 +63,5 @@ int main(int argc, char* argv[])
}
#endif // AZ_TESTS_ENABLED
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}

@ -69,3 +69,14 @@ int APIENTRY WinMain([[maybe_unused]] HINSTANCE hInstance, [[maybe_unused]] HINS
return static_cast<int>(status);
}
void CVar_OnViewportPosition(const AZ::Vector2& value)
{
if (HWND windowHandle = GetActiveWindow())
{
SetWindowPos(windowHandle, nullptr,
value.GetX(),
value.GetY(),
0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE);
}
}

@ -23,3 +23,5 @@ int main(int argc, char* argv[])
[pool release];
return 0;
}
void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}

@ -196,6 +196,16 @@ function(ly_delayed_generate_static_modules_inl)
ly_get_gem_load_dependencies(all_game_gem_dependencies ${project_name}.GameLauncher)
foreach(game_gem_dependency ${all_game_gem_dependencies})
# Sometimes, a gem's Client variant may be an interface library
# which dependes on multiple gem targets. The interface libraries
# should be skipped; the real dependencies of the interface will be processed
if(TARGET ${game_gem_dependency})
get_target_property(target_type ${game_gem_dependency} TYPE)
if(${target_type} STREQUAL "INTERFACE_LIBRARY")
continue()
endif()
endif()
# To match the convention on how gems targets vs gem modules are named,
# we remove the ".Static" from the suffix
# Replace "." with "_"
@ -224,6 +234,14 @@ function(ly_delayed_generate_static_modules_inl)
list(APPEND all_server_gem_dependencies ${server_gem_load_dependencies} ${server_gem_dependency})
endforeach()
foreach(server_gem_dependency ${all_server_gem_dependencies})
# Skip interface libraries
if(TARGET ${server_gem_dependency})
get_target_property(target_type ${server_gem_dependency} TYPE)
if(${target_type} STREQUAL "INTERFACE_LIBRARY")
continue()
endif()
endif()
# Replace "." with "_"
string(REPLACE "." "_" server_gem_dependency ${server_gem_dependency})

@ -1,24 +1,26 @@
/*
* 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.
*
*/
* 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 "EditorDefs.h"
#include "AzAssetBrowserWindow.h"
// AzToolsFramework
#include <AzCore/Console/IConsole.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/ViewPaneOptions.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
// AzQtComponents
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
@ -31,6 +33,7 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <AzAssetBrowser/ui_AzAssetBrowserWindow.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView);
class ListenerForShowAssetEditorEvent
: public QObject
@ -66,32 +69,75 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::AzAssetBrowserWindowClass())
, m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent))
, m_tableModel(new AzToolsFramework::AssetBrowser::AssetBrowserTableModel(parent))
{
m_ui->setupUi(this);
m_ui->m_searchWidget->Setup(true, true);
using namespace AzToolsFramework::AssetBrowser;
AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AssetBrowserComponentRequests::GetAssetBrowserModel);
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
AzAssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(m_assetBrowserModel, &AzAssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel);
AZ_Assert(m_assetBrowserModel, "Failed to get filebrowser model");
m_filterModel->setSourceModel(m_assetBrowserModel);
m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter());
m_ui->m_viewSwitcherCheckBox->setVisible(false);
m_ui->m_assetBrowserTableViewWidget->setVisible(false);
if (ed_useNewAssetBrowserTableView)
{
m_ui->m_viewSwitcherCheckBox->setVisible(true);
m_tableModel->setFilterRole(Qt::DisplayRole);
m_tableModel->setSourceModel(m_filterModel.data());
m_ui->m_assetBrowserTableViewWidget->setModel(m_tableModel.data());
connect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, m_tableModel.data(),
&AzAssetBrowser::AssetBrowserTableModel::UpdateTableModelMaps);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
&AzAssetBrowserWindow::SelectionChangedSlot);
connect(
m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this,
&AzAssetBrowserWindow::DoubleClickedItem);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearStringFilter);
connect(
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
m_ui->m_assetBrowserTableViewWidget->SetName("AssetBrowserTableView_main");
connect(m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView);
connect(m_ui->m_viewSwitcherCheckBox, &QCheckBox::stateChanged, this, &AzAssetBrowserWindow::LockToDefaultView);
}
m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data());
connect(m_ui->m_searchWidget->GetFilter().data(), &AssetBrowserEntryFilter::updatedSignal,
m_filterModel.data(), &AssetBrowserFilterModel::filterUpdatedSlot);
connect(m_filterModel.data(), &AssetBrowserFilterModel::filterChanged, this, [this]()
{
const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
const bool selectFirstFilteredIndex = false;
m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex);
});
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::selectionChangedSignal,
this, &AzAssetBrowserWindow::SelectionChangedSlot);
connect(
m_ui->m_searchWidget->GetFilter().data(), &AzAssetBrowser::AssetBrowserEntryFilter::updatedSignal, m_filterModel.data(),
&AzAssetBrowser::AssetBrowserFilterModel::filterUpdatedSlot);
connect(
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
[this]()
{
const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
const bool selectFirstFilteredIndex = false;
m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex);
});
connect(
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::selectionChangedSignal, this,
&AzAssetBrowserWindow::SelectionChangedSlot);
connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget, &SearchWidget::ClearStringFilter);
connect(m_ui->m_assetBrowserTreeViewWidget, &AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget, &SearchWidget::ClearTypeFilter);
connect(
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearStringFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearStringFilter);
connect(
m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter, m_ui->m_searchWidget,
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
m_ui->m_assetBrowserTreeViewWidget->SetName("AssetBrowserTreeView_main");
}
@ -117,7 +163,10 @@ QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* pa
void AzAssetBrowserWindow::UpdatePreview() const
{
auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible()
? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()
: m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets();
if (selectedAssets.size() != 1)
{
m_ui->m_previewerFrame->Clear();
@ -148,8 +197,6 @@ static void ExpandTreeToIndex(QTreeView* treeView, const QModelIndex& index)
void AzAssetBrowserWindow::SelectAsset(const QString& assetPath)
{
using namespace AzToolsFramework::AssetBrowser;
QModelIndex index = m_assetBrowserModel->findIndex(assetPath);
if (index.isValid())
{
@ -161,18 +208,21 @@ void AzAssetBrowserWindow::SelectAsset(const QString& assetPath)
// interferes with the update from the select and expand, and if you don't
// queue it, the tree doesn't expand reliably.
QTimer::singleShot(0, this, [this, filteredIndex = index] {
// the treeview has a filter model so we have to backwards go from that
QModelIndex index = m_filterModel->mapFromSource(filteredIndex);
QTimer::singleShot(
0, this,
[this, filteredIndex = index]
{
// the treeview has a filter model so we have to backwards go from that
QModelIndex index = m_filterModel->mapFromSource(filteredIndex);
QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget;
ExpandTreeToIndex(treeView, index);
QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget;
ExpandTreeToIndex(treeView, index);
treeView->scrollTo(index);
treeView->setCurrentIndex(index);
treeView->scrollTo(index);
treeView->setCurrentIndex(index);
treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
});
treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
});
}
}
@ -185,31 +235,34 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected
// just becuase on some OS clicking once is activation.
void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element)
{
using namespace AzToolsFramework;
using namespace AzToolsFramework::AssetBrowser;
// assumption: Double clicking an item selects it before telling us we double clicked it.
auto selectedAssets = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
for (const AssetBrowserEntry* entry : selectedAssets)
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible()
? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()
: m_ui->m_assetBrowserTableViewWidget->GetSelectedAssets();
for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets)
{
AZ::Data::AssetId assetIdToOpen;
AZStd::string fullFilePath;
if (const ProductAssetBrowserEntry* productEntry = azrtti_cast<const ProductAssetBrowserEntry*>(entry))
if (const AzAssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast<const AzAssetBrowser::ProductAssetBrowserEntry*>(entry))
{
assetIdToOpen = productEntry->GetAssetId();
fullFilePath = entry->GetFullPath();
}
else if (const SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry))
else if (const AzAssetBrowser::SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const AzAssetBrowser::SourceAssetBrowserEntry*>(entry))
{
// manufacture an empty AssetID with the source's UUID
assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0);
fullFilePath = entry->GetFullPath();
}
bool handledBySomeone = false;
if (assetIdToOpen.IsValid())
{
AssetBrowserInteractionNotificationBus::Broadcast(&AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone);
AzAssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast(
&AzAssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone);
}
if (!handledBySomeone && !fullFilePath.empty())
@ -217,7 +270,27 @@ void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex&
AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath);
}
}
}
void AzAssetBrowserWindow::SwitchDisplayView(bool state)
{
m_ui->m_assetBrowserTableViewWidget->setVisible(state);
m_ui->m_assetBrowserTreeViewWidget->setVisible(!state);
}
void AzAssetBrowserWindow::LockToDefaultView(bool state)
{
using AzToolsFramework::AssetBrowser::AssetBrowserFilterModel;
SwitchDisplayView(!state);
if (state == true)
{
disconnect(
m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView);
}
else
{
connect(m_filterModel.data(), &AssetBrowserFilterModel::stringFilterPopulated, this, &AzAssetBrowserWindow::SwitchDisplayView);
}
}
#include <AzAssetBrowser/moc_AzAssetBrowserWindow.cpp>

@ -29,7 +29,9 @@ namespace AzToolsFramework
namespace AssetBrowser
{
class AssetBrowserFilterModel;
class AssetBrowserTableModel;
class AssetBrowserModel;
class AssetBrowserTableFilterModel;
}
}
@ -53,6 +55,7 @@ private:
QScopedPointer<Ui::AzAssetBrowserWindowClass> m_ui;
QScopedPointer<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel> m_filterModel;
QScopedPointer<AzToolsFramework::AssetBrowser::AssetBrowserTableModel> m_tableModel;
AzToolsFramework::AssetBrowser::AssetBrowserModel* m_assetBrowserModel;
void UpdatePreview() const;
@ -60,6 +63,8 @@ private:
private Q_SLOTS:
void SelectionChangedSlot(const QItemSelection& selected, const QItemSelection& deselected) const;
void DoubleClickedItem(const QModelIndex& element);
void SwitchDisplayView(bool state);
void LockToDefaultView(bool state);
};
extern const char* AZ_ASSET_BROWSER_PREVIEW_NAME;

@ -65,6 +65,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="m_viewSwitcherCheckBox">
<property name="text">
<string>Switch View</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -107,6 +114,49 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTableView" name="m_assetBrowserTableViewWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<widget class="AzToolsFramework::AssetBrowser::AssetBrowserTreeView" name="m_assetBrowserTreeViewWidget">
<property name="sizePolicy">
@ -162,6 +212,11 @@
<header>AzToolsFramework/AssetBrowser/Previewer/PreviewerFrame.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzToolsFramework::AssetBrowser::AssetBrowserTableView</class>
<extends>QTableView</extends>
<header>AzToolsFramework/AssetBrowser/Views/AssetBrowserTableView.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save