Skip to content

Commit 95b42d3

Browse files
committed
Done Example Scenes
1 parent 05e3bb0 commit 95b42d3

File tree

84 files changed

+7641
-274
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+7641
-274
lines changed

.sconsign.dblite

11.9 KB
Binary file not shown.

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1-
![](./project_logo.png)
1+
![](./images/project_logo.png)
22

3-
The godot-quarkphysics project is a native plugin that integrates QuarkPhysics into Godot Engine versions 4.3 and above.
3+
The project is a native plugin that integrates QuarkPhysics into Godot Engine versions 4.3 and above.
44
QuarkPhysics is a physics engine for 2D games, capable of simulating Rigid Body, Soft Body, and various dynamics. You can find detailed information on the [project page](https://github.com/erayzesen/QuarkPhysics).
55

6-
Note: This project is currently in the alpha stage. You can test it and contribute by reporting bugs.
6+
[![Watch Examples](./images/examples_play.png)()](https://www.youtube.com/watch?v=KxsLLHMrB-Q)
77

88
## Features of the Extension:
99

1010
* Covers the entire API of the physics engine and can be easily used with the engine's up-to-date [documentation](https://erayzesen.github.io/QuarkPhysics/documentation/).
1111
* Can render QMeshNode objects with various advanced settings.
12-
* Includes tools for editing QMeshNode objects.
12+
* Includes `QMeshEditor` plugin for editing QMeshAdvancedNode objects.
1313
* Supports all platforms provided by Godot's GDExtension.
1414
* Includes example projects created with QuarkPhysics.
1515

1616
## How to Use?
17+
You can use it like other Godot 4 native extension and plugins. Simply download the latest version from the Releases section and add the `addons` folder to your project as is. If you want to use the `QMeshEditor` plugin, make sure it is activated in Godot's project settings.
18+
19+
Additionally, you can check out the example scenes and applications in the `examples` folder. If you don't need them, there's no need to add anything other than the addon folder to your project.
20+
21+
## How to Contribute?
22+
You can contribute to the project in many ways. At this stage, the most valuable contribution is to test the project and report any bugs you encounter. You can also share your experiences. Additionally, you can help by working on documentation, creating tutorials, or promoting the project.
23+
24+
If you use QuarkPhysics in one of your game projects, please don’t forget to let me know at erayzesen@gmail.com. This would be a significant contribution to the showcase we plan to present soon.
25+
1726

18-
Until a stable release is published, no precompiled builds or initial releases are available. You can fork the project and compile it using Godot's official GDExtension compilation tutorials. The builds are placed in the `/project/addons/quarkphysics/bin` directory. You can copy the `addons` folder under the `project` directory to your project and start using it. Alternatively, you can check out the example Godot project available in the `/project` directory of this repository.
1927

2028

2129

images/examples_play.png

197 KB
Loading
File renamed without changes.
Binary file not shown.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
class_name QMeshSnapHelper extends RefCounted
2+
3+
#These Helper Functions were taken from: @bitwes - https://github.com/godotengine/godot-proposals/issues/10529#issuecomment-2501936526
4+
var found_all_editor_controls = false
5+
var snap_enabled: bool = false
6+
var snap_step : Vector2 = Vector2.ZERO
7+
var snap_offset: Vector2 = Vector2.ZERO
8+
9+
var editor_controls = {
10+
dialog = null,
11+
grid_offset_x = null,
12+
grid_offset_y = null,
13+
grid_snap_x = null,
14+
grid_snap_y = null,
15+
snap_toggle = null
16+
}
17+
18+
19+
func _init():
20+
if(Engine.is_editor_hint()):
21+
setup_controls()
22+
update_values_from_editor()
23+
24+
25+
func setup_controls():
26+
var main_screen = EditorInterface.get_editor_main_screen()
27+
editor_controls.snap_toggle = safe_get_descendant(main_screen, ["HBoxContainer", 12], Button)
28+
29+
var snap_dia_grid_container = safe_get_descendant(main_screen, ["SnapDialog", "GridContainer"], GridContainer)
30+
editor_controls.grid_offset_x = safe_get_child(snap_dia_grid_container, 1, SpinBox)
31+
editor_controls.grid_offset_y = safe_get_child(snap_dia_grid_container, 2, SpinBox)
32+
editor_controls.grid_snap_x = safe_get_child(snap_dia_grid_container, 4, SpinBox)
33+
editor_controls.grid_snap_y = safe_get_child(snap_dia_grid_container, 5, SpinBox)
34+
editor_controls.dialog = find_child_with_name_containing(main_screen, "SnapDialog", ConfirmationDialog)
35+
36+
if(null in editor_controls.values()):
37+
push_error("One or more Editor Controls could not be found.")
38+
return
39+
else:
40+
found_all_editor_controls = true
41+
42+
editor_controls.snap_toggle.toggled.connect(func(tog: bool) -> void: snap_enabled = tog)
43+
editor_controls.dialog.get_ok_button().pressed.connect(update_values_from_editor)
44+
45+
46+
func update_values_from_editor():
47+
if(!found_all_editor_controls):
48+
return
49+
snap_enabled = editor_controls.snap_toggle.button_pressed
50+
snap_offset.x = editor_controls.grid_offset_x.value
51+
snap_offset.y = editor_controls.grid_offset_y.value
52+
snap_step.x = editor_controls.grid_snap_x.value
53+
snap_step.y = editor_controls.grid_snap_y.value
54+
55+
func find_child_with_name_containing(parent, child_name, expected_type = null):
56+
var to_return = parent.find_child("*%s*" % child_name, true, false)
57+
58+
if(to_return == null):
59+
push_error("Could not find child named ", child_name, " in ", parent)
60+
elif(expected_type != null and not is_instance_of(to_return, expected_type)):
61+
push_error("Expected ", to_return, ' to be an instance of ', expected_type)
62+
to_return = null
63+
64+
return to_return
65+
66+
func safe_get_child(parent, index, expected_type = null):
67+
if(parent != null and index < parent.get_child_count()):
68+
var found = parent.get_child(index)
69+
if(expected_type != null and not is_instance_of(found, expected_type)):
70+
push_error("Expected ", found, ' to be an instance of ', expected_type)
71+
found = null
72+
return found
73+
else:
74+
push_error(str("Could not get child index ", index, " on ", parent, '. ', parent, ' has ', parent.get_child_count(), ' children.'))
75+
return null
76+
77+
func safe_get_descendant(parent, names_and_or_indexes, expected_type = null):
78+
var here = parent
79+
var index = 0
80+
81+
while(here != null and index < names_and_or_indexes.size()):
82+
var entry = names_and_or_indexes[index]
83+
if(typeof(entry) == TYPE_STRING):
84+
here = find_child_with_name_containing(here, entry)
85+
elif(typeof(entry) == TYPE_INT):
86+
here = safe_get_child(here, entry)
87+
index += 1
88+
89+
if(here != null and expected_type != null and not is_instance_of(here, expected_type)):
90+
push_error("Expected ", here, ' to be an instance of ', expected_type)
91+
here = null
92+
93+
return here
94+
95+
var descendant_props_to_print = ['text', 'value']
96+
97+
func print_descendants(parent, indent='', child_index = -1):
98+
var text = ""
99+
if(child_index != -1):
100+
text = str(indent, '[', child_index, '] ', parent)
101+
else:
102+
text = str(indent, parent)
103+
104+
for ptp in descendant_props_to_print:
105+
if(parent.get(ptp) != null):
106+
text += str(' ', ptp, ' = ', parent.get(ptp))
107+
print(indent, text)
108+
109+
for i in range(parent.get_child_count()):
110+
print_descendants(parent.get_child(i), indent + ' ', i)

project/addons/quarkphysics/qmesh_editor/qmesh_editor_tool.gd

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ var last_mouse_motion_position=Vector2.ZERO
1111
#Plugin References
1212
var radiusSpinBox:SpinBox
1313
var internalCheckBox:CheckBox
14+
var operationOptionButton:OptionButton
1415
var plugin:EditorPlugin
1516

17+
#Helpers
18+
var snapHelper:QMeshSnapHelper=QMeshSnapHelper.new()
19+
1620
func _reset_tool() :
1721
mouse_drag_mode=false
1822
last_mouse_pressed_position=Vector2.ZERO
@@ -32,6 +36,7 @@ func _internal_checkbox_toggled(toggled_on:bool) :
3236
func _radius_bar_changed(value:float) :
3337
pass
3438

39+
3540
func toScreen(point:Vector2) ->Vector2 :
3641
var viewport_transform=EditorInterface.get_editor_viewport_2d().get_final_transform()*EditorInterface.get_base_control().get_canvas_transform()
3742
return viewport_transform*point
@@ -48,12 +53,77 @@ func get_nearest_particle_index(targetMeshNode:QMeshNode,position:Vector2) ->int
4853
return i
4954
return -1;
5055

56+
func get_nearest_spring_index(targetMeshNode:QMeshNode, position:Vector2, internal:bool)->int :
57+
var min_distance:float=INF
58+
var search_range=3.0
59+
var spring_index=-1
60+
var springs=targetMeshNode.data_internal_springs if internal else targetMeshNode.data_springs
61+
for i in range(springs.size()) :
62+
var spring=springs[i]
63+
var pA=targetMeshNode.data_particle_positions[ spring[0] ]+meshNode.global_position
64+
var pB=targetMeshNode.data_particle_positions[ spring[1] ]++meshNode.global_position
65+
var a_vec=(pB-pA)
66+
var unit=a_vec.normalized()
67+
var normal=unit.orthogonal()
68+
var b_vec=position-pA
69+
var proj=b_vec.dot(unit)
70+
if proj>=0 && proj<=a_vec.length() :
71+
var distance=abs( b_vec.dot(normal) )
72+
if distance<search_range :
73+
if distance<min_distance :
74+
spring_index=i
75+
min_distance=distance
76+
77+
78+
return spring_index
79+
80+
func snap_to_grid(point:Vector2) ->Vector2:
81+
var snapped_x = roundf(point.x / snapHelper.snap_step.x) * snapHelper.snap_step.x
82+
var snapped_y = roundf(point.y / snapHelper.snap_step.y) * snapHelper.snap_step.y
83+
var res = Vector2(snapped_x, snapped_y) + snapHelper.snap_offset
84+
return res
85+
86+
87+
func get_uv_map_index_at_position(targetMeshNode:QMeshNode, position:Vector2) -> int:
88+
for i in range(targetMeshNode.data_uv_maps.size()) :
89+
var map=targetMeshNode.data_uv_maps[i]
90+
var uv_poly:PackedVector2Array
91+
for j in range(map.size()):
92+
uv_poly.push_back( targetMeshNode.data_particle_positions[ map[j] ]+targetMeshNode.global_position )
93+
if Geometry2D.is_point_in_polygon(position,uv_poly) :
94+
return i
95+
return -1
96+
97+
pass
98+
99+
51100
func search_spring(targetMeshNode:QMeshNode,first_index:int,second_index:int,internal_springs:bool)->int:
52101
var spring_list=targetMeshNode.data_springs if internal_springs==false else targetMeshNode.data_internal_springs
53102
for i in range(spring_list.size()) :
54-
if targetMeshNode.data_springs[i][0]==first_index && targetMeshNode.data_springs[i][1]==second_index :
103+
if spring_list[i][0]==first_index && spring_list[i][1]==second_index :
55104
return i
56105

57-
if targetMeshNode.data_springs[i][1]==first_index && targetMeshNode.data_springs[i][0]==second_index :
106+
if spring_list[i][1]==first_index && spring_list[i][0]==second_index :
58107
return i
59108
return -1
109+
110+
func is_particle_exist_in_springs(targetMeshNode:QMeshNode,particleIndex:int, internal:bool)->bool:
111+
var springs=targetMeshNode.data_springs if internal==false else targetMeshNode.data_internal_springs
112+
for i in range(springs.size()):
113+
var spring=springs[i]
114+
if spring[0]==particleIndex || spring[1]==particleIndex :
115+
return true
116+
return false
117+
118+
func is_particle_exist_in_polygon(targetMeshNode:QMeshNode,particleIndex:int)->bool:
119+
for i in range(targetMeshNode.data_polygon.size() ):
120+
if targetMeshNode.data_polygon[i]==particleIndex :
121+
return true
122+
return false
123+
func is_particle_exist_in_uv(targetMeshNode:QMeshNode,particleIndex:int)->bool:
124+
for i in range(targetMeshNode.data_uv_maps.size() ):
125+
var map=targetMeshNode.data_uv_maps[i]
126+
for j in range(map.size()) :
127+
if map[j]==particleIndex :
128+
return true
129+
return false

project/addons/quarkphysics/qmesh_editor/qmesh_editor_tool_bar.tscn

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
[gd_scene load_steps=3 format=3 uid="uid://1iyw5k4em234"]
1+
[gd_scene load_steps=8 format=3 uid="uid://1iyw5k4em234"]
22

33
[ext_resource type="Script" path="res://addons/quarkphysics/qmesh_editor/qmesh_editor_tool_bar.gd" id="1_8n687"]
4+
[ext_resource type="Texture2D" uid="uid://dp3nkgnpb61vc" path="res://addons/quarkphysics/src/icon_select_tool.svg" id="2_7b1ko"]
5+
[ext_resource type="Texture2D" uid="uid://dxqm8s0qwcci" path="res://addons/quarkphysics/src/icon_particle_tool.svg" id="3_ujk4v"]
6+
[ext_resource type="Texture2D" uid="uid://4flsqjsp3hwi" path="res://addons/quarkphysics/src/icon_spring_tool.svg" id="4_m4cpb"]
7+
[ext_resource type="Texture2D" uid="uid://cq6tapejdqkxs" path="res://addons/quarkphysics/src/icon_polygon_tool.svg" id="5_parlp"]
8+
[ext_resource type="Texture2D" uid="uid://24koqbn2wrt6" path="res://addons/quarkphysics/src/icon_uv_tool.svg" id="6_0a26w"]
49

510
[sub_resource type="ButtonGroup" id="ButtonGroup_l0uhj"]
611

712
[node name="Control" type="HBoxContainer"]
8-
custom_minimum_size = Vector2(605, 0)
13+
custom_minimum_size = Vector2(500, 0)
914
anchors_preset = 15
1015
anchor_right = 1.0
1116
anchor_bottom = 1.0
@@ -16,49 +21,75 @@ script = ExtResource("1_8n687")
1621
[node name="EditButton" type="Button" parent="."]
1722
layout_mode = 2
1823
size_flags_vertical = 4
24+
tooltip_text = "Edit QMesh"
1925
toggle_mode = true
2026
text = "Edit QMesh"
2127

2228
[node name="SelectButton" type="Button" parent="."]
2329
visible = false
30+
custom_minimum_size = Vector2(32, 32)
2431
layout_mode = 2
2532
size_flags_vertical = 4
33+
tooltip_text = "Select And Edit Particles"
2634
toggle_mode = true
2735
button_pressed = true
2836
button_group = SubResource("ButtonGroup_l0uhj")
29-
text = "Select"
37+
icon = ExtResource("2_7b1ko")
38+
icon_alignment = 1
3039

3140
[node name="ParticleButton" type="Button" parent="."]
3241
visible = false
42+
custom_minimum_size = Vector2(32, 32)
3343
layout_mode = 2
3444
size_flags_vertical = 4
45+
tooltip_text = "Add/Remove Particle"
3546
toggle_mode = true
3647
button_group = SubResource("ButtonGroup_l0uhj")
37-
text = "Particle"
48+
icon = ExtResource("3_ujk4v")
49+
icon_alignment = 1
3850

3951
[node name="SpringButton" type="Button" parent="."]
4052
visible = false
53+
custom_minimum_size = Vector2(32, 32)
4154
layout_mode = 2
4255
size_flags_vertical = 4
56+
tooltip_text = "Add/Remove Spring"
4357
toggle_mode = true
4458
button_group = SubResource("ButtonGroup_l0uhj")
45-
text = "Spring"
59+
icon = ExtResource("4_m4cpb")
60+
icon_alignment = 1
4661

4762
[node name="PolygonButton" type="Button" parent="."]
4863
visible = false
64+
custom_minimum_size = Vector2(32, 32)
4965
layout_mode = 2
5066
size_flags_vertical = 4
67+
tooltip_text = "Edit Polygon"
5168
toggle_mode = true
5269
button_group = SubResource("ButtonGroup_l0uhj")
53-
text = "Polygon"
70+
icon = ExtResource("5_parlp")
71+
icon_alignment = 1
5472

5573
[node name="UVButton" type="Button" parent="."]
5674
visible = false
75+
custom_minimum_size = Vector2(32, 32)
5776
layout_mode = 2
5877
size_flags_vertical = 4
78+
tooltip_text = "Add/Remove UV Map"
5979
toggle_mode = true
6080
button_group = SubResource("ButtonGroup_l0uhj")
61-
text = "UV"
81+
icon = ExtResource("6_0a26w")
82+
icon_alignment = 1
83+
84+
[node name="OperationOptionButton" type="OptionButton" parent="."]
85+
visible = false
86+
layout_mode = 2
87+
size_flags_vertical = 4
88+
selected = 0
89+
item_count = 2
90+
popup/item_0/text = "ADD"
91+
popup/item_1/text = "REMOVE"
92+
popup/item_1/id = 1
6293

6394
[node name="InternalCheckBox" type="CheckBox" parent="."]
6495
visible = false

0 commit comments

Comments
 (0)