diff options
-rw-r--r-- | editor/spatial_editor_gizmos.cpp | 44 | ||||
-rw-r--r-- | scene/3d/camera.cpp | 216 | ||||
-rw-r--r-- | scene/3d/camera.h | 52 | ||||
-rw-r--r-- | scene/register_scene_types.cpp | 1 |
4 files changed, 310 insertions, 3 deletions
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 889d34544a..20ddfa99a8 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -1337,6 +1337,48 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { p_gizmo->add_collision_segments(lines); p_gizmo->add_unscaled_billboard(icon, 0.05); p_gizmo->add_handles(handles, get_material("handles")); + + ClippedCamera *clipcam = Object::cast_to<ClippedCamera>(camera); + if (clipcam) { + Spatial *parent = Object::cast_to<Spatial>(camera->get_parent()); + if (!parent) { + return; + } + Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); + Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); + Vector3 cam_pos = camera->get_global_transform().origin; + Vector3 parent_pos = parent->get_global_transform().origin; + + Plane parent_plane(parent_pos, cam_normal); + Vector3 ray_from = parent_plane.project(cam_pos); + + lines.clear(); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + + if (parent_plane.distance_to(cam_pos) < 0) { + lines.push_back(ray_from); + lines.push_back(cam_pos); + } + + Transform local = camera->get_global_transform().affine_inverse(); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = local.xform(lines[i]); + } + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + } } ////// @@ -3613,7 +3655,7 @@ void NavigationMeshSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { p_gizmo->add_collision_segments(lines); } -////// + ////// #define BODY_A_RADIUS 0.25 #define BODY_B_RADIUS 0.27 diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 2176b45faf..8d820baa4d 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -31,9 +31,10 @@ #include "camera.h" #include "camera_matrix.h" +#include "collision_object.h" +#include "engine.h" #include "scene/resources/material.h" #include "scene/resources/surface_tool.h" - void Camera::_update_audio_listener_state() { } @@ -313,6 +314,32 @@ bool Camera::is_position_behind(const Vector3 &p_pos) const { return eyedir.dot(p_pos) < (eyedir.dot(t.origin) + near); } +Vector<Vector3> Camera::get_near_plane_points() const { + if (!is_inside_tree()) { + ERR_EXPLAIN("Camera is not inside scene."); + ERR_FAIL_COND_V(!is_inside_tree(), Vector<Vector3>()); + } + + Size2 viewport_size = get_viewport()->get_visible_rect().size; + + CameraMatrix cm; + + if (mode == PROJECTION_ORTHOGONAL) + cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); + else + cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH); + + Vector3 endpoints[8]; + cm.get_endpoints(Transform(), endpoints); + + Vector<Vector3> points; + points.push_back(Vector3()); + for (int i = 0; i < 4; i++) { + points.push_back(endpoints[i + 4]); + } + return points; +} + Point2 Camera::unproject_position(const Vector3 &p_pos) const { if (!is_inside_tree()) { @@ -484,7 +511,7 @@ void Camera::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_znear", "get_znear"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); @@ -638,3 +665,188 @@ Camera::~Camera() { VisualServer::get_singleton()->free(camera); } + +//////////////////////////////////////// + +void ClippedCamera::set_margin(float p_margin) { + margin = p_margin; +} +float ClippedCamera::get_margin() const { + return margin; +} +void ClippedCamera::set_process_mode(ProcessMode p_mode) { + + if (process_mode == p_mode) { + return; + } + set_process_internal(p_mode == CLIP_PROCESS_IDLE); + set_physics_process_internal(p_mode == CLIP_PROCESS_PHYSICS); +} +ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const { + return process_mode; +} + +Transform ClippedCamera::get_camera_transform() const { + + Transform t = Camera::get_camera_transform(); + t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset; + return t; +} + +void ClippedCamera::_notification(int p_what) { + if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { + + Spatial *parent = Object::cast_to<Spatial>(get_parent()); + if (!parent) { + return; + } + + PhysicsDirectSpaceState *dspace = get_world()->get_direct_space_state(); + ERR_FAIL_COND(!dspace); // most likely physics set to threads + + Vector3 cam_fw = -get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); + Vector3 cam_pos = get_global_transform().origin; + Vector3 parent_pos = parent->get_global_transform().origin; + + Plane parent_plane(parent_pos, cam_fw); + + if (parent_plane.is_point_over(cam_pos)) { + //cam is beyond parent plane + return; + } + + Vector3 ray_from = parent_plane.project(cam_pos); + + clip_offset = 0; //reset by defau;t + + { //check if points changed + Vector<Vector3> local_points = get_near_plane_points(); + + bool all_equal = true; + + for (int i = 0; i < 5; i++) { + if (points[i] != local_points[i]) { + all_equal = false; + break; + } + } + + if (!all_equal) { + PhysicsServer::get_singleton()->shape_set_data(pyramid_shape, local_points); + points = local_points; + } + } + + Transform xf = get_global_transform(); + xf.origin = ray_from; + xf.orthonormalize(); + + float csafe, cunsafe; + if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, csafe, cunsafe, exclude, collision_mask)) { + clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from).normalized() * csafe); + } + + _update_camera(); + } + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + update_gizmo(); + } +} + +void ClippedCamera::set_collision_mask(uint32_t p_mask) { + + collision_mask = p_mask; +} + +uint32_t ClippedCamera::get_collision_mask() const { + + return collision_mask; +} + +void ClippedCamera::set_collision_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_collision_mask(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_mask(mask); +} + +bool ClippedCamera::get_collision_mask_bit(int p_bit) const { + + return get_collision_mask() & (1 << p_bit); +} + +void ClippedCamera::add_exception_rid(const RID &p_rid) { + + exclude.insert(p_rid); +} + +void ClippedCamera::add_exception(const Object *p_object) { + + ERR_FAIL_NULL(p_object); + const CollisionObject *co = Object::cast_to<CollisionObject>(p_object); + if (!co) + return; + add_exception_rid(co->get_rid()); +} + +void ClippedCamera::remove_exception_rid(const RID &p_rid) { + + exclude.erase(p_rid); +} + +void ClippedCamera::remove_exception(const Object *p_object) { + + ERR_FAIL_NULL(p_object); + const CollisionObject *co = Object::cast_to<CollisionObject>(p_object); + if (!co) + return; + remove_exception_rid(co->get_rid()); +} + +void ClippedCamera::clear_exceptions() { + + exclude.clear(); +} +void ClippedCamera::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera::set_margin); + ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera::get_margin); + + ClassDB::bind_method(D_METHOD("set_process_mode", "process_mode"), &ClippedCamera::set_process_mode); + ClassDB::bind_method(D_METHOD("get_process_mode"), &ClippedCamera::get_process_mode); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &ClippedCamera::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &ClippedCamera::get_collision_mask_bit); + + ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera::add_exception_rid); + ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera::add_exception); + + ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ClippedCamera::remove_exception_rid); + ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ClippedCamera::remove_exception); + + ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera::clear_exceptions); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_margin", "get_margin"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); +} +ClippedCamera::ClippedCamera() { + margin = 0; + clip_offset = 0; + process_mode = CLIP_PROCESS_PHYSICS; + set_physics_process_internal(true); + collision_mask = 1; + set_notify_local_transform(Engine::get_singleton()->is_editor_hint()); + points.resize(5); + pyramid_shape = PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONVEX_POLYGON); +} +ClippedCamera::~ClippedCamera() { + PhysicsServer::get_singleton()->free(pyramid_shape); +} diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 97705d8ae0..002d23b8fe 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -139,6 +139,8 @@ public: bool is_position_behind(const Vector3 &p_pos) const; virtual Vector3 project_position(const Point2 &p_point) const; + Vector<Vector3> get_near_plane_points() const; + void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; @@ -172,4 +174,54 @@ VARIANT_ENUM_CAST(Camera::Projection); VARIANT_ENUM_CAST(Camera::KeepAspect); VARIANT_ENUM_CAST(Camera::DopplerTracking); +class ClippedCamera : public Camera { + + GDCLASS(ClippedCamera, Camera); + +public: + enum ProcessMode { + CLIP_PROCESS_PHYSICS, + CLIP_PROCESS_IDLE, + }; + +private: + ProcessMode process_mode; + RID pyramid_shape; + float margin; + float clip_offset; + uint32_t collision_mask; + + Set<RID> exclude; + + Vector<Vector3> points; + +protected: + void _notification(int p_what); + static void _bind_methods(); + virtual Transform get_camera_transform() const; + +public: + void set_margin(float p_margin); + float get_margin() const; + + void set_process_mode(ProcessMode p_mode); + ProcessMode get_process_mode() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + + void add_exception_rid(const RID &p_rid); + void add_exception(const Object *p_object); + void remove_exception_rid(const RID &p_rid); + void remove_exception(const Object *p_object); + void clear_exceptions(); + + ClippedCamera(); + ~ClippedCamera(); +}; + +VARIANT_ENUM_CAST(ClippedCamera::ProcessMode); #endif diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 673d44dab2..da4e2f991d 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -375,6 +375,7 @@ void register_scene_types() { ClassDB::register_virtual_class<VisualInstance>(); ClassDB::register_virtual_class<GeometryInstance>(); ClassDB::register_class<Camera>(); + ClassDB::register_class<ClippedCamera>(); ClassDB::register_class<Listener>(); ClassDB::register_class<ARVRCamera>(); ClassDB::register_class<ARVRController>(); |