diff --git a/src/libs/gltf/gltf_view/lv_gltf.h b/src/libs/gltf/gltf_view/lv_gltf.h index c5a02aa188..a88bc621f0 100644 --- a/src/libs/gltf/gltf_view/lv_gltf.h +++ b/src/libs/gltf/gltf_view/lv_gltf.h @@ -15,6 +15,7 @@ #if LV_USE_GLTF #include "../../../misc/lv_types.h" +#include "../../../misc/lv_area.h" #include "../../../misc/lv_color.h" #include "../gltf_data/lv_gltf_model.h" @@ -43,6 +44,19 @@ typedef enum { LV_GLTF_BG_MODE_ENVIRONMENT = 1, /** Environnement background*/ } lv_gltf_bg_mode_t; +typedef struct { + float x; + float y; + float z; +} lv_3dpoint_t; + +typedef struct { + lv_3dpoint_t origin; + lv_3dpoint_t direction; +} lv_3dplane_t; + +typedef lv_3dplane_t lv_3dray_t; + #define LV_GLTF_ANIM_SPEED_TENTH 100 #define LV_GLTF_ANIM_SPEED_QUARTER 250 #define LV_GLTF_ANIM_SPEED_HALF 500 @@ -130,12 +144,19 @@ float lv_gltf_get_pitch(const lv_obj_t * obj); void lv_gltf_set_distance(lv_obj_t * obj, float value); /** - * Get the camera distance from the focal point + * Get the camera distance scale factor from the focal point * @param obj pointer to a GLTF viewer object - * @return distance value + * @return distance scaling factor value */ float lv_gltf_get_distance(const lv_obj_t * obj); +/** + * Get the camera distance from the focal point in world units + * @param obj pointer to a GLTF viewer object + * @return world unit distance value + */ +float lv_gltf_get_world_distance(const lv_obj_t * obj); + /********************** * Viewport Functions **********************/ @@ -340,6 +361,54 @@ void lv_gltf_set_antialiasing_mode(lv_obj_t * obj, lv_gltf_aa_mode_t value); */ lv_gltf_aa_mode_t lv_gltf_get_antialiasing_mode(const lv_obj_t * obj); +/*********************** + * Raycasting Functions + ***********************/ + +/** + * Get a plane that faces the current view camera, centered some units in front of it + * @param obj pointer to a GLTF viewer object + * @param distance distance in front of the camera to set the plane, in world units. see lv_gltf_get_world_distance to get the auto-distance + * @return camera facing plane + */ +lv_3dplane_t lv_gltf_get_current_view_plane(lv_obj_t * obj, float distance); + +/** + * Get a plane that faces upward, centered at a given height + * @param obj pointer to a GLTF viewer object + * @param elevation elevation of the ground plane, in world units. this is usually zero + * @return ground plane + */ +lv_3dplane_t lv_gltf_get_ground_plane(float elevation); + +/** + * Calculates a ray originating from the camera and passing through the specified mouse position on the screen. + * @param obj pointer to a GLTF viewer object + * @param screen_pos screen co-ordinate, in pixels + * @return mouse point ray + */ +lv_3dray_t lv_gltf_get_ray_from_2d_coordinate(lv_obj_t * obj, const lv_point_t * screen_pos); + +/** + * Get the point that a given ray intersects with a specified plane at, if any + * @param ray the intersection test ray + * @param screen_y the plane to test ray intersection with + * @param collision_point output lv_3dpoint_t holder, values are only valid if true is the return value + * @return LV_RESULT_OK if intersection, LV_RESULT_INVALID if no intersection + */ +lv_result_t lv_gltf_intersect_ray_with_plane(const lv_3dray_t * ray, const lv_3dplane_t * plane, + lv_3dpoint_t * collision_point); + +/** + * Get the screen position of a 3d point + * @param obj pointer to a GLTF viewer object + * @param world_pos world position to convert + * @param lv_point_t the resulting point, in pixels. only valid if return value is true + * @return LV_RESULT_OK if conversion valid, LV_RESULT_INVALID if no valid conversion + */ +lv_result_t lv_gltf_world_to_screen(lv_obj_t * obj, const lv_3dpoint_t world_pos, lv_point_t * screen_pos); + + /********************** * MACROS **********************/ diff --git a/src/libs/gltf/gltf_view/lv_gltf_view.cpp b/src/libs/gltf/gltf_view/lv_gltf_view.cpp index bd47c76eeb..406bacb93a 100644 --- a/src/libs/gltf/gltf_view/lv_gltf_view.cpp +++ b/src/libs/gltf/gltf_view/lv_gltf_view.cpp @@ -209,6 +209,19 @@ float lv_gltf_get_distance(const lv_obj_t * obj) return viewer->desc.distance; } +float lv_gltf_get_world_distance(const lv_obj_t * obj) +{ + LV_ASSERT_NULL(obj); + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_gltf_t * viewer = (lv_gltf_t *)obj; + lv_gltf_view_desc_t * view_desc = &viewer->desc; + if(viewer->models.size == 0) { + return 0.0f; + } + lv_gltf_model_t * model = *(lv_gltf_model_t **)lv_array_at(&viewer->models, 0); + return (lv_gltf_data_get_radius(model) * LV_GLTF_DISTANCE_SCALE_FACTOR) * view_desc->distance; +} + void lv_gltf_set_animation_speed(lv_obj_t * obj, uint32_t value) { LV_ASSERT_NULL(obj); @@ -427,6 +440,116 @@ void lv_gltf_recenter(lv_obj_t * obj, lv_gltf_model_t * model) viewer->desc.focal_z = center_position[2]; } +lv_3dray_t lv_gltf_get_ray_from_2d_coordinate(lv_obj_t * obj, const lv_point_t * screen_pos) +{ + LV_ASSERT_NULL(obj); + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_gltf_t * viewer = (lv_gltf_t *)obj; + + float norm_mouse_x = (float)screen_pos->x / (float)(lv_obj_get_width(obj)); + float norm_mouse_y = (float)screen_pos->y / (float)(lv_obj_get_height(obj)); + + lv_3dray_t outray = {0}; + + fastgltf::math::fmat4x4 proj_mat = fastgltf::math::invert(fastgltf::math::fmat4x4(viewer->projection_matrix)); + + /* Convert mouse coordinates to NDC */ + float x = norm_mouse_x * 2.0f - 1.0f; + float y = 1.0f - (norm_mouse_y * 2.0f); + float z = -1.0f; /* Clip space z */ + + fastgltf::math::fvec4 clip_space_pos = fastgltf::math::fvec4(x, y, z, 1.f); + auto ray_eye = (proj_mat) * clip_space_pos; + ray_eye[2] = -1.0f; + ray_eye[3] = 0.0f; + + /* Calculate ray world direction */ + fastgltf::math::fvec4 ray_world = fastgltf::math::invert(viewer->view_matrix) * ray_eye; + auto ray_direction = fastgltf::math::normalize(fastgltf::math::fvec3(ray_world[0], ray_world[1], ray_world[2])); + + outray.direction = {ray_direction[0], ray_direction[1], ray_direction[2]}; + outray.origin = {viewer->camera_pos[0], viewer->camera_pos[1], viewer->camera_pos[2]}; + + return outray; +} + +lv_result_t lv_gltf_intersect_ray_with_plane(const lv_3dray_t * ray, const lv_3dplane_t * plane, + lv_3dpoint_t * collision_point) +{ + fastgltf::math::fvec3 plane_center = fastgltf::math::fvec3(plane->origin.x, plane->origin.y, plane->origin.z); + fastgltf::math::fvec3 plane_normal = fastgltf::math::fvec3(plane->direction.x, plane->direction.y, plane->direction.z); + fastgltf::math::fvec3 ray_start = fastgltf::math::fvec3(ray->origin.x, ray->origin.y, ray->origin.z); + fastgltf::math::fvec3 ray_direction = fastgltf::math::fvec3(ray->direction.x, ray->direction.y, ray->direction.z); + + float denom = fastgltf::math::dot(plane_normal, ray_direction); + if(fabs(denom) > 1e-6) { /* Check if the ray is not parallel to the plane */ + fastgltf::math::fvec3 diff = plane_center - ray_start; + float t = fastgltf::math::dot(diff, plane_normal) / denom; + + if(t >= 0) { /* Intersection occurs ahead of the ray origin */ + /* Calculate the collision point */ + (*collision_point).x = ray_start[0] + t * ray_direction[0]; + (*collision_point).y = ray_start[1] + t * ray_direction[1]; + (*collision_point).z = ray_start[2] + t * ray_direction[2]; + return LV_RESULT_OK; /* Collision point found */ + } + } + return LV_RESULT_INVALID; /* No intersection */ +} + +lv_3dplane_t lv_gltf_get_ground_plane(float elevation) +{ + lv_3dplane_t outplane = {0}; + outplane.origin = {0.0f, elevation, 0.0f}; + outplane.direction = {0.0f, 1.0f, 0.0f}; + return outplane; +} + +lv_3dplane_t lv_gltf_get_current_view_plane(lv_obj_t * obj, float distance) +{ + LV_ASSERT_NULL(obj); + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_gltf_t * viewer = (lv_gltf_t *)obj; + lv_3dplane_t outplane = {0}; + + /* Forward vector is the third column of the matrix */ + auto forward = fastgltf::math::fvec3(viewer->view_matrix[0][2], viewer->view_matrix[1][2], viewer->view_matrix[2][2]); + forward = fastgltf::math::normalize(forward); + + /* Calculate the plane center */ + const auto & camera_pos = viewer->camera_pos; + auto plane_pos = fastgltf::math::fvec3(camera_pos[0], camera_pos[1], camera_pos[2]) - forward * distance; + outplane.origin = {plane_pos[0], plane_pos[1], plane_pos[2]}; + outplane.direction = {-forward[0], -forward[1], -forward[2]}; + return outplane; +} + +lv_result_t lv_gltf_world_to_screen(lv_obj_t * obj, const lv_3dpoint_t world_pos, lv_point_t * screen_pos) +{ + LV_ASSERT_NULL(obj); + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_gltf_t * viewer = (lv_gltf_t *)obj; + + fastgltf::math::fvec4 world_position_h = fastgltf::math::fvec4(world_pos.x, world_pos.y, world_pos.z, 1.0f); + fastgltf::math::fvec4 clip_space_pos = viewer->projection_matrix * viewer->view_matrix * world_position_h; + + /* Check for perspective division (w must not be zero) */ + if(clip_space_pos[3] == 0.0f) { + screen_pos->x = -1; + screen_pos->y = -1; + return LV_RESULT_INVALID; /* Position is not valid for screen mapping */ + } + + clip_space_pos /= clip_space_pos[3]; + float norm_screen_x = clip_space_pos[0] * 0.5f + 0.5f; + float norm_screen_y = 0.5f - (clip_space_pos[1] * 0.5f); + int32_t win_width = lv_obj_get_width(obj); + int32_t win_height = lv_obj_get_height(obj); + screen_pos->x = norm_screen_x * win_width; + screen_pos->y = norm_screen_y * win_height; + return LV_RESULT_OK; +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/libs/gltf/gltf_view/lv_gltf_view_internal.h b/src/libs/gltf/gltf_view/lv_gltf_view_internal.h index b4c811fdf1..7a91785853 100644 --- a/src/libs/gltf/gltf_view/lv_gltf_view_internal.h +++ b/src/libs/gltf/gltf_view/lv_gltf_view_internal.h @@ -25,6 +25,7 @@ * DEFINES *********************/ +#define LV_GLTF_DISTANCE_SCALE_FACTOR 2.5f /********************** * TYPEDEFS diff --git a/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp b/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp index f270abea0f..42ed0c18cd 100644 --- a/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp +++ b/src/libs/gltf/gltf_view/lv_gltf_view_render.cpp @@ -1035,7 +1035,7 @@ static void setup_view_proj_matrix(lv_gltf_t * viewer, lv_gltf_view_desc_t * vie bool transmission_pass) { auto b_radius = lv_gltf_data_get_radius(gltf_data); - float radius = b_radius * 2.5; + float radius = b_radius * LV_GLTF_DISTANCE_SCALE_FACTOR; radius *= view_desc->distance; fastgltf::math::fvec3 rcam_dir = fastgltf::math::fvec3(0.0f, 0.0f, 1.0f);