mirror of
https://github.com/SteamFramelyHomebrew/framely.git
synced 2026-10-17 22:01:37 +02:00
54 lines
3.1 KiB
C++
54 lines
3.1 KiB
C++
#pragma once
|
|
#include "geometry_math.h"
|
|
struct MenuPlacement {vr::HmdMatrix34_t transform{};float width=0,radius=0;};
|
|
// Continue the Dock arc at the icon, with the main window's upward direction.
|
|
// The endpoint tangent, rather than the popup center, determines orientation.
|
|
inline bool menu_placement(const vr::HmdMatrix34_t& button,float button_size,MenuPlacement& out,float radius=0,const vr::HmdMatrix34_t* main=nullptr){
|
|
vr::HmdVector3_t right{{button.m[0][0],0,button.m[2][0]}},up{{0,1,0}};
|
|
if(main){for(int r=0;r<3;r++){right.v[r]=main->m[r][0];up.v[r]=main->m[r][1];}}
|
|
if(!normalize_dock_vector(up)||!std::isfinite(button_size)||button_size<=0)return false;
|
|
if(radius>0){
|
|
// Match the horizontal tangent at the Dock attachment point.
|
|
for(int r=0;r<3;r++)right.v[r]=button.m[r][0];
|
|
float projection=0;for(int r=0;r<3;r++)projection+=right.v[r]*up.v[r];
|
|
for(int r=0;r<3;r++)right.v[r]-=up.v[r]*projection;
|
|
}
|
|
if(!normalize_dock_vector(right))return false;
|
|
auto normal=cross_dock_vector(right,up);if(!normalize_dock_vector(normal))return false;up=cross_dock_vector(normal,right);
|
|
constexpr float width=.32f,height=width*840.f/600.f,gap=.012f;
|
|
float edge_x=width*.5f,edge_z=0;out.radius=radius;
|
|
if(radius>0){
|
|
float angle=width*.5f/radius,c=std::cos(angle),s=std::sin(angle);
|
|
// Move from the right endpoint back along the same circle to its midpoint.
|
|
for(int r=0;r<3;r++){float t=right.v[r],n=normal.v[r];right.v[r]=t*c-n*s;normal.v[r]=n*c+t*s;}
|
|
edge_x=radius*s;edge_z=radius*(1-c);
|
|
}
|
|
for(int r=0;r<3;r++){
|
|
out.transform.m[r][0]=right.v[r];out.transform.m[r][1]=up.v[r];out.transform.m[r][2]=normal.v[r];
|
|
float corner=button.m[r][3]+(button.m[r][1]+button.m[r][0])*button_size*.5f;
|
|
if(radius>0){float a=button_size*.5f/radius;corner=button.m[r][3]+button.m[r][1]*button_size*.5f+button.m[r][0]*radius*std::sin(a)+button.m[r][2]*radius*(1-std::cos(a));}
|
|
out.transform.m[r][3]=corner-right.v[r]*edge_x-normal.v[r]*edge_z+up.v[r]*(height*.5f+gap);
|
|
if(!std::isfinite(out.transform.m[r][3]))return false;
|
|
}
|
|
out.width=width;return true;
|
|
}
|
|
|
|
// The button transform carries the shared circle axis and radial normal.
|
|
// Pre-curve pitch slopes only the Dock row; the popup rises along that same axis.
|
|
inline bool dock_menu_placement(const vr::HmdMatrix34_t& button,float size,float radius,float dock_pitch,MenuPlacement& out){
|
|
if(!std::isfinite(radius)||radius<=0||!std::isfinite(size)||size<=0||!std::isfinite(dock_pitch))return false;
|
|
constexpr float width=.32f,height=.448f,gap=.012f;
|
|
float h=size*.5f,top_radius=radius-h*std::sin(dock_pitch);
|
|
if(top_radius<=0)return false;
|
|
float angle=h/radius-width*.5f/top_radius,c=std::cos(angle),s=std::sin(angle);
|
|
for(int r=0;r<3;r++){
|
|
float t=button.m[r][0],up=button.m[r][1],n=button.m[r][2];
|
|
float normal=n*c-t*s;
|
|
out.transform.m[r][0]=t*c+n*s;out.transform.m[r][1]=up;out.transform.m[r][2]=normal;
|
|
float circle=button.m[r][3]+n*radius;
|
|
out.transform.m[r][3]=circle-normal*top_radius+up*(h*std::cos(dock_pitch)+gap+height*.5f);
|
|
if(!std::isfinite(out.transform.m[r][3]))return false;
|
|
}
|
|
out.width=width;out.radius=top_radius;return true;
|
|
}
|