// Isolated, opt-in wearer diagnostic. Never part of the installed app. // Usage: helper geometry.json ready-file stop-file #include "openvr.h" #include "recording_region.h" #include #include #include #include #include #include namespace { constexpr int texture_size=512; constexpr double aspect=4./3.; constexpr int fovs[]{80,75,70,65,60}; constexpr std::array colors[]{ {255,48,48},{255,230,32},{32,255,255},{48,255,64},{255,255,255} }; // Five columns, seven rows; each row's most significant bit is the left pixel. constexpr unsigned char digits[10][7]{ {14,17,19,21,25,17,14},{4,12,4,4,4,4,14}, {14,17,1,2,4,8,31},{30,1,1,14,1,1,30}, {2,6,10,18,31,2,2},{31,16,16,30,1,1,30}, {14,16,16,30,17,17,14},{31,1,2,4,8,8,8}, {14,17,17,14,17,17,14},{14,17,17,15,1,1,14} }; std::vector pixels(int fov,const std::array& color){ std::vector image(texture_size*texture_size*4); auto put=[&](int x,int y){ auto index=(y*texture_size+x)*4; for(int c=0;c<3;c++)image[index+c]=color[c]; image[index+3]=255; }; constexpr int stroke=3; for(int y=0;y=texture_size-stroke||y=texture_size-stroke)put(x,y); } // Center the FOV number just inside the top edge. Large block digits remain // readable without CEF or a font installation; all other pixels are clear. // Keep the label shorter than the angular gap to the next rectangle, so // an inner horizontal border cannot strike through the outer FOV number. constexpr int scale=2,digit_width=5*scale,gap=2*scale; constexpr int start_x=(texture_size-(2*digit_width+gap))/2,start_y=5; for(int digit=0;digit<2;digit++){ int value=digit==0?fov/10:fov%10; for(int row=0;row<7;row++)for(int column=0;column<5;column++){ if(!(digits[value][row]&(1<<(4-column))))continue; for(int dy=0;dyGetOverlayErrorNameFromEnum(error)); } struct Cleanup { vr::IVROverlay* api=nullptr; std::vector handles; bool initialized=false; ~Cleanup(){ for(auto handle:handles)if(api){ auto hidden=api->HideOverlay(handle),destroyed=api->DestroyOverlay(handle); if(hidden||destroyed)std::cerr<<"Diagnostic overlay cleanup error: "<>view; const double cx=view.at("centerTanX"),cy=view.at("centerTanY"); vr::EVRInitError error{};vr::VR_Init(&error,vr::VRApplication_Overlay); if(error)throw std::runtime_error(vr::VR_GetVRInitErrorAsEnglishDescription(error)); cleanup.initialized=true;cleanup.api=vr::VROverlay(); if(!cleanup.api)throw std::runtime_error("OpenVR overlay interface unavailable"); auto* api=cleanup.api; for(int i=0;i<5;i++){ RecordingRegionPlane plane; const double tangent=std::tan(fovs[i]*std::acos(-1.)/360.); if(!recording_region_plane(view,tangent,cx,cy,aspect,plane))throw std::runtime_error("Invalid capture geometry"); vr::VROverlayHandle_t handle=0; const std::string key="framely.recording.visibility.fixture."+std::to_string(fovs[i]); check(api,api->CreateOverlay(key.c_str(),("Recording visibility "+std::to_string(fovs[i])).c_str(),&handle),"CreateOverlay"); cleanup.handles.push_back(handle); check(api,api->SetOverlayInputMethod(handle,vr::VROverlayInputMethod_None),"SetOverlayInputMethod"); check(api,api->SetOverlayFlag(handle,vr::VROverlayFlags_MakeOverlaysInteractiveIfVisible,false),"Disable interactive overlay"); check(api,api->SetOverlayFlag(handle,vr::VROverlayFlags_HideLaserIntersection,true),"Hide laser intersection"); check(api,api->SetOverlayFlag(handle,vr::VROverlayFlags_VisibleInDashboard,true),"VisibleInDashboard"); check(api,api->SetOverlayFlag(handle,vr::VROverlayFlags_SortWithNonSceneOverlays,true),"Foreground sort flag"); check(api,api->SetOverlaySortOrder(handle,1000+i),"SetOverlaySortOrder"); check(api,api->SetOverlayWidthInMeters(handle,plane.width),"SetOverlayWidthInMeters"); check(api,api->SetOverlayTexelAspect(handle,aspect),"SetOverlayTexelAspect"); check(api,api->SetOverlayTransformTrackedDeviceRelative(handle,vr::k_unTrackedDeviceIndex_Hmd,&plane.transform),"SetOverlayTransformTrackedDeviceRelative"); auto image=pixels(fovs[i],colors[i]); check(api,api->SetOverlayRaw(handle,image.data(),texture_size,texture_size,4),"SetOverlayRaw"); check(api,api->ShowOverlay(handle),"ShowOverlay"); } std::ofstream ready(argv[2]);if(!ready)throw std::runtime_error("Cannot open ready file"); ready<<"ready\n";ready.close();if(!ready)throw std::runtime_error("Cannot write ready file"); std::cout<<"Visibility diagnostic ready: eye="<