瀏覽代碼

Add Physics Tests project

PouleyKetchoupp 5 年之前
父節點
當前提交
afd99e5aed
共有 33 個文件被更改,包括 1383 次插入0 次删除
  1. 18 0
      3d/physics_tests/README.md
  2. 二進制
      3d/physics_tests/assets/robot_head/godot3_robot_head.mesh
  3. 3 0
      3d/physics_tests/assets/robot_head/godot3_robot_head_collision.tres
  4. 11 0
      3d/physics_tests/assets/robot_head/readme.txt
  5. 3 0
      3d/physics_tests/default_env.tres
  6. 二進制
      3d/physics_tests/icon.png
  7. 34 0
      3d/physics_tests/icon.png.import
  8. 174 0
      3d/physics_tests/main.tscn
  9. 68 0
      3d/physics_tests/project.godot
  10. 0 0
      3d/physics_tests/screenshots/.gdignore
  11. 二進制
      3d/physics_tests/screenshots/screenshot.png
  12. 36 0
      3d/physics_tests/test.gd
  13. 27 0
      3d/physics_tests/tests.gd
  14. 134 0
      3d/physics_tests/tests/functional/test_compound_shapes.tscn
  15. 185 0
      3d/physics_tests/tests/functional/test_friction.tscn
  16. 67 0
      3d/physics_tests/tests/functional/test_shapes.tscn
  17. 151 0
      3d/physics_tests/tests/performance/test_perf_contacts.gd
  18. 90 0
      3d/physics_tests/tests/performance/test_perf_contacts.tscn
  19. 32 0
      3d/physics_tests/tests/static_scene.tscn
  20. 16 0
      3d/physics_tests/tests/test_options.tscn
  21. 53 0
      3d/physics_tests/tests_menu.gd
  22. 35 0
      3d/physics_tests/utils/camera_orbit.gd
  23. 37 0
      3d/physics_tests/utils/container_log.gd
  24. 30 0
      3d/physics_tests/utils/control3d.gd
  25. 8 0
      3d/physics_tests/utils/exception_cylinder.gd
  26. 14 0
      3d/physics_tests/utils/label_engine.gd
  27. 5 0
      3d/physics_tests/utils/label_fps.gd
  28. 13 0
      3d/physics_tests/utils/label_test.gd
  29. 5 0
      3d/physics_tests/utils/label_version.gd
  30. 47 0
      3d/physics_tests/utils/option_menu.gd
  31. 14 0
      3d/physics_tests/utils/scroll_log.gd
  32. 53 0
      3d/physics_tests/utils/system.gd
  33. 20 0
      3d/physics_tests/utils/system_log.gd

+ 18 - 0
3d/physics_tests/README.md

@@ -0,0 +1,18 @@
+# 3D Physics Tests
+
+This demo contains a series of tests for the 3D 
+physics engine.
+
+They can be used for different purpose:
+- Functional tests to check for regressions and 
+compare the behavior between physics engines
+- Performance tests to evaluate and compare 
+performance between physics engines
+
+Language: GDScript
+
+Renderer: GLES 2
+
+## Screenshots
+
+![Screenshot](screenshots/screenshot.png)

二進制
3d/physics_tests/assets/robot_head/godot3_robot_head.mesh


File diff suppressed because it is too large
+ 3 - 0
3d/physics_tests/assets/robot_head/godot3_robot_head_collision.tres


+ 11 - 0
3d/physics_tests/assets/robot_head/readme.txt

@@ -0,0 +1,11 @@
+- Robot Head model created by James Redmond (fracteed) 2018
+
+- http://www.fracteed.com/godot.html
+
+- created with Godot 3.0.5
+
+- models created using Blender 2.79 and Sunstance Painter/Designer
+
+- Creative Commons License CC-BY:
+"This license lets others distribute, remix, tweak, and build upon your work, even commercially, as long as they credit you for the original creation.
+This is the most accommodating of licenses offered. Recommended for maximum dissemination and use of licensed materials."

+ 3 - 0
3d/physics_tests/default_env.tres

@@ -0,0 +1,3 @@
+[gd_resource type="Environment" format=2]
+
+[resource]

二進制
3d/physics_tests/icon.png


+ 34 - 0
3d/physics_tests/icon.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.png"
+dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0

+ 174 - 0
3d/physics_tests/main.tscn

