1 /*************************************************************************/
2 /*  proximity_group.cpp                                                  */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 
31 #include "proximity_group.h"
32 
33 #include "core/math/math_funcs.h"
34 
clear_groups()35 void ProximityGroup::clear_groups() {
36 
37 	Map<StringName, uint32_t>::Element *E;
38 
39 	{
40 		const int size = 16;
41 		StringName remove_list[size];
42 		E = groups.front();
43 		int num = 0;
44 		while (E && num < size) {
45 
46 			if (E->get() != group_version) {
47 				remove_list[num++] = E->key();
48 			};
49 
50 			E = E->next();
51 		};
52 		for (int i = 0; i < num; i++) {
53 
54 			groups.erase(remove_list[i]);
55 		};
56 	};
57 
58 	if (E) {
59 		clear_groups(); // call until we go through the whole list
60 	};
61 };
62 
update_groups()63 void ProximityGroup::update_groups() {
64 
65 	if (grid_radius == Vector3(0, 0, 0))
66 		return;
67 
68 	++group_version;
69 
70 	Vector3 pos = get_global_transform().get_origin();
71 	Vector3 vcell = pos / cell_size;
72 	int cell[3] = { Math::fast_ftoi(vcell.x), Math::fast_ftoi(vcell.y), Math::fast_ftoi(vcell.z) };
73 
74 	add_groups(cell, group_name, 0);
75 
76 	clear_groups();
77 };
78 
add_groups(int * p_cell,String p_base,int p_depth)79 void ProximityGroup::add_groups(int *p_cell, String p_base, int p_depth) {
80 
81 	p_base = p_base + "|";
82 	if (grid_radius[p_depth] == 0) {
83 
84 		if (p_depth == 2) {
85 			_new_group(p_base);
86 		} else {
87 			add_groups(p_cell, p_base, p_depth + 1);
88 		};
89 	};
90 
91 	int start = p_cell[p_depth] - grid_radius[p_depth];
92 	int end = p_cell[p_depth] + grid_radius[p_depth];
93 
94 	for (int i = start; i <= end; i++) {
95 
96 		String gname = p_base + itos(i);
97 		if (p_depth == 2) {
98 			_new_group(gname);
99 		} else {
100 			add_groups(p_cell, gname, p_depth + 1);
101 		};
102 	};
103 };
104 
_new_group(StringName p_name)105 void ProximityGroup::_new_group(StringName p_name) {
106 
107 	const Map<StringName, uint32_t>::Element *E = groups.find(p_name);
108 	if (!E) {
109 		add_to_group(p_name);
110 	};
111 
112 	groups[p_name] = group_version;
113 };
114 
_notification(int p_what)115 void ProximityGroup::_notification(int p_what) {
116 
117 	switch (p_what) {
118 
119 		case NOTIFICATION_EXIT_TREE:
120 			++group_version;
121 			clear_groups();
122 			break;
123 		case NOTIFICATION_TRANSFORM_CHANGED:
124 			update_groups();
125 			break;
126 	};
127 };
128 
broadcast(String p_name,Variant p_params)129 void ProximityGroup::broadcast(String p_name, Variant p_params) {
130 
131 	Map<StringName, uint32_t>::Element *E;
132 	E = groups.front();
133 	while (E) {
134 
135 		get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_name, p_params);
136 		E = E->next();
137 	};
138 };
139 
_proximity_group_broadcast(String p_name,Variant p_params)140 void ProximityGroup::_proximity_group_broadcast(String p_name, Variant p_params) {
141 
142 	if (dispatch_mode == MODE_PROXY) {
143 
144 		get_parent()->call(p_name, p_params);
145 	} else {
146 
147 		emit_signal("broadcast", p_name, p_params);
148 	};
149 };
150 
set_group_name(const String & p_group_name)151 void ProximityGroup::set_group_name(const String &p_group_name) {
152 
153 	group_name = p_group_name;
154 };
155 
get_group_name() const156 String ProximityGroup::get_group_name() const {
157 
158 	return group_name;
159 };
160 
set_dispatch_mode(DispatchMode p_mode)161 void ProximityGroup::set_dispatch_mode(DispatchMode p_mode) {
162 
163 	dispatch_mode = p_mode;
164 };
165 
get_dispatch_mode() const166 ProximityGroup::DispatchMode ProximityGroup::get_dispatch_mode() const {
167 
168 	return dispatch_mode;
169 };
170 
set_grid_radius(const Vector3 & p_radius)171 void ProximityGroup::set_grid_radius(const Vector3 &p_radius) {
172 
173 	grid_radius = p_radius;
174 };
175 
get_grid_radius() const176 Vector3 ProximityGroup::get_grid_radius() const {
177 
178 	return grid_radius;
179 };
180 
_bind_methods()181 void ProximityGroup::_bind_methods() {
182 
183 	ClassDB::bind_method(D_METHOD("set_group_name", "name"), &ProximityGroup::set_group_name);
184 	ClassDB::bind_method(D_METHOD("get_group_name"), &ProximityGroup::get_group_name);
185 	ClassDB::bind_method(D_METHOD("set_dispatch_mode", "mode"), &ProximityGroup::set_dispatch_mode);
186 	ClassDB::bind_method(D_METHOD("get_dispatch_mode"), &ProximityGroup::get_dispatch_mode);
187 	ClassDB::bind_method(D_METHOD("set_grid_radius", "radius"), &ProximityGroup::set_grid_radius);
188 	ClassDB::bind_method(D_METHOD("get_grid_radius"), &ProximityGroup::get_grid_radius);
189 	ClassDB::bind_method(D_METHOD("broadcast", "name", "parameters"), &ProximityGroup::broadcast);
190 	ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "name", "params"), &ProximityGroup::_proximity_group_broadcast);
191 
192 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "group_name"), "set_group_name", "get_group_name");
193 	ADD_PROPERTY(PropertyInfo(Variant::INT, "dispatch_mode", PROPERTY_HINT_ENUM, "Proxy,Signal"), "set_dispatch_mode", "get_dispatch_mode");
194 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "grid_radius"), "set_grid_radius", "get_grid_radius");
195 
196 	ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "group_name"), PropertyInfo(Variant::ARRAY, "parameters")));
197 
198 	BIND_ENUM_CONSTANT(MODE_PROXY);
199 	BIND_ENUM_CONSTANT(MODE_SIGNAL);
200 };
201 
ProximityGroup()202 ProximityGroup::ProximityGroup() {
203 
204 	group_version = 0;
205 	dispatch_mode = MODE_PROXY;
206 
207 	cell_size = 1.0;
208 	grid_radius = Vector3(1, 1, 1);
209 	set_notify_transform(true);
210 };
211 
~ProximityGroup()212 ProximityGroup::~ProximityGroup(){
213 
214 };
215