| 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 | |
| 37 | void Camera3D::_update_audio_listener_state() { |
| 38 | } |
| 39 | |
| 40 | void Camera3D::_request_camera_update() { |
| 41 | _update_camera(); |
| 42 | } |
| 43 | |
| 44 | void 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 | |
| 60 | void 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 | |
| 87 | void 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 | |
| 101 | void 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 | |
| 165 | Transform3D 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 | |
| 172 | Projection 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 | |
| 191 | Projection 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 | |
| 196 | void 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 | |
| 211 | void 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 | |
| 227 | void 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 | |
| 244 | void 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 | |
| 252 | RID Camera3D::get_camera() const { |
| 253 | return camera; |
| 254 | }; |
| 255 | |
| 256 | void 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 | |
| 266 | void 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 | |
| 281 | void Camera3D::set_current(bool p_enabled) { |
| 282 | if (p_enabled) { |
| 283 | make_current(); |
| 284 | } else { |
| 285 | clear_current(); |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | bool 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 | |
| 297 | Vector3 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 | |
| 302 | Vector3 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 | |
| 320 | Vector3 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 | |
| 349 | bool 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 | |
| 355 | Vector<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 | |
| 373 | Point2 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 | |
| 392 | Vector3 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 | |
| 414 | void 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 | |
| 424 | Ref<Environment> Camera3D::get_environment() const { |
| 425 | return environment; |
| 426 | } |
| 427 | |
| 428 | void 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 | |
| 453 | Ref<CameraAttributes> Camera3D::get_attributes() const { |
| 454 | return attributes; |
| 455 | } |
| 456 | |
| 457 | void 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 | |
| 468 | void 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 | |
| 475 | Camera3D::KeepAspect Camera3D::get_keep_aspect_mode() const { |
| 476 | return keep_aspect; |
| 477 | } |
| 478 | |
| 479 | void 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 | |
| 494 | Camera3D::DopplerTracking Camera3D::get_doppler_tracking() const { |
| 495 | return doppler_tracking; |
| 496 | } |
| 497 | |
| 498 | void 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 | |
| 577 | real_t Camera3D::get_fov() const { |
| 578 | return fov; |
| 579 | } |
| 580 | |
| 581 | real_t Camera3D::get_size() const { |
| 582 | return size; |
| 583 | } |
| 584 | |
| 585 | real_t Camera3D::get_near() const { |
| 586 | return near; |
| 587 | } |
| 588 | |
| 589 | Vector2 Camera3D::get_frustum_offset() const { |
| 590 | return frustum_offset; |
| 591 | } |
| 592 | |
| 593 | real_t Camera3D::get_far() const { |
| 594 | return far; |
| 595 | } |
| 596 | |
| 597 | Camera3D::ProjectionType Camera3D::get_projection() const { |
| 598 | return mode; |
| 599 | } |
| 600 | |
| 601 | void 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 | |
| 607 | void 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 | |
| 613 | void Camera3D::set_near(real_t p_near) { |
| 614 | near = p_near; |
| 615 | _update_camera_mode(); |
| 616 | } |
| 617 | |
| 618 | void Camera3D::set_frustum_offset(Vector2 p_offset) { |
| 619 | frustum_offset = p_offset; |
| 620 | _update_camera_mode(); |
| 621 | } |
| 622 | |
| 623 | void Camera3D::set_far(real_t p_far) { |
| 624 | far = p_far; |
| 625 | _update_camera_mode(); |
| 626 | } |
| 627 | |
| 628 | void 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 | |
| 634 | uint32_t Camera3D::get_cull_mask() const { |
| 635 | return layers; |
| 636 | } |
| 637 | |
| 638 | void 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 | |
| 650 | bool 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 | |
| 656 | Vector<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 | |
| 664 | TypedArray<Plane> Camera3D::_get_frustum() const { |
| 665 | Variant ret = get_frustum(); |
| 666 | return ret; |
| 667 | } |
| 668 | |
| 669 | bool 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 | |
| 679 | void Camera3D::set_v_offset(real_t p_offset) { |
| 680 | v_offset = p_offset; |
| 681 | _update_camera(); |
| 682 | } |
| 683 | |
| 684 | real_t Camera3D::get_v_offset() const { |
| 685 | return v_offset; |
| 686 | } |
| 687 | |
| 688 | void Camera3D::set_h_offset(real_t p_offset) { |
| 689 | h_offset = p_offset; |
| 690 | _update_camera(); |
| 691 | } |
| 692 | |
| 693 | real_t Camera3D::get_h_offset() const { |
| 694 | return h_offset; |
| 695 | } |
| 696 | |
| 697 | Vector3 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 | |
| 705 | RID 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 | |
| 733 | Camera3D::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 | |
| 743 | Camera3D::~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 | |