1/**************************************************************************/
2/* camera_3d.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
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_3d.h"
32
33#include "collision_object_3d.h"
34#include "core/math/projection.h"
35#include "scene/main/viewport.h"
36
37void Camera3D::_update_audio_listener_state() {
38}
39
40void Camera3D::_request_camera_update() {
41 _update_camera();
42}
43
44void Camera3D::_update_camera_mode() {
45 force_change = true;
46 switch (mode) {
47 case PROJECTION_PERSPECTIVE: {
48 set_perspective(fov, near, far);
49
50 } break;
51 case PROJECTION_ORTHOGONAL: {
52 set_orthogonal(size, near, far);
53 } break;
54 case PROJECTION_FRUSTUM: {
55 set_frustum(size, frustum_offset, near, far);
56 } break;
57 }
58}
59
60void Camera3D::_validate_property(PropertyInfo &p_property) const {
61 if (p_property.name == "fov") {
62 if (mode != PROJECTION_PERSPECTIVE) {
63 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
64 }
65 } else if (p_property.name == "size") {
66 if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) {
67 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
68 }
69 } else if (p_property.name == "frustum_offset") {
70 if (mode != PROJECTION_FRUSTUM) {
71 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
72 }
73 }
74
75 if (attributes.is_valid()) {
76 const CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr());
77 if (physical_attributes) {
78 if (p_property.name == "near" || p_property.name == "far" || p_property.name == "fov" || p_property.name == "keep_aspect") {
79 p_property.usage = PROPERTY_USAGE_READ_ONLY | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR;
80 }
81 }
82 }
83
84 Node3D::_validate_property(p_property);
85}
86
87void Camera3D::_update_camera() {
88 if (!is_inside_tree()) {
89 return;
90 }
91
92 RenderingServer::get_singleton()->camera_set_transform(camera, get_camera_transform());
93
94 if (get_tree()->is_node_being_edited(this) || !is_current()) {
95 return;
96 }
97
98 get_viewport()->_camera_3d_transform_changed_notify();
99}
100
101void Camera3D::_notification(int p_what) {
102 switch (p_what) {
103 case NOTIFICATION_ENTER_WORLD: {
104 // Needs to track the Viewport because it's needed on NOTIFICATION_EXIT_WORLD
105 // and Spatial will handle it first, including clearing its reference to the Viewport,
106 // therefore making it impossible to subclasses to access it
107 viewport = get_viewport();
108 ERR_FAIL_NULL(viewport);
109
110 bool first_camera = viewport->_camera_3d_add(this);
111 if (current || first_camera) {
112 viewport->_camera_3d_set(this);
113 }
114
115#ifdef TOOLS_ENABLED
116 if (Engine::get_singleton()->is_editor_hint()) {
117 viewport->connect(SNAME("size_changed"), callable_mp((Node3D *)this, &Camera3D::update_gizmos));
118 }
119#endif
120 } break;
121
122 case NOTIFICATION_TRANSFORM_CHANGED: {
123 _request_camera_update();
124 if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
125 velocity_tracker->update_position(get_global_transform().origin);
126 }
127 } break;
128
129 case NOTIFICATION_EXIT_WORLD: {
130 if (!get_tree()->is_node_being_edited(this)) {
131 if (is_current()) {
132 clear_current();
133 current = true; //keep it true
134
135 } else {
136 current = false;
137 }
138 }
139
140 if (viewport) {
141#ifdef TOOLS_ENABLED
142 if (Engine::get_singleton()->is_editor_hint()) {
143 viewport->disconnect(SNAME("size_changed"), callable_mp((Node3D *)this, &Camera3D::update_gizmos));
144 }
145#endif
146 viewport->_camera_3d_remove(this);
147 viewport = nullptr;
148 }
149 } break;
150
151 case NOTIFICATION_BECAME_CURRENT: {
152 if (viewport) {
153 viewport->find_world_3d()->_register_camera(this);
154 }
155 } break;
156
157 case NOTIFICATION_LOST_CURRENT: {
158 if (viewport) {
159 viewport->find_world_3d()->_remove_camera(this);
160 }
161 } break;
162 }
163}
164
165Transform3D Camera3D::get_camera_transform() const {
166 Transform3D tr = get_global_transform().orthonormalized();
167 tr.origin += tr.basis.get_column(1) * v_offset;
168 tr.origin += tr.basis.get_column(0) * h_offset;
169 return tr;
170}
171
172Projection Camera3D::_get_camera_projection(real_t p_near) const {
173 Size2 viewport_size = get_viewport()->get_visible_rect().size;
174 Projection cm;
175
176 switch (mode) {
177 case PROJECTION_PERSPECTIVE: {
178 cm.set_perspective(fov, viewport_size.aspect(), p_near, far, keep_aspect == KEEP_WIDTH);
179 } break;
180 case PROJECTION_ORTHOGONAL: {
181 cm.set_orthogonal(size, viewport_size.aspect(), p_near, far, keep_aspect == KEEP_WIDTH);
182 } break;
183 case PROJECTION_FRUSTUM: {
184 cm.set_frustum(size, viewport_size.aspect(), frustum_offset, p_near, far);
185 } break;
186 }
187
188 return cm;
189}
190
191Projection Camera3D::get_camera_projection() const {
192 ERR_FAIL_COND_V_MSG(!is_inside_tree(), Projection(), "Camera is not inside the scene tree.");
193 return _get_camera_projection(near);
194}
195
196void Camera3D::set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) {
197 if (!force_change && fov == p_fovy_degrees && p_z_near == near && p_z_far == far && mode == PROJECTION_PERSPECTIVE) {
198 return;
199 }
200
201 fov = p_fovy_degrees;
202 near = p_z_near;
203 far = p_z_far;
204 mode = PROJECTION_PERSPECTIVE;
205
206 RenderingServer::get_singleton()->camera_set_perspective(camera, fov, near, far);
207 update_gizmos();
208 force_change = false;
209}
210
211void Camera3D::set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) {
212 if (!force_change && size == p_size && p_z_near == near && p_z_far == far && mode == PROJECTION_ORTHOGONAL) {
213 return;
214 }
215
216 size = p_size;
217
218 near = p_z_near;
219 far = p_z_far;
220 mode = PROJECTION_ORTHOGONAL;
221 force_change = false;
222
223 RenderingServer::get_singleton()->camera_set_orthogonal(camera, size, near, far);
224 update_gizmos();
225}
226
227void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far) {
228 if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM) {
229 return;
230 }
231
232 size = p_size;
233 frustum_offset = p_offset;
234
235 near = p_z_near;
236 far = p_z_far;
237 mode = PROJECTION_FRUSTUM;
238 force_change = false;
239
240 RenderingServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far);
241 update_gizmos();
242}
243
244void Camera3D::set_projection(ProjectionType p_mode) {
245 if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
246 mode = p_mode;
247 _update_camera_mode();
248 notify_property_list_changed();
249 }
250}
251
252RID Camera3D::get_camera() const {
253 return camera;
254};
255
256void Camera3D::make_current() {
257 current = true;
258
259 if (!is_inside_tree()) {
260 return;
261 }
262
263 get_viewport()->_camera_3d_set(this);
264}
265
266void Camera3D::clear_current(bool p_enable_next) {
267 current = false;
268 if (!is_inside_tree()) {
269 return;
270 }
271
272 if (get_viewport()->get_camera_3d() == this) {
273 get_viewport()->_camera_3d_set(nullptr);
274
275 if (p_enable_next) {
276 get_viewport()->_camera_3d_make_next_current(this);
277 }
278 }
279}
280
281void Camera3D::set_current(bool p_enabled) {
282 if (p_enabled) {
283 make_current();
284 } else {
285 clear_current();
286 }
287}
288
289bool Camera3D::is_current() const {
290 if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) {
291 return get_viewport()->get_camera_3d() == this;
292 } else {
293 return current;
294 }
295}
296
297Vector3 Camera3D::project_ray_normal(const Point2 &p_pos) const {
298 Vector3 ray = project_local_ray_normal(p_pos);
299 return get_camera_transform().basis.xform(ray).normalized();
300};
301
302Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const {
303 ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
304
305 Size2 viewport_size = get_viewport()->get_camera_rect_size();
306 Vector2 cpos = get_viewport()->get_camera_coords(p_pos);
307 Vector3 ray;
308
309 if (mode == PROJECTION_ORTHOGONAL) {
310 ray = Vector3(0, 0, -1);
311 } else {
312 Projection cm = _get_camera_projection(near);
313 Vector2 screen_he = cm.get_viewport_half_extents();
314 ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -near).normalized();
315 }
316
317 return ray;
318};
319
320Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const {
321 ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
322
323 Size2 viewport_size = get_viewport()->get_camera_rect_size();
324 Vector2 cpos = get_viewport()->get_camera_coords(p_pos);
325 ERR_FAIL_COND_V(viewport_size.y == 0, Vector3());
326
327 if (mode == PROJECTION_ORTHOGONAL) {
328 Vector2 pos = cpos / viewport_size;
329 real_t vsize, hsize;
330 if (keep_aspect == KEEP_WIDTH) {
331 vsize = size / viewport_size.aspect();
332 hsize = size;
333 } else {
334 hsize = size * viewport_size.aspect();
335 vsize = size;
336 }
337
338 Vector3 ray;
339 ray.x = pos.x * (hsize)-hsize / 2;
340 ray.y = (1.0 - pos.y) * (vsize)-vsize / 2;
341 ray.z = -near;
342 ray = get_camera_transform().xform(ray);
343 return ray;
344 } else {
345 return get_camera_transform().origin;
346 };
347};
348
349bool Camera3D::is_position_behind(const Vector3 &p_pos) const {
350 Transform3D t = get_global_transform();
351 Vector3 eyedir = -t.basis.get_column(2).normalized();
352 return eyedir.dot(p_pos - t.origin) < near;
353}
354
355Vector<Vector3> Camera3D::get_near_plane_points() const {
356 ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector<Vector3>(), "Camera is not inside scene.");
357
358 Projection cm = _get_camera_projection(near);
359
360 Vector3 endpoints[8];
361 cm.get_endpoints(Transform3D(), endpoints);
362
363 Vector<Vector3> points = {
364 Vector3(),
365 endpoints[4],
366 endpoints[5],
367 endpoints[6],
368 endpoints[7]
369 };
370 return points;
371}
372
373Point2 Camera3D::unproject_position(const Vector3 &p_pos) const {
374 ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector2(), "Camera is not inside scene.");
375
376 Size2 viewport_size = get_viewport()->get_visible_rect().size;
377
378 Projection cm = _get_camera_projection(near);
379
380 Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
381
382 p = cm.xform4(p);
383 p.normal /= p.d;
384
385 Point2 res;
386 res.x = (p.normal.x * 0.5 + 0.5) * viewport_size.x;
387 res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y;
388
389 return res;
390}
391
392Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) const {
393 ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene.");
394
395 if (p_z_depth == 0 && mode != PROJECTION_ORTHOGONAL) {
396 return get_global_transform().origin;
397 }
398 Size2 viewport_size = get_viewport()->get_visible_rect().size;
399
400 Projection cm = _get_camera_projection(p_z_depth);
401
402 Vector2 vp_he = cm.get_viewport_half_extents();
403
404 Vector2 point;
405 point.x = (p_point.x / viewport_size.x) * 2.0 - 1.0;
406 point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0;
407 point *= vp_he;
408
409 Vector3 p(point.x, point.y, -p_z_depth);
410
411 return get_camera_transform().xform(p);
412}
413
414void Camera3D::set_environment(const Ref<Environment> &p_environment) {
415 environment = p_environment;
416 if (environment.is_valid()) {
417 RS::get_singleton()->camera_set_environment(camera, environment->get_rid());
418 } else {
419 RS::get_singleton()->camera_set_environment(camera, RID());
420 }
421 _update_camera_mode();
422}
423
424Ref<Environment> Camera3D::get_environment() const {
425 return environment;
426}
427
428void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) {
429 if (attributes.is_valid()) {
430 CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr());
431 if (physical_attributes) {
432 attributes->disconnect_changed(callable_mp(this, &Camera3D::_attributes_changed));
433 }
434 }
435
436 attributes = p_attributes;
437
438 if (attributes.is_valid()) {
439 CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr());
440 if (physical_attributes) {
441 attributes->connect_changed(callable_mp(this, &Camera3D::_attributes_changed));
442 _attributes_changed();
443 }
444
445 RS::get_singleton()->camera_set_camera_attributes(camera, attributes->get_rid());
446 } else {
447 RS::get_singleton()->camera_set_camera_attributes(camera, RID());
448 }
449
450 notify_property_list_changed();
451}
452
453Ref<CameraAttributes> Camera3D::get_attributes() const {
454 return attributes;
455}
456
457void Camera3D::_attributes_changed() {
458 CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr());
459 ERR_FAIL_NULL(physical_attributes);
460
461 fov = physical_attributes->get_fov();
462 near = physical_attributes->get_near();
463 far = physical_attributes->get_far();
464 keep_aspect = KEEP_HEIGHT;
465 _update_camera_mode();
466}
467
468void Camera3D::set_keep_aspect_mode(KeepAspect p_aspect) {
469 keep_aspect = p_aspect;
470 RenderingServer::get_singleton()->camera_set_use_vertical_aspect(camera, p_aspect == KEEP_WIDTH);
471 _update_camera_mode();
472 notify_property_list_changed();
473}
474
475Camera3D::KeepAspect Camera3D::get_keep_aspect_mode() const {
476 return keep_aspect;
477}
478
479void Camera3D::set_doppler_tracking(DopplerTracking p_tracking) {
480 if (doppler_tracking == p_tracking) {
481 return;
482 }
483
484 doppler_tracking = p_tracking;
485 if (p_tracking != DOPPLER_TRACKING_DISABLED) {
486 velocity_tracker->set_track_physics_step(doppler_tracking == DOPPLER_TRACKING_PHYSICS_STEP);
487 if (is_inside_tree()) {
488 velocity_tracker->reset(get_global_transform().origin);
489 }
490 }
491 _update_camera_mode();
492}
493
494Camera3D::DopplerTracking Camera3D::get_doppler_tracking() const {
495 return doppler_tracking;
496}
497
498void Camera3D::_bind_methods() {
499 ClassDB::bind_method(D_METHOD("project_ray_normal", "screen_point"), &Camera3D::project_ray_normal);
500 ClassDB::bind_method(D_METHOD("project_local_ray_normal", "screen_point"), &Camera3D::project_local_ray_normal);
501 ClassDB::bind_method(D_METHOD("project_ray_origin", "screen_point"), &Camera3D::project_ray_origin);
502 ClassDB::bind_method(D_METHOD("unproject_position", "world_point"), &Camera3D::unproject_position);
503 ClassDB::bind_method(D_METHOD("is_position_behind", "world_point"), &Camera3D::is_position_behind);
504 ClassDB::bind_method(D_METHOD("project_position", "screen_point", "z_depth"), &Camera3D::project_position);
505 ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera3D::set_perspective);
506 ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera3D::set_orthogonal);
507 ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera3D::set_frustum);
508 ClassDB::bind_method(D_METHOD("make_current"), &Camera3D::make_current);
509 ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera3D::clear_current, DEFVAL(true));
510 ClassDB::bind_method(D_METHOD("set_current", "enabled"), &Camera3D::set_current);
511 ClassDB::bind_method(D_METHOD("is_current"), &Camera3D::is_current);
512 ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera3D::get_camera_transform);
513 ClassDB::bind_method(D_METHOD("get_camera_projection"), &Camera3D::get_camera_projection);
514 ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov);
515 ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera3D::get_frustum_offset);
516 ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size);
517 ClassDB::bind_method(D_METHOD("get_far"), &Camera3D::get_far);
518 ClassDB::bind_method(D_METHOD("get_near"), &Camera3D::get_near);
519 ClassDB::bind_method(D_METHOD("set_fov", "fov"), &Camera3D::set_fov);
520 ClassDB::bind_method(D_METHOD("set_frustum_offset", "offset"), &Camera3D::set_frustum_offset);
521 ClassDB::bind_method(D_METHOD("set_size", "size"), &Camera3D::set_size);
522 ClassDB::bind_method(D_METHOD("set_far", "far"), &Camera3D::set_far);
523 ClassDB::bind_method(D_METHOD("set_near", "near"), &Camera3D::set_near);
524 ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection);
525 ClassDB::bind_method(D_METHOD("set_projection", "mode"), &Camera3D::set_projection);
526 ClassDB::bind_method(D_METHOD("set_h_offset", "offset"), &Camera3D::set_h_offset);
527 ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera3D::get_h_offset);
528 ClassDB::bind_method(D_METHOD("set_v_offset", "offset"), &Camera3D::set_v_offset);
529 ClassDB::bind_method(D_METHOD("get_v_offset"), &Camera3D::get_v_offset);
530 ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &Camera3D::set_cull_mask);
531 ClassDB::bind_method(D_METHOD("get_cull_mask"), &Camera3D::get_cull_mask);
532 ClassDB::bind_method(D_METHOD("set_environment", "env"), &Camera3D::set_environment);
533 ClassDB::bind_method(D_METHOD("get_environment"), &Camera3D::get_environment);
534 ClassDB::bind_method(D_METHOD("set_attributes", "env"), &Camera3D::set_attributes);
535 ClassDB::bind_method(D_METHOD("get_attributes"), &Camera3D::get_attributes);
536 ClassDB::bind_method(D_METHOD("set_keep_aspect_mode", "mode"), &Camera3D::set_keep_aspect_mode);
537 ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera3D::get_keep_aspect_mode);
538 ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking);
539 ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera3D::get_doppler_tracking);
540 ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::_get_frustum);
541 ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum);
542 ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera);
543 ClassDB::bind_method(D_METHOD("get_pyramid_shape_rid"), &Camera3D::get_pyramid_shape_rid);
544
545 ClassDB::bind_method(D_METHOD("set_cull_mask_value", "layer_number", "value"), &Camera3D::set_cull_mask_value);
546 ClassDB::bind_method(D_METHOD("get_cull_mask_value", "layer_number"), &Camera3D::get_cull_mask_value);
547
548 //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current );
549
550 ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode");
551 ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
552 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment");
553 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_attributes", "get_attributes");
554 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset");
555 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_v_offset", "get_v_offset");
556 ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking");
557 ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection");
558 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
559 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov");
560 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001,or_greater,suffix:m"), "set_size", "get_size");
561 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_frustum_offset", "get_frustum_offset");
562 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near");
563 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far");
564
565 BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
566 BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
567 BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM);
568
569 BIND_ENUM_CONSTANT(KEEP_WIDTH);
570 BIND_ENUM_CONSTANT(KEEP_HEIGHT);
571
572 BIND_ENUM_CONSTANT(DOPPLER_TRACKING_DISABLED);
573 BIND_ENUM_CONSTANT(DOPPLER_TRACKING_IDLE_STEP);
574 BIND_ENUM_CONSTANT(DOPPLER_TRACKING_PHYSICS_STEP);
575}
576
577real_t Camera3D::get_fov() const {
578 return fov;
579}
580
581real_t Camera3D::get_size() const {
582 return size;
583}
584
585real_t Camera3D::get_near() const {
586 return near;
587}
588
589Vector2 Camera3D::get_frustum_offset() const {
590 return frustum_offset;
591}
592
593real_t Camera3D::get_far() const {
594 return far;
595}
596
597Camera3D::ProjectionType Camera3D::get_projection() const {
598 return mode;
599}
600
601void Camera3D::set_fov(real_t p_fov) {
602 ERR_FAIL_COND(p_fov < 1 || p_fov > 179);
603 fov = p_fov;
604 _update_camera_mode();
605}
606
607void Camera3D::set_size(real_t p_size) {
608 ERR_FAIL_COND(p_size <= CMP_EPSILON);
609 size = p_size;
610 _update_camera_mode();
611}
612
613void Camera3D::set_near(real_t p_near) {
614 near = p_near;
615 _update_camera_mode();
616}
617
618void Camera3D::set_frustum_offset(Vector2 p_offset) {
619 frustum_offset = p_offset;
620 _update_camera_mode();
621}
622
623void Camera3D::set_far(real_t p_far) {
624 far = p_far;
625 _update_camera_mode();
626}
627
628void Camera3D::set_cull_mask(uint32_t p_layers) {
629 layers = p_layers;
630 RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
631 _update_camera_mode();
632}
633
634uint32_t Camera3D::get_cull_mask() const {
635 return layers;
636}
637
638void Camera3D::set_cull_mask_value(int p_layer_number, bool p_value) {
639 ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive.");
640 ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive.");
641 uint32_t mask = get_cull_mask();
642 if (p_value) {
643 mask |= 1 << (p_layer_number - 1);
644 } else {
645 mask &= ~(1 << (p_layer_number - 1));
646 }
647 set_cull_mask(mask);
648}
649
650bool Camera3D::get_cull_mask_value(int p_layer_number) const {
651 ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive.");
652 ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive.");
653 return layers & (1 << (p_layer_number - 1));
654}
655
656Vector<Plane> Camera3D::get_frustum() const {
657 ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
658
659 Projection cm = _get_camera_projection(near);
660
661 return cm.get_projection_planes(get_camera_transform());
662}
663
664TypedArray<Plane> Camera3D::_get_frustum() const {
665 Variant ret = get_frustum();
666 return ret;
667}
668
669bool Camera3D::is_position_in_frustum(const Vector3 &p_position) const {
670 Vector<Plane> frustum = get_frustum();
671 for (int i = 0; i < frustum.size(); i++) {
672 if (frustum[i].is_point_over(p_position)) {
673 return false;
674 }
675 }
676 return true;
677}
678
679void Camera3D::set_v_offset(real_t p_offset) {
680 v_offset = p_offset;
681 _update_camera();
682}
683
684real_t Camera3D::get_v_offset() const {
685 return v_offset;
686}
687
688void Camera3D::set_h_offset(real_t p_offset) {
689 h_offset = p_offset;
690 _update_camera();
691}
692
693real_t Camera3D::get_h_offset() const {
694 return h_offset;
695}
696
697Vector3 Camera3D::get_doppler_tracked_velocity() const {
698 if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
699 return velocity_tracker->get_tracked_linear_velocity();
700 } else {
701 return Vector3();
702 }
703}
704
705RID Camera3D::get_pyramid_shape_rid() {
706 ERR_FAIL_COND_V_MSG(!is_inside_tree(), RID(), "Camera is not inside scene.");
707 if (pyramid_shape == RID()) {
708 pyramid_shape_points = get_near_plane_points();
709 pyramid_shape = PhysicsServer3D::get_singleton()->convex_polygon_shape_create();
710 PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, pyramid_shape_points);
711
712 } else { //check if points changed
713 Vector<Vector3> local_points = get_near_plane_points();
714
715 bool all_equal = true;
716
717 for (int i = 0; i < 5; i++) {
718 if (local_points[i] != pyramid_shape_points[i]) {
719 all_equal = false;
720 break;
721 }
722 }
723
724 if (!all_equal) {
725 PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, local_points);
726 pyramid_shape_points = local_points;
727 }
728 }
729
730 return pyramid_shape;
731}
732
733Camera3D::Camera3D() {
734 camera = RenderingServer::get_singleton()->camera_create();
735 set_perspective(75.0, 0.05, 4000.0);
736 RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
737 //active=false;
738 velocity_tracker.instantiate();
739 set_notify_transform(true);
740 set_disable_scale(true);
741}
742
743Camera3D::~Camera3D() {
744 ERR_FAIL_NULL(RenderingServer::get_singleton());
745 RenderingServer::get_singleton()->free(camera);
746 if (pyramid_shape.is_valid()) {
747 ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
748 PhysicsServer3D::get_singleton()->free(pyramid_shape);
749 }
750}
751