SHMessage
Textures
Shaders
+XRResources
XSMessage
XSTEPResource
XmlOcafResource
n Cocoa
r Textures
r Shaders
+r XRResources
t TKMeshVS
t TKOpenGl
t TKD3DHost
if { "$::HAVE_FFMPEG" == "true" } {
wokdep:SearchFFmpeg anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs
}
+ if { "$::HAVE_OPENVR" == "true" } {
+ wokdep:SearchOpenVR anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs
+ }
if { "$::HAVE_TBB" == "true" } {
wokdep:SearchTBB anIncErrs anLib32Errs anLib64Errs anBin32Errs anBin64Errs
}
ttk::label .myFrame.myChecks.myFImageLbl -text "Use FreeImage"
checkbutton .myFrame.myChecks.myTbbCheck -offvalue "false" -onvalue "true" -variable HAVE_TBB -command wokdep:gui:UpdateList
ttk::label .myFrame.myChecks.myTbbLbl -text "Use Intel TBB"
+checkbutton .myFrame.myChecks.myOpenVrCheck -offvalue "false" -onvalue "true" -variable HAVE_OPENVR -command wokdep:gui:UpdateList
+ttk::label .myFrame.myChecks.myOpenVrLbl -text "Use OpenVR"
if { "$::tcl_platform(os)" != "Darwin" } {
checkbutton .myFrame.myChecks.myGlesCheck -offvalue "false" -onvalue "true" -variable HAVE_GLES2 -command wokdep:gui:UpdateList
ttk::label .myFrame.myChecks.myGlesLbl -text "Use OpenGL ES"
incr aCheckRowIter
grid .myFrame.myChecks.myRapidJsonCheck -row $aCheckRowIter -column 0 -sticky e
grid .myFrame.myChecks.myRapidJsonLbl -row $aCheckRowIter -column 1 -sticky w
+grid .myFrame.myChecks.myOpenVrCheck -row $aCheckRowIter -column 4 -sticky e
+grid .myFrame.myChecks.myOpenVrLbl -row $aCheckRowIter -column 5 -sticky w
grid .myFrame.myChecks.myE57Check -row $aCheckRowIter -column 6 -sticky e
grid .myFrame.myChecks.myE57Lbl -row $aCheckRowIter -column 7 -sticky w
}
# fetch environment variables (e.g. set by custom.sh or custom.bat) and set them as tcl variables with the same name
-set THE_ENV_VARIABLES {HAVE_FREEIMAGE HAVE_FFMPEG HAVE_TBB HAVE_GLES2 HAVE_D3D HAVE_VTK HAVE_ZLIB HAVE_LIBLZMA HAVE_E57 HAVE_RAPIDJSON HAVE_OPENCL CHECK_QT4 CHECK_JDK MACOSX_USE_GLX HAVE_RelWithDebInfo BUILD_Inspector}
+set THE_ENV_VARIABLES {HAVE_FREEIMAGE HAVE_FFMPEG HAVE_TBB HAVE_GLES2 HAVE_D3D HAVE_VTK HAVE_ZLIB HAVE_LIBLZMA HAVE_E57 HAVE_RAPIDJSON HAVE_OPENVR HAVE_OPENCL CHECK_QT4 CHECK_JDK MACOSX_USE_GLX HAVE_RelWithDebInfo BUILD_Inspector}
foreach anEnvIter $THE_ENV_VARIABLES {
set ${anEnvIter} "false"
if { [info exists ::env(${anEnvIter})] } {
return "$isFound"
}
+# Search OpenVR SDK placement
+proc wokdep:SearchOpenVR {theErrInc theErrLib32 theErrLib64 theErrBin32 theErrBin64} {
+ upvar $theErrInc anErrInc
+ upvar $theErrLib32 anErrLib32
+ upvar $theErrLib64 anErrLib64
+ upvar $theErrBin32 anErrBin32
+ upvar $theErrBin64 anErrBin64
+
+ set isFound "true"
+ set anOpenVrHPath [wokdep:SearchHeader "openvr.h"]
+ if { "$anOpenVrHPath" == "" } {
+ set aPath [wokdep:Preferred [glob -nocomplain -directory "$::PRODUCTS_PATH" -type d *{openvr}*] "$::VCVER" "$::ARCH" ]
+ if { "$aPath" != "" && [file exists "$aPath/include/openvr.h"] } {
+ lappend ::CSF_OPT_INC "$aPath/include"
+ } elseif { "$aPath" != "" && [file exists "$aPath/headers/openvr.h"] } {
+ lappend ::CSF_OPT_INC "$aPath/headers"
+ } else {
+ lappend anErrInc "Error: 'openvr.h' not found (OpenVR)"
+ set isFound "false"
+ }
+ }
+
+ set aPlatform "unknown"
+ if { "$::tcl_platform(platform)" == "windows" } {
+ set aPlatform "win"
+ } elseif { "$::tcl_platform(os)" == "Darwin" } {
+ set aPlatform "osx"
+ } elseif { "$::tcl_platform(os)" == "Linux" } {
+ set aPlatform "linux"
+ }
+
+ foreach anArchIter {64 32} {
+ set anOpenVrLibPath [wokdep:SearchLib "openvr_api" "$anArchIter"]
+ if { "$anOpenVrLibPath" == "" } {
+ set aPath [wokdep:Preferred [glob -nocomplain -directory "$::PRODUCTS_PATH" -type d *{openvr}*] "$::VCVER" "$anArchIter" ]
+ set anOpenVrLibPath [wokdep:SearchLib "openvr_api" "$anArchIter" "$aPath/lib/${aPlatform}${anArchIter}"]
+ set anOpenVrLibPath2 [wokdep:SearchLib "openvr_api" "$anArchIter" "$aPath/lib"]
+ if { "$anOpenVrLibPath" != "" } {
+ lappend ::CSF_OPT_LIB$anArchIter "$aPath/lib/${aPlatform}${anArchIter}"
+ lappend ::CSF_OPT_BIN$anArchIter "$aPath/bin/${aPlatform}${anArchIter}"
+ } elseif { "$anOpenVrLibPath2" != "" } {
+ lappend ::CSF_OPT_LIB$anArchIter "$aPath/lib"
+ lappend ::CSF_OPT_BIN$anArchIter "$aPath/bin"
+ } else {
+ lappend anErrLib$anArchIter "Error: '${::SYS_LIB_PREFIX}openvr_api.${::SYS_LIB_SUFFIX}' not found (OpenVR)"
+ if { "$::ARCH" == "$anArchIter"} { set isFound "false" }
+ }
+ }
+ }
+
+ return "$isFound"
+}
+
# Search TBB library placement
proc wokdep:SearchTBB {theErrInc theErrLib32 theErrLib64 theErrBin32 theErrBin64} {
upvar $theErrInc anErrInc
if { "$::HAVE_LIBLZMA" == "true" } {
set aLibsMap(CSF_LIBLZMA) "liblzma"
}
+ if { "$::HAVE_OPENVR" == "true" } {
+ set aLibsMap(CSF_OpenVR) "openvr_api"
+ }
if { "$::HAVE_E57" == "true" && "$theOS" != "wnt" } {
# exclude wnt, as there are different pragma lib depending on debug/release
set aLibsMap(CSF_E57) "E57RefImpl"
set "HAVE_ZLIB=false"
set "HAVE_LIBLZMA=false"
set "HAVE_RAPIDJSON=false"
+set "HAVE_OPENVR=false"
set "HAVE_E57=false"
set "CSF_OPT_INC="
set "CSF_OPT_LIB32="
if ["%HAVE_ZLIB%"] == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_ZLIB" & set "CSF_DEFINES=HAVE_ZLIB;%CSF_DEFINES%"
if ["%HAVE_LIBLZMA%"] == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_LIBLZMA" & set "CSF_DEFINES=HAVE_LIBLZMA;%CSF_DEFINES%"
if ["%HAVE_RAPIDJSON%"] == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_RAPIDJSON" & set "CSF_DEFINES=HAVE_RAPIDJSON;%CSF_DEFINES%"
+if ["%HAVE_OPENVR%"] == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_OPENVR" & set "CSF_DEFINES=HAVE_OPENVR;%CSF_DEFINES%"
if ["%HAVE_E57%"] == ["true"] set "PRODUCTS_DEFINES=%PRODUCTS_DEFINES% -DHAVE_E57" & set "CSF_DEFINES=HAVE_E57;%CSF_DEFINES%"
rem Eliminate VS warning
export HAVE_ZLIB="false";
export HAVE_LIBLZMA="false";
export HAVE_RAPIDJSON="false";
+export HAVE_OPENVR="false";
export HAVE_E57="false";
export MACOSX_USE_GLX="false";
export CSF_OPT_INC=""
if [ "$HAVE_ZLIB" == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_ZLIB"; fi
if [ "$HAVE_LIBLZMA" == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_LIBLZMA"; fi
if [ "$HAVE_RAPIDJSON" == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_RAPIDJSON"; fi
+if [ "$HAVE_OPENVR" == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_OPENVR"; fi
if [ "$HAVE_E57" == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DHAVE_E57"; fi
# Option to compile OCCT with X11 libs on Mac OS X
if [ "$MACOSX_USE_GLX" == "true" ]; then export CSF_OPT_CMPL="${CSF_OPT_CMPL} -DMACOSX_USE_GLX"; fi
#include <AIS_Manipulator.hxx>
#include <AIS_Point.hxx>
#include <AIS_RubberBand.hxx>
+#include <AIS_XRTrackedDevice.hxx>
+#include <Aspect_XRSession.hxx>
#include <Aspect_Grid.hxx>
#include <Geom_CartesianPoint.hxx>
+#include <Graphic3d_ArrayOfSegments.hxx>
+#include <Graphic3d_Texture2Dmanual.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <gp_Quaternion.hxx>
myPrevMoveTo (-1, -1),
myHasHlrOnBeforeRotation (false),
//
+ myXRPrsDevices (0, 0),
+ myXRLaserTeleColor (Quantity_NOC_GREEN),
+ myXRLaserPickColor (Quantity_NOC_BLUE),
+ myXRLastTeleportHand(Aspect_XRTrackedDeviceRole_Other),
+ myXRLastPickingHand (Aspect_XRTrackedDeviceRole_Other),
+ myXRLastPickDepthLeft (Precision::Infinite()),
+ myXRLastPickDepthRight(Precision::Infinite()),
+ myXRTurnAngle (M_PI_4),
+ myToDisplayXRAuxDevices (false),
+ myToDisplayXRHands (true),
+ //
myMouseClickThreshold (3.0),
myMouseDoubleClickInt (0.4),
myScrollZoomRatio (15.0f),
myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton, AIS_MouseGesture_Pan);
myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton | Aspect_VKeyFlags_CTRL, AIS_MouseGesture_Pan);
+
+ myXRTeleportHaptic.Duration = 3600.0f;
+ myXRTeleportHaptic.Frequency = 0.1f;
+ myXRTeleportHaptic.Amplitude = 0.2f;
+
+ myXRPickingHaptic.Duration = 0.1f;
+ myXRPickingHaptic.Frequency = 4.0f;
+ myXRPickingHaptic.Amplitude = 0.1f;
+
+ myXRSelectHaptic.Duration = 0.2f;
+ myXRSelectHaptic.Frequency = 4.0f;
+ myXRSelectHaptic.Amplitude = 0.5f;
}
// =======================================================================
{
theView->Pan (myGL.Panning.Delta.x(), myGL.Panning.Delta.y());
theView->Invalidate();
+ theView->View()->SynchronizeXRPosedToBaseCamera();
return;
}
aPanTrsf.SetTranslation (aCameraPan);
aCam->Transform (aPanTrsf);
theView->Invalidate();
+ theView->View()->SynchronizeXRPosedToBaseCamera();
}
// =======================================================================
aRotPnt.y() += myGL.ZRotate.Angle * aViewPort.y();
theView->Rotation (int(aRotPnt.x()), int(aRotPnt.y()));
theView->Invalidate();
+ theView->View()->SynchronizeXRPosedToBaseCamera();
}
// =======================================================================
aCoeff = theParams.Delta > 0.0 ? aCoeff : 1.0 / aCoeff;
theView->SetZoom (aCoeff, true);
theView->Invalidate();
+ theView->View()->SynchronizeXRPosedToBaseCamera();
return;
}
aPanTrsf.SetTranslation (aCameraPan);
aCam->Transform (aPanTrsf);
theView->Invalidate();
+ theView->View()->SynchronizeXRPosedToBaseCamera();
}
// =======================================================================
&& aFocus < 2.0)
{
theView->Camera()->SetZFocus (theView->Camera()->ZFocusType(), aFocus);
- theView->Redraw();
+ theView->Invalidate();
}
}
return;
}
- const Handle(Graphic3d_Camera)& aCam = theView->Camera();
+ const Handle(Graphic3d_Camera)& aCam = theView->View()->IsActiveXR()
+ ? theView->View()->BaseXRCamera()
+ : theView->Camera();
if (myGL.OrbitRotation.ToStart)
{
// default alternatives
theView->Window()->Size (aWinXY.x(), aWinXY.y());
double aYawAngleDelta = ((myGL.OrbitRotation.PointStart.x() - myGL.OrbitRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
double aPitchAngleDelta = -((myGL.OrbitRotation.PointStart.y() - myGL.OrbitRotation.PointTo.y()) / double (aWinXY.y())) * (M_PI * 0.5);
- const double aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
- const double aYawAngleNew = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
- const double aRoll = 0.0;
+ double aPitchAngleNew = 0.0, aRoll = 0.0;
+ const double aYawAngleNew = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
+ if (!theView->View()->IsActiveXR())
+ {
+ aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
+ aRoll = 0.0;
+ }
gp_Quaternion aRot;
aRot.SetEulerAngles (gp_YawPitchRoll, aYawAngleNew, aPitchAngleNew, aRoll);
}
theView->Invalidate();
+ theView->View()->SynchronizeXRBaseToPosedCamera();
}
// =======================================================================
}
}
+// =======================================================================
+// function : handleXRInput
+// purpose :
+// =======================================================================
+void AIS_ViewController::handleXRInput (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView,
+ const AIS_WalkDelta& )
+{
+ theView->View()->ProcessXRInput();
+ if (!theView->View()->IsActiveXR())
+ {
+ return;
+ }
+ if (myXRCameraTmp.IsNull())
+ {
+ myXRCameraTmp = new Graphic3d_Camera();
+ }
+ handleXRTurnPad (theCtx, theView);
+ handleXRTeleport(theCtx, theView);
+ handleXRPicking (theCtx, theView);
+}
+
+// =======================================================================
+// function : handleXRTurnPad
+// purpose :
+// =======================================================================
+void AIS_ViewController::handleXRTurnPad (const Handle(AIS_InteractiveContext)& ,
+ const Handle(V3d_View)& theView)
+{
+ if (myXRTurnAngle <= 0.0
+ || !theView->View()->IsActiveXR())
+ {
+ return;
+ }
+
+ // turn left/right at 45 degrees on left/right trackpad clicks
+ for (int aHand = 0; aHand < 2; ++aHand)
+ {
+ const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
+ const Handle(Aspect_XRAction)& aPadClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadClick);
+ const Handle(Aspect_XRAction)& aPadPosAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadPosition);
+ if (aPadClickAct.IsNull()
+ || aPadPosAct.IsNull())
+ {
+ continue;
+ }
+
+ const Aspect_XRDigitalActionData aPadClick = theView->View()->XRSession()->GetDigitalActionData (aPadClickAct);
+ const Aspect_XRAnalogActionData aPadPos = theView->View()->XRSession()->GetAnalogActionData (aPadPosAct);
+ if (aPadClick.IsActive
+ && aPadClick.IsPressed
+ && aPadClick.IsChanged
+ && aPadPos.IsActive
+ && Abs (aPadPos.VecXYZ.y()) < 0.5f
+ && Abs (aPadPos.VecXYZ.x()) > 0.7f)
+ {
+ gp_Trsf aTrsfTurn;
+ aTrsfTurn.SetRotation (gp_Ax1 (gp::Origin(), theView->View()->BaseXRCamera()->Up()), aPadPos.VecXYZ.x() < 0.0f ? myXRTurnAngle : -myXRTurnAngle);
+ theView->View()->TurnViewXRCamera (aTrsfTurn);
+ break;
+ }
+ }
+}
+
+// =======================================================================
+// function : handleXRTeleport
+// purpose :
+// =======================================================================
+void AIS_ViewController::handleXRTeleport (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView)
+{
+ if (!theView->View()->IsActiveXR())
+ {
+ return;
+ }
+
+ // teleport on forward trackpad unclicks
+ const Aspect_XRTrackedDeviceRole aTeleOld = myXRLastTeleportHand;
+ myXRLastTeleportHand = Aspect_XRTrackedDeviceRole_Other;
+ for (int aHand = 0; aHand < 2; ++aHand)
+ {
+ const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
+ const Standard_Integer aDeviceId = theView->View()->XRSession()->NamedTrackedDevice (aRole);
+ if (aDeviceId == -1)
+ {
+ continue;
+ }
+
+ const Handle(Aspect_XRAction)& aPadClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadClick);
+ const Handle(Aspect_XRAction)& aPadPosAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTrackPadPosition);
+ if (aPadClickAct.IsNull()
+ || aPadPosAct.IsNull())
+ {
+ continue;
+ }
+
+ const Aspect_XRDigitalActionData aPadClick = theView->View()->XRSession()->GetDigitalActionData (aPadClickAct);
+ const Aspect_XRAnalogActionData aPadPos = theView->View()->XRSession()->GetAnalogActionData (aPadPosAct);
+ const bool isPressed = aPadClick.IsPressed;
+ const bool isClicked = !aPadClick.IsPressed
+ && aPadClick.IsChanged;
+ if (aPadClick.IsActive
+ && (isPressed || isClicked)
+ && aPadPos.IsActive
+ && aPadPos.VecXYZ.y() > 0.6f
+ && Abs (aPadPos.VecXYZ.x()) < 0.5f)
+ {
+ const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceId];
+ if (!aPose.IsValidPose)
+ {
+ continue;
+ }
+
+ myXRLastTeleportHand = aRole;
+ Standard_Real& aPickDepth = aRole == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
+ aPickDepth = Precision::Infinite();
+ Graphic3d_Vec3 aPickNorm;
+ const gp_Trsf aHandBase = theView->View()->PoseXRToWorld (aPose.Orientation);
+ const Standard_Real aHeadHeight = theView->View()->XRSession()->HeadPose().TranslationPart().Y();
+ {
+ const Standard_Integer aPickedId = handleXRMoveTo (theCtx, theView, aPose.Orientation, false);
+ if (aPickedId >= 1)
+ {
+ const SelectMgr_SortCriterion& aPickedData = theCtx->MainSelector()->PickedData (aPickedId);
+ aPickNorm = aPickedData.Normal;
+ if (aPickNorm.SquareModulus() > ShortRealEpsilon())
+ {
+ aPickDepth = aPickedData.Point.Distance (aHandBase.TranslationPart());
+ }
+ }
+ }
+ if (isClicked)
+ {
+ myXRLastTeleportHand = Aspect_XRTrackedDeviceRole_Other;
+ if (!Precision::IsInfinite (aPickDepth))
+ {
+ const gp_Dir aTeleDir = -gp::DZ().Transformed (aHandBase);
+ const gp_Dir anUpDir = theView->View()->BaseXRCamera()->Up();
+
+ bool isHorizontal = false;
+ gp_Dir aPickNormDir (aPickNorm.x(), aPickNorm.y(), aPickNorm.z());
+ if (anUpDir.IsEqual ( aPickNormDir, M_PI_4)
+ || anUpDir.IsEqual (-aPickNormDir, M_PI_4))
+ {
+ isHorizontal = true;
+ }
+
+ gp_Pnt aNewEye = aHandBase.TranslationPart();
+ if (isHorizontal)
+ {
+ aNewEye = aHandBase.TranslationPart()
+ + aTeleDir.XYZ() * aPickDepth
+ + anUpDir.XYZ() * aHeadHeight;
+ }
+ else
+ {
+ if (aPickNormDir.Dot (aTeleDir) < 0.0)
+ {
+ aPickNormDir.Reverse();
+ }
+ aNewEye = aHandBase.TranslationPart()
+ + aTeleDir.XYZ() * aPickDepth
+ - aPickNormDir.XYZ() * aHeadHeight / 4;
+ }
+
+ theView->View()->PosedXRCamera()->MoveEyeTo (aNewEye);
+ theView->View()->ComputeXRBaseCameraFromPosed (theView->View()->PosedXRCamera(), theView->View()->XRSession()->HeadPose());
+ }
+ }
+ break;
+ }
+ }
+
+ if (myXRLastTeleportHand != aTeleOld)
+ {
+ if (aTeleOld != Aspect_XRTrackedDeviceRole_Other)
+ {
+ if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (aTeleOld, Aspect_XRGenericAction_OutputHaptic))
+ {
+ theView->View()->XRSession()->AbortHapticVibrationAction (aHaptic);
+ }
+ }
+ if (myXRLastTeleportHand != Aspect_XRTrackedDeviceRole_Other)
+ {
+ if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastTeleportHand, Aspect_XRGenericAction_OutputHaptic))
+ {
+ theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRTeleportHaptic);
+ }
+ }
+ }
+}
+
+// =======================================================================
+// function : handleXRPicking
+// purpose :
+// =======================================================================
+void AIS_ViewController::handleXRPicking (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView)
+{
+ if (!theView->View()->IsActiveXR())
+ {
+ return;
+ }
+
+ // handle selection on trigger clicks
+ Aspect_XRTrackedDeviceRole aPickDevOld = myXRLastPickingHand;
+ myXRLastPickingHand = Aspect_XRTrackedDeviceRole_Other;
+ for (int aHand = 0; aHand < 2; ++aHand)
+ {
+ const Aspect_XRTrackedDeviceRole aRole = aHand == 0 ? Aspect_XRTrackedDeviceRole_RightHand : Aspect_XRTrackedDeviceRole_LeftHand;
+ const Handle(Aspect_XRAction)& aTrigClickAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTriggerClick);
+ const Handle(Aspect_XRAction)& aTrigPullAct = theView->View()->XRSession()->GenericAction (aRole, Aspect_XRGenericAction_InputTriggerPull);
+ if (aTrigClickAct.IsNull()
+ || aTrigPullAct.IsNull())
+ {
+ continue;
+ }
+
+ const Aspect_XRDigitalActionData aTrigClick = theView->View()->XRSession()->GetDigitalActionData (aTrigClickAct);
+ const Aspect_XRAnalogActionData aTrigPos = theView->View()->XRSession()->GetAnalogActionData (aTrigPullAct);
+ if (aTrigPos.IsActive
+ && Abs (aTrigPos.VecXYZ.x()) > 0.1f)
+ {
+ myXRLastPickingHand = aRole;
+ handleXRHighlight (theCtx, theView);
+ if (aTrigClick.IsActive
+ && aTrigClick.IsPressed
+ && aTrigClick.IsChanged)
+ {
+ theCtx->Select (false);
+ OnSelectionChanged (theCtx, theView);
+ if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastPickingHand, Aspect_XRGenericAction_OutputHaptic))
+ {
+ theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRSelectHaptic);
+ }
+ }
+ break;
+ }
+ }
+ if (myXRLastPickingHand != aPickDevOld)
+ {
+ theCtx->ClearDetected();
+ }
+}
+
// =======================================================================
// function : OnSelectionChanged
// purpose :
myPrevMoveTo = thePnt;
Handle(SelectMgr_EntityOwner) aLastPicked = theCtx->DetectedOwner();
+ theView->AutoZFit();
theCtx->MoveTo (thePnt.x(), thePnt.y(), theView, false);
Handle(SelectMgr_EntityOwner) aNewPicked = theCtx->DetectedOwner();
else if (myToAllowHighlight)
{
if (myPrevMoveTo != aMoveToPnt
- || myGL.OrbitRotation.ToRotate
- || myGL.ViewRotation.ToRotate
- || theView->IsInvalidated())
+ || (!theView->View()->IsActiveXR()
+ && (myGL.OrbitRotation.ToRotate
+ || myGL.ViewRotation.ToRotate
+ || theView->IsInvalidated())))
{
ResetPreviousMoveTo();
contextLazyMoveTo (theCtx, theView, aMoveToPnt);
setAskNextFrame();
}
+ if (theView->View()->IsActiveXR())
+ {
+ // VR requires continuous rendering
+ myToAskNextFrame = true;
+ }
+
for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
{
const Handle(V3d_View)& aView = aViewIter.Value();
}
}
+// =======================================================================
+// function : handleXRMoveTo
+// purpose :
+// =======================================================================
+Standard_Integer AIS_ViewController::handleXRMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView,
+ const gp_Trsf& thePose,
+ const Standard_Boolean theToHighlight)
+{
+ //ResetPreviousMoveTo();
+ Standard_Integer aPickResult = 0;
+
+ Handle(Graphic3d_Camera) aCamBack = theView->Camera();
+ myXRCameraTmp->Copy (aCamBack);
+ theView->View()->ComputeXRPosedCameraFromBase (*myXRCameraTmp, thePose);
+ theView->SetCamera (myXRCameraTmp);
+ Graphic3d_Vec2i aPickPixel;
+ theView->Window()->Size (aPickPixel.x(), aPickPixel.y());
+ aPickPixel /= 2;
+ const Standard_Integer aSelTolerBack = theCtx->MainSelector()->CustomPixelTolerance();
+ theCtx->MainSelector()->SetPixelTolerance (1);
+ theView->AutoZFit();
+ if (theToHighlight)
+ {
+ theCtx->MoveTo (aPickPixel.x(), aPickPixel.y(), theView, false);
+ if (!theCtx->DetectedOwner().IsNull())
+ {
+ // ignore 2D objects
+ for (aPickResult = 1; !theCtx->DetectedOwner()->Selectable()->TransformPersistence().IsNull(); ++aPickResult)
+ {
+ if (theCtx->HilightNextDetected (theView, false) <= 1)
+ {
+ theCtx->ClearDetected();
+ aPickResult = 0;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ theCtx->MainSelector()->Pick (aPickPixel.x(), aPickPixel.y(), theView);
+ for (Standard_Integer aPickIter = 1; aPickIter <= theCtx->MainSelector()->NbPicked(); ++aPickIter)
+ {
+ const SelectMgr_SortCriterion& aPickedData = theCtx->MainSelector()->PickedData (aPickIter);
+ if (!aPickedData.Entity->OwnerId()->Selectable()->TransformPersistence().IsNull())
+ {
+ // skip 2d objects
+ continue;
+ }
+
+ aPickResult = aPickIter;
+ break;
+ }
+ }
+ theCtx->MainSelector()->SetPixelTolerance (aSelTolerBack);
+ theView->SetCamera (aCamBack);
+ return aPickResult;
+}
+
+// =======================================================================
+// function : handleXRHighlight
+// purpose :
+// =======================================================================
+void AIS_ViewController::handleXRHighlight (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView)
+{
+ if (myXRLastPickingHand != Aspect_XRTrackedDeviceRole_LeftHand
+ && myXRLastPickingHand != Aspect_XRTrackedDeviceRole_RightHand)
+ {
+ return;
+ }
+
+ const Standard_Integer aDeviceId = theView->View()->XRSession()->NamedTrackedDevice (myXRLastPickingHand);
+ if (aDeviceId == -1)
+ {
+ return;
+ }
+
+ const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceId];
+ if (!aPose.IsValidPose)
+ {
+ return;
+ }
+
+ Handle(SelectMgr_EntityOwner) aDetOld = theCtx->DetectedOwner();
+ handleXRMoveTo (theCtx, theView, aPose.Orientation, true);
+ if (!theCtx->DetectedOwner().IsNull()
+ && theCtx->DetectedOwner() != aDetOld)
+ {
+ if (const Handle(Aspect_XRAction)& aHaptic = theView->View()->XRSession()->GenericAction (myXRLastPickingHand, Aspect_XRGenericAction_OutputHaptic))
+ {
+ theView->View()->XRSession()->TriggerHapticVibrationAction (aHaptic, myXRPickingHaptic);
+ }
+ }
+
+ Standard_Real& aPickDepth = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
+ aPickDepth = Precision::Infinite();
+ if (theCtx->MainSelector()->NbPicked() > 0)
+ {
+ const gp_Trsf aHandBase = theView->View()->PoseXRToWorld (aPose.Orientation);
+ const SelectMgr_SortCriterion& aPicked = theCtx->MainSelector()->PickedData (1);
+ aPickDepth = aPicked.Point.Distance (aHandBase.TranslationPart());
+ }
+}
+
+// =======================================================================
+// function : handleXRPresentations
+// purpose :
+// =======================================================================
+void AIS_ViewController::handleXRPresentations (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView)
+{
+ if (!theView->View()->IsActiveXR()
+ || (!myToDisplayXRAuxDevices
+ && !myToDisplayXRHands))
+ {
+ for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
+ {
+ if (!aPrsIter.Value().IsNull()
+ && aPrsIter.Value()->HasInteractiveContext())
+ {
+ theCtx->Remove (aPrsIter.Value(), false);
+ }
+ aPrsIter.ChangeValue().Nullify();
+ }
+ return;
+ }
+
+ if (myXRPrsDevices.Length() != theView->View()->XRSession()->TrackedPoses().Length())
+ {
+ for (NCollection_Array1<Handle(AIS_XRTrackedDevice)>::Iterator aPrsIter (myXRPrsDevices); aPrsIter.More(); aPrsIter.Next())
+ {
+ if (!aPrsIter.Value().IsNull())
+ {
+ theCtx->Remove (aPrsIter.Value(), false);
+ }
+ }
+ myXRPrsDevices.Resize (theView->View()->XRSession()->TrackedPoses().Lower(), theView->View()->XRSession()->TrackedPoses().Upper(), false);
+ }
+
+ const Standard_Integer aHeadDevice = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_Head);
+ const Standard_Integer aLeftDevice = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_LeftHand);
+ const Standard_Integer aRightDevice = theView->View()->XRSession()->NamedTrackedDevice (Aspect_XRTrackedDeviceRole_RightHand);
+ for (Standard_Integer aDeviceIter = theView->View()->XRSession()->TrackedPoses().Lower(); aDeviceIter <= theView->View()->XRSession()->TrackedPoses().Upper(); ++aDeviceIter)
+ {
+ const Aspect_TrackedDevicePose& aPose = theView->View()->XRSession()->TrackedPoses()[aDeviceIter];
+ Handle(AIS_XRTrackedDevice)& aPosePrs = myXRPrsDevices[aDeviceIter];
+ if (!aPose.IsValidPose)
+ {
+ continue;
+ }
+
+ const bool isHand = aDeviceIter == aLeftDevice
+ || aDeviceIter == aRightDevice;
+ if ((!myToDisplayXRHands && isHand)
+ || (!myToDisplayXRAuxDevices && !isHand))
+ {
+ if (!aPosePrs.IsNull()
+ && aPosePrs->HasInteractiveContext())
+ {
+ theCtx->Remove (aPosePrs, false);
+ }
+ continue;
+ }
+
+ Aspect_XRTrackedDeviceRole aRole = Aspect_XRTrackedDeviceRole_Other;
+ if (aDeviceIter == aLeftDevice)
+ {
+ aRole = Aspect_XRTrackedDeviceRole_LeftHand;
+ }
+ else if (aDeviceIter == aRightDevice)
+ {
+ aRole = Aspect_XRTrackedDeviceRole_RightHand;
+ }
+
+ if (!aPosePrs.IsNull()
+ && aPosePrs->UnitFactor() != (float )theView->View()->UnitFactor())
+ {
+ theCtx->Remove (aPosePrs, false);
+ aPosePrs.Nullify();
+ }
+
+ if (aPosePrs.IsNull())
+ {
+ Handle(Image_Texture) aTexture;
+ Handle(Graphic3d_ArrayOfTriangles) aTris;
+ if (aDeviceIter != aHeadDevice)
+ {
+ aTris = theView->View()->XRSession()->LoadRenderModel (aDeviceIter, aTexture);
+ }
+ if (!aTris.IsNull())
+ {
+ aPosePrs = new AIS_XRTrackedDevice (aTris, aTexture);
+ }
+ else
+ {
+ aPosePrs = new AIS_XRTrackedDevice();
+ }
+ aPosePrs->SetUnitFactor ((float )theView->View()->UnitFactor());
+ aPosePrs->SetMutable (true);
+ aPosePrs->SetInfiniteState (true);
+ }
+ aPosePrs->SetRole (aRole);
+
+ if (!aPosePrs->HasInteractiveContext())
+ {
+ theCtx->Display (aPosePrs, 0, -1, false);
+ }
+
+ gp_Trsf aPoseLocal = aPose.Orientation;
+ if (aDeviceIter == aHeadDevice)
+ {
+ // show headset position on floor level
+ aPoseLocal.SetTranslationPart (gp_Vec (aPoseLocal.TranslationPart().X(), 0.0, aPoseLocal.TranslationPart().Z()));
+ }
+ const gp_Trsf aPoseWorld = theView->View()->PoseXRToWorld (aPoseLocal);
+ theCtx->SetLocation (aPosePrs, aPoseWorld);
+
+ Standard_Real aLaserLen = 0.0;
+ if (isHand
+ && aPosePrs->Role() == myXRLastPickingHand)
+ {
+ aLaserLen = myXRLastPickingHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
+ if (Precision::IsInfinite (aLaserLen))
+ {
+ const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
+ if (!aViewBox.IsVoid())
+ {
+ aLaserLen = Sqrt (aViewBox.SquareExtent());
+ }
+ else
+ {
+ aLaserLen = 100.0;
+ }
+ }
+ aPosePrs->SetLaserColor (myXRLaserPickColor);
+ }
+ else if (isHand
+ && aPosePrs->Role() == myXRLastTeleportHand)
+ {
+ aLaserLen = myXRLastTeleportHand == Aspect_XRTrackedDeviceRole_LeftHand ? myXRLastPickDepthLeft : myXRLastPickDepthRight;
+ if (Precision::IsInfinite (aLaserLen))
+ {
+ const Bnd_Box aViewBox = theView->View()->MinMaxValues (true);
+ if (!aViewBox.IsVoid())
+ {
+ aLaserLen = Sqrt (aViewBox.SquareExtent());
+ }
+ else
+ {
+ aLaserLen = 100.0;
+ }
+ }
+ aPosePrs->SetLaserColor (myXRLaserTeleColor);
+ }
+ aPosePrs->SetLaserLength ((float )aLaserLen);
+ }
+}
+
// =======================================================================
// function : HandleViewEvents
// purpose :
void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
const Handle(V3d_View)& theView)
{
- handleMoveTo (theCtx, theView);
+ const bool wasImmediateUpdate = theView->SetImmediateUpdate (false);
const AIS_WalkDelta aWalk = FetchNavigationKeys (1.0, 1.0);
+ handleXRInput (theCtx, theView, aWalk);
+ if (theView->View()->IsActiveXR())
+ {
+ theView->View()->SetupXRPosedCamera();
+ }
handleCameraActions (theCtx, theView, aWalk);
+ theView->View()->SynchronizeXRPosedToBaseCamera(); // handleCameraActions() may modify posed camera position - copy this modifications also to the base camera
+ handleXRPresentations (theCtx, theView);
+
+ handleMoveTo (theCtx, theView);
handleViewRedraw (theCtx, theView);
+ theView->View()->UnsetXRPosedCamera();
+
+ theView->SetImmediateUpdate (wasImmediateUpdate);
// make sure to not process the same events twice
myGL.Reset();
#include <Aspect_VKeySet.hxx>
#include <Aspect_TouchMap.hxx>
+#include <Aspect_XRHapticActionData.hxx>
+#include <Aspect_XRTrackedDeviceRole.hxx>
#include <AIS_DragAction.hxx>
#include <AIS_MouseGesture.hxx>
#include <AIS_NavigationMode.hxx>
#include <NCollection_Array1.hxx>
#include <OSD_Timer.hxx>
#include <Precision.hxx>
+#include <Quantity_ColorRGBA.hxx>
#include <Standard_Mutex.hxx>
class AIS_AnimationCamera;
class AIS_InteractiveContext;
class AIS_Point;
class AIS_RubberBand;
+class AIS_XRTrackedDevice;
+class Graphic3d_Camera;
class V3d_View;
//! Auxiliary structure for handling viewer events between GUI and Rendering threads.
//! Reset previous position of MoveTo.
void ResetPreviousMoveTo() { myPrevMoveTo = Graphic3d_Vec2i (-1); }
+ //! Return TRUE to display auxiliary tracked XR devices (like tracking stations).
+ bool ToDisplayXRAuxDevices() const { return myToDisplayXRAuxDevices; }
+
+ //! Set if auxiliary tracked XR devices should be displayed.
+ void SetDisplayXRAuxDevices (bool theToDisplay) { myToDisplayXRAuxDevices = theToDisplay; }
+
+ //! Return TRUE to display XR hand controllers.
+ bool ToDisplayXRHands() const { return myToDisplayXRHands; }
+
+ //! Set if tracked XR hand controllers should be displayed.
+ void SetDisplayXRHands (bool theToDisplay) { myToDisplayXRHands = theToDisplay; }
+
public: //! @name keyboard input
//! Return keyboard state.
Standard_EXPORT virtual void handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
const Handle(V3d_View)& theView);
+public:
+
+ //! Perform XR input.
+ //! This method is expected to be called from rendering thread.
+ Standard_EXPORT virtual void handleXRInput (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView,
+ const AIS_WalkDelta& theWalk);
+
+ //! Handle trackpad view turn action.
+ Standard_EXPORT virtual void handleXRTurnPad (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView);
+
+ //! Handle trackpad teleportation action.
+ Standard_EXPORT virtual void handleXRTeleport (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView);
+
+ //! Handle picking on trigger click.
+ Standard_EXPORT virtual void handleXRPicking (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView);
+
+ //! Perform dynamic highlighting for active hand.
+ Standard_EXPORT virtual void handleXRHighlight (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView);
+
+ //! Display auxiliary XR presentations.
+ Standard_EXPORT virtual void handleXRPresentations (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView);
+
+ //! Perform picking with/without dynamic highlighting for XR pose.
+ Standard_EXPORT virtual Standard_Integer handleXRMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
+ const Handle(V3d_View)& theView,
+ const gp_Trsf& thePose,
+ const Standard_Boolean theToHighlight);
+
protected:
//! Flush buffers.
Graphic3d_Vec2i myPrevMoveTo; //!< previous position of MoveTo event in 3D viewer
Standard_Boolean myHasHlrOnBeforeRotation; //!< flag for restoring Computed mode after rotation
+protected: //! @name XR input variables
+
+ NCollection_Array1<Handle(AIS_XRTrackedDevice)> myXRPrsDevices; //!< array of XR tracked devices presentations
+ Handle(Graphic3d_Camera) myXRCameraTmp; //!< temporary camera
+ Quantity_Color myXRLaserTeleColor; //!< color of teleport laser
+ Quantity_Color myXRLaserPickColor; //!< color of picking laser
+ Aspect_XRTrackedDeviceRole myXRLastTeleportHand;//!< active hand for teleport
+ Aspect_XRTrackedDeviceRole myXRLastPickingHand; //!< active hand for picking objects
+ Aspect_XRHapticActionData myXRTeleportHaptic; //!< vibration on picking teleport destination
+ Aspect_XRHapticActionData myXRPickingHaptic; //!< vibration on dynamic highlighting
+ Aspect_XRHapticActionData myXRSelectHaptic; //!< vibration on selection
+ Standard_Real myXRLastPickDepthLeft; //!< last picking depth for left hand
+ Standard_Real myXRLastPickDepthRight; //!< last picking depth for right hand
+ Standard_Real myXRTurnAngle; //!< discrete turn angle for XR trackpad
+ Standard_Boolean myToDisplayXRAuxDevices; //!< flag to display auxiliary tracked XR devices
+ Standard_Boolean myToDisplayXRHands; //!< flag to display XR hands
+
protected: //! @name keyboard input variables
Aspect_VKeySet myKeys; //!< keyboard state
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <AIS_XRTrackedDevice.hxx>
+
+#include <Graphic3d_ArrayOfSegments.hxx>
+#include <Graphic3d_ArrayOfTriangles.hxx>
+#include <Graphic3d_Group.hxx>
+#include <Graphic3d_Texture2Dmanual.hxx>
+#include <Image_Texture.hxx>
+#include <Prs3d_LineAspect.hxx>
+#include <Prs3d_ShadingAspect.hxx>
+#include <Select3D_SensitivePrimitiveArray.hxx>
+#include <SelectMgr_EntityOwner.hxx>
+
+//! Texture holder.
+class AIS_XRTrackedDevice::XRTexture : public Graphic3d_Texture2Dmanual
+{
+public:
+
+ //! Constructor.
+ XRTexture (const Handle(Image_Texture)& theImageSource,
+ const Graphic3d_TextureUnit theUnit = Graphic3d_TextureUnit_BaseColor)
+ : Graphic3d_Texture2Dmanual (""), myImageSource (theImageSource)
+ {
+ if (!theImageSource->TextureId().IsEmpty())
+ {
+ myTexId = theImageSource->TextureId();
+ }
+ myParams->SetTextureUnit (theUnit);
+ myIsColorMap = theUnit == Graphic3d_TextureUnit_BaseColor
+ || theUnit == Graphic3d_TextureUnit_Emissive;
+ }
+
+ //! Image reader.
+ virtual Handle(Image_PixMap) GetImage() const Standard_OVERRIDE { return myImageSource->ReadImage(); }
+
+protected:
+ Handle(Image_Texture) myImageSource;
+};
+
+IMPLEMENT_STANDARD_RTTIEXT(AIS_XRTrackedDevice, AIS_InteractiveObject)
+
+//=======================================================================
+//function : AIS_XRTrackedDevice
+//purpose :
+//=======================================================================
+AIS_XRTrackedDevice::AIS_XRTrackedDevice (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
+ const Handle(Image_Texture)& theTexture)
+: myTris (theTris),
+ myLaserColor (Quantity_NOC_BLUE),
+ myLaserLength (0.0f),
+ myUnitFactor (1.0f),
+ myRole (Aspect_XRTrackedDeviceRole_Other),
+ myToShowAxes (false)
+{
+ myDrawer->SetShadingAspect (new Prs3d_ShadingAspect());
+ myDrawer->ShadingAspect()->SetMaterial (Graphic3d_NOM_DEFAULT);
+ myDrawer->ShadingAspect()->SetColor (Quantity_NOC_WHITE);
+ if (!theTexture.IsNull())
+ {
+ myDrawer->ShadingAspect()->Aspect()->SetTextureMap (new XRTexture (theTexture));
+ myDrawer->ShadingAspect()->Aspect()->SetTextureMapOn (true);
+ }
+}
+
+//=======================================================================
+//function : AIS_XRTrackedDevice
+//purpose :
+//=======================================================================
+AIS_XRTrackedDevice::AIS_XRTrackedDevice()
+: myLaserColor (Quantity_NOC_BLUE),
+ myLaserLength (0.0f),
+ myUnitFactor (1.0f),
+ myRole (Aspect_XRTrackedDeviceRole_Other),
+ myToShowAxes (true)
+{
+ //
+}
+
+//=======================================================================
+//function : SetLaserColor
+//purpose :
+//=======================================================================
+void AIS_XRTrackedDevice::SetLaserColor (const Quantity_Color& theColor)
+{
+ if (!myLaserColor.IsEqual (theColor))
+ {
+ myLaserColor = theColor;
+ computeLaserRay();
+ }
+}
+
+//=======================================================================
+//function : SetLaserLength
+//purpose :
+//=======================================================================
+void AIS_XRTrackedDevice::SetLaserLength (Standard_ShortReal theLength)
+{
+ if (myLaserLength != theLength)
+ {
+ myLaserLength = theLength;
+ computeLaserRay();
+ }
+}
+
+//=======================================================================
+//function : computeLaserRay
+//purpose :
+//=======================================================================
+void AIS_XRTrackedDevice::computeLaserRay()
+{
+ if (myRayGroup.IsNull())
+ {
+ return;
+ }
+
+ if (!myRayGroup->IsEmpty())
+ {
+ myRayGroup->Clear();
+ }
+ if (myLaserLength <= 0.0f)
+ {
+ return;
+ }
+
+ Handle(Graphic3d_ArrayOfPrimitives) aLines = new Graphic3d_ArrayOfSegments (2, 0, Graphic3d_ArrayFlags_VertexColor);
+ aLines->AddVertex (gp_Pnt (0.0, 0.0, 0.0), myLaserColor);
+ aLines->AddVertex (gp_Pnt (0.0, 0.0, -myLaserLength), myLaserColor);
+ myRayGroup->SetGroupPrimitivesAspect (myDrawer->LineAspect()->Aspect());
+ myRayGroup->AddPrimitiveArray (aLines, false); // do not extend camera frustum by ray
+}
+
+//=======================================================================
+//function : Compute
+//purpose :
+//=======================================================================
+void AIS_XRTrackedDevice::Compute (const Handle(PrsMgr_PresentationManager3d)& ,
+ const Handle(Prs3d_Presentation)& thePrs,
+ const Standard_Integer theMode)
+{
+ if (theMode != 0)
+ {
+ return;
+ }
+
+ thePrs->SetInfiniteState (myInfiniteState);
+ Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
+ if (!myTris.IsNull())
+ {
+ aGroup->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
+ aGroup->AddPrimitiveArray (myTris);
+ }
+
+ if (myToShowAxes || myTris.IsNull())
+ {
+ const float aSize = 0.1f * myUnitFactor;
+ aGroup->SetGroupPrimitivesAspect (myDrawer->LineAspect()->Aspect());
+ Handle(Graphic3d_ArrayOfPrimitives) aLines = new Graphic3d_ArrayOfSegments (6, 0, Graphic3d_ArrayFlags_VertexColor);
+ aLines->AddVertex (gp_Pnt (0.0, 0.0, 0.0), Quantity_Color (Quantity_NOC_RED));
+ aLines->AddVertex (gp_Pnt (aSize, 0.0, 0.0), Quantity_Color (Quantity_NOC_RED));
+ aLines->AddVertex (gp_Pnt (0.0, 0.0, 0.0), Quantity_Color (Quantity_NOC_GREEN));
+ aLines->AddVertex (gp_Pnt (0.0, aSize, 0.0), Quantity_Color (Quantity_NOC_GREEN));
+ aLines->AddVertex (gp_Pnt (0.0, 0.0, 0.0), Quantity_Color (Quantity_NOC_BLUE));
+ aLines->AddVertex (gp_Pnt (0.0, 0.0, aSize), Quantity_Color (Quantity_NOC_BLUE));
+ aGroup->AddPrimitiveArray (aLines);
+ }
+
+ myRayGroup = thePrs->NewGroup();
+ computeLaserRay();
+}
+
+//=======================================================================
+//function : ComputeSelection
+//purpose :
+//=======================================================================
+void AIS_XRTrackedDevice::ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
+ const Standard_Integer theMode)
+{
+ if (theMode != 0)
+ {
+ return;
+ }
+
+ if (!myTris.IsNull())
+ {
+ Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this);
+ Handle(Select3D_SensitivePrimitiveArray) aSensitive = new Select3D_SensitivePrimitiveArray (anOwner);
+ aSensitive->InitTriangulation (myTris->Attributes(), myTris->Indices(), TopLoc_Location(), true);
+ theSel->Add (aSensitive);
+ }
+}
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_XRTrackedDevice_HeaderFile
+#define _AIS_XRTrackedDevice_HeaderFile
+
+#include <AIS_InteractiveObject.hxx>
+#include <Aspect_XRTrackedDeviceRole.hxx>
+
+class Graphic3d_ArrayOfTriangles;
+class Image_Texture;
+
+//! Auxiliary textured mesh presentation of tracked XR device.
+class AIS_XRTrackedDevice : public AIS_InteractiveObject
+{
+ DEFINE_STANDARD_RTTIEXT(AIS_XRTrackedDevice, AIS_InteractiveObject)
+public:
+ //! Main constructor.
+ Standard_EXPORT AIS_XRTrackedDevice (const Handle(Graphic3d_ArrayOfTriangles)& theTris,
+ const Handle(Image_Texture)& theTexture);
+
+ //! Empty constructor.
+ Standard_EXPORT AIS_XRTrackedDevice();
+
+ //! Return device role.
+ Aspect_XRTrackedDeviceRole Role() const { return myRole; }
+
+ //! Set device role.
+ void SetRole (Aspect_XRTrackedDeviceRole theRole) { myRole = theRole; }
+
+ //! Return laser color.
+ const Quantity_Color& LaserColor() const { return myLaserColor; }
+
+ //! Set laser color.
+ Standard_EXPORT void SetLaserColor (const Quantity_Color& theColor);
+
+ //! Return laser length.
+ Standard_ShortReal LaserLength() const { return myLaserLength; }
+
+ //! Set laser length.
+ Standard_EXPORT void SetLaserLength (Standard_ShortReal theLength);
+
+ //! Return unit scale factor.
+ Standard_ShortReal UnitFactor() const { return myUnitFactor; }
+
+ //! Set unit scale factor.
+ void SetUnitFactor (Standard_ShortReal theFactor) { myUnitFactor = theFactor; }
+
+protected:
+
+ //! Returns true for 0 mode.
+ virtual Standard_Boolean AcceptDisplayMode (const Standard_Integer theMode) const Standard_OVERRIDE { return theMode == 0; }
+
+ //! Compute presentation.
+ Standard_EXPORT virtual void Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
+ const Handle(Prs3d_Presentation)& thePrs,
+ const Standard_Integer theMode) Standard_OVERRIDE;
+
+ //! Compute selection.
+ Standard_EXPORT virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSel,
+ const Standard_Integer theMode) Standard_OVERRIDE;
+
+ //! Compute laser ray presentation.
+ Standard_EXPORT void computeLaserRay();
+
+private:
+ //! Texture holder.
+ class XRTexture;
+
+private:
+
+ Handle(Graphic3d_Group) myRayGroup;
+
+ Handle(Graphic3d_ArrayOfTriangles) myTris;
+ Quantity_Color myLaserColor;
+ Standard_ShortReal myLaserLength;
+ Standard_ShortReal myUnitFactor;
+ Aspect_XRTrackedDeviceRole myRole;
+ Standard_Boolean myToShowAxes;
+};
+
+#endif // _AIS_XRTrackedDevice_HeaderFile
AIS_Relation.hxx
AIS_SymmetricRelation.hxx
AIS_TangentRelation.hxx
+AIS_XRTrackedDevice.cxx
+AIS_XRTrackedDevice.hxx
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_ColorSpace_HeaderFile
+#define _Aspect_ColorSpace_HeaderFile
+
+#include <Aspect_GraphicsLibrary.hxx>
+
+//! Texture color spaces accepted by XR composer.
+enum Aspect_ColorSpace
+{
+ Aspect_ColorSpace_sRGB = 0, //!< non-linear sRGB color space
+ Aspect_ColorSpace_Linear = 1, //!< linear RGB color space
+};
+
+#endif // _Aspect_ColorSpace_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_Eye_HeaderFile
+#define _Aspect_Eye_HeaderFile
+
+//! Camera eye index within stereoscopic pair.
+enum Aspect_Eye
+{
+ Aspect_Eye_Left,
+ Aspect_Eye_Right
+};
+
+#endif // _Aspect_Eye_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_FrustumLRBT_HeaderFile
+#define _Aspect_FrustumLRBT_HeaderFile
+
+//! Structure defining frustum boundaries.
+template<typename Elem_t>
+struct Aspect_FrustumLRBT
+{
+ Elem_t Left;
+ Elem_t Right;
+ Elem_t Bottom;
+ Elem_t Top;
+
+ //! Empty constructor.
+ Aspect_FrustumLRBT() : Left (0), Right (0), Bottom (0), Top (0) {}
+
+ //! Copy/cast constructor.
+ template<typename Other_t>
+ explicit Aspect_FrustumLRBT (const Aspect_FrustumLRBT<Other_t>& theOther)
+ : Left (static_cast<Elem_t> (theOther.Left)),
+ Right (static_cast<Elem_t> (theOther.Right)),
+ Bottom(static_cast<Elem_t> (theOther.Bottom)),
+ Top (static_cast<Elem_t> (theOther.Top)) {}
+
+ //! Apply multiply factor.
+ void Multiply (Elem_t theScale)
+ {
+ Left *= theScale;
+ Right *= theScale;
+ Bottom *= theScale;
+ Top *= theScale;
+ }
+
+ //! Return multiplied frustum.
+ Aspect_FrustumLRBT<Elem_t> Multiplied (Elem_t theScale)
+ {
+ Aspect_FrustumLRBT<Elem_t> aCopy (*this);
+ aCopy.Multiply (theScale);
+ return aCopy;
+ }
+};
+
+#endif // _Aspect_FrustumLRBT_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_GraphicsLibrary_HeaderFile
+#define _Aspect_GraphicsLibrary_HeaderFile
+
+//! Graphics API enumeration.
+enum Aspect_GraphicsLibrary
+{
+ Aspect_GraphicsLibrary_OpenGL,
+ Aspect_GraphicsLibrary_OpenGLES,
+};
+
+#endif // _Aspect_GraphicsLibrary_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Aspect_OpenVRSession.hxx>
+
+#include <Graphic3d_ArrayOfTriangles.hxx>
+#include <Image_PixMap.hxx>
+#include <Image_Texture.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <NCollection_LocalArray.hxx>
+#include <OSD.hxx>
+#include <OSD_Environment.hxx>
+#include <OSD_File.hxx>
+#include <OSD_Process.hxx>
+
+#ifdef HAVE_OPENVR
+ #include <openvr.h>
+
+namespace
+{
+ //! Print OpenVR compositor error.
+ static const char* getVRCompositorError (vr::EVRCompositorError theVRError)
+ {
+ switch (theVRError)
+ {
+ case vr::VRCompositorError_None: return "None";
+ case vr::VRCompositorError_RequestFailed: return "Request Failed";
+ case vr::VRCompositorError_IncompatibleVersion: return "Incompatible Version";
+ case vr::VRCompositorError_DoNotHaveFocus: return "Do not have focus";
+ case vr::VRCompositorError_InvalidTexture: return "Invalid Texture";
+ case vr::VRCompositorError_IsNotSceneApplication: return "Is not scene application";
+ case vr::VRCompositorError_TextureIsOnWrongDevice: return "Texture is on wrong device";
+ case vr::VRCompositorError_TextureUsesUnsupportedFormat: return "Texture uses unsupported format";
+ case vr::VRCompositorError_SharedTexturesNotSupported: return "Shared textures not supported";
+ case vr::VRCompositorError_IndexOutOfRange: return "Index out of range";
+ case vr::VRCompositorError_AlreadySubmitted: return "Already submitted";
+ case vr::VRCompositorError_InvalidBounds: return "Invalid Bounds";
+ case vr::VRCompositorError_AlreadySet: return "Already set";
+ }
+ return "UNKNOWN";
+ }
+
+ //! Print OpenVR input error.
+ static const char* getVRInputError (vr::EVRInputError theVRError)
+ {
+ switch (theVRError)
+ {
+ case vr::VRInputError_None: return "None";
+ case vr::VRInputError_NameNotFound: return "NameNotFound";
+ case vr::VRInputError_WrongType: return "WrongType";
+ case vr::VRInputError_InvalidHandle: return "InvalidHandle";
+ case vr::VRInputError_InvalidParam: return "InvalidParam";
+ case vr::VRInputError_NoSteam: return "NoSteam:";
+ case vr::VRInputError_MaxCapacityReached: return "MaxCapacityReached";
+ case vr::VRInputError_IPCError: return "IPCError";
+ case vr::VRInputError_NoActiveActionSet: return "NoActiveActionSet";
+ case vr::VRInputError_InvalidDevice: return "InvalidDevice";
+ case vr::VRInputError_InvalidSkeleton: return "InvalidSkeleton";
+ case vr::VRInputError_InvalidBoneCount: return "InvalidBoneCount";
+ case vr::VRInputError_InvalidCompressedData: return "InvalidCompressedData";
+ case vr::VRInputError_NoData: return "NoData";
+ case vr::VRInputError_BufferTooSmall: return "BufferTooSmall";
+ case vr::VRInputError_MismatchedActionManifest: return "MismatchedActionManifest";
+ case vr::VRInputError_MissingSkeletonData: return "MissingSkeletonData";
+ case vr::VRInputError_InvalidBoneIndex: return "InvalidBoneIndex";
+ case vr::VRInputError_InvalidPriority: return "InvalidPriority";
+ case vr::VRInputError_PermissionDenied: return "PermissionDenied";
+ case vr::VRInputError_InvalidRenderModel: return "InvalidRenderModel";
+ }
+ return "UNKNOWN";
+ }
+
+ //! Convert OpenVR mat4x4 into OCCT mat4x4.
+ static NCollection_Mat4<double> mat44vr2Occ (const vr::HmdMatrix44_t& theMat4)
+ {
+ NCollection_Mat4<double> aMat4;
+ for (int aRow = 0; aRow < 4; ++aRow)
+ {
+ aMat4.SetRow (aRow, NCollection_Vec4<double> (theMat4.m[aRow][0], theMat4.m[aRow][1], theMat4.m[aRow][2], theMat4.m[aRow][3]));
+ }
+ return aMat4;
+ }
+
+ //! Convert OpenVR mat3x4 into OCCT gp_Trsf.
+ static gp_Trsf mat34vr2OccTrsf (const vr::HmdMatrix34_t& theMat4)
+ {
+ gp_Trsf aTrsf;
+ aTrsf.SetValues (theMat4.m[0][0], theMat4.m[0][1], theMat4.m[0][2], theMat4.m[0][3],
+ theMat4.m[1][0], theMat4.m[1][1], theMat4.m[1][2], theMat4.m[1][3],
+ theMat4.m[2][0], theMat4.m[2][1], theMat4.m[2][2], theMat4.m[2][3]);
+ return aTrsf;
+ }
+
+ //! Convert OpenVR tracked pose.
+ static Aspect_TrackedDevicePose poseVr2Occ (const vr::TrackedDevicePose_t& theVrPose,
+ const Standard_Real theUnitFactor)
+ {
+ Aspect_TrackedDevicePose aPose;
+ aPose.Velocity.SetCoord (theVrPose.vVelocity.v[0], theVrPose.vVelocity.v[1], theVrPose.vVelocity.v[2]);
+ aPose.AngularVelocity.SetCoord (theVrPose.vAngularVelocity.v[0], theVrPose.vAngularVelocity.v[1], theVrPose.vAngularVelocity.v[2]);
+ aPose.IsValidPose = theVrPose.bPoseIsValid;
+ aPose.IsConnectedDevice = theVrPose.bDeviceIsConnected;
+ if (aPose.IsValidPose)
+ {
+ aPose.Orientation = mat34vr2OccTrsf (theVrPose.mDeviceToAbsoluteTracking);
+ if (theUnitFactor != 1.0)
+ {
+ aPose.Orientation.SetTranslationPart (aPose.Orientation.TranslationPart() * theUnitFactor);
+ }
+ }
+ return aPose;
+ }
+
+ //! Find location of default actions manifest file (based on CSF_OCCTResourcePath or CASROOT variables).
+ TCollection_AsciiString defaultActionsManifestInit()
+ {
+ const TCollection_AsciiString THE_ACTIONS_JSON = "occtvr_actions.json";
+ const TCollection_AsciiString aResRoot (OSD_Environment ("CSF_OCCTResourcePath").Value());
+ if (!aResRoot.IsEmpty())
+ {
+ if (OSD_File (aResRoot + "/" + THE_ACTIONS_JSON).Exists())
+ {
+ return aResRoot + "/" + THE_ACTIONS_JSON;
+ }
+ if (OSD_File (aResRoot + "/XRResources/" + THE_ACTIONS_JSON).Exists())
+ {
+ return aResRoot + "/XRResources/" + THE_ACTIONS_JSON;
+ }
+ }
+ const TCollection_AsciiString aCasRoot (OSD_Environment ("CASROOT").Value());
+ if (!aCasRoot.IsEmpty())
+ {
+ if (OSD_File (aCasRoot + "/" + THE_ACTIONS_JSON).Exists())
+ {
+ return aCasRoot + "/" + THE_ACTIONS_JSON;
+ }
+ if (OSD_File (aCasRoot + "/XRResources/" + THE_ACTIONS_JSON).Exists())
+ {
+ return aCasRoot + "/XRResources/" + THE_ACTIONS_JSON;
+ }
+ if (OSD_File (aCasRoot + "/XRResources/src/" + THE_ACTIONS_JSON).Exists())
+ {
+ return aCasRoot + "/XRResources/src/" + THE_ACTIONS_JSON;
+ }
+ }
+ return OSD_Process::ExecutablePath() + "/occtvr_actions.json";
+ }
+}
+#endif
+
+IMPLEMENT_STANDARD_RTTIEXT(Aspect_OpenVRSession, Aspect_XRSession)
+
+struct Aspect_OpenVRSession::VRContext
+{
+#ifdef HAVE_OPENVR
+ vr::TrackedDevicePose_t TrackedPoses[vr::k_unMaxTrackedDeviceCount]; //!< array of tracked devices poses
+ vr::IVRSystem* System; //!< OpenVR session object
+
+ //! Empty constructor.
+ Aspect_OpenVRSession::VRContext() : System (NULL)
+ {
+ memset (TrackedPoses, 0, sizeof(TrackedPoses));
+ }
+
+ //! IVRSystem::PollNextEvent() wrapper.
+ bool PollNextEvent (vr::VREvent_t& theEvent)
+ {
+ return System->PollNextEvent (&theEvent, sizeof(vr::VREvent_t));
+ }
+
+ //! IVRSystem::GetControllerState() wrapper.
+ bool GetControllerState (vr::VRControllerState_t& theState,
+ vr::TrackedDeviceIndex_t theDevice)
+ {
+ return System->GetControllerState (theDevice, &theState, sizeof(vr::VRControllerState_t&));
+ }
+
+ //! Retrieve string property from OpenVR.
+ TCollection_AsciiString getVrTrackedDeviceString (vr::TrackedDeviceIndex_t theDevice,
+ vr::TrackedDeviceProperty theProperty,
+ vr::TrackedPropertyError* theError = NULL)
+ {
+ const uint32_t aBuffLen = System->GetStringTrackedDeviceProperty(theDevice, theProperty, NULL, 0, theError);
+ if (aBuffLen == 0)
+ {
+ return TCollection_AsciiString();
+ }
+
+ NCollection_LocalArray<char> aBuffer (aBuffLen + 1);
+ System->GetStringTrackedDeviceProperty (theDevice, theProperty, aBuffer, aBuffLen, theError);
+ aBuffer[aBuffLen] = '\0';
+ const TCollection_AsciiString aResult (aBuffer);
+ return aResult;
+ }
+#endif
+};
+
+#ifdef HAVE_OPENVR
+//! Image wrapping vr::RenderModel_TextureMap_t.
+class Aspect_OpenVRSession::VRImagePixmap : public Image_PixMap
+{
+public:
+ //! Empty constructor.
+ VRImagePixmap() : myVrTexture (NULL) {}
+
+ //! Load the texture.
+ bool Load (vr::TextureID_t theTexture, const TCollection_AsciiString& theVrModelName)
+ {
+ vr::RenderModel_TextureMap_t* aVrTexture = NULL;
+ vr::EVRRenderModelError aVrError = vr::VRRenderModelError_Loading;
+ for (; aVrError == vr::VRRenderModelError_Loading;)
+ {
+ aVrError = vr::VRRenderModels()->LoadTexture_Async (theTexture, &aVrTexture);
+ OSD::MilliSecSleep (1);
+ }
+ if (aVrError != vr::VRRenderModelError_None
+ || aVrTexture == NULL)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load render model texture: ") + theVrModelName + " - " + int(aVrError), Message_Fail);
+ return false;
+ }
+
+ InitWrapper (Image_Format_RGBA, (Standard_Byte* )aVrTexture->rubTextureMapData, aVrTexture->unWidth, aVrTexture->unHeight);
+ myVrTexture = aVrTexture;
+ return true;
+ }
+
+ virtual ~VRImagePixmap()
+ {
+ if (myVrTexture != NULL)
+ {
+ vr::VRRenderModels()->FreeTexture (myVrTexture);
+ }
+ }
+private:
+ vr::RenderModel_TextureMap_t* myVrTexture;
+};
+
+//! Image_Texture extension using vr::VRRenderModels().
+class Aspect_OpenVRSession::VRTextureSource : public Image_Texture
+{
+public:
+
+ //! Main constructor.
+ VRTextureSource (vr::TextureID_t theTextureId, const TCollection_AsciiString& theVrModelName)
+ : Image_Texture (""), myVrTextureId (theTextureId), myVrModelName (theVrModelName)
+ {
+ myTextureId = TCollection_AsciiString ("texturevr://map_#") + (int )theTextureId;
+ }
+
+protected:
+ //! Read image.
+ virtual Handle(Image_PixMap) ReadImage() const Standard_OVERRIDE
+ {
+ Handle(VRImagePixmap) aPixmap = new VRImagePixmap();
+ if (!aPixmap->Load (myVrTextureId, myVrModelName))
+ {
+ return Handle(VRImagePixmap)();
+ }
+ return aPixmap;
+ }
+private:
+ vr::TextureID_t myVrTextureId;
+ TCollection_AsciiString myVrModelName;
+};
+#endif
+
+// =======================================================================
+// function : IsHmdPresent
+// purpose :
+// =======================================================================
+bool Aspect_OpenVRSession::IsHmdPresent()
+{
+#ifdef HAVE_OPENVR
+ return vr::VR_IsHmdPresent();
+#else
+ return false;
+#endif
+}
+
+// =======================================================================
+// function : defaultActionsManifest
+// purpose :
+// =======================================================================
+TCollection_AsciiString Aspect_OpenVRSession::defaultActionsManifest()
+{
+#ifdef HAVE_OPENVR
+ static const TCollection_AsciiString THE_ACTIONS_JSON_FULL = defaultActionsManifestInit();
+ return THE_ACTIONS_JSON_FULL;
+#else
+ return TCollection_AsciiString();
+#endif
+}
+
+// =======================================================================
+// function : Aspect_OpenVRSession
+// purpose :
+// =======================================================================
+Aspect_OpenVRSession::Aspect_OpenVRSession()
+: myContext (new VRContext())
+{
+#ifdef HAVE_OPENVR
+ myActionsManifest = defaultActionsManifest();
+ myTrackedPoses.Resize (0, (Standard_Integer )vr::k_unMaxTrackedDeviceCount - 1, false);
+ {
+ Handle(Aspect_XRActionSet) aHeadActionSet = new Aspect_XRActionSet ("/actions/generic_head");
+ myActionSets.Add (aHeadActionSet->Id(), aHeadActionSet);
+
+ Handle(Aspect_XRAction) aHeadsetOn = new Aspect_XRAction (aHeadActionSet->Id() + "/in/headset_on_head", Aspect_XRActionType_InputDigital);
+ aHeadActionSet->AddAction (aHeadsetOn);
+ NCollection_Array1<Handle(Aspect_XRAction)>& aGenericSet = myRoleActions[Aspect_XRTrackedDeviceRole_Head];
+ aGenericSet[Aspect_XRGenericAction_IsHeadsetOn] = aHeadsetOn;
+ }
+ for (int aHand = 0; aHand < 2; ++aHand)
+ {
+ NCollection_Array1<Handle(Aspect_XRAction)>& aGenericSet = myRoleActions[aHand == 0 ? Aspect_XRTrackedDeviceRole_LeftHand : Aspect_XRTrackedDeviceRole_RightHand];
+ Handle(Aspect_XRActionSet) anActionSet = new Aspect_XRActionSet (aHand == 0 ? "/actions/generic_left" : "/actions/generic_right");
+ myActionSets.Add (anActionSet->Id(), anActionSet);
+
+ Handle(Aspect_XRAction) anAppMenuClick = new Aspect_XRAction (anActionSet->Id() + "/in/appmenu_click", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (anAppMenuClick);
+ aGenericSet[Aspect_XRGenericAction_InputAppMenu] = anAppMenuClick;
+
+ Handle(Aspect_XRAction) aSysMenuClick = new Aspect_XRAction (anActionSet->Id() + "/in/sysmenu_click", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (aSysMenuClick);
+ aGenericSet[Aspect_XRGenericAction_InputSysMenu] = aSysMenuClick;
+
+ Handle(Aspect_XRAction) aTriggerPull = new Aspect_XRAction (anActionSet->Id() + "/in/trigger_pull", Aspect_XRActionType_InputAnalog);
+ anActionSet->AddAction (aTriggerPull);
+ aGenericSet[Aspect_XRGenericAction_InputTriggerPull] = aTriggerPull;
+
+ Handle(Aspect_XRAction) aTriggerClick = new Aspect_XRAction (anActionSet->Id() + "/in/trigger_click", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (aTriggerClick);
+ aGenericSet[Aspect_XRGenericAction_InputTriggerClick] = aTriggerClick;
+
+ Handle(Aspect_XRAction) aGripClick = new Aspect_XRAction (anActionSet->Id() + "/in/grip_click", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (aGripClick);
+ aGenericSet[Aspect_XRGenericAction_InputGripClick] = aGripClick;
+
+ Handle(Aspect_XRAction) aPadPos = new Aspect_XRAction (anActionSet->Id() + "/in/trackpad_position", Aspect_XRActionType_InputAnalog);
+ anActionSet->AddAction (aPadPos);
+ aGenericSet[Aspect_XRGenericAction_InputTrackPadPosition] = aPadPos;
+
+ Handle(Aspect_XRAction) aPadTouch = new Aspect_XRAction (anActionSet->Id() + "/in/trackpad_touch", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (aPadTouch);
+ aGenericSet[Aspect_XRGenericAction_InputTrackPadTouch] = aPadTouch;
+
+ Handle(Aspect_XRAction) aPadClick = new Aspect_XRAction (anActionSet->Id() + "/in/trackpad_click", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (aPadClick);
+ aGenericSet[Aspect_XRGenericAction_InputTrackPadClick] = aPadClick;
+
+ Handle(Aspect_XRAction) aPoseBase = new Aspect_XRAction (anActionSet->Id() + "/in/pose_base", Aspect_XRActionType_InputPose);
+ anActionSet->AddAction (aPoseBase);
+ aGenericSet[Aspect_XRGenericAction_InputPoseBase] = aPoseBase;
+
+ Handle(Aspect_XRAction) aPoseFront = new Aspect_XRAction (anActionSet->Id() + "/in/pose_front", Aspect_XRActionType_InputPose);
+ anActionSet->AddAction (aPoseFront);
+ aGenericSet[Aspect_XRGenericAction_InputPoseFront] = aPoseFront;
+
+ Handle(Aspect_XRAction) aPoseGrip = new Aspect_XRAction (anActionSet->Id() + "/in/pose_handgrip", Aspect_XRActionType_InputPose);
+ anActionSet->AddAction (aPoseGrip);
+ aGenericSet[Aspect_XRGenericAction_InputPoseHandGrip] = aPoseGrip;
+
+ Handle(Aspect_XRAction) aPoseTip = new Aspect_XRAction (anActionSet->Id() + "/in/pose_tip", Aspect_XRActionType_InputPose);
+ anActionSet->AddAction (aPoseTip);
+ aGenericSet[Aspect_XRGenericAction_InputPoseFingerTip] = aPoseTip;
+
+ Handle(Aspect_XRAction) aHaptic = new Aspect_XRAction (anActionSet->Id() + "/out/haptic", Aspect_XRActionType_OutputHaptic);
+ anActionSet->AddAction (aHaptic);
+ aGenericSet[Aspect_XRGenericAction_OutputHaptic] = aHaptic;
+
+ Handle(Aspect_XRAction) aThumbsctickPos = new Aspect_XRAction (anActionSet->Id() + "/in/thumbstick_position", Aspect_XRActionType_InputAnalog);
+ anActionSet->AddAction (aThumbsctickPos);
+ aGenericSet[Aspect_XRGenericAction_InputThumbstickPosition] = aThumbsctickPos;
+
+ Handle(Aspect_XRAction) aThumbsctickTouch = new Aspect_XRAction (anActionSet->Id() + "/in/thumbstick_touch", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (aThumbsctickTouch);
+ aGenericSet[Aspect_XRGenericAction_InputThumbstickTouch] = aThumbsctickTouch;
+
+ Handle(Aspect_XRAction) aThumbsctickClick = new Aspect_XRAction (anActionSet->Id() + "/in/thumbstick_click", Aspect_XRActionType_InputDigital);
+ anActionSet->AddAction (aThumbsctickClick);
+ aGenericSet[Aspect_XRGenericAction_InputThumbstickClick] = aThumbsctickClick;
+ }
+#endif
+}
+
+// =======================================================================
+// function : ~Aspect_OpenVRSession
+// purpose :
+// =======================================================================
+Aspect_OpenVRSession::~Aspect_OpenVRSession()
+{
+ closeVR();
+ delete myContext;
+}
+
+// =======================================================================
+// function : closeVR
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::closeVR()
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL)
+ {
+ vr::VR_Shutdown();
+ myContext->System = NULL;
+ }
+#endif
+}
+
+// =======================================================================
+// function : getVRSystem
+// purpose :
+// =======================================================================
+void* Aspect_OpenVRSession::getVRSystem() const
+{
+#ifdef HAVE_OPENVR
+ return myContext->System;
+#else
+ return NULL;
+#endif
+}
+
+// =======================================================================
+// function : Close
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::Close()
+{
+ closeVR();
+}
+
+// =======================================================================
+// function : IsOpen
+// purpose :
+// =======================================================================
+bool Aspect_OpenVRSession::IsOpen() const
+{
+#ifdef HAVE_OPENVR
+ return myContext->System != NULL;
+#else
+ return false;
+#endif
+}
+
+// =======================================================================
+// function : Open
+// purpose :
+// =======================================================================
+bool Aspect_OpenVRSession::Open()
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL)
+ {
+ return true;
+ }
+
+ vr::EVRInitError aVrError = vr::VRInitError_None;
+ myContext->System = vr::VR_Init (&aVrError, vr::VRApplication_Scene);
+ if (aVrError != vr::VRInitError_None)
+ {
+ myContext->System = NULL;
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to init VR runtime: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError),
+ Message_Fail);
+ Close();
+ return false;
+ }
+
+ /*vr::IVRRenderModels* aRenderModels = (vr::IVRRenderModels* )vr::VR_GetGenericInterface (vr::IVRRenderModels_Version, &aVrError);
+ if (aRenderModels == NULL)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Unable to get render model interface: ") + vr::VR_GetVRInitErrorAsEnglishDescription (aVrError),
+ Message_Fail);;
+ }*/
+
+ NCollection_Vec2<uint32_t> aRenderSize;
+ myContext->System->GetRecommendedRenderTargetSize (&aRenderSize.x(), &aRenderSize.y());
+ myRendSize = NCollection_Vec2<int> (aRenderSize);
+ myDispFreq = myContext->System->GetFloatTrackedDeviceProperty (vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
+ if (myRendSize.x() <= 0
+ || myRendSize.y() <= 0)
+ {
+ Close();
+ return false;
+ }
+ updateProjectionFrustums();
+ initInput();
+ return true;
+#else
+ Message::DefaultMessenger()->Send ("Open CASCADE has been built without OpenVR support", Message_Fail);
+ return false;
+#endif
+}
+
+// =======================================================================
+// function : initInput
+// purpose :
+// =======================================================================
+bool Aspect_OpenVRSession::initInput()
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System == NULL)
+ {
+ return false;
+ }
+
+ if (!OSD_File (myActionsManifest).Exists())
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to open actions manifest '") + myActionsManifest + "'", Message_Fail);
+ return false;
+ }
+
+ vr::EVRInputError aVrError = vr::VRInput()->SetActionManifestPath (myActionsManifest.ToCString());
+ if (aVrError != vr::VRInputError_None)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load actions manifest '") + myActionsManifest
+ + "': " + getVRInputError (aVrError), Message_Fail);
+ return false;
+ }
+
+ bool hasErrors = false;
+ for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
+ {
+ const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
+ for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
+ {
+ const Handle(Aspect_XRAction)& anAction = anActionIter.Value();
+ vr::VRActionHandle_t anActionHandle = 0;
+ aVrError = vr::VRInput()->GetActionHandle (anAction->Id().ToCString(), &anActionHandle);
+ if (aVrError == vr::VRInputError_None)
+ {
+ anAction->SetRawHandle (anActionHandle);
+ }
+ else
+ {
+ hasErrors = true;
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load action '") + anAction->Id() + "' from '" + myActionsManifest
+ + "': " + getVRInputError (aVrError), Message_Fail);
+ }
+ }
+
+ vr::VRActionSetHandle_t anActionSetHandle = 0;
+ aVrError = vr::VRInput()->GetActionSetHandle (anActionSet->Id().ToCString(), &anActionSetHandle);
+ if (aVrError == vr::VRInputError_None)
+ {
+ anActionSet->SetRawHandle (anActionSetHandle);
+ }
+ else
+ {
+ hasErrors = true;
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load action set '") + anActionSet->Id() + "' from '" + myActionsManifest
+ + "': " + getVRInputError (aVrError), Message_Fail);
+ }
+ }
+ return !hasErrors;
+#else
+ return false;
+#endif
+}
+
+// =======================================================================
+// function : GetString
+// purpose :
+// =======================================================================
+TCollection_AsciiString Aspect_OpenVRSession::GetString (InfoString theInfo) const
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL)
+ {
+ vr::ETrackedDeviceProperty aVrProp = vr::Prop_Invalid;
+ switch (theInfo)
+ {
+ case InfoString_Vendor: aVrProp = vr::Prop_ManufacturerName_String; break;
+ case InfoString_Device: aVrProp = vr::Prop_ModelNumber_String; break;
+ case InfoString_Tracker: aVrProp = vr::Prop_TrackingSystemName_String; break;
+ case InfoString_SerialNumber: aVrProp = vr::Prop_SerialNumber_String; break;
+ }
+ if (aVrProp != vr::Prop_Invalid)
+ {
+ return myContext->getVrTrackedDeviceString (vr::k_unTrackedDeviceIndex_Hmd, aVrProp);
+ }
+ }
+#else
+ (void )theInfo;
+#endif
+ return TCollection_AsciiString();
+}
+
+// =======================================================================
+// function : NamedTrackedDevice
+// purpose :
+// =======================================================================
+Standard_Integer Aspect_OpenVRSession::NamedTrackedDevice (Aspect_XRTrackedDeviceRole theDevice) const
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL)
+ {
+ vr::TrackedDeviceIndex_t aDevIndex = vr::k_unTrackedDeviceIndexInvalid;
+ switch (theDevice)
+ {
+ case Aspect_XRTrackedDeviceRole_Head: aDevIndex = vr::k_unTrackedDeviceIndex_Hmd; break;
+ case Aspect_XRTrackedDeviceRole_LeftHand: aDevIndex = myContext->System->GetTrackedDeviceIndexForControllerRole (vr::TrackedControllerRole_LeftHand); break;
+ case Aspect_XRTrackedDeviceRole_RightHand: aDevIndex = myContext->System->GetTrackedDeviceIndexForControllerRole (vr::TrackedControllerRole_RightHand); break;
+ case Aspect_XRTrackedDeviceRole_Other: break;
+ }
+ if (aDevIndex == vr::k_unTrackedDeviceIndexInvalid)
+ {
+ return -1;
+ }
+ return (Standard_Integer )aDevIndex;
+ }
+#else
+ (void )theDevice;
+#endif
+ return -1;
+}
+
+// =======================================================================
+// function : loadRenderModel
+// purpose :
+// =======================================================================
+Handle(Graphic3d_ArrayOfTriangles) Aspect_OpenVRSession::loadRenderModel (Standard_Integer theDevice,
+ Standard_Boolean theToApplyUnitFactor,
+ Handle(Image_Texture)& theTexture)
+{
+ if (theDevice < 0)
+ {
+ return Handle(Graphic3d_ArrayOfTriangles)();
+ }
+#ifdef HAVE_OPENVR
+ if (myContext->System == NULL)
+ {
+ return Handle(Graphic3d_ArrayOfTriangles)();
+ }
+
+ const TCollection_AsciiString aRenderModelName = myContext->getVrTrackedDeviceString (theDevice, vr::Prop_RenderModelName_String);
+ if (aRenderModelName.IsEmpty())
+ {
+ return Handle(Graphic3d_ArrayOfTriangles)();
+ }
+
+ vr::RenderModel_t* aVrModel = NULL;
+ vr::EVRRenderModelError aVrError = vr::VRRenderModelError_Loading;
+ for (; aVrError == vr::VRRenderModelError_Loading;)
+ {
+ aVrError = vr::VRRenderModels()->LoadRenderModel_Async (aRenderModelName.ToCString(), &aVrModel);
+ OSD::MilliSecSleep (1);
+ }
+ if (aVrError != vr::VRRenderModelError_None)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Unable to load render model: ") + aRenderModelName + " - " + int(aVrError), Message_Fail);
+ return Handle(Graphic3d_ArrayOfTriangles)();
+ }
+
+ if (aVrModel->diffuseTextureId >= 0)
+ {
+ theTexture = new VRTextureSource (aVrModel->diffuseTextureId, aRenderModelName);
+ }
+
+ const float aScale = theToApplyUnitFactor ? float(myUnitFactor) : 1.0f;
+ Handle(Graphic3d_ArrayOfTriangles) aTris = new Graphic3d_ArrayOfTriangles ((int )aVrModel->unVertexCount, (int )aVrModel->unTriangleCount * 3,
+ Graphic3d_ArrayFlags_VertexNormal | Graphic3d_ArrayFlags_VertexTexel);
+ for (uint32_t aVertIter = 0; aVertIter < aVrModel->unVertexCount; ++aVertIter)
+ {
+ const vr::RenderModel_Vertex_t& aVert = aVrModel->rVertexData[aVertIter];
+ aTris->AddVertex (aVert.vPosition.v[0] * aScale, aVert.vPosition.v[1] * aScale, aVert.vPosition.v[2] * aScale,
+ aVert.vNormal.v[0], aVert.vNormal.v[1], aVert.vNormal.v[2],
+ aVert.rfTextureCoord[0], aVert.rfTextureCoord[1]);
+ }
+ for (uint32_t aTriIter = 0; aTriIter < aVrModel->unTriangleCount; ++aTriIter)
+ {
+ aTris->AddEdges (aVrModel->rIndexData[aTriIter * 3 + 0] + 1,
+ aVrModel->rIndexData[aTriIter * 3 + 1] + 1,
+ aVrModel->rIndexData[aTriIter * 3 + 2] + 1);
+ }
+
+ vr::VRRenderModels()->FreeRenderModel (aVrModel);
+ return aTris;
+#else
+ (void )theToApplyUnitFactor;
+ (void )theTexture;
+ return Handle(Graphic3d_ArrayOfTriangles)();
+#endif
+}
+
+// =======================================================================
+// function : EyeToHeadTransform
+// purpose :
+// =======================================================================
+NCollection_Mat4<double> Aspect_OpenVRSession::EyeToHeadTransform (Aspect_Eye theEye) const
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL)
+ {
+ const vr::HmdMatrix34_t aMatVr = myContext->System->GetEyeToHeadTransform (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left);
+ gp_Trsf aTrsf = mat34vr2OccTrsf (aMatVr);
+ if (myUnitFactor != 1.0)
+ {
+ aTrsf.SetTranslationPart (aTrsf.TranslationPart() * myUnitFactor);
+ }
+ NCollection_Mat4<double> aMat4;
+ aTrsf.GetMat4 (aMat4);
+ return aMat4;
+ }
+#else
+ (void )theEye;
+#endif
+ return NCollection_Mat4<double>();
+}
+
+// =======================================================================
+// function : ProjectionMatrix
+// purpose :
+// =======================================================================
+NCollection_Mat4<double> Aspect_OpenVRSession::ProjectionMatrix (Aspect_Eye theEye,
+ double theZNear,
+ double theZFar) const
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL)
+ {
+ const vr::HmdMatrix44_t aMat4 = myContext->System->GetProjectionMatrix (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left,
+ (float )theZNear, (float )theZFar);
+ return mat44vr2Occ (aMat4);
+ }
+#else
+ (void )theEye;
+ (void )theZNear;
+ (void )theZFar;
+#endif
+ return NCollection_Mat4<double>();
+}
+
+// =======================================================================
+// function : updateProjectionFrustums
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::updateProjectionFrustums()
+{
+#ifdef HAVE_OPENVR
+ Aspect_FrustumLRBT<float> aFrustL, aFrustR;
+ myContext->System->GetProjectionRaw (vr::Eye_Left, &aFrustL.Left, &aFrustL.Right, &aFrustL.Top, &aFrustL.Bottom);
+ myContext->System->GetProjectionRaw (vr::Eye_Right, &aFrustR.Left, &aFrustR.Right, &aFrustR.Top, &aFrustR.Bottom);
+ myFrustumL = Aspect_FrustumLRBT<double> (aFrustL);
+ myFrustumR = Aspect_FrustumLRBT<double> (aFrustR);
+ std::swap (myFrustumL.Top, myFrustumL.Bottom);
+ std::swap (myFrustumR.Top, myFrustumR.Bottom);
+
+ const NCollection_Vec2<double> aTanHalfFov (NCollection_Vec4<float>(-aFrustL.Left, aFrustL.Right, -aFrustR.Left, aFrustR.Right).maxComp(),
+ NCollection_Vec4<float>(-aFrustL.Top, aFrustL.Bottom, -aFrustR.Top, aFrustR.Bottom).maxComp());
+ myAspect = aTanHalfFov.x() / aTanHalfFov.y();
+ myFieldOfView = 2.0 * ATan(aTanHalfFov.y()) * 180.0 / M_PI;
+
+ // Intra-ocular Distance can be changed in runtime
+ //const vr::HmdMatrix34_t aLeftToHead = myContext->System->GetEyeToHeadTransform (vr::Eye_Left);
+ //const vr::HmdMatrix34_t aRightToHead = myContext->System->GetEyeToHeadTransform (vr::Eye_Right);
+ //myIod = aRightToHead.m[0][3] - aLeftToHead.m[0][3];
+ myIod = myContext->System->GetFloatTrackedDeviceProperty (vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_UserIpdMeters_Float);
+ myIod *= myUnitFactor;
+#endif
+}
+
+// =======================================================================
+// function : SetTrackingOrigin
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::SetTrackingOrigin (TrackingUniverseOrigin theOrigin)
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL)
+ {
+ vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseStanding;
+ switch (theOrigin)
+ {
+ case TrackingUniverseOrigin_Seated: anOrigin = vr::TrackingUniverseSeated; break;
+ case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
+ }
+ vr::VRCompositor()->SetTrackingSpace (anOrigin);
+ }
+#endif
+ myTrackOrigin = theOrigin;
+}
+
+// =======================================================================
+// function : WaitPoses
+// purpose :
+// =======================================================================
+bool Aspect_OpenVRSession::WaitPoses()
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System == NULL)
+ {
+ return false;
+ }
+
+ switch (vr::VRCompositor()->GetTrackingSpace())
+ {
+ case vr::TrackingUniverseSeated:
+ myTrackOrigin = TrackingUniverseOrigin_Seated;
+ break;
+ case vr::TrackingUniverseRawAndUncalibrated:
+ case vr::TrackingUniverseStanding:
+ myTrackOrigin = TrackingUniverseOrigin_Standing;
+ break;
+ }
+
+ const vr::EVRCompositorError aVRError = vr::VRCompositor()->WaitGetPoses (myContext->TrackedPoses, vr::k_unMaxTrackedDeviceCount, NULL, 0);
+ if (aVRError != vr::VRCompositorError_None)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Compositor wait poses error: ") + getVRCompositorError (aVRError), Message_Trace);
+ }
+
+ for (Standard_Integer aPoseIter = myTrackedPoses.Lower(); aPoseIter <= myTrackedPoses.Upper(); ++aPoseIter)
+ {
+ const vr::TrackedDevicePose_t& aVrPose = myContext->TrackedPoses[aPoseIter];
+ myTrackedPoses[aPoseIter] = poseVr2Occ (aVrPose, myUnitFactor);
+ }
+
+ if (myContext->TrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid)
+ {
+ myHeadPose = myTrackedPoses[vr::k_unTrackedDeviceIndex_Hmd].Orientation;
+ updateProjectionFrustums();
+ }
+ return aVRError != vr::VRCompositorError_None;
+#else
+ return false;
+#endif
+}
+
+// =======================================================================
+// function : GetDigitalActionData
+// purpose :
+// =======================================================================
+Aspect_XRDigitalActionData Aspect_OpenVRSession::GetDigitalActionData (const Handle(Aspect_XRAction)& theAction) const
+{
+ if (theAction.IsNull()
+ || theAction->Type() != Aspect_XRActionType_InputDigital)
+ {
+ throw Standard_ProgramError("Aspect_OpenVRSession::GetDigitalActionData() called for wrong action");
+ }
+
+ Aspect_XRDigitalActionData anActionData;
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL
+ && theAction->RawHandle() != 0)
+ {
+ vr::InputDigitalActionData_t aNewData = {};
+ vr::EVRInputError anInErr = vr::VRInput()->GetDigitalActionData (theAction->RawHandle(), &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
+ if (anInErr != vr::VRInputError_None)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
+ return anActionData;
+ }
+
+ anActionData.IsActive = aNewData.bActive;
+ anActionData.IsChanged = aNewData.bChanged;
+ anActionData.IsPressed = aNewData.bState;
+ anActionData.UpdateTime = aNewData.fUpdateTime;
+ anActionData.ActiveOrigin = aNewData.activeOrigin;
+ }
+#endif
+ return anActionData;
+}
+
+// =======================================================================
+// function : GetAnalogActionData
+// purpose :
+// =======================================================================
+Aspect_XRAnalogActionData Aspect_OpenVRSession::GetAnalogActionData (const Handle(Aspect_XRAction)& theAction) const
+{
+ if (theAction.IsNull()
+ || theAction->Type() != Aspect_XRActionType_InputAnalog)
+ {
+ throw Standard_ProgramError("Aspect_OpenVRSession::GetAnalogActionData() called for wrong action");
+ }
+
+ Aspect_XRAnalogActionData anActionData;
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL
+ && theAction->RawHandle() != 0)
+ {
+ vr::InputAnalogActionData_t aNewData = {};
+ vr::EVRInputError anInErr = vr::VRInput()->GetAnalogActionData (theAction->RawHandle(), &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
+ if (anInErr != vr::VRInputError_None)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
+ return anActionData;
+ }
+
+ anActionData.IsActive = aNewData.bActive;
+ anActionData.VecXYZ.SetValues (aNewData.x, aNewData.y, aNewData.z);
+ anActionData.DeltaXYZ.SetValues (aNewData.deltaX, aNewData.deltaY, aNewData.deltaZ);
+ anActionData.UpdateTime = aNewData.fUpdateTime;
+ anActionData.ActiveOrigin = aNewData.activeOrigin;
+ }
+#endif
+ return anActionData;
+}
+
+// =======================================================================
+// function : GetPoseActionDataForNextFrame
+// purpose :
+// =======================================================================
+Aspect_XRPoseActionData Aspect_OpenVRSession::GetPoseActionDataForNextFrame (const Handle(Aspect_XRAction)& theAction) const
+{
+ if (theAction.IsNull()
+ || theAction->Type() != Aspect_XRActionType_InputPose)
+ {
+ throw Standard_ProgramError("Aspect_OpenVRSession::GetPoseActionDataForNextFrame() called for wrong action");
+ }
+
+ Aspect_XRPoseActionData anActionData;
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL
+ && theAction->RawHandle() != 0)
+ {
+ vr::ETrackingUniverseOrigin anOrigin = vr::TrackingUniverseSeated;
+ switch (myTrackOrigin)
+ {
+ case TrackingUniverseOrigin_Seated: anOrigin = vr::TrackingUniverseSeated; break;
+ case TrackingUniverseOrigin_Standing: anOrigin = vr::TrackingUniverseStanding; break;
+ }
+ vr::InputPoseActionData_t aNewData = {};
+ vr::EVRInputError anInErr = vr::VRInput()->GetPoseActionDataForNextFrame (theAction->RawHandle(), anOrigin, &aNewData, sizeof(aNewData), vr::k_ulInvalidInputValueHandle);
+ if (anInErr != vr::VRInputError_None)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Input Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
+ return anActionData;
+ }
+
+ anActionData.Pose = poseVr2Occ (aNewData.pose, myUnitFactor);
+ anActionData.IsActive = aNewData.bActive;
+ anActionData.ActiveOrigin = aNewData.activeOrigin;
+ }
+#endif
+ return anActionData;
+}
+
+// =======================================================================
+// function : triggerHapticVibrationAction
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::triggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
+ const Aspect_XRHapticActionData& theParams)
+{
+ if (theAction.IsNull()
+ || theAction->Type() != Aspect_XRActionType_OutputHaptic)
+ {
+ throw Standard_ProgramError("Aspect_OpenVRSession::triggerHapticVibrationAction() called for wrong action");
+ }
+
+#ifdef HAVE_OPENVR
+ if (myContext->System != NULL
+ && theAction->RawHandle() != 0)
+ {
+ Aspect_XRHapticActionData aParams = theParams;
+ if (!theParams.IsValid())
+ {
+ // preset for aborting
+ aParams.Duration = 0.0f;
+ aParams.Frequency = 1.0f;
+ aParams.Amplitude = 0.1f;
+ }
+ vr::EVRInputError anInErr = vr::VRInput()->TriggerHapticVibrationAction (theAction->RawHandle(),
+ aParams.Delay, aParams.Duration, aParams.Frequency, aParams.Amplitude,
+ vr::k_ulInvalidInputValueHandle);
+ if (anInErr != vr::VRInputError_None)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Output Error on '") + theAction->Id() + "': " + getVRInputError (anInErr), Message_Fail);
+ }
+ }
+#else
+ (void )theParams;
+#endif
+}
+
+// =======================================================================
+// function : ProcessEvents
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::ProcessEvents()
+{
+#ifdef HAVE_OPENVR
+ if (myContext->System == NULL)
+ {
+ return;
+ }
+
+ // process OpenVR events
+ vr::VREvent_t aVREvent = {};
+ for (; myContext->PollNextEvent (aVREvent);)
+ {
+ switch (aVREvent.eventType)
+ {
+ case vr::VREvent_TrackedDeviceActivated: onTrackedDeviceActivated ((int )aVREvent.trackedDeviceIndex); break;
+ case vr::VREvent_TrackedDeviceDeactivated: onTrackedDeviceDeactivated((int )aVREvent.trackedDeviceIndex); break;
+ case vr::VREvent_TrackedDeviceUpdated: onTrackedDeviceUpdated ((int )aVREvent.trackedDeviceIndex); break;
+ }
+ }
+
+ // process OpenVR action state
+ if (myActionSets.Extent() > 0)
+ {
+ NCollection_LocalArray<vr::VRActiveActionSet_t, 8> anActionSets (myActionSets.Extent());
+ memset (anActionSets, 0, sizeof(vr::VRActiveActionSet_t) * myActionSets.Extent());
+ for (Standard_Integer aSetIter = 0; aSetIter < myActionSets.Extent(); ++aSetIter)
+ {
+ anActionSets[aSetIter].ulActionSet = myActionSets.FindFromIndex (aSetIter + 1)->RawHandle();
+ }
+ vr::VRInput()->UpdateActionState (anActionSets, sizeof(vr::VRActiveActionSet_t), myActionSets.Extent());
+ }
+
+ WaitPoses();
+
+ for (Aspect_XRActionSetMap::Iterator aSetIter (myActionSets); aSetIter.More(); aSetIter.Next())
+ {
+ const Handle(Aspect_XRActionSet)& anActionSet = aSetIter.Value();
+ for (Aspect_XRActionMap::Iterator anActionIter (anActionSet->Actions()); anActionIter.More(); anActionIter.Next())
+ {
+ const Handle(Aspect_XRAction)& anAction = anActionIter.Value();
+ if (anAction->RawHandle() == 0
+ || anAction->Id().IsEmpty())
+ {
+ continue;
+ }
+
+ switch (anAction->Type())
+ {
+ case Aspect_XRActionType_InputDigital:
+ {
+ Aspect_XRDigitalActionData aData = GetDigitalActionData (anAction);
+ //if (aData.IsChanged) { std::cout << " " << anAction->Id() << " pressed: " << aData.IsPressed << "\n"; }
+ break;
+ }
+ case Aspect_XRActionType_InputAnalog:
+ {
+ Aspect_XRAnalogActionData aData = GetAnalogActionData (anAction);
+ //if (aData.IsChanged()) { std::cout << " " << anAction->Id() << " changed: " << aData.VecXYZ[0] << " " << aData.VecXYZ[1] << " " << aData.VecXYZ[2] << "\n"; }
+ break;
+ }
+ case Aspect_XRActionType_InputPose:
+ {
+ GetPoseActionDataForNextFrame (anAction);
+ break;
+ }
+ }
+ }
+ }
+
+ // process OpenVR controller state using deprecated API
+ //for (vr::TrackedDeviceIndex_t aDevIter = 0; aDevIter < vr::k_unMaxTrackedDeviceCount; ++aDevIter) {
+ // vr::VRControllerState_t aCtrlState = {}; if (myContext->GetControllerState (aCtrlState, aDevIter)) { aCtrlState.ulButtonPressed == 0; }
+ //}
+#endif
+}
+
+// =======================================================================
+// function : onTrackedDeviceActivated
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::onTrackedDeviceActivated (Standard_Integer theDeviceIndex)
+{
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " attached", Message_Trace);
+}
+
+// =======================================================================
+// function : onTrackedDeviceDeactivated
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::onTrackedDeviceDeactivated (Standard_Integer theDeviceIndex)
+{
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " detached", Message_Trace);
+}
+
+// =======================================================================
+// function : onTrackedDeviceUpdated
+// purpose :
+// =======================================================================
+void Aspect_OpenVRSession::onTrackedDeviceUpdated (Standard_Integer theDeviceIndex)
+{
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("OpenVR, Device ") + theDeviceIndex + " updated", Message_Trace);
+}
+
+// =======================================================================
+// function : SubmitEye
+// purpose :
+// =======================================================================
+bool Aspect_OpenVRSession::SubmitEye (void* theTexture,
+ Aspect_GraphicsLibrary theGraphicsLib,
+ Aspect_ColorSpace theColorSpace,
+ Aspect_Eye theEye)
+{
+ if (theTexture == NULL)
+ {
+ return false;
+ }
+#ifdef HAVE_OPENVR
+ if (myContext->System == NULL)
+ {
+ return false;
+ }
+
+ vr::Texture_t aVRTexture = { (void* )theTexture, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
+ switch (theGraphicsLib)
+ {
+ case Aspect_GraphicsLibrary_OpenGL:
+ aVRTexture.eType = vr::TextureType_OpenGL;
+ break;
+ default:
+ Message::DefaultMessenger()->Send ("Compositor error: unsupported Graphics API", Message_Fail);
+ return false;
+ }
+ switch (theColorSpace)
+ {
+ case Aspect_ColorSpace_sRGB: aVRTexture.eColorSpace = vr::ColorSpace_Gamma; break;
+ case Aspect_ColorSpace_Linear: aVRTexture.eColorSpace = vr::ColorSpace_Linear; break;
+ }
+
+ const vr::EVRCompositorError aVRError = vr::VRCompositor()->Submit (theEye == Aspect_Eye_Right ? vr::Eye_Right : vr::Eye_Left, &aVRTexture);
+ if (aVRError != vr::VRCompositorError_None)
+ {
+ if (aVRError != vr::VRCompositorError_AlreadySubmitted)
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError), Message_Fail);
+ }
+ else
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Compositor Error: ") + getVRCompositorError (aVRError), Message_Trace);
+ }
+ return false;
+ }
+ return true;
+#else
+ (void )theGraphicsLib;
+ (void )theColorSpace;
+ (void )theEye;
+ return false;
+#endif
+}
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_OpenVRSession_HeaderFile
+#define _Aspect_OpenVRSession_HeaderFile
+
+#include <Aspect_XRSession.hxx>
+
+//! OpenVR wrapper implementing Aspect_XRSession interface.
+class Aspect_OpenVRSession : public Aspect_XRSession
+{
+ DEFINE_STANDARD_RTTIEXT(Aspect_OpenVRSession, Aspect_XRSession)
+public:
+
+ //! Return TRUE if an HMD may be presented on the system (e.g. to show VR checkbox in application GUI).
+ //! This is fast check, and even if it returns TRUE, opening session may fail.
+ Standard_EXPORT static bool IsHmdPresent();
+
+public:
+
+ //! Empty constructor.
+ Standard_EXPORT Aspect_OpenVRSession();
+
+ //! Destructor.
+ Standard_EXPORT virtual ~Aspect_OpenVRSession();
+
+ //! Return TRUE if session is opened.
+ Standard_EXPORT virtual bool IsOpen() const Standard_OVERRIDE;
+
+ //! Initialize session.
+ Standard_EXPORT virtual bool Open() Standard_OVERRIDE;
+
+ //! Release session.
+ Standard_EXPORT virtual void Close() Standard_OVERRIDE;
+
+ //! Fetch actual poses of tracked devices.
+ Standard_EXPORT virtual bool WaitPoses() Standard_OVERRIDE;
+
+ //! Return recommended viewport Width x Height for rendering into VR.
+ virtual NCollection_Vec2<int> RecommendedViewport() const Standard_OVERRIDE { return myRendSize; }
+
+ //! Return transformation from eye to head.
+ //! vr::GetEyeToHeadTransform() wrapper.
+ Standard_EXPORT virtual NCollection_Mat4<double> EyeToHeadTransform (Aspect_Eye theEye) const Standard_OVERRIDE;
+
+ //! Return projection matrix.
+ Standard_EXPORT virtual NCollection_Mat4<double> ProjectionMatrix (Aspect_Eye theEye,
+ double theZNear,
+ double theZFar) const Standard_OVERRIDE;
+
+ //! Return TRUE.
+ virtual bool HasProjectionFrustums() const Standard_OVERRIDE { return true; }
+
+ //! Receive XR events.
+ Standard_EXPORT virtual void ProcessEvents() Standard_OVERRIDE;
+
+ //! Submit texture eye to XR Composer.
+ //! @param theTexture [in] texture handle
+ //! @param theGraphicsLib [in] graphics library in which texture handle is defined
+ //! @param theColorSpace [in] texture color space;
+ //! sRGB means no color conversion by composer;
+ //! Linear means to sRGB color conversion by composer
+ //! @param theEye [in] eye to display
+ //! @return FALSE on error
+ Standard_EXPORT virtual bool SubmitEye (void* theTexture,
+ Aspect_GraphicsLibrary theGraphicsLib,
+ Aspect_ColorSpace theColorSpace,
+ Aspect_Eye theEye) Standard_OVERRIDE;
+
+ //! Query information.
+ Standard_EXPORT virtual TCollection_AsciiString GetString (InfoString theInfo) const Standard_OVERRIDE;
+
+ //! Return index of tracked device of known role.
+ Standard_EXPORT virtual Standard_Integer NamedTrackedDevice (Aspect_XRTrackedDeviceRole theDevice) const Standard_OVERRIDE;
+
+ //! Fetch data for digital input action (like button).
+ Standard_EXPORT virtual Aspect_XRDigitalActionData GetDigitalActionData (const Handle(Aspect_XRAction)& theAction) const Standard_OVERRIDE;
+
+ //! Fetch data for analog input action (like axis).
+ Standard_EXPORT virtual Aspect_XRAnalogActionData GetAnalogActionData (const Handle(Aspect_XRAction)& theAction) const Standard_OVERRIDE;
+
+ //! Fetch data for pose input action (like fingertip position).
+ Standard_EXPORT virtual Aspect_XRPoseActionData GetPoseActionDataForNextFrame (const Handle(Aspect_XRAction)& theAction) const Standard_OVERRIDE;
+
+ //! Set tracking origin.
+ Standard_EXPORT virtual void SetTrackingOrigin (TrackingUniverseOrigin theOrigin) Standard_OVERRIDE;
+
+protected:
+
+ //! Find location of default actions manifest file (based on CSF_OCCTResourcePath or CASROOT variables).
+ Standard_EXPORT TCollection_AsciiString defaultActionsManifest();
+
+ //! Release OpenVR device.
+ Standard_EXPORT void closeVR();
+
+ //! Update projection frustums.
+ Standard_EXPORT virtual void updateProjectionFrustums();
+
+ //! Init VR input.
+ Standard_EXPORT virtual bool initInput();
+
+ //! Handle tracked device activation.
+ Standard_EXPORT virtual void onTrackedDeviceActivated (Standard_Integer theDeviceIndex);
+
+ //! Handle tracked device deactivation.
+ Standard_EXPORT virtual void onTrackedDeviceDeactivated (Standard_Integer theDeviceIndex);
+
+ //! Handle tracked device update.
+ Standard_EXPORT virtual void onTrackedDeviceUpdated (Standard_Integer theDeviceIndex);
+
+ //! Trigger vibration.
+ Standard_EXPORT virtual void triggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
+ const Aspect_XRHapticActionData& theParams) Standard_OVERRIDE;
+
+ //! Return model for displaying device.
+ Standard_EXPORT virtual Handle(Graphic3d_ArrayOfTriangles) loadRenderModel (Standard_Integer theDevice,
+ Standard_Boolean theToApplyUnitFactor,
+ Handle(Image_Texture)& theTexture) Standard_OVERRIDE;
+
+protected:
+
+ //! Access vr::IVRSystem* - OpenVR session object.
+ Standard_EXPORT void* getVRSystem() const;
+
+private:
+
+ //! Internal fields
+ struct VRContext;
+ class VRImagePixmap;
+ class VRTextureSource;
+
+protected:
+
+ VRContext* myContext;
+ TCollection_AsciiString myActionsManifest;
+
+};
+
+#endif // _Aspect_OpenVRSession_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_TrackedDevicePose_HeaderFile
+#define _Aspect_TrackedDevicePose_HeaderFile
+
+#include <gp_Trsf.hxx>
+#include <NCollection_Array1.hxx>
+
+//! Describes a single pose for a tracked object (for XR).
+struct Aspect_TrackedDevicePose
+{
+ gp_Trsf Orientation; //!< device to absolute transformation
+ gp_Vec Velocity; //!< velocity in tracker space in m/s
+ gp_Vec AngularVelocity; //!< angular velocity in radians/s
+ bool IsValidPose; //!< indicates valid pose
+ bool IsConnectedDevice; //!< indicates connected state
+
+ //! Empty constructor.
+ Aspect_TrackedDevicePose() : IsValidPose (false), IsConnectedDevice (false) {}
+};
+
+//! Array of tracked poses.
+typedef NCollection_Array1<Aspect_TrackedDevicePose> Aspect_TrackedDevicePoseArray;
+
+#endif // _Aspect_TrackedDevicePose_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRAction_HeaderFile
+#define _Aspect_XRAction_HeaderFile
+
+#include <Aspect_XRActionType.hxx>
+#include <NCollection_IndexedDataMap.hxx>
+#include <Standard_Transient.hxx>
+#include <Standard_Type.hxx>
+#include <TCollection_AsciiString.hxx>
+
+//! XR action definition.
+class Aspect_XRAction : public Standard_Transient
+{
+ DEFINE_STANDARD_RTTIEXT(Aspect_XRAction, Standard_Transient)
+public:
+
+ //! Return action id.
+ const TCollection_AsciiString& Id() const { return myId; }
+
+ //! Return action type.
+ Aspect_XRActionType Type() const { return myType; }
+
+ //! Return TRUE if action is defined.
+ bool IsValid() const { return myRawHandle != 0; }
+
+ //! Return action handle.
+ uint64_t RawHandle() const { return myRawHandle; }
+
+ //! Set action handle.
+ void SetRawHandle (uint64_t theHande) { myRawHandle = theHande; }
+
+ //! Main constructor.
+ Aspect_XRAction (const TCollection_AsciiString& theId,
+ const Aspect_XRActionType theType)
+ : myId (theId), myRawHandle (0), myType (theType) {}
+
+protected:
+ TCollection_AsciiString myId; //!< action id
+ uint64_t myRawHandle; //!< action handle
+ Aspect_XRActionType myType; //!< action type
+};
+
+//! Map of actions with action Id as a key.
+typedef NCollection_IndexedDataMap<TCollection_AsciiString, Handle(Aspect_XRAction), TCollection_AsciiString> Aspect_XRActionMap;
+
+#endif // _Aspect_XRAction_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRActionSet_HeaderFile
+#define _Aspect_XRActionSet_HeaderFile
+
+#include <Aspect_XRAction.hxx>
+
+//! XR action set.
+class Aspect_XRActionSet : public Standard_Transient
+{
+ DEFINE_STANDARD_RTTIEXT(Aspect_XRActionSet, Standard_Transient)
+public:
+
+ //! Return action id.
+ const TCollection_AsciiString& Id() const { return myId; }
+
+ //! Return action handle.
+ uint64_t RawHandle() const { return myRawHandle; }
+
+ //! Set action handle.
+ void SetRawHandle (uint64_t theHande) { myRawHandle = theHande; }
+
+ //! Add action.
+ void AddAction (const Handle(Aspect_XRAction)& theAction)
+ {
+ myActions.Add (theAction->Id(), theAction);
+ }
+
+ //! Return map of actions.
+ const Aspect_XRActionMap& Actions() const { return myActions; }
+
+ //! Main constructor.
+ Aspect_XRActionSet (const TCollection_AsciiString& theId)
+ : myId (theId), myRawHandle (0) {}
+
+protected:
+ TCollection_AsciiString myId; //!< action set id
+ uint64_t myRawHandle; //!< action set handle
+ Aspect_XRActionMap myActions; //!< map of actions
+};
+
+typedef NCollection_IndexedDataMap<TCollection_AsciiString, Handle(Aspect_XRActionSet), TCollection_AsciiString> Aspect_XRActionSetMap;
+
+#endif // _Aspect_XRActionSet_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRActionType_HeaderFile
+#define _Aspect_XRActionType_HeaderFile
+
+//! XR action type.
+enum Aspect_XRActionType
+{
+ Aspect_XRActionType_InputDigital, //!< boolean input (like button)
+ Aspect_XRActionType_InputAnalog, //!< analog input (1/2/3 axes)
+ Aspect_XRActionType_InputPose, //!< positional input
+ Aspect_XRActionType_InputSkeletal, //!< skeletal input
+ Aspect_XRActionType_OutputHaptic //!< haptic output (vibration)
+};
+
+#endif // _Aspect_XRActionType_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRAnalogActionData_HeaderFile
+#define _Aspect_XRAnalogActionData_HeaderFile
+
+#include <NCollection_Vec3.hxx>
+
+//! Analog input XR action data.
+struct Aspect_XRAnalogActionData
+{
+ uint64_t ActiveOrigin; //!< The origin that caused this action's current state
+ float UpdateTime; //!< Time relative to now when this event happened. Will be negative to indicate a past time
+ NCollection_Vec3<float> VecXYZ; //!< the current state of this action
+ NCollection_Vec3<float> DeltaXYZ; //!< deltas since the previous update
+ bool IsActive; //!< whether or not this action is currently available to be bound in the active action set
+
+ //! Return TRUE if delta is non-zero.
+ bool IsChanged() { return !DeltaXYZ.IsEqual (NCollection_Vec3<float> (0.0f, 0.0f, 0.0f)); }
+
+ //! Empty constructor.
+ Aspect_XRAnalogActionData() : ActiveOrigin (0), UpdateTime (0.0f), IsActive (false) {}
+};
+
+#endif // _Aspect_XRAnalogActionData_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRDigitalActionData_HeaderFile
+#define _Aspect_XRDigitalActionData_HeaderFile
+
+#include <Standard_TypeDef.hxx>
+
+//! Digital input XR action data.
+struct Aspect_XRDigitalActionData
+{
+ uint64_t ActiveOrigin; //!< The origin that caused this action's current state
+ float UpdateTime; //!< Time relative to now when this event happened. Will be negative to indicate a past time
+ bool IsActive; //!< whether or not this action is currently available to be bound in the active action set
+ bool IsPressed; //!< Aspect_InputActionType_Digital state - The current state of this action; will be true if currently pressed
+ bool IsChanged; //!< Aspect_InputActionType_Digital state - this is true if the state has changed since the last frame
+
+ //! Empty constructor.
+ Aspect_XRDigitalActionData() : ActiveOrigin (0), UpdateTime (0.0f), IsActive (false), IsPressed (false), IsChanged (false) {}
+};
+
+#endif // _Aspect_XRDigitalActionData_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRGenericAction_HeaderFile
+#define _Aspect_XRGenericAction_HeaderFile
+
+//! Generic XR action.
+enum Aspect_XRGenericAction
+{
+ Aspect_XRGenericAction_IsHeadsetOn, //!< headset is on/off head
+ Aspect_XRGenericAction_InputAppMenu, //!< application menu button pressed/released
+ Aspect_XRGenericAction_InputSysMenu, //!< system menu button pressed/released
+ Aspect_XRGenericAction_InputTriggerPull, //!< trigger squeezing [0..1], 1 to click
+ Aspect_XRGenericAction_InputTriggerClick, //!< trigger clicked/released
+ Aspect_XRGenericAction_InputGripClick, //!< grip state on/off
+ Aspect_XRGenericAction_InputTrackPadPosition, //!< trackpad 2D position [-1,+1] with X and Y axes
+ Aspect_XRGenericAction_InputTrackPadTouch, //!< trackpad touched/untouched
+ Aspect_XRGenericAction_InputTrackPadClick, //!< trackpad clicked/released
+ Aspect_XRGenericAction_InputThumbstickPosition, //!< thumbstick 2D position [-1,+1] with X and Y axes
+ Aspect_XRGenericAction_InputThumbstickTouch, //!< thumbstick touched/untouched
+ Aspect_XRGenericAction_InputThumbstickClick, //!< thumbstick clicked/released
+ Aspect_XRGenericAction_InputPoseBase, //!< base position of hand
+ Aspect_XRGenericAction_InputPoseFront, //!< front position of hand
+ Aspect_XRGenericAction_InputPoseHandGrip, //!< position of main handgrip
+ Aspect_XRGenericAction_InputPoseFingerTip, //!< position of main fingertip
+ Aspect_XRGenericAction_OutputHaptic //!< haptic output (vibration)
+};
+enum { Aspect_XRGenericAction_NB = Aspect_XRGenericAction_OutputHaptic + 1 };
+
+#endif // _Aspect_XRGenericAction_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRHapticActionData_HeaderFile
+#define _Aspect_XRHapticActionData_HeaderFile
+
+//! Haptic output XR action data.
+struct Aspect_XRHapticActionData
+{
+ float Delay; //!< delay in seconds before start
+ float Duration; //!< duration in seconds
+ float Frequency; //!< vibration frequency
+ float Amplitude; //!< vibration amplitude
+
+ //! Return TRUE if data is not empty.
+ bool IsValid() const
+ {
+ return Duration > 0.0f
+ && Amplitude > 0.0f
+ && Frequency > 0.0f
+ && Delay >= 0.0f;
+ }
+
+ //! Empty constructor.
+ Aspect_XRHapticActionData() : Delay (0.0f), Duration (0.0f), Frequency (0.0f), Amplitude (0.0f) {}
+};
+
+#endif // _Aspect_XRHapticActionData_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRPoseActionData_HeaderFile
+#define _Aspect_XRPoseActionData_HeaderFile
+
+#include <Aspect_TrackedDevicePose.hxx>
+#include <Standard_TypeDef.hxx>
+
+//! Pose input XR action data.
+struct Aspect_XRPoseActionData
+{
+ Aspect_TrackedDevicePose Pose; //!< pose state
+ uint64_t ActiveOrigin; //!< The origin that caused this action's current state
+ bool IsActive; //!< whether or not this action is currently available to be bound in the active action set
+
+ //! Empty constructor.
+ Aspect_XRPoseActionData() : ActiveOrigin (0), IsActive (false) {}
+};
+
+#endif // _Aspect_XRPoseActionData_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Aspect_XRSession.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(Aspect_XRSession, Standard_Transient)
+IMPLEMENT_STANDARD_RTTIEXT(Aspect_XRAction, Standard_Transient)
+IMPLEMENT_STANDARD_RTTIEXT(Aspect_XRActionSet, Standard_Transient)
+
+// =======================================================================
+// function : Aspect_XRSession
+// purpose :
+// =======================================================================
+Aspect_XRSession::Aspect_XRSession()
+: myTrackOrigin (TrackingUniverseOrigin_Standing),
+ myTrackedPoses (0, 0),
+ myUnitFactor (1.0),
+ myAspect (1.0),
+ myFieldOfView (90.0),
+ myIod (0.0),
+ myDispFreq (0.0f)
+{
+ for (Standard_Integer aRoleIter = 0; aRoleIter < Aspect_XRTrackedDeviceRole_NB; ++aRoleIter)
+ {
+ myRoleActions[aRoleIter].Resize (0, Aspect_XRGenericAction_NB - 1, false);
+ }
+}
+
+// =======================================================================
+// function : AbortHapticVibrationAction
+// purpose :
+// =======================================================================
+void Aspect_XRSession::AbortHapticVibrationAction (const Handle(Aspect_XRAction)& theAction)
+{
+ triggerHapticVibrationAction (theAction, Aspect_XRHapticActionData());
+}
+
+// =======================================================================
+// function : TriggerHapticVibrationAction
+// purpose :
+// =======================================================================
+void Aspect_XRSession::TriggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
+ const Aspect_XRHapticActionData& theParams)
+{
+ if (!theParams.IsValid())
+ {
+ throw Standard_ProgramError("Aspect_OpenVRSession::TriggerHapticVibrationAction() called for wrong action");
+ }
+ triggerHapticVibrationAction (theAction, theParams);
+}
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRSession_HeaderFile
+#define _Aspect_XRSession_HeaderFile
+
+#include <Aspect_ColorSpace.hxx>
+#include <Aspect_Eye.hxx>
+#include <Aspect_FrustumLRBT.hxx>
+#include <Aspect_GraphicsLibrary.hxx>
+#include <Aspect_XRActionSet.hxx>
+#include <Aspect_XRAnalogActionData.hxx>
+#include <Aspect_XRDigitalActionData.hxx>
+#include <Aspect_XRGenericAction.hxx>
+#include <Aspect_XRHapticActionData.hxx>
+#include <Aspect_XRPoseActionData.hxx>
+#include <Aspect_XRTrackedDeviceRole.hxx>
+#include <gp_Trsf.hxx>
+#include <NCollection_Array1.hxx>
+
+class Graphic3d_ArrayOfTriangles;
+class Image_Texture;
+
+//! Extended Reality (XR) Session interface.
+class Aspect_XRSession : public Standard_Transient
+{
+ DEFINE_STANDARD_RTTIEXT(Aspect_XRSession, Standard_Transient)
+public:
+
+ //! Identifies which style of tracking origin the application wants to use for the poses it is requesting.
+ enum TrackingUniverseOrigin
+ {
+ TrackingUniverseOrigin_Seated, //! poses are provided relative to the seated zero pose
+ TrackingUniverseOrigin_Standing, //! poses are provided relative to the safe bounds configured by the user
+ };
+
+public:
+
+ //! Return TRUE if session is opened.
+ virtual bool IsOpen() const = 0;
+
+ //! Initialize session.
+ virtual bool Open() = 0;
+
+ //! Release session.
+ virtual void Close() = 0;
+
+ //! Fetch actual poses of tracked devices.
+ virtual bool WaitPoses() = 0;
+
+ //! Return recommended viewport Width x Height for rendering into VR.
+ virtual NCollection_Vec2<int> RecommendedViewport() const = 0;
+
+ //! Return transformation from eye to head.
+ virtual NCollection_Mat4<double> EyeToHeadTransform (Aspect_Eye theEye) const = 0;
+
+ //! Return transformation from head to eye.
+ NCollection_Mat4<double> HeadToEyeTransform (Aspect_Eye theEye) const
+ {
+ NCollection_Mat4<double> aMat;
+ EyeToHeadTransform (theEye).Inverted (aMat);
+ return aMat;
+ }
+
+ //! Return projection matrix.
+ virtual NCollection_Mat4<double> ProjectionMatrix (Aspect_Eye theEye,
+ double theZNear,
+ double theZFar) const = 0;
+
+ //! Return FALSE if projection frustums are unsupported and general 4x4 projection matrix should be fetched instead
+ virtual bool HasProjectionFrustums() const = 0;
+
+ //! Receive XR events.
+ virtual void ProcessEvents() = 0;
+
+ //! Submit texture eye to XR Composer.
+ //! @param theTexture [in] texture handle
+ //! @param theGraphicsLib [in] graphics library in which texture handle is defined
+ //! @param theColorSpace [in] texture color space;
+ //! sRGB means no color conversion by composer;
+ //! Linear means to sRGB color conversion by composer
+ //! @param theEye [in] eye to display
+ //! @return FALSE on error
+ virtual bool SubmitEye (void* theTexture,
+ Aspect_GraphicsLibrary theGraphicsLib,
+ Aspect_ColorSpace theColorSpace,
+ Aspect_Eye theEye) = 0;
+
+ //! Return unit scale factor defined as scale factor for m (meters); 1.0 by default.
+ Standard_Real UnitFactor() const { return myUnitFactor; }
+
+ //! Set unit scale factor.
+ void SetUnitFactor (Standard_Real theFactor) { myUnitFactor = theFactor; }
+
+ //! Return aspect ratio.
+ Standard_Real Aspect() const { return myAspect; }
+
+ //! Return field of view.
+ Standard_Real FieldOfView() const { return myFieldOfView; }
+
+ //! Return Intra-ocular Distance (IOD); also known as Interpupillary Distance (IPD).
+ //! Defined in meters by default (@sa UnitFactor()).
+ Standard_Real IOD() const { return myIod; }
+
+ //! Return display frequency or 0 if unknown.
+ Standard_ShortReal DisplayFrequency() const { return myDispFreq; }
+
+ //! Return projection frustum.
+ //! @sa HasProjectionFrustums().
+ const Aspect_FrustumLRBT<double>& ProjectionFrustum (Aspect_Eye theEye) const
+ {
+ return theEye == Aspect_Eye_Right ? myFrustumR : myFrustumL;
+ }
+
+ //! Return head orientation in right-handed system:
+ //! +y is up
+ //! +x is to the right
+ //! -z is forward
+ //! Distance unit is meters by default (@sa UnitFactor()).
+ const gp_Trsf& HeadPose() const { return myHeadPose; }
+
+ //! Return left hand orientation.
+ gp_Trsf LeftHandPose() const
+ {
+ const Standard_Integer aDevice = NamedTrackedDevice (Aspect_XRTrackedDeviceRole_LeftHand);
+ return aDevice != -1 ? myTrackedPoses[aDevice].Orientation : gp_Trsf();
+ }
+
+ //! Return right hand orientation.
+ gp_Trsf RightHandPose() const
+ {
+ const Standard_Integer aDevice = NamedTrackedDevice (Aspect_XRTrackedDeviceRole_RightHand);
+ return aDevice != -1 ? myTrackedPoses[aDevice].Orientation : gp_Trsf();
+ }
+
+ //! Return number of tracked poses array.
+ const Aspect_TrackedDevicePoseArray& TrackedPoses() const { return myTrackedPoses; }
+
+ //! Return TRUE if device orientation is defined.
+ bool HasTrackedPose (Standard_Integer theDevice) const { return myTrackedPoses[theDevice].IsValidPose; }
+
+ //! Return index of tracked device of known role, or -1 if undefined.
+ virtual Standard_Integer NamedTrackedDevice (Aspect_XRTrackedDeviceRole theDevice) const = 0;
+
+ //! Load model for displaying device.
+ //! @param theDevice [in] device index
+ //! @param theTexture [out] texture source
+ //! @return model triangulation or NULL if not found
+ Handle(Graphic3d_ArrayOfTriangles) LoadRenderModel (Standard_Integer theDevice,
+ Handle(Image_Texture)& theTexture)
+ {
+ return loadRenderModel (theDevice, true, theTexture);
+ }
+
+ //! Load model for displaying device.
+ //! @param theDevice [in] device index
+ //! @param theToApplyUnitFactor [in] flag to apply unit scale factor
+ //! @param theTexture [out] texture source
+ //! @return model triangulation or NULL if not found
+ Handle(Graphic3d_ArrayOfTriangles) LoadRenderModel (Standard_Integer theDevice,
+ Standard_Boolean theToApplyUnitFactor,
+ Handle(Image_Texture)& theTexture)
+ {
+ return loadRenderModel (theDevice, theToApplyUnitFactor, theTexture);
+ }
+
+ //! Fetch data for digital input action (like button).
+ //! @param theAction [in] action of Aspect_XRActionType_InputDigital type
+ virtual Aspect_XRDigitalActionData GetDigitalActionData (const Handle(Aspect_XRAction)& theAction) const = 0;
+
+ //! Fetch data for digital input action (like axis).
+ //! @param theAction [in] action of Aspect_XRActionType_InputAnalog type
+ virtual Aspect_XRAnalogActionData GetAnalogActionData (const Handle(Aspect_XRAction)& theAction) const = 0;
+
+ //! Fetch data for pose input action (like fingertip position).
+ //! The returned values will match the values returned by the last call to WaitPoses().
+ //! @param theAction [in] action of Aspect_XRActionType_InputPose type
+ virtual Aspect_XRPoseActionData GetPoseActionDataForNextFrame (const Handle(Aspect_XRAction)& theAction) const = 0;
+
+ //! Trigger vibration.
+ Standard_EXPORT void TriggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
+ const Aspect_XRHapticActionData& theParams);
+
+ //! Abort vibration.
+ Standard_EXPORT void AbortHapticVibrationAction (const Handle(Aspect_XRAction)& theAction);
+
+ //! Return tracking origin.
+ TrackingUniverseOrigin TrackingOrigin() const { return myTrackOrigin; }
+
+ //! Set tracking origin.
+ virtual void SetTrackingOrigin (TrackingUniverseOrigin theOrigin) { myTrackOrigin = theOrigin; }
+
+ //! Return generic action for specific hand or NULL if undefined.
+ const Handle(Aspect_XRAction)& GenericAction (Aspect_XRTrackedDeviceRole theDevice,
+ Aspect_XRGenericAction theAction) const
+ {
+ const NCollection_Array1<Handle(Aspect_XRAction)>& anActions = myRoleActions[theDevice];
+ return anActions[theAction];
+ }
+
+public:
+
+ //! Info string enumeration.
+ enum InfoString
+ {
+ InfoString_Vendor,
+ InfoString_Device,
+ InfoString_Tracker,
+ InfoString_SerialNumber,
+ };
+
+ //! Query information.
+ virtual TCollection_AsciiString GetString (InfoString theInfo) const = 0;
+
+protected:
+
+ //! Empty constructor.
+ Standard_EXPORT Aspect_XRSession();
+
+ //! Load model for displaying device.
+ //! @param theDevice [in] device index
+ //! @param theToApplyUnitFactor [in] flag to apply unit scale factor
+ //! @param theTexture [out] texture source
+ //! @return model triangulation or NULL if not found
+ virtual Handle(Graphic3d_ArrayOfTriangles) loadRenderModel (Standard_Integer theDevice,
+ Standard_Boolean theToApplyUnitFactor,
+ Handle(Image_Texture)& theTexture) = 0;
+
+ //! Trigger vibration.
+ virtual void triggerHapticVibrationAction (const Handle(Aspect_XRAction)& theAction,
+ const Aspect_XRHapticActionData& theParams) = 0;
+
+protected:
+
+ NCollection_Array1<Handle(Aspect_XRAction)>
+ myRoleActions[Aspect_XRTrackedDeviceRole_NB]; //!< generic actions
+ Aspect_XRActionSetMap myActionSets; //!< actions sets
+ TrackingUniverseOrigin myTrackOrigin; //!< tracking origin
+ Aspect_TrackedDevicePoseArray myTrackedPoses; //!< array of tracked poses
+ gp_Trsf myHeadPose; //!< head orientation
+ NCollection_Vec2<int> myRendSize; //!< viewport Width x Height for rendering into VR
+ Aspect_FrustumLRBT<double> myFrustumL; //!< left eye projection frustum
+ Aspect_FrustumLRBT<double> myFrustumR; //!< right eye projection frustum
+ Standard_Real myUnitFactor; //!< unit scale factor defined as scale factor for m (meters)
+ Standard_Real myAspect; //!< aspect ratio
+ Standard_Real myFieldOfView; //!< field of view
+ Standard_Real myIod; //!< intra-ocular distance in meters
+ Standard_ShortReal myDispFreq; //!< display frequency
+
+};
+
+#endif // _Aspect_XRSession_HeaderFile
--- /dev/null
+// Copyright (c) 2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_XRTrackedDeviceRole_HeaderFile
+#define _Aspect_XRTrackedDeviceRole_HeaderFile
+
+//! Predefined tracked devices.
+enum Aspect_XRTrackedDeviceRole
+{
+ Aspect_XRTrackedDeviceRole_Head, //!< head
+ Aspect_XRTrackedDeviceRole_LeftHand, //!< left hand
+ Aspect_XRTrackedDeviceRole_RightHand, //!< right hand
+ Aspect_XRTrackedDeviceRole_Other, //!< other devices
+};
+enum { Aspect_XRTrackedDeviceRole_NB = Aspect_XRTrackedDeviceRole_Other + 1 };
+
+#endif // _Aspect_XRTrackedDeviceRole_HeaderFile
Aspect_Background.hxx
Aspect_CircularGrid.cxx
Aspect_CircularGrid.hxx
+Aspect_ColorSpace.hxx
Aspect_Convert.hxx
Aspect_Display.hxx
Aspect_DisplayConnection.cxx
Aspect_DisplayConnection.hxx
Aspect_DisplayConnectionDefinitionError.hxx
Aspect_Drawable.hxx
+Aspect_Eye.hxx
Aspect_FBConfig.hxx
Aspect_FillMethod.hxx
+Aspect_FrustumLRBT.hxx
Aspect_GenId.cxx
Aspect_GenId.hxx
Aspect_GradientBackground.cxx
Aspect_GradientBackground.hxx
Aspect_GradientFillMethod.hxx
Aspect_GraphicDeviceDefinitionError.hxx
+Aspect_GraphicsLibrary.hxx
Aspect_Grid.cxx
Aspect_Grid.hxx
Aspect_GridDrawMode.hxx
Aspect_GridType.hxx
Aspect_NeutralWindow.cxx
Aspect_NeutralWindow.hxx
+Aspect_OpenVRSession.cxx
+Aspect_OpenVRSession.hxx
Aspect_Handle.hxx
Aspect_HatchStyle.hxx
Aspect_IdentDefinitionError.hxx
Aspect_ScrollDelta.hxx
Aspect_Touch.hxx
Aspect_TouchMap.hxx
+Aspect_TrackedDevicePose.hxx
Aspect_TypeOfColorScaleData.hxx
Aspect_TypeOfColorScaleOrientation.hxx
Aspect_TypeOfColorScalePosition.hxx
Aspect_WindowDefinitionError.hxx
Aspect_WindowError.hxx
Aspect_XAtom.hxx
+Aspect_XRAction.hxx
+Aspect_XRActionSet.hxx
+Aspect_XRActionType.hxx
+Aspect_XRAnalogActionData.hxx
+Aspect_XRDigitalActionData.hxx
+Aspect_XRGenericAction.hxx
+Aspect_XRHapticActionData.hxx
+Aspect_XRPoseActionData.hxx
+Aspect_XRSession.cxx
+Aspect_XRSession.hxx
+Aspect_XRTrackedDeviceRole.hxx
Aspect_XWD.hxx
#else
di << "OpenGL: desktop\n";
#endif
+#ifdef HAVE_OPENVR
+ di << "OpenVR enabled (HAVE_OPENVR)\n";
+#else
+ di << "OpenVR disabled\n";
+#endif
#ifdef HAVE_RAPIDJSON
di << "RapidJSON enabled (HAVE_RAPIDJSON)\n";
#else
#include <Graphic3d_CView.hxx>
+#include <Aspect_OpenVRSession.hxx>
#include <Graphic3d_Layer.hxx>
#include <Graphic3d_MapIteratorOfMapOfStructure.hxx>
#include <Graphic3d_StructureManager.hxx>
myIsActive (Standard_False),
myIsRemoved (Standard_False),
myShadingModel (Graphic3d_TOSM_FRAGMENT),
- myVisualization (Graphic3d_TOV_WIREFRAME)
+ myVisualization (Graphic3d_TOV_WIREFRAME),
+ myUnitFactor (1.0)
{
myId = myStructureManager->Identification (this);
}
//=======================================================================
Graphic3d_CView::~Graphic3d_CView()
{
+ myXRSession.Nullify();
if (!IsRemoved())
{
myStructureManager->UnIdentification (this);
myShadingModel = theModel;
}
+
+// =======================================================================
+// function : SetUnitFactor
+// purpose :
+// =======================================================================
+void Graphic3d_CView::SetUnitFactor (Standard_Real theFactor)
+{
+ if (theFactor <= 0.0)
+ {
+ throw Standard_ProgramError ("Graphic3d_CView::SetUnitFactor() - invalid unit factor");
+ }
+ myUnitFactor = theFactor;
+ if (!myXRSession.IsNull())
+ {
+ myXRSession->SetUnitFactor (theFactor);
+ }
+}
+
+// =======================================================================
+// function : IsActiveXR
+// purpose :
+// =======================================================================
+bool Graphic3d_CView::IsActiveXR() const
+{
+ return !myXRSession.IsNull()
+ && myXRSession->IsOpen();
+}
+
+// =======================================================================
+// function : InitXR
+// purpose :
+// =======================================================================
+bool Graphic3d_CView::InitXR()
+{
+ if (myXRSession.IsNull())
+ {
+ myXRSession = new Aspect_OpenVRSession();
+ myXRSession->SetUnitFactor (myUnitFactor);
+ }
+ if (!myXRSession->IsOpen())
+ {
+ myXRSession->Open();
+ if (myBackXRCamera.IsNull())
+ {
+ // backup camera properties
+ myBackXRCamera = new Graphic3d_Camera (myCamera);
+ }
+ }
+ return myXRSession->IsOpen();
+}
+
+// =======================================================================
+// function : ReleaseXR
+// purpose :
+// =======================================================================
+void Graphic3d_CView::ReleaseXR()
+{
+ if (!myXRSession.IsNull())
+ {
+ if (myXRSession->IsOpen()
+ && !myBackXRCamera.IsNull())
+ {
+ // restore projection properties overridden by HMD
+ myCamera->SetFOV2d (myBackXRCamera->FOV2d());
+ myCamera->SetFOVy (myBackXRCamera->FOVy());
+ myCamera->SetAspect(myBackXRCamera->Aspect());
+ myCamera->SetIOD (myBackXRCamera->GetIODType(), myBackXRCamera->IOD());
+ myCamera->SetZFocus(myBackXRCamera->ZFocusType(), myBackXRCamera->ZFocus());
+ myCamera->ResetCustomProjection();
+ myBackXRCamera.Nullify();
+ }
+ myXRSession->Close();
+ }
+}
+
+//=======================================================================
+//function : ProcessXRInput
+//purpose :
+//=======================================================================
+void Graphic3d_CView::ProcessXRInput()
+{
+ if (myRenderParams.StereoMode == Graphic3d_StereoMode_OpenVR
+ && myCamera->ProjectionType() == Graphic3d_Camera::Projection_Stereo)
+ {
+ InitXR();
+ }
+ else
+ {
+ ReleaseXR();
+ }
+
+ if (!IsActiveXR())
+ {
+ myBaseXRCamera.Nullify();
+ myPosedXRCamera.Nullify();
+ return;
+ }
+
+ myXRSession->ProcessEvents();
+ Invalidate();
+
+ myCamera->SetFOV2d (myRenderParams.HmdFov2d);
+ myCamera->SetAspect(myXRSession->Aspect());
+ myCamera->SetFOVy (myXRSession->FieldOfView());
+ myCamera->SetIOD (Graphic3d_Camera::IODType_Absolute, myXRSession->IOD());
+ myCamera->SetZFocus(Graphic3d_Camera::FocusType_Absolute, 1.0 * myUnitFactor);
+
+ // VR APIs tend to decompose camera orientation-projection matrices into the following components:
+ // @begincode
+ // Model * [View * Eye^-1] * [Projection]
+ // @endcode
+ // so that Eye position is encoded into Orientation matrix, and there should be 2 Orientation matrices and 2 Projection matrices to make the stereo.
+ // Graphic3d_Camera historically follows different decomposition, with Eye position encoded into Projection matrix,
+ // so that there is only 1 Orientation matrix (matching mono view) and 2 Projection matrices.
+ if (myXRSession->HasProjectionFrustums())
+ {
+ // note that this definition does not include a small forward/backward offset from head to eye
+ myCamera->SetCustomStereoFrustums (myXRSession->ProjectionFrustum (Aspect_Eye_Left),
+ myXRSession->ProjectionFrustum (Aspect_Eye_Right));
+ }
+ else
+ {
+ const Graphic3d_Mat4d aPoseL = myXRSession->HeadToEyeTransform (Aspect_Eye_Left);
+ const Graphic3d_Mat4d aPoseR = myXRSession->HeadToEyeTransform (Aspect_Eye_Right);
+ const Graphic3d_Mat4d aProjL = myXRSession->ProjectionMatrix (Aspect_Eye_Left, myCamera->ZNear(), myCamera->ZFar());
+ const Graphic3d_Mat4d aProjR = myXRSession->ProjectionMatrix (Aspect_Eye_Right, myCamera->ZNear(), myCamera->ZFar());
+ myCamera->SetCustomStereoProjection (aProjL * aPoseL, aProjR * aPoseR);
+ }
+ myBaseXRCamera = myCamera;
+ if (myPosedXRCamera.IsNull())
+ {
+ myPosedXRCamera = new Graphic3d_Camera();
+ }
+ SynchronizeXRBaseToPosedCamera();
+}
+
+//=======================================================================
+//function : SynchronizeXRBaseToPosedCamera
+//purpose :
+//=======================================================================
+void Graphic3d_CView::SynchronizeXRBaseToPosedCamera()
+{
+ if (!myPosedXRCamera.IsNull())
+ {
+ ComputeXRPosedCameraFromBase (*myPosedXRCamera, myXRSession->HeadPose());
+ }
+}
+
+//=======================================================================
+//function : ComputeXRPosedCameraFromBase
+//purpose :
+//=======================================================================
+void Graphic3d_CView::ComputeXRPosedCameraFromBase (Graphic3d_Camera& theCam,
+ const gp_Trsf& theXRTrsf) const
+{
+ theCam.Copy (myBaseXRCamera);
+
+ // convert head pose into camera transformation
+ const gp_Ax3 anAxVr (gp::Origin(), gp::DZ(), gp::DX());
+ const gp_Ax3 aCameraCS (gp::Origin(), -myBaseXRCamera->Direction(), -myBaseXRCamera->SideRight());
+ gp_Trsf aTrsfCS;
+ aTrsfCS.SetTransformation (aCameraCS, anAxVr);
+ const gp_Trsf aTrsfToCamera = aTrsfCS * theXRTrsf * aTrsfCS.Inverted();
+ gp_Trsf aTrsfToEye;
+ aTrsfToEye.SetTranslation (myBaseXRCamera->Eye().XYZ());
+
+ const gp_Trsf aTrsf = aTrsfToEye * aTrsfToCamera;
+ const gp_Dir anUpNew = myBaseXRCamera->Up().Transformed (aTrsf);
+ const gp_Dir aDirNew = myBaseXRCamera->Direction().Transformed (aTrsf);
+ const gp_Pnt anEyeNew = gp::Origin().Translated (aTrsf.TranslationPart());
+ theCam.SetUp (anUpNew);
+ theCam.SetDirectionFromEye (aDirNew);
+ theCam.MoveEyeTo (anEyeNew);
+}
+
+//=======================================================================
+//function : SynchronizeXRPosedToBaseCamera
+//purpose :
+//=======================================================================
+void Graphic3d_CView::SynchronizeXRPosedToBaseCamera()
+{
+ if (myPosedXRCameraCopy.IsNull()
+ || myPosedXRCamera.IsNull()
+ || myBaseXRCamera.IsNull()
+ || myCamera != myPosedXRCamera)
+ {
+ return;
+ }
+
+ if (myPosedXRCameraCopy->Eye().IsEqual (myPosedXRCamera->Eye(), gp::Resolution())
+ && (myPosedXRCameraCopy->Distance() - myPosedXRCamera->Distance()) <= gp::Resolution()
+ && myPosedXRCameraCopy->Direction().IsEqual (myPosedXRCamera->Direction(), gp::Resolution())
+ && myPosedXRCameraCopy->Up().IsEqual (myPosedXRCamera->Up(), gp::Resolution()))
+ {
+ // avoid floating point math in case of no changes
+ return;
+ }
+
+ // re-compute myBaseXRCamera from myPosedXRCamera by applying reversed head pose transformation
+ ComputeXRBaseCameraFromPosed (myPosedXRCamera, myXRSession->HeadPose());
+ myPosedXRCameraCopy->Copy (myPosedXRCamera);
+}
+
+//=======================================================================
+//function : ComputeXRBaseCameraFromPosed
+//purpose :
+//=======================================================================
+void Graphic3d_CView::ComputeXRBaseCameraFromPosed (const Graphic3d_Camera& theCamPosed,
+ const gp_Trsf& thePoseTrsf)
+{
+ const gp_Ax3 anAxVr (gp::Origin(), gp::DZ(), gp::DX());
+ const gp_Ax3 aCameraCS (gp::Origin(), -myBaseXRCamera->Direction(), -myBaseXRCamera->SideRight());
+ gp_Trsf aTrsfCS;
+ aTrsfCS.SetTransformation (aCameraCS, anAxVr);
+ const gp_Trsf aTrsfToCamera = aTrsfCS * thePoseTrsf * aTrsfCS.Inverted();
+ const gp_Trsf aTrsfCamToHead = aTrsfToCamera.Inverted();
+ const gp_Dir anUpNew = theCamPosed.Up().Transformed (aTrsfCamToHead);
+ const gp_Dir aDirNew = theCamPosed.Direction().Transformed (aTrsfCamToHead);
+ const gp_Pnt anEyeNew = theCamPosed.Eye().Translated (aTrsfToCamera.TranslationPart().Reversed());
+ myBaseXRCamera->SetUp (anUpNew);
+ myBaseXRCamera->SetDirectionFromEye (aDirNew);
+ myBaseXRCamera->MoveEyeTo (anEyeNew);
+}
+
+//=======================================================================
+//function : TurnViewXRCamera
+//purpose :
+//=======================================================================
+void Graphic3d_CView::TurnViewXRCamera (const gp_Trsf& theTrsfTurn)
+{
+ // use current eye position as an anchor
+ const Handle(Graphic3d_Camera)& aCamBase = myBaseXRCamera;
+ gp_Trsf aHeadTrsfLocal;
+ aHeadTrsfLocal.SetTranslationPart (myXRSession->HeadPose().TranslationPart());
+ const gp_Pnt anEyeAnchor = PoseXRToWorld (aHeadTrsfLocal).TranslationPart();
+
+ // turn the view
+ aCamBase->SetDirectionFromEye (aCamBase->Direction().Transformed (theTrsfTurn));
+
+ // recompute new eye
+ const gp_Ax3 anAxVr (gp::Origin(), gp::DZ(), gp::DX());
+ const gp_Ax3 aCameraCS (gp::Origin(), -aCamBase->Direction(), -aCamBase->SideRight());
+ gp_Trsf aTrsfCS;
+ aTrsfCS.SetTransformation (aCameraCS, anAxVr);
+ const gp_Trsf aTrsfToCamera = aTrsfCS * aHeadTrsfLocal * aTrsfCS.Inverted();
+ const gp_Pnt anEyeNew = anEyeAnchor.Translated (aTrsfToCamera.TranslationPart().Reversed());
+ aCamBase->MoveEyeTo (anEyeNew);
+
+ SynchronizeXRBaseToPosedCamera();
+}
+
+//=======================================================================
+//function : SetupXRPosedCamera
+//purpose :
+//=======================================================================
+void Graphic3d_CView::SetupXRPosedCamera()
+{
+ if (!myPosedXRCamera.IsNull())
+ {
+ myCamera = myPosedXRCamera;
+ if (myPosedXRCameraCopy.IsNull())
+ {
+ myPosedXRCameraCopy = new Graphic3d_Camera();
+ }
+ myPosedXRCameraCopy->Copy (myPosedXRCamera);
+ }
+}
+
+//=======================================================================
+//function : UnsetXRPosedCamera
+//purpose :
+//=======================================================================
+void Graphic3d_CView::UnsetXRPosedCamera()
+{
+ if (myCamera == myPosedXRCamera
+ && !myBaseXRCamera.IsNull())
+ {
+ SynchronizeXRPosedToBaseCamera();
+ myCamera = myBaseXRCamera;
+ }
+}
+
+//=======================================================================
+//function : DiagnosticInformation
+//purpose :
+//=======================================================================
+void Graphic3d_CView::DiagnosticInformation (TColStd_IndexedDataMapOfStringString& theDict,
+ Graphic3d_DiagnosticInfo theFlags) const
+{
+ if ((theFlags & Graphic3d_DiagnosticInfo_Device) != 0
+ && !myXRSession.IsNull())
+ {
+ TCollection_AsciiString aVendor = myXRSession->GetString (Aspect_XRSession::InfoString_Vendor);
+ TCollection_AsciiString aDevice = myXRSession->GetString (Aspect_XRSession::InfoString_Device);
+ TCollection_AsciiString aTracker = myXRSession->GetString (Aspect_XRSession::InfoString_Tracker);
+ TCollection_AsciiString aSerial = myXRSession->GetString (Aspect_XRSession::InfoString_SerialNumber);
+ TCollection_AsciiString aDisplay = TCollection_AsciiString()
+ + myXRSession->RecommendedViewport().x() + "x" + myXRSession->RecommendedViewport().y()
+ + "@" + (int )Round (myXRSession->DisplayFrequency())
+ + " [FOVy: " + (int )Round (myXRSession->FieldOfView()) + "]";
+
+ theDict.ChangeFromIndex (theDict.Add ("VRvendor", aVendor)) = aVendor;
+ theDict.ChangeFromIndex (theDict.Add ("VRdevice", aDevice)) = aDevice;
+ theDict.ChangeFromIndex (theDict.Add ("VRtracker", aTracker)) = aTracker;
+ theDict.ChangeFromIndex (theDict.Add ("VRdisplay", aDisplay)) = aDisplay;
+ theDict.ChangeFromIndex (theDict.Add ("VRserial", aSerial)) = aSerial;
+ }
+}
#include <Standard_Transient.hxx>
#include <TColStd_IndexedDataMapOfStringString.hxx>
+class Aspect_XRSession;
class Graphic3d_CView;
class Graphic3d_GraphicDriver;
class Graphic3d_Layer;
//! The format of returned information (e.g. key-value layout)
//! is NOT part of this API and can be changed at any time.
//! Thus application should not parse returned information to weed out specific parameters.
- virtual void DiagnosticInformation (TColStd_IndexedDataMapOfStringString& theDict,
- Graphic3d_DiagnosticInfo theFlags) const = 0;
+ Standard_EXPORT virtual void DiagnosticInformation (TColStd_IndexedDataMapOfStringString& theDict,
+ Graphic3d_DiagnosticInfo theFlags) const = 0;
//! Returns string with statistic performance info.
virtual TCollection_AsciiString StatisticInformation() const = 0;
//! Fills in the dictionary with statistic performance info.
virtual void StatisticInformation (TColStd_IndexedDataMapOfStringString& theDict) const = 0;
+public:
+
+ //! Return unit scale factor defined as scale factor for m (meters); 1.0 by default.
+ //! Normally, view definition is unitless, however some operations like VR input requires proper units mapping.
+ Standard_Real UnitFactor() const { return myUnitFactor; }
+
+ //! Set unit scale factor.
+ Standard_EXPORT void SetUnitFactor (Standard_Real theFactor);
+
+ //! Return XR session.
+ const Handle(Aspect_XRSession)& XRSession() const { return myXRSession; }
+
+ //! Set XR session.
+ void SetXRSession (const Handle(Aspect_XRSession)& theSession) { myXRSession = theSession; }
+
+ //! Return TRUE if there is active XR session.
+ Standard_EXPORT bool IsActiveXR() const;
+
+ //! Initialize XR session.
+ Standard_EXPORT virtual bool InitXR();
+
+ //! Release XR session.
+ Standard_EXPORT virtual void ReleaseXR();
+
+ //! Process input.
+ Standard_EXPORT virtual void ProcessXRInput();
+
+ //! Compute PosedXRCamera() based on current XR head pose and make it active.
+ Standard_EXPORT void SetupXRPosedCamera();
+
+ //! Set current camera back to BaseXRCamera() and copy temporary modifications of PosedXRCamera().
+ //! Calls SynchronizeXRPosedToBaseCamera() beforehand.
+ Standard_EXPORT void UnsetXRPosedCamera();
+
+ //! Returns transient XR camera position with tracked head orientation applied.
+ const Handle(Graphic3d_Camera)& PosedXRCamera() const { return myPosedXRCamera; }
+
+ //! Sets transient XR camera position with tracked head orientation applied.
+ void SetPosedXRCamera (const Handle(Graphic3d_Camera)& theCamera) { myPosedXRCamera = theCamera; }
+
+ //! Returns anchor camera definition (without tracked head orientation).
+ const Handle(Graphic3d_Camera)& BaseXRCamera() const { return myBaseXRCamera; }
+
+ //! Sets anchor camera definition.
+ void SetBaseXRCamera (const Handle(Graphic3d_Camera)& theCamera) { myBaseXRCamera = theCamera; }
+
+ //! Convert XR pose to world space.
+ //! @param theTrsfXR [in] transformation defined in VR local coordinate system,
+ //! oriented as Y-up, X-right and -Z-forward
+ //! @return transformation defining orientation of XR pose in world space
+ gp_Trsf PoseXRToWorld (const gp_Trsf& thePoseXR) const
+ {
+ const Handle(Graphic3d_Camera)& anOrigin = myBaseXRCamera;
+ const gp_Ax3 anAxVr (gp::Origin(), gp::DZ(), gp::DX());
+ const gp_Ax3 aCameraCS (anOrigin->Eye().XYZ(), -anOrigin->Direction(), -anOrigin->SideRight());
+ gp_Trsf aTrsfCS;
+ aTrsfCS.SetTransformation (aCameraCS, anAxVr);
+ return aTrsfCS * thePoseXR;
+ }
+
+ //! Recomputes PosedXRCamera() based on BaseXRCamera() and head orientation.
+ Standard_EXPORT void SynchronizeXRBaseToPosedCamera();
+
+ //! Checks if PosedXRCamera() has been modified since SetupXRPosedCamera()
+ //! and copies these modifications to BaseXRCamera().
+ Standard_EXPORT void SynchronizeXRPosedToBaseCamera();
+
+ //! Compute camera position based on XR pose.
+ Standard_EXPORT void ComputeXRPosedCameraFromBase (Graphic3d_Camera& theCam,
+ const gp_Trsf& theXRTrsf) const;
+
+ //! Update based camera from posed camera by applying reversed transformation.
+ Standard_EXPORT void ComputeXRBaseCameraFromPosed (const Graphic3d_Camera& theCamPosed,
+ const gp_Trsf& thePoseTrsf);
+
+ //! Turn XR camera direction using current (head) eye position as anchor.
+ Standard_EXPORT void TurnViewXRCamera (const gp_Trsf& theTrsfTurn);
+
public: //! @name obsolete Graduated Trihedron functionality
//! Returns data of a graduated trihedron
Graphic3d_TypeOfShadingModel myShadingModel;
Graphic3d_TypeOfVisualization myVisualization;
+ Handle(Aspect_XRSession) myXRSession;
+ Handle(Graphic3d_Camera) myBackXRCamera; //!< camera projection parameters to restore after closing XR session (FOV, aspect and similar)
+ Handle(Graphic3d_Camera) myBaseXRCamera; //!< neutral camera orientation defining coordinate system in which head tracking is defined
+ Handle(Graphic3d_Camera) myPosedXRCamera; //!< transient XR camera orientation with tracked head orientation applied (based on myBaseXRCamera)
+ Handle(Graphic3d_Camera) myPosedXRCameraCopy; //!< neutral camera orientation copy at the beginning of processing input
+ Standard_Real myUnitFactor; //!< unit scale factor defined as scale factor for m (meters)
+
protected:
Graphic3d_GraduatedTrihedron myGTrihedronData;
myAxialScale (1.0, 1.0, 1.0),
myProjType (Projection_Orthographic),
myFOVy (45.0),
+ myFOVx (45.0),
+ myFOV2d (180.0),
myFOVyTan (Tan (DTR_HALF * 45.0)),
myZNear (DEFAULT_ZNEAR),
myZFar (DEFAULT_ZFAR),
myZFocus (1.0),
myZFocusType (FocusType_Relative),
myIOD (0.05),
- myIODType (IODType_Relative)
+ myIODType (IODType_Relative),
+ myIsCustomProjMatM (false),
+ myIsCustomProjMatLR(false),
+ myIsCustomFrustomLR(false)
{
myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
(Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
myAxialScale (1.0, 1.0, 1.0),
myProjType (Projection_Orthographic),
myFOVy (45.0),
+ myFOVx (45.0),
+ myFOV2d (180.0),
myFOVyTan (Tan (DTR_HALF * 45.0)),
myZNear (DEFAULT_ZNEAR),
myZFar (DEFAULT_ZFAR),
myZFocus (1.0),
myZFocusType (FocusType_Relative),
myIOD (0.05),
- myIODType (IODType_Relative)
+ myIODType (IODType_Relative),
+ myIsCustomProjMatM (false),
+ myIsCustomProjMatLR(false),
+ myIsCustomFrustomLR(false)
{
myWorldViewProjState.Initialize (this);
void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
{
SetFOVy (theOtherCamera->FOVy());
+ SetFOV2d (theOtherCamera->FOV2d());
SetZRange (theOtherCamera->ZNear(), theOtherCamera->ZFar());
SetAspect (theOtherCamera->Aspect());
SetScale (theOtherCamera->Scale());
SetIOD (theOtherCamera->GetIODType(), theOtherCamera->IOD());
SetProjectionType (theOtherCamera->ProjectionType());
SetTile (theOtherCamera->myTile);
+
+ ResetCustomProjection();
+ if (theOtherCamera->IsCustomStereoProjection())
+ {
+ SetCustomStereoProjection (theOtherCamera->myCustomProjMatL, theOtherCamera->myCustomProjMatR);
+ }
+ else if (theOtherCamera->IsCustomStereoFrustum())
+ {
+ SetCustomStereoFrustums (theOtherCamera->myCustomFrustumL, theOtherCamera->myCustomFrustumR);
+ }
+ if (theOtherCamera->IsCustomMonoProjection())
+ {
+ SetCustomMonoProjection (theOtherCamera->myCustomProjMatM);
+ }
}
// =======================================================================
}
myFOVy = theFOVy;
+ myFOVx = theFOVy * myAspect;
myFOVyTan = Tan(DTR_HALF * myFOVy);
InvalidateProjection();
}
+// =======================================================================
+// function : SetFOV2d
+// purpose :
+// =======================================================================
+void Graphic3d_Camera::SetFOV2d (const Standard_Real theFOV)
+{
+ if (FOV2d() == theFOV)
+ {
+ return;
+ }
+
+ myFOV2d = theFOV;
+ InvalidateProjection();
+}
+
// =======================================================================
// function : SetZRange
// purpose :
}
myAspect = theAspect;
+ myFOVx = myFOVy * theAspect;
InvalidateProjection();
}
return UpdateProjection (myMatricesF).RProjection;
}
+// =======================================================================
+// function : ResetCustomProjection
+// purpose :
+// =======================================================================
+void Graphic3d_Camera::ResetCustomProjection()
+{
+ if (myIsCustomFrustomLR
+ || myIsCustomProjMatLR
+ || myIsCustomProjMatM)
+ {
+ myIsCustomFrustomLR = false;
+ myIsCustomProjMatLR = false;
+ myIsCustomProjMatM = false;
+ InvalidateProjection();
+ }
+}
+
+// =======================================================================
+// function : SetCustomStereoFrustums
+// purpose :
+// =======================================================================
+void Graphic3d_Camera::SetCustomStereoFrustums (const Aspect_FrustumLRBT<Standard_Real>& theFrustumL,
+ const Aspect_FrustumLRBT<Standard_Real>& theFrustumR)
+{
+ myCustomFrustumL = theFrustumL;
+ myCustomFrustumR = theFrustumR;
+ myIsCustomFrustomLR = true;
+ myIsCustomProjMatLR = false;
+ InvalidateProjection();
+}
+
+// =======================================================================
+// function : SetCustomStereoProjection
+// purpose :
+// =======================================================================
+void Graphic3d_Camera::SetCustomStereoProjection (const Graphic3d_Mat4d& theProjL,
+ const Graphic3d_Mat4d& theProjR)
+{
+ myCustomProjMatL = theProjL;
+ myCustomProjMatR = theProjR;
+ myIsCustomProjMatLR = true;
+ myIsCustomFrustomLR = false;
+ InvalidateProjection();
+}
+
+// =======================================================================
+// function : SetCustomMonoProjection
+// purpose :
+// =======================================================================
+void Graphic3d_Camera::SetCustomMonoProjection (const Graphic3d_Mat4d& theProj)
+{
+ myCustomProjMatM = theProj;
+ myIsCustomProjMatM = true;
+ InvalidateProjection();
+}
+
// =======================================================================
// function : UpdateProjection
// purpose :
Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
if (IsOrthographic())
{
- aDXHalf = aScale * Elem_t (0.5);
- aDYHalf = aScale * Elem_t (0.5);
+ aDXHalf = aDYHalf = aScale * Elem_t (0.5);
}
else
{
- aDXHalf = aZNear * Elem_t (myFOVyTan);
- aDYHalf = aZNear * Elem_t (myFOVyTan);
+ aDXHalf = aDYHalf = aZNear * Elem_t (myFOVyTan);
}
if (anAspect > 1.0)
}
// sets right of frustum based on aspect ratio
- Elem_t aLeft = -aDXHalf;
- Elem_t aRight = aDXHalf;
- Elem_t aBot = -aDYHalf;
- Elem_t aTop = aDYHalf;
+ Aspect_FrustumLRBT<Elem_t> anLRBT;
+ anLRBT.Left = -aDXHalf;
+ anLRBT.Right = aDXHalf;
+ anLRBT.Bottom = -aDYHalf;
+ anLRBT.Top = aDYHalf;
Elem_t aIOD = myIODType == IODType_Relative
? static_cast<Elem_t> (myIOD * Distance())
const Elem_t aDXFull = Elem_t(2) * aDXHalf;
const Elem_t aDYFull = Elem_t(2) * aDYHalf;
const Graphic3d_Vec2i anOffset = myTile.OffsetLowerLeft();
- aLeft = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
- aRight = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
- aBot = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
- aTop = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
+ anLRBT.Left = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
+ anLRBT.Right = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
+ anLRBT.Bottom = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
+ anLRBT.Top = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
}
+ if (myIsCustomProjMatM)
+ {
+ theMatrices.MProjection.ConvertFrom (myCustomProjMatM);
+ }
switch (myProjType)
{
- case Projection_Orthographic :
- OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
- break;
-
- case Projection_Perspective :
- PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
- break;
-
- case Projection_MonoLeftEye :
+ case Projection_Orthographic:
{
- StereoEyeProj (aLeft, aRight, aBot, aTop,
- aZNear, aZFar, aIOD, aFocus,
- Standard_True, theMatrices.MProjection);
- theMatrices.LProjection = theMatrices.MProjection;
+ if (!myIsCustomProjMatM)
+ {
+ orthoProj (theMatrices.MProjection, anLRBT, aZNear, aZFar);
+ }
break;
}
-
- case Projection_MonoRightEye :
+ case Projection_Perspective:
{
- StereoEyeProj (aLeft, aRight, aBot, aTop,
- aZNear, aZFar, aIOD, aFocus,
- Standard_False, theMatrices.MProjection);
- theMatrices.RProjection = theMatrices.MProjection;
+ if (!myIsCustomProjMatM)
+ {
+ perspectiveProj (theMatrices.MProjection, anLRBT, aZNear, aZFar);
+ }
break;
}
-
- case Projection_Stereo :
+ case Projection_MonoLeftEye:
+ case Projection_MonoRightEye:
+ case Projection_Stereo:
{
- PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
-
- StereoEyeProj (aLeft, aRight, aBot, aTop,
- aZNear, aZFar, aIOD, aFocus,
- Standard_True,
- theMatrices.LProjection);
-
- StereoEyeProj (aLeft, aRight, aBot, aTop,
- aZNear, aZFar, aIOD, aFocus,
- Standard_False,
- theMatrices.RProjection);
+ if (!myIsCustomProjMatM)
+ {
+ perspectiveProj (theMatrices.MProjection, anLRBT, aZNear, aZFar);
+ }
+ if (myIsCustomProjMatLR)
+ {
+ theMatrices.LProjection.ConvertFrom (myCustomProjMatL);
+ theMatrices.RProjection.ConvertFrom (myCustomProjMatR);
+ }
+ else if (myIsCustomFrustomLR)
+ {
+ anLRBT = Aspect_FrustumLRBT<Elem_t> (myCustomFrustumL).Multiplied (aZNear);
+ perspectiveProj (theMatrices.LProjection, anLRBT, aZNear, aZFar);
+ if (aIOD != Elem_t (0.0))
+ {
+ theMatrices.LProjection.Translate (NCollection_Vec3<Elem_t> (Elem_t (0.5) * aIOD, Elem_t (0.0), Elem_t (0.0)));
+ }
+
+ anLRBT = Aspect_FrustumLRBT<Elem_t> (myCustomFrustumR).Multiplied (aZNear);
+ perspectiveProj (theMatrices.RProjection, anLRBT, aZNear, aZFar);
+ if (aIOD != Elem_t (0.0))
+ {
+ theMatrices.RProjection.Translate (NCollection_Vec3<Elem_t> (Elem_t (-0.5) * aIOD, Elem_t (0.0), Elem_t (0.0)));
+ }
+ }
+ else
+ {
+ stereoEyeProj (theMatrices.LProjection,
+ anLRBT, aZNear, aZFar, aIOD, aFocus,
+ Aspect_Eye_Left);
+ stereoEyeProj (theMatrices.RProjection,
+ anLRBT, aZNear, aZFar, aIOD, aFocus,
+ Aspect_Eye_Right);
+ }
break;
}
}
+ if (myProjType == Projection_MonoLeftEye)
+ {
+ theMatrices.MProjection = theMatrices.LProjection;
+ }
+ else if (myProjType == Projection_MonoRightEye)
+ {
+ theMatrices.MProjection = theMatrices.RProjection;
+ }
return theMatrices; // for inline accessors
}
}
// =======================================================================
-// function : OrthoProj
+// function : orthoProj
// purpose :
// =======================================================================
template <typename Elem_t>
-void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,
- const Elem_t theRight,
- const Elem_t theBottom,
- const Elem_t theTop,
+void Graphic3d_Camera::orthoProj (NCollection_Mat4<Elem_t>& theOutMx,
+ const Aspect_FrustumLRBT<Elem_t>& theLRBT,
const Elem_t theNear,
- const Elem_t theFar,
- NCollection_Mat4<Elem_t>& theOutMx)
+ const Elem_t theFar)
{
// row 0
- theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);
+ theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theLRBT.Right - theLRBT.Left);
theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
- theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);
+ theOutMx.ChangeValue (0, 3) = - (theLRBT.Right + theLRBT.Left) / (theLRBT.Right - theLRBT.Left);
// row 1
theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
- theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);
+ theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theLRBT.Top - theLRBT.Bottom);
theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
- theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);
+ theOutMx.ChangeValue (1, 3) = - (theLRBT.Top + theLRBT.Bottom) / (theLRBT.Top - theLRBT.Bottom);
// row 2
theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
// purpose :
// =======================================================================
template <typename Elem_t>
-void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,
- const Elem_t theRight,
- const Elem_t theBottom,
- const Elem_t theTop,
+void Graphic3d_Camera::perspectiveProj (NCollection_Mat4<Elem_t>& theOutMx,
+ const Aspect_FrustumLRBT<Elem_t>& theLRBT,
const Elem_t theNear,
- const Elem_t theFar,
- NCollection_Mat4<Elem_t>& theOutMx)
+ const Elem_t theFar)
{
// column 0
- theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);
+ theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theLRBT.Right - theLRBT.Left);
theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
// column 1
theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
- theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);
+ theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theLRBT.Top - theLRBT.Bottom);
theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
// column 2
- theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);
- theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);
+ theOutMx.ChangeValue (0, 2) = (theLRBT.Right + theLRBT.Left) / (theLRBT.Right - theLRBT.Left);
+ theOutMx.ChangeValue (1, 2) = (theLRBT.Top + theLRBT.Bottom) / (theLRBT.Top - theLRBT.Bottom);
theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
// purpose :
// =======================================================================
template <typename Elem_t>
-void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
- const Elem_t theRight,
- const Elem_t theBottom,
- const Elem_t theTop,
+void Graphic3d_Camera::stereoEyeProj (NCollection_Mat4<Elem_t>& theOutMx,
+ const Aspect_FrustumLRBT<Elem_t>& theLRBT,
const Elem_t theNear,
const Elem_t theFar,
const Elem_t theIOD,
const Elem_t theZFocus,
- const Standard_Boolean theIsLeft,
- NCollection_Mat4<Elem_t>& theOutMx)
+ const Aspect_Eye theEyeIndex)
{
- Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
+ Elem_t aDx = theEyeIndex == Aspect_Eye_Left ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
Elem_t aDXStereoShift = aDx * theNear / theZFocus;
// construct eye projection matrix
- PerspectiveProj (theLeft + aDXStereoShift,
- theRight + aDXStereoShift,
- theBottom, theTop, theNear, theFar,
- theOutMx);
+ Aspect_FrustumLRBT<Elem_t> aLRBT = theLRBT;
+ aLRBT.Left = theLRBT.Left + aDXStereoShift;
+ aLRBT.Right = theLRBT.Right + aDXStereoShift;
+ perspectiveProj (theOutMx, aLRBT, theNear, theFar);
if (theIOD != Elem_t (0.0))
{
#ifndef _Graphic3d_Camera_HeaderFile
#define _Graphic3d_Camera_HeaderFile
+#include <Aspect_Eye.hxx>
+#include <Aspect_FrustumLRBT.hxx>
#include <Graphic3d_CameraTile.hxx>
#include <Graphic3d_Mat4d.hxx>
#include <Graphic3d_Mat4.hxx>
//! Return a copy of orthogonalized up direction vector.
Standard_EXPORT gp_Dir OrthogonalizedUp() const;
+ //! Right side direction.
+ gp_Dir SideRight() const
+ {
+ return -(gp_Vec (Direction()) ^ gp_Vec (OrthogonalizedUp()));
+ }
+
//! Get camera Eye position.
//! @return camera eye location.
const gp_Pnt& Eye() const { return myEye; }
}
//! Set Field Of View (FOV) in y axis for perspective projection.
+ //! Field of View in x axis is automatically scaled from view aspect ratio.
//! @param theFOVy [in] the FOV in degrees.
Standard_EXPORT void SetFOVy (const Standard_Real theFOVy);
//! Get Field Of View (FOV) in y axis.
//! @return the FOV value in degrees.
- Standard_Real FOVy() const
- {
- return myFOVy;
- }
+ Standard_Real FOVy() const { return myFOVy; }
+
+ //! Get Field Of View (FOV) in x axis.
+ //! @return the FOV value in degrees.
+ Standard_Real FOVx() const { return myFOVx; }
+
+ //! Get Field Of View (FOV) restriction for 2D on-screen elements; 180 degrees by default.
+ //! When 2D FOV is smaller than FOVy or FOVx, 2D elements defined within offset from view corner
+ //! will be extended to fit into specified 2D FOV.
+ //! This can be useful to make 2D elements sharply visible, like in case of HMD normally having extra large FOVy.
+ Standard_Real FOV2d() const { return myFOV2d; }
+
+ //! Set Field Of View (FOV) restriction for 2D on-screen elements.
+ Standard_EXPORT void SetFOV2d (Standard_Real theFOV);
//! Estimate Z-min and Z-max planes of projection volume to match the
//! displayed objects. The methods ensures that view volume will
//! @return values in form of gp_Pnt (Width, Height, Depth).
Standard_EXPORT gp_XYZ ViewDimensions (const Standard_Real theZValue) const;
+ //! Return offset to the view corner in NDC space within dimension X for 2d on-screen elements, which is normally 0.5.
+ //! Can be clamped when FOVx exceeds FOV2d.
+ Standard_Real NDC2dOffsetX() const
+ {
+ return myFOV2d >= myFOVx
+ ? 0.5
+ : 0.5 * myFOV2d / myFOVx;
+ }
+
+ //! Return offset to the view corner in NDC space within dimension X for 2d on-screen elements, which is normally 0.5.
+ //! Can be clamped when FOVy exceeds FOV2d.
+ Standard_Real NDC2dOffsetY() const
+ {
+ return myFOV2d >= myFOVy
+ ? 0.5
+ : 0.5 * myFOV2d / myFOVy;
+ }
+
//! Calculate WCS frustum planes for the camera projection volume.
//! Frustum is a convex volume determined by six planes directing
//! inwards.
//! The matrix will be updated on request.
Standard_EXPORT void InvalidateOrientation();
+public:
+
+ //! Unset all custom frustums and projection matrices.
+ Standard_EXPORT void ResetCustomProjection();
+
+ //! Return TRUE if custom stereo frustums are set.
+ bool IsCustomStereoFrustum() const { return myIsCustomFrustomLR; }
+
+ //! Set custom stereo frustums.
+ //! These can be retrieved from APIs like OpenVR.
+ Standard_EXPORT void SetCustomStereoFrustums (const Aspect_FrustumLRBT<Standard_Real>& theFrustumL,
+ const Aspect_FrustumLRBT<Standard_Real>& theFrustumR);
+
+ //! Return TRUE if custom stereo projection matrices are set.
+ bool IsCustomStereoProjection() const { return myIsCustomProjMatLR; }
+
+ //! Set custom stereo projection matrices.
+ Standard_EXPORT void SetCustomStereoProjection (const Graphic3d_Mat4d& theProjL,
+ const Graphic3d_Mat4d& theProjR);
+
+ //! Return TRUE if custom projection matrix is set.
+ bool IsCustomMonoProjection() const { return myIsCustomProjMatM; }
+
+ //! Set custom projection matrix.
+ Standard_EXPORT void SetCustomMonoProjection (const Graphic3d_Mat4d& theProj);
+
//! Dumps the content of me into the stream
Standard_EXPORT void DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth = -1) const;
private:
- //! Compose orthographic projection matrix for
- //! the passed camera volume mapping.
- //! @param theLeft [in] the left mapping (clipping) coordinate.
- //! @param theRight [in] the right mapping (clipping) coordinate.
- //! @param theBottom [in] the bottom mapping (clipping) coordinate.
- //! @param theTop [in] the top mapping (clipping) coordinate.
- //! @param theNear [in] the near mapping (clipping) coordinate.
- //! @param theFar [in] the far mapping (clipping) coordinate.
- //! @param theOutMx [out] the projection matrix.
+ //! Compose orthographic projection matrix for the passed camera volume mapping.
+ //! @param theOutMx [out] the projection matrix
+ //! @param theLRBT [in] the left/right/bottom/top mapping (clipping) coordinates
+ //! @param theNear [in] the near mapping (clipping) coordinate
+ //! @param theFar [in] the far mapping (clipping) coordinate
template <typename Elem_t>
- static void
- OrthoProj (const Elem_t theLeft,
- const Elem_t theRight,
- const Elem_t theBottom,
- const Elem_t theTop,
- const Elem_t theNear,
- const Elem_t theFar,
- NCollection_Mat4<Elem_t>& theOutMx);
-
- //! Compose perspective projection matrix for
- //! the passed camera volume mapping.
- //! @param theLeft [in] the left mapping (clipping) coordinate.
- //! @param theRight [in] the right mapping (clipping) coordinate.
- //! @param theBottom [in] the bottom mapping (clipping) coordinate.
- //! @param theTop [in] the top mapping (clipping) coordinate.
- //! @param theNear [in] the near mapping (clipping) coordinate.
- //! @param theFar [in] the far mapping (clipping) coordinate.
- //! @param theOutMx [out] the projection matrix.
+ static void orthoProj (NCollection_Mat4<Elem_t>& theOutMx,
+ const Aspect_FrustumLRBT<Elem_t>& theLRBT,
+ const Elem_t theNear,
+ const Elem_t theFar);
+
+ //! Compose perspective projection matrix for the passed camera volume mapping.
+ //! @param theOutMx [out] the projection matrix
+ //! @param theLRBT [in] the left/right/bottom/top mapping (clipping) coordinates
+ //! @param theNear [in] the near mapping (clipping) coordinate
+ //! @param theFar [in] the far mapping (clipping) coordinate
template <typename Elem_t>
- static void
- PerspectiveProj (const Elem_t theLeft,
- const Elem_t theRight,
- const Elem_t theBottom,
- const Elem_t theTop,
- const Elem_t theNear,
- const Elem_t theFar,
- NCollection_Mat4<Elem_t>& theOutMx);
+ static void perspectiveProj (NCollection_Mat4<Elem_t>& theOutMx,
+ const Aspect_FrustumLRBT<Elem_t>& theLRBT,
+ const Elem_t theNear,
+ const Elem_t theFar);
//! Compose projection matrix for L/R stereo eyes.
- //! @param theLeft [in] the left mapping (clipping) coordinate.
- //! @param theRight [in] the right mapping (clipping) coordinate.
- //! @param theBottom [in] the bottom mapping (clipping) coordinate.
- //! @param theTop [in] the top mapping (clipping) coordinate.
- //! @param theNear [in] the near mapping (clipping) coordinate.
- //! @param theFar [in] the far mapping (clipping) coordinate.
- //! @param theIOD [in] the Intraocular distance.
- //! @param theZFocus [in] the z coordinate of off-axis
- //! projection plane with zero parallax.
- //! @param theIsLeft [in] boolean flag to choose between L/R eyes.
- //! @param theOutMx [out] the projection matrix.
+ //! @param theOutMx [out] the projection matrix
+ //! @param theLRBT [in] the left/right/bottom/top mapping (clipping) coordinates
+ //! @param theNear [in] the near mapping (clipping) coordinate
+ //! @param theFar [in] the far mapping (clipping) coordinate
+ //! @param theIOD [in] the Intraocular distance
+ //! @param theZFocus [in] the z coordinate of off-axis projection plane with zero parallax
+ //! @param theEyeIndex [in] choose between L/R eyes
template <typename Elem_t>
- static void
- StereoEyeProj (const Elem_t theLeft,
- const Elem_t theRight,
- const Elem_t theBottom,
- const Elem_t theTop,
- const Elem_t theNear,
- const Elem_t theFar,
- const Elem_t theIOD,
- const Elem_t theZFocus,
- const Standard_Boolean theIsLeft,
- NCollection_Mat4<Elem_t>& theOutMx);
+ static void stereoEyeProj (NCollection_Mat4<Elem_t>& theOutMx,
+ const Aspect_FrustumLRBT<Elem_t>& theLRBT,
+ const Elem_t theNear,
+ const Elem_t theFar,
+ const Elem_t theIOD,
+ const Elem_t theZFocus,
+ const Aspect_Eye theEyeIndex);
//! Construct "look at" orientation transformation.
//! Reference point differs for perspective and ortho modes
Projection myProjType; //!< Projection type used for rendering.
Standard_Real myFOVy; //!< Field Of View in y axis.
+ Standard_Real myFOVx; //!< Field Of View in x axis.
+ Standard_Real myFOV2d; //!< Field Of View limit for 2d on-screen elements
Standard_Real myFOVyTan; //!< Field Of View as Tan(DTR_HALF * myFOVy)
Standard_Real myZNear; //!< Distance to near clipping plane.
Standard_Real myZFar; //!< Distance to far clipping plane.
Graphic3d_CameraTile myTile;//!< Tile defining sub-area for drawing
+ Graphic3d_Mat4d myCustomProjMatM;
+ Graphic3d_Mat4d myCustomProjMatL;
+ Graphic3d_Mat4d myCustomProjMatR;
+ Aspect_FrustumLRBT<Standard_Real> myCustomFrustumL; //!< left custom frustum
+ Aspect_FrustumLRBT<Standard_Real> myCustomFrustumR; //!< right custom frustum
+ Standard_Boolean myIsCustomProjMatM; //!< flag indicating usage of custom projection matrix
+ Standard_Boolean myIsCustomProjMatLR; //!< flag indicating usage of custom stereo projection matrices
+ Standard_Boolean myIsCustomFrustomLR; //!< flag indicating usage of custom stereo frustums
+
mutable TransformMatrices<Standard_Real> myMatricesD;
mutable TransformMatrices<Standard_ShortReal> myMatricesF;
WhitePoint (1.f),
// stereoscopic parameters
StereoMode (Graphic3d_StereoMode_QuadBuffer),
+ HmdFov2d (30.0f),
AnaglyphFilter (Anaglyph_RedCyan_Optimized),
ToReverseStereo (Standard_False),
+ ToMirrorComposer (Standard_True),
//
StatsPosition (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER, Graphic3d_Vec2i (20, 20))),
ChartPosition (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_RIGHT_UPPER, Graphic3d_Vec2i (20, 20))),
Standard_ShortReal WhitePoint; //!< white point value used in filmic tone mapping (path tracing), 1.0 by default
Graphic3d_StereoMode StereoMode; //!< stereoscopic output mode, Graphic3d_StereoMode_QuadBuffer by default
+ Standard_ShortReal HmdFov2d; //!< sharp field of view range in degrees for displaying on-screen 2D elements, 30.0 by default;
Anaglyph AnaglyphFilter; //!< filter for anaglyph output, Anaglyph_RedCyan_Optimized by default
Graphic3d_Mat4 AnaglyphLeft; //!< left anaglyph filter (in normalized colorspace), Color = AnaglyphRight * theColorRight + AnaglyphLeft * theColorLeft;
Graphic3d_Mat4 AnaglyphRight; //!< right anaglyph filter (in normalized colorspace), Color = AnaglyphRight * theColorRight + AnaglyphLeft * theColorLeft;
Standard_Boolean ToReverseStereo; //!< flag to reverse stereo pair, FALSE by default
+ Standard_Boolean ToMirrorComposer; //!< if output device is an external composer - mirror rendering results in window in addition to sending frame to composer, TRUE by default
Handle(Graphic3d_TransformPers) StatsPosition; //!< location of stats, upper-left position by default
Handle(Graphic3d_TransformPers) ChartPosition; //!< location of stats chart, upper-right position by default
Graphic3d_StereoMode_SideBySide, //!< horizontal pair
Graphic3d_StereoMode_OverUnder, //!< vertical pair
Graphic3d_StereoMode_SoftPageFlip, //!< software PageFlip for shutter glasses, should NOT be used!
+ Graphic3d_StereoMode_OpenVR, //!< OpenVR (HMD)
Graphic3d_StereoMode_NB //!< the number of modes
};
{
const Standard_Real anOffsetX = (Standard_Real(myParams.Params2d.OffsetX) + aJitterComp) * aScale;
const gp_Dir aSide = aForward.Crossed (theCamera->Up());
- const gp_XYZ aDeltaX = aSide.XYZ() * (Abs(aViewDim.X()) * 0.5 - anOffsetX);
+ const gp_XYZ aDeltaX = aSide.XYZ() * (Abs(aViewDim.X()) * theCamera->NDC2dOffsetX() - anOffsetX);
if ((myParams.Params2d.Corner & Aspect_TOTP_RIGHT) != 0)
{
aCenter += aDeltaX;
if ((myParams.Params2d.Corner & (Aspect_TOTP_TOP | Aspect_TOTP_BOTTOM)) != 0)
{
const Standard_Real anOffsetY = (Standard_Real(myParams.Params2d.OffsetY) + aJitterComp) * aScale;
- const gp_XYZ aDeltaY = theCamera->Up().XYZ() * (Abs(aViewDim.Y()) * 0.5 - anOffsetY);
+ const gp_XYZ aDeltaY = theCamera->Up().XYZ() * (Abs(aViewDim.Y()) * theCamera->NDC2dOffsetY() - anOffsetY);
if ((myParams.Params2d.Corner & Aspect_TOTP_TOP) != 0)
{
aCenter += aDeltaY;
gp_XYZ aCenter (0.0, 0.0, -aFocus);
if ((myParams.Params2d.Corner & (Aspect_TOTP_LEFT | Aspect_TOTP_RIGHT)) != 0)
{
- aCenter.SetX (-aViewDim.X() * 0.5 + (Standard_Real(myParams.Params2d.OffsetX) + aJitterComp) * aScale);
+ aCenter.SetX (-aViewDim.X() * theCamera->NDC2dOffsetX() + (Standard_Real(myParams.Params2d.OffsetX) + aJitterComp) * aScale);
if ((myParams.Params2d.Corner & Aspect_TOTP_RIGHT) != 0)
{
aCenter.SetX (-aCenter.X());
}
if ((myParams.Params2d.Corner & (Aspect_TOTP_TOP | Aspect_TOTP_BOTTOM)) != 0)
{
- aCenter.SetY (-aViewDim.Y() * 0.5 + (Standard_Real(myParams.Params2d.OffsetY) + aJitterComp) * aScale);
+ aCenter.SetY (-aViewDim.Y() * theCamera->NDC2dOffsetY() + (Standard_Real(myParams.Params2d.OffsetY) + aJitterComp) * aScale);
if ((myParams.Params2d.Corner & Aspect_TOTP_TOP) != 0)
{
aCenter.SetY (-aCenter.Y());
;# Autres UDs a prendre.
;#
proc Visualization:ressources { } {
- return [list \
- [list both r Textures {}] \
- [list both r Shaders {}] \
- ]
+ return [list \
+ [list both r Textures {}] \
+ [list both r Shaders {}] \
+ [list both r XRResources {}] \
+ ]
}
;#
;# Nom du module
myPolygonOffset = theOffset;
}
+// =======================================================================
+// function : SetCamera
+// purpose :
+// =======================================================================
+void OpenGl_Context::SetCamera (const Handle(Graphic3d_Camera)& theCamera)
+{
+ myCamera = theCamera;
+ if (!theCamera.IsNull())
+ {
+ ProjectionState.SetCurrent (theCamera->ProjectionMatrixF());
+ WorldViewState .SetCurrent (theCamera->OrientationMatrixF());
+ ApplyProjectionMatrix();
+ ApplyWorldViewMatrix();
+ }
+}
+
// =======================================================================
// function : ApplyModelWorldMatrix
// purpose :
typedef OpenGl_TmplCore45<OpenGl_GlCore44Back> OpenGl_GlCore45Back;
typedef OpenGl_TmplCore45<OpenGl_GlCore44> OpenGl_GlCore45;
+class Graphic3d_Camera;
class Graphic3d_PresentationAttributes;
class OpenGl_Aspects;
class OpenGl_FrameBuffer;
//! Returns currently applied polygon offset parameters.
const Graphic3d_PolygonOffset& PolygonOffset() const { return myPolygonOffset; }
+ //! Returns camera object.
+ const Handle(Graphic3d_Camera)& Camera() const { return myCamera; }
+
+ //! Sets camera object to the context and update matrices.
+ Standard_EXPORT void SetCamera (const Handle(Graphic3d_Camera)& theCamera);
+
//! Applies matrix into shader manager stored in ModelWorldState to OpenGl.
//! In "model -> world -> view -> projection" it performs:
//! model -> world
private: //! @name fields tracking current state
+ Handle(Graphic3d_Camera) myCamera; //!< active camera object
Handle(OpenGl_FrameStats) myFrameStats; //!< structure accumulating frame statistics
Handle(OpenGl_ShaderProgram) myActiveProgram; //!< currently active GLSL program
Handle(OpenGl_TextureSet) myActiveTextures; //!< currently bound textures
aCtx->WorldViewState.Push();
if (!myCountersTrsfPers.IsNull())
{
- myCountersTrsfPers->Apply (theWorkspace->View()->Camera(),
+ myCountersTrsfPers->Apply (aCtx->Camera(),
aCtx->ProjectionState.Current(), aCtx->WorldViewState.ChangeCurrent(),
aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
}
const Standard_Integer aHeight = theWorkspace->Height();
// Take into account Transform Persistence
- aContext->ModelWorldState.SetCurrent (aTransMode.Compute (theWorkspace->View()->Camera(), aProjection, aWorldView, aWidth, aHeight));
+ aContext->ModelWorldState.SetCurrent (aTransMode.Compute (aContext->Camera(), aProjection, aWorldView, aWidth, aHeight));
aContext->ApplyModelViewMatrix();
anAxis.Arrow.Render (theWorkspace);
}
case Graphic3d_StereoMode_QuadBuffer:
case Graphic3d_StereoMode_SoftPageFlip:
+ case Graphic3d_StereoMode_OpenVR:
default:
{
/*const Handle(OpenGl_ShaderProgram)& aProgram = myStereoPrograms[Graphic3d_StereoMode_QuadBuffer];
{
aCtx->WorldViewState.Push();
OpenGl_Mat4& aWorldView = aCtx->WorldViewState.ChangeCurrent();
- myTrsfPers->Apply (theWorkspace->View()->Camera(),
+ myTrsfPers->Apply (aCtx->Camera(),
aCtx->ProjectionState.Current(), aWorldView,
aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
if (myText->HasPlane() && myText->HasOwnAnchorPoint())
{
- myOrientationMatrix = theWorkspace->View()->Camera()->OrientationMatrix();
+ myOrientationMatrix = aCtx->Camera()->OrientationMatrix();
// reset translation part
myOrientationMatrix.ChangeValue (0, 3) = 0.0;
myOrientationMatrix.ChangeValue (1, 3) = 0.0;
myFboColorFormat (GL_SRGB8_ALPHA8), // note that GL_SRGB8 is not required to be renderable, unlike GL_RGB8, GL_RGBA8, GL_SRGB8_ALPHA8
myFboDepthFormat (GL_DEPTH24_STENCIL8),
myToFlipOutput (Standard_False),
+ //
myFrameCounter (0),
myHasFboBlit (Standard_True),
myToDisableOIT (Standard_False),
myImmediateSceneFbos[1] = new OpenGl_FrameBuffer();
myImmediateSceneFbosOit[0] = new OpenGl_FrameBuffer();
myImmediateSceneFbosOit[1] = new OpenGl_FrameBuffer();
+ myXrSceneFbo = new OpenGl_FrameBuffer();
myOpenGlFBO = new OpenGl_FrameBuffer();
myOpenGlFBO2 = new OpenGl_FrameBuffer();
myRaytraceFBO1[0] = new OpenGl_FrameBuffer();
myImmediateSceneFbos[1] ->Release (theCtx.get());
myImmediateSceneFbosOit[0]->Release (theCtx.get());
myImmediateSceneFbosOit[1]->Release (theCtx.get());
+ myXrSceneFbo ->Release (theCtx.get());
myOpenGlFBO ->Release (theCtx.get());
myOpenGlFBO2 ->Release (theCtx.get());
myFullScreenQuad .Release (theCtx.get());
{
myPBREnvironment->Release (theCtx.get());
}
+ ReleaseXR();
}
// =======================================================================
void OpenGl_View::DiagnosticInformation (TColStd_IndexedDataMapOfStringString& theDict,
Graphic3d_DiagnosticInfo theFlags) const
{
+ base_type::DiagnosticInformation (theDict, theFlags);
Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext();
if (!myWorkspace->Activate()
|| aCtx.IsNull())
Handle(OpenGl_FrameBuffer) myMainSceneFbosOit[2]; //!< Additional buffers for transparent draw of main layer.
Handle(OpenGl_FrameBuffer) myImmediateSceneFbos[2]; //!< Additional buffers for immediate layer in stereo mode.
Handle(OpenGl_FrameBuffer) myImmediateSceneFbosOit[2]; //!< Additional buffers for transparency draw of immediate layer.
+ Handle(OpenGl_FrameBuffer) myXrSceneFbo; //!< additional FBO (without MSAA) for submitting to XR
OpenGl_VertexBuffer myFullScreenQuad; //!< Vertices for full-screen quad rendering.
OpenGl_VertexBuffer myFullScreenQuadFlip;
Standard_Boolean myToFlipOutput; //!< Flag to draw result image upside-down
#include <OpenGl_GlCore11.hxx>
+#include <Aspect_XRSession.hxx>
#include <Graphic3d_GraphicDriver.hxx>
#include <Graphic3d_StructureManager.hxx>
#include <Graphic3d_TextureParams.hxx>
return;
}
- myWindow->SetSwapInterval();
+ // implicitly disable VSync when using HMD composer (can be mirrored in window for debugging)
+ myWindow->SetSwapInterval (IsActiveXR());
++myFrameCounter;
const Graphic3d_StereoMode aStereoMode = myRenderParams.StereoMode;
OpenGl_FrameBuffer* aFrameBuffer = myFBO.get();
bool toSwap = aCtx->IsRender()
&& !aCtx->caps->buffersNoSwap
- && aFrameBuffer == NULL;
+ && aFrameBuffer == NULL
+ && (!IsActiveXR() || myRenderParams.ToMirrorComposer);
+
+ Standard_Integer aSizeX = myWindow->Width();
+ Standard_Integer aSizeY = myWindow->Height();
+ if (aFrameBuffer != NULL)
+ {
+ aSizeX = aFrameBuffer->GetVPSizeX();
+ aSizeY = aFrameBuffer->GetVPSizeY();
+ }
+ else if (IsActiveXR())
+ {
+ aSizeX = myXRSession->RecommendedViewport().x();
+ aSizeY = myXRSession->RecommendedViewport().y();
+ }
- const Standard_Integer aSizeX = aFrameBuffer != NULL ? aFrameBuffer->GetVPSizeX() : myWindow->Width();
- const Standard_Integer aSizeY = aFrameBuffer != NULL ? aFrameBuffer->GetVPSizeY() : myWindow->Height();
const Standard_Integer aRendSizeX = Standard_Integer(myRenderParams.RenderResolutionScale * aSizeX + 0.5f);
const Standard_Integer aRendSizeY = Standard_Integer(myRenderParams.RenderResolutionScale * aSizeY + 0.5f);
if (aSizeX < 1
myMainSceneFbos [1]->Release (aCtx.operator->());
myImmediateSceneFbos[0]->Release (aCtx.operator->());
myImmediateSceneFbos[1]->Release (aCtx.operator->());
+ myXrSceneFbo ->Release (aCtx.operator->());
myMainSceneFbos [0]->ChangeViewport (0, 0);
myMainSceneFbos [1]->ChangeViewport (0, 0);
myImmediateSceneFbos[0]->ChangeViewport (0, 0);
myImmediateSceneFbos[1]->ChangeViewport (0, 0);
+ myXrSceneFbo ->ChangeViewport (0, 0);
}
+ bool hasXRBlitFbo = false;
if (aProjectType == Graphic3d_Camera::Projection_Stereo
+ && IsActiveXR()
&& myMainSceneFbos[0]->IsValid())
+ {
+ if (aNbSamples != 0
+ || aSizeX != aRendSizeX)
+ {
+ hasXRBlitFbo = myXrSceneFbo->InitLazy (aCtx, aSizeX, aSizeY, myFboColorFormat, myFboDepthFormat, 0);
+ if (!hasXRBlitFbo)
+ {
+ TCollection_ExtendedString aMsg = TCollection_ExtendedString() + "Error! VR FBO "
+ + printFboFormat (myXrSceneFbo) + " initialization has failed";
+ aCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
+ }
+ }
+ }
+ else if (aProjectType == Graphic3d_Camera::Projection_Stereo
+ && myMainSceneFbos[0]->IsValid())
{
const bool wasFailedMain1 = checkWasFailedFbo (myMainSceneFbos[1], myMainSceneFbos[0]);
if (!myMainSceneFbos[1]->InitLazy (aCtx, *myMainSceneFbos[0])
}
}
}
+ if (!hasXRBlitFbo)
+ {
+ myXrSceneFbo->Release (aCtx.get());
+ myXrSceneFbo->ChangeViewport (0, 0);
+ }
// process PBR environment
if (myShadingModel == Graphic3d_TOSM_PBR
myImmediateSceneFbosOit[0]->IsValid() ? myImmediateSceneFbosOit[0].operator->() : NULL
};
- if (!myTransientDrawToFront)
+ if (IsActiveXR())
+ {
+ // use single frame for both views - caching main scene content makes no sense
+ // when head position is expected to be updated each frame redraw with high accuracy
+ aMainFbos[1] = aMainFbos[0];
+ aMainFbosOit[1] = aMainFbosOit[0];
+ anImmFbos[0] = aMainFbos[0];
+ anImmFbos[1] = aMainFbos[1];
+ anImmFbosOit[0] = aMainFbosOit[0];
+ anImmFbosOit[1] = aMainFbosOit[1];
+ }
+ else if (!myTransientDrawToFront)
{
anImmFbos [0] = aMainFbos [0];
anImmFbos [1] = aMainFbos [1];
aCtx->SwapBuffers();
}
+ if (IsActiveXR())
+ {
+ // push Left frame to HMD display composer
+ OpenGl_FrameBuffer* anXRFbo = hasXRBlitFbo ? myXrSceneFbo.get() : aMainFbos[0];
+ if (anXRFbo != aMainFbos[0])
+ {
+ blitBuffers (aMainFbos[0], anXRFbo); // resize or resolve MSAA samples
+ }
+ #if !defined(GL_ES_VERSION_2_0)
+ const Aspect_GraphicsLibrary aGraphicsLib = Aspect_GraphicsLibrary_OpenGL;
+ #else
+ const Aspect_GraphicsLibrary aGraphicsLib = Aspect_GraphicsLibrary_OpenGLES;
+ #endif
+ myXRSession->SubmitEye ((void* )(size_t )anXRFbo->ColorTexture()->TextureId(),
+ aGraphicsLib, Aspect_ColorSpace_sRGB, Aspect_Eye_Left);
+ }
+
#if !defined(GL_ES_VERSION_2_0)
aCtx->SetReadDrawBuffer (aStereoMode == Graphic3d_StereoMode_QuadBuffer ? GL_BACK_RIGHT : GL_BACK);
#endif
toSwap = false;
}
- if (anImmFbos[0] != NULL)
+ if (IsActiveXR())
+ {
+ // push Right frame to HMD display composer
+ OpenGl_FrameBuffer* anXRFbo = hasXRBlitFbo ? myXrSceneFbo.get() : aMainFbos[1];
+ if (anXRFbo != aMainFbos[1])
+ {
+ blitBuffers (aMainFbos[1], anXRFbo); // resize or resolve MSAA samples
+ }
+ #if !defined(GL_ES_VERSION_2_0)
+ const Aspect_GraphicsLibrary aGraphicsLib = Aspect_GraphicsLibrary_OpenGL;
+ #else
+ const Aspect_GraphicsLibrary aGraphicsLib = Aspect_GraphicsLibrary_OpenGLES;
+ #endif
+ myXRSession->SubmitEye ((void* )(size_t )anXRFbo->ColorTexture()->TextureId(),
+ aGraphicsLib, Aspect_ColorSpace_sRGB, Aspect_Eye_Right);
+ ::glFinish();
+
+ if (myRenderParams.ToMirrorComposer)
+ {
+ blitBuffers (anXRFbo, aFrameBuffer, myToFlipOutput);
+ }
+ }
+ else if (anImmFbos[0] != NULL)
{
aCtx->SetResolution (myRenderParams.Resolution, myRenderParams.ResolutionRatio(), 1.0f);
drawStereoPair (aFrameBuffer);
if (!myWorkspace->Activate())
return;
+ // no special handling of HMD display, since it will force full Redraw() due to no frame caching (myBackBufferRestored)
Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext();
if (!myTransientDrawToFront
|| !myBackBufferRestored
const Handle(OpenGl_Context)& aCtx = myWorkspace->GetGlContext();
GLboolean toCopyBackToFront = GL_FALSE;
if (theDrawFbo == theReadFbo
- && theDrawFbo != NULL)
+ && theDrawFbo != NULL
+ && theDrawFbo->IsValid())
{
myBackBufferRestored = Standard_False;
+ theDrawFbo->BindBuffer (aCtx);
}
else if (theReadFbo != NULL
&& theReadFbo->IsValid()
}
myLocalOrigin.SetCoord (0.0, 0.0, 0.0);
- aContext->ProjectionState.SetCurrent (myCamera->ProjectionMatrixF());
- aContext->WorldViewState .SetCurrent (myCamera->OrientationMatrixF());
- aContext->ApplyProjectionMatrix();
- aContext->ApplyWorldViewMatrix();
+ aContext->SetCamera (myCamera);
if (aManager->ModelWorldState().Index() == 0)
{
aContext->ShaderManager()->UpdateModelWorldStateTo (OpenGl_Mat4());
#if !defined(GL_ES_VERSION_2_0)
// if the view is scaled normal vectors are scaled to unit
// length for correct displaying of shaded objects
- const gp_Pnt anAxialScale = myCamera->AxialScale();
+ const gp_Pnt anAxialScale = aContext->Camera()->AxialScale();
if (anAxialScale.X() != 1.F ||
anAxialScale.Y() != 1.F ||
anAxialScale.Z() != 1.F)
// Redraw 3d scene
if (theProjection == Graphic3d_Camera::Projection_MonoLeftEye)
{
- aContext->ProjectionState.SetCurrent (myCamera->ProjectionStereoLeftF());
+ aContext->ProjectionState.SetCurrent (aContext->Camera()->ProjectionStereoLeftF());
aContext->ApplyProjectionMatrix();
}
else if (theProjection == Graphic3d_Camera::Projection_MonoRightEye)
{
- aContext->ProjectionState.SetCurrent (myCamera->ProjectionStereoRightF());
+ aContext->ProjectionState.SetCurrent (aContext->Camera()->ProjectionStereoRightF());
aContext->ApplyProjectionMatrix();
}
// function : SetSwapInterval
// purpose :
// =======================================================================
-void OpenGl_Window::SetSwapInterval()
+void OpenGl_Window::SetSwapInterval (Standard_Boolean theToForceNoSync)
{
- if (mySwapInterval != myGlContext->caps->swapInterval)
+ const Standard_Integer aSwapInterval = theToForceNoSync ? 0 : myGlContext->caps->swapInterval;
+ if (mySwapInterval != aSwapInterval)
{
- mySwapInterval = myGlContext->caps->swapInterval;
+ mySwapInterval = aSwapInterval;
myGlContext->SetSwapInterval (mySwapInterval);
}
}
Standard_EXPORT virtual Standard_Boolean Activate();
//! Sets swap interval for this window according to the context's settings.
- Standard_EXPORT void SetSwapInterval();
+ Standard_EXPORT void SetSwapInterval (Standard_Boolean theToForceNoSync);
protected:
// function : SetSwapInterval
// purpose :
// =======================================================================
-void OpenGl_Window::SetSwapInterval()
+void OpenGl_Window::SetSwapInterval (Standard_Boolean theToForceNoSync)
{
- if (mySwapInterval != myGlContext->caps->swapInterval)
+ const Standard_Integer aSwapInterval = theToForceNoSync ? 0 : myGlContext->caps->swapInterval;
+ if (mySwapInterval != aSwapInterval)
{
- mySwapInterval = myGlContext->caps->swapInterval;
+ mySwapInterval = aSwapInterval;
myGlContext->SetSwapInterval (mySwapInterval);
}
}
//! Empties all the tables, removes all selections...
Standard_EXPORT void Clear();
- //! returns the Sensitivity of picking
- Standard_Real Sensitivity() const { return myTolerances.Tolerance(); }
-
- //! Returns the pixel tolerance.
- Standard_Integer PixelTolerance() const { return myTolerances.Tolerance(); }
+ //! Returns custom pixel tolerance value.
+ Standard_Integer CustomPixelTolerance() const { return myTolerances.CustomTolerance(); }
//! Sets the pixel tolerance <theTolerance>.
Standard_EXPORT void SetPixelTolerance (const Standard_Integer theTolerance);
+ //! Returns the largest sensitivity of picking
+ Standard_Real Sensitivity() const { return myTolerances.Tolerance(); }
+
+ //! Returns the largest pixel tolerance.
+ Standard_Integer PixelTolerance() const { return myTolerances.Tolerance(); }
+
//! Sorts the detected entites by priority and distance.
//! to be redefined if other criterion are used...
Standard_EXPORT void SortResult();
TKMath
CSF_user32
CSF_advapi32
+CSF_OpenVR
CSF_OpenGlLibs
CSF_advapi32
CSF_user32
myIsInvalidatedImmediate = Standard_False;
myView->Update();
myView->Compute();
+ AutoZFit();
myView->Redraw();
}
aCamera->SetUp (gp_Dir (xu, yu, zu));
- AutoZFit();
-
SwitchSetFront = !SwitchSetFront;
ImmediateUpdate();
aCamera->Transform (aTrsf);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->Transform (aTrsf);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->Transform (aRotation);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->Transform (aRotation);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->Transform (aTrsf);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->Transform (aRotation);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->SetUp (gp_Dir (myYscreenAxis));
aCamera->Transform (aTrsf);
- AutoZFit();
-
ImmediateUpdate();
}
SetTwist (aTwistBefore);
- AutoZFit();
-
SetImmediateUpdate (wasUpdateEnabled);
ImmediateUpdate();
aCamera->SetCenter (aCameraCenter);
}
- AutoZFit();
-
ImmediateUpdate();
}
SetTwist(aTwistBefore);
- AutoZFit();
-
SetImmediateUpdate (wasUpdateEnabled);
ImmediateUpdate();
Panning (anOriginVCS.X(), anOriginVCS.Y());
- AutoZFit();
-
ImmediateUpdate();
}
SetTwist (aTwistBefore);
- AutoZFit();
-
SetImmediateUpdate (wasUpdateEnabled);
ImmediateUpdate();
aCamera->SetUp (gp_Dir (myYscreenAxis));
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->SetUp (gp_Dir (myYscreenAxis));
- AutoZFit();
-
ImmediateUpdate();
}
void V3d_View::ResetViewOrientation()
{
Camera()->CopyOrientationData (myDefaultCamera);
-
- AutoZFit();
-
ImmediateUpdate();
}
void V3d_View::ResetViewMapping()
{
Camera()->CopyMappingData (myDefaultCamera);
-
- AutoZFit();
-
ImmediateUpdate();
}
{
Camera()->Copy (myDefaultCamera);
- AutoZFit();
-
SwitchSetFront = Standard_False;
if (myImmediateUpdate || theToUpdate)
aCamera->SetScale (aCamera->Aspect() >= 1.0 ? theSize / aCamera->Aspect() : theSize);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->SetCenter (myCamStartOpCenter);
aCamera->SetScale (aCamera->Scale() / aCoef);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->SetAspect (myDefaultCamera->Aspect());
aCamera->SetScale (aDefaultScale / Coef);
- AutoZFit();
-
ImmediateUpdate();
}
V3d_BadValue_Raise_if( Sx <= 0. || Sy <= 0. || Sz <= 0.,"V3d_View::SetAxialScale, bad coefficient");
Camera()->SetAxialScale (gp_XYZ (Sx, Sy, Sz));
-
- AutoZFit();
}
//=============================================================================
return;
}
- AutoZFit();
-
if (myImmediateUpdate || theToUpdate)
{
Update();
Translate (aCamera, aPanVec.X(), -aPanVec.Y());
Scale (aCamera, aUSize, aVSize);
- AutoZFit();
}
else
{
aCamera->SetScale (aCamera->Scale() / aCoef);
Translate (aCamera, aZoomAtPointXv - aDxv, aZoomAtPointYv - aDyv);
- AutoZFit();
-
SetImmediateUpdate (wasUpdateEnabled);
ImmediateUpdate();
Translate (aCamera, (theXmin + theXmax) * 0.5, (theYmin + theYmax) * 0.5);
Scale (aCamera, aFitSizeU, aFitSizeV);
- AutoZFit();
-
ImmediateUpdate();
}
{
aCamera->SetAspect (Standard_Real(aTargetSize.x()) / Standard_Real(aTargetSize.y()));
}
- AutoZFit();
// render immediate structures into back buffer rather than front
const Standard_Boolean aPrevImmediateMode = myView->SetImmediateModeDrawToFront (Standard_False);
+ Dz * gp_Pnt (ZX, ZY, ZZ).XYZ()
);
- AutoZFit();
-
ImmediateUpdate();
}
aCamera->SetEye (myCamStartOpEye);
aCamera->SetEye (aCamera->Eye().XYZ() + theLength * myDefaultViewAxis.XYZ());
- AutoZFit();
-
ImmediateUpdate();
}
- Dz * myZscreenAxis.XYZ()
);
- AutoZFit();
-
ImmediateUpdate();
}
gp_Pnt aNewCenter (myCamStartOpCenter.XYZ() - myDefaultViewAxis.XYZ() * theLength);
aCamera->SetCenter (aNewCenter);
- AutoZFit();
-
ImmediateUpdate();
}
#include <StdSelect_ViewerSelector3d.hxx>
#include <TopTools_MapOfShape.hxx>
#include <ViewerTest_AutoUpdater.hxx>
+#include <Aspect_XRSession.hxx>
#include <stdio.h>
Standard_Integer theArgNb,
Standard_CString* theArgVec)
{
+ Handle(V3d_View) aView = ViewerTest::CurrentView();
if (theArgNb < 2)
{
Message::SendFail ("Error: wrong number of arguments! Image file name should be specified at least.");
return 1;
}
+ if (aView.IsNull())
+ {
+ Message::SendFail() << "Error: cannot find an active view!";
+ return 1;
+ }
Standard_Integer anArgIter = 1;
Standard_CString aFilePath = theArgVec[anArgIter++];
ViewerTest_StereoPair aStereoPair = ViewerTest_SP_Single;
V3d_ImageDumpOptions aParams;
+ Handle(Graphic3d_Camera) aCustomCam;
aParams.BufferType = Graphic3d_BT_RGB;
aParams.StereoOptions = V3d_SDO_MONO;
for (; anArgIter < theArgNb; ++anArgIter)
return 1;
}
}
+ else if (anArgIter + 1 < theArgNb
+ && anArg == "-xrpose")
+ {
+ TCollection_AsciiString anXRArg (theArgVec[++anArgIter]);
+ anXRArg.LowerCase();
+ if (anXRArg == "base")
+ {
+ aCustomCam = aView->View()->BaseXRCamera();
+ }
+ else if (anXRArg == "head")
+ {
+ aCustomCam = aView->View()->PosedXRCamera();
+ }
+ else if (anXRArg == "handleft"
+ || anXRArg == "handright")
+ {
+ if (aView->View()->IsActiveXR())
+ {
+ aCustomCam = new Graphic3d_Camera();
+ aView->View()->ComputeXRPosedCameraFromBase (*aCustomCam, anXRArg == "handleft"
+ ? aView->View()->XRSession()->LeftHandPose()
+ : aView->View()->XRSession()->RightHandPose());
+ }
+ }
+ else
+ {
+ Message::SendFail() << "Syntax error: unknown XR pose '" << anXRArg << "'";
+ return 1;
+ }
+ if (aCustomCam.IsNull())
+ {
+ Message::SendFail() << "Error: undefined XR pose";
+ return 0;
+ }
+ }
else if (anArg == "-stereo")
{
if (++anArgIter >= theArgNb)
return 1;
}
- Handle(V3d_View) aView = ViewerTest::CurrentView();
- if (aView.IsNull())
- {
- Message::SendFail() << "Error: cannot find an active view!";
- return 1;
- }
-
if (aParams.Width <= 0 || aParams.Height <= 0)
{
aView->Window()->Size (aParams.Width, aParams.Height);
case Graphic3d_BT_Red: aFormat = Image_Format_Gray; break;
}
+ const bool wasImmUpdate = aView->SetImmediateUpdate (false);
+ Handle(Graphic3d_Camera) aCamBack = aView->Camera();
+ if (!aCustomCam.IsNull())
+ {
+ aView->SetCamera (aCustomCam);
+ }
switch (aStereoPair)
{
case ViewerTest_SP_Single:
break;
}
}
+ if (!aCustomCam.IsNull())
+ {
+ aView->SetCamera (aCamBack);
+ }
+ aView->SetImmediateUpdate (wasImmUpdate);
if (!aPixMap.Save (aFilePath))
{
"vdump <filename>." DUMP_FORMATS " [-width Width -height Height]"
"\n\t\t: [-buffer rgb|rgba|depth=rgb]"
"\n\t\t: [-stereo mono|left|right|blend|sideBySide|overUnder=mono]"
+ "\n\t\t: [-xrPose base|head|handLeft|handRight=base]"
"\n\t\t: [-tileSize Size=0]"
"\n\t\t: Dumps content of the active view into image file",
__FILE__,VDump,group);
}
aCurrentView->SetAutoZFitMode (isOn, aScale);
- aCurrentView->AutoZFit();
aCurrentView->Redraw();
-
return 0;
}
{
theDI << "ProjType: " << projTypeName (aCamera->ProjectionType()) << "\n";
theDI << "FOVy: " << aCamera->FOVy() << "\n";
+ theDI << "FOVx: " << aCamera->FOVx() << "\n";
+ theDI << "FOV2d: " << aCamera->FOV2d() << "\n";
theDI << "Distance: " << aCamera->Distance() << "\n";
theDI << "IOD: " << aCamera->IOD() << "\n";
theDI << "IODType: " << (aCamera->GetIODType() == Graphic3d_Camera::IODType_Absolute ? "absolute" : "relative") << "\n";
case Graphic3d_Camera::FocusType_Relative: theDI << "relative "; break;
}
}
+ else if (anArgCase == "-lockzup"
+ || anArgCase == "-turntable")
+ {
+ bool toLockUp = true;
+ if (++anArgIter < theArgsNb
+ && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toLockUp))
+ {
+ --anArgIter;
+ }
+ ViewerTest::CurrentEventManager()->SetLockOrbitZUp (toLockUp);
+ }
else if (anArgCase == "-fov"
- || anArgCase == "-fovy")
+ || anArgCase == "-fovy"
+ || anArgCase == "-fovx"
+ || anArgCase == "-fov2d")
{
Standard_CString anArgValue = (anArgIter + 1 < theArgsNb) ? theArgVec[anArgIter + 1] : NULL;
if (anArgValue != NULL
&& *anArgValue != '-')
{
++anArgIter;
- aCamera->SetFOVy (Draw::Atof (anArgValue));
+ if (anArgCase == "-fov2d")
+ {
+ aCamera->SetFOV2d (Draw::Atof (anArgValue));
+ }
+ else if (anArgCase == "-fovx")
+ {
+ aCamera->SetFOVy (Draw::Atof (anArgValue) / aCamera->Aspect());///
+ }
+ else
+ {
+ aCamera->SetFOVy (Draw::Atof (anArgValue));
+ }
continue;
}
- theDI << aCamera->FOVy() << " ";
+ if (anArgCase == "-fov2d")
+ {
+ theDI << aCamera->FOV2d() << " ";
+ }
+ else if (anArgCase == "-fovx")
+ {
+ theDI << aCamera->FOVx() << " ";
+ }
+ else
+ {
+ theDI << aCamera->FOVy() << " ";
+ }
+ }
+ else if (anArgIter + 1 < theArgsNb
+ && anArgCase == "-xrpose")
+ {
+ TCollection_AsciiString anXRArg (theArgVec[++anArgIter]);
+ anXRArg.LowerCase();
+ if (anXRArg == "base")
+ {
+ aCamera = aView->View()->BaseXRCamera();
+ }
+ else if (anXRArg == "head")
+ {
+ aCamera = aView->View()->PosedXRCamera();
+ }
+ else
+ {
+ Message::SendFail() << "Syntax error: unknown XR pose '" << anXRArg << "'";
+ return 1;
+ }
+ if (aCamera.IsNull())
+ {
+ Message::SendFail() << "Error: undefined XR pose";
+ return 0;
+ }
+ if (aView->AutoZFitMode())
+ {
+ const Bnd_Box aMinMaxBox = aView->View()->MinMaxValues (false);
+ const Bnd_Box aGraphicBox = aView->View()->MinMaxValues (true);
+ aCamera->ZFitAll (aView->AutoZFitScaleFactor(), aMinMaxBox, aGraphicBox);
+ }
}
else if (aPrsName.IsEmpty()
&& !anArgCase.StartsWith ("-"))
if (aPrsName.IsEmpty()
|| theArgsNb > 2)
{
- aView->AutoZFit();
aView->Redraw();
}
ViewerTest::GetAISContext()->Erase (aCameraFrustum, false);
aView->ZFitAll();
}
- aCameraFrustum->SetCameraFrustum (aView->Camera());
+ aCameraFrustum->SetCameraFrustum (aCamera);
ViewerTest::Display (aPrsName, aCameraFrustum);
}
{
theMode = Graphic3d_StereoMode_SoftPageFlip;
}
+ else if (aFlag == "openvr"
+ || aFlag == "vr")
+ {
+ theMode = Graphic3d_StereoMode_OpenVR;
+ }
else
{
return Standard_False;
case Graphic3d_StereoMode_SideBySide : aMode = "sideBySide"; break;
case Graphic3d_StereoMode_OverUnder : aMode = "overUnder"; break;
case Graphic3d_StereoMode_SoftPageFlip : aMode = "softpageflip"; break;
+ case Graphic3d_StereoMode_OpenVR : aMode = "openVR"; break;
case Graphic3d_StereoMode_Anaglyph :
aMode = "anaglyph";
switch (aView->RenderingParams().AnaglyphFilter)
aCamera->SetProjectionType (Graphic3d_Camera::Projection_Stereo);
}
ViewerTest_myDefaultCaps.contextStereo = Standard_True;
- return 0;
+ if (aParams->StereoMode != Graphic3d_StereoMode_OpenVR)
+ {
+ return 0;
+ }
}
else if (aFlag == "-reverse"
|| aFlag == "-reversed"
ViewerTest_myDefaultCaps.contextStereo = Standard_True;
}
}
+ else if (anArgIter + 1 < theArgNb
+ && aFlag == "-hmdfov2d")
+ {
+ aParams->HmdFov2d = (float )Draw::Atof (theArgVec[++anArgIter]);
+ if (aParams->HmdFov2d < 10.0f
+ || aParams->HmdFov2d > 180.0f)
+ {
+ Message::SendFail() << "Error: FOV is out of range";
+ return 1;
+ }
+ }
+ else if (aFlag == "-mirror"
+ || aFlag == "-mirrorcomposer")
+ {
+ Standard_Boolean toEnable = Standard_True;
+ if (++anArgIter < theArgNb
+ && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable))
+ {
+ --anArgIter;
+ }
+ aParams->ToMirrorComposer = toEnable;
+ }
+ else if (anArgIter + 1 < theArgNb
+ && (aFlag == "-unitfactor"
+ || aFlag == "-unitscale"))
+ {
+ aView->View()->SetUnitFactor (Draw::Atof (theArgVec[++anArgIter]));
+ }
else
{
Message::SendFail() << "Syntax error at '" << anArg << "'";
{
aParams->StereoMode = aMode;
aCamera->SetProjectionType (Graphic3d_Camera::Projection_Stereo);
+ if (aParams->StereoMode == Graphic3d_StereoMode_OpenVR)
+ {
+ // initiate implicit continuous rendering
+ ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), aView, true);
+ }
}
return 0;
}
}
const Handle(AIS_InteractiveContext)& aContext = ViewerTest::GetAISContext();
+ const Handle(V3d_View)& aView = ViewerTest::CurrentView();
if (aContext.IsNull())
{
Message::SendFail ("Error: no active viewer");
TCollection_AsciiString aFile;
StdSelect_TypeOfSelectionImage aType = StdSelect_TypeOfSelectionImage_NormalizedDepth;
+ Handle(Graphic3d_Camera) aCustomCam;
Image_Format anImgFormat = Image_Format_BGR;
Standard_Integer aPickedIndex = 1;
for (Standard_Integer anArgIter = 1; anArgIter < theArgsNb; ++anArgIter)
aPickedIndex = Draw::Atoi (theArgVec[anArgIter]);
}
+ else if (anArgIter + 1 < theArgsNb
+ && aParam == "-xrpose")
+ {
+ TCollection_AsciiString anXRArg (theArgVec[++anArgIter]);
+ anXRArg.LowerCase();
+ if (anXRArg == "base")
+ {
+ aCustomCam = aView->View()->BaseXRCamera();
+ }
+ else if (anXRArg == "head")
+ {
+ aCustomCam = aView->View()->PosedXRCamera();
+ }
+ else
+ {
+ Message::SendFail() << "Syntax error: unknown XR pose '" << anXRArg << "'";
+ return 1;
+ }
+ if (aCustomCam.IsNull())
+ {
+ Message::SendFail() << "Error: undefined XR pose";
+ return 0;
+ }
+ }
else if (aFile.IsEmpty())
{
aFile = theArgVec[anArgIter];
return 1;
}
- const Handle(V3d_View)& aView = ViewerTest::CurrentView();
Standard_Integer aWidth = 0, aHeight = 0;
aView->Window()->Size (aWidth, aHeight);
Message::SendFail ("Error: can't allocate image");
return 1;
}
+
+ const bool wasImmUpdate = aView->SetImmediateUpdate (false);
+ Handle(Graphic3d_Camera) aCamBack = aView->Camera();
+ if (!aCustomCam.IsNull())
+ {
+ aView->SetCamera (aCustomCam);
+ }
if (!aContext->MainSelector()->ToPixMap (aPixMap, aView, aType, aPickedIndex))
{
Message::SendFail ("Error: can't generate selection image");
return 1;
}
+ if (!aCustomCam.IsNull())
+ {
+ aView->SetCamera (aCamBack);
+ }
+ aView->SetImmediateUpdate (wasImmUpdate);
+
if (!aPixMap.Save (aFile))
{
Message::SendFail ("Error: can't save selection image");
__FILE__, VVbo, group);
theCommands.Add ("vstereo",
"vstereo [0|1] [-mode Mode] [-reverse {0|1}]"
+ "\n\t\t: [-mirrorComposer] [-hmdfov2d AngleDegrees] [-unitFactor MetersFactor]"
"\n\t\t: [-anaglyph Filter]"
- "\n\t\t: Control stereo output mode. Available modes for -mode:"
+ "\n\t\t: Control stereo output mode."
+ "\n\t\t: When -mirrorComposer is specified, VR rendered frame will be mirrored in window (debug)."
+ "\n\t\t: Parameter -unitFactor specifies meters scale factor for mapping VR input."
+ "\n\t\t: Available modes for -mode:"
"\n\t\t: quadBuffer - OpenGL QuadBuffer stereo,"
"\n\t\t: requires driver support."
"\n\t\t: Should be called BEFORE vinit!"
"\n\t\t: chessBoard - chess-board output"
"\n\t\t: sideBySide - horizontal pair"
"\n\t\t: overUnder - vertical pair"
+ "\n\t\t: openVR - OpenVR (HMD)"
"\n\t\t: Available Anaglyph filters for -anaglyph:"
"\n\t\t: redCyan, redCyanSimple, yellowBlue, yellowBlueSimple,"
"\n\t\t: greenMagentaSimple",
"\n\t\t: [-stereo] [-leftEye] [-rightEye]"
"\n\t\t: [-iod [Distance]] [-iodType [absolute|relative]]"
"\n\t\t: [-zfocus [Value]] [-zfocusType [absolute|relative]]"
+ "\n\t\t: [-fov2d [Angle]] [-lockZup {0|1}]"
+ "\n\t\t: [-xrPose base|head=base]"
"\n\t\t: Manages camera parameters."
"\n\t\t: Displays frustum when presntation name PrsName is specified."
"\n\t\t: Prints current value when option called without argument."
"\n\t\t: Perspective camera:"
"\n\t\t: -persp activate perspective projection (mono)"
"\n\t\t: -fovy field of view in y axis, in degrees"
+ "\n\t\t: -fov2d field of view limit for 2d on-screen elements"
"\n\t\t: -distance distance of eye from camera center"
+ "\n\t\t: -lockZup lock Z up (tunrtable mode)"
"\n\t\t: Stereoscopic camera:"
"\n\t\t: -stereo perspective projection (stereo)"
"\n\t\t: -leftEye perspective projection (left eye)"
theCommands.Add ("vseldump",
"vseldump file -type {depth|unnormDepth|object|owner|selMode|entity}=depth -pickedIndex Index=1"
+ "\n\t\t: [-xrPose base|head=base]"
"\n\t\t: Generate an image based on detection results:"
"\n\t\t: depth normalized depth values"
"\n\t\t: unnormDepth unnormalized depth values"
--- /dev/null
+srcinc:::occtvr_actions.json
+srcinc:::occtvr_bindings_generic.json
+srcinc:::occtvr_bindings_holographic_hmd.json
+srcinc:::occtvr_bindings_index_hmd.json
+srcinc:::occtvr_bindings_rift.json
+srcinc:::occtvr_bindings_vive.json
+srcinc:::occtvr_bindings_vive_controller.json
+srcinc:::occtvr_bindings_vive_cosmos.json
+srcinc:::occtvr_bindings_vive_pro.json
--- /dev/null
+{
+ "actions": [
+ {
+ "name": "/actions/generic_head/in/headset_on_head",
+ "type": "boolean",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/generic_left/in/pose_base",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_right/in/pose_base",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_left/in/pose_front",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_right/in/pose_front",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_left/in/pose_handgrip",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_right/in/pose_handgrip",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_left/in/pose_tip",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_right/in/pose_tip",
+ "type": "pose"
+ },
+ {
+ "name": "/actions/generic_left/out/haptic",
+ "type": "vibration"
+ },
+ {
+ "name": "/actions/generic_right/out/haptic",
+ "type": "vibration"
+ },
+ {
+ "name": "/actions/generic_left/in/appmenu_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_right/in/appmenu_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_left/in/sysmenu_click",
+ "type": "boolean",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/generic_right/in/sysmenu_click",
+ "type": "boolean",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/generic_left/in/trigger_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_right/in/trigger_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_left/in/trigger_pull",
+ "type": "vector1"
+ },
+ {
+ "name": "/actions/generic_right/in/trigger_pull",
+ "type": "vector1"
+ },
+ {
+ "name": "/actions/generic_left/in/grip_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_right/in/grip_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_left/in/trackpad_position",
+ "type": "vector2"
+ },
+ {
+ "name": "/actions/generic_right/in/trackpad_position",
+ "type": "vector2"
+ },
+ {
+ "name": "/actions/generic_left/in/trackpad_touch",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_right/in/trackpad_touch",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_left/in/trackpad_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_right/in/trackpad_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_left/in/thumbstick_position",
+ "type": "vector2"
+ },
+ {
+ "name": "/actions/generic_right/in/thumbstick_position",
+ "type": "vector2"
+ },
+ {
+ "name": "/actions/generic_left/in/thumbstick_touch",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_right/in/thumbstick_touch",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_left/in/thumbstick_click",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/generic_right/in/thumbstick_click",
+ "type": "boolean"
+ }
+ ],
+ "action_sets": [
+ {
+ "name": "/actions/generic_head",
+ "usage": "single"
+ },
+ {
+ "name": "/actions/generic_left",
+ "usage": "leftright"
+ },
+ {
+ "name": "/actions/generic_right",
+ "usage": "leftright"
+ }
+ ],
+ "default_bindings": [
+ {
+ "controller_type": "vive_controller",
+ "binding_url": "occtvr_bindings_vive_controller.json"
+ },
+ {
+ "controller_type": "generic",
+ "binding_url": "occtvr_bindings_generic.json"
+ },
+ {
+ "controller_type": "holographic_controller",
+ "binding_url": "occtvr_bindings_holographic_hmd.json"
+ },
+ {
+ "controller_type": "indexhmd",
+ "binding_url": "occtvr_bindings_index_hmd.json"
+ },
+ {
+ "controller_type": "rift",
+ "binding_url": "occtvr_bindings_rift.json"
+ },
+ {
+ "controller_type": "vive",
+ "binding_url": "occtvr_bindings_vive.json"
+ },
+ {
+ "controller_type": "vive_cosmos",
+ "binding_url": "occtvr_bindings_vive_cosmos.json"
+ },
+ {
+ "controller_type": "vive_pro",
+ "binding_url": "occtvr_bindings_vive_pro.json"
+ }
+ ],
+ "localization": [
+ {
+ "language_tag": "en_US",
+ "/actions/generic_head/in/headset_on_head": "Headset is on head",
+ "/actions/generic_left/in/appmenu_click": "Left app menu",
+ "/actions/generic_left/in/sysmenu_click": "Left system menu",
+ "/actions/generic_left/in/trigger_click": "Left trigger click",
+ "/actions/generic_left/in/trigger_pull": "Left trigger squeeze",
+ "/actions/generic_left/in/grip_click": "Left hand full grip",
+ "/actions/generic_left/in/pose_base": "Left hand base",
+ "/actions/generic_left/in/pose_front": "Left hand front",
+ "/actions/generic_left/in/pose_handgrip": "Left handgrip pose",
+ "/actions/generic_left/in/pose_tip": "Left forefinger tip",
+ "/actions/generic_left/in/trackpad_position": "Left trackpad position",
+ "/actions/generic_left/in/trackpad_touch": "Left trackpad touch",
+ "/actions/generic_left/in/trackpad_click": "Left trackpad click",
+ "/actions/generic_left/in/thumbstick_position": "Left thumbstick position",
+ "/actions/generic_left/in/thumbstick_touch": "Left thumbstick touch",
+ "/actions/generic_left/in/thumbstick_click": "Left thumbstick click",
+ "/actions/generic_left/out/haptic": "Left hand haptic",
+ "/actions/generic_right/in/appmenu_click": "Right app menu",
+ "/actions/generic_right/in/sysmenu_click": "Right system menu",
+ "/actions/generic_right/in/trigger_click": "Right trigger click",
+ "/actions/generic_right/in/trigger_pull": "Right trigger squeeze",
+ "/actions/generic_right/in/grip_click": "Right hand full grip",
+ "/actions/generic_right/in/pose_base": "Right hand base",
+ "/actions/generic_right/in/pose_front": "Right hand front",
+ "/actions/generic_right/in/pose_handgrip": "Right handgrip pose",
+ "/actions/generic_right/in/pose_tip": "Right forefinger tip",
+ "/actions/generic_right/in/trackpad_position": "Right trackpad position",
+ "/actions/generic_right/in/trackpad_touch": "Right trackpad touch",
+ "/actions/generic_right/in/trackpad_click": "Right trackpad click",
+ "/actions/generic_right/in/thumbstick_position": "Right thumbstick position",
+ "/actions/generic_right/in/thumbstick_touch": "Right thumbstick touch",
+ "/actions/generic_right/in/thumbstick_click": "Right thumbstick click",
+ "/actions/generic_right/out/haptic": "Right hand haptic"
+ }
+ ]
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_left": {
+ "haptics": [
+ {
+ "output": "/actions/generic_left/out/haptic",
+ "path": "/user/hand/left/output/haptic"
+ }
+ ],
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/appmenu_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/left/input/application_menu"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/trigger_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/trackpad_click" },
+ "touch": { "output": "/actions/generic_left/in/trackpad_touch" },
+ "position": { "output": "/actions/generic_left/in/trackpad_position" }
+ },
+ "mode": "trackpad",
+ "path": "/user/hand/left/input/trackpad"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/grip_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/left/input/grip"
+ }
+ ]
+ },
+ "/actions/generic_right": {
+ "haptics": [
+ {
+ "output": "/actions/generic_right/out/haptic",
+ "path": "/user/hand/right/output/haptic"
+ }
+ ],
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/appmenu_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/right/input/application_menu"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/trigger_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/trackpad_click" },
+ "touch": { "output": "/actions/generic_right/in/trackpad_touch" },
+ "position": { "output": "/actions/generic_right/in/trackpad_position" }
+ },
+ "mode": "trackpad",
+ "path": "/user/hand/right/input/trackpad"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/grip_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/right/input/grip"
+ }
+ ]
+ }
+ },
+ "controller_type": "generic",
+ "description": "Standard Open CASCADE Technology VR bindings for a generic controller",
+ "name": "OCCT VR bindings for a generic controller"
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_head": {
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_head/in/headset_on_head" }
+ },
+ "mode": "button",
+ "path": "/user/head/proximity"
+ }
+ ]
+ }
+ },
+ "controller_type": "holographic_hmd",
+ "description": "",
+ "name": "holographic_hmd defaults"
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_head": {
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_head/in/headset_on_head" }
+ },
+ "mode": "button",
+ "path": "/user/head/proximity"
+ }
+ ]
+ }
+ },
+ "controller_type": "indexhmd",
+ "description": "",
+ "name": "index hmd defaults"
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_head": {
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_head/in/headset_on_head" }
+ },
+ "mode": "button",
+ "path": "/user/head/proximity"
+ }
+ ]
+ }
+ },
+ "controller_type": "rift",
+ "description": "",
+ "name": "rift defaults"
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_head": {
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_head/in/headset_on_head" }
+ },
+ "mode": "button",
+ "path": "/user/head/proximity"
+ }
+ ]
+ }
+ },
+ "controller_type": "vive",
+ "description": "",
+ "name": "vive defaults"
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_left": {
+ "haptics": [
+ {
+ "output": "/actions/generic_left/out/haptic",
+ "path": "/user/hand/left/output/haptic"
+ }
+ ],
+ "poses": [
+ {
+ "output": "/actions/generic_left/in/pose_base",
+ "path": "/user/hand/left/pose/base"
+ },
+ {
+ "output": "/actions/generic_left/in/pose_front",
+ "path": "/user/hand/left/pose/front"
+ },
+ {
+ "output": "/actions/generic_left/in/pose_handgrip",
+ "path": "/user/hand/left/pose/handgrip"
+ },
+ {
+ "output": "/actions/generic_left/in/pose_tip",
+ "path": "/user/hand/left/pose/tip"
+ }
+ ],
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/appmenu_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/left/input/application_menu"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/trigger_click" },
+ "pull": { "output": "/actions/generic_left/in/trigger_pull" }
+ },
+ "mode": "trigger",
+ "path": "/user/hand/left/input/trigger"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/trackpad_click" },
+ "touch": { "output": "/actions/generic_left/in/trackpad_touch" },
+ "position": { "output": "/actions/generic_left/in/trackpad_position" }
+ },
+ "mode": "trackpad",
+ "path": "/user/hand/left/input/trackpad"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/grip_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/left/input/grip"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_left/in/sysmenu_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/left/input/system"
+ }
+ ]
+ },
+ "/actions/generic_right": {
+ "haptics": [
+ {
+ "output": "/actions/generic_right/out/haptic",
+ "path": "/user/hand/right/output/haptic"
+ }
+ ],
+ "poses": [
+ {
+ "output": "/actions/generic_right/in/pose_base",
+ "path": "/user/hand/right/pose/base"
+ },
+ {
+ "output": "/actions/generic_right/in/pose_front",
+ "path": "/user/hand/right/pose/front"
+ },
+ {
+ "output": "/actions/generic_right/in/pose_handgrip",
+ "path": "/user/hand/right/pose/handgrip"
+ },
+ {
+ "output": "/actions/generic_right/in/pose_tip",
+ "path": "/user/hand/right/pose/tip"
+ }
+ ],
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/appmenu_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/right/input/application_menu"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/trigger_click" },
+ "pull": { "output": "/actions/generic_right/in/trigger_pull" }
+ },
+ "mode": "trigger",
+ "path": "/user/hand/right/input/trigger"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/trackpad_click" },
+ "touch": { "output": "/actions/generic_right/in/trackpad_touch" },
+ "position": { "output": "/actions/generic_right/in/trackpad_position" }
+ },
+ "mode": "trackpad",
+ "path": "/user/hand/right/input/trackpad"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/grip_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/right/input/grip"
+ },
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_right/in/sysmenu_click" }
+ },
+ "mode": "button",
+ "path": "/user/hand/right/input/system"
+ }
+ ]
+ }
+ },
+ "controller_type": "vive_controller",
+ "description": "Standard Open CASCADE Technology VR bindings for the Vive controller",
+ "name": "OCCT VR bindings for the Vive controller"
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_head": {
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_head/in/headset_on_head" }
+ },
+ "mode": "button",
+ "path": "/user/head/proximity"
+ }
+ ]
+ }
+ },
+ "controller_type": "vive_cosmos",
+ "description": "",
+ "name": "vive cosmos hmd defaults",
+}
--- /dev/null
+{
+ "bindings": {
+ "/actions/generic_head": {
+ "sources": [
+ {
+ "inputs": {
+ "click": { "output": "/actions/generic_head/in/headset_on_head" }
+ },
+ "mode": "button",
+ "path": "/user/head/proximity"
+ }
+ ]
+ }
+ },
+ "controller_type": "vive_pro",
+ "description": "",
+ "name": "vive_pro defaults"
+}