@@ -0,0 +1,174 @@
+[gd_scene load_steps=10 format=2]
+
+[ext_resource path="res://utils/label_fps.gd" type="Script" id=1]
+[ext_resource path="res://utils/label_version.gd" type="Script" id=2]
+[ext_resource path="res://utils/label_engine.gd" type="Script" id=3]
+[ext_resource path="res://tests_menu.gd" type="Script" id=4]
+[ext_resource path="res://utils/label_test.gd" type="Script" id=5]
+[ext_resource path="res://utils/container_log.gd" type="Script" id=10]
+[ext_resource path="res://utils/scroll_log.gd" type="Script" id=11]
+[ext_resource path="res://tests.gd" type="Script" id=12]
+
+[sub_resource type="StyleBoxFlat" id=1]
+bg_color = Color( 0, 0, 0, 0.176471 )
+
+[node name="Main" type="Control"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+mouse_filter = 2
+script = ExtResource( 12 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="TestsMenu" type="MenuButton" parent="."]
+margin_left = 10.0
+margin_top = 10.0
+margin_right = 125.0
+margin_bottom = 30.0
+text = "TESTS"
+flat = false
+script = ExtResource( 4 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelControls" type="Label" parent="."]
+margin_left = 157.0
+margin_top = 13.0
+margin_right = 375.0
+margin_bottom = 27.0
+text = "R - RESTART / D - TOGGLE COLLISION / F - TOGGLE FULL SCREEN / ESC - QUIT"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelFPS" type="Label" parent="."]
+anchor_top = 1.0
+anchor_bottom = 1.0
+margin_left = 10.0
+margin_top = -19.0
+margin_right = 50.0
+margin_bottom = -5.0
+text = "FPS: 0"
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelEngine" type="Label" parent="."]
+anchor_top = 1.0
+anchor_bottom = 1.0
+margin_left = 10.0
+margin_top = -39.0
+margin_right = 50.0
+margin_bottom = -25.0
+text = "Physics engine:"
+script = ExtResource( 3 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelVersion" type="Label" parent="."]
+anchor_top = 1.0
+anchor_bottom = 1.0
+margin_left = 10.0
+margin_top = -59.0
+margin_right = 50.0
+margin_bottom = -45.0
+text = "Godot Version:"
+script = ExtResource( 2 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelTest" type="Label" parent="."]
+anchor_top = 1.0
+anchor_bottom = 1.0
+margin_left = 10.0
+margin_top = -79.0
+margin_right = 50.0
+margin_bottom = -65.0
+text = "Test:"
+script = ExtResource( 5 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="PanelLog" type="Panel" parent="."]
+anchor_left = 1.0
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = -428.0
+margin_top = -125.0
+custom_styles/panel = SubResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="ButtonClear" type="Button" parent="PanelLog"]
+anchor_left = 1.0
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = -48.0
+margin_top = -25.0
+margin_right = -5.0
+margin_bottom = -5.0
+focus_mode = 0
+enabled_focus_mode = 0
+text = "clear"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="CheckBoxScroll" type="CheckBox" parent="PanelLog"]
+anchor_left = 1.0
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = -150.0
+margin_top = -27.0
+margin_right = -54.0
+margin_bottom = -3.0
+focus_mode = 0
+pressed = true
+enabled_focus_mode = 0
+text = "auto-scroll"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="ScrollLog" type="ScrollContainer" parent="PanelLog"]
+margin_left = 10.0
+margin_top = 5.0
+margin_right = 418.0
+margin_bottom = 94.0
+scroll_horizontal_enabled = false
+script = ExtResource( 11 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+auto_scroll = true
+
+[node name="VBoxLog" type="VBoxContainer" parent="PanelLog/ScrollLog"]
+margin_right = 408.0
+margin_bottom = 89.0
+size_flags_horizontal = 3
+size_flags_vertical = 3
+alignment = 2
+script = ExtResource( 10 )
+
+[node name="LabelLog" type="Label" parent="PanelLog/ScrollLog/VBoxLog"]
+margin_top = 75.0
+margin_right = 408.0
+margin_bottom = 89.0
+text = "Log start"
+valign = 2
+max_lines_visible = 5
+__meta__ = {
+"_edit_use_anchors_": false
+}
+[connection signal="pressed" from="PanelLog/ButtonClear" to="PanelLog/ScrollLog/VBoxLog" method="clear"]
+[connection signal="toggled" from="PanelLog/CheckBoxScroll" to="PanelLog/ScrollLog" method="set_auto_scroll"]

+ 68 - 0
3d/physics_tests/project.godot

@@ -0,0 +1,68 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+;   [section] ; section goes between []
+;   param=value ; assign values to parameters
+
+config_version=4
+
+_global_script_classes=[ {
+"base": "MenuButton",
+"class": "OptionMenu",
+"language": "GDScript",
+"path": "res://utils/option_menu.gd"
+}, {
+"base": "Node",
+"class": "Test",
+"language": "GDScript",
+"path": "res://test.gd"
+} ]
+_global_script_class_icons={
+"OptionMenu": "",
+"Test": ""
+}
+
+[application]
+
+config/name="Physics Tests"
+run/main_scene="res://main.tscn"
+config/icon="res://icon.png"
+
+[autoload]
+
+Log="*res://utils/system_log.gd"
+System="*res://utils/system.gd"
+
+[debug]
+
+gdscript/warnings/return_value_discarded=false
+
+[input]
+
+toggle_full_screen={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":70,"unicode":0,"echo":false,"script":null)
+ ]
+}
+exit={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777217,"unicode":0,"echo":false,"script":null)
+ ]
+}
+toggle_debug_collision={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null)
+ ]
+}
+restart_test={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":82,"unicode":0,"echo":false,"script":null)
+ ]
+}
+
+[rendering]
+
+quality/driver/driver_name="GLES2"
+environment/default_environment="res://default_env.tres"

+ 0 - 0
3d/physics_tests/screenshots/.gdignore


二進制
3d/physics_tests/screenshots/screenshot.png


+ 36 - 0
3d/physics_tests/test.gd

@@ -0,0 +1,36 @@
+class_name Test
+extends Node
+
+
+var _timer
+var _timer_started = false
+
+
+func start_timer(timeout):
+	if _timer == null:
+		_timer = Timer.new()
+		_timer.one_shot = true
+		add_child(_timer)
+		_timer.connect("timeout", self, "_on_timer_done")
+	else:
+		cancel_timer()
+	
+	_timer.start(timeout)
+	_timer_started = true
+	
+	return _timer
+
+
+func cancel_timer():
+	if _timer_started:
+		_timer.paused = true
+		_timer.emit_signal("timeout")
+		_timer.paused = false
+
+
+func is_timer_canceled():
+	return _timer.paused
+
+
+func _on_timer_done():
+	_timer_started = false

+ 27 - 0
3d/physics_tests/tests.gd

@@ -0,0 +1,27 @@
+extends Node
+
+
+var _tests = [
+	{
+		"id" : "Functional Tests/Shapes",
+		"path" : "res://tests/functional/test_shapes.tscn",
+	},
+	{
+		"id" : "Functional Tests/Compound Shapes",
+		"path" : "res://tests/functional/test_compound_shapes.tscn",
+	},
+	{
+		"id" : "Functional Tests/Friction",
+		"path" : "res://tests/functional/test_friction.tscn",
+	},
+	{
+		"id" : "Performance Tests/Contacts",
+		"path" : "res://tests/performance/test_perf_contacts.tscn",
+	},
+]
+
+
+func _ready():
+	var test_menu = $TestsMenu
+	for test in _tests:
+		test_menu.add_test(test.id, test.path)

+ 134 - 0
3d/physics_tests/tests/functional/test_compound_shapes.tscn

@@ -0,0 +1,134 @@
+[gd_scene load_steps=6 format=2]
+
+[ext_resource path="res://tests/static_scene.tscn" type="PackedScene" id=1]
+[ext_resource path="res://test.gd" type="Script" id=2]
+[ext_resource path="res://utils/camera_orbit.gd" type="Script" id=3]
+
+[sub_resource type="BoxShape" id=1]
+
+[sub_resource type="CapsuleShape" id=2]
+radius = 0.2
+
+[node name="Test" type="Spatial"]
+script = ExtResource( 2 )
+
+[node name="DynamicShapes" type="Spatial" parent="."]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9.35591, 0 )
+
+[node name="RigidBodyG" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -5.51361, 0, 0 )
+
+[node name="CollisionShape1" type="CollisionShape" parent="DynamicShapes/RigidBodyG"]
+transform = Transform( -2.44381e-07, -0.3, 0, 1.5, -4.88762e-08, 0, 0, 0, 0.3, -0.8, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape2" type="CollisionShape" parent="DynamicShapes/RigidBodyG"]
+transform = Transform( 0.8, 0, 4.26326e-15, 0, 0.3, 0, -1.13687e-14, 0, 0.3, 0.3, -1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape3" type="CollisionShape" parent="DynamicShapes/RigidBodyG"]
+transform = Transform( -8.14603e-08, -0.3, 0, 0.5, -4.88762e-08, 0, 0, 0, 0.3, 0.8, -0.5, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape4" type="CollisionShape" parent="DynamicShapes/RigidBodyG"]
+transform = Transform( 0.8, 0, 4.26326e-15, 0, 0.3, 0, -1.13687e-14, 0, 0.3, 0.3, 1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="RigidBodyO" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.65654, 0, 0 )
+
+[node name="CollisionShape1" type="CollisionShape" parent="DynamicShapes/RigidBodyO"]
+transform = Transform( -2.44381e-07, -0.3, 0, 1.5, -4.88762e-08, 0, 0, 0, 0.3, -0.8, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape2" type="CollisionShape" parent="DynamicShapes/RigidBodyO"]
+transform = Transform( 0.5, 0, 4.26326e-15, 0, 0.3, 0, -7.10543e-15, 0, 0.3, 0, 1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape3" type="CollisionShape" parent="DynamicShapes/RigidBodyO"]
+transform = Transform( 0.5, 0, 4.26326e-15, 0, 0.3, 0, -7.10543e-15, 0, 0.3, 0, -1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape4" type="CollisionShape" parent="DynamicShapes/RigidBodyO"]
+transform = Transform( -2.44381e-07, -0.3, 0, 1.5, -4.88762e-08, 0, 0, 0, 0.3, 0.8, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="RigidBodyD" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.248918, 0, 0 )
+
+[node name="CollisionShape1" type="CollisionShape" parent="DynamicShapes/RigidBodyD"]
+transform = Transform( -2.44381e-07, -0.3, 0, 1.5, -4.88762e-08, 0, 0, 0, 0.3, -0.8, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape2" type="CollisionShape" parent="DynamicShapes/RigidBodyD"]
+transform = Transform( 0.5, 0, 4.26326e-15, 0, 0.3, 0, -7.10543e-15, 0, 0.3, 0, 1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape3" type="CollisionShape" parent="DynamicShapes/RigidBodyD"]
+transform = Transform( 0.5, 0, 4.26326e-15, 0, 0.3, 0, -7.10543e-15, 0, 0.3, 0, -1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape4" type="CollisionShape" parent="DynamicShapes/RigidBodyD"]
+transform = Transform( -1.46629e-07, -0.3, 0, 0.9, -4.88762e-08, 0, 0, 0, 0.3, 0.8, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="RigidBodyO2" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2.97932, 0, 0 )
+
+[node name="CollisionShape1" type="CollisionShape" parent="DynamicShapes/RigidBodyO2"]
+transform = Transform( -2.44381e-07, -0.3, 0, 1.5, -4.88762e-08, 0, 0, 0, 0.3, -0.8, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape2" type="CollisionShape" parent="DynamicShapes/RigidBodyO2"]
+transform = Transform( 0.5, 0, 4.26326e-15, 0, 0.3, 0, -7.10543e-15, 0, 0.3, 0, 1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape3" type="CollisionShape" parent="DynamicShapes/RigidBodyO2"]
+transform = Transform( 0.5, 0, 4.26326e-15, 0, 0.3, 0, -7.10543e-15, 0, 0.3, 0, -1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape4" type="CollisionShape" parent="DynamicShapes/RigidBodyO2"]
+transform = Transform( -2.44381e-07, -0.3, 0, 1.5, -4.88762e-08, 0, 0, 0, 0.3, 0.8, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="RigidBodyT" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 5.46836, 0, 0 )
+
+[node name="CollisionShape1" type="CollisionShape" parent="DynamicShapes/RigidBodyT"]
+transform = Transform( -1.95505e-07, -0.3, 0, 1.2, -4.88762e-08, 0, 0, 0, 0.3, 0, -0.3, 0 )
+shape = SubResource( 1 )
+
+[node name="CollisionShape2" type="CollisionShape" parent="DynamicShapes/RigidBodyT"]
+transform = Transform( 1, 0, 4.26326e-15, 0, 0.3, 0, -1.42109e-14, 0, 0.3, 0, 1.2, 0 )
+shape = SubResource( 1 )
+
+[node name="StaticBodyPins" type="StaticBody" parent="."]
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyPins"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9.13207, 0 )
+shape = SubResource( 2 )
+
+[node name="CollisionShape2" type="CollisionShape" parent="StaticBodyPins"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -2.71742, 9.13207, 0 )
+shape = SubResource( 2 )
+
+[node name="CollisionShape3" type="CollisionShape" parent="StaticBodyPins"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -5.64188, 9.13207, 0 )
+shape = SubResource( 2 )
+
+[node name="CollisionShape4" type="CollisionShape" parent="StaticBodyPins"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2.87444, 9.13207, 0 )
+shape = SubResource( 2 )
+
+[node name="CollisionShape5" type="CollisionShape" parent="StaticBodyPins"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 4.81639, 9.13207, 0 )
+shape = SubResource( 2 )
+
+[node name="StaticScene" parent="." instance=ExtResource( 1 )]
+
+[node name="Camera" type="Camera" parent="."]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.53602, 22.1236 )
+script = ExtResource( 3 )
+
+[node name="OmniLight" type="OmniLight" parent="Camera"]
+omni_range = 50.0

+ 185 - 0
3d/physics_tests/tests/functional/test_friction.tscn

@@ -0,0 +1,185 @@
+[gd_scene load_steps=10 format=2]
+
+[ext_resource path="res://test.gd" type="Script" id=1]
+[ext_resource path="res://utils/control3d.gd" type="Script" id=2]
+
+[sub_resource type="PhysicsMaterial" id=1]
+friction = 0.0
+
+[sub_resource type="ConcavePolygonShape" id=2]
+data = PoolVector3Array( -1, 0, 1, 1, 0, -1, 1, 0, 1, -1, 0, 1, -1, 0, -1, 1, 0, -1 )
+
+[sub_resource type="PhysicsMaterial" id=3]
+friction = 0.0
+
+[sub_resource type="BoxShape" id=4]
+
+[sub_resource type="PhysicsMaterial" id=5]
+friction = 0.5
+
+[sub_resource type="PhysicsMaterial" id=6]
+
+[sub_resource type="PhysicsMaterial" id=7]
+
+[node name="Test" type="Spatial"]
+script = ExtResource( 1 )
+
+[node name="StaticBodyFriction0" type="StaticBody" parent="."]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, -6, 8.581, 0 )
+physics_material_override = SubResource( 1 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction0"]
+transform = Transform( 5, 0, 0, 0, 1, 0, 0, 0, 50, 0, 0, 0 )
+shape = SubResource( 2 )
+
+[node name="RigidBodies" type="Spatial" parent="StaticBodyFriction0"]
+transform = Transform( 1, 0, 0, 0, 0.866026, 0.5, 0, -0.5, 0.866026, -3, -7.431, 4.291 )
+
+[node name="RigidBodyFriction0" type="RigidBody" parent="StaticBodyFriction0/RigidBodies"]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, 0, 10, 0 )
+physics_material_override = SubResource( 3 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction0/RigidBodies/RigidBodyFriction0"]
+shape = SubResource( 4 )
+
+[node name="LabelFriction" type="Label" parent="StaticBodyFriction0/RigidBodies/RigidBodyFriction0"]
+margin_right = 40.0
+margin_bottom = 14.0
+text = "0"
+align = 1
+script = ExtResource( 2 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+world_offset = Vector3( 0, 1.5, 0 )
+
+[node name="RigidBodyFriction05" type="RigidBody" parent="StaticBodyFriction0/RigidBodies"]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, 3, 10, 0 )
+physics_material_override = SubResource( 5 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction0/RigidBodies/RigidBodyFriction05"]
+shape = SubResource( 4 )
+
+[node name="LabelFriction" type="Label" parent="StaticBodyFriction0/RigidBodies/RigidBodyFriction05"]
+margin_right = 40.0
+margin_bottom = 14.0
+text = "0.5"
+align = 1
+script = ExtResource( 2 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+world_offset = Vector3( 0, 1.5, 0 )
+
+[node name="RigidBodyFriction1" type="RigidBody" parent="StaticBodyFriction0/RigidBodies"]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, 6, 10, 0 )
+physics_material_override = SubResource( 6 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction0/RigidBodies/RigidBodyFriction1"]
+shape = SubResource( 4 )
+
+[node name="LabelFriction" type="Label" parent="StaticBodyFriction0/RigidBodies/RigidBodyFriction1"]
+margin_right = 40.0
+margin_bottom = 14.0
+text = "1"
+align = 1
+script = ExtResource( 2 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+world_offset = Vector3( 0, 1.5, 0 )
+
+[node name="StaticBodyFriction1" type="StaticBody" parent="."]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, 6, 8.581, 0 )
+physics_material_override = SubResource( 7 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction1"]
+transform = Transform( 5, 0, 0, 0, 1, 0, 0, 0, 50, 0, 0, 0 )
+shape = SubResource( 2 )
+
+[node name="RigidBodies" type="Spatial" parent="StaticBodyFriction1"]
+transform = Transform( 1, 0, 0, 0, 0.866026, 0.5, 0, -0.5, 0.866026, -3, -7.431, 4.291 )
+
+[node name="RigidBodyFriction0" type="RigidBody" parent="StaticBodyFriction1/RigidBodies"]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, 0, 10, 0 )
+physics_material_override = SubResource( 3 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction1/RigidBodies/RigidBodyFriction0"]
+shape = SubResource( 4 )
+
+[node name="LabelFriction" type="Label" parent="StaticBodyFriction1/RigidBodies/RigidBodyFriction0"]
+margin_right = 40.0
+margin_bottom = 14.0
+text = "0"
+align = 1
+script = ExtResource( 2 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+world_offset = Vector3( 0, 1.5, 0 )
+
+[node name="RigidBodyFriction05" type="RigidBody" parent="StaticBodyFriction1/RigidBodies"]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, 3, 10, 0 )
+physics_material_override = SubResource( 5 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction1/RigidBodies/RigidBodyFriction05"]
+shape = SubResource( 4 )
+
+[node name="LabelFriction" type="Label" parent="StaticBodyFriction1/RigidBodies/RigidBodyFriction05"]
+margin_right = 40.0
+margin_bottom = 14.0
+text = "0.5"
+align = 1
+script = ExtResource( 2 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+world_offset = Vector3( 0, 1.5, 0 )
+
+[node name="RigidBodyFriction1" type="RigidBody" parent="StaticBodyFriction1/RigidBodies"]
+transform = Transform( 1, 0, 0, 0, 0.866025, -0.5, 0, 0.5, 0.866025, 6, 10, 0 )
+physics_material_override = SubResource( 6 )
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyFriction1/RigidBodies/RigidBodyFriction1"]
+shape = SubResource( 4 )
+
+[node name="LabelFriction" type="Label" parent="StaticBodyFriction1/RigidBodies/RigidBodyFriction1"]
+margin_right = 40.0
+margin_bottom = 14.0
+text = "1"
+align = 1
+script = ExtResource( 2 )
+__meta__ = {
+"_edit_use_anchors_": false
+}
+world_offset = Vector3( 0, 1.5, 0 )
+
+[node name="Camera" type="Camera" parent="."]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.53602, 22.1236 )
+
+[node name="OmniLight" type="OmniLight" parent="Camera"]
+omni_range = 50.0
+
+[node name="LabelGround0" type="Label" parent="."]
+anchor_left = 0.5
+anchor_right = 0.5
+margin_left = -164.0
+margin_top = 80.0
+margin_right = -28.0
+margin_bottom = 94.0
+text = "GROUND FRICTION 0"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelGround1" type="Label" parent="."]
+anchor_left = 0.5
+anchor_right = 0.5
+margin_left = 31.0
+margin_top = 80.0
+margin_right = 167.0
+margin_bottom = 94.0
+text = "GROUND FRICTION 1"
+__meta__ = {
+"_edit_use_anchors_": false
+}

+ 67 - 0
3d/physics_tests/tests/functional/test_shapes.tscn

@@ -0,0 +1,67 @@
+[gd_scene load_steps=10 format=2]
+
+[ext_resource path="res://test.gd" type="Script" id=2]
+[ext_resource path="res://utils/exception_cylinder.gd" type="Script" id=3]
+[ext_resource path="res://utils/camera_orbit.gd" type="Script" id=4]
+[ext_resource path="res://tests/static_scene.tscn" type="PackedScene" id=6]
+
+[sub_resource type="BoxShape" id=1]
+
+[sub_resource type="CapsuleShape" id=2]
+
+[sub_resource type="CylinderShape" id=3]
+
+[sub_resource type="ConvexPolygonShape" id=4]
+points = PoolVector3Array( -0.7, 0, -0.7, -0.3, 0, 0.8, 0.8, 0, -0.3, 0, -1, 0 )
+
+[sub_resource type="SphereShape" id=5]
+
+[node name="Test" type="Spatial"]
+script = ExtResource( 2 )
+
+[node name="DynamicShapes" type="Spatial" parent="."]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9.35591, 0 )
+
+[node name="RigidBodyBox" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 0, 0 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyBox"]
+transform = Transform( 0.579556, 0.0885213, 0.145926, 0, 0.939693, -0.205212, -0.155291, 0.330366, 0.544604, 0, 0, 0 )
+shape = SubResource( 1 )
+
+[node name="RigidBodyCapsule" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0, 0 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyCapsule"]
+transform = Transform( 0.8, 0, 0, 0, -1.30337e-07, -0.8, 0, 0.8, -1.30337e-07, 0, 0, 0 )
+shape = SubResource( 2 )
+
+[node name="RigidBodyCylinder" type="RigidBody" parent="DynamicShapes"]
+script = ExtResource( 3 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyCylinder"]
+transform = Transform( 0.772741, -0.258819, 2.59821e-08, 0.2, 0.933013, -0.207055, 0.0535898, 0.25, 0.772741, 0, 0, 0 )
+shape = SubResource( 3 )
+
+[node name="RigidBodyConvex" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0, 0 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyConvex"]
+transform = Transform( 1.5, 0, 0, 0, 1.93185, -0.388229, 0, 0.517638, 1.44889, 0, 0, 0 )
+shape = SubResource( 4 )
+
+[node name="RigidBodySphere" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 0, 0 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodySphere"]
+transform = Transform( 0.8, 0, 0, 0, 0.8, 0, 0, 0, 0.8, 0, 0, 0 )
+shape = SubResource( 5 )
+
+[node name="StaticScene" parent="." instance=ExtResource( 6 )]
+
+[node name="Camera" type="Camera" parent="."]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.53602, 22.1236 )
+script = ExtResource( 4 )
+
+[node name="OmniLight" type="OmniLight" parent="Camera"]
+omni_range = 50.0

+ 151 - 0
3d/physics_tests/tests/performance/test_perf_contacts.gd

@@ -0,0 +1,151 @@
+extends Test
+
+
+const OPTION_TYPE_ALL = "Shape type/All"
+const OPTION_TYPE_BOX = "Shape type/Box"
+const OPTION_TYPE_CAPSULE = "Shape type/Capsule"
+const OPTION_TYPE_CYLINDER = "Shape type/Cylinder"
+const OPTION_TYPE_CONVEX = "Shape type/Convex"
+const OPTION_TYPE_SPHERE = "Shape type/Sphere"
+
+export(int) var spawn_count = 1
+
+var _object_templates = []
+
+
+func _ready():
+	yield(start_timer(0.5), "timeout")
+	if is_timer_canceled():
+		return
+	
+	while $DynamicShapes.get_child_count():
+		var type_node = $DynamicShapes.get_child(0)
+		_object_templates.push_back(type_node)
+		$DynamicShapes.remove_child(type_node)
+	
+	$Options.add_menu_item(OPTION_TYPE_ALL)
+	$Options.add_menu_item(OPTION_TYPE_BOX)
+	$Options.add_menu_item(OPTION_TYPE_CAPSULE)
+	$Options.add_menu_item(OPTION_TYPE_CYLINDER)
+	$Options.add_menu_item(OPTION_TYPE_CONVEX)
+	$Options.add_menu_item(OPTION_TYPE_SPHERE)
+	$Options.connect("option_selected", self, "_on_option_selected")
+	
+	_start_all_types()
+
+
+func _on_option_selected(option):
+	cancel_timer()
+	
+	_despawn_objects()
+	
+	match option:
+		OPTION_TYPE_ALL:
+			_start_all_types()
+		OPTION_TYPE_BOX:
+			_start_type(_find_type_index("Box"))
+		OPTION_TYPE_CAPSULE:
+			_start_type(_find_type_index("Capsule"))
+		OPTION_TYPE_CYLINDER:
+			_start_type(_find_type_index("Cylinder"))
+		OPTION_TYPE_CONVEX:
+			_start_type(_find_type_index("Convex"))
+		OPTION_TYPE_SPHERE:
+			_start_type(_find_type_index("Sphere"))
+
+
+func _find_type_index(type_name):
+	for type_index in _object_templates.size():
+		var type_node = _object_templates[type_index]
+		if type_node.name.find(type_name) > -1:
+			return type_index
+	
+	Log.print_error("Invalid shape type: " + type_name)
+	return -1
+
+
+func _start_type(type_index):
+	if type_index < 0:
+		return
+	if type_index >= _object_templates.size():
+		return
+	
+	yield(start_timer(1.0), "timeout")
+	if is_timer_canceled():
+		return
+	
+	_spawn_objects(type_index)
+	
+	yield(start_timer(1.0), "timeout")
+	if is_timer_canceled():
+		return
+	
+	_activate_objects()
+	
+	yield(start_timer(5.0), "timeout")
+	if is_timer_canceled():
+		return
+	
+	_despawn_objects()
+	
+	Log.print_log("* Done.")
+
+
+func _start_all_types():
+	for type_index in _object_templates.size():
+		yield(start_timer(1.0), "timeout")
+		if is_timer_canceled():
+			return
+		
+		_spawn_objects(type_index)
+		
+		yield(start_timer(1.0), "timeout")
+		if is_timer_canceled():
+			return
+		
+		_activate_objects()
+		
+		yield(start_timer(5.0), "timeout")
+		if is_timer_canceled():
+			return
+		
+		_despawn_objects()
+	
+	Log.print_log("* Done.")
+
+
+func _spawn_objects(type_index):
+	var template_node = _object_templates[type_index]
+	var spawn_parent = $SpawnTarget
+	
+	Log.print_log("* Spawning: " + template_node.name)
+	
+	for node_index in spawn_count:
+		var node = template_node.duplicate() as Spatial
+		node.transform = Transform.IDENTITY
+		spawn_parent.add_child(node)
+
+
+func _activate_objects():
+	var spawn_parent = $SpawnTarget
+	
+	Log.print_log("* Activating")
+	
+	for node_index in spawn_parent.get_child_count():
+		var node = spawn_parent.get_child(node_index) as RigidBody
+		node.set_sleeping(false)
+
+
+func _despawn_objects():
+	var spawn_parent = $SpawnTarget
+	
+	if spawn_parent.get_child_count() == 0:
+		return
+	
+	Log.print_log("* Despawning")
+	
+	while spawn_parent.get_child_count():
+		var node_index = spawn_parent.get_child_count() - 1
+		var node = spawn_parent.get_child(node_index)
+		spawn_parent.remove_child(node)
+		node.queue_free()

+ 90 - 0
3d/physics_tests/tests/performance/test_perf_contacts.tscn

@@ -0,0 +1,90 @@
+[gd_scene load_steps=12 format=2]
+
+[ext_resource path="res://tests/static_scene.tscn" type="PackedScene" id=1]
+[ext_resource path="res://tests/performance/test_perf_contacts.gd" type="Script" id=2]
+[ext_resource path="res://utils/exception_cylinder.gd" type="Script" id=3]
+[ext_resource path="res://tests/test_options.tscn" type="PackedScene" id=4]
+[ext_resource path="res://utils/camera_orbit.gd" type="Script" id=5]
+
+[sub_resource type="BoxShape" id=1]
+
+[sub_resource type="CapsuleShape" id=2]
+
+[sub_resource type="CylinderShape" id=3]
+
+[sub_resource type="ConvexPolygonShape" id=4]
+points = PoolVector3Array( -0.7, 0, -0.7, -0.3, 0, 0.8, 0.8, 0, -0.3, 0, -1, 0 )
+
+[sub_resource type="SphereShape" id=5]
+
+[sub_resource type="ConcavePolygonShape" id=6]
+data = PoolVector3Array( -1, 0, 1, 1, 0, -1, 1, 0, 1, -1, 0, 1, -1, 0, -1, 1, 0, -1 )
+
+[node name="Test" type="Spatial"]
+script = ExtResource( 2 )
+spawn_count = 100
+
+[node name="Options" parent="." instance=ExtResource( 4 )]
+
+[node name="SpawnTarget" type="Spatial" parent="."]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 7.06418, -1.24693 )
+
+[node name="DynamicShapes" type="Spatial" parent="."]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -26.3192, 1.2359, 38.0117 )
+
+[node name="RigidBodyBox" type="RigidBody" parent="DynamicShapes"]
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyBox"]
+shape = SubResource( 1 )
+
+[node name="RigidBodyCapsule" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, 0 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyCapsule"]
+shape = SubResource( 2 )
+
+[node name="RigidBodyCylinder" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 4, 0, 0 )
+script = ExtResource( 3 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyCylinder"]
+shape = SubResource( 3 )
+
+[node name="RigidBodyConvex" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 0, 0 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodyConvex"]
+shape = SubResource( 4 )
+
+[node name="RigidBodySphere" type="RigidBody" parent="DynamicShapes"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 0, 0 )
+
+[node name="CollisionShape" type="CollisionShape" parent="DynamicShapes/RigidBodySphere"]
+shape = SubResource( 5 )
+
+[node name="StaticBodyWalls" type="StaticBody" parent="."]
+
+[node name="CollisionShape1" type="CollisionShape" parent="StaticBodyWalls"]
+transform = Transform( -1.62921e-05, 1, 0, -100, -1.62921e-07, 0, 0, 0, 100, -5, 0, 0 )
+shape = SubResource( 6 )
+
+[node name="CollisionShape2" type="CollisionShape" parent="StaticBodyWalls"]
+transform = Transform( -1.62921e-05, -1, 0, 100, -1.62921e-07, 0, 0, 0, 100, 5, 0, 0 )
+shape = SubResource( 6 )
+
+[node name="CollisionShape3" type="CollisionShape" parent="StaticBodyWalls"]
+transform = Transform( 2.65431e-12, 1.62921e-07, 100, 100, -1.62921e-07, 0, 1.62921e-05, 1, -1.62921e-05, 0, 0, -5 )
+shape = SubResource( 6 )
+
+[node name="CollisionShape4" type="CollisionShape" parent="StaticBodyWalls"]
+transform = Transform( 2.65431e-12, 1.62921e-07, -100, 100, -1.62921e-07, 0, -1.62921e-05, -1, -1.62921e-05, 0, 0, 5 )
+shape = SubResource( 6 )
+
+[node name="StaticScene" parent="." instance=ExtResource( 1 )]
+
+[node name="Camera" type="Camera" parent="."]
+transform = Transform( 1, 0, 0, 0, 0.881757, 0.471705, 0, -0.471705, 0.881757, 0, 20.4125, 41.0426 )
+script = ExtResource( 5 )
+
+[node name="OmniLight" type="OmniLight" parent="Camera"]
+omni_range = 80.0

+ 32 - 0
3d/physics_tests/tests/static_scene.tscn

@@ -0,0 +1,32 @@
+[gd_scene load_steps=5 format=2]
+
+[ext_resource path="res://assets/robot_head/godot3_robot_head_collision.tres" type="Shape" id=1]
+[ext_resource path="res://assets/robot_head/godot3_robot_head.mesh" type="ArrayMesh" id=2]
+
+[sub_resource type="PlaneMesh" id=1]
+
+[sub_resource type="ConcavePolygonShape" id=2]
+data = PoolVector3Array( -1, 0, 1, 1, 0, -1, 1, 0, 1, -1, 0, 1, -1, 0, -1, 1, 0, -1 )
+
+[node name="StaticScene" type="Spatial"]
+
+[node name="StaticBodyPlane" type="StaticBody" parent="."]
+
+[node name="MeshInstance" type="MeshInstance" parent="StaticBodyPlane"]
+transform = Transform( 50, 0, 0, 0, 1, 0, 0, 0, 50, 0, 0, 0 )
+mesh = SubResource( 1 )
+material/0 = null
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyPlane"]
+transform = Transform( 50, 0, 0, 0, 1, 0, 0, 0, 50, 0, 0, 0 )
+shape = SubResource( 2 )
+
+[node name="StaticBodyHead" type="StaticBody" parent="."]
+transform = Transform( 10, 0, 0, 0, 8.66025, 5, 0, -5, 8.66025, 0, -11.1389, 2.29332 )
+
+[node name="RobotHead" type="MeshInstance" parent="StaticBodyHead"]
+mesh = ExtResource( 2 )
+material/0 = null
+
+[node name="CollisionShape" type="CollisionShape" parent="StaticBodyHead"]
+shape = ExtResource( 1 )

+ 16 - 0
3d/physics_tests/tests/test_options.tscn

@@ -0,0 +1,16 @@
+[gd_scene load_steps=2 format=2]
+
+[ext_resource path="res://utils/option_menu.gd" type="Script" id=1]
+
+[node name="Options" type="MenuButton"]
+margin_left = 10.0
+margin_top = 106.719
+margin_right = 125.0
+margin_bottom = 126.719
+text = "TEST OPTIONS"
+flat = false
+align = 0
+script = ExtResource( 1 )
+__meta__ = {
+"_edit_use_anchors_": false
+}

+ 53 - 0
3d/physics_tests/tests_menu.gd

@@ -0,0 +1,53 @@
+extends OptionMenu
+
+
+class TestData :
+	var id
+	var scene_path
+
+
+var _test_list = []
+
+var _current_test = null
+var _current_test_scene = null
+
+
+func _ready():
+	connect("option_selected", self, "_on_option_selected")
+
+
+func _process(_delta):
+	if Input.is_action_just_pressed("restart_test"):
+		if _current_test:
+			_start_test(_current_test)
+
+
+func add_test(id, scene_path):
+	var test_data = TestData.new()
+	test_data.id = id
+	test_data.scene_path = scene_path
+	_test_list.append(test_data)
+	
+	add_menu_item(id)
+
+
+func _on_option_selected(item_path):
+	for test in _test_list:
+		if test.id == item_path:
+			_start_test(test)
+
+
+func _start_test(test):
+	_current_test = test
+	
+	if _current_test_scene:
+		_current_test_scene.queue_free()
+		_current_test_scene = null
+	
+	Log.print_log("*** STARTING TEST: " + test.id)
+	var scene = load(test.scene_path)
+	_current_test_scene = scene.instance()
+	get_tree().root.add_child(_current_test_scene)
+	
+	var label_test = get_node("../LabelTest")
+	label_test.test_name = test.id

+ 35 - 0
3d/physics_tests/utils/camera_orbit.gd

@@ -0,0 +1,35 @@
+extends Camera
+
+
+const ROTATION_COEFF = 0.02
+
+var _rotation_enabled = false
+var _rotation_pivot
+
+
+func _ready():
+	call_deferred("_initialize_pivot")
+
+
+func _unhandled_input(event):
+	var mouse_button_event = event as InputEventMouseButton
+	if mouse_button_event:
+		if mouse_button_event.button_index == BUTTON_LEFT:
+			_rotation_enabled = mouse_button_event.pressed
+		return
+	
+	if not _rotation_enabled:
+		return
+	
+	var mouse_motion_event = event as InputEventMouseMotion
+	if mouse_motion_event:
+		var rotation_delta = mouse_motion_event.relative.x
+		_rotation_pivot.rotate(Vector3.UP, -rotation_delta * ROTATION_COEFF)
+
+
+func _initialize_pivot():
+	_rotation_pivot = Spatial.new()
+	var camera_parent = get_parent()
+	camera_parent.add_child(_rotation_pivot)
+	camera_parent.remove_child(self)
+	_rotation_pivot.add_child(self)

+ 37 - 0
3d/physics_tests/utils/container_log.gd

@@ -0,0 +1,37 @@
+extends Control
+
+
+const MAX_ENTRIES = 100
+
+var _entry_template
+
+
+func _enter_tree():
+	Log.connect("entry_logged", self, "_on_log_entry")
+	
+	_entry_template = get_child(0) as Label
+	remove_child(_entry_template)
+
+
+func clear():
+	while get_child_count():
+		var entry = get_child(get_child_count() - 1)
+		remove_child(entry)
+		entry.queue_free()
+
+
+func _on_log_entry(message, type):
+	var new_entry = _entry_template.duplicate() as Label
+	
+	new_entry.set_text(message)
+	if type == Log.LogType.ERROR:
+		new_entry.modulate = Color.red
+	else:
+		new_entry.modulate = Color.white
+	
+	if get_child_count() >= MAX_ENTRIES:
+		var first_entry = get_child(0) as Label
+		remove_child(first_entry)
+		first_entry.queue_free()
+	
+	add_child(new_entry)

+ 30 - 0
3d/physics_tests/utils/control3d.gd

@@ -0,0 +1,30 @@
+extends Control
+
+
+export(Vector3) var world_offset
+
+var _pos_offset
+var _attachment
+
+
+func _ready():
+	_pos_offset = rect_position
+	_attachment = get_parent() as Spatial
+
+
+func _process(_delta):
+	if _attachment == null:
+		return
+	
+	var viewport = get_viewport()
+	if viewport == null:
+		return
+	
+	var camera = viewport.get_camera()
+	if camera == null:
+		return
+	
+	var world_pos = world_offset + _attachment.global_transform.origin
+	var screen_pos = camera.unproject_position(world_pos)
+	
+	rect_position = _pos_offset + screen_pos - 0.5 * rect_size

+ 8 - 0
3d/physics_tests/utils/exception_cylinder.gd

@@ -0,0 +1,8 @@
+extends Node
+
+
+func _enter_tree():
+	if System.get_physics_engine() == System.PhysicsEngine.GODOT_PHYSICS:
+		Log.print_error("Cylinder shapes not supported, removing '%s'." % name)
+		get_parent().remove_child(self)
+		queue_free()

+ 14 - 0
3d/physics_tests/utils/label_engine.gd

@@ -0,0 +1,14 @@
+extends Label
+
+
+func _process(_delta):
+	var engine_name = ""
+	match System.get_physics_engine():
+		System.PhysicsEngine.BULLET:
+			engine_name = "Bullet"
+		System.PhysicsEngine.GODOT_PHYSICS:
+			engine_name = "Godot Physics"
+		System.PhysicsEngine.OTHER:
+			var engine_setting = ProjectSettings.get_setting("physics/3d/physics_engine")
+			engine_name = "Other (%s)" % engine_setting
+	set_text("Physics engine: %s" % engine_name)

+ 5 - 0
3d/physics_tests/utils/label_fps.gd

@@ -0,0 +1,5 @@
+extends Label
+
+
+func _process(_delta):
+	set_text("FPS: %d" % Engine.get_frames_per_second())

+ 13 - 0
3d/physics_tests/utils/label_test.gd

@@ -0,0 +1,13 @@
+extends Label
+
+
+var test_name setget _set_test_name
+
+
+func _ready():
+	set_text("Select a test from the menu to start it")
+
+
+func _set_test_name(value):
+	test_name = value
+	set_text("Test: %s" % test_name)

+ 5 - 0
3d/physics_tests/utils/label_version.gd

@@ -0,0 +1,5 @@
+extends Label
+
+
+func _process(_delta):
+	set_text("Godot Version: %s" % Engine.get_version_info().string)

+ 47 - 0
3d/physics_tests/utils/option_menu.gd

@@ -0,0 +1,47 @@
+class_name OptionMenu
+extends MenuButton
+
+
+signal option_selected(item_path)
+
+
+func add_menu_item(item_path):
+	var path_elements = item_path.split("/", false)
+	var path_element_count = path_elements.size()
+	assert(path_element_count > 0)
+	
+	var path = ""
+	var popup = get_popup()
+	for element_index in path_element_count - 1:
+		var popup_label = path_elements[element_index]
+		path += popup_label + "/"
+		popup = _add_popup(popup, path, popup_label)
+	
+	_add_item(popup, path_elements[path_element_count - 1])
+
+
+func _add_item(parent_popup, label):
+	parent_popup.add_item(label)
+
+
+func _add_popup(parent_popup, path, label):
+	if parent_popup.has_node(label):
+		var popup_node = parent_popup.get_node(label)
+		var popup_menu = popup_node as PopupMenu
+		assert(popup_menu)
+		return popup_menu
+	
+	var popup_menu = PopupMenu.new()
+	popup_menu.name = label
+	
+	parent_popup.add_child(popup_menu)
+	parent_popup.add_submenu_item(label, label)
+	
+	popup_menu.connect("index_pressed", self, "_on_item_pressed", [popup_menu, path])
+	
+	return popup_menu
+
+
+func _on_item_pressed(item_index, popup_menu, path):
+	var item_path = path + popup_menu.get_item_text(item_index)
+	emit_signal("option_selected", item_path)

+ 14 - 0
3d/physics_tests/utils/scroll_log.gd

@@ -0,0 +1,14 @@
+extends ScrollContainer
+
+
+export(bool) var auto_scroll = false setget set_auto_scroll
+
+
+func _process(_delta):
+	if auto_scroll:
+		var scrollbar = get_v_scrollbar()
+		scrollbar.value = scrollbar.max_value
+
+
+func set_auto_scroll(value):
+	auto_scroll = value

+ 53 - 0
3d/physics_tests/utils/system.gd

@@ -0,0 +1,53 @@
+extends Node
+
+
+enum PhysicsEngine {
+	BULLET,
+	GODOT_PHYSICS,
+	OTHER,
+}
+
+var _engine = PhysicsEngine.OTHER
+
+
+func _enter_tree():
+	get_tree().debug_collisions_hint = true
+	
+	var engine_string = ProjectSettings.get_setting("physics/3d/physics_engine")
+	match engine_string:
+		"DEFAULT":
+			_engine = PhysicsEngine.BULLET
+		"Bullet":
+			_engine = PhysicsEngine.BULLET
+		"GodotPhysics":
+			_engine = PhysicsEngine.GODOT_PHYSICS
+		_:
+			_engine = PhysicsEngine.OTHER
+
+
+func _process(_delta):
+	if Input.is_action_just_pressed("toggle_full_screen"):
+		OS.window_fullscreen = not OS.window_fullscreen
+	
+	if Input.is_action_just_pressed("toggle_debug_collision"):
+		var debug_collision_enabled = not _is_debug_collision_enabled()
+		_set_debug_collision_enabled(debug_collision_enabled)
+		if debug_collision_enabled:
+			Log.print_log("Debug Collision ON")
+		else:
+			Log.print_log("Debug Collision OFF")
+	
+	if Input.is_action_just_pressed("exit"):
+		get_tree().quit()
+
+
+func get_physics_engine():
+	return _engine
+
+
+func _set_debug_collision_enabled(enabled):
+	get_tree().debug_collisions_hint = enabled
+
+
+func _is_debug_collision_enabled():
+	return get_tree().debug_collisions_hint

+ 20 - 0
3d/physics_tests/utils/system_log.gd

@@ -0,0 +1,20 @@
+extends Node
+
+
+enum LogType {
+	LOG,
+	ERROR,
+}
+
+signal entry_logged(message, type)
+
+
+func print_log(message):
+	print(message)
+	emit_signal("entry_logged", message, LogType.LOG)
+
+
+func print_error(message):
+	push_error(message)
+	printerr(message)
+	emit_signal("entry_logged", message, LogType.ERROR)

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