1 /*************************************************************************/
2 /*  camera_2d.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 "camera_2d.h"
32 
33 #include "core/engine.h"
34 #include "core/math/math_funcs.h"
35 #include "scene/scene_string_names.h"
36 #include "servers/visual_server.h"
37 
_update_scroll()38 void Camera2D::_update_scroll() {
39 
40 	if (!is_inside_tree())
41 		return;
42 
43 	if (Engine::get_singleton()->is_editor_hint()) {
44 		update(); //will just be drawn
45 		return;
46 	}
47 
48 	if (!viewport)
49 		return;
50 
51 	if (current) {
52 
53 		ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
54 
55 		Transform2D xform = get_camera_transform();
56 
57 		viewport->set_canvas_transform(xform);
58 
59 		Size2 screen_size = viewport->get_visible_rect().size;
60 		Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
61 
62 		get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset);
63 	};
64 }
65 
_update_process_mode()66 void Camera2D::_update_process_mode() {
67 
68 	if (Engine::get_singleton()->is_editor_hint()) {
69 		set_process_internal(false);
70 		set_physics_process_internal(false);
71 	} else if (process_mode == CAMERA2D_PROCESS_IDLE) {
72 		set_process_internal(true);
73 		set_physics_process_internal(false);
74 	} else {
75 		set_process_internal(false);
76 		set_physics_process_internal(true);
77 	}
78 }
79 
set_zoom(const Vector2 & p_zoom)80 void Camera2D::set_zoom(const Vector2 &p_zoom) {
81 
82 	zoom = p_zoom;
83 	Point2 old_smoothed_camera_pos = smoothed_camera_pos;
84 	_update_scroll();
85 	smoothed_camera_pos = old_smoothed_camera_pos;
86 };
87 
get_zoom() const88 Vector2 Camera2D::get_zoom() const {
89 
90 	return zoom;
91 };
92 
get_camera_transform()93 Transform2D Camera2D::get_camera_transform() {
94 
95 	if (!get_tree())
96 		return Transform2D();
97 
98 	ERR_FAIL_COND_V(custom_viewport && !ObjectDB::get_instance(custom_viewport_id), Transform2D());
99 
100 	Size2 screen_size = viewport->get_visible_rect().size;
101 
102 	Point2 new_camera_pos = get_global_transform().get_origin();
103 	Point2 ret_camera_pos;
104 
105 	if (!first) {
106 
107 		if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
108 
109 			if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint() && !h_offset_changed) {
110 				camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_LEFT]));
111 				camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_RIGHT]));
112 			} else {
113 
114 				if (h_ofs < 0) {
115 					camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs;
116 				} else {
117 					camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs;
118 				}
119 
120 				h_offset_changed = false;
121 			}
122 
123 			if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint() && !v_offset_changed) {
124 
125 				camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_TOP]));
126 				camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_BOTTOM]));
127 
128 			} else {
129 
130 				if (v_ofs < 0) {
131 					camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs;
132 				} else {
133 					camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs;
134 				}
135 
136 				v_offset_changed = false;
137 			}
138 
139 		} else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) {
140 
141 			camera_pos = new_camera_pos;
142 		}
143 
144 		Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2());
145 		Rect2 screen_rect(-screen_offset + camera_pos, screen_size * zoom);
146 
147 		if (limit_smoothing_enabled) {
148 			if (screen_rect.position.x < limit[MARGIN_LEFT])
149 				camera_pos.x -= screen_rect.position.x - limit[MARGIN_LEFT];
150 
151 			if (screen_rect.position.x + screen_rect.size.x > limit[MARGIN_RIGHT])
152 				camera_pos.x -= screen_rect.position.x + screen_rect.size.x - limit[MARGIN_RIGHT];
153 
154 			if (screen_rect.position.y + screen_rect.size.y > limit[MARGIN_BOTTOM])
155 				camera_pos.y -= screen_rect.position.y + screen_rect.size.y - limit[MARGIN_BOTTOM];
156 
157 			if (screen_rect.position.y < limit[MARGIN_TOP])
158 				camera_pos.y -= screen_rect.position.y - limit[MARGIN_TOP];
159 		}
160 
161 		if (smoothing_enabled && !Engine::get_singleton()->is_editor_hint()) {
162 
163 			float c = smoothing * (process_mode == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time());
164 			smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos;
165 			ret_camera_pos = smoothed_camera_pos;
166 			//camera_pos=camera_pos*(1.0-smoothing)+new_camera_pos*smoothing;
167 		} else {
168 
169 			ret_camera_pos = smoothed_camera_pos = camera_pos;
170 		}
171 
172 	} else {
173 		ret_camera_pos = smoothed_camera_pos = camera_pos = new_camera_pos;
174 		first = false;
175 	}
176 
177 	Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2());
178 
179 	float angle = get_global_transform().get_rotation();
180 	if (rotating) {
181 		screen_offset = screen_offset.rotated(angle);
182 	}
183 
184 	Rect2 screen_rect(-screen_offset + ret_camera_pos, screen_size * zoom);
185 	if (screen_rect.position.x < limit[MARGIN_LEFT])
186 		screen_rect.position.x = limit[MARGIN_LEFT];
187 
188 	if (screen_rect.position.x + screen_rect.size.x > limit[MARGIN_RIGHT])
189 		screen_rect.position.x = limit[MARGIN_RIGHT] - screen_rect.size.x;
190 
191 	if (screen_rect.position.y + screen_rect.size.y > limit[MARGIN_BOTTOM])
192 		screen_rect.position.y = limit[MARGIN_BOTTOM] - screen_rect.size.y;
193 
194 	if (screen_rect.position.y < limit[MARGIN_TOP])
195 		screen_rect.position.y = limit[MARGIN_TOP];
196 
197 	if (offset != Vector2())
198 		screen_rect.position += offset;
199 
200 	camera_screen_center = screen_rect.position + screen_rect.size * 0.5;
201 
202 	Transform2D xform;
203 	if (rotating) {
204 		xform.set_rotation(angle);
205 	}
206 	xform.scale_basis(zoom);
207 	xform.set_origin(screen_rect.position /*.floor()*/);
208 
209 	/*
210 	if (0) {
211 		xform = get_global_transform() * xform;
212 	} else {
213 		xform.elements[2]+=get_global_transform().get_origin();
214 	}
215 */
216 
217 	return (xform).affine_inverse();
218 }
219 
_notification(int p_what)220 void Camera2D::_notification(int p_what) {
221 
222 	switch (p_what) {
223 
224 		case NOTIFICATION_INTERNAL_PROCESS:
225 		case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
226 
227 			_update_scroll();
228 
229 		} break;
230 		case NOTIFICATION_TRANSFORM_CHANGED: {
231 
232 			if (!is_processing_internal() && !is_physics_processing_internal())
233 				_update_scroll();
234 
235 		} break;
236 		case NOTIFICATION_ENTER_TREE: {
237 
238 			if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
239 				viewport = custom_viewport;
240 			} else {
241 				viewport = get_viewport();
242 			}
243 
244 			canvas = get_canvas();
245 
246 			RID vp = viewport->get_viewport_rid();
247 
248 			group_name = "__cameras_" + itos(vp.get_id());
249 			canvas_group_name = "__cameras_c" + itos(canvas.get_id());
250 			add_to_group(group_name);
251 			add_to_group(canvas_group_name);
252 
253 			_update_process_mode();
254 			_update_scroll();
255 			first = true;
256 
257 		} break;
258 		case NOTIFICATION_EXIT_TREE: {
259 
260 			if (is_current()) {
261 				if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) {
262 					viewport->set_canvas_transform(Transform2D());
263 				}
264 			}
265 			remove_from_group(group_name);
266 			remove_from_group(canvas_group_name);
267 			viewport = NULL;
268 
269 		} break;
270 		case NOTIFICATION_DRAW: {
271 
272 			if (!is_inside_tree() || !Engine::get_singleton()->is_editor_hint())
273 				break;
274 
275 			if (screen_drawing_enabled) {
276 				Color area_axis_color(0.5, 0.42, 0.87, 0.63);
277 				float area_axis_width = 1;
278 				if (is_current()) {
279 					area_axis_width = 3;
280 					area_axis_color.a = 0.83;
281 				}
282 
283 				Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
284 				Size2 screen_size = get_viewport_rect().size;
285 
286 				Vector2 screen_endpoints[4] = {
287 					inv_camera_transform.xform(Vector2(0, 0)),
288 					inv_camera_transform.xform(Vector2(screen_size.width, 0)),
289 					inv_camera_transform.xform(Vector2(screen_size.width, screen_size.height)),
290 					inv_camera_transform.xform(Vector2(0, screen_size.height))
291 				};
292 
293 				Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space
294 
295 				for (int i = 0; i < 4; i++) {
296 					draw_line(inv_transform.xform(screen_endpoints[i]), inv_transform.xform(screen_endpoints[(i + 1) % 4]), area_axis_color, area_axis_width);
297 				}
298 			}
299 
300 			if (limit_drawing_enabled) {
301 				Color limit_drawing_color(1, 1, 0, 0.63);
302 				float limit_drawing_width = 1;
303 				if (is_current()) {
304 					limit_drawing_color.a = 0.83;
305 					limit_drawing_width = 3;
306 				}
307 
308 				Vector2 camera_origin = get_global_transform().get_origin();
309 				Vector2 camera_scale = get_global_transform().get_scale().abs();
310 				Vector2 limit_points[4] = {
311 					(Vector2(limit[MARGIN_LEFT], limit[MARGIN_TOP]) - camera_origin) / camera_scale,
312 					(Vector2(limit[MARGIN_RIGHT], limit[MARGIN_TOP]) - camera_origin) / camera_scale,
313 					(Vector2(limit[MARGIN_RIGHT], limit[MARGIN_BOTTOM]) - camera_origin) / camera_scale,
314 					(Vector2(limit[MARGIN_LEFT], limit[MARGIN_BOTTOM]) - camera_origin) / camera_scale
315 				};
316 
317 				for (int i = 0; i < 4; i++) {
318 					draw_line(limit_points[i], limit_points[(i + 1) % 4], limit_drawing_color, limit_drawing_width);
319 				}
320 			}
321 
322 			if (margin_drawing_enabled) {
323 				Color margin_drawing_color(0, 1, 1, 0.63);
324 				float margin_drawing_width = 1;
325 				if (is_current()) {
326 					margin_drawing_width = 3;
327 					margin_drawing_color.a = 0.83;
328 				}
329 
330 				Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
331 				Size2 screen_size = get_viewport_rect().size;
332 
333 				Vector2 margin_endpoints[4] = {
334 					inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[MARGIN_LEFT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[MARGIN_TOP]))),
335 					inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[MARGIN_RIGHT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[MARGIN_TOP]))),
336 					inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[MARGIN_RIGHT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[MARGIN_BOTTOM]))),
337 					inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[MARGIN_LEFT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[MARGIN_BOTTOM])))
338 				};
339 
340 				Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space
341 
342 				for (int i = 0; i < 4; i++) {
343 					draw_line(inv_transform.xform(margin_endpoints[i]), inv_transform.xform(margin_endpoints[(i + 1) % 4]), margin_drawing_color, margin_drawing_width);
344 				}
345 			}
346 
347 		} break;
348 	}
349 }
350 
set_offset(const Vector2 & p_offset)351 void Camera2D::set_offset(const Vector2 &p_offset) {
352 
353 	offset = p_offset;
354 	Point2 old_smoothed_camera_pos = smoothed_camera_pos;
355 	_update_scroll();
356 	smoothed_camera_pos = old_smoothed_camera_pos;
357 }
358 
get_offset() const359 Vector2 Camera2D::get_offset() const {
360 
361 	return offset;
362 }
363 
set_anchor_mode(AnchorMode p_anchor_mode)364 void Camera2D::set_anchor_mode(AnchorMode p_anchor_mode) {
365 
366 	anchor_mode = p_anchor_mode;
367 	_update_scroll();
368 }
369 
get_anchor_mode() const370 Camera2D::AnchorMode Camera2D::get_anchor_mode() const {
371 
372 	return anchor_mode;
373 }
374 
set_rotating(bool p_rotating)375 void Camera2D::set_rotating(bool p_rotating) {
376 
377 	rotating = p_rotating;
378 	Point2 old_smoothed_camera_pos = smoothed_camera_pos;
379 	_update_scroll();
380 	smoothed_camera_pos = old_smoothed_camera_pos;
381 }
382 
is_rotating() const383 bool Camera2D::is_rotating() const {
384 
385 	return rotating;
386 }
387 
set_process_mode(Camera2DProcessMode p_mode)388 void Camera2D::set_process_mode(Camera2DProcessMode p_mode) {
389 
390 	if (process_mode == p_mode)
391 		return;
392 
393 	process_mode = p_mode;
394 	_update_process_mode();
395 }
396 
get_process_mode() const397 Camera2D::Camera2DProcessMode Camera2D::get_process_mode() const {
398 
399 	return process_mode;
400 }
401 
_make_current(Object * p_which)402 void Camera2D::_make_current(Object *p_which) {
403 
404 	if (p_which == this) {
405 
406 		current = true;
407 	} else {
408 		current = false;
409 	}
410 }
411 
_set_current(bool p_current)412 void Camera2D::_set_current(bool p_current) {
413 
414 	if (p_current)
415 		make_current();
416 
417 	current = p_current;
418 	update();
419 }
420 
is_current() const421 bool Camera2D::is_current() const {
422 
423 	return current;
424 }
425 
make_current()426 void Camera2D::make_current() {
427 
428 	if (!is_inside_tree()) {
429 		current = true;
430 	} else {
431 		get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", this);
432 	}
433 	_update_scroll();
434 }
435 
clear_current()436 void Camera2D::clear_current() {
437 
438 	current = false;
439 	if (is_inside_tree()) {
440 		get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", (Object *)(NULL));
441 	}
442 }
443 
set_limit(Margin p_margin,int p_limit)444 void Camera2D::set_limit(Margin p_margin, int p_limit) {
445 
446 	ERR_FAIL_INDEX((int)p_margin, 4);
447 	limit[p_margin] = p_limit;
448 	update();
449 }
450 
get_limit(Margin p_margin) const451 int Camera2D::get_limit(Margin p_margin) const {
452 
453 	ERR_FAIL_INDEX_V((int)p_margin, 4, 0);
454 	return limit[p_margin];
455 }
456 
set_limit_smoothing_enabled(bool enable)457 void Camera2D::set_limit_smoothing_enabled(bool enable) {
458 
459 	limit_smoothing_enabled = enable;
460 	_update_scroll();
461 }
462 
is_limit_smoothing_enabled() const463 bool Camera2D::is_limit_smoothing_enabled() const {
464 
465 	return limit_smoothing_enabled;
466 }
467 
set_drag_margin(Margin p_margin,float p_drag_margin)468 void Camera2D::set_drag_margin(Margin p_margin, float p_drag_margin) {
469 
470 	ERR_FAIL_INDEX((int)p_margin, 4);
471 	drag_margin[p_margin] = p_drag_margin;
472 	update();
473 }
474 
get_drag_margin(Margin p_margin) const475 float Camera2D::get_drag_margin(Margin p_margin) const {
476 
477 	ERR_FAIL_INDEX_V((int)p_margin, 4, 0);
478 	return drag_margin[p_margin];
479 }
480 
get_camera_position() const481 Vector2 Camera2D::get_camera_position() const {
482 
483 	return camera_pos;
484 }
485 
force_update_scroll()486 void Camera2D::force_update_scroll() {
487 
488 	_update_scroll();
489 }
490 
reset_smoothing()491 void Camera2D::reset_smoothing() {
492 
493 	smoothed_camera_pos = camera_pos;
494 	_update_scroll();
495 }
496 
align()497 void Camera2D::align() {
498 
499 	ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
500 
501 	Size2 screen_size = viewport->get_visible_rect().size;
502 
503 	Point2 current_camera_pos = get_global_transform().get_origin();
504 	if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
505 		if (h_ofs < 0) {
506 			camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs;
507 		} else {
508 			camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs;
509 		}
510 		if (v_ofs < 0) {
511 			camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs;
512 		} else {
513 			camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs;
514 		}
515 	} else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) {
516 
517 		camera_pos = current_camera_pos;
518 	}
519 
520 	_update_scroll();
521 }
522 
set_follow_smoothing(float p_speed)523 void Camera2D::set_follow_smoothing(float p_speed) {
524 
525 	smoothing = p_speed;
526 	if (smoothing > 0 && !(is_inside_tree() && Engine::get_singleton()->is_editor_hint()))
527 		set_process_internal(true);
528 	else
529 		set_process_internal(false);
530 }
531 
get_follow_smoothing() const532 float Camera2D::get_follow_smoothing() const {
533 
534 	return smoothing;
535 }
536 
get_camera_screen_center() const537 Point2 Camera2D::get_camera_screen_center() const {
538 
539 	return camera_screen_center;
540 }
541 
set_h_drag_enabled(bool p_enabled)542 void Camera2D::set_h_drag_enabled(bool p_enabled) {
543 
544 	h_drag_enabled = p_enabled;
545 }
546 
is_h_drag_enabled() const547 bool Camera2D::is_h_drag_enabled() const {
548 
549 	return h_drag_enabled;
550 }
551 
set_v_drag_enabled(bool p_enabled)552 void Camera2D::set_v_drag_enabled(bool p_enabled) {
553 
554 	v_drag_enabled = p_enabled;
555 }
556 
is_v_drag_enabled() const557 bool Camera2D::is_v_drag_enabled() const {
558 
559 	return v_drag_enabled;
560 }
561 
set_v_offset(float p_offset)562 void Camera2D::set_v_offset(float p_offset) {
563 
564 	v_ofs = p_offset;
565 	v_offset_changed = true;
566 	Point2 old_smoothed_camera_pos = smoothed_camera_pos;
567 	_update_scroll();
568 	smoothed_camera_pos = old_smoothed_camera_pos;
569 }
570 
get_v_offset() const571 float Camera2D::get_v_offset() const {
572 
573 	return v_ofs;
574 }
575 
set_h_offset(float p_offset)576 void Camera2D::set_h_offset(float p_offset) {
577 
578 	h_ofs = p_offset;
579 	h_offset_changed = true;
580 	Point2 old_smoothed_camera_pos = smoothed_camera_pos;
581 	_update_scroll();
582 	smoothed_camera_pos = old_smoothed_camera_pos;
583 }
get_h_offset() const584 float Camera2D::get_h_offset() const {
585 
586 	return h_ofs;
587 }
588 
_set_old_smoothing(float p_enable)589 void Camera2D::_set_old_smoothing(float p_enable) {
590 	//compatibility
591 	if (p_enable > 0) {
592 		smoothing_enabled = true;
593 		set_follow_smoothing(p_enable);
594 	}
595 }
596 
set_enable_follow_smoothing(bool p_enabled)597 void Camera2D::set_enable_follow_smoothing(bool p_enabled) {
598 
599 	smoothing_enabled = p_enabled;
600 }
601 
is_follow_smoothing_enabled() const602 bool Camera2D::is_follow_smoothing_enabled() const {
603 
604 	return smoothing_enabled;
605 }
606 
set_custom_viewport(Node * p_viewport)607 void Camera2D::set_custom_viewport(Node *p_viewport) {
608 	ERR_FAIL_NULL(p_viewport);
609 	if (is_inside_tree()) {
610 		remove_from_group(group_name);
611 		remove_from_group(canvas_group_name);
612 	}
613 
614 	custom_viewport = Object::cast_to<Viewport>(p_viewport);
615 
616 	if (custom_viewport) {
617 		custom_viewport_id = custom_viewport->get_instance_id();
618 	} else {
619 		custom_viewport_id = 0;
620 	}
621 
622 	if (is_inside_tree()) {
623 
624 		if (custom_viewport)
625 			viewport = custom_viewport;
626 		else
627 			viewport = get_viewport();
628 
629 		RID vp = viewport->get_viewport_rid();
630 		group_name = "__cameras_" + itos(vp.get_id());
631 		canvas_group_name = "__cameras_c" + itos(canvas.get_id());
632 		add_to_group(group_name);
633 		add_to_group(canvas_group_name);
634 	}
635 }
636 
get_custom_viewport() const637 Node *Camera2D::get_custom_viewport() const {
638 
639 	return custom_viewport;
640 }
641 
set_screen_drawing_enabled(bool enable)642 void Camera2D::set_screen_drawing_enabled(bool enable) {
643 	screen_drawing_enabled = enable;
644 	update();
645 }
646 
is_screen_drawing_enabled() const647 bool Camera2D::is_screen_drawing_enabled() const {
648 	return screen_drawing_enabled;
649 }
650 
set_limit_drawing_enabled(bool enable)651 void Camera2D::set_limit_drawing_enabled(bool enable) {
652 	limit_drawing_enabled = enable;
653 	update();
654 }
655 
is_limit_drawing_enabled() const656 bool Camera2D::is_limit_drawing_enabled() const {
657 	return limit_drawing_enabled;
658 }
659 
set_margin_drawing_enabled(bool enable)660 void Camera2D::set_margin_drawing_enabled(bool enable) {
661 	margin_drawing_enabled = enable;
662 	update();
663 }
664 
is_margin_drawing_enabled() const665 bool Camera2D::is_margin_drawing_enabled() const {
666 	return margin_drawing_enabled;
667 }
668 
_bind_methods()669 void Camera2D::_bind_methods() {
670 
671 	ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Camera2D::set_offset);
672 	ClassDB::bind_method(D_METHOD("get_offset"), &Camera2D::get_offset);
673 
674 	ClassDB::bind_method(D_METHOD("set_anchor_mode", "anchor_mode"), &Camera2D::set_anchor_mode);
675 	ClassDB::bind_method(D_METHOD("get_anchor_mode"), &Camera2D::get_anchor_mode);
676 
677 	ClassDB::bind_method(D_METHOD("set_rotating", "rotating"), &Camera2D::set_rotating);
678 	ClassDB::bind_method(D_METHOD("is_rotating"), &Camera2D::is_rotating);
679 
680 	ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current);
681 	ClassDB::bind_method(D_METHOD("clear_current"), &Camera2D::clear_current);
682 	ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current);
683 
684 	ClassDB::bind_method(D_METHOD("_update_scroll"), &Camera2D::_update_scroll);
685 
686 	ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Camera2D::set_process_mode);
687 	ClassDB::bind_method(D_METHOD("get_process_mode"), &Camera2D::get_process_mode);
688 
689 	ClassDB::bind_method(D_METHOD("_set_current", "current"), &Camera2D::_set_current);
690 	ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current);
691 
692 	ClassDB::bind_method(D_METHOD("set_limit", "margin", "limit"), &Camera2D::set_limit);
693 	ClassDB::bind_method(D_METHOD("get_limit", "margin"), &Camera2D::get_limit);
694 
695 	ClassDB::bind_method(D_METHOD("set_limit_smoothing_enabled", "limit_smoothing_enabled"), &Camera2D::set_limit_smoothing_enabled);
696 	ClassDB::bind_method(D_METHOD("is_limit_smoothing_enabled"), &Camera2D::is_limit_smoothing_enabled);
697 
698 	ClassDB::bind_method(D_METHOD("set_v_drag_enabled", "enabled"), &Camera2D::set_v_drag_enabled);
699 	ClassDB::bind_method(D_METHOD("is_v_drag_enabled"), &Camera2D::is_v_drag_enabled);
700 
701 	ClassDB::bind_method(D_METHOD("set_h_drag_enabled", "enabled"), &Camera2D::set_h_drag_enabled);
702 	ClassDB::bind_method(D_METHOD("is_h_drag_enabled"), &Camera2D::is_h_drag_enabled);
703 
704 	ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera2D::set_v_offset);
705 	ClassDB::bind_method(D_METHOD("get_v_offset"), &Camera2D::get_v_offset);
706 
707 	ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera2D::set_h_offset);
708 	ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera2D::get_h_offset);
709 
710 	ClassDB::bind_method(D_METHOD("set_drag_margin", "margin", "drag_margin"), &Camera2D::set_drag_margin);
711 	ClassDB::bind_method(D_METHOD("get_drag_margin", "margin"), &Camera2D::get_drag_margin);
712 
713 	ClassDB::bind_method(D_METHOD("get_camera_position"), &Camera2D::get_camera_position);
714 	ClassDB::bind_method(D_METHOD("get_camera_screen_center"), &Camera2D::get_camera_screen_center);
715 
716 	ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &Camera2D::set_zoom);
717 	ClassDB::bind_method(D_METHOD("get_zoom"), &Camera2D::get_zoom);
718 
719 	ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &Camera2D::set_custom_viewport);
720 	ClassDB::bind_method(D_METHOD("get_custom_viewport"), &Camera2D::get_custom_viewport);
721 
722 	ClassDB::bind_method(D_METHOD("set_follow_smoothing", "follow_smoothing"), &Camera2D::set_follow_smoothing);
723 	ClassDB::bind_method(D_METHOD("get_follow_smoothing"), &Camera2D::get_follow_smoothing);
724 
725 	ClassDB::bind_method(D_METHOD("set_enable_follow_smoothing", "follow_smoothing"), &Camera2D::set_enable_follow_smoothing);
726 	ClassDB::bind_method(D_METHOD("is_follow_smoothing_enabled"), &Camera2D::is_follow_smoothing_enabled);
727 
728 	ClassDB::bind_method(D_METHOD("force_update_scroll"), &Camera2D::force_update_scroll);
729 	ClassDB::bind_method(D_METHOD("reset_smoothing"), &Camera2D::reset_smoothing);
730 	ClassDB::bind_method(D_METHOD("align"), &Camera2D::align);
731 
732 	ClassDB::bind_method(D_METHOD("_set_old_smoothing", "follow_smoothing"), &Camera2D::_set_old_smoothing);
733 
734 	ClassDB::bind_method(D_METHOD("set_screen_drawing_enabled", "screen_drawing_enabled"), &Camera2D::set_screen_drawing_enabled);
735 	ClassDB::bind_method(D_METHOD("is_screen_drawing_enabled"), &Camera2D::is_screen_drawing_enabled);
736 
737 	ClassDB::bind_method(D_METHOD("set_limit_drawing_enabled", "limit_drawing_enabled"), &Camera2D::set_limit_drawing_enabled);
738 	ClassDB::bind_method(D_METHOD("is_limit_drawing_enabled"), &Camera2D::is_limit_drawing_enabled);
739 
740 	ClassDB::bind_method(D_METHOD("set_margin_drawing_enabled", "margin_drawing_enabled"), &Camera2D::set_margin_drawing_enabled);
741 	ClassDB::bind_method(D_METHOD("is_margin_drawing_enabled"), &Camera2D::is_margin_drawing_enabled);
742 
743 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
744 	ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode");
745 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotating"), "set_rotating", "is_rotating");
746 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "_set_current", "is_current");
747 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom"), "set_zoom", "get_zoom");
748 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", 0), "set_custom_viewport", "get_custom_viewport");
749 	ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode");
750 
751 	ADD_GROUP("Limit", "limit_");
752 	ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_left"), "set_limit", "get_limit", MARGIN_LEFT);
753 	ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_top"), "set_limit", "get_limit", MARGIN_TOP);
754 	ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_right"), "set_limit", "get_limit", MARGIN_RIGHT);
755 	ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_bottom"), "set_limit", "get_limit", MARGIN_BOTTOM);
756 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_smoothed"), "set_limit_smoothing_enabled", "is_limit_smoothing_enabled");
757 
758 	ADD_GROUP("Draw Margin", "draw_margin_");
759 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_margin_h_enabled"), "set_h_drag_enabled", "is_h_drag_enabled");
760 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_margin_v_enabled"), "set_v_drag_enabled", "is_v_drag_enabled");
761 
762 	ADD_GROUP("Smoothing", "smoothing_");
763 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smoothing_enabled"), "set_enable_follow_smoothing", "is_follow_smoothing_enabled");
764 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "smoothing_speed"), "set_follow_smoothing", "get_follow_smoothing");
765 
766 	ADD_GROUP("Offset", "offset_");
767 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_h", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_h_offset", "get_h_offset");
768 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_v", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_v_offset", "get_v_offset");
769 
770 	ADD_GROUP("Drag Margin", "drag_margin_");
771 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_LEFT);
772 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_top", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_TOP);
773 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_right", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_RIGHT);
774 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_BOTTOM);
775 
776 	ADD_GROUP("Editor", "editor_");
777 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_screen"), "set_screen_drawing_enabled", "is_screen_drawing_enabled");
778 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_limits"), "set_limit_drawing_enabled", "is_limit_drawing_enabled");
779 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_drag_margin"), "set_margin_drawing_enabled", "is_margin_drawing_enabled");
780 
781 	BIND_ENUM_CONSTANT(ANCHOR_MODE_FIXED_TOP_LEFT);
782 	BIND_ENUM_CONSTANT(ANCHOR_MODE_DRAG_CENTER);
783 	BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_PHYSICS);
784 	BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_IDLE);
785 }
786 
Camera2D()787 Camera2D::Camera2D() {
788 
789 	anchor_mode = ANCHOR_MODE_DRAG_CENTER;
790 	rotating = false;
791 	current = false;
792 	limit[MARGIN_LEFT] = -10000000;
793 	limit[MARGIN_TOP] = -10000000;
794 	limit[MARGIN_RIGHT] = 10000000;
795 	limit[MARGIN_BOTTOM] = 10000000;
796 
797 	drag_margin[MARGIN_LEFT] = 0.2;
798 	drag_margin[MARGIN_TOP] = 0.2;
799 	drag_margin[MARGIN_RIGHT] = 0.2;
800 	drag_margin[MARGIN_BOTTOM] = 0.2;
801 	camera_pos = Vector2();
802 	first = true;
803 	smoothing_enabled = false;
804 	limit_smoothing_enabled = false;
805 	custom_viewport = NULL;
806 	custom_viewport_id = 0;
807 	process_mode = CAMERA2D_PROCESS_IDLE;
808 
809 	smoothing = 5.0;
810 	zoom = Vector2(1, 1);
811 
812 	screen_drawing_enabled = true;
813 	limit_drawing_enabled = false;
814 	margin_drawing_enabled = false;
815 
816 	h_drag_enabled = false;
817 	v_drag_enabled = false;
818 	h_ofs = 0;
819 	v_ofs = 0;
820 	h_offset_changed = false;
821 	v_offset_changed = false;
822 
823 	set_notify_transform(true);
824 }
825