As my investigation continues, i applied high maths to achieve some results.

The mission was to create an draggable object in 3d space that does what grab function does in blender - moves object in 3d space when you move mouse in a direction based on camera look direction (in parallel)

Like this

I kinda achieved what i want but not sure if its optimal.

Like @xyz suggested, i create a plane that orients itself to camera, not looking at camera but at same rotation in space.
This plane is created at the ball origin.
This plane is then scaled up based on trig function to get the "max height of the FOV at the ball origin".
Then i get the raycast from camera to the plane and position the ball on the plane at the ray collision point.

One problem is that if the ball is at the edge of the screen then you can see the plane edges,

The trig function does not produce the "correct" result, idk why..

So the idea was to do the trig this way

	# calculate size of a projection plane
	var b = (ball_position - camera_3d.global_transform.origin).length()
	var angle_trig = camera_3d.fov/2
	var c = b / (cos(deg_to_rad(angle_trig)))
	var plane_height = sqrt(pow(c,2) -pow(b,2))*2

But the result gives me small plane

So i have to scale it up 2x when i insert the plane

# Create new plane object
	var new_mesh:StaticBody3D = PLANE_MESH.instantiate() 
	
	# Set plane orientatin and scale
	new_mesh.position = ball_position
	var new_basis: Basis
	var direction:Vector3 = (camera_3d.global_transform.origin-ball_position)
	new_basis.z = -direction
	new_basis.x = camera_3d.transform.basis.x
	new_basis.y = camera_3d.transform.basis.y
	new_mesh.transform.basis = new_basis
	new_mesh.scale = Vector3.ONE * plane_height*2

So my question is am i doing something wrong with the trig function? or am i on right path? Or maybe there is other way to do it?

  • xyz replied to this.
  • kuligs2 That's not the triangle you want and that triangle is not even right-angled. You want the triangle: cam - center of screen projected onto the plane - edge of screen projected onto the plane.

    In other words the proper length of the b side is the projection of the vector cam-ball onto the plane's normal vector (or camera's z basis)

    Also this can be done in a simpler way without collider nodes, using math only. Construct a Plane object from the object position and camera z basis. To find the position at mouse pointer just intersect the plane with the mouse ray by calling Plane::intersect_ray() . This way you can skip any wonky scaling calculations as the parametric plane is of infinite size.

    kuligs2 That's not the triangle you want and that triangle is not even right-angled. You want the triangle: cam - center of screen projected onto the plane - edge of screen projected onto the plane.

    In other words the proper length of the b side is the projection of the vector cam-ball onto the plane's normal vector (or camera's z basis)

    Also this can be done in a simpler way without collider nodes, using math only. Construct a Plane object from the object position and camera z basis. To find the position at mouse pointer just intersect the plane with the mouse ray by calling Plane::intersect_ray() . This way you can skip any wonky scaling calculations as the parametric plane is of infinite size.

      xyz Kinda works but not really.

      i create temple_plane on mouse click (inside the ball script):

      temp_plane = Plane(cam.global_transform.basis.z,position)

      then on mouse motion:

      	if cam != null:
      
      		var RAY_LENGTH = (cam.global_position - global_position).length()+2
      		
      		var space_state = get_world_3d().direct_space_state
      		var mousepos = get_viewport().get_mouse_position()
      
      		var origin = cam.project_ray_origin(mousepos)
      		var end = origin + cam.project_ray_normal(mousepos) * RAY_LENGTH
      		var query = PhysicsRayQueryParameters3D.create(origin, end)
      		query.exclude = [self]
      		query.collide_with_areas = true
      
      		var intersect = temp_plane.intersects_ray(end, cam.global_transform.origin-position)
      		if intersect:
      			position = intersect
      			look_at(cam.global_transform.origin,Vector3.UP)
      			print("interset: ", intersect)

      The result is kinda wonky. If you zoom in then the precision is too sensitive and the ball position sometimes shoots away like crazy..
      I previously used meshinstance to visualize whats going on. With Plane object i dont understand/see if its correctly constructed..

      EDIT: ok so i guess the ray length was the culprit. -> Changed to +10% and it works.

      	if cam != null:
      
      		var RAY_LENGTH = (cam.global_position - global_position).length()*1.1

      thanks! @xyz

      • xyz replied to this.

        kuligs2

        temp_plane = Plane(cam.global_transform.basis.z, global_position)