Merge branch 'development' into Atom/dmcdiar/ATOM-5702
@@ -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 the follower to move above the lead or Timeout
|
||||
normalizedStartPos = JointsHelper.getRelativeVector(lead.position, follower.position)
|
||||
normalizedStartPos = normalizedStartPos.GetNormalizedSafe()
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(4.0) # wait for lead and follower to move
|
||||
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);
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -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());
|
||||
}
|
||||
// sources with no children should be greyed out.
|
||||
if (sourceEntry->GetChildCount() == 0)
|
||||
{
|
||||
isEnabled = false; // draw in disabled style.
|
||||
actualPalette.setCurrentColorGroup(QPalette::Disabled);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
|
||||
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.
|
||||
|
||||
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,19 +402,83 @@ 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);
|
||||
if (HasContainerEntity())
|
||||
{
|
||||
if (!callback(m_containerEntity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetEntities_Impl(callback))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& [instanceAlias, instance] : m_nestedInstances)
|
||||
{
|
||||
instance->GetNestedEntities(callback);
|
||||
if (!instance->GetAllEntitiesInHierarchy_Impl(callback))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Instance::GetAllEntitiesInHierarchyConst_Impl(const AZStd::function<bool(const AZ::Entity&)>& callback) const
|
||||
{
|
||||
if (HasContainerEntity())
|
||||
{
|
||||
if (!callback(*m_containerEntity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetConstEntities_Impl(callback))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& [instanceAlias, instance] : m_nestedInstances)
|
||||
{
|
||||
if (!instance->GetAllEntitiesInHierarchyConst_Impl(callback))
|
||||
{
|
||||
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)
|
||||
@@ -417,44 +489,6 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::GetEntities(const AZStd::function<bool(AZStd::unique_ptr<AZ::Entity>&)>& callback)
|
||||
{
|
||||
for (auto& [entityAlias, entity] : m_entities)
|
||||
{
|
||||
if (!callback(entity))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::GetEntities(EntityList& entities, bool includeNestedEntities)
|
||||
{
|
||||
// Non-recursive traversal of instances
|
||||
AZStd::vector<Instance*> instancesToTraverse = { this };
|
||||
while (!instancesToTraverse.empty())
|
||||
{
|
||||
Instance* currentInstance = instancesToTraverse.back();
|
||||
instancesToTraverse.pop_back();
|
||||
if (includeNestedEntities)
|
||||
{
|
||||
instancesToTraverse.reserve(instancesToTraverse.size() + currentInstance->m_nestedInstances.size());
|
||||
for (const auto& instanceByAlias : currentInstance->m_nestedInstances)
|
||||
{
|
||||
instancesToTraverse.push_back(instanceByAlias.second.get());
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
entities.push_back(entityByAlias.second.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityAliasOptionalReference Instance::GetEntityAlias(const AZ::EntityId& id)
|
||||
{
|
||||
if (m_instanceToTemplateEntityIdMap.count(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::CreateSpaceSelectionCluster()
|
||||
void EditorTransformComponentSelection::CreateSnappingCluster()
|
||||
{
|
||||
// create the cluster for switching spaces/reference frames
|
||||
ViewportUi::ViewportUiRequestBus::EventResult(
|
||||
m_spaceCluster.m_spaceClusterId, ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::CreateCluster,
|
||||
m_snappingCluster.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_snappingCluster.m_snapToWorldButtonId = RegisterClusterButton(m_snappingCluster.m_clusterId, "Grid");
|
||||
|
||||
// set button tooltips
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
ViewportUi::DefaultViewportId, &ViewportUi::ViewportUiRequestBus::Events::SetClusterButtonTooltip,
|
||||
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_worldButtonId, SpaceClusterWorldTooltip);
|
||||
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::SetClusterButtonTooltip,
|
||||
m_spaceCluster.m_spaceClusterId, m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip);
|
||||
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_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_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_localButtonId, SpaceClusterLocalTooltip);
|
||||
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_clusterId,
|
||||
m_spaceCluster.m_parentButtonId, SpaceClusterParentTooltip);
|
||||
ViewportUi::ViewportUiRequestBus::Event(
|
||||
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> {};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionSingleEntityWithParentPivotFixture,
|
||||
PivotOrientationMatchesReferenceFrameEntityWithParent)
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
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> {};
|
||||
|
||||
TEST_P(
|
||||
EditorTransformComponentSelectionMultipleEntitiesPivotFixture,
|
||||
PivotOrientationMatchesReferenceFrameMultipleEntities)
|
||||
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
||||
{
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
@@ -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/>
|
||||
|
||||