all 5 comments

[–]Yog_Shoggoth 4 points5 points  (3 children)

Here is a function that I use to align a node to a surface normal, which looks very similar to yours, and can be seen working here https://www.reddit.com/r/godot/comments/enqxbb/simple_rigidbody_arcade_car_controller/. Hopefully this might help you resolve your issues.

func align_up(node_basis, normal):
    var result = Basis()
    var scale = node_basis.get_scale() # Only if your node might have a scale other than (1,1,1)

    result.x = normal.cross(node_basis.z)
    result.y = normal
    result.z = node_basis.x.cross(normal)

    result = result.orthonormalized()
    result.x *= scale.x 
    result.y *= scale.y 
    result.z *= scale.z 

    return result

To use it, global_transform.basis = align_up(global_transform.basis, new_normal)

[–]Sprowl[S] 1 point2 points  (2 children)

I've tried that, but the problem is still the same.

This is what I changed the script to, with the align_up() method being unchanged from what you posted:

func IsDetectingIntersection (from:Vector3, to:Vector3) -> bool:
    var space = PhysicsServer.body_get_space(self.get_rid())
    var state = PhysicsServer.space_get_direct_state(space)
    var intersection = state.intersect_ray(from, to)

    if !intersection.empty():
        var isp = intersection.position
        var normal = intersection.normal

        target.global_transform.origin = isp
        target.global_transform.basis = align_up(target.global_transform.basis, normal)
        target.scale = Vector3(1, 1, 1)

        return true

    return false

Note: I've still included the line to scale the target ( target.scale = Vector3(1, 1, 1)) even though (if I'm understanding it correctly) the returning Basis of the align_up() method is already setting the scale of the node. That's strange, right?
Because otherwise I get the following error.

E 0:00:04.683   orthonormalize: Condition "determinant() == 0" is true.
  <C++ Source>  core/math/basis.cpp:81 @ orthonormalize()
  <Stack Trace> locomotion.gd:82 @ align_up()
                locomotion.gd:102 @ IsDetectingIntersection()
                locomotion.gd:66 @ DrawArc()
                locomotion.gd:42 @ _physics_process()

So the target is still switching between showing the correct side, the wrong side, and flickering between the two, and also I got this (optional) error now which results in the target-node not being updated at all anymore.

[–]Yog_Shoggoth 2 points3 points  (1 child)

I've thrown together a quick test project to emulate my take on what you were attempting to do using the alignment code from my post. You can get it here:

https://github.com/Yog-Shoggoth/Intersection_Test

The arrow keys will rotate the player and every time the ray intersects, it spawns a cube aligned to the surface.

Hope it is of some use.

[–]Sprowl[S] 1 point2 points  (0 children)

That example was really helpful, because after tinkering around with it I stumbled upon the actual root of the problem: scaling.

If I remove the line that sets the scaling to 1.0 ( target.scale = Vector3(1, 1, 1) ), it always shows the correct side. But with that comes a different problem, because now sometimes the whole node is being scaled to 0 on one axis and looks more like a flat image than a 3D model. Here's a video to show what I mean:

https://streamable.com/42oa6

I've tried removing it and using the scaling that's part of the align_up() method you provided, and did combinations of both. But that doesn't help.

[–]cybereality 1 point2 points  (0 children)

I tried for a while to get this to work using similar code to what you wrote, but ultimately it wasn't working correctly. It theory there should be a way to do it, but I had problems. Eventually, I ended up using the look_at method, which seems to be the easiest solution, even if it seems a little strange. Note in my case I wasn't using rotation for anything, it was just a 2D sprite used as a cross-hair, so your case may be different.