#include "psx_openxr.h" #include #include #include "xr_surface_math.h" /* Evaluate side effects and failures even when NDEBUG is defined. */ #define CHECK(expr) do { \ if (!(expr)) { \ fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #expr); \ return 1; \ } \ } while (0) int main(void) { /* Shared head center gives coherent binocular geometry. Billboarding must * ignore attachment roll, respond to head motion, and reject near-plane * crossings. Common translation of eye/head/surface preserves projection. */ PSXModOpenXRSurface plane={0};plane.orientation_xyzw[3]=1; plane.position_m[2]=-1;plane.size_m[0]=plane.size_m[1]=.2f; float eye[7]={-.03f,0,0,0,0,0,1},head[3]={0,0,0}; const float fov[4]={-.785398f,.785398f,.785398f,-.785398f}; float left[36],right[36],other[36]; CHECK(xr_surface_vertices_head(&plane,eye,fov,head,left)); eye[0]=.03f;CHECK(xr_surface_vertices_head(&plane,eye,fov,head,right)); CHECK(left[0]>right[0]); plane.flags=PSX_MOD_OPENXR_SURFACE_FACE_HEAD; CHECK(xr_surface_vertices_head(&plane,eye,fov,head,left)); plane.orientation_xyzw[0]=1;plane.orientation_xyzw[3]=0; CHECK(xr_surface_vertices_head(&plane,eye,fov,head,right)); for(int i=0;i<36;i++)CHECK(fabsf(left[i]-right[i])<.00001f); head[0]=.5f;CHECK(xr_surface_vertices_head(&plane,eye,fov,head,other)); CHECK(fabsf(left[0]-other[0])+fabsf(left[3]-other[3])>.001f); head[0]=0;plane.position_m[0]+=2;plane.position_m[1]+=3; eye[0]+=2;eye[1]+=3;head[0]+=2;head[1]+=3; CHECK(xr_surface_vertices_head(&plane,eye,fov,head,other)); for(int i=0;i<36;i++)CHECK(fabsf(left[i]-other[i])<.00001f); plane.position_m[2]=.1f;CHECK(!xr_surface_vertices_head(&plane,eye,fov,head,other)); plane.position_m[0]=head[0];plane.position_m[1]=head[1];plane.position_m[2]=head[2]; CHECK(!xr_surface_vertices_head(&plane,eye,fov,head,other)); uint8_t pixel[4]={255,0,0,255}; PSXModOpenXRSurface surface={0};surface.struct_size=sizeof surface; surface.width_px=surface.height_px=1;surface.rgba=pixel; surface.size_m[0]=surface.size_m[1]=.1f;surface.orientation_xyzw[3]=1; CHECK(psx_openxr_surface_valid(&surface));CHECK(!psx_openxr_surface(&surface)); surface.flags=PSX_MOD_OPENXR_SURFACE_FACE_HEAD;CHECK(psx_openxr_surface_valid(&surface)); surface.flags=2;CHECK(!psx_openxr_surface_valid(&surface));surface.flags=0; surface.id=2;CHECK(!psx_openxr_surface_valid(&surface));surface.id=0; surface.position_m[0]=NAN;CHECK(!psx_openxr_surface_valid(&surface));surface.position_m[0]=0; surface.orientation_xyzw[3]=0;CHECK(!psx_openxr_surface_valid(&surface));surface.orientation_xyzw[3]=1; surface.width_px=1025;CHECK(!psx_openxr_surface_valid(&surface));surface.width_px=1; surface.rgba=NULL;CHECK(!psx_openxr_surface_valid(&surface)); /* Interpolation gate: only a compiled, enabled, initialized, RUNNING session * suspends presentation interpolation; every other state is a no-op. */ CHECK(psx_openxr_interp_gate(1,1,1,1)); for(unsigned m=0;m<15;m++) CHECK(!psx_openxr_interp_gate(m&1,(m>>1)&1,(m>>2)&1,(m>>3)&1)); CHECK(!psx_openxr_session_active()); /* nothing enabled in this process */ /* UI requests require a live tracked frame. Off/unavailable requests * must leave submission state neutral, including invalid dimensions. */ CHECK(!psx_openxr_quad(2,2,1.5)); CHECK(!psx_openxr_quad(NAN,2,1.5)); CHECK(!psx_openxr_quad(.1,2,1.5)); CHECK(!psx_openxr_quad(2,0,1.5)); CHECK(!psx_openxr_quad(0,0,0)); PSXOpenXRStats stats;psx_openxr_stats(&stats); CHECK(!stats.submitted_layer && !stats.quad_submitted); CHECK(!stats.submitted_source && !stats.native_submitted && !stats.submitted_native_frame); PSXModOpenXRInput p={0},s={0}; p.struct_size=sizeof p; CHECK(psx_openxr_input(&p) && !p.focused && p.stick[0][0]==0 && !p.synthetic); s.struct_size=sizeof s;s.focused=1;s.active[0]=s.active[1]=1; s.stick[0][0]=-.75f;s.stick[0][1]=1;s.stick[1][0]=.6f; s.trigger_active[1]=1;s.trigger[1]=.75f; s.squeeze_active[0]=1;s.squeeze[0]=.6f; s.buttons_active[0]=PSX_MOD_XR_PRIMARY|PSX_MOD_XR_MENU; s.buttons[0]=PSX_MOD_XR_PRIMARY|PSX_MOD_XR_SECONDARY|PSX_MOD_XR_MENU; CHECK(psx_openxr_input_override(&s)); CHECK(psx_openxr_input(&p) && p.synthetic && p.stick[0][0]==-.75f && p.stick[1][0]==.6f); CHECK(p.trigger[1]==.75f && p.squeeze[0]==.6f); CHECK(p.buttons[0]==(PSX_MOD_XR_PRIMARY|PSX_MOD_XR_MENU)); uint64_t seq=p.sequence;psx_openxr_input_snapshot(&p);CHECK(p.sequence==seq); s.focused=0;CHECK(psx_openxr_input_override(&s)); CHECK(psx_openxr_input(&p) && p.synthetic && !p.focused && p.stick[0][1]==0 && p.stick[1][0]==0); CHECK(!p.trigger[1] && !p.squeeze[0] && !p.buttons[0]); s.focused=1;s.active[0]=0;CHECK(psx_openxr_input_override(&s)); CHECK(psx_openxr_input(&p) && p.stick[0][0]==0 && p.stick[1][0]==.6f); CHECK(p.squeeze[0]==.6f && p.buttons[0]); /* independent of stick activity */ s.trigger_active[1]=0;s.squeeze_active[0]=0;s.buttons_active[0]=0; CHECK(psx_openxr_input_override(&s)); CHECK(psx_openxr_input(&p) && !p.trigger[1] && !p.squeeze[0] && !p.buttons[0]); s.trigger[0]=NAN;CHECK(!psx_openxr_input_override(&s));s.trigger[0]=0; s.squeeze[0]=1.1f;CHECK(!psx_openxr_input_override(&s));s.squeeze[0]=0; s.buttons_active[0]=16;CHECK(!psx_openxr_input_override(&s));s.buttons_active[0]=0; s.stick[0][0]=NAN;CHECK(!psx_openxr_input_override(&s)); s.stick[0][0]=1.1f;CHECK(!psx_openxr_input_override(&s)); CHECK(psx_openxr_input_override(0)); CHECK(psx_openxr_input(&p) && !p.synthetic && p.stick[1][0]==0); s.stick[0][0]=1;CHECK(psx_openxr_input_override(&s)); psx_openxr_shutdown();CHECK(psx_openxr_input(&p) && !p.synthetic && !p.focused); PSXModOpenXRHands h={0},got={0};got.struct_size=sizeof got; CHECK(psx_openxr_hands(&got) && !got.focused && got.age_ms==UINT32_MAX); h.struct_size=sizeof h;h.focused=1;h.origin_valid=1;h.origin_orientation_xyzw[3]=1; h.pose[1][1].active=1;h.pose[1][1].flags=15; h.pose[1][1].orientation_xyzw[3]=1;h.pose[1][1].position_m[0]=.3f; CHECK(psx_openxr_hands_override(&h)); CHECK(psx_openxr_hands(&got) && got.synthetic && got.age_ms==0 && got.pose[1][1].position_m[0]==.3f); uint64_t hands_seq=got.sequence; CHECK(psx_openxr_hands(&got) && got.sequence==hands_seq); /* Read only. */ h.pose[1][1].orientation_xyzw[3]=0;CHECK(!psx_openxr_hands_override(&h)); CHECK(psx_openxr_hands(&got) && got.sequence==hands_seq); /* Reject before mutation. */ h.pose[1][1].orientation_xyzw[3]=1;h.pose[1][1].flags=16;CHECK(!psx_openxr_hands_override(&h)); h.pose[1][1].flags=1;CHECK(psx_openxr_hands_override(&h)); /* Partial flags retained. */ CHECK(psx_openxr_hands(&got) && got.pose[1][1].flags==1); h.pose[1][1].position_m[0]=NAN;CHECK(!psx_openxr_hands_override(&h));h.pose[1][1].position_m[0]=.3f; h.focused=0;CHECK(psx_openxr_hands_override(&h)); CHECK(psx_openxr_hands(&got) && !got.focused && !got.pose[1][1].flags && !got.pose[1][1].active); h.focused=1;h.pose[1][1].active=0;CHECK(psx_openxr_hands_override(&h)); CHECK(psx_openxr_hands(&got) && !got.pose[1][1].flags); CHECK(psx_openxr_hands_override(NULL)); CHECK(psx_openxr_hands(&got) && !got.synthetic && !got.pose[1][1].flags); got.struct_size=0;CHECK(!psx_openxr_hands(&got));got.struct_size=sizeof got; h.pose[1][1].active=1;CHECK(psx_openxr_hands_override(&h)); psx_openxr_shutdown();CHECK(psx_openxr_hands(&got) && !got.synthetic && !got.origin_valid); return 0; }