Merge branch 'development' into redcode/crythread-2nd-pass
Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com>monroegm-disable-blank-issue-2
commit
f55ffae994
@ -0,0 +1,19 @@
|
||||
{
|
||||
"Amazon": {
|
||||
"AssetProcessor": {
|
||||
"Settings": {
|
||||
"RC cgf": {
|
||||
"ignore": true
|
||||
},
|
||||
"RC fbx": {
|
||||
"ignore": true
|
||||
},
|
||||
"ScanFolder AtomTestData": {
|
||||
"watch": "@ENGINEROOT@/Gems/Atom/TestData",
|
||||
"recursive": 1,
|
||||
"order": 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
import azlmbr.materialeditor will fail with a ModuleNotFound error when using this script with Editor.exe
|
||||
This is because azlmbr.materialeditor only binds to MaterialEditor.exe and not Editor.exe
|
||||
You need to launch this script with MaterialEditor.exe in order for azlmbr.materialeditor to appear.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import azlmbr.math as math
|
||||
import azlmbr.paths
|
||||
|
||||
sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
|
||||
|
||||
import atom_renderer.atom_utils.material_editor_utils as material_editor
|
||||
|
||||
NEW_MATERIAL = "test_material.material"
|
||||
NEW_MATERIAL_1 = "test_material_1.material"
|
||||
NEW_MATERIAL_2 = "test_material_2.material"
|
||||
TEST_MATERIAL_1 = "001_DefaultWhite.material"
|
||||
TEST_MATERIAL_2 = "002_BaseColorLerp.material"
|
||||
TEST_MATERIAL_3 = "003_MetalMatte.material"
|
||||
TEST_DATA_PATH = os.path.join(
|
||||
azlmbr.paths.devroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases"
|
||||
)
|
||||
MATERIAL_TYPE_PATH = os.path.join(
|
||||
azlmbr.paths.devroot, "Gems", "Atom", "Feature", "Common", "Assets",
|
||||
"Materials", "Types", "StandardPBR.materialtype",
|
||||
)
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
Material Editor basic tests including the below
|
||||
1. Opening an Existing Asset
|
||||
2. Creating a New Asset
|
||||
3. Closing Selected Material
|
||||
4. Closing All Materials
|
||||
5. Closing all but Selected Material
|
||||
6. Saving Material
|
||||
7. Saving as a New Material
|
||||
8. Saving as a Child Material
|
||||
9. Saving all Open Materials
|
||||
|
||||
Expected Result:
|
||||
All the above functions work as expected in Material Editor.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# 1) Test Case: Opening an Existing Asset
|
||||
document_id = material_editor.open_material(MATERIAL_TYPE_PATH)
|
||||
print(f"Material opened: {material_editor.is_open(document_id)}")
|
||||
|
||||
# Verify if the test material exists initially
|
||||
target_path = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL)
|
||||
print(f"Test asset doesn't exist initially: {not os.path.exists(target_path)}")
|
||||
|
||||
# 2) Test Case: Creating a New Material Using Existing One
|
||||
material_editor.save_document_as_child(document_id, target_path)
|
||||
material_editor.wait_for_condition(lambda: os.path.exists(target_path), 2.0)
|
||||
print(f"New asset created: {os.path.exists(target_path)}")
|
||||
|
||||
# Verify if the newly created document is open
|
||||
new_document_id = material_editor.open_material(target_path)
|
||||
material_editor.wait_for_condition(lambda: material_editor.is_open(new_document_id))
|
||||
print(f"New Material opened: {material_editor.is_open(new_document_id)}")
|
||||
|
||||
# 3) Test Case: Closing Selected Material
|
||||
print(f"Material closed: {material_editor.close_document(new_document_id)}")
|
||||
|
||||
# Open materials initially
|
||||
document1_id, document2_id, document3_id = (
|
||||
material_editor.open_material(os.path.join(TEST_DATA_PATH, material))
|
||||
for material in [TEST_MATERIAL_1, TEST_MATERIAL_2, TEST_MATERIAL_3]
|
||||
)
|
||||
|
||||
# 4) Test Case: Closing All Materials
|
||||
print(f"All documents closed: {material_editor.close_all_documents()}")
|
||||
|
||||
# 5) Test Case: Closing all but Selected Material
|
||||
document1_id, document2_id, document3_id = (
|
||||
material_editor.open_material(os.path.join(TEST_DATA_PATH, material))
|
||||
for material in [TEST_MATERIAL_1, TEST_MATERIAL_2, TEST_MATERIAL_3]
|
||||
)
|
||||
result = material_editor.close_all_except_selected(document1_id)
|
||||
print(f"Close All Except Selected worked as expected: {result and material_editor.is_open(document1_id)}")
|
||||
|
||||
# 6) Test Case: Saving Material
|
||||
document_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
|
||||
property_name = azlmbr.name.Name("baseColor.color")
|
||||
initial_color = material_editor.get_property(document_id, property_name)
|
||||
# Assign new color to the material file and save the actual material
|
||||
expected_color = math.Color(0.25, 0.25, 0.25, 1.0)
|
||||
material_editor.set_property(document_id, property_name, expected_color)
|
||||
material_editor.save_document(document_id)
|
||||
|
||||
# 7) Test Case: Saving as a New Material
|
||||
# Assign new color to the material file and save the document as copy
|
||||
expected_color_1 = math.Color(0.5, 0.5, 0.5, 1.0)
|
||||
material_editor.set_property(document_id, property_name, expected_color_1)
|
||||
target_path_1 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_1)
|
||||
material_editor.save_document_as_copy(document_id, target_path_1)
|
||||
time.sleep(2.0)
|
||||
|
||||
# 8) Test Case: Saving as a Child Material
|
||||
# Assign new color to the material file save the document as child
|
||||
expected_color_2 = math.Color(0.75, 0.75, 0.75, 1.0)
|
||||
material_editor.set_property(document_id, property_name, expected_color_2)
|
||||
target_path_2 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_2)
|
||||
material_editor.save_document_as_child(document_id, target_path_2)
|
||||
time.sleep(2.0)
|
||||
|
||||
# Close/Reopen documents
|
||||
material_editor.close_all_documents()
|
||||
document_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
|
||||
document1_id = material_editor.open_material(target_path_1)
|
||||
document2_id = material_editor.open_material(target_path_2)
|
||||
|
||||
# Verify if the changes are saved in the actual document
|
||||
actual_color = material_editor.get_property(document_id, property_name)
|
||||
print(f"Actual Document saved with changes: {material_editor.compare_colors(actual_color, expected_color)}")
|
||||
|
||||
# Verify if the changes are saved in the document saved as copy
|
||||
actual_color = material_editor.get_property(document1_id, property_name)
|
||||
result_copy = material_editor.compare_colors(actual_color, expected_color_1)
|
||||
print(f"Document saved as copy is saved with changes: {result_copy}")
|
||||
|
||||
# Verify if the changes are saved in the document saved as child
|
||||
actual_color = material_editor.get_property(document2_id, property_name)
|
||||
result_child = material_editor.compare_colors(actual_color, expected_color_2)
|
||||
print(f"Document saved as child is saved with changes: {result_child}")
|
||||
|
||||
# Revert back the changes in the actual document
|
||||
material_editor.set_property(document_id, property_name, initial_color)
|
||||
material_editor.save_document(document_id)
|
||||
material_editor.close_all_documents()
|
||||
|
||||
# 9) Test Case: Saving all Open Materials
|
||||
# Open first material and make change to the values
|
||||
document1_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
|
||||
property1_name = azlmbr.name.Name("metallic.factor")
|
||||
initial_metallic_factor = material_editor.get_property(document1_id, property1_name)
|
||||
expected_metallic_factor = 0.444
|
||||
material_editor.set_property(document1_id, property1_name, expected_metallic_factor)
|
||||
|
||||
# Open second material and make change to the values
|
||||
document2_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_2))
|
||||
property2_name = azlmbr.name.Name("baseColor.color")
|
||||
initial_color = material_editor.get_property(document2_id, property2_name)
|
||||
expected_color = math.Color(0.4156, 0.0196, 0.6862, 1.0)
|
||||
material_editor.set_property(document2_id, property2_name, expected_color)
|
||||
|
||||
# Save all and close all documents
|
||||
material_editor.save_all()
|
||||
material_editor.close_all_documents()
|
||||
|
||||
# Reopen materials and verify values
|
||||
document1_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
|
||||
result = material_editor.is_close(
|
||||
material_editor.get_property(document1_id, property1_name), expected_metallic_factor, 0.00001
|
||||
)
|
||||
document2_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_2))
|
||||
result = result and material_editor.compare_colors(
|
||||
expected_color, material_editor.get_property(document2_id, property2_name))
|
||||
print(f"Save All worked as expected: {result}")
|
||||
|
||||
# Revert the changes made
|
||||
material_editor.set_property(document1_id, property1_name, initial_metallic_factor)
|
||||
material_editor.set_property(document2_id, property2_name, initial_color)
|
||||
material_editor.save_all()
|
||||
material_editor.close_all_documents()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@ -0,0 +1,268 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
Hydra script that is used to create an entity with a Light component attached.
|
||||
It then updates the property values of the Light component and takes a screenshot.
|
||||
The screenshot is compared against an expected golden image for test verification.
|
||||
|
||||
See the run() function for more in-depth test info.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import azlmbr.asset as asset
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.math as math
|
||||
import azlmbr.paths
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
|
||||
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
from atom_renderer.atom_utils import screenshot_utils
|
||||
from atom_renderer.atom_utils import atom_component_helper
|
||||
from editor_python_test_tools.editor_test_helper import EditorTestHelper
|
||||
|
||||
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
|
||||
|
||||
LEVEL_NAME = "auto_test"
|
||||
LIGHT_COMPONENT = "Light"
|
||||
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
|
||||
DEGREE_RADIAN_FACTOR = 0.0174533
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Sets up the tests by making sure the required level is created & setup correctly.
|
||||
It then executes 2 test cases - see each associated test function's docstring for more info.
|
||||
|
||||
Finally prints the string "Light component tests completed" after completion
|
||||
|
||||
Tests will fail immediately if any of these log lines are found:
|
||||
1. Trace::Assert
|
||||
2. Trace::Error
|
||||
3. Traceback (most recent call last):
|
||||
|
||||
:return: None
|
||||
"""
|
||||
atom_component_helper.create_basic_atom_level(level_name=LEVEL_NAME)
|
||||
|
||||
# Run tests.
|
||||
area_light_test()
|
||||
spot_light_test()
|
||||
general.log("Light component tests completed.")
|
||||
|
||||
|
||||
def area_light_test():
|
||||
"""
|
||||
Basic test for the "Light" component attached to an "area_light" entity.
|
||||
|
||||
Test Case - Light Component: Capsule, Spot (disk), and Point (sphere):
|
||||
1. Creates "area_light" entity w/ a Light component that has a Capsule Light type w/ the color set to 255, 0, 0
|
||||
2. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
3. Sets the Light component Intensity Mode to Lumens (default).
|
||||
4. Ensures the Light component Mode is Automatic (default).
|
||||
5. Sets the Intensity value of the Light component to 0.0
|
||||
6. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
7. Updates the Intensity value of the Light component to 1000.0
|
||||
8. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
9. Swaps the Capsule light type option to Spot (disk) light type on the Light component
|
||||
10. Updates "area_light" entity Transform rotate value to x: 90.0, y:0.0, z:0.0
|
||||
11. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
12. Swaps the Spot (disk) light type for the Point (sphere) light type in the Light component.
|
||||
13. Enters game mode again, takes another screenshot for comparison, then exits game mode.
|
||||
14. Deletes the Light component from the "area_light" entity and verifies its successful.
|
||||
"""
|
||||
# Create an "area_light" entity with "Light" component using Light type of "Capsule"
|
||||
area_light_entity_name = "area_light"
|
||||
area_light = hydra.Entity(area_light_entity_name)
|
||||
area_light.create_entity(math.Vector3(-1.0, -2.0, 3.0), [LIGHT_COMPONENT])
|
||||
general.log(
|
||||
f"{area_light_entity_name}_test: Component added to the entity: "
|
||||
f"{hydra.has_components(area_light.id, [LIGHT_COMPONENT])}")
|
||||
light_component_id_pair = hydra.attach_component_to_entity(area_light.id, LIGHT_COMPONENT)
|
||||
|
||||
# Select the "Capsule" light type option.
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_id_pair,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_component_helper.LIGHT_TYPES['capsule']
|
||||
)
|
||||
|
||||
# Update color and take screenshot in game mode
|
||||
color = math.Color(255.0, 0.0, 0.0, 0.0)
|
||||
area_light.get_set_test(0, "Controller|Configuration|Color", color)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_1", area_light_entity_name)
|
||||
|
||||
# Update intensity value to 0.0 and take screenshot in game mode
|
||||
area_light.get_set_test(0, "Controller|Configuration|Attenuation Radius|Mode", 1)
|
||||
area_light.get_set_test(0, "Controller|Configuration|Intensity", 0.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_2", area_light_entity_name)
|
||||
|
||||
# Update intensity value to 1000.0 and take screenshot in game mode
|
||||
area_light.get_set_test(0, "Controller|Configuration|Intensity", 1000.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_3", area_light_entity_name)
|
||||
|
||||
# Swap the "Capsule" light type option to "Spot (disk)" light type
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_id_pair,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_component_helper.LIGHT_TYPES['spot_disk']
|
||||
)
|
||||
area_light_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 90.0, 0.0, 0.0)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", area_light.id, area_light_rotation)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_4", area_light_entity_name)
|
||||
|
||||
# Swap the "Spot (disk)" light type to the "Point (sphere)" light type and take screenshot.
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_id_pair,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_component_helper.LIGHT_TYPES['sphere']
|
||||
)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("AreaLight_5", area_light_entity_name)
|
||||
|
||||
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", area_light.id)
|
||||
|
||||
|
||||
def spot_light_test():
|
||||
"""
|
||||
Basic test for the Light component attached to a "spot_light" entity.
|
||||
|
||||
Test Case - Light Component: Spot (disk) with shadows & colors:
|
||||
1. Creates "spot_light" entity w/ a Light component attached to it.
|
||||
2. Selects the "directional_light" entity already present in the level and disables it.
|
||||
3. Selects the "global_skylight" entity already present in the level and disables the HDRi Skybox component,
|
||||
as well as the Global Skylight (IBL) component.
|
||||
4. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
5. Selects the "ground_plane" entity and changes updates the material to a new material.
|
||||
6. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
7. Selects the "spot_light" entity and increases the Light component Intensity to 800 lm
|
||||
8. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
9. Selects the "spot_light" entity and sets the Light component Color to 47, 75, 37
|
||||
10. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
11. Selects the "spot_light" entity and modifies the Shutter controls to the following values:
|
||||
- Enable shutters: True
|
||||
- Inner Angle: 60.0
|
||||
- Outer Angle: 75.0
|
||||
12. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
13. Selects the "spot_light" entity and modifies the Shadow controls to the following values:
|
||||
- Enable Shadow: True
|
||||
- ShadowmapSize: 256
|
||||
14. Modifies the world translate position of the "spot_light" entity to 0.7, -2.0, 1.9 (for casting shadows better)
|
||||
15. Enters game mode to take a screenshot for comparison, then exits game mode.
|
||||
"""
|
||||
# Disable "Directional Light" component for the "directional_light" entity
|
||||
# "directional_light" entity is created by the create_basic_atom_level() function by default.
|
||||
directional_light_entity_id = hydra.find_entity_by_name("directional_light")
|
||||
directional_light = hydra.Entity(name='directional_light', id=directional_light_entity_id)
|
||||
directional_light_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Directional Light"], 0)[0]
|
||||
directional_light_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', directional_light.id, directional_light_component_type
|
||||
).GetValue()
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [directional_light_component])
|
||||
general.idle_wait(0.5)
|
||||
|
||||
# Disable "Global Skylight (IBL)" and "HDRi Skybox" components for the "global_skylight" entity
|
||||
global_skylight_entity_id = hydra.find_entity_by_name("global_skylight")
|
||||
global_skylight = hydra.Entity(name='global_skylight', id=global_skylight_entity_id)
|
||||
global_skylight_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Global Skylight (IBL)"], 0)[0]
|
||||
global_skylight_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, global_skylight_component_type
|
||||
).GetValue()
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [global_skylight_component])
|
||||
hdri_skybox_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["HDRi Skybox"], 0)[0]
|
||||
hdri_skybox_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, hdri_skybox_component_type
|
||||
).GetValue()
|
||||
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [hdri_skybox_component])
|
||||
general.idle_wait(0.5)
|
||||
|
||||
# Create a "spot_light" entity with "Light" component using Light Type of "Spot (disk)"
|
||||
spot_light_entity_name = "spot_light"
|
||||
spot_light = hydra.Entity(spot_light_entity_name)
|
||||
spot_light.create_entity(math.Vector3(0.7, -2.0, 1.0), [LIGHT_COMPONENT])
|
||||
general.log(
|
||||
f"{spot_light_entity_name}_test: Component added to the entity: "
|
||||
f"{hydra.has_components(spot_light.id, [LIGHT_COMPONENT])}")
|
||||
rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 300.0, 0.0, 0.0)
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", spot_light.id, rotation)
|
||||
light_component_type = hydra.attach_component_to_entity(spot_light.id, LIGHT_COMPONENT)
|
||||
editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
light_component_type,
|
||||
LIGHT_TYPE_PROPERTY,
|
||||
atom_component_helper.LIGHT_TYPES['spot_disk']
|
||||
)
|
||||
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_1", spot_light_entity_name)
|
||||
|
||||
# Change default material of ground plane entity and take screenshot
|
||||
ground_plane_entity_id = hydra.find_entity_by_name("ground_plane")
|
||||
ground_plane = hydra.Entity(name='ground_plane', id=ground_plane_entity_id)
|
||||
ground_plane_asset_path = os.path.join("Materials", "Presets", "MacBeth", "22_neutral_5-0_0-70d.azmaterial")
|
||||
ground_plane_asset_value = asset.AssetCatalogRequestBus(
|
||||
bus.Broadcast, "GetAssetIdByPath", ground_plane_asset_path, math.Uuid(), False)
|
||||
material_property_path = "Default Material|Material Asset"
|
||||
material_component_type = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Material"], 0)[0]
|
||||
material_component = azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast, 'GetComponentOfType', ground_plane.id, material_component_type).GetValue()
|
||||
editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
'SetComponentProperty',
|
||||
material_component,
|
||||
material_property_path,
|
||||
ground_plane_asset_value
|
||||
)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_2", spot_light_entity_name)
|
||||
|
||||
# Increase intensity value of the Spot light and take screenshot in game mode
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Intensity", 800.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_3", spot_light_entity_name)
|
||||
|
||||
# Update the Spot light color and take screenshot in game mode
|
||||
color_value = math.Color(47.0 / 255.0, 75.0 / 255.0, 37.0 / 255.0, 255.0 / 255.0)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Color", color_value)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_4", spot_light_entity_name)
|
||||
|
||||
# Update the Shutter controls of the Light component and take screenshot
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Enable shutters", True)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Inner angle", 60.0)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Outer angle", 75.0)
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_5", spot_light_entity_name)
|
||||
|
||||
# Update the Shadow controls, move the spot_light entity world translate position and take screenshot
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shadows|Enable shadow", True)
|
||||
spot_light.get_set_test(0, "Controller|Configuration|Shadows|Shadowmap size", 256.0)
|
||||
azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "SetWorldTranslation", spot_light.id, math.Vector3(0.7, -2.0, 1.9))
|
||||
general.idle_wait(1.0)
|
||||
screenshot_utils.take_screenshot_game_mode("SpotLight_6", spot_light_entity_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@ -0,0 +1,19 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
Hold constants used across both hydra and non-hydra scripts.
|
||||
"""
|
||||
|
||||
# Light type options for the Light component.
|
||||
LIGHT_TYPES = {
|
||||
'unknown': 0,
|
||||
'sphere': 1,
|
||||
'spot_disk': 2,
|
||||
'capsule': 3,
|
||||
'quad': 4,
|
||||
'polygon': 5,
|
||||
'simple_point': 6,
|
||||
'simple_spot': 7,
|
||||
}
|
||||
@ -0,0 +1,274 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
import azlmbr.materialeditor will fail with a ModuleNotFound error when using this script with Editor.exe
|
||||
This is because azlmbr.materialeditor only binds to MaterialEditor.exe and not Editor.exe
|
||||
You need to launch this script with MaterialEditor.exe in order for azlmbr.materialeditor to appear.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import azlmbr.atom
|
||||
import azlmbr.materialeditor as materialeditor
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.atomtools.general as general
|
||||
|
||||
|
||||
def is_close(actual, expected, buffer=sys.float_info.min):
|
||||
"""
|
||||
:param actual: actual value
|
||||
:param expected: expected value
|
||||
:param buffer: acceptable variation from expected
|
||||
:return: bool
|
||||
"""
|
||||
return abs(actual - expected) < buffer
|
||||
|
||||
|
||||
def compare_colors(color1, color2, buffer=0.00001):
|
||||
"""
|
||||
Compares the red, green and blue properties of a color allowing a slight variance of buffer
|
||||
:param color1: first color to compare
|
||||
:param color2: second color
|
||||
:param buffer: allowed variance in individual color value
|
||||
:return: bool
|
||||
"""
|
||||
return (
|
||||
is_close(color1.r, color2.r, buffer)
|
||||
and is_close(color1.g, color2.g, buffer)
|
||||
and is_close(color1.b, color2.b, buffer)
|
||||
)
|
||||
|
||||
|
||||
def open_material(file_path):
|
||||
"""
|
||||
:return: uuid of material document opened
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "OpenDocument", file_path)
|
||||
|
||||
|
||||
def is_open(document_id):
|
||||
"""
|
||||
:return: bool
|
||||
"""
|
||||
return materialeditor.MaterialDocumentRequestBus(bus.Event, "IsOpen", document_id)
|
||||
|
||||
|
||||
def save_document(document_id):
|
||||
"""
|
||||
:return: bool success
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "SaveDocument", document_id)
|
||||
|
||||
|
||||
def save_document_as_copy(document_id, target_path):
|
||||
"""
|
||||
:return: bool success
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(
|
||||
bus.Broadcast, "SaveDocumentAsCopy", document_id, target_path
|
||||
)
|
||||
|
||||
|
||||
def save_document_as_child(document_id, target_path):
|
||||
"""
|
||||
:return: bool success
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(
|
||||
bus.Broadcast, "SaveDocumentAsChild", document_id, target_path
|
||||
)
|
||||
|
||||
|
||||
def save_all():
|
||||
"""
|
||||
:return: bool success
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "SaveAllDocuments")
|
||||
|
||||
|
||||
def close_document(document_id):
|
||||
"""
|
||||
:return: bool success
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "CloseDocument", document_id)
|
||||
|
||||
|
||||
def close_all_documents():
|
||||
"""
|
||||
:return: bool success
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "CloseAllDocuments")
|
||||
|
||||
|
||||
def close_all_except_selected(document_id):
|
||||
"""
|
||||
:return: bool success
|
||||
"""
|
||||
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "CloseAllDocumentsExcept", document_id)
|
||||
|
||||
|
||||
def get_property(document_id, property_name):
|
||||
"""
|
||||
:return: property value or invalid value if the document is not open or the property_name can't be found
|
||||
"""
|
||||
return materialeditor.MaterialDocumentRequestBus(bus.Event, "GetPropertyValue", document_id, property_name)
|
||||
|
||||
|
||||
def set_property(document_id, property_name, value):
|
||||
materialeditor.MaterialDocumentRequestBus(bus.Event, "SetPropertyValue", document_id, property_name, value)
|
||||
|
||||
|
||||
def is_pane_visible(pane_name):
|
||||
"""
|
||||
:return: bool
|
||||
"""
|
||||
return materialeditor.MaterialEditorWindowRequestBus(bus.Broadcast, "IsDockWidgetVisible", pane_name)
|
||||
|
||||
|
||||
def set_pane_visibility(pane_name, value):
|
||||
materialeditor.MaterialEditorWindowRequestBus(bus.Broadcast, "SetDockWidgetVisible", pane_name, value)
|
||||
|
||||
|
||||
def select_lighting_config(config_name):
|
||||
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectLightingPresetByName", config_name)
|
||||
|
||||
|
||||
def set_grid_enable_disable(value):
|
||||
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SetGridEnabled", value)
|
||||
|
||||
|
||||
def get_grid_enable_disable():
|
||||
"""
|
||||
:return: bool
|
||||
"""
|
||||
return azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "GetGridEnabled")
|
||||
|
||||
|
||||
def set_shadowcatcher_enable_disable(value):
|
||||
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SetShadowCatcherEnabled", value)
|
||||
|
||||
|
||||
def get_shadowcatcher_enable_disable():
|
||||
"""
|
||||
:return: bool
|
||||
"""
|
||||
return azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "GetShadowCatcherEnabled")
|
||||
|
||||
|
||||
def select_model_config(configname):
|
||||
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname)
|
||||
|
||||
|
||||
def wait_for_condition(function, timeout_in_seconds=1.0):
|
||||
# type: (function, float) -> bool
|
||||
"""
|
||||
Function to run until it returns True or timeout is reached
|
||||
the function can have no parameters and
|
||||
waiting idle__wait_* is handled here not in the function
|
||||
|
||||
:param function: a function that returns a boolean indicating a desired condition is achieved
|
||||
:param timeout_in_seconds: when reached, function execution is abandoned and False is returned
|
||||
"""
|
||||
with Timeout(timeout_in_seconds) as t:
|
||||
while True:
|
||||
try:
|
||||
general.idle_wait_frames(1)
|
||||
except Exception:
|
||||
print("WARNING: Couldn't wait for frame")
|
||||
|
||||
if t.timed_out:
|
||||
return False
|
||||
|
||||
ret = function()
|
||||
if not isinstance(ret, bool):
|
||||
raise TypeError("return value for wait_for_condition function must be a bool")
|
||||
if ret:
|
||||
return True
|
||||
|
||||
|
||||
class Timeout:
|
||||
# type: (float) -> None
|
||||
"""
|
||||
contextual timeout
|
||||
:param seconds: float seconds to allow before timed_out is True
|
||||
"""
|
||||
|
||||
def __init__(self, seconds):
|
||||
self.seconds = seconds
|
||||
|
||||
def __enter__(self):
|
||||
self.die_after = time.time() + self.seconds
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
pass
|
||||
|
||||
@property
|
||||
def timed_out(self):
|
||||
return time.time() > self.die_after
|
||||
|
||||
|
||||
screenshotsFolder = os.path.join(azlmbr.paths.devroot, "AtomTest", "Cache" "pc", "Screenshots")
|
||||
|
||||
|
||||
class ScreenshotHelper:
|
||||
"""
|
||||
A helper to capture screenshots and wait for them.
|
||||
"""
|
||||
|
||||
def __init__(self, idle_wait_frames_callback):
|
||||
super().__init__()
|
||||
self.done = False
|
||||
self.capturedScreenshot = False
|
||||
self.max_frames_to_wait = 60
|
||||
|
||||
self.idle_wait_frames_callback = idle_wait_frames_callback
|
||||
|
||||
def capture_screenshot_blocking(self, filename):
|
||||
"""
|
||||
Capture a screenshot and block the execution until the screenshot has been written to the disk.
|
||||
"""
|
||||
self.handler = azlmbr.atom.FrameCaptureNotificationBusHandler()
|
||||
self.handler.connect()
|
||||
self.handler.add_callback("OnCaptureFinished", self.on_screenshot_captured)
|
||||
|
||||
self.done = False
|
||||
self.capturedScreenshot = False
|
||||
success = azlmbr.atom.FrameCaptureRequestBus(azlmbr.bus.Broadcast, "CaptureScreenshot", filename)
|
||||
if success:
|
||||
self.wait_until_screenshot()
|
||||
print("Screenshot taken.")
|
||||
else:
|
||||
print("screenshot failed")
|
||||
return self.capturedScreenshot
|
||||
|
||||
def on_screenshot_captured(self, parameters):
|
||||
# the parameters come in as a tuple
|
||||
if parameters[0]:
|
||||
print("screenshot saved: {}".format(parameters[1]))
|
||||
self.capturedScreenshot = True
|
||||
else:
|
||||
print("screenshot failed: {}".format(parameters[1]))
|
||||
self.done = True
|
||||
self.handler.disconnect()
|
||||
|
||||
def wait_until_screenshot(self):
|
||||
frames_waited = 0
|
||||
while self.done == False:
|
||||
self.idle_wait_frames_callback(1)
|
||||
if frames_waited > self.max_frames_to_wait:
|
||||
print("timeout while waiting for the screenshot to be written")
|
||||
self.handler.disconnect()
|
||||
break
|
||||
else:
|
||||
frames_waited = frames_waited + 1
|
||||
print("(waited {} frames)".format(frames_waited))
|
||||
|
||||
|
||||
def capture_screenshot(file_path):
|
||||
return ScreenshotHelper(azlmbr.atomtools.general.idle_wait_frames).capture_screenshot_blocking(
|
||||
os.path.join(file_path)
|
||||
)
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:954d7d0df47c840a24e313893800eb3126d0c0d47c3380926776b51833778db7
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e81c19128f42ba362a2d5f3ccf159dfbc942d67ceeb1ac8c21f295a6fd9d2ce5
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5e20801213e065b6ea8c95ede81c23faa9b6dc70a2002dc5bced293e1bed989f
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e250f812e594e5152bf2d6f23caa8b53b78276bfdf344d7a8d355dd96cb995c0
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:95be359041f8291c74b335297a4dfe9902a180510f24a181b15e1a5ba4d3b024
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:118e43e4b915e262726183467cc4b82f244565213fea5b6bfe02be07f0851ab1
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dc2ce3256a6552975962c9e113c52c1a22bf3817d417151f6f60640dd568e0fa
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:287d98890b35427688999760f9d066bcbff1a3bc9001534241dc212b32edabd8
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:66e91c92c868167c850078cd91714db47e10a96e23cc30191994486bd79c353f
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d950d173f5101820c5e18205401ca08ce5feeff2302ac2920b292750d86a8fa4
|
||||
size 6220817
|
||||
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:72eddb7126eae0c839b933886e0fb69d78229f72d49ef13199de28df2b7879db
|
||||
size 6220817
|
||||
@ -0,0 +1,66 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# fmt:off
|
||||
class Tests():
|
||||
create_new_entity = ("Entity: 'CreateNewEntity' passed", "Entity: 'CreateNewEntity' failed")
|
||||
create_prefab = ("Prefab: 'CreatePrefab' passed", "Prefab: 'CreatePrefab' failed")
|
||||
instantiate_prefab = ("Prefab: 'InstantiatePrefab' passed", "Prefab: 'InstantiatePrefab' failed")
|
||||
new_prefab_position = ("Prefab: new prefab's position is at the expected position", "Prefab: new prefab's position is *not* at the expected position")
|
||||
# fmt:on
|
||||
|
||||
def PrefabLevel_BasicWorkflow():
|
||||
"""
|
||||
This test will help verify if the following functions related to Prefab work as expected:
|
||||
- CreatePrefab
|
||||
- InstantiatePrefab
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import editor_python_test_tools.hydra_editor_utils as hydra
|
||||
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.entity as entity
|
||||
from azlmbr.entity import EntityId
|
||||
import azlmbr.editor as editor
|
||||
import azlmbr.prefab as prefab
|
||||
from azlmbr.math import Vector3
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
EXPECTED_NEW_PREFAB_POSITION = Vector3(10.00, 20.0, 30.0)
|
||||
|
||||
helper.init_idle()
|
||||
helper.open_level("Prefab", "Base")
|
||||
|
||||
# Create a new Entity at the root level
|
||||
new_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId())
|
||||
Report.result(Tests.create_new_entity, new_entity_id.IsValid())
|
||||
|
||||
# Checks for prefab creation passed or not
|
||||
new_prefab_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'new_prefab.prefab')
|
||||
create_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreatePrefabInMemory', [new_entity_id], new_prefab_file_path)
|
||||
Report.result(Tests.create_prefab, create_prefab_result)
|
||||
|
||||
# Checks for prefab instantiation passed or not
|
||||
container_entity_id = prefab.PrefabPublicRequestBus(bus.Broadcast, 'InstantiatePrefab', new_prefab_file_path, EntityId(), EXPECTED_NEW_PREFAB_POSITION)
|
||||
Report.result(Tests.instantiate_prefab, container_entity_id.IsValid())
|
||||
|
||||
# Checks if the new prefab is at the correct position and if it fails, it will provide the expected postion and the actual postion of the entity in the Editor log
|
||||
new_prefab_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", container_entity_id)
|
||||
is_at_position = new_prefab_position.IsClose(EXPECTED_NEW_PREFAB_POSITION)
|
||||
Report.result(Tests.new_prefab_position, is_at_position)
|
||||
if not is_at_position:
|
||||
Report.info(f'Expected position: {EXPECTED_NEW_PREFAB_POSITION.ToString()}, actual position: {new_prefab_position.ToString()}')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(PrefabLevel_BasicWorkflow)
|
||||
@ -0,0 +1,44 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
|
||||
UI Apps: AutomatedTesting.GameLauncher
|
||||
Launch AutomatedTesting.GameLauncher with Simple level
|
||||
Test should run in both gpu and non gpu
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import psutil
|
||||
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
import editor_python_test_tools.hydra_test_utils as editor_test_utils
|
||||
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
|
||||
from ly_remote_console.remote_console_commands import (
|
||||
send_command_and_expect_response as send_command_and_expect_response,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("launcher_platform", ["windows"])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("level", ["Simple"])
|
||||
@pytest.mark.SUITE_smoke
|
||||
class TestRemoteConsoleLoadLevelWorks(object):
|
||||
@pytest.fixture
|
||||
def remote_console_instance(self, request):
|
||||
console = RemoteConsole()
|
||||
|
||||
def teardown():
|
||||
if console.connected:
|
||||
console.stop()
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return console
|
||||
|
||||
def test_RemoteConsole_LoadLevel_Works(self, launcher, level, remote_console_instance, launcher_platform):
|
||||
expected_lines = ['Level system is loading "Simple"']
|
||||
|
||||
editor_test_utils.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, null_renderer=True)
|
||||
@ -0,0 +1,43 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
|
||||
UI Apps: AutomatedTesting.GameLauncher
|
||||
Launch AutomatedTesting.GameLauncher with Simple level
|
||||
Test should run in both gpu and non gpu
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import psutil
|
||||
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
import editor_python_test_tools.hydra_test_utils as editor_test_utils
|
||||
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
|
||||
from ly_remote_console.remote_console_commands import (
|
||||
send_command_and_expect_response as send_command_and_expect_response,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("launcher_platform", ["windows"])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("level", ["Simple"])
|
||||
class TestRemoteConsoleLoadLevelWorks(object):
|
||||
@pytest.fixture
|
||||
def remote_console_instance(self, request):
|
||||
console = RemoteConsole()
|
||||
|
||||
def teardown():
|
||||
if console.connected:
|
||||
console.stop()
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return console
|
||||
|
||||
def test_RemoteConsole_LoadLevel_Works(self, launcher, level, remote_console_instance, launcher_platform):
|
||||
expected_lines = ['Level system is loading "Simple"']
|
||||
|
||||
editor_test_utils.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines, null_renderer=False)
|
||||
@ -1,105 +0,0 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
|
||||
UI Apps: AutomatedTesting.GameLauncher
|
||||
Launch AutomatedTesting.GameLauncher with Simple level
|
||||
Test should run in both gpu and non gpu
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import psutil
|
||||
|
||||
# Bail on the test if ly_test_tools doesn't exist.
|
||||
pytest.importorskip("ly_test_tools")
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
|
||||
from ly_remote_console.remote_console_commands import (
|
||||
send_command_and_expect_response as send_command_and_expect_response,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("launcher_platform", ["windows"])
|
||||
@pytest.mark.parametrize("project", ["AutomatedTesting"])
|
||||
@pytest.mark.parametrize("level", ["Simple"])
|
||||
@pytest.mark.SUITE_sandbox
|
||||
class TestRemoteConsoleLoadLevelWorks(object):
|
||||
@pytest.fixture
|
||||
def remote_console_instance(self, request):
|
||||
console = RemoteConsole()
|
||||
|
||||
def teardown():
|
||||
if console.connected:
|
||||
console.stop()
|
||||
|
||||
request.addfinalizer(teardown)
|
||||
|
||||
return console
|
||||
|
||||
def test_RemoteConsole_LoadLevel_Works(self, launcher, level, remote_console_instance, launcher_platform):
|
||||
expected_lines = ['Level system is loading "Simple"']
|
||||
|
||||
self.launch_and_validate_results_launcher(launcher, level, remote_console_instance, expected_lines)
|
||||
|
||||
def launch_and_validate_results_launcher(
|
||||
self,
|
||||
launcher,
|
||||
level,
|
||||
remote_console_instance,
|
||||
expected_lines,
|
||||
null_renderer=False,
|
||||
port_listener_timeout=120,
|
||||
log_monitor_timeout=300,
|
||||
remote_console_port=4600,
|
||||
):
|
||||
"""
|
||||
Runs the launcher with the specified level, and monitors Game.log for expected lines.
|
||||
:param launcher: Configured launcher object to run test against.
|
||||
:param level: The level to load in the launcher.
|
||||
:param remote_console_instance: Configured Remote Console object.
|
||||
:param expected_lines: Expected lines to search log for.
|
||||
:oaram null_renderer: Specifies the test does not require the renderer. Defaults to True.
|
||||
:param port_listener_timeout: Timeout for verifying successful connection to Remote Console.
|
||||
:param log_monitor_timeout: Timeout for monitoring for lines in Game.log
|
||||
:param remote_console_port: The port used to communicate with the Remote Console.
|
||||
"""
|
||||
|
||||
def _check_for_listening_port(port):
|
||||
"""
|
||||
Checks to see if the connection to the designated port was established.
|
||||
:param port: Port to listen to.
|
||||
:return: True if port is listening.
|
||||
"""
|
||||
port_listening = False
|
||||
for conn in psutil.net_connections():
|
||||
if "port={}".format(port) in str(conn):
|
||||
port_listening = True
|
||||
return port_listening
|
||||
|
||||
if null_renderer:
|
||||
launcher.args.extend(["-NullRenderer"])
|
||||
|
||||
# Start the Launcher
|
||||
with launcher.start():
|
||||
|
||||
# Ensure Remote Console can be reached
|
||||
waiter.wait_for(
|
||||
lambda: _check_for_listening_port(remote_console_port),
|
||||
port_listener_timeout,
|
||||
exc=AssertionError("Port {} not listening.".format(remote_console_port)),
|
||||
)
|
||||
remote_console_instance.start(timeout=30)
|
||||
|
||||
# Load the specified level in the launcher
|
||||
send_command_and_expect_response(
|
||||
remote_console_instance, f"loadlevel {level}", "LEVEL_LOAD_END", timeout=30
|
||||
)
|
||||
|
||||
# Monitor the console for expected lines
|
||||
for line in expected_lines:
|
||||
assert remote_console_instance.expect_log_line(
|
||||
line, log_monitor_timeout
|
||||
), f"Expected line not found: {line}"
|
||||
@ -0,0 +1,53 @@
|
||||
{
|
||||
"ContainerEntity": {
|
||||
"Id": "Entity_[1146574390643]",
|
||||
"Name": "Level",
|
||||
"Components": {
|
||||
"Component_[10641544592923449938]": {
|
||||
"$type": "EditorInspectorComponent",
|
||||
"Id": 10641544592923449938
|
||||
},
|
||||
"Component_[12039882709170782873]": {
|
||||
"$type": "EditorOnlyEntityComponent",
|
||||
"Id": 12039882709170782873
|
||||
},
|
||||
"Component_[12265484671603697631]": {
|
||||
"$type": "EditorPendingCompositionComponent",
|
||||
"Id": 12265484671603697631
|
||||
},
|
||||
"Component_[14126657869720434043]": {
|
||||
"$type": "EditorEntitySortComponent",
|
||||
"Id": 14126657869720434043
|
||||
},
|
||||
"Component_[15230859088967841193]": {
|
||||
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
|
||||
"Id": 15230859088967841193,
|
||||
"Parent Entity": ""
|
||||
},
|
||||
"Component_[16239496886950819870]": {
|
||||
"$type": "EditorDisabledCompositionComponent",
|
||||
"Id": 16239496886950819870
|
||||
},
|
||||
"Component_[5688118765544765547]": {
|
||||
"$type": "EditorEntityIconComponent",
|
||||
"Id": 5688118765544765547
|
||||
},
|
||||
"Component_[6545738857812235305]": {
|
||||
"$type": "SelectionComponent",
|
||||
"Id": 6545738857812235305
|
||||
},
|
||||
"Component_[7247035804068349658]": {
|
||||
"$type": "EditorPrefabComponent",
|
||||
"Id": 7247035804068349658
|
||||
},
|
||||
"Component_[9307224322037797205]": {
|
||||
"$type": "EditorLockComponent",
|
||||
"Id": 9307224322037797205
|
||||
},
|
||||
"Component_[9562516168917670048]": {
|
||||
"$type": "EditorVisibilityComponent",
|
||||
"Id": 9562516168917670048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
#include "SkeletonHierarchy.h"
|
||||
|
||||
using namespace Skeleton;
|
||||
|
||||
/*
|
||||
|
||||
CHierarchy
|
||||
|
||||
*/
|
||||
|
||||
CHierarchy::CHierarchy()
|
||||
{
|
||||
}
|
||||
|
||||
CHierarchy::~CHierarchy()
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
uint32 CHierarchy::AddNode(const char* name, const QuatT& pose, int32 parent)
|
||||
{
|
||||
int32 index = FindNodeIndexByName(name);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
m_nodes.push_back(SNode());
|
||||
index = int32(m_nodes.size() - 1);
|
||||
}
|
||||
|
||||
m_nodes[index].name = name;
|
||||
m_nodes[index].pose = pose;
|
||||
m_nodes[index].parent = parent;
|
||||
return uint32(index);
|
||||
}
|
||||
|
||||
int32 CHierarchy::FindNodeIndexByName(const char* name) const
|
||||
{
|
||||
uint32 count = uint32(m_nodes.size());
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
if (::_stricmp(m_nodes[i].name, name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const CHierarchy::SNode* CHierarchy::FindNode(const char* name) const
|
||||
{
|
||||
int32 index = FindNodeIndexByName(name);
|
||||
return index < 0 ? nullptr : &m_nodes[index];
|
||||
}
|
||||
|
||||
void CHierarchy::CreateFrom(IDefaultSkeleton* pIDefaultSkeleton)
|
||||
{
|
||||
const uint32 jointCount = pIDefaultSkeleton->GetJointCount();
|
||||
|
||||
m_nodes.clear();
|
||||
m_nodes.reserve(jointCount);
|
||||
for (uint32 i = 0; i < jointCount; ++i)
|
||||
{
|
||||
m_nodes.push_back(SNode());
|
||||
|
||||
m_nodes.back().name = pIDefaultSkeleton->GetJointNameByID(int32(i));
|
||||
m_nodes.back().pose = pIDefaultSkeleton->GetDefaultAbsJointByID(int32(i));
|
||||
|
||||
m_nodes.back().parent = pIDefaultSkeleton->GetJointParentIDByID(int32(i));
|
||||
}
|
||||
|
||||
ValidateReferences();
|
||||
}
|
||||
|
||||
void CHierarchy::ValidateReferences()
|
||||
{
|
||||
uint32 nodeCount = m_nodes.size();
|
||||
if (!nodeCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < nodeCount; ++i)
|
||||
{
|
||||
if (m_nodes[i].parent < nodeCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_nodes[i].parent = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void CHierarchy::AbsoluteToRelative(const QuatT* pSource, QuatT* pDestination)
|
||||
{
|
||||
uint32 count = uint32(m_nodes.size());
|
||||
std::vector<QuatT> absolutes(count);
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
absolutes[i] = pSource[i];
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
int32 parent = m_nodes[i].parent;
|
||||
if (parent < 0)
|
||||
{
|
||||
pDestination[i] = absolutes[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
pDestination[i].t = (absolutes[i].t - absolutes[parent].t) * absolutes[parent].q;
|
||||
pDestination[i].q = absolutes[parent].q.GetInverted() * absolutes[i].q;
|
||||
}
|
||||
}
|
||||
|
||||
bool CHierarchy::SerializeTo(XmlNodeRef& node)
|
||||
{
|
||||
XmlNodeRef hierarchy = node->newChild("Hierarchy");
|
||||
|
||||
uint32 nodeCount = uint32(m_nodes.size());
|
||||
std::vector<IXmlNode*> nodes(nodeCount);
|
||||
for (uint32 i = 0; i < nodeCount; ++i)
|
||||
{
|
||||
XmlNodeRef parent = hierarchy;
|
||||
if (m_nodes[i].parent > -1)
|
||||
{
|
||||
parent = nodes[m_nodes[i].parent];
|
||||
}
|
||||
|
||||
nodes[i] = parent->newChild("Node");
|
||||
nodes[i]->setAttr("name", m_nodes[i].name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_EDITOR_ANIMATION_SKELETONHIERARCHY_H
|
||||
#define CRYINCLUDE_EDITOR_ANIMATION_SKELETONHIERARCHY_H
|
||||
#pragma once
|
||||
|
||||
namespace Skeleton {
|
||||
class CHierarchy
|
||||
: public _reference_target_t
|
||||
{
|
||||
public:
|
||||
struct SNode
|
||||
{
|
||||
string name;
|
||||
QuatT pose;
|
||||
|
||||
int32 parent;
|
||||
|
||||
/* TODO: Implement
|
||||
uint32 childrenIndex;
|
||||
uint32 childrenCount;
|
||||
*/
|
||||
};
|
||||
|
||||
public:
|
||||
CHierarchy();
|
||||
~CHierarchy();
|
||||
|
||||
public:
|
||||
uint32 AddNode(const char* name, const QuatT& pose, int32 parent = -1);
|
||||
uint32 GetNodeCount() const { return uint32(m_nodes.size()); }
|
||||
SNode* GetNode(uint32 index) { return &m_nodes[index]; }
|
||||
const SNode* GetNode(uint32 index) const { return &m_nodes[index]; }
|
||||
int32 FindNodeIndexByName(const char* name) const;
|
||||
const SNode* FindNode(const char* name) const;
|
||||
void ClearNodes() { m_nodes.clear(); }
|
||||
|
||||
void CreateFrom(IDefaultSkeleton* rIDefaultSkeleton);
|
||||
void ValidateReferences();
|
||||
|
||||
void AbsoluteToRelative(const QuatT* pSource, QuatT* pDestination);
|
||||
|
||||
bool SerializeTo(XmlNodeRef& node);
|
||||
|
||||
private:
|
||||
std::vector<SNode> m_nodes;
|
||||
};
|
||||
} // namespace Skeleton
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_ANIMATION_SKELETONHIERARCHY_H
|
||||
@ -1,368 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "SkeletonMapper.h"
|
||||
|
||||
using namespace Skeleton;
|
||||
|
||||
/*
|
||||
|
||||
CMapper
|
||||
|
||||
*/
|
||||
|
||||
CMapper::CMapper()
|
||||
{
|
||||
}
|
||||
|
||||
CMapper::~CMapper()
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void CMapper::CreateFromHierarchy()
|
||||
{
|
||||
m_nodes.clear();
|
||||
|
||||
uint32 nodeCount = m_hierarchy.GetNodeCount();
|
||||
m_nodes.resize(nodeCount);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
uint32 CMapper::CreateLocation(const char* name)
|
||||
{
|
||||
int32 index = FindLocation(name);
|
||||
if (index < 1)
|
||||
{
|
||||
CMapperLocation* pLocation = new CMapperLocation();
|
||||
pLocation->SetName(name);
|
||||
m_locations.push_back(pLocation);
|
||||
}
|
||||
return uint32(m_locations.size() - 1);
|
||||
}
|
||||
|
||||
void CMapper::ClearLocations()
|
||||
{
|
||||
uint32 count = uint32(m_nodes.size());
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
m_nodes[i].position = nullptr;
|
||||
m_nodes[i].orientation = nullptr;
|
||||
}
|
||||
|
||||
m_locations.clear();
|
||||
}
|
||||
|
||||
int32 CMapper::FindLocation(const char* name) const
|
||||
{
|
||||
uint32 count = uint32(m_locations.size());
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
{
|
||||
if (::_stricmp(m_locations[i]->GetName(), name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return int32(i);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CMapper::SetLocation(CMapperLocation& location)
|
||||
{
|
||||
int32 index = FindLocation(location.GetName());
|
||||
if (index < 0)
|
||||
{
|
||||
m_locations.push_back(&location);
|
||||
return;
|
||||
}
|
||||
|
||||
m_locations[index] = &location;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool CMapper::CreateLocationsHierarchy(uint32 index, CHierarchy& hierarchy, int32 hierarchyParent)
|
||||
{
|
||||
if (NodeHasLocation(index))
|
||||
{
|
||||
const CHierarchy::SNode* pNode = m_hierarchy.GetNode(index);
|
||||
uint32 nodeIndex = hierarchy.AddNode(pNode->name, pNode->pose, hierarchyParent);
|
||||
hierarchyParent = uint32(nodeIndex);
|
||||
}
|
||||
|
||||
std::vector<uint32> children;
|
||||
GetChildrenIndices(index, children);
|
||||
uint32 childCount = uint32(children.size());
|
||||
for (uint32 i = 0; i < childCount; ++i)
|
||||
{
|
||||
CreateLocationsHierarchy(children[i], hierarchy, hierarchyParent);
|
||||
}
|
||||
|
||||
return hierarchy.GetNodeCount() != 0;
|
||||
}
|
||||
|
||||
bool CMapper::CreateLocationsHierarchy(CHierarchy& hierarchy)
|
||||
{
|
||||
hierarchy.ClearNodes();
|
||||
if (!CreateLocationsHierarchy(0, hierarchy, -1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hierarchy.ValidateReferences();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMapper::Map(QuatT* pResult)
|
||||
{
|
||||
uint32 outputCount = m_hierarchy.GetNodeCount();
|
||||
std::vector<Quat> absolutes(outputCount);
|
||||
for (uint32 i = 0; i < outputCount; ++i)
|
||||
{
|
||||
pResult[i].SetIdentity();
|
||||
absolutes[i].SetIdentity();
|
||||
|
||||
CHierarchy::SNode* pNode = m_hierarchy.GetNode(i);
|
||||
if (!pNode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CHierarchy::SNode* pParent = pNode->parent < 0 ?
|
||||
nullptr : m_hierarchy.GetNode(pNode->parent);
|
||||
if (pParent)
|
||||
{
|
||||
pResult[i].t =
|
||||
(pNode->pose.t - pParent->pose.t) * pParent->pose.q;
|
||||
}
|
||||
|
||||
if (m_nodes[i].position)
|
||||
{
|
||||
pResult[i].t = m_nodes[i].position->Compute().t;
|
||||
}
|
||||
|
||||
if (m_nodes[i].orientation)
|
||||
{
|
||||
absolutes[i] = m_nodes[i].orientation->Compute().q;
|
||||
}
|
||||
else if (pParent)
|
||||
{
|
||||
Quat relative = pParent->pose.q.GetInverted() * pNode->pose.q;
|
||||
absolutes[i] = absolutes[pNode->parent] * relative;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < outputCount; ++i)
|
||||
{
|
||||
CHierarchy::SNode* pNode = m_hierarchy.GetNode(i);
|
||||
if (!pNode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CHierarchy::SNode* pParent = pNode->parent < 0 ?
|
||||
nullptr : m_hierarchy.GetNode(pNode->parent);
|
||||
if (!pParent)
|
||||
{
|
||||
pResult[i].q = absolutes[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
pResult[i].q = absolutes[i];
|
||||
if (!m_nodes[i].position)
|
||||
{
|
||||
pResult[i].t = pResult[pNode->parent].t +
|
||||
pResult[i].t * absolutes[pNode->parent].GetInverted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool CMapper::NodeHasLocation(uint32 index)
|
||||
{
|
||||
if (CMapperOperator* pOperator = m_nodes[index].position)
|
||||
{
|
||||
if (pOperator->IsOfClass("Location"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (pOperator->HasLinksOfClass("Location"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (CMapperOperator* pOperator = m_nodes[index].orientation)
|
||||
{
|
||||
if (pOperator->IsOfClass("Location"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (pOperator->HasLinksOfClass("Location"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CMapper::GetChildrenIndices(uint32 parent, std::vector<uint32>& children)
|
||||
{
|
||||
uint32 nodeCount = m_hierarchy.GetNodeCount();
|
||||
for (uint32 i = 0; i < nodeCount; ++i)
|
||||
{
|
||||
if (m_hierarchy.GetNode(i)->parent != parent)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
children.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool CMapper::ChildrenHaveLocation(uint32 index)
|
||||
{
|
||||
std::vector<uint32> children;
|
||||
GetChildrenIndices(index, children);
|
||||
|
||||
uint32 childrenCount = uint32(children.size());
|
||||
for (uint32 i = 0; i < childrenCount; ++i)
|
||||
{
|
||||
if (ChildrenHaveLocation(children[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMapper::NodeOrChildrenHaveLocation(uint32 index)
|
||||
{
|
||||
if (NodeHasLocation(index))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint32> children;
|
||||
GetChildrenIndices(index, children);
|
||||
|
||||
uint32 childrenCount = uint32(children.size());
|
||||
for (uint32 i = 0; i < childrenCount; ++i)
|
||||
{
|
||||
if (NodeOrChildrenHaveLocation(children[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMapper::SerializeTo(XmlNodeRef& node)
|
||||
{
|
||||
XmlNodeRef hierarchy = node->newChild("Hierarchy");
|
||||
|
||||
uint32 nodeCount = GetNodeCount();
|
||||
std::vector<IXmlNode*> nodes(nodeCount);
|
||||
for (uint32 i = 0; i < nodeCount; ++i)
|
||||
{
|
||||
if (!NodeOrChildrenHaveLocation(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CHierarchy::SNode* pNode = m_hierarchy.GetNode(i);
|
||||
if (!pNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlNodeRef xmlParent = hierarchy;
|
||||
int32 parent = pNode->parent;
|
||||
if (parent > -1)
|
||||
{
|
||||
xmlParent = nodes[parent];
|
||||
}
|
||||
|
||||
nodes[i] = xmlParent->newChild("Node");
|
||||
nodes[i]->setAttr("name", pNode->name);
|
||||
|
||||
if (CMapperOperator* pOperator = m_nodes[i].position)
|
||||
{
|
||||
XmlNodeRef position = nodes[i]->newChild("Position");
|
||||
XmlNodeRef child = position->newChild("Operator");
|
||||
if (!pOperator->SerializeWithLinksTo(child))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (CMapperOperator* pOperator = m_nodes[i].orientation)
|
||||
{
|
||||
XmlNodeRef orientation = nodes[i]->newChild("Orientation");
|
||||
XmlNodeRef child = orientation->newChild("Operator");
|
||||
if (!pOperator->SerializeWithLinksTo(child))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMapper::SerializeFrom(XmlNodeRef& node, int32 parent)
|
||||
{
|
||||
int childCount = uint32(node->getChildCount());
|
||||
for (int i = 0; i < childCount; ++i)
|
||||
{
|
||||
XmlNodeRef child = node->getChild(i);
|
||||
if (::_stricmp(child->getTag(), "Node"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 index = m_hierarchy.AddNode(child->getAttr("name"), QuatT(IDENTITY), parent);
|
||||
if (!SerializeFrom(child, int32(index)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMapper::SerializeFrom(XmlNodeRef& node)
|
||||
{
|
||||
XmlNodeRef hierarchy = node->findChild("Hierarchy");
|
||||
if (!hierarchy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hierarchy.ClearNodes();
|
||||
|
||||
if (!SerializeFrom(hierarchy, -1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nodes.resize(m_hierarchy.GetNodeCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_EDITOR_ANIMATION_SKELETONMAPPER_H
|
||||
#define CRYINCLUDE_EDITOR_ANIMATION_SKELETONMAPPER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "SkeletonHierarchy.h"
|
||||
#include "SkeletonMapperOperator.h"
|
||||
|
||||
namespace Skeleton {
|
||||
class CMapper
|
||||
{
|
||||
public:
|
||||
struct SNode
|
||||
{
|
||||
_smart_ptr<CMapperOperator> position;
|
||||
_smart_ptr<CMapperOperator> orientation;
|
||||
};
|
||||
|
||||
public:
|
||||
CMapper();
|
||||
~CMapper();
|
||||
|
||||
public:
|
||||
CHierarchy& GetHierarchy() { return m_hierarchy; }
|
||||
void CreateFromHierarchy();
|
||||
|
||||
uint32 GetNodeCount() const { return uint32(m_nodes.size()); }
|
||||
SNode* GetNode(uint32 index) { return &m_nodes[index]; }
|
||||
const SNode* GetNode(uint32 index) const { return &m_nodes[index]; }
|
||||
|
||||
uint32 CreateLocation(const char* name);
|
||||
void ClearLocations();
|
||||
int32 FindLocation(const char* name) const;
|
||||
|
||||
uint32 GetLocationCount() const { return uint32(m_locations.size()); }
|
||||
void SetLocation(CMapperLocation& location);
|
||||
CMapperLocation* GetLocation(uint32 index) { return m_locations[index]; }
|
||||
const CMapperLocation* GetLocation(uint32 index) const { return m_locations[index]; }
|
||||
|
||||
bool CreateLocationsHierarchy(CHierarchy& hierarchy);
|
||||
|
||||
void Map(QuatT* pResult);
|
||||
|
||||
bool SerializeTo(XmlNodeRef& node);
|
||||
bool SerializeFrom(XmlNodeRef& node);
|
||||
|
||||
private:
|
||||
bool NodeHasLocation(uint32 index);
|
||||
bool ChildrenHaveLocation(uint32 index);
|
||||
bool NodeOrChildrenHaveLocation(uint32 index);
|
||||
|
||||
bool SerializeFrom(XmlNodeRef& node, int32 parent);
|
||||
|
||||
bool CreateLocationsHierarchy(uint32 index, CHierarchy& hierarchy, int32 hierarchyParent = -1);
|
||||
|
||||
// TEMP
|
||||
void GetChildrenIndices(uint32 parent, std::vector<uint32>& children);
|
||||
|
||||
private:
|
||||
CHierarchy m_hierarchy;
|
||||
std::vector<_smart_ptr<CMapperLocation> > m_locations;
|
||||
|
||||
std::vector<SNode> m_nodes;
|
||||
};
|
||||
} // namespace Skeleton
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_ANIMATION_SKELETONMAPPER_H
|
||||
@ -1,284 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "SkeletonMapperOperator.h"
|
||||
|
||||
using namespace Skeleton;
|
||||
|
||||
/*
|
||||
|
||||
CMapperOperatorDesc
|
||||
|
||||
*/
|
||||
|
||||
std::vector<CMapperOperatorDesc*> CMapperOperatorDesc::s_descs;
|
||||
|
||||
//
|
||||
|
||||
CMapperOperatorDesc::CMapperOperatorDesc(const char* name)
|
||||
{
|
||||
s_descs.push_back(this);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
CMapperOperator
|
||||
|
||||
*/
|
||||
|
||||
CMapperOperator::CMapperOperator(const char* className, uint32 positionCount, uint32 orientationCount)
|
||||
{
|
||||
m_className = className;
|
||||
m_position.resize(positionCount, nullptr);
|
||||
m_orientation.resize(orientationCount, nullptr);
|
||||
}
|
||||
|
||||
CMapperOperator::~CMapperOperator()
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool CMapperOperator::IsOfClass(const char* className)
|
||||
{
|
||||
if (::_stricmp(m_className, className))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 CMapperOperator::HasLinksOfClass(const char* className)
|
||||
{
|
||||
uint32 count = 0;
|
||||
|
||||
uint32 positionCount = m_position.size();
|
||||
for (uint32 i = 0; i < positionCount; ++i)
|
||||
{
|
||||
CMapperOperator* pOperator = m_position[i];
|
||||
if (!pOperator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pOperator->IsOfClass(className))
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 orientationCount = m_orientation.size();
|
||||
for (uint32 i = 0; i < orientationCount; ++i)
|
||||
{
|
||||
CMapperOperator* pOperator = m_orientation[i];
|
||||
if (!pOperator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pOperator->IsOfClass(className))
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool CMapperOperator::SerializeTo(XmlNodeRef& node)
|
||||
{
|
||||
node->setAttr("class", m_className);
|
||||
|
||||
uint32 parameterCount = uint32(m_parameters.size());
|
||||
for (uint32 i = 0; i < parameterCount; ++i)
|
||||
{
|
||||
m_parameters[i]->Serialize(node, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMapperOperator::SerializeFrom(XmlNodeRef& node)
|
||||
{
|
||||
uint32 parameterCount = uint32(m_parameters.size());
|
||||
for (uint32 i = 0; i < parameterCount; ++i)
|
||||
{
|
||||
m_parameters[i]->Serialize(node, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMapperOperator::SerializeWithLinksTo(XmlNodeRef& node)
|
||||
{
|
||||
if (!SerializeTo(node))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 positionCount = uint32(m_position.size());
|
||||
for (uint32 i = 0; i < positionCount; ++i)
|
||||
{
|
||||
CMapperOperator* pOperator = m_position[i];
|
||||
if (!pOperator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
XmlNodeRef position = node->newChild("Position");
|
||||
position->setAttr("index", i);
|
||||
|
||||
XmlNodeRef child = position->newChild("Operator");
|
||||
if (!pOperator->SerializeWithLinksTo(child))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 orientationCount = uint32(m_orientation.size());
|
||||
for (uint32 i = 0; i < orientationCount; ++i)
|
||||
{
|
||||
CMapperOperator* pOperator = m_orientation[i];
|
||||
if (!pOperator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
XmlNodeRef orientation = node->newChild("Orientation");
|
||||
orientation->setAttr("index", i);
|
||||
|
||||
XmlNodeRef child = orientation->newChild("Operator");
|
||||
if (!pOperator->SerializeWithLinksTo(child))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMapperOperator::SerializeWithLinksFrom(XmlNodeRef& node)
|
||||
{
|
||||
if (!SerializeFrom(node))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
|
||||
CMapperOperator_Transform
|
||||
|
||||
*/
|
||||
|
||||
class CMapperOperator_Transform
|
||||
: public CMapperOperator
|
||||
{
|
||||
public:
|
||||
CMapperOperator_Transform()
|
||||
: CMapperOperator("Transform", 1, 1)
|
||||
{
|
||||
m_pAngles = new CVariable<Vec3>();
|
||||
m_pAngles->SetName("rotation");
|
||||
m_pAngles->Set(Vec3(0.0f, 0.0f, 0.0f));
|
||||
m_pAngles->SetLimits(-180.0f, 180.0f);
|
||||
AddParameter(*m_pAngles);
|
||||
|
||||
m_pVector = new CVariable<Vec3>();
|
||||
m_pVector->SetName("vector");
|
||||
m_pVector->Set(Vec3(0.0f, 0.0f, 0.0f));
|
||||
AddParameter(*m_pVector);
|
||||
|
||||
m_pScale = new CVariable<Vec3>();
|
||||
m_pScale->SetName("scale");
|
||||
m_pScale->Set(Vec3(1.0f, 1.0f, 1.0f));
|
||||
AddParameter(*m_pScale);
|
||||
}
|
||||
|
||||
// CMapperOperator
|
||||
public:
|
||||
virtual QuatT CMapperOperator_Transform::Compute()
|
||||
{
|
||||
QuatT result(IDENTITY);
|
||||
m_pVector->Get(result.t);
|
||||
|
||||
Vec3 scale;
|
||||
m_pScale->Get(scale);
|
||||
|
||||
Vec3 angles;
|
||||
m_pAngles->Get(angles);
|
||||
|
||||
result.q = Quat::CreateRotationXYZ(
|
||||
Ang3(DEG2RAD(angles.x), DEG2RAD(angles.y), DEG2RAD(angles.z)));
|
||||
|
||||
if (CMapperOperator* pOperator = GetPosition(0))
|
||||
{
|
||||
result.t = pOperator->Compute().t.CompMul(scale) + result.t;
|
||||
}
|
||||
if (CMapperOperator* pOperator = GetOrientation(0))
|
||||
{
|
||||
result.q = pOperator->Compute().q * result.q;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
CVariable<Vec3>* m_pVector;
|
||||
CVariable<Vec3>* m_pAngles;
|
||||
CVariable<Vec3>* m_pScale;
|
||||
};
|
||||
|
||||
SkeletonMapperOperatorRegister(Transform, CMapperOperator_Transform)
|
||||
|
||||
class CMapperOperator_PositionsToOrientation
|
||||
: public CMapperOperator
|
||||
{
|
||||
public:
|
||||
CMapperOperator_PositionsToOrientation()
|
||||
: CMapperOperator("PositionsToOrientation", 3, 0)
|
||||
{
|
||||
}
|
||||
|
||||
// CMapperOperator
|
||||
public:
|
||||
virtual QuatT Compute()
|
||||
{
|
||||
CMapperOperator* pOperator0 = GetPosition(0);
|
||||
CMapperOperator* pOperator1 = GetPosition(1);
|
||||
CMapperOperator* pOperator2 = GetPosition(2);
|
||||
if (!pOperator0 || !pOperator1 || !pOperator2)
|
||||
{
|
||||
return QuatT(IDENTITY);
|
||||
}
|
||||
|
||||
Vec3 p0 = pOperator0->Compute().t;
|
||||
Vec3 p1 = pOperator1->Compute().t;
|
||||
Vec3 p2 = pOperator2->Compute().t;
|
||||
|
||||
Vec3 m = (p1 + p2) * 0.5f;
|
||||
Vec3 y = (m - p0).GetNormalized();
|
||||
Vec3 z = (p1 - p2).GetNormalized();
|
||||
Vec3 x = y % z;
|
||||
z = x % y;
|
||||
|
||||
Matrix33 m33;
|
||||
m33.SetFromVectors(x, y, z);
|
||||
QuatT result(IDENTITY);
|
||||
result.q = Quat(m33);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
SkeletonMapperOperatorRegister(PositionsToOrientation, CMapperOperator_PositionsToOrientation)
|
||||
@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_EDITOR_ANIMATION_SKELETONMAPPEROPERATOR_H
|
||||
#define CRYINCLUDE_EDITOR_ANIMATION_SKELETONMAPPEROPERATOR_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "../Util/Variable.h"
|
||||
|
||||
#undef GetClassName
|
||||
|
||||
#define SkeletonMapperOperatorRegister(name, className) \
|
||||
class CMapperOperatorDesc_##name \
|
||||
: public CMapperOperatorDesc \
|
||||
{ \
|
||||
public: \
|
||||
CMapperOperatorDesc_##name() \
|
||||
: CMapperOperatorDesc(#name) { } \
|
||||
protected: \
|
||||
virtual const char* GetName() { return #name; } \
|
||||
virtual CMapperOperator* Create() { return new className(); } \
|
||||
} mapperOperatorDesc__##name;
|
||||
|
||||
namespace Skeleton {
|
||||
class CMapperOperator;
|
||||
|
||||
class CMapperOperatorDesc
|
||||
{
|
||||
public:
|
||||
static uint32 GetCount() { return uint32(s_descs.size()); }
|
||||
static const char* GetName(uint32 index) { return s_descs[index]->GetName(); }
|
||||
static CMapperOperator* Create(uint32 index) { return s_descs[index]->Create(); }
|
||||
|
||||
private:
|
||||
static std::vector<CMapperOperatorDesc*> s_descs;
|
||||
|
||||
public:
|
||||
CMapperOperatorDesc(const char* name);
|
||||
|
||||
protected:
|
||||
virtual const char* GetName() = 0;
|
||||
virtual CMapperOperator* Create() = 0;
|
||||
};
|
||||
|
||||
class CMapperOperator
|
||||
: public _reference_target_t
|
||||
{
|
||||
protected:
|
||||
CMapperOperator(const char* className, uint32 positionCount, uint32 orientationCount);
|
||||
~CMapperOperator();
|
||||
|
||||
public:
|
||||
const char* GetClassName() { return m_className; }
|
||||
|
||||
uint32 GetPositionCount() const { return uint32(m_position.size()); }
|
||||
void SetPosition(uint32 index, CMapperOperator* pOperator) { m_position[index] = pOperator; }
|
||||
CMapperOperator* GetPosition(uint32 index) { return m_position[index]; }
|
||||
|
||||
uint32 GetOrientationCount() const { return uint32(m_orientation.size()); }
|
||||
void SetOrientation(uint32 index, CMapperOperator* pOperator) { m_orientation[index] = pOperator; }
|
||||
CMapperOperator* GetOrientation(uint32 index) { return m_orientation[index]; }
|
||||
|
||||
uint32 GetParameterCount() { return uint32(m_parameters.size()); }
|
||||
IVariable* GetParameter(uint32 index) { return m_parameters[index]; }
|
||||
|
||||
bool IsOfClass(const char* className);
|
||||
uint32 HasLinksOfClass(const char* className);
|
||||
|
||||
bool SerializeTo(XmlNodeRef& node);
|
||||
bool SerializeFrom(XmlNodeRef& node);
|
||||
|
||||
bool SerializeWithLinksTo(XmlNodeRef& node);
|
||||
bool SerializeWithLinksFrom(XmlNodeRef& node);
|
||||
|
||||
protected:
|
||||
void AddParameter(IVariable& variable) { m_parameters.push_back(&variable); }
|
||||
|
||||
public:
|
||||
virtual QuatT Compute() = 0;
|
||||
|
||||
private:
|
||||
const char* m_className;
|
||||
std::vector<_smart_ptr<CMapperOperator> > m_position;
|
||||
std::vector<_smart_ptr<CMapperOperator> > m_orientation;
|
||||
|
||||
std::vector<IVariablePtr> m_parameters;
|
||||
};
|
||||
|
||||
class CMapperLocation
|
||||
: public CMapperOperator
|
||||
{
|
||||
public:
|
||||
CMapperLocation()
|
||||
: CMapperOperator("Location", 0, 0)
|
||||
{
|
||||
m_pName = new CVariable<CString>();
|
||||
m_pName->SetName("name");
|
||||
m_pName->SetFlags(m_pName->GetFlags() | IVariable::UI_INVISIBLE);
|
||||
AddParameter(*m_pName);
|
||||
|
||||
m_pAxis = new CVariable<Vec3>();
|
||||
m_pAxis->SetName("axis");
|
||||
m_pAxis->SetLimits(-3.0f, +3.0f);
|
||||
m_pAxis->Set(Vec3(1.0f, 2.0f, 3.0f));
|
||||
AddParameter(*m_pAxis);
|
||||
|
||||
m_location = QuatT(IDENTITY);
|
||||
}
|
||||
|
||||
public:
|
||||
void SetName(const char* name) { m_pName->Set(name); }
|
||||
CString GetName() const { CString s; m_pName->Get(s); return s; }
|
||||
|
||||
void SetLocation(const QuatT& location) { m_location = location; }
|
||||
const QuatT& GetLocation() const { return m_location; }
|
||||
|
||||
// CMapperOperator
|
||||
public:
|
||||
virtual QuatT Compute()
|
||||
{
|
||||
Vec3 axis;
|
||||
m_pAxis->Get(axis);
|
||||
|
||||
uint32 x = fabs_tpl(axis.x);
|
||||
uint32 y = fabs_tpl(axis.y);
|
||||
uint32 z = fabs_tpl(axis.z);
|
||||
if (x < 1 || y < 1 || z < 1 ||
|
||||
x > 3 || y > 3 || y > 3 ||
|
||||
x == y || x == z || y == z)
|
||||
{
|
||||
return QuatT(IDENTITY);
|
||||
}
|
||||
|
||||
Matrix33 matrix;
|
||||
matrix.SetFromVectors(
|
||||
m_location.q.GetColumn(x - 1) * f32(::sgn(axis.x)),
|
||||
m_location.q.GetColumn(y - 1) * f32(::sgn(axis.y)),
|
||||
m_location.q.GetColumn(z - 1) * f32(::sgn(axis.z)));
|
||||
if (!matrix.IsOrthonormalRH(0.01f))
|
||||
{
|
||||
return QuatT(IDENTITY);
|
||||
}
|
||||
|
||||
QuatT result = m_location;
|
||||
result.q = Quat(matrix);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
CVariable<CString>* m_pName;
|
||||
CVariable<Vec3>* m_pAxis;
|
||||
|
||||
QuatT m_location;
|
||||
};
|
||||
} // namespace Skeleton
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_ANIMATION_SKELETONMAPPEROPERATOR_H
|
||||
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
#include "ControlMRU.h"
|
||||
|
||||
IMPLEMENT_XTP_CONTROL(CControlMRU, CXTPControlRecentFileList)
|
||||
|
||||
bool CControlMRU::DoesFileExist(CString& sFileName)
|
||||
{
|
||||
return (_access(sFileName.GetBuffer(), 0) == 0);
|
||||
}
|
||||
|
||||
void CControlMRU::OnCalcDynamicSize(DWORD dwMode)
|
||||
{
|
||||
CRecentFileList* pRecentFileList = GetRecentFileList();
|
||||
|
||||
if (!pRecentFileList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CString* pArrNames = pRecentFileList->m_arrNames;
|
||||
|
||||
assert(pArrNames != NULL);
|
||||
if (!pArrNames)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (m_nIndex + 1 < m_pControls->GetCount())
|
||||
{
|
||||
CXTPControl* pControl = m_pControls->GetAt(m_nIndex + 1);
|
||||
assert(pControl);
|
||||
if (pControl->GetID() >= GetFirstMruID()
|
||||
&& pControl->GetID() <= GetFirstMruID() + pRecentFileList->m_nSize)
|
||||
{
|
||||
m_pControls->Remove(pControl);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pParent->IsCustomizeMode())
|
||||
{
|
||||
m_dwHideFlags = 0;
|
||||
SetEnabled(TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pArrNames[0].IsEmpty())
|
||||
{
|
||||
SetCaption(CString(MAKEINTRESOURCE(IDS_NORECENTFILE_CAPTION)));
|
||||
SetDescription("No recently opened files");
|
||||
m_dwHideFlags = 0;
|
||||
SetEnabled(FALSE);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCaption(CString(MAKEINTRESOURCE(IDS_RECENTFILE_CAPTION)));
|
||||
SetDescription("Open this document");
|
||||
}
|
||||
|
||||
m_dwHideFlags |= xtpHideGeneric;
|
||||
|
||||
CString sCurDir = (Path::GetEditingGameDataFolder() + "\\").c_str();
|
||||
int nCurDir = sCurDir.GetLength();
|
||||
|
||||
CString strName;
|
||||
CString strTemp;
|
||||
int iLastValidMRU = 0;
|
||||
|
||||
for (int iMRU = 0; iMRU < pRecentFileList->m_nSize; iMRU++)
|
||||
{
|
||||
if (!pRecentFileList->GetDisplayName(strName, iMRU, sCurDir.GetBuffer(), nCurDir))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (DoesFileExist(pArrNames[iMRU]))
|
||||
{
|
||||
CString sCurEntryDir = pArrNames[iMRU].Left(nCurDir);
|
||||
|
||||
if (sCurEntryDir.CompareNoCase(sCurDir) != 0)
|
||||
{
|
||||
//unavailable entry (wrong directory)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//invalid entry (not existing)
|
||||
continue;
|
||||
}
|
||||
|
||||
int nId = iMRU + GetFirstMruID();
|
||||
|
||||
CXTPControl* pControl = m_pControls->Add(xtpControlButton, nId, _T(""), m_nIndex + iLastValidMRU + 1, TRUE);
|
||||
assert(pControl);
|
||||
|
||||
pControl->SetCaption(CXTPControlWindowList::ConstructCaption(strName, iLastValidMRU + 1));
|
||||
pControl->SetFlags(xtpFlagManualUpdate);
|
||||
pControl->SetBeginGroup(iLastValidMRU == 0 && m_nIndex != 0);
|
||||
pControl->SetParameter(pArrNames[iMRU]);
|
||||
|
||||
CString sDescription = "Open file: " + pArrNames[iMRU];
|
||||
pControl->SetDescription(sDescription);
|
||||
|
||||
if ((GetFlags() & xtpFlagWrapRow) && iMRU == 0)
|
||||
{
|
||||
pControl->SetFlags(pControl->GetFlags() | xtpFlagWrapRow);
|
||||
}
|
||||
|
||||
++iLastValidMRU;
|
||||
}
|
||||
|
||||
//if no entry was valid, treat as none would exist
|
||||
if (iLastValidMRU == 0)
|
||||
{
|
||||
SetCaption(CString(MAKEINTRESOURCE(IDS_NORECENTFILE_CAPTION)));
|
||||
SetDescription("No recently opened files");
|
||||
m_dwHideFlags = 0;
|
||||
SetEnabled(FALSE);
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef CRYINCLUDE_EDITOR_CONTROLMRU_H
|
||||
#define CRYINCLUDE_EDITOR_CONTROLMRU_H
|
||||
|
||||
class CControlMRU
|
||||
: public CXTPControlRecentFileList
|
||||
{
|
||||
protected:
|
||||
virtual void OnCalcDynamicSize(DWORD dwMode);
|
||||
|
||||
private:
|
||||
DECLARE_XTP_CONTROL(CControlMRU)
|
||||
bool DoesFileExist(CString& sFileName);
|
||||
};
|
||||
#endif // CRYINCLUDE_EDITOR_CONTROLMRU_H
|
||||
@ -1,543 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
#include "ConsoleSCBMFC.h"
|
||||
#include "PropertiesDialog.h"
|
||||
#include "QtViewPaneManager.h"
|
||||
#include "Core/QtEditorApplication.h"
|
||||
|
||||
#include <Controls/ui_ConsoleSCBMFC.h>
|
||||
|
||||
#include <QtUtil.h>
|
||||
#include <QtUtilWin.h>
|
||||
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QScopedPointer>
|
||||
#include <QtCore/QPoint>
|
||||
#include <QtGui/QCursor>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtWidgets/QStyle>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
namespace MFC
|
||||
{
|
||||
|
||||
static CPropertiesDialog* gPropertiesDlg = nullptr;
|
||||
static CString mfc_popup_helper(HWND hwnd, int x, int y);
|
||||
static CConsoleSCB* s_consoleSCB = nullptr;
|
||||
|
||||
static QString RemoveColorCode(const QString& text, int& iColorCode)
|
||||
{
|
||||
QString cleanString;
|
||||
cleanString.reserve(text.size());
|
||||
|
||||
const int textSize = text.size();
|
||||
for (int i = 0; i < textSize; ++i)
|
||||
{
|
||||
QChar c = text.at(i);
|
||||
bool isLast = i == textSize - 1;
|
||||
if (c == '$' && !isLast && text.at(i + 1).isDigit())
|
||||
{
|
||||
if (iColorCode == 0)
|
||||
{
|
||||
iColorCode = text.at(i + 1).digitValue();
|
||||
}
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
cleanString.append(c);
|
||||
}
|
||||
|
||||
return cleanString;
|
||||
}
|
||||
|
||||
ConsoleLineEdit::ConsoleLineEdit(QWidget* parent)
|
||||
: QLineEdit(parent)
|
||||
, m_historyIndex(0)
|
||||
, m_bReusedHistory(false)
|
||||
{
|
||||
}
|
||||
|
||||
void ConsoleLineEdit::mousePressEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (ev->type() == QEvent::MouseButtonPress && ev->button() & Qt::RightButton)
|
||||
{
|
||||
Q_EMIT variableEditorRequested();
|
||||
}
|
||||
|
||||
QLineEdit::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
void ConsoleLineEdit::mouseDoubleClickEvent(QMouseEvent* ev)
|
||||
{
|
||||
Q_EMIT variableEditorRequested();
|
||||
}
|
||||
|
||||
bool ConsoleLineEdit::event(QEvent* ev)
|
||||
{
|
||||
// Tab key doesn't go to keyPressEvent(), must be processed here
|
||||
|
||||
if (ev->type() != QEvent::KeyPress)
|
||||
{
|
||||
return QLineEdit::event(ev);
|
||||
}
|
||||
|
||||
QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
|
||||
if (ke->key() != Qt::Key_Tab)
|
||||
{
|
||||
return QLineEdit::event(ev);
|
||||
}
|
||||
|
||||
QString inputStr = text();
|
||||
QString newStr;
|
||||
|
||||
QStringList tokens = inputStr.split(" ");
|
||||
inputStr = tokens.isEmpty() ? QString() : tokens.first();
|
||||
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
|
||||
|
||||
const bool ctrlPressed = ke->modifiers() & Qt::ControlModifier;
|
||||
CString cstring = QtUtil::ToCString(inputStr); // TODO: Use QString once the backend stops using QString
|
||||
if (ctrlPressed)
|
||||
{
|
||||
newStr = QtUtil::ToString(console->AutoCompletePrev(cstring));
|
||||
}
|
||||
else
|
||||
{
|
||||
newStr = QtUtil::ToString(console->ProcessCompletion(cstring));
|
||||
newStr = QtUtil::ToString(console->AutoComplete(cstring));
|
||||
|
||||
if (newStr.isEmpty())
|
||||
{
|
||||
newStr = QtUtil::ToQString(GetIEditor()->GetCommandManager()->AutoComplete(QtUtil::ToString(newStr)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!newStr.isEmpty())
|
||||
{
|
||||
newStr += " ";
|
||||
setText(newStr);
|
||||
}
|
||||
|
||||
deselect();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConsoleLineEdit::keyPressEvent(QKeyEvent* ev)
|
||||
{
|
||||
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
|
||||
auto commandManager = GetIEditor()->GetCommandManager();
|
||||
|
||||
console->ResetAutoCompletion();
|
||||
|
||||
switch (ev->key())
|
||||
{
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
{
|
||||
QString str = text().trimmed();
|
||||
if (!str.isEmpty())
|
||||
{
|
||||
if (commandManager->IsRegistered(QtUtil::ToCString(str)))
|
||||
{
|
||||
commandManager->Execute(QtUtil::ToString(str));
|
||||
}
|
||||
else
|
||||
{
|
||||
CLogFile::WriteLine(QtUtil::ToCString(str));
|
||||
GetIEditor()->GetSystem()->GetIConsole()->ExecuteString(QtUtil::ToCString(str));
|
||||
}
|
||||
|
||||
// If a history command was reused directly via up arrow enter, do not reset history index
|
||||
if (m_history.size() > 0 && m_historyIndex < m_history.size() && m_history[m_historyIndex] == str)
|
||||
{
|
||||
m_bReusedHistory = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_historyIndex = m_history.size();
|
||||
}
|
||||
|
||||
// Do not add the same string if it is the top of the stack, but allow duplicate entries otherwise
|
||||
if (m_history.isEmpty() || m_history.back() != str)
|
||||
{
|
||||
m_history.push_back(str);
|
||||
if (!m_bReusedHistory)
|
||||
{
|
||||
m_historyIndex = m_history.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_historyIndex = m_history.size();
|
||||
}
|
||||
|
||||
setText(QString());
|
||||
break;
|
||||
}
|
||||
case Qt::Key_AsciiTilde: // ~
|
||||
case Qt::Key_Agrave: // `
|
||||
// disable log.
|
||||
GetIEditor()->ShowConsole(false);
|
||||
setText(QString());
|
||||
m_historyIndex = m_history.size();
|
||||
break;
|
||||
case Qt::Key_Escape:
|
||||
setText(QString());
|
||||
m_historyIndex = m_history.size();
|
||||
break;
|
||||
case Qt::Key_Up:
|
||||
DisplayHistory(false /*bForward*/);
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
DisplayHistory(true /*bForward*/);
|
||||
break;
|
||||
default:
|
||||
QLineEdit::keyPressEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleLineEdit::DisplayHistory(bool bForward)
|
||||
{
|
||||
if (m_history.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Immediately after reusing a history entry, ensure up arrow re-displays command just used
|
||||
if (!m_bReusedHistory || bForward)
|
||||
{
|
||||
m_historyIndex = static_cast<unsigned int>(clamp_tpl(static_cast<int>(m_historyIndex) + (bForward ? 1 : -1), 0, m_history.size() - 1));
|
||||
}
|
||||
m_bReusedHistory = false;
|
||||
|
||||
setText(m_history[m_historyIndex]);
|
||||
}
|
||||
|
||||
ConsoleTextEdit::ConsoleTextEdit(QWidget* parent)
|
||||
: QTextEdit(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Lines CConsoleSCB::s_pendingLines;
|
||||
|
||||
CConsoleSCB::CConsoleSCB(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::ConsoleMFC())
|
||||
, m_richEditTextLength(0)
|
||||
, m_backgroundTheme(gSettings.consoleBackgroundColorTheme)
|
||||
{
|
||||
m_lines = s_pendingLines;
|
||||
s_pendingLines.clear();
|
||||
s_consoleSCB = this;
|
||||
ui->setupUi(this);
|
||||
setMinimumHeight(120);
|
||||
|
||||
// Setup the color table for the default (light) theme
|
||||
m_colorTable << QColor(0, 0, 0)
|
||||
<< QColor(0, 0, 0)
|
||||
<< QColor(0, 0, 200) // blue
|
||||
<< QColor(0, 200, 0) // green
|
||||
<< QColor(200, 0, 0) // red
|
||||
<< QColor(0, 200, 200) // cyan
|
||||
<< QColor(128, 112, 0) // yellow
|
||||
<< QColor(200, 0, 200) // red+blue
|
||||
<< QColor(0x000080ff)
|
||||
<< QColor(0x008f8f8f);
|
||||
OnStyleSettingsChanged();
|
||||
|
||||
connect(ui->button, &QPushButton::clicked, this, &CConsoleSCB::showVariableEditor);
|
||||
connect(ui->lineEdit, &MFC::ConsoleLineEdit::variableEditorRequested, this, &MFC::CConsoleSCB::showVariableEditor);
|
||||
connect(Editor::EditorQtApplication::instance(), &Editor::EditorQtApplication::skinChanged, this, &MFC::CConsoleSCB::OnStyleSettingsChanged);
|
||||
|
||||
if (GetIEditor()->IsInConsolewMode())
|
||||
{
|
||||
// Attach / register edit box
|
||||
//CLogFile::AttachEditBox(m_edit.GetSafeHwnd()); // FIXME
|
||||
}
|
||||
}
|
||||
|
||||
CConsoleSCB::~CConsoleSCB()
|
||||
{
|
||||
s_consoleSCB = nullptr;
|
||||
delete gPropertiesDlg;
|
||||
gPropertiesDlg = nullptr;
|
||||
CLogFile::AttachEditBox(nullptr);
|
||||
}
|
||||
|
||||
void CConsoleSCB::RegisterViewClass()
|
||||
{
|
||||
QtViewOptions opts;
|
||||
opts.preferedDockingArea = Qt::BottomDockWidgetArea;
|
||||
opts.isDeletable = false;
|
||||
opts.isStandard = true;
|
||||
opts.showInMenu = true;
|
||||
opts.builtInActionId = ID_VIEW_CONSOLEWINDOW;
|
||||
opts.sendViewPaneNameBackToAmazonAnalyticsServers = true;
|
||||
RegisterQtViewPane<CConsoleSCB>(GetIEditor(), LyViewPane::Console, LyViewPane::CategoryTools, opts);
|
||||
}
|
||||
|
||||
void CConsoleSCB::OnStyleSettingsChanged()
|
||||
{
|
||||
ui->button->setIcon(QIcon(QString(":/controls/img/cvar_dark.bmp")));
|
||||
|
||||
// Set the debug/warning text colors appropriately for the background theme
|
||||
// (e.g. not have black text on black background)
|
||||
QColor textColor = Qt::black;
|
||||
m_backgroundTheme = gSettings.consoleBackgroundColorTheme;
|
||||
if (m_backgroundTheme == SEditorSettings::ConsoleColorTheme::Dark)
|
||||
{
|
||||
textColor = Qt::white;
|
||||
}
|
||||
m_colorTable[0] = textColor;
|
||||
m_colorTable[1] = textColor;
|
||||
|
||||
QColor bgColor;
|
||||
if (!GetIEditor()->IsInConsolewMode() && CConsoleSCB::GetCreatedInstance() && m_backgroundTheme == SEditorSettings::ConsoleColorTheme::Dark)
|
||||
{
|
||||
bgColor = Qt::black;
|
||||
}
|
||||
else
|
||||
{
|
||||
bgColor = Qt::white;
|
||||
}
|
||||
|
||||
ui->textEdit->setStyleSheet(QString("QTextEdit{ background: %1 }").arg(bgColor.name(QColor::HexRgb)));
|
||||
|
||||
// Clear out the console text when we change our background color since
|
||||
// some of the previous text colors may not be appropriate for the
|
||||
// new background color
|
||||
ui->textEdit->clear();
|
||||
}
|
||||
|
||||
void CConsoleSCB::showVariableEditor()
|
||||
{
|
||||
const QPoint cursorPos = QCursor::pos();
|
||||
CString str = mfc_popup_helper(0, cursorPos.x(), cursorPos.y());
|
||||
if (!str.IsEmpty())
|
||||
{
|
||||
ui->lineEdit->setText(QtUtil::ToQString(str));
|
||||
}
|
||||
}
|
||||
|
||||
void CConsoleSCB::SetInputFocus()
|
||||
{
|
||||
ui->lineEdit->setFocus();
|
||||
ui->lineEdit->setText(QString());
|
||||
}
|
||||
|
||||
void CConsoleSCB::AddToConsole(const QString& text, bool bNewLine)
|
||||
{
|
||||
m_lines.push_back({ text, bNewLine });
|
||||
FlushText();
|
||||
}
|
||||
|
||||
void CConsoleSCB::FlushText()
|
||||
{
|
||||
if (m_lines.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Store our current cursor in case we need to restore it, and check if
|
||||
// the user has scrolled the text edit away from the bottom
|
||||
const QTextCursor oldCursor = ui->textEdit->textCursor();
|
||||
QScrollBar* scrollBar = ui->textEdit->verticalScrollBar();
|
||||
const int oldScrollValue = scrollBar->value();
|
||||
bool scrolledOffBottom = oldScrollValue != scrollBar->maximum();
|
||||
|
||||
ui->textEdit->moveCursor(QTextCursor::End);
|
||||
QTextCursor textCursor = ui->textEdit->textCursor();
|
||||
|
||||
while (!m_lines.empty())
|
||||
{
|
||||
ConsoleLine line = m_lines.front();
|
||||
m_lines.pop_front();
|
||||
|
||||
int iColor = 0;
|
||||
QString text = MFC::RemoveColorCode(line.text, iColor);
|
||||
if (iColor < 0 || iColor >= m_colorTable.size())
|
||||
{
|
||||
iColor = 0;
|
||||
}
|
||||
|
||||
if (line.newLine)
|
||||
{
|
||||
text = QtUtil::trimRight(text);
|
||||
text = "\r\n" + text;
|
||||
}
|
||||
|
||||
QTextCharFormat format;
|
||||
const QColor color(m_colorTable[iColor]);
|
||||
format.setForeground(color);
|
||||
|
||||
if (iColor != 0)
|
||||
{
|
||||
format.setFontWeight(QFont::Bold);
|
||||
}
|
||||
|
||||
textCursor.setCharFormat(format);
|
||||
textCursor.insertText(text);
|
||||
}
|
||||
|
||||
// If the user has selected some text in the text edit area or has scrolled
|
||||
// away from the bottom, then restore the previous cursor and keep the scroll
|
||||
// bar in the same location
|
||||
if (oldCursor.hasSelection() || scrolledOffBottom)
|
||||
{
|
||||
ui->textEdit->setTextCursor(oldCursor);
|
||||
scrollBar->setValue(oldScrollValue);
|
||||
}
|
||||
// Otherwise scroll to the bottom so the latest text can be seen
|
||||
else
|
||||
{
|
||||
scrollBar->setValue(scrollBar->maximum());
|
||||
}
|
||||
}
|
||||
|
||||
QSize CConsoleSCB::minimumSizeHint() const
|
||||
{
|
||||
return QSize(-1, -1);
|
||||
}
|
||||
|
||||
QSize CConsoleSCB::sizeHint() const
|
||||
{
|
||||
return QSize(100, 100);
|
||||
}
|
||||
|
||||
/** static */
|
||||
void CConsoleSCB::AddToPendingLines(const QString& text, bool bNewLine)
|
||||
{
|
||||
s_pendingLines.push_back({ text, bNewLine });
|
||||
}
|
||||
|
||||
static CVarBlock* VarBlockFromConsoleVars()
|
||||
{
|
||||
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
|
||||
std::vector<const char*> cmds;
|
||||
cmds.resize(console->GetNumVars());
|
||||
size_t cmdCount = console->GetSortedVars(&cmds[0], cmds.size());
|
||||
|
||||
CVarBlock* vb = new CVarBlock;
|
||||
IVariable* pVariable = 0;
|
||||
for (int i = 0; i < cmdCount; i++)
|
||||
{
|
||||
ICVar* pCVar = console->GetCVar(cmds[i]);
|
||||
if (!pCVar)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int varType = pCVar->GetType();
|
||||
|
||||
switch (varType)
|
||||
{
|
||||
case CVAR_INT:
|
||||
pVariable = new CVariable<int>();
|
||||
pVariable->Set(pCVar->GetIVal());
|
||||
break;
|
||||
case CVAR_FLOAT:
|
||||
pVariable = new CVariable<float>();
|
||||
pVariable->Set(pCVar->GetFVal());
|
||||
break;
|
||||
case CVAR_STRING:
|
||||
pVariable = new CVariable<CString>();
|
||||
pVariable->Set(pCVar->GetString());
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
pVariable->SetDescription(pCVar->GetHelp());
|
||||
pVariable->SetName(cmds[i]);
|
||||
|
||||
if (pVariable)
|
||||
{
|
||||
vb->AddVariable(pVariable);
|
||||
}
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
||||
static void OnConsoleVariableUpdated(IVariable* pVar)
|
||||
{
|
||||
if (!pVar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CString varName = pVar->GetName();
|
||||
ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(varName);
|
||||
if (!pCVar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (pVar->GetType() == IVariable::INT)
|
||||
{
|
||||
int val;
|
||||
pVar->Get(val);
|
||||
pCVar->Set(val);
|
||||
}
|
||||
else if (pVar->GetType() == IVariable::FLOAT)
|
||||
{
|
||||
float val;
|
||||
pVar->Get(val);
|
||||
pCVar->Set(val);
|
||||
}
|
||||
else if (pVar->GetType() == IVariable::STRING)
|
||||
{
|
||||
CString val;
|
||||
pVar->Get(val);
|
||||
pCVar->Set(val);
|
||||
}
|
||||
}
|
||||
|
||||
static CString mfc_popup_helper(HWND hwnd, int x, int y)
|
||||
{
|
||||
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
|
||||
|
||||
TSmartPtr<CVarBlock> vb = VarBlockFromConsoleVars();
|
||||
XmlNodeRef node;
|
||||
if (!gPropertiesDlg)
|
||||
{
|
||||
gPropertiesDlg = new CPropertiesDialog("Console Variables", node, AfxGetMainWnd(), true);
|
||||
}
|
||||
if (!gPropertiesDlg->m_hWnd)
|
||||
{
|
||||
gPropertiesDlg->Create(CPropertiesDialog::IDD, AfxGetMainWnd());
|
||||
gPropertiesDlg->SetUpdateCallback(AZStd::bind(OnConsoleVariableUpdated, AZStd::placeholders::_1));
|
||||
}
|
||||
gPropertiesDlg->ShowWindow(SW_SHOW);
|
||||
gPropertiesDlg->BringWindowToTop();
|
||||
gPropertiesDlg->GetPropertyCtrl()->AddVarBlock(vb);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
CConsoleSCB* CConsoleSCB::GetCreatedInstance()
|
||||
{
|
||||
return s_consoleSCB;
|
||||
}
|
||||
|
||||
} // namespace MFC
|
||||
|
||||
#include <Controls/moc_ConsoleSCBMFC.cpp>
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue