diff --git a/doc/api/class_terrain3d.rst b/doc/api/class_terrain3d.rst index 0fe32ffb..5302b8a4 100644 --- a/doc/api/class_terrain3d.rst +++ b/doc/api/class_terrain3d.rst @@ -89,29 +89,29 @@ Methods .. table:: :widths: auto - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Mesh` | :ref:`bake_mesh`\ (\ lod\: :ref:`int`, filter\: :ref:`HeightFilter`\ ) |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`PackedVector3Array` | :ref:`generate_nav_mesh_source_geometry`\ (\ global_aabb\: :ref:`AABB`, require_nav\: :ref:`bool` = true\ ) |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Camera3D` | :ref:`get_camera`\ (\ ) |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`RID` | :ref:`get_collision_rid`\ (\ ) |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Terrain3DEditor` | :ref:`get_editor`\ (\ ) |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Vector3` | :ref:`get_intersection`\ (\ src_pos\: :ref:`Vector3`, direction\: :ref:`Vector3`\ ) | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`EditorPlugin` | :ref:`get_plugin`\ (\ ) |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`bool` | :ref:`is_compatibility_mode`\ (\ ) |const| | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | |void| | :ref:`set_camera`\ (\ camera\: :ref:`Camera3D`\ ) | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | |void| | :ref:`set_editor`\ (\ editor\: :ref:`Terrain3DEditor`\ ) | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | |void| | :ref:`set_plugin`\ (\ plugin\: :ref:`EditorPlugin`\ ) | - +-----------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Mesh` | :ref:`bake_mesh`\ (\ lod\: :ref:`int`, filter\: :ref:`HeightFilter`\ ) |const| | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`PackedVector3Array` | :ref:`generate_nav_mesh_source_geometry`\ (\ global_aabb\: :ref:`AABB`, require_nav\: :ref:`bool` = true\ ) |const| | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Camera3D` | :ref:`get_camera`\ (\ ) |const| | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`RID` | :ref:`get_collision_rid`\ (\ ) |const| | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Terrain3DEditor` | :ref:`get_editor`\ (\ ) |const| | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`Vector3` | :ref:`get_intersection`\ (\ src_pos\: :ref:`Vector3`, direction\: :ref:`Vector3`, gpu_mode\: :ref:`bool` = false\ ) | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`EditorPlugin` | :ref:`get_plugin`\ (\ ) |const| | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | :ref:`bool` | :ref:`is_compatibility_mode`\ (\ ) |const| | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_camera`\ (\ camera\: :ref:`Camera3D`\ ) | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_editor`\ (\ editor\: :ref:`Terrain3DEditor`\ ) | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + | |void| | :ref:`set_plugin`\ (\ plugin\: :ref:`EditorPlugin`\ ) | + +-----------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -757,9 +757,39 @@ Returns the current Terrain3DEditor instance, if it has been set. .. rst-class:: classref-method -:ref:`Vector3` **get_intersection**\ (\ src_pos\: :ref:`Vector3`, direction\: :ref:`Vector3`\ ) :ref:`🔗` +:ref:`Vector3` **get_intersection**\ (\ src_pos\: :ref:`Vector3`, direction\: :ref:`Vector3`, gpu_mode\: :ref:`bool` = false\ ) :ref:`🔗` + +Casts a ray from ``src_pos`` pointing towards ``direction``, attempting to intersect the terrain. This operation is does not use physics, so enabling collision is unnecessary. + + + +This function can operate in one of two modes defined by ``gpu_mode``: + +- If gpu_mode is disabled (default), it raymarches from the camera until the terrain is intersected, up to 4000m away. This works with one function call, but only where regions exist. It is slower than gpu_mode and gets increasingly slower the farther away the terrain is, though you may not notice. + + + +- If gpu_mode is enabled, it uses the GPU to detect the mouse. This works wherever the terrain is visible, even outside of regions, but may need to be called twice. + + + +GPU mode places a camera at the specified point and "looks" at the terrain. It uses the depth texture to determine how far away the intersection point is. It requires the use of an editor render layer (default 32) while using this function. See :ref:`mouse_layer`. + + + +The main caveats of using this mode is that the call to get_intersection() requests a viewport be drawn, but cannot wait for it to finish as there is no "await" in C++ and no force draw function in Godot. So the return value is one frame behind, and invalid on the first call. This also means the function cannot be used more than once per frame. This mode works well when used continuously, once per frame, where one frame of difference won't matter. The editor uses this mode to place the mouse cursor decal. + + + +This mode can also be used by your plugins and games, such as a space ship firing lasers at the terrain and causing an explosion at the hit point. However if the calls aren't continuous, you'll need to call once to capture the viewport image, wait for it to be drawn, then call again to get the result: + +:: + + var target_point = terrain.get_intersection(camera_pos, camera_dir) + await RenderingServer.frame_post_draw + target_point = terrain.get_intersection(camera_pos, camera_dir) + -Casts a ray from ``src_pos`` pointing towards ``direction``, attempting to intersect the terrain. Possible return values: @@ -769,12 +799,6 @@ Possible return values: - On error, it returns ``Vector3(NAN, NAN, NAN)`` and prints a message to the console. -This ray cast does not use physics, so enabling collision is unnecessary. It places a camera at the specified point and "looks" at the terrain. It then uses the renderer's depth texture to determine how far away the intersection point is. - -This function is used by the editor plugin to place the mouse cursor. It can also be used by 3rd party plugins, and even during gameplay, such as a space ship firing lasers at the terrain and causing an explosion at the hit point. - -It does require the use of an editor render layer (21-32) that should be dedicated while using this function. See :ref:`mouse_layer`. - .. rst-class:: classref-item-separator ---- diff --git a/doc/classes/Terrain3D.xml b/doc/classes/Terrain3D.xml index 028e5c25..620c942c 100644 --- a/doc/classes/Terrain3D.xml +++ b/doc/classes/Terrain3D.xml @@ -51,15 +51,30 @@ + - Casts a ray from [code skip-lint]src_pos[/code] pointing towards [code skip-lint]direction[/code], attempting to intersect the terrain. + Casts a ray from [code skip-lint]src_pos[/code] pointing towards [code skip-lint]direction[/code], attempting to intersect the terrain. This operation is does not use physics, so enabling collision is unnecessary. + + This function can operate in one of two modes defined by [code skip-lint]gpu_mode[/code]: + - If gpu_mode is disabled (default), it raymarches from the camera until the terrain is intersected, up to 4000m away. This works with one function call, but only where regions exist. It is slower than gpu_mode and gets increasingly slower the farther away the terrain is, though you may not notice. + + - If gpu_mode is enabled, it uses the GPU to detect the mouse. This works wherever the terrain is visible, even outside of regions, but may need to be called twice. + + GPU mode places a camera at the specified point and "looks" at the terrain. It uses the depth texture to determine how far away the intersection point is. It requires the use of an editor render layer (default 32) while using this function. See [member mouse_layer]. + + The main caveats of using this mode is that the call to get_intersection() requests a viewport be drawn, but cannot wait for it to finish as there is no "await" in C++ and no force draw function in Godot. So the return value is one frame behind, and invalid on the first call. This also means the function cannot be used more than once per frame. This mode works well when used continuously, once per frame, where one frame of difference won't matter. The editor uses this mode to place the mouse cursor decal. + + This mode can also be used by your plugins and games, such as a space ship firing lasers at the terrain and causing an explosion at the hit point. However if the calls aren't continuous, you'll need to call once to capture the viewport image, wait for it to be drawn, then call again to get the result: + [codeblock] + var target_point = terrain.get_intersection(camera_pos, camera_dir) + await RenderingServer.frame_post_draw + target_point = terrain.get_intersection(camera_pos, camera_dir) + [/codeblock] + Possible return values: - If the terrain is hit, the intersection point is returned. - If there is no intersection, eg. the ray points towards the sky, it returns the maximum double float value [code skip-lint]Vector3(3.402823466e+38F,...)[/code]. You can check this case with this code: [code skip-lint]if point.z > 3.4e38:[/code] - On error, it returns [code skip-lint]Vector3(NAN, NAN, NAN)[/code] and prints a message to the console. - This ray cast does not use physics, so enabling collision is unnecessary. It places a camera at the specified point and "looks" at the terrain. It then uses the renderer's depth texture to determine how far away the intersection point is. - This function is used by the editor plugin to place the mouse cursor. It can also be used by 3rd party plugins, and even during gameplay, such as a space ship firing lasers at the terrain and causing an explosion at the hit point. - It does require the use of an editor render layer (21-32) that should be dedicated while using this function. See [member mouse_layer]. diff --git a/project/addons/terrain_3d/editor.gd b/project/addons/terrain_3d/editor.gd index 6f01cf3b..fb78532a 100644 --- a/project/addons/terrain_3d/editor.gd +++ b/project/addons/terrain_3d/editor.gd @@ -177,7 +177,7 @@ func _forward_3d_gui_input(p_viewport_camera: Camera3D, p_event: InputEvent) -> mouse_global_position = (camera_pos + t * camera_dir) else: # Else look for intersection with terrain - var intersection_point: Vector3 = terrain.get_intersection(camera_pos, camera_dir) + var intersection_point: Vector3 = terrain.get_intersection(camera_pos, camera_dir, true) if intersection_point.z > 3.4e38 or is_nan(intersection_point.y): # max double or nan return AFTER_GUI_INPUT_STOP mouse_global_position = intersection_point diff --git a/src/terrain_3d.cpp b/src/terrain_3d.cpp index 6341ab06..e3636d22 100644 --- a/src/terrain_3d.cpp +++ b/src/terrain_3d.cpp @@ -1162,12 +1162,13 @@ void Terrain3D::update_aabbs() { } } -/* Returns the point a ray intersects the ground using the GPU depth texture +/* Returns the point a ray intersects the ground using either raymarching or the GPU depth texture * p_src_pos (camera position) * p_direction (camera direction looking at the terrain) + * p_gpu_mode - false: use raymarching, true: use GPU mode * Returns Vec3(NAN) on error or vec3(3.402823466e+38F) on no intersection. Test w/ if (var.x < 3.4e38) */ -Vector3 Terrain3D::get_intersection(const Vector3 &p_src_pos, const Vector3 &p_direction) { +Vector3 Terrain3D::get_intersection(const Vector3 &p_src_pos, const Vector3 &p_direction, const bool p_gpu_mode) { if (!is_instance_valid(_camera_instance_id)) { LOG(ERROR, "Invalid camera"); return Vector3(NAN, NAN, NAN); @@ -1182,7 +1183,7 @@ Vector3 Terrain3D::get_intersection(const Vector3 &p_src_pos, const Vector3 &p_d // Position mouse cam one unit behind the requested position _mouse_cam->set_global_position(p_src_pos - direction); - // If looking straight down (eg orthogonal camera), look_at won't work + // If looking straight down (eg orthogonal camera), just return height. look_at won't work if ((direction - Vector3(0.f, -1.f, 0.f)).length_squared() < 0.00001f) { _mouse_cam->set_rotation_degrees(Vector3(-90.f, 0.f, 0.f)); point = p_src_pos; @@ -1190,7 +1191,21 @@ Vector3 Terrain3D::get_intersection(const Vector3 &p_src_pos, const Vector3 &p_d if (std::isnan(point.y)) { point.y = 0; } + + } else if (!p_gpu_mode) { + // Else if not gpu mode, use raymarching mode + point = p_src_pos; + for (int i = 0; i < 4000; i++) { + real_t height = _data->get_height(point); + if (point.y - height <= 0) { + return point; + } + point += direction; + } + return V3_MAX; + } else { + // Else use GPU mode, which requires multiple calls // Get depth from perspective camera snapshot _mouse_cam->look_at(_mouse_cam->get_global_position() + direction, Vector3(0.f, 1.f, 0.f)); _mouse_vp->set_update_mode(SubViewport::UPDATE_ONCE); @@ -1515,7 +1530,7 @@ void Terrain3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_compatibility_mode"), &Terrain3D::is_compatibility_mode); // Utility - ClassDB::bind_method(D_METHOD("get_intersection", "src_pos", "direction"), &Terrain3D::get_intersection); + ClassDB::bind_method(D_METHOD("get_intersection", "src_pos", "direction", "gpu_mode"), &Terrain3D::get_intersection, DEFVAL(false)); ClassDB::bind_method(D_METHOD("bake_mesh", "lod", "filter"), &Terrain3D::bake_mesh); ClassDB::bind_method(D_METHOD("generate_nav_mesh_source_geometry", "global_aabb", "require_nav"), &Terrain3D::generate_nav_mesh_source_geometry, DEFVAL(true)); diff --git a/src/terrain_3d.h b/src/terrain_3d.h index 36487c64..3a77ac30 100644 --- a/src/terrain_3d.h +++ b/src/terrain_3d.h @@ -215,7 +215,7 @@ class Terrain3D : public Node3D { void update_aabbs(); // Utility - Vector3 get_intersection(const Vector3 &p_src_pos, const Vector3 &p_direction); + Vector3 get_intersection(const Vector3 &p_src_pos, const Vector3 &p_direction, const bool p_gpu_mode = false); Ref bake_mesh(const int p_lod, const Terrain3DData::HeightFilter p_filter = Terrain3DData::HEIGHT_FILTER_NEAREST) const; PackedVector3Array generate_nav_mesh_source_geometry(const AABB &p_global_aabb, const bool p_require_nav = true) const;