[vlc-commits] [Git][videolan/vlc][master] 18 commits: viewpoint: add euler conversion operations
Steve Lhomme (@robUx4)
gitlab at videolan.org
Wed Dec 18 10:13:52 UTC 2024
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
3eea3b51 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
viewpoint: add euler conversion operations
This change prepare the viewpoint structure to use quaternions in
the core, while still using Euler angles in the current clients.
Euler angles have singularity issues, which in the convention
we chosed happens at the north and south poles. It didn't matter
when using usual yaw/pitch controllers since there are not a lot
of way to do this, but it definitively matters when the orientation
is already described by a quaternion. In particular, phone devices
or HMD headset with gyroscopes can easily reach the north and south
poles while having a different roll-like rotation, so can trigger
those bugs.
Using quaternions from the start won't trigger those issues in
cases where the control is not done through the euler-angle-like
paradigm, while still allowing clients to use it as long as they
handle the problem themselves.
The commits afterwards will make use of the function to interact with
the viewpoint.
- - - - -
32503234 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
vlccore: export vlc_viewpoint_from/to_euler
- - - - -
a3133526 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
lib/media_track.c: use vlc_viewpoint_to_euler
- - - - -
6e28e2ca by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
lib/video: use vlc_viewpoint_to_euler
- - - - -
997e614f by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
spatialaudio: use vlc_viewpoint_to_euler
- - - - -
4543df57 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
matroska_segment_parse: use vlc_viewpoint_from_euler
- - - - -
3e647695 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
essetup: use vlc_viewpoint_from_euler
- - - - -
22bacb69 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
win32/sensors: use vlc_viewpoint_from_euler
- - - - -
59e11c1d by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
jpeg: use vlc_viewpoint_from_euler
- - - - -
8f713938 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
input: use vlc_viewpoint_from/to_euler
- - - - -
c563181d by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
es_out: use vlc_viewpoint_to_euler
- - - - -
75ecaa43 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
hotkeys: use vlc_viewpoint_from_euler
- - - - -
0a5fa346 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
player: input: use vlc_viewpoint_to/from_euler
- - - - -
e3e8e447 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
display: call set_viewpoint on vout_SetDisplayViewpoint
We don't really need to check here, since most clients will just store
the values, and others would check the value.
- - - - -
8198bf66 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
input: use vlc_viewpoint_from/to_euler
- - - - -
81bec6c4 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
viewpoint: orientation: use from_euler/to_euler
- - - - -
af875474 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
viewpoint: add test for viewpoint-euler conversions
- - - - -
f7cc4f77 by Alexandre Janniaux at 2024-12-18T09:49:15+00:00
viewpoint: add test for conversion to 4x4 matrix
- - - - -
17 changed files:
- include/vlc_viewpoint.h
- lib/media_track.c
- lib/video.c
- modules/audio_filter/channel_mixer/spatialaudio.cpp
- modules/codec/jpeg.c
- modules/control/hotkeys.c
- modules/demux/mkv/matroska_segment_parse.cpp
- modules/demux/mp4/essetup.c
- modules/video_output/win32/sensors.cpp
- src/input/es_out.c
- src/input/input.c
- src/libvlccore.sym
- src/misc/viewpoint.c
- src/player/input.c
- src/video_output/display.c
- test/Makefile.am
- + test/src/misc/viewpoint.c
Changes:
=====================================
include/vlc_viewpoint.h
=====================================
@@ -71,4 +71,12 @@ static inline void vlc_viewpoint_clip( vlc_viewpoint_t *p_vp )
VLC_API
void vlc_viewpoint_to_4x4( const vlc_viewpoint_t *vp, float *matrix );
+VLC_API
+void vlc_viewpoint_from_euler( vlc_viewpoint_t *vp,
+ float yaw, float pitch, float roll );
+
+VLC_API
+void vlc_viewpoint_to_euler( const vlc_viewpoint_t *vp,
+ float *yaw, float *pitch, float *roll );
+
#endif /* VLC_VIEWPOINT_H_ */
=====================================
lib/media_track.c
=====================================
@@ -86,9 +86,10 @@ libvlc_media_trackpriv_from_es( libvlc_media_trackpriv_t *trackpriv,
( es->video.projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD ) );
track->video->i_projection = (int) es->video.projection_mode;
- track->video->pose.f_yaw = es->video.pose.yaw;
- track->video->pose.f_pitch = es->video.pose.pitch;
- track->video->pose.f_roll = es->video.pose.roll;
+ vlc_viewpoint_to_euler(&es->video.pose,
+ &track->video->pose.f_yaw,
+ &track->video->pose.f_pitch,
+ &track->video->pose.f_roll);
track->video->pose.f_field_of_view = es->video.pose.fov;
assert( es->video.multiview_mode >= MULTIVIEW_2D &&
=====================================
lib/video.c
=====================================
@@ -287,12 +287,11 @@ int libvlc_video_update_viewpoint( libvlc_media_player_t *p_mi,
const libvlc_video_viewpoint_t *p_viewpoint,
bool b_absolute )
{
- vlc_viewpoint_t update = {
- .yaw = p_viewpoint->f_yaw,
- .pitch = p_viewpoint->f_pitch,
- .roll = p_viewpoint->f_roll,
- .fov = p_viewpoint->f_field_of_view,
- };
+ vlc_viewpoint_t update = { .fov = p_viewpoint->f_field_of_view };
+ vlc_viewpoint_from_euler( &update,
+ p_viewpoint->f_yaw,
+ p_viewpoint->f_pitch,
+ p_viewpoint->f_roll);
enum vlc_player_whence whence = b_absolute ? VLC_PLAYER_WHENCE_ABSOLUTE
: VLC_PLAYER_WHENCE_RELATIVE;
=====================================
modules/audio_filter/channel_mixer/spatialaudio.cpp
=====================================
@@ -272,10 +272,13 @@ static void ChangeViewpoint( filter_t *p_filter, const vlc_viewpoint_t *p_vp)
{
filter_spatialaudio *p_sys = reinterpret_cast<filter_spatialaudio *>(p_filter->p_sys);
+ float yaw, pitch, roll;
+ vlc_viewpoint_to_euler(p_vp, &yaw, &pitch, &roll);
+
#define RAD(d) ((float) ((d) * M_PI / 180.f))
- p_sys->f_teta = -RAD(p_vp->yaw);
- p_sys->f_phi = RAD(p_vp->pitch);
- p_sys->f_roll = RAD(p_vp->roll);
+ p_sys->f_teta = -RAD(yaw);
+ p_sys->f_phi = RAD(pitch);
+ p_sys->f_roll = RAD(roll);
if (p_vp->fov >= FIELD_OF_VIEW_DEGREES_DEFAULT)
p_sys->f_zoom = 0.f; // no unzoom as it does not really make sense.
=====================================
modules/codec/jpeg.c
=====================================
@@ -288,31 +288,26 @@ static bool jpeg_ParseXMP(const uint8_t *p_buf, size_t i_buf,
fmt->projection_mode = PROJECTION_MODE_EQUIRECTANGULAR;
/* pose handling */
+ float yaw = 0.f, pitch = 0.f, roll = 0.f;
float value;
if (getRDFFloat(psz_rdf, &value, "PoseHeadingDegrees") &&
value >= 0.f && value <= 360.f)
- fmt->pose.yaw = value;
+ yaw = value;
- if (getRDFFloat(psz_rdf, &value, "PosePitchDegrees"))
- fmt->pose.pitch = value;
-
- if (getRDFFloat(psz_rdf, &value, "PoseRollDegrees"))
- fmt->pose.roll = value;
+ getRDFFloat(psz_rdf, &pitch, "PosePitchDegrees");
+ getRDFFloat(psz_rdf, &roll, "PoseRollDegrees");
/* initial view */
- if (getRDFFloat(psz_rdf, &value, "InitialViewHeadingDegrees"))
- fmt->pose.yaw = value;
-
- if (getRDFFloat(psz_rdf, &value, "InitialViewPitchDegrees"))
- fmt->pose.pitch = value;
-
- if (getRDFFloat(psz_rdf, &value, "InitialViewRollDegrees"))
- fmt->pose.roll = value;
+ getRDFFloat(psz_rdf, &yaw, "InitialViewHeadingDegrees");
+ getRDFFloat(psz_rdf, &pitch, "InitialViewPitchDegrees");
+ getRDFFloat(psz_rdf, &roll, "InitialViewRollDegrees");
if (getRDFFloat(psz_rdf, &value, "InitialHorizontalFOVDegrees") &&
value >= FIELD_OF_VIEW_DEGREES_MIN && value <= FIELD_OF_VIEW_DEGREES_MAX)
fmt->pose.fov = value;
+ vlc_viewpoint_from_euler(&fmt->pose, yaw, pitch, roll);
+
free(psz_rdf);
return true;
=====================================
modules/control/hotkeys.c
=====================================
@@ -575,7 +575,7 @@ PLAYER_ACTION_HANDLER(Navigate)
PLAYER_ACTION_HANDLER(Viewpoint)
{
VLC_UNUSED(intf);
- vlc_viewpoint_t viewpoint;
+ vlc_viewpoint_t viewpoint = { .fov = 0.f };
switch (action_id)
{
case ACTIONID_VIEWPOINT_FOV_IN:
@@ -585,10 +585,12 @@ PLAYER_ACTION_HANDLER(Viewpoint)
viewpoint.fov = +1.f;
break;
case ACTIONID_VIEWPOINT_ROLL_CLOCK:
- viewpoint.roll = -1.f;
+ vlc_viewpoint_from_euler(&viewpoint,
+ 0.f, 0.f, -1.f);
break;
case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
- viewpoint.roll = +1.f;
+ vlc_viewpoint_from_euler(&viewpoint,
+ 0.f, 0.f, 1.f);
break;
default:
vlc_assert_unreachable();
@@ -1109,11 +1111,11 @@ MouseMovedCallback(vlc_object_t *obj, char const *var,
{
int i_horizontal = newval.coords.x - sys->vrnav.x;
int i_vertical = newval.coords.y - sys->vrnav.y;
- vlc_viewpoint_t viewpoint =
- {
- .yaw = -i_horizontal * 0.05f,
- .pitch = -i_vertical * 0.05f,
- };
+ vlc_viewpoint_t viewpoint = { .fov = 0.f };
+ vlc_viewpoint_from_euler(&viewpoint,
+ -i_horizontal * 0.05f, /* yaw */
+ -i_vertical * 0.05f, /* pitch */
+ 0); /* roll */
vlc_player_Lock(player);
vlc_player_UpdateViewpoint(player, &viewpoint,
VLC_PLAYER_WHENCE_RELATIVE);
=====================================
modules/demux/mkv/matroska_segment_parse.cpp
=====================================
@@ -257,8 +257,14 @@ void matroska_segment_c::ParseTrackEntry( const KaxTrackEntry *m )
unsigned int chroma_sit_horizontal;
} track_video_info;
+ struct {
+ float yaw;
+ float pitch;
+ float roll;
+ } pose;
+
} metadata_payload = {
- this, p_track, &sys.demuxer, bSupported, 3, { }
+ this, p_track, &sys.demuxer, bSupported, 3, { }, { }
};
MKV_SWITCH_CREATE( EbmlTypeDispatcher, MetaDataHandlers, MetaDataCapture )
@@ -623,17 +629,17 @@ void matroska_segment_c::ParseTrackEntry( const KaxTrackEntry *m )
E_CASE( KaxVideoProjectionPoseYaw, pose )
{
ONLY_FMT(VIDEO);
- vars.tk->fmt.video.pose.yaw = static_cast<float>( pose );
+ vars.pose.yaw = static_cast<float>( pose );
}
E_CASE( KaxVideoProjectionPosePitch, pose )
{
ONLY_FMT(VIDEO);
- vars.tk->fmt.video.pose.pitch = static_cast<float>( pose );
+ vars.pose.pitch = static_cast<float>( pose );
}
E_CASE( KaxVideoProjectionPoseRoll, pose )
{
ONLY_FMT(VIDEO);
- vars.tk->fmt.video.pose.roll = static_cast<float>( pose );
+ vars.pose.roll = static_cast<float>( pose );
}
#endif
E_CASE( KaxVideoFlagInterlaced, fint ) // UNUSED
@@ -1059,6 +1065,11 @@ void matroska_segment_c::ParseTrackEntry( const KaxTrackEntry *m )
MetaDataHandlers::Dispatcher().iterate ( m->begin(), m->end(), &metadata_payload );
+ vlc_viewpoint_from_euler( &p_track->fmt.video.pose,
+ metadata_payload.pose.yaw,
+ metadata_payload.pose.pitch,
+ metadata_payload.pose.roll);
+
if( p_track->i_number == 0 )
{
msg_Warn( &sys.demuxer, "Missing KaxTrackNumber, discarding track!" );
=====================================
modules/demux/mp4/essetup.c
=====================================
@@ -402,9 +402,10 @@ int SetupVideoES( demux_t *p_demux, const mp4_track_t *p_track, const MP4_Box_t
const MP4_Box_t *p_prhd = MP4_BoxGet( p_sample, "sv3d/proj/prhd" );
if (p_prhd && BOXDATA(p_prhd))
{
- p_fmt->video.pose.yaw = BOXDATA(p_prhd)->f_pose_yaw_degrees;
- p_fmt->video.pose.pitch = BOXDATA(p_prhd)->f_pose_pitch_degrees;
- p_fmt->video.pose.roll = BOXDATA(p_prhd)->f_pose_roll_degrees;
+ vlc_viewpoint_from_euler(&p_fmt->video.pose,
+ BOXDATA(p_prhd)->f_pose_yaw_degrees,
+ BOXDATA(p_prhd)->f_pose_pitch_degrees,
+ BOXDATA(p_prhd)->f_pose_roll_degrees);
}
const MP4_Box_t *p_equi = MP4_BoxGet( p_sample, "sv3d/proj/equi" );
=====================================
modules/video_output/win32/sensors.cpp
=====================================
@@ -122,14 +122,18 @@ public:
PropVariantClear(&pvRot);
}
- vlc_viewpoint_t vp = {
+ // TODO: use current_pos directly?
+ /* Zero initialize vp for field of view. */
+ vlc_viewpoint_t vp {};
+ vp.fov = 0.f;
+ vlc_viewpoint_from_euler(&vp,
old_pos.yaw - current_pos.yaw,
old_pos.pitch - current_pos.pitch,
- old_pos.roll - current_pos.roll,
- 0.0f
- };
+ old_pos.roll - current_pos.roll
+ );
if (owner.viewpoint_moved)
owner.viewpoint_moved(owner.sys, &vp);
+
return S_OK;
}
=====================================
src/input/es_out.c
=====================================
@@ -4540,12 +4540,15 @@ static void EsOutUpdateInfo(es_out_sys_t *p_sys,
info_category_AddInfo( p_cat, _("Projection"), "%s",
vlc_gettext(psz_loc_name) );
+ float yaw, pitch, roll;
+ vlc_viewpoint_to_euler(&fmt->video.pose, &yaw, &pitch, &roll);
+
info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Yaw"),
- "%.2f", fmt->video.pose.yaw );
+ "%.2f", yaw );
info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Pitch"),
- "%.2f", fmt->video.pose.pitch );
+ "%.2f", pitch );
info_category_AddInfo( p_cat, vlc_pgettext("ViewPoint", "Roll"),
- "%.2f", fmt->video.pose.roll );
+ "%.2f", roll );
info_category_AddInfo( p_cat,
vlc_pgettext("ViewPoint", "Field of view"),
"%.2f", fmt->video.pose.fov );
=====================================
src/input/input.c
=====================================
@@ -1504,9 +1504,14 @@ static size_t ControlGetReducedIndexLocked( input_thread_t *p_input,
}
else if ( i_ct == INPUT_CONTROL_UPDATE_VIEWPOINT )
{
- c->param.viewpoint.yaw += prev_control->param.viewpoint.yaw;
- c->param.viewpoint.pitch += prev_control->param.viewpoint.pitch;
- c->param.viewpoint.roll += prev_control->param.viewpoint.roll;
+ float yaw1, pitch1, roll1;
+ float yaw2, pitch2, roll2;
+ vlc_viewpoint_to_euler(&prev_control->param.viewpoint, &yaw1, &pitch1, &roll1);
+ vlc_viewpoint_to_euler(&c->param.viewpoint, &yaw2, &pitch2, &roll2);
+ vlc_viewpoint_from_euler(&c->param.viewpoint,
+ yaw1 + yaw2,
+ pitch1 + pitch2,
+ roll1 + roll2);
c->param.viewpoint.fov += prev_control->param.viewpoint.fov;
return sys->i_control - 1;
}
@@ -2160,10 +2165,18 @@ static bool Control( input_thread_t *p_input,
else
{
priv->viewpoint_changed = true;
- priv->viewpoint.yaw += param.viewpoint.yaw;
- priv->viewpoint.pitch += param.viewpoint.pitch;
- priv->viewpoint.roll += param.viewpoint.roll;
- priv->viewpoint.fov += param.viewpoint.fov;
+ float previous[3], update[3];
+ vlc_viewpoint_to_euler(&priv->viewpoint, &previous[0],
+ &previous[1], &previous[2]);
+ vlc_viewpoint_to_euler(¶m.viewpoint, &update[0],
+ &update[1], &update[2]);
+
+ /* Clip the pitch to avoid glitches at singularity points. */
+ vlc_viewpoint_from_euler(&priv->viewpoint,
+ previous[0] + update[0],
+ VLC_CLIP(previous[1] + update[1], -85.f, 85.f),
+ previous[2] + update[2]);
+ priv->viewpoint.fov += param.viewpoint.fov;
}
ViewpointApply( p_input );
=====================================
src/libvlccore.sym
=====================================
@@ -1022,6 +1022,8 @@ vlc_media_tree_Find
vlc_media_tree_Preparse
vlc_viewpoint_to_4x4
vlc_viewpoint_from_orientation
+vlc_viewpoint_from_euler
+vlc_viewpoint_to_euler
vlc_video_context_Create
vlc_video_context_Release
vlc_video_context_GetType
=====================================
src/misc/viewpoint.c
=====================================
@@ -79,27 +79,37 @@ void vlc_viewpoint_from_orientation(vlc_viewpoint_t *vp,
default:
case ORIENT_NORMAL:
case ORIENT_HFLIPPED:
- *vp = (vlc_viewpoint_t) { .fov = vp->fov };
+ vlc_viewpoint_from_euler(vp, 0.f, 0.f, 0.f);
break;
case ORIENT_ROTATED_90:
case ORIENT_ANTI_TRANSPOSED:
- *vp = (vlc_viewpoint_t) {
- .roll = -90.f, .fov = vp->fov
- };
+ vlc_viewpoint_from_euler(vp, 0.f, 0.f, -90.f);
break;
case ORIENT_ROTATED_180:
case ORIENT_VFLIPPED:
- *vp = (vlc_viewpoint_t) {
- .roll = -180.f, .fov = vp->fov
- };
+ vlc_viewpoint_from_euler(vp, 0.f, 0.f, -180.f);
break;
case ORIENT_ROTATED_270:
case ORIENT_TRANSPOSED:
- *vp = (vlc_viewpoint_t) {
- .roll = -270.f, .fov = vp->fov
- };
+ vlc_viewpoint_from_euler(vp, 0.f, 0.f, -270.f);
break;
}
}
+
+void vlc_viewpoint_from_euler(vlc_viewpoint_t *vp,
+ float yaw, float pitch, float roll)
+{
+ vp->yaw = yaw;
+ vp->pitch = pitch;
+ vp->roll = roll;
+}
+
+void vlc_viewpoint_to_euler(const vlc_viewpoint_t *vp,
+ float *yaw, float *pitch, float *roll)
+{
+ *yaw = vp->yaw;
+ *pitch = vp->pitch;
+ *roll = vp->roll;
+}
=====================================
src/player/input.c
=====================================
@@ -798,29 +798,33 @@ vlc_player_input_NavigationFallback(struct vlc_player_input *input, int nav_type
/* Handle Up/Down/Left/Right if the demux can't navigate */
vlc_viewpoint_t vp = { 0 };
+ bool viewpoint_updated = true;
+ float yaw = 0.f, pitch = 0.f, roll = 0.f;
+
int vol_direction = 0;
int seek_direction = 0;
switch (nav_type)
{
case INPUT_CONTROL_NAV_UP:
vol_direction = 1;
- vp.pitch = -1.f;
+ pitch = -1.f;
break;
case INPUT_CONTROL_NAV_DOWN:
vol_direction = -1;
- vp.pitch = 1.f;
+ pitch = 1.f;
break;
case INPUT_CONTROL_NAV_LEFT:
seek_direction = -1;
- vp.yaw = -1.f;
+ yaw = -1.f;
break;
case INPUT_CONTROL_NAV_RIGHT:
seek_direction = 1;
- vp.yaw = 1.f;
+ yaw = 1.f;
break;
case INPUT_CONTROL_NAV_ACTIVATE:
case INPUT_CONTROL_NAV_POPUP:
case INPUT_CONTROL_NAV_MENU:
+ viewpoint_updated = false;
return;
default:
vlc_assert_unreachable();
@@ -838,8 +842,11 @@ vlc_player_input_NavigationFallback(struct vlc_player_input *input, int nav_type
}
free(vouts);
- if (viewpoint_ch)
+ if (viewpoint_ch && viewpoint_updated)
+ {
+ vlc_viewpoint_from_euler(&vp, yaw, pitch, roll);
vlc_player_input_UpdateViewpoint(input, &vp, VLC_PLAYER_WHENCE_RELATIVE);
+ }
else if (seek_direction != 0)
{
/* Seek or change volume if the input doesn't have navigation or viewpoint */
=====================================
src/video_output/display.c
=====================================
@@ -707,21 +707,14 @@ void vout_SetDisplayViewpoint(vout_display_t *vd,
const vlc_viewpoint_t *p_viewpoint)
{
vout_display_priv_t *osys = container_of(vd, vout_display_priv_t, display);
+ vlc_viewpoint_t old_vp = osys->cfg.viewpoint;
+ osys->cfg.viewpoint = *p_viewpoint;
- if (osys->cfg.viewpoint.yaw != p_viewpoint->yaw ||
- osys->cfg.viewpoint.pitch != p_viewpoint->pitch ||
- osys->cfg.viewpoint.roll != p_viewpoint->roll ||
- osys->cfg.viewpoint.fov != p_viewpoint->fov) {
- vlc_viewpoint_t old_vp = osys->cfg.viewpoint;
-
- osys->cfg.viewpoint = *p_viewpoint;
-
- if (vd->ops->set_viewpoint)
- {
- if (vd->ops->set_viewpoint(vd, &osys->cfg.viewpoint)) {
- msg_Err(vd, "Failed to change Viewpoint");
- osys->cfg.viewpoint = old_vp;
- }
+ if (vd->ops->set_viewpoint)
+ {
+ if (vd->ops->set_viewpoint(vd, &osys->cfg.viewpoint)) {
+ msg_Err(vd, "Failed to change Viewpoint");
+ osys->cfg.viewpoint = old_vp;
}
}
}
=====================================
test/Makefile.am
=====================================
@@ -40,6 +40,7 @@ check_PROGRAMS = \
test_src_misc_epg \
test_src_misc_keystore \
test_src_misc_image \
+ test_src_misc_viewpoint \
test_src_video_output \
test_src_video_output_opengl \
test_modules_lua_extension \
@@ -197,6 +198,8 @@ test_src_misc_keystore_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_src_misc_image_cvpx_SOURCES = src/misc/image_cvpx.c
test_src_misc_image_cvpx_LDADD = $(LIBVLCCORE) $(LIBVLC) ../modules/libvlc_vtutils.la
test_src_misc_image_cvpx_LDFLAGS = $(AM_LDFLAGS) -Wl,-framework,CoreVideo
+test_src_misc_viewpoint_SOURCES = src/misc/viewpoint.c
+test_src_misc_viewpoint_LDADD = $(LIBVLCCORE) $(LIBM)
test_src_interface_dialog_SOURCES = src/interface/dialog.c
test_src_interface_dialog_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_src_media_source_LDADD = $(LIBVLCCORE) $(LIBVLC)
=====================================
test/src/misc/viewpoint.c
=====================================
@@ -0,0 +1,221 @@
+/*****************************************************************************
+ * viewpoint.c: test for viewpoint
+ *****************************************************************************
+ * Copyright (C) 2019 VLC Authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include <vlc_viewpoint.h>
+#include <math.h>
+#include "../../libvlc/test.h"
+
+
+static void mat4x4_for_angles( float *m, float *angles )
+{
+ const float yaw = -angles[0] * (float)M_PI / 180.f;
+ const float pitch = -angles[1] * (float)M_PI / 180.f;
+ const float roll = -angles[2] * (float)M_PI / 180.f;
+
+ float s, c;
+
+ s = sinf(pitch);
+ c = cosf(pitch);
+ const float x_rot[4][4] = {
+ { 1.f, 0.f, 0.f, 0.f },
+ { 0.f, c, -s, 0.f },
+ { 0.f, s, c, 0.f },
+ { 0.f, 0.f, 0.f, 1.f } };
+
+ s = sinf(yaw);
+ c = cosf(yaw);
+ const float y_rot[4][4] = {
+ { c, 0.f, s, 0.f },
+ { 0.f, 1.f, 0.f, 0.f },
+ { -s, 0.f, c, 0.f },
+ { 0.f, 0.f, 0.f, 1.f } };
+
+ s = sinf(roll);
+ c = cosf(roll);
+ const float z_rot[4][4] = {
+ { c, s, 0.f, 0.f },
+ { -s, c, 0.f, 0.f },
+ { 0.f, 0.f, 1.f, 0.f },
+ { 0.f, 0.f, 0.f, 1.f } };
+
+ /**
+ * Column-major matrix multiplication mathematically equal to
+ * z_rot * x_rot * y_rot
+ */
+ memset(m, 0, 16 * sizeof(float));
+ for (int i=0; i<4; ++i)
+ for (int j=0; j<4; ++j)
+ for (int k=0; k<4; ++k)
+ for (int l=0; l<4; ++l)
+ m[4*i+l] += y_rot[i][j] * x_rot[j][k] * z_rot[k][l];
+}
+
+static bool
+compare_angles(float epsilon, const float a1[3], const float a2[3])
+{
+ const float MAX_YAW = 180.f;
+ const float MAX_PITCH = 360.f;
+ const float MAX_ROLL = 180.f;
+
+ /* We add MAX_YAW, MAX_PITCH and MAX_ROLL to ensure the value for fmodf
+ * will stay positive. The value will be between 0 and {MAX_ANGLE}. */
+ float dy = fmodf(MAX_YAW + (a1[0] - a2[0]), MAX_YAW);
+ float dp = fmodf(MAX_PITCH + (a1[1] - a2[1]), MAX_PITCH);
+ float dr = fmodf(MAX_ROLL + (a1[2] - a2[2]), MAX_ROLL);
+
+ /* Check the two borders of the torus, 0.f and 180.f or 360.f depending
+ * on the range of the compared value. */
+ return (dy < epsilon || MAX_YAW - dy < epsilon) &&
+ (dp < epsilon || MAX_PITCH - dp < epsilon) &&
+ (dr < epsilon || MAX_ROLL - dr < epsilon);
+}
+
+/**
+ * Execute conversion back and forth from Euler angles to quaternion.
+ * Check that the original angles are preserved by the conversion methods.
+ */
+static bool
+reciprocal_euler(float epsilon, float yaw, float pitch, float roll)
+{
+ vlc_viewpoint_t vp;
+ vlc_viewpoint_from_euler(&vp, yaw, pitch, roll);
+
+ float yaw2, pitch2, roll2;
+ vlc_viewpoint_to_euler(&vp, &yaw2, &pitch2, &roll2);
+
+ fprintf(stderr, "==========================================\n");
+ fprintf(stderr, "original: yaw=%f, pitch=%f, roll=%f\n", yaw, pitch, roll);
+ fprintf(stderr, "converted: yaw=%f, pitch=%f, roll=%f\n", yaw2, pitch2, roll2);
+ fprintf(stderr, "==========================================\n");
+
+ return compare_angles(epsilon,
+ (float[]){yaw, pitch, roll},
+ (float[]){yaw, pitch, roll});
+}
+
+static void test_conversion()
+{
+ const float epsilon = 0.1f;
+ assert(reciprocal_euler(epsilon, 0.f, 0.f, 0.f));
+ assert(reciprocal_euler(epsilon, 45.f, 0.f, 0.f));
+ assert(reciprocal_euler(epsilon, 0.f, 45.f, 0.f));
+ assert(reciprocal_euler(epsilon, 0.f, 0.f, 45.f));
+ assert(reciprocal_euler(epsilon, 45.f, 45.f, 0.f));
+ assert(reciprocal_euler(epsilon, 0.f, 45.f, 45.f));
+ assert(reciprocal_euler(epsilon, 45.f, 45.f, 45.f));
+
+ assert(reciprocal_euler(epsilon, -45.f, 0.f, 0.f));
+ assert(reciprocal_euler(epsilon, 0.f, -45.f, 0.f));
+ assert(reciprocal_euler(epsilon, 0.f, 0.f, -45.f));
+ assert(reciprocal_euler(epsilon, -45.f, -45.f, 0.f));
+ assert(reciprocal_euler(epsilon, 0.f, -45.f, -45.f));
+ assert(reciprocal_euler(epsilon, -45.f, -45.f, -45.f));
+}
+
+static int fuzzy_memcmp(const float *a, const float *b,
+ size_t size, float epsilon)
+{
+ for (size_t i=0; i < size; ++i)
+ {
+ if (fabs(a[i]-b[i]) > epsilon)
+ {
+ fprintf(stderr, "Difference at %zu, a[%zu]=%f, b[%zu]=%f\n",
+ i, i, a[i], i, b[i]);
+ return a[i] < b[i] ? -1 : 1;
+ }
+ }
+ return 0;
+}
+
+struct example_mat4x4
+{
+ float angles[3];
+};
+
+struct example_mat4x4 examples_mat4x4[] = {
+ { .angles = { 0.f, 45.f, 45.f } },
+ { .angles = { 45.f, 45.f, 0.f } },
+ { .angles = { 45.f, 0.f, 0.f } },
+ { .angles = { -45.f, 0.f, 0.f } },
+ { .angles = { 0.f, 45.f, 0.f } },
+ { .angles = { 0.f, 0.f, 45.f } },
+ { .angles = { 90.f, 0.f, 0.f } },
+ { .angles = { 0.f, 90.f, 0.f } },
+ { .angles = { 0.f, 0.f, 90.f } },
+ { .angles = { 90.f, 90.f, 0.f } },
+ { .angles = { 90.f, 0.f, 90.f } },
+ { .angles = { 0.f, 90.f, 90.f } },
+ { .angles = { 90.f, 90.f, 90.f } },
+};
+
+static void
+test_conversion_viewpoint_mat4x4()
+{
+ const float epsilon = 0.1f;
+ vlc_viewpoint_t vp;
+ float mat[16];
+
+#define MATLINE "[%f %f %f %f]\n"
+#define MAT MATLINE MATLINE MATLINE MATLINE
+#define printmat(title, mat) \
+ fprintf(stderr, title ":\n" MAT "\n", \
+ mat[0], mat[1], mat[2], mat[3], \
+ mat[4], mat[5], mat[6], mat[7], \
+ mat[8], mat[9], mat[10], mat[11], \
+ mat[12], mat[13], mat[14], mat[15]);
+
+ bool success = true;
+ for (size_t i=0; i<ARRAY_SIZE(examples_mat4x4); ++i)
+ {
+ struct example_mat4x4 *ex = &examples_mat4x4[i];
+ vlc_viewpoint_from_euler(&vp,
+ ex->angles[0],
+ ex->angles[1],
+ ex->angles[2]);
+ vlc_viewpoint_to_4x4(&vp, mat);
+ fprintf(stderr, "angles: %f %f %f\n",
+ ex->angles[0], ex->angles[1],
+ ex->angles[2]);
+
+ float expect_mat[16];
+ mat4x4_for_angles(expect_mat, ex->angles);
+
+ bool diff = fuzzy_memcmp(mat, expect_mat,
+ ARRAY_SIZE(mat), epsilon);
+ if (!diff)
+ continue;
+
+ printmat("EXPECT COMPUTED", expect_mat);
+ printmat("RESULT", mat);
+ success = false;
+ }
+
+ assert(success);
+}
+
+int main( void )
+{
+ test_init();
+
+ test_conversion();
+ test_conversion_viewpoint_mat4x4();
+
+ return 0;
+}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6d56eb0706560080ec94e788628af33f1c982b63...f7cc4f770613f6f30a7a4348f452466ff53b46ba
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6d56eb0706560080ec94e788628af33f1c982b63...f7cc4f770613f6f30a7a4348f452466ff53b46ba
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list