Ver Fonte

Merge pull request #53354 from reduz/remove-clipped-camera

Rémi Verschelde há 3 anos atrás
pai
commit
7722048ed0

+ 7 - 1
doc/classes/Camera3D.xml

@@ -26,7 +26,7 @@
 		<method name="get_camera_transform" qualifiers="const">
 			<return type="Transform3D" />
 			<description>
-				Returns the transform of the camera plus the vertical ([member v_offset]) and horizontal ([member h_offset]) offsets; and any other adjustments made to the position and orientation of the camera by subclassed cameras such as [ClippedCamera3D] and [XRCamera3D].
+				Returns the transform of the camera plus the vertical ([member v_offset]) and horizontal ([member h_offset]) offsets; and any other adjustments made to the position and orientation of the camera by subclassed cameras such as [XRCamera3D].
 			</description>
 		</method>
 		<method name="get_cull_mask_value" qualifiers="const">
@@ -42,6 +42,12 @@
 				Returns the camera's frustum planes in world space units as an array of [Plane]s in the following order: near, far, left, top, right, bottom. Not to be confused with [member frustum_offset].
 			</description>
 		</method>
+		<method name="get_pyramid_shape_rid">
+			<return type="RID" />
+			<description>
+				Returns the RID of a pyramid shape encompassing the camera's view frustum, ignoring the camera's near plane. The tip of the pyramid represents the position of the camera.
+			</description>
+		</method>
 		<method name="is_position_behind" qualifiers="const">
 			<return type="bool" />
 			<argument index="0" name="world_point" type="Vector3" />

+ 0 - 93
doc/classes/ClippedCamera3D.xml

@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ClippedCamera3D" inherits="Camera3D" version="4.0">
-	<brief_description>
-		A [Camera3D] that includes collision.
-	</brief_description>
-	<description>
-		This node extends [Camera3D] to add collisions with [Area3D] and/or [PhysicsBody3D] nodes. The camera cannot move through colliding objects.
-	</description>
-	<tutorials>
-	</tutorials>
-	<methods>
-		<method name="add_exception">
-			<return type="void" />
-			<argument index="0" name="node" type="Object" />
-			<description>
-				Adds a collision exception so the camera does not collide with the specified node.
-			</description>
-		</method>
-		<method name="add_exception_rid">
-			<return type="void" />
-			<argument index="0" name="rid" type="RID" />
-			<description>
-				Adds a collision exception so the camera does not collide with the specified [RID].
-			</description>
-		</method>
-		<method name="clear_exceptions">
-			<return type="void" />
-			<description>
-				Removes all collision exceptions.
-			</description>
-		</method>
-		<method name="get_clip_offset" qualifiers="const">
-			<return type="float" />
-			<description>
-				Returns the distance the camera has been offset due to a collision.
-			</description>
-		</method>
-		<method name="get_collision_mask_value" qualifiers="const">
-			<return type="bool" />
-			<argument index="0" name="layer_number" type="int" />
-			<description>
-				Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
-			</description>
-		</method>
-		<method name="remove_exception">
-			<return type="void" />
-			<argument index="0" name="node" type="Object" />
-			<description>
-				Removes a collision exception with the specified node.
-			</description>
-		</method>
-		<method name="remove_exception_rid">
-			<return type="void" />
-			<argument index="0" name="rid" type="RID" />
-			<description>
-				Removes a collision exception with the specified [RID].
-			</description>
-		</method>
-		<method name="set_collision_mask_value">
-			<return type="void" />
-			<argument index="0" name="layer_number" type="int" />
-			<argument index="1" name="value" type="bool" />
-			<description>
-				Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32.
-			</description>
-		</method>
-	</methods>
-	<members>
-		<member name="clip_to_areas" type="bool" setter="set_clip_to_areas" getter="is_clip_to_areas_enabled" default="false">
-			If [code]true[/code], the camera stops on contact with [Area3D]s.
-		</member>
-		<member name="clip_to_bodies" type="bool" setter="set_clip_to_bodies" getter="is_clip_to_bodies_enabled" default="true">
-			If [code]true[/code], the camera stops on contact with [PhysicsBody3D]s.
-		</member>
-		<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
-			The camera's collision mask. Only objects in at least one collision layer matching the mask will be detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
-		</member>
-		<member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.0">
-			The camera's collision margin. The camera can't get closer than this distance to a colliding object.
-		</member>
-		<member name="process_callback" type="int" setter="set_process_callback" getter="get_process_callback" enum="ClippedCamera3D.ClipProcessCallback" default="0">
-			The camera's process callback. See [enum ClipProcessCallback].
-		</member>
-	</members>
-	<constants>
-		<constant name="CLIP_PROCESS_PHYSICS" value="0" enum="ClipProcessCallback">
-			The camera updates with the [code]_physics_process[/code] callback.
-		</constant>
-		<constant name="CLIP_PROCESS_IDLE" value="1" enum="ClipProcessCallback">
-			The camera updates with the [code]_process[/code] callback.
-		</constant>
-	</constants>
-</class>

+ 0 - 41
editor/plugins/node_3d_editor_gizmos.cpp

@@ -1840,47 +1840,6 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
 
 	p_gizmo->add_lines(lines, material);
 	p_gizmo->add_handles(handles, get_material("handles"));
-
-	ClippedCamera3D *clipcam = Object::cast_to<ClippedCamera3D>(camera);
-	if (clipcam) {
-		Node3D *parent = Object::cast_to<Node3D>(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);
-		}
-
-		Transform3D 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);
-	}
 }
 
 //////

+ 29 - 215
scene/3d/camera_3d.cpp

@@ -492,6 +492,7 @@ void Camera3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum);
 	ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum);
 	ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera);
+	ClassDB::bind_method(D_METHOD("get_pyramid_shape_rid"), &Camera3D::get_pyramid_shape_rid);
 
 	ClassDB::bind_method(D_METHOD("set_cull_mask_value", "layer_number", "value"), &Camera3D::set_cull_mask_value);
 	ClassDB::bind_method(D_METHOD("get_cull_mask_value", "layer_number"), &Camera3D::get_cull_mask_value);
@@ -654,233 +655,46 @@ Vector3 Camera3D::get_doppler_tracked_velocity() const {
 	}
 }
 
-Camera3D::Camera3D() {
-	camera = RenderingServer::get_singleton()->camera_create();
-	set_perspective(75.0, 0.05, 4000.0);
-	RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
-	//active=false;
-	velocity_tracker.instantiate();
-	set_notify_transform(true);
-	set_disable_scale(true);
-}
-
-Camera3D::~Camera3D() {
-	RenderingServer::get_singleton()->free(camera);
-}
+RID Camera3D::get_pyramid_shape_rid() {
+	if (pyramid_shape == RID()) {
+		pyramid_shape_points = get_near_plane_points();
+		pyramid_shape = PhysicsServer3D::get_singleton()->convex_polygon_shape_create();
+		PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, pyramid_shape_points);
 
-////////////////////////////////////////
+	} else { //check if points changed
+		Vector<Vector3> local_points = get_near_plane_points();
 
-void ClippedCamera3D::set_margin(real_t p_margin) {
-	margin = p_margin;
-}
-
-real_t ClippedCamera3D::get_margin() const {
-	return margin;
-}
-
-void ClippedCamera3D::set_process_callback(ClipProcessCallback p_mode) {
-	if (process_callback == p_mode) {
-		return;
-	}
-	process_callback = p_mode;
-	set_process_internal(process_callback == CLIP_PROCESS_IDLE);
-	set_physics_process_internal(process_callback == CLIP_PROCESS_PHYSICS);
-}
-
-ClippedCamera3D::ClipProcessCallback ClippedCamera3D::get_process_callback() const {
-	return process_callback;
-}
-
-Transform3D ClippedCamera3D::get_camera_transform() const {
-	Transform3D t = Camera3D::get_camera_transform();
-	t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset;
-	return t;
-}
-
-void ClippedCamera3D::_notification(int p_what) {
-	if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
-		Node3D *parent = Object::cast_to<Node3D>(get_parent());
-		if (!parent) {
-			return;
-		}
+		bool all_equal = true;
 
-		PhysicsDirectSpaceState3D *dspace = get_world_3d()->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 default
-
-		{ //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) {
-				PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, local_points);
-				points = local_points;
+		for (int i = 0; i < 5; i++) {
+			if (local_points[i] != pyramid_shape_points[i]) {
+				all_equal = false;
+				break;
 			}
 		}
 
-		Transform3D xf = get_global_transform();
-		xf.origin = ray_from;
-		xf.orthonormalize();
-
-		real_t closest_safe = 1.0f, closest_unsafe = 1.0f;
-		if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, closest_safe, closest_unsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) {
-			clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * closest_safe);
+		if (!all_equal) {
+			PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, local_points);
+			pyramid_shape_points = local_points;
 		}
-
-		_update_camera();
-	}
-
-	if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
-		update_gizmos();
 	}
-}
-
-void ClippedCamera3D::set_collision_mask(uint32_t p_mask) {
-	collision_mask = p_mask;
-}
 
-uint32_t ClippedCamera3D::get_collision_mask() const {
-	return collision_mask;
+	return pyramid_shape;
 }
 
-void ClippedCamera3D::set_collision_mask_value(int p_layer_number, bool p_value) {
-	ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
-	ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
-	uint32_t mask = get_collision_mask();
-	if (p_value) {
-		mask |= 1 << (p_layer_number - 1);
-	} else {
-		mask &= ~(1 << (p_layer_number - 1));
-	}
-	set_collision_mask(mask);
-}
-
-bool ClippedCamera3D::get_collision_mask_value(int p_layer_number) const {
-	ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
-	ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
-	return get_collision_mask() & (1 << (p_layer_number - 1));
-}
-
-void ClippedCamera3D::add_exception_rid(const RID &p_rid) {
-	exclude.insert(p_rid);
-}
-
-void ClippedCamera3D::add_exception(const Object *p_object) {
-	ERR_FAIL_NULL(p_object);
-	const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
-	if (!co) {
-		return;
-	}
-	add_exception_rid(co->get_rid());
-}
-
-void ClippedCamera3D::remove_exception_rid(const RID &p_rid) {
-	exclude.erase(p_rid);
+Camera3D::Camera3D() {
+	camera = RenderingServer::get_singleton()->camera_create();
+	set_perspective(75.0, 0.05, 4000.0);
+	RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
+	//active=false;
+	velocity_tracker.instantiate();
+	set_notify_transform(true);
+	set_disable_scale(true);
 }
 
-void ClippedCamera3D::remove_exception(const Object *p_object) {
-	ERR_FAIL_NULL(p_object);
-	const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
-	if (!co) {
-		return;
+Camera3D::~Camera3D() {
+	RenderingServer::get_singleton()->free(camera);
+	if (pyramid_shape.is_valid()) {
+		PhysicsServer3D::get_singleton()->free(pyramid_shape);
 	}
-	remove_exception_rid(co->get_rid());
-}
-
-void ClippedCamera3D::clear_exceptions() {
-	exclude.clear();
-}
-
-real_t ClippedCamera3D::get_clip_offset() const {
-	return clip_offset;
-}
-
-void ClippedCamera3D::set_clip_to_areas(bool p_clip) {
-	clip_to_areas = p_clip;
-}
-
-bool ClippedCamera3D::is_clip_to_areas_enabled() const {
-	return clip_to_areas;
-}
-
-void ClippedCamera3D::set_clip_to_bodies(bool p_clip) {
-	clip_to_bodies = p_clip;
-}
-
-bool ClippedCamera3D::is_clip_to_bodies_enabled() const {
-	return clip_to_bodies;
-}
-
-void ClippedCamera3D::_bind_methods() {
-	ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera3D::set_margin);
-	ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera3D::get_margin);
-
-	ClassDB::bind_method(D_METHOD("set_process_callback", "process_callback"), &ClippedCamera3D::set_process_callback);
-	ClassDB::bind_method(D_METHOD("get_process_callback"), &ClippedCamera3D::get_process_callback);
-
-	ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera3D::set_collision_mask);
-	ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera3D::get_collision_mask);
-
-	ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ClippedCamera3D::set_collision_mask_value);
-	ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ClippedCamera3D::get_collision_mask_value);
-
-	ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera3D::add_exception_rid);
-	ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera3D::add_exception);
-
-	ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ClippedCamera3D::remove_exception_rid);
-	ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ClippedCamera3D::remove_exception);
-
-	ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera3D::set_clip_to_areas);
-	ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera3D::is_clip_to_areas_enabled);
-
-	ClassDB::bind_method(D_METHOD("get_clip_offset"), &ClippedCamera3D::get_clip_offset);
-
-	ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera3D::set_clip_to_bodies);
-	ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera3D::is_clip_to_bodies_enabled);
-
-	ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera3D::clear_exceptions);
-
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_margin", "get_margin");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
-
-	ADD_GROUP("Clip To", "clip_to");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_areas", "is_clip_to_areas_enabled");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_bodies", "is_clip_to_bodies_enabled");
-
-	BIND_ENUM_CONSTANT(CLIP_PROCESS_PHYSICS);
-	BIND_ENUM_CONSTANT(CLIP_PROCESS_IDLE);
-}
-
-ClippedCamera3D::ClippedCamera3D() {
-	set_physics_process_internal(true);
-	set_notify_local_transform(Engine::get_singleton()->is_editor_hint());
-	points.resize(5);
-	pyramid_shape = PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_CONVEX_POLYGON);
-}
-
-ClippedCamera3D::~ClippedCamera3D() {
-	PhysicsServer3D::get_singleton()->free(pyramid_shape);
 }

+ 5 - 59
scene/3d/camera_3d.h

@@ -86,6 +86,9 @@ private:
 	DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED;
 	Ref<VelocityTracker3D> velocity_tracker;
 
+	RID pyramid_shape;
+	Vector<Vector3> pyramid_shape_points;
+
 protected:
 	void _update_camera();
 	virtual void _request_camera_update();
@@ -168,6 +171,8 @@ public:
 
 	Vector3 get_doppler_tracked_velocity() const;
 
+	RID get_pyramid_shape_rid();
+
 	Camera3D();
 	~Camera3D();
 };
@@ -176,63 +181,4 @@ VARIANT_ENUM_CAST(Camera3D::Projection);
 VARIANT_ENUM_CAST(Camera3D::KeepAspect);
 VARIANT_ENUM_CAST(Camera3D::DopplerTracking);
 
-class ClippedCamera3D : public Camera3D {
-	GDCLASS(ClippedCamera3D, Camera3D);
-
-public:
-	enum ClipProcessCallback {
-		CLIP_PROCESS_PHYSICS,
-		CLIP_PROCESS_IDLE,
-	};
-
-private:
-	ClipProcessCallback process_callback = CLIP_PROCESS_PHYSICS;
-	RID pyramid_shape;
-	real_t margin = 0.0;
-	real_t clip_offset = 0.0;
-	uint32_t collision_mask = 1;
-	bool clip_to_areas = false;
-	bool clip_to_bodies = true;
-
-	Set<RID> exclude;
-
-	Vector<Vector3> points;
-
-protected:
-	void _notification(int p_what);
-	static void _bind_methods();
-	virtual Transform3D get_camera_transform() const override;
-
-public:
-	void set_clip_to_areas(bool p_clip);
-	bool is_clip_to_areas_enabled() const;
-
-	void set_clip_to_bodies(bool p_clip);
-	bool is_clip_to_bodies_enabled() const;
-
-	void set_margin(real_t p_margin);
-	real_t get_margin() const;
-
-	void set_process_callback(ClipProcessCallback p_mode);
-	ClipProcessCallback get_process_callback() const;
-
-	void set_collision_mask(uint32_t p_mask);
-	uint32_t get_collision_mask() const;
-
-	void set_collision_mask_value(int p_layer_number, bool p_value);
-	bool get_collision_mask_value(int p_layer_number) 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();
-
-	real_t get_clip_offset() const;
-
-	ClippedCamera3D();
-	~ClippedCamera3D();
-};
-
-VARIANT_ENUM_CAST(ClippedCamera3D::ClipProcessCallback);
 #endif

+ 24 - 8
scene/3d/spring_arm_3d.cpp

@@ -29,6 +29,7 @@
 /*************************************************************************/
 
 #include "spring_arm_3d.h"
+#include "scene/3d/camera_3d.h"
 
 void SpringArm3D::_notification(int p_what) {
 	switch (p_what) {
@@ -133,17 +134,32 @@ void SpringArm3D::process_spring() {
 	Vector3 motion;
 	const Vector3 cast_direction(get_global_transform().basis.xform(Vector3(0, 0, 1)));
 
+	motion = Vector3(cast_direction * (spring_length));
+
 	if (shape.is_null()) {
-		motion = Vector3(cast_direction * (spring_length));
-		PhysicsDirectSpaceState3D::RayResult r;
-		bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask);
-		if (intersected) {
-			real_t dist = get_global_transform().origin.distance_to(r.position);
-			dist -= margin;
-			motion_delta = dist / (spring_length);
+		Camera3D *camera = nullptr;
+		for (int i = get_child_count() - 1; 0 <= i; --i) {
+			camera = Object::cast_to<Camera3D>(get_child(i));
+			if (camera) {
+				break;
+			}
+		}
+
+		if (camera != nullptr) {
+			//use camera rotation, but spring arm position
+			Transform3D base_transform = camera->get_global_transform();
+			base_transform.origin = get_global_transform().origin;
+			get_world_3d()->get_direct_space_state()->cast_motion(camera->get_pyramid_shape_rid(), base_transform, motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask);
+		} else {
+			PhysicsDirectSpaceState3D::RayResult r;
+			bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask);
+			if (intersected) {
+				real_t dist = get_global_transform().origin.distance_to(r.position);
+				dist -= margin;
+				motion_delta = dist / (spring_length);
+			}
 		}
 	} else {
-		motion = Vector3(cast_direction * spring_length);
 		get_world_3d()->get_direct_space_state()->cast_motion(shape->get_rid(), get_global_transform(), motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask);
 	}
 

+ 0 - 1
scene/register_scene_types.cpp

@@ -448,7 +448,6 @@ void register_scene_types() {
 	GDREGISTER_VIRTUAL_CLASS(VisualInstance3D);
 	GDREGISTER_VIRTUAL_CLASS(GeometryInstance3D);
 	GDREGISTER_CLASS(Camera3D);
-	GDREGISTER_CLASS(ClippedCamera3D);
 	GDREGISTER_CLASS(AudioListener3D);
 	GDREGISTER_CLASS(XRCamera3D);
 	GDREGISTER_CLASS(XRController3D);