[Godot 2] How to click and drag an Area in 3D space?

woopdeedoowoopdeedoo Posts: 106Member
edited August 10 in 3D

The purpose is to make handles for editing 3D meshes, and my desired behavior is quite the same as the little handles that you find in Godot's CollisionShapes, to adjust their extents.

Below is the code I have so far in the Area's script. The area is ray pickable, and I'm using input events as they seem straightforward and easy to setup, and it seems like this might be achievable with that.

As it is though, I can only drag the thing if I move the mouse slowly, or else it goes outside the Area and it stops receiving input events. I managed to make it so I can move the mouse outside the Area a little bit, but it has problems, as it doesn't reset the state (frame) of the Sprite, at least in the way I've coded it. It's suppose to work a bit like a button, so the Sprite has 3 frames for normal state, clicked and hovered.

The scene tree is just an Area, with a billboard Sprite3D (for the icon) and a sphere CollisionShape. Its parent is a spatial, which is the 3D view scene of my app.

It might help if someone could point me to the files in Godot's own source code where the CollisionShape handles are implemented, so I could see how they did this. I've been trying to find that, but so far no luck...

extends Area
# <- trying to make the the code more readable here, as it's not preserving my empty lines
onready var view = get_parent().get_parent().get_parent()
onready var sprite = get_node("Sprite3D")
#
enum {NONE, HOVERED, CLICKED}
var state
#
func _ready():
    switch_state(NONE)
#
func switch_state(st):
    if st != state:
        state = st
        sprite.set_frame(state)
#
func _on_TopHandle_input_event( camera, event, click_pos, click_normal, shape_idx ):
    if event.type==InputEvent.MOUSE_MOTION and state == CLICKED:
        # it's only supposed to move up and down, in y axis
        set_translation(Vector3(get_translation().x, click_pos.y, get_translation().z))
#
    if event.is_action_pressed("editor_select"):
        switch_state(CLICKED)
    elif event.is_action_released("editor_select"):
        switch_state(HOVERED)
#
func _on_TopHandle_mouse_enter():
    switch_state(HOVERED)
#
func _on_TopHandle_mouse_exit():
    # my attempt at making it not let go until I release the mouse button
    if state != CLICKED:
        switch_state(NONE)

Answers

  • TwistedTwiglegTwistedTwigleg Posts: 851Admin

    It might help if someone could point me to the files in Godot's own source code where the CollisionShape handles are implemented, so I could see how they did this. I've been trying to find that, but so far no luck...

    I think the gizmo's are implemented in spatial_editor_gizmos.h and spatial_editor_gizmos.cpp. Here are links to both:

    spatial_editor_gizmos.h
    spatial_editor_gizmos.cpp

    Hopefully that helps!

  • woopdeedoowoopdeedoo Posts: 106Member
    edited August 10

    Hmm, it seems they all inherit from EditorSpatialGizmo, which might actually be what I need if two conditions are met:

    1. Can it be made to show up and be used in-game/in-app? I ask this because the docs say "These are created by EditorPlugin.create_spatial_gizmo()" which, as I understand it, means it's an editor only kind of thing.
    2. If that first condition is true, can the handle be given a custom graphic? On a glance, the functions add_mesh() and add_unscaled_billboard() seem to be for that purpose, but I'm a little confused about them too.

    Otherwise I think I'm back to my problem... Unless I'm missing something.

  • woopdeedoowoopdeedoo Posts: 106Member
    edited August 10

    Well, in the meanwhile I managed to get it to almost work with this piece of code (and removed lines 18-20 from the input event):

    func _input(event):
        if state == CLICKED:
            if event.type == InputEvent.MOUSE_MOTION:
                var yrel = event.relative_y*0.02
                var pos = get_translation()
                set_translation(Vector3(pos.x, pos.y - yrel, pos.z))
            # also to prevent it from sticking after releasing the mouse outside the CollisionShape
            if event.is_action_released("editor_select"):
                switch_state(NONE)
    

    It doesn't fully work, as the node doesn't move exactly relative to the mouse's Y position. Depending on that multiplier I have there, it can move faster or slower, and it also depends on its distance in 3D space as well as the perspective of the camera.

  • woopdeedoowoopdeedoo Posts: 106Member
    edited August 11 Answer ✓

    Actually managed to achieve this by using a helper Area/PlaneShape that snaps to the position of the handled that's being grabbed and is always facing the camera (not as a billboard, but always upright, with the normal perpendicular to the Y axis). It detects input events and passes the click_pos to the handle. It gets hidden away when handle is released.

    It's not perfect yet, as I still need to figure out how to make the plane face perpendicular to the camera instead, but it's almost there.

  • TwistedTwiglegTwistedTwigleg Posts: 851Admin

    @woopdeedoo said:
    Actually managed to achieve this by using a helper Area/PlaneShape that snaps to the position of the handled that's being grabbed and is always facing the camera (not as a billboard, but always upright, with the normal perpendicular to the Y axis). It detects input events and passes the click_pos to the handle. It gets hidden away when handle is released.

    Great! I'm glad you found a solution! That seems like a good way to get around the EditorSpatialGizmo problem!

    Hmm, it seems they all inherit from EditorSpatialGizmo, which might actually be what I need if two conditions are met:

    I do not think you can use EditorSpatialGizmo in-game, but I have never used it before so I'm cannot say it will not work for sure. Sorry, I wish I had a more conclusive answer. Thankfully it looks like you do not need it anymore :smile:

    (Also, my apologizes for not replying until now. I've been busy with other stuff and accidentally forgot to reply to your earlier questions :sweat_smile: )

Sign In or Register to comment.