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 | |