diff --git a/Multiview Display Calibrations.meta b/Display Configuration Files.meta
similarity index 77%
rename from Multiview Display Calibrations.meta
rename to Display Configuration Files.meta
index dfeeb05..7943c3a 100644
--- a/Multiview Display Calibrations.meta
+++ b/Display Configuration Files.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 64b2e49765f63fd44933ea99546981eb
+guid: fe7d104fab1525341bab38e03c383d31
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Multiview Display Calibrations/Holobox_27-Inch_17Q.txt b/Display Configuration Files/05G_27-Inch.txt
similarity index 86%
rename from Multiview Display Calibrations/Holobox_27-Inch_17Q.txt
rename to Display Configuration Files/05G_27-Inch.txt
index 97471e0..747c0ea 100644
--- a/Multiview Display Calibrations/Holobox_27-Inch_17Q.txt
+++ b/Display Configuration Files/05G_27-Inch.txt
@@ -7,11 +7,11 @@ DistanceSensorToPanelCenterMM=0.000000
HorizontalResolution=5120
VerticalResolution=2880
SensorMountingPosition=0
-ConfigCode=17q
-NativeViewcount=17
-AngleRatioNumerator=4
-AngleRatioDenominator=5
-LeftLensOrientation=0
+ConfigCode=05G
+NativeViewcount=5
+AngleRatioNumerator=5
+AngleRatioDenominator=7
+LeftLensOrientation=1
isBGR=false
ViewOffsetAtBaseDistance=3
BlackBorderDefault=0
@@ -30,4 +30,4 @@ zCorrectionFunctionBase=0.768900
ZCompensationValueAtMinimumWorkingDistance=41
ZCompensationValueAtMaximumWorkingDistance=-14
LatencyCorrectionTimeShift=0.000000
-ApertureAngle=22.1
+ApertureAngle=14.9
diff --git a/Multiview Display Calibrations/Display_32-Inch_07G.txt.meta b/Display Configuration Files/05G_27-Inch.txt.meta
similarity index 75%
rename from Multiview Display Calibrations/Display_32-Inch_07G.txt.meta
rename to Display Configuration Files/05G_27-Inch.txt.meta
index e3953f6..e558836 100644
--- a/Multiview Display Calibrations/Display_32-Inch_07G.txt.meta
+++ b/Display Configuration Files/05G_27-Inch.txt.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 5da331dd91712f44db9ae3a00c573bdc
+guid: 056b498ec6017af439fe019ed2b9291a
TextScriptImporter:
externalObjects: {}
userData:
diff --git a/Multiview Display Calibrations/Display_32-Inch_07D.txt b/Display Configuration Files/07D_32-Inch.txt
similarity index 100%
rename from Multiview Display Calibrations/Display_32-Inch_07D.txt
rename to Display Configuration Files/07D_32-Inch.txt
diff --git a/Multiview Display Calibrations/Display_13-3-Inch_S1D.txt.meta b/Display Configuration Files/07D_32-Inch.txt.meta
similarity index 75%
rename from Multiview Display Calibrations/Display_13-3-Inch_S1D.txt.meta
rename to Display Configuration Files/07D_32-Inch.txt.meta
index bf1ce13..8ce5100 100644
--- a/Multiview Display Calibrations/Display_13-3-Inch_S1D.txt.meta
+++ b/Display Configuration Files/07D_32-Inch.txt.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 52a10a10d88467e4dbaf0e1ae367460b
+guid: af1924a6280862340a43910cb96aa8bd
TextScriptImporter:
externalObjects: {}
userData:
diff --git a/Multiview Display Calibrations/Display_32-Inch_07G.txt b/Display Configuration Files/07G_32-Inch.txt
similarity index 100%
rename from Multiview Display Calibrations/Display_32-Inch_07G.txt
rename to Display Configuration Files/07G_32-Inch.txt
diff --git a/Multiview Display Calibrations/Display_32-Inch_07D.txt.meta b/Display Configuration Files/07G_32-Inch.txt.meta
similarity index 75%
rename from Multiview Display Calibrations/Display_32-Inch_07D.txt.meta
rename to Display Configuration Files/07G_32-Inch.txt.meta
index 4a3e5e5..0dd2c6e 100644
--- a/Multiview Display Calibrations/Display_32-Inch_07D.txt.meta
+++ b/Display Configuration Files/07G_32-Inch.txt.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: a5be5953d58ab064786c1db43d6d168b
+guid: 17e90fc46bb44a74fb56a4b475919d3a
TextScriptImporter:
externalObjects: {}
userData:
diff --git a/Multiview Display Calibrations/Display_65-Inch_17D.txt b/Display Configuration Files/17D_65-Inch.txt
similarity index 100%
rename from Multiview Display Calibrations/Display_65-Inch_17D.txt
rename to Display Configuration Files/17D_65-Inch.txt
diff --git a/Multiview Display Calibrations/Display_27-Inch_17D - alternativ.txt.meta b/Display Configuration Files/17D_65-Inch.txt.meta
similarity index 75%
rename from Multiview Display Calibrations/Display_27-Inch_17D - alternativ.txt.meta
rename to Display Configuration Files/17D_65-Inch.txt.meta
index b9d4354..154a46e 100644
--- a/Multiview Display Calibrations/Display_27-Inch_17D - alternativ.txt.meta
+++ b/Display Configuration Files/17D_65-Inch.txt.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 45fdfb59b6c506c4fb4d5c76f3e2286f
+guid: b805cda4717557d409db7c08f8ae3313
TextScriptImporter:
externalObjects: {}
userData:
diff --git a/Multiview Display Calibrations/Display_27-Inch_17D - alternativ.txt b/Display Configuration Files/17Q_27-Inch.txt
similarity index 98%
rename from Multiview Display Calibrations/Display_27-Inch_17D - alternativ.txt
rename to Display Configuration Files/17Q_27-Inch.txt
index 00336b3..6b7c909 100644
--- a/Multiview Display Calibrations/Display_27-Inch_17D - alternativ.txt
+++ b/Display Configuration Files/17Q_27-Inch.txt
@@ -7,7 +7,7 @@ DistanceSensorToPanelCenterMM=0.000000
HorizontalResolution=5120
VerticalResolution=2880
SensorMountingPosition=0
-ConfigCode=17d
+ConfigCode=17Q
NativeViewcount=17
AngleRatioNumerator=4
AngleRatioDenominator=5
diff --git a/Display Configuration Files/17Q_27-Inch.txt.meta b/Display Configuration Files/17Q_27-Inch.txt.meta
new file mode 100644
index 0000000..d46a229
--- /dev/null
+++ b/Display Configuration Files/17Q_27-Inch.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: a6ce2751473f29b4db2d325e076123d1
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Multiview Display Calibrations/Display_43-Inch_N8D.txt b/Display Configuration Files/N8D_43-Inch.txt
similarity index 100%
rename from Multiview Display Calibrations/Display_43-Inch_N8D.txt
rename to Display Configuration Files/N8D_43-Inch.txt
diff --git a/Display Configuration Files/N8D_43-Inch.txt.meta b/Display Configuration Files/N8D_43-Inch.txt.meta
new file mode 100644
index 0000000..cd3db59
--- /dev/null
+++ b/Display Configuration Files/N8D_43-Inch.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9c6a1ca4c5dea5c43abae232a5855386
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Multiview Display Calibrations/Display_13-3-Inch_S1D.txt b/Display Configuration Files/S1D_13-3-Inch.txt
similarity index 100%
rename from Multiview Display Calibrations/Display_13-3-Inch_S1D.txt
rename to Display Configuration Files/S1D_13-3-Inch.txt
diff --git a/Display Configuration Files/S1D_13-3-Inch.txt.meta b/Display Configuration Files/S1D_13-3-Inch.txt.meta
new file mode 100644
index 0000000..a0979e9
--- /dev/null
+++ b/Display Configuration Files/S1D_13-3-Inch.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 7fb82fc3139d3b049a1f006296b04dff
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/G3DCamera.cs b/G3DCamera.cs
index 5fc14fd..c8cf065 100644
--- a/G3DCamera.cs
+++ b/G3DCamera.cs
@@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
-using UnityEditor;
+using G3D.RenderPipeline;
using UnityEngine;
using UnityEngine.Rendering;
-#if UNITY_EDITOR
-
-#endif
-
#if G3D_HDRP
using UnityEngine.Rendering.HighDefinition;
#endif
@@ -17,368 +13,349 @@
using UnityEngine.UI;
#endif
-///
-/// IMPORTANT: This script must not be attached to a camera already using a G3D camera script.
-///
-[RequireComponent(typeof(Camera))]
-[DisallowMultipleComponent]
-public class G3DCamera
- : MonoBehaviour,
- ITNewHeadPositionCallback,
- ITNewShaderParametersCallback,
- ITNewErrorMessageCallback
+namespace G3D
{
- [Tooltip("Drop the calibration file for the display you want to use here.")]
- public TextAsset calibrationFile;
-
- [Tooltip(
- "This path has to be set to the directory where the folder containing the calibration files for your monitor are located. The folder has to have the same name as your camera model."
- )]
- public string calibrationPathOverwrite = "";
-
- #region 3D Effect settings
- public G3DCameraMode mode = G3DCameraMode.DIORAMA;
- public static string CAMERA_NAME_PREFIX = "g3dcam_";
-
- [Tooltip(
- "This value can be used to scale the real world values used to calibrate the extension. For example if your scene is 10 times larger than the real world, you can set this value to 10. Do NOT change this while the game is already running!"
- )]
- [Min(0.000001f)]
- public float sceneScaleFactor = 1.0f;
-
- [Tooltip(
- "If set to true, the views will be flipped horizontally. This is necessary for holoboxes."
- )]
- public bool mirrorViews = false;
-
- [Tooltip(
- "Set a percentage value to render only that percentage of the width and height per view. E.g. a reduction of 50% will reduce the rendered size by a factor of 4. Adapt Render Resolution To Views takes precedence."
- )]
- [Range(1, 100)]
- public int renderResolutionScale = 100;
-
- [Tooltip(
- "Set the dolly zoom effekt. 1 correponds to no dolly zoom. 0 is all the way zoomed in to the focus plane. 3 is all the way zoomed out."
- )]
- [Range(0.001f, 3)]
- public float dollyZoom = 1;
-
- [Tooltip(
- "Scale the view offset up or down. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance. This can be used to adjust the view offset down for very large scenes."
- )]
- [Range(0.0f, 5.0f)]
- public float viewOffsetScale = 1.0f; // scale the view offset to the focus distance. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance.
-
///
- /// Shifts the individual views to the left or right by the specified number of views.
+ /// IMPORTANT: This script must not be attached to a camera already using a G3D camera script.
///
- [Tooltip("Shifts the individual views to the left or right by the specified number of views.")]
- public int viewOffset = 0;
-
- [Tooltip(
- "Scales the strength of the head tracking effect. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance."
- )]
- [Min(0.0f)]
- public float headTrackingScale = 1.0f; // scale the head tracking effect
- #endregion
-
- #region Advanced settings
- [Tooltip(
- "Smoothes the head position (Size of the filter kernel). Not filtering is applied, if set to all zeros. DO NOT CHANGE THIS WHILE GAME IS ALREADY RUNNING!"
- )]
- public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5);
- public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE;
-
- [Tooltip("If set to true, the head tracking library will print debug messages to the console.")]
- public bool debugMessages = false;
+ [RequireComponent(typeof(Camera))]
+ [DisallowMultipleComponent]
+ public class G3DCamera : MonoBehaviour
+ {
+ [Tooltip("Drop the configuration file for the display you want to use here.")]
+ public TextAsset configurationFile;
+
+ [Tooltip(
+ "This path has to be set to the directory where the folder containing the configuration files for your monitor are located. The folder has to have the same name as your camera model."
+ )]
+ ///
+ /// This path has to be set to the directory where the folder containing the configuration files for your monitor are located. The folder has to have the same name as your camera model.
+ ///
+ public string configurationPathOverwrite = "";
+
+ public G3DCameraMode mode = G3DCameraMode.MULTIVIEW;
+
+ ///
+ /// prefix added to the cameras created by this script.
+ ///
+ public static string CAMERA_NAME_PREFIX = "g3dcam_";
+
+ [Tooltip(
+ "If set to true, the views will be flipped horizontally. This is necessary for holoboxes."
+ )]
+ public bool mirrorViews = false;
+
+ [Tooltip(
+ "Set a percentage value to render only that percentage of the width and height per view. E.g. a reduction of 50% will reduce the rendered size by a factor of 4."
+ )]
+ [Range(1, 100)]
+ ///
+ /// "Set a percentage value to render only that percentage of the width and height per view.
+ /// E.g. a reduction of 50% will reduce the rendered size by a factor of 4.
+
+ ///
+ public int renderResolutionScale = 100;
+
+ [Tooltip(
+ "Set the dolly zoom effekt. 1 correponds to no dolly zoom. 0 is all the way zoomed in to the focus plane. 3 is all the way zoomed out."
+ )]
+ [Range(0.001f, 3)]
+ public float dollyZoom = 1;
+
+ [Tooltip(
+ "Scale the view offset up or down. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance. This can be used to adjust the view offset down for very large scenes."
+ )]
+ [Range(0.0f, 20.0f)]
+ ///
+ /// Scale the distance between the individual views (cameras). The base value is calculated from the configuration file.
+ /// 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance.
+ ///
+ public float viewOffsetScale = 1.0f;
+
+ ///
+ /// The distance between the camera and the focus plane in meters. Default is 70 cm.
+ /// Is read from configuration file at startup.
+ ///
+ [Tooltip(
+ "The distance between the camera and the focus plane in meters. Default is 70 cm. Is read from configuration file at startup."
+ )]
+ [Min(0.0f)]
+ public float focusDistance = 0.7f;
+
+ ///
+ /// Shifts the individual views to the left or right by the specified number of views.
+ /// This does not shift the cameras. This shifts the views you see on the display.
+ ///
+ [Tooltip(
+ "Shifts the individual views to the left or right by the specified number of views."
+ )]
+ public int viewOffset = 0;
+
+ [Tooltip(
+ "Scales the strength of the head tracking effect. 1.0f is no scaling, 0.5f is half the distance, 2.0f is double the distance."
+ )]
+ [Min(0.0f)]
+ ///
+ /// Scale the head tracking effect. Dont set this lower than 0.0f.
+ ///
+ public float headTrackingSensitivity = 1.0f; // scale the head tracking effect
+ #region Advanced settings
+ ///
+ /// Smoothes the head position (Size of the filter kernel). No filtering is applied, if set to all zeros. DO NOT CHANGE THIS WHILE GAME IS ALREADY RUNNING!
+ ///
+ public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5);
+
+ ///
+ /// How latency correction is handled by the headtracking library.
+ ///
+ public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE;
+
+ [Tooltip(
+ "If set to true, the head tracking library will print debug messages to the console."
+ )]
+ ///
+ /// If set to true, the head tracking library will print debug messages to the console.
+ ///
+ public bool debugMessages = false;
+
+ [Tooltip(
+ "If set to true, the gizmos for the focus distance (green) and eye separation (blue) will be shown."
+ )]
+ public bool showGizmos = true;
+
+ [Tooltip("Scales the gizmos. Affectd by scene scale factor.")]
+ [Range(0.005f, 5.0f)]
+ public float gizmoSize = 1.0f;
+
+ public bool invertViewsInHeadtracking = false;
+
+ [Tooltip(
+ "Where the views start to yoyo in the index map in percent. Index map contains the order of views."
+ )]
+ [Range(0, 100)]
+ ///
+ /// Where the views start to yoyo in the index map in percent. Index map contains the order of views.
+ /// [0, 1, 2, 3, 4, 5, 6, 7] with yoyo start at 50% would become [6, 5, 4, 3, 4, 5, 6]
+ ///
+ public int indexMapYoyoStart = 0;
+
+ [Tooltip("Inverts the entire index map. Index map contains the order of views.")]
+ ///
+ /// Inverts the entire index map. Index map contains the order of views.
+ /// [0, 1, 2, 3, 4, 5, 6, 7] would become [7, 6, 5, 4, 3, 2, 1, 0]
+ ///
+ public bool invertIndexMap = false;
+
+ [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")]
+ ///
+ /// Inverts the individual indices in the index map. Index map contains the order of views.
+ /// [6, 5, 4, 3, 4, 5, 6] would become [0, 1, 2, 3, 2, 1, 0]
+ ///
+ public bool invertIndexMapIndices = false;
+
+ public HeadtrackingConnection headtrackingConnection;
+
+ #endregion
+
+ #region Private variables
+ private IndexMap indexMap = IndexMap.Instance;
+
+ // distance between the two cameras for headtracking mode (in meters). DO NOT USE FOR MULTIVIEW MODE!
+ private float viewSeparation = 0.065f;
+
+ private const int MAX_CAMERAS = 16; //shaders dont have dynamic arrays and this is the max supported. change it here? change it in the shaders as well ...
+ private int internalCameraCount = 2;
+ private int oldRenderResolutionScale = 100;
+
+ private static object shaderLock = new object();
+
+ private Camera mainCamera;
+ private List cameras = null;
+ private GameObject focusPlaneObject = null;
+ private GameObject cameraParent = null;
+
+ private Material material;
+#if G3D_URP
+ private G3D.RenderPipeline.URP.ScriptableRP customPass;
+ private UnityEngine.Rendering.Universal.AntialiasingMode antialiasingMode = UnityEngine
+ .Rendering
+ .Universal
+ .AntialiasingMode
+ .None;
+#endif
- [Tooltip(
- "If set to true, the gizmos for the focus distance (green) and eye separation (blue) will be shown."
- )]
- public bool showGizmos = true;
+ private ShaderHandles shaderHandles;
+ private G3DShaderParameters shaderParameters;
- [Tooltip("Scales the gizmos. Affectd by scene scale factor.")]
- [Range(0.005f, 1.0f)]
- public float gizmoSize = 0.2f;
+ private Vector2Int cachedWindowPosition;
+ private Vector2Int cachedWindowSize;
- public bool invertViewsInDiorama = false;
+ ///
+ /// This value is calculated based on the configuration file
+ ///
+ private float displayFOV = 16.0f;
- [Tooltip(
- "Where the views start to yoyo in the index map. Index map contains the order of views."
- )]
- [Range(0.0f, 1.0f)]
- public float indexMapYoyoStart = 0.0f;
+ private bool showTestFrame = false;
- [Tooltip("Inverts the entire index map. Index map contains the order of views.")]
- public bool invertIndexMap = false;
+ private float scaledViewSeparation
+ {
+ get { return viewSeparation * viewOffsetScale; }
+ }
- [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")]
- public bool invertIndexMapIndices = false;
+ private float focusDistWithDollyZoom
+ {
+ get { return focusDistance * dollyZoom; }
+ }
- #endregion
+ #endregion
- #region Private variables
- private PreviousValues previousValues;
- private IndexMap indexMap = IndexMap.Instance;
- private LibInterface libInterface;
- private string calibrationPath;
+ private RenderTexture[] colorRenderTextures = null;
+ private int mainCamCullingMask = -1;
+ private CameraClearFlags originalMainClearFlags;
- // distance between the two cameras for diorama mode (in meters). DO NOT USE FOR MULTIVIEW MODE!
- private float viewSeparation = 0.065f;
+#if G3D_HDRP
+ private G3D.RenderPipeline.HDRP.CustomPassController customPassController;
+#endif
- ///
- /// The distance between the camera and the focus plane in meters. Default is 70 cm.
- /// Is read from calibration file at startup
- ///
- private float focusDistance = 0.7f;
+ private bool mainCamInactiveLastFrame = false;
- private const int MAX_CAMERAS = 16; //shaders dont have dynamic arrays and this is the max supported. change it here? change it in the shaders as well ...
- private int internalCameraCount = 2;
- private int oldRenderResolutionScale = 100;
+ #region Initialization
- ///
- /// This struct is used to store the current head position.
- /// It is updated in a different thread, so always use getHeadPosition() to get the current head position.
- /// NEVER use headPosition directly.
- ///
- private HeadPosition headPosition;
- private HeadPosition filteredHeadPosition;
+ void Awake()
+ {
+ InitMainCamera();
+ }
- private static object headPosLock = new object();
- private static object shaderLock = new object();
+ void Start()
+ {
+ oldRenderResolutionScale = renderResolutionScale;
+ setupCameras();
- private Camera mainCamera;
- private List cameras = null;
- private GameObject focusPlaneObject = null;
- private GameObject cameraParent = null;
+ reinitializeShader();
- private Material material;
#if G3D_HDRP
- private G3DHDRPCustomPass customPass;
- private HDAdditionalCameraData.AntialiasingMode antialiasingMode = HDAdditionalCameraData
- .AntialiasingMode
- .None;
+ customPassController =
+ gameObject.AddComponent();
+ customPassController.init(ref material);
#endif
+
#if G3D_URP
- private G3DUrpScriptableRenderPass customPass;
- private AntialiasingMode antialiasingMode = AntialiasingMode.None;
+ customPass = new G3D.RenderPipeline.URP.ScriptableRP(material);
+ antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing;
+ mainCamera.GetUniversalAdditionalCameraData().antialiasing = UnityEngine
+ .Rendering
+ .Universal
+ .AntialiasingMode
+ .None;
#endif
- private ShaderHandles shaderHandles;
- private G3DShaderParameters shaderParameters;
-
- private Vector2Int cachedWindowPosition;
- private Vector2Int cachedWindowSize;
-
- // half of the width of field of view at start at focus distance
- private float halfCameraWidthAtStart = 1.0f;
+ shaderHandles = new ShaderHandles();
+ shaderHandles.init();
- private Queue headPositionLog;
+ headtrackingConnection = new HeadtrackingConnection(
+ focusDistance,
+ headTrackingSensitivity,
+ configurationPathOverwrite,
+ this,
+ debugMessages,
+ headPositionFilter,
+ latencyCorrectionMode
+ );
+ if (mode == G3DCameraMode.HEADTRACKING)
+ {
+ headtrackingConnection.initLibrary();
+ headtrackingConnection.startHeadTracking();
+ }
- private HeadtrackingHandler headtrackingHandler;
+ updateScreenViewportProperties();
- ///
- /// This value is calculated based on the calibration file
- ///
- private float baseFieldOfView = 16.0f;
+ loadShaderParametersFromConfigurationFile();
+ updateShaderParameters();
- private bool showTestFrame = false;
+ updateCameras();
+ updateRenderTextures();
- ///
- /// Focus distance scaled by scene scale factor.
- ///
- public float scaledFocusDistance
- {
- get { return focusDistance * sceneScaleFactor; }
- }
+ // This has to be done after the cameras are updated
+ cachedWindowPosition = new Vector2Int(
+ Screen.mainWindowPosition.x,
+ Screen.mainWindowPosition.y
+ );
+ cachedWindowSize = new Vector2Int(Screen.width, Screen.height);
- public float scaledFocusDistanceAndDolly
- {
- get
- {
- float dollyZoomFactor = scaledFocusDistance - scaledFocusDistance * dollyZoom;
- float focusDistanceWithDollyZoom = scaledFocusDistance - dollyZoomFactor;
- return focusDistanceWithDollyZoom;
+ indexMap.UpdateIndexMap(
+ getCameraCountFromConfigurationFile(),
+ internalCameraCount,
+ indexMapYoyoStart / 100.0f,
+ invertIndexMap,
+ invertIndexMapIndices
+ );
}
- }
-
- private float scaledViewSeparation
- {
- get { return viewSeparation * sceneScaleFactor * viewOffsetScale; }
- }
-
- private float scaledHalfCameraWidthAtStart
- {
- get { return halfCameraWidthAtStart * sceneScaleFactor; }
- }
-
- #endregion
- // TODO Handle viewport resizing/ moving
-
- #region Initialization
- void Start()
- {
- previousValues.init();
-
- calibrationPath = System.Environment.GetFolderPath(
- Environment.SpecialFolder.CommonDocuments
- );
- calibrationPath = Path.Combine(calibrationPath, "3D Global", "calibrations");
- if (!string.IsNullOrEmpty(calibrationPathOverwrite))
+ void OnApplicationQuit()
{
- calibrationPath = calibrationPathOverwrite;
+ headtrackingConnection.deinitLibrary();
}
- mainCamera = GetComponent();
- oldRenderResolutionScale = renderResolutionScale;
- setupCameras();
-
- // create a focus plane object at focus distance from camera.
- // then parent the camera parent to that object
- // this way we can place the camera parent relative to the focus plane
+ private void OnEnable()
+ {
+ InitMainCamera();
- // disable rendering on main camera after other cameras have been created and settings have been copied over
- // otherwise the secondary cameras are initialized wrong
- mainCamera.cullingMask = 0; //disable rendering of the main camera
- mainCamera.clearFlags = CameraClearFlags.Color;
+ mainCamera.cullingMask = 0; //disable rendering of the main camera
+ mainCamera.clearFlags = CameraClearFlags.Color;
- shaderHandles = new ShaderHandles()
- {
- leftViewportPosition = Shader.PropertyToID("v_pos_x"),
- bottomViewportPosition = Shader.PropertyToID("v_pos_y"),
- screenWidth = Shader.PropertyToID("s_width"),
- screenHeight = Shader.PropertyToID("s_height"),
- nativeViewCount = Shader.PropertyToID("nativeViewCount"),
- angleRatioNumerator = Shader.PropertyToID("zwinkel"),
- angleRatioDenominator = Shader.PropertyToID("nwinkel"),
- leftLensOrientation = Shader.PropertyToID("isleft"),
- BGRPixelLayout = Shader.PropertyToID("isBGR"),
- mstart = Shader.PropertyToID("mstart"),
- showTestFrame = Shader.PropertyToID("test"),
- showTestStripe = Shader.PropertyToID("stest"),
- testGapWidth = Shader.PropertyToID("testgap"),
- track = Shader.PropertyToID("track"),
- hqViewCount = Shader.PropertyToID("hqview"),
- hviews1 = Shader.PropertyToID("hviews1"),
- hviews2 = Shader.PropertyToID("hviews2"),
- blur = Shader.PropertyToID("blur"),
- blackBorder = Shader.PropertyToID("bborder"),
- blackSpace = Shader.PropertyToID("bspace"),
- bls = Shader.PropertyToID("bls"),
- ble = Shader.PropertyToID("ble"),
- brs = Shader.PropertyToID("brs"),
- bre = Shader.PropertyToID("bre"),
- zCorrectionValue = Shader.PropertyToID("tvx"),
- zCompensationValue = Shader.PropertyToID("zkom"),
- };
-
- if (mode == G3DCameraMode.DIORAMA)
- {
- initLibrary();
+#if G3D_URP
+ RenderPipelineManager.beginCameraRendering += OnBeginCamera;
+#endif
}
- reinitializeShader();
- updateScreenViewportProperties();
-
- loadShaderParametersFromCalibrationFile();
- updateShaderParameters();
- if (mode == G3DCameraMode.DIORAMA)
+ public void OnDisable()
{
- try
- {
- libInterface.startHeadTracking();
- }
- catch (Exception e)
+ if (cameras != null && cameras.Count > 0)
{
- Debug.LogError("Failed to start head tracking: " + e.Message);
+ // disable all cameras when the script is disabled
+ for (int i = 0; i < MAX_CAMERAS; i++)
+ {
+ cameras[i]?.gameObject.SetActive(false);
+ }
}
- }
-#if G3D_HDRP
- // init fullscreen postprocessing for hd render pipeline
- var customPassVolume = gameObject.AddComponent();
- customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess;
- customPassVolume.isGlobal = true;
- // Make the volume invisible in the inspector
- customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave;
- customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass;
- customPass.fullscreenPassMaterial = material;
- customPass.materialPassName = "G3DFullScreen3D";
-
- antialiasingMode = mainCamera.GetComponent().antialiasing;
-#endif
+ mainCamera.cullingMask = mainCamCullingMask;
+ mainCamera.clearFlags = originalMainClearFlags;
#if G3D_URP
- customPass = new G3DUrpScriptableRenderPass(material);
- antialiasingMode = mainCamera.GetUniversalAdditionalCameraData().antialiasing;
- mainCamera.GetUniversalAdditionalCameraData().antialiasing = AntialiasingMode.None;
+ RenderPipelineManager.beginCameraRendering -= OnBeginCamera;
#endif
-
- headtrackingHandler = new HeadtrackingHandler(focusDistance);
-
- updateCameras();
- updateShaderRenderTextures();
-
- // This has to be done after the cameras are updated
- cachedWindowPosition = new Vector2Int(
- Screen.mainWindowPosition.x,
- Screen.mainWindowPosition.y
- );
- cachedWindowSize = new Vector2Int(Screen.width, Screen.height);
-
- headPositionLog = new Queue(10000);
-
- indexMap.UpdateIndexMap(
- getCameraCountFromCalibrationFile(),
- internalCameraCount,
- indexMapYoyoStart,
- invertIndexMap,
- invertIndexMapIndices
- );
- }
-
- void OnApplicationQuit()
- {
- deinitLibrary();
- }
-
- ///
- /// this variable is onle here to track changes made to the public calibration file from the editor.
- ///
-
-
- ///
- /// OnValidate gets called every time the script is changed in the editor.
- /// This is used to react to changes made to the parameters.
- ///
- void OnValidate()
- {
- if (enabled == false)
- {
- // do not run this code if the script is not enabled
- return;
}
- if (
- calibrationFile != previousValues.calibrationFile
- || previousValues.sceneScaleFactor != sceneScaleFactor
- )
+#if G3D_URP
+ private void OnBeginCamera(ScriptableRenderContext context, Camera cam)
{
- previousValues.calibrationFile = calibrationFile;
- setupCameras(true);
+ // Use the EnqueuePass method to inject a custom render pass
+ cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass);
+
+ if (mainCamera.GetUniversalAdditionalCameraData().renderPostProcessing)
+ {
+ for (int i = 0; i < MAX_CAMERAS; i++)
+ {
+ cameras[i].GetUniversalAdditionalCameraData().renderPostProcessing = true;
+ }
+ }
}
+#endif
- if (previousValues.mode != mode)
+ ///
+ /// Call this function after the mode has been changed (e.g. multiview to headtracking)
+ ///
+ public void updateMode()
{
- previousValues.mode = mode;
if (mode == G3DCameraMode.MULTIVIEW)
{
- CalibrationProvider calibration = CalibrationProvider.getFromString(
- calibrationFile.text
+ ConfigurationProvider configuration = ConfigurationProvider.getFromString(
+ configurationFile.text
);
- loadMultiviewViewSeparationFromCalibration(calibration);
+ internalCameraCount = getCameraCountFromConfigurationFile(configuration);
+ loadMultiviewViewSeparationFromConfiguration(configuration);
}
else
{
@@ -388,1221 +365,999 @@ void OnValidate()
updateCameraCountBasedOnMode();
}
- if (
- previousValues.indexMapYoyoStart != indexMapYoyoStart
- || previousValues.invertIndexMap != invertIndexMap
- || previousValues.invertIndexMapIndices != invertIndexMapIndices
- )
+ public void updateIndexMap()
{
- previousValues.indexMapYoyoStart = indexMapYoyoStart;
- previousValues.invertIndexMap = invertIndexMap;
- previousValues.invertIndexMapIndices = invertIndexMapIndices;
-
indexMap.UpdateIndexMap(
- getCameraCountFromCalibrationFile(),
+ getCameraCountFromConfigurationFile(),
internalCameraCount,
- indexMapYoyoStart,
+ indexMapYoyoStart / 100.0f,
invertIndexMap,
invertIndexMapIndices
);
}
- }
- ///
- /// Us this to run OnValidate after parameters changed from a script.
- ///
- public void Validate()
- {
- OnValidate();
- }
-
- public void loadShaderParametersFromCalibrationFile()
- {
- if (calibrationFile == null)
+ public string indexMapToString()
{
- Debug.LogError(
- "No calibration file set. Please set a calibration file. Using default values."
- );
- return;
+ return indexMap.currentMapToString();
}
- lock (shaderLock)
+ ///
+ /// Load shader parameters from current configuration file.
+ ///
+ public void loadShaderParametersFromConfigurationFile()
{
- CalibrationProvider calibrationProvider = CalibrationProvider.getFromString(
- calibrationFile.text
- );
- shaderParameters = calibrationProvider.getShaderParameters();
- }
- }
+ if (configurationFile == null)
+ {
+ Debug.LogError(
+ "No configuration file set. Please set a configuration file. Using default values."
+ );
+ return;
+ }
- private void loadMultiviewViewSeparationFromCalibration(CalibrationProvider calibration)
- {
- if (mode != G3DCameraMode.MULTIVIEW)
- {
- return;
+ lock (shaderLock)
+ {
+ ConfigurationProvider configurationProvider = ConfigurationProvider.getFromString(
+ configurationFile.text
+ );
+ shaderParameters = configurationProvider.getShaderParameters();
+ }
}
- int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM");
- int NativeViewcount = calibration.getInt("NativeViewcount");
- float ApertureAngle = 14.0f;
- try
- {
- ApertureAngle = calibration.getFloat("ApertureAngle");
- }
- catch (Exception e)
+ ///
+ /// Updates all camera parameters based on the configuration file (i.e. focus distance, fov, etc.).
+ /// Includes shader parameters (i.e. lense shear angle, camera count, etc.).
+ ///
+ /// Updates configuration file as well.
+ ///
+ public void setupCameras(TextAsset configurationFile)
{
- Debug.LogWarning(e.Message);
+ this.configurationFile = configurationFile;
+ setupCameras();
}
- float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f;
- float halfZoneOpeningAngleRad = ApertureAngle * Mathf.Deg2Rad / 2.0f;
- float halfWidthZoneAtbasicDistance =
- Mathf.Tan(halfZoneOpeningAngleRad) * BasicWorkingDistanceMeter;
-
- // calculate eye separation/ view separation
- if (mode == G3DCameraMode.MULTIVIEW)
+ ///
+ /// Sets up all camera parameters based on the configuration file (i.e. focus distance, fov, etc.).
+ /// Includes shader parameters (i.e. lense shear angle, camera count, etc.).
+ ///
+ /// Does not update configuration file.
+ ///
+ public void setupCameras(bool updateFocusDist = false)
{
- viewSeparation = halfWidthZoneAtbasicDistance * 2 / NativeViewcount;
- }
- }
+ if (mainCamera == null)
+ {
+ InitMainCamera();
+ }
+ if (Application.isPlaying)
+ {
+ // only run this code if not in editor mode
+ initCamerasAndParents();
+ }
+ if (configurationFile == null)
+ {
+ Debug.LogError(
+ "No configuration file set. Please set a configuration file. Using default values."
+ );
+ if (updateFocusDist)
+ {
+ updateFocusDistance(0.7f);
+ }
+ viewSeparation = 0.065f;
+ headtrackingConnection?.setBasicWorkingDistance(focusDistance);
- ///
- /// Updates all camera parameters based on the calibration file (i.e. focus distance, fov, etc.).
- /// Includes shader parameters (i.e. lense shear angle, camera count, etc.).
- ///
- /// Updates calibration file as well.
- ///
- public void setupCameras(TextAsset calibrationFile, bool calledFromValidate = false)
- {
- this.calibrationFile = calibrationFile;
- setupCameras(calledFromValidate);
- }
+ if (mode == G3DCameraMode.MULTIVIEW)
+ {
+ viewSeparation = 0.031f;
+ }
+ return;
+ }
- ///
- /// Sets up all camera parameters based on the calibration file (i.e. focus distance, fov, etc.).
- /// Includes shader parameters (i.e. lense shear angle, camera count, etc.).
- ///
- /// Does not update calibration file.
- ///
- public void setupCameras(bool calledFromValidate = false)
- {
- if (mainCamera == null)
- {
- mainCamera = GetComponent();
- }
- if (Application.isPlaying && !calledFromValidate)
- {
- // only run this code if not in editor mode (this function (setupCameras()) is called from OnValidate as well -> from editor ui)
- initCamerasAndParents();
- }
- if (calibrationFile == null)
- {
- Debug.LogError(
- "No calibration file set. Please set a calibration file. Using default values."
+ // load values from configuration file
+ ConfigurationProvider configuration = ConfigurationProvider.getFromString(
+ configurationFile.text
);
- mainCamera.fieldOfView = 16.0f;
- focusDistance = 0.7f;
- viewSeparation = 0.065f;
+ int BasicWorkingDistanceMM = configuration.getInt("BasicWorkingDistanceMM");
+ float PhysicalSizeInch = configuration.getFloat("PhysicalSizeInch");
+ int NativeViewcount = configuration.getInt("NativeViewcount");
+ int HorizontalResolution = configuration.getInt("HorizontalResolution");
+ int VerticalResolution = configuration.getInt("VerticalResolution");
+
+ // calculate intermediate values
+ float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f;
+ float physicalSizeInMeter = PhysicalSizeInch * 0.0254f;
+ float aspectRatio = (float)HorizontalResolution / (float)VerticalResolution;
+ float FOV =
+ 2
+ * Mathf.Atan(physicalSizeInMeter / 2.0f / BasicWorkingDistanceMeter)
+ * Mathf.Rad2Deg;
+ // set camera fov
+ displayFOV = Camera.HorizontalToVerticalFieldOfView(FOV, aspectRatio);
+
+ // set focus distance
+ if (updateFocusDist)
+ {
+ updateFocusDistance(BasicWorkingDistanceMeter);
+ }
+ headtrackingConnection?.setBasicWorkingDistance(BasicWorkingDistanceMeter);
+
+ // calculate eye separation/ view separation
if (mode == G3DCameraMode.MULTIVIEW)
{
- viewSeparation = 0.031f;
+ loadMultiviewViewSeparationFromConfiguration(configuration);
+ internalCameraCount = NativeViewcount;
}
- if (focusPlaneObject != null)
+ else
{
- focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance);
+ viewSeparation = 0.065f;
}
- return;
- }
- // load values from calibration file
- CalibrationProvider calibration = CalibrationProvider.getFromString(calibrationFile.text);
- int BasicWorkingDistanceMM = calibration.getInt("BasicWorkingDistanceMM");
- float PhysicalSizeInch = calibration.getFloat("PhysicalSizeInch");
- int NativeViewcount = calibration.getInt("NativeViewcount");
- int HorizontalResolution = calibration.getInt("HorizontalResolution");
- int VerticalResolution = calibration.getInt("VerticalResolution");
-
- // calculate intermediate values
- float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f;
- float physicalSizeInMeter = PhysicalSizeInch * 0.0254f;
- float aspectRatio = (float)HorizontalResolution / (float)VerticalResolution;
- float FOV =
- 2 * Mathf.Atan(physicalSizeInMeter / 2.0f / BasicWorkingDistanceMeter) * Mathf.Rad2Deg;
-
- // set focus distance
- focusDistance = (float)BasicWorkingDistanceMeter;
-
- // set camera fov
- baseFieldOfView = Camera.HorizontalToVerticalFieldOfView(FOV, aspectRatio);
- mainCamera.fieldOfView = baseFieldOfView;
- // calculate eye separation/ view separation
- if (mode == G3DCameraMode.MULTIVIEW)
- {
- loadMultiviewViewSeparationFromCalibration(calibration);
+ updateCameraCountBasedOnMode();
+
+ loadShaderParametersFromConfigurationFile();
}
- else
+
+ public void updateRenderTextures()
{
- viewSeparation = 0.065f;
- }
+ if (material == null)
+ return;
+ if (cameras == null)
+ return;
- updateCameraCountBasedOnMode();
+ //prevent any memory leaks
+ for (int i = 0; i < MAX_CAMERAS; i++)
+ cameras[i].targetTexture?.Release();
- // only run this code if not in editor mode (this function (setupCameras()) is called from OnValidate as well -> from editor ui)
- if (Application.isPlaying)
- {
- // update focus plane distance
- if (focusPlaneObject != null)
+ for (int i = 0; i < colorRenderTextures?.Length; i++)
{
- focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance);
+ if (colorRenderTextures[i] != null)
+ colorRenderTextures[i].Release();
}
- if (cameraParent != null)
+
+ colorRenderTextures = new RenderTexture[internalCameraCount];
+
+ //set only those we need
+ for (int i = 0; i < internalCameraCount; i++)
{
- cameraParent.transform.localPosition = new Vector3(0, 0, -scaledFocusDistance);
+ addRenderTextureToCamera(colorRenderTextures, i, i);
}
}
- halfCameraWidthAtStart =
- Mathf.Tan(mainCamera.fieldOfView * Mathf.Deg2Rad / 2) * focusDistance;
-
- loadShaderParametersFromCalibrationFile();
- }
-
- ///
- /// Initializes the cameras and their parents.
- /// if cameras are already initialized, this function only returns the focus plane.
- ///
- ///
- /// The focus plane game object.
- ///
- private void initCamerasAndParents()
- {
- if (focusPlaneObject == null)
+ public void logCameraPositionsToFile()
{
- focusPlaneObject = new GameObject("focus plane center");
- focusPlaneObject.transform.parent = transform;
- focusPlaneObject.transform.localPosition = new Vector3(0, 0, scaledFocusDistance);
- focusPlaneObject.transform.localRotation = Quaternion.identity;
+ headtrackingConnection.logCameraPositionsToFile();
}
- //initialize cameras
- if (cameraParent == null)
+ ///
+ /// Shifts views to the left. This does not shift the cameras. This shifts the views you see on the display.
+ /// Only works in headtracking mode, in multiview use viewOffset.
+ ///
+ public void shiftViewToLeft()
{
- cameraParent = new GameObject("g3dcams");
- cameraParent.transform.parent = focusPlaneObject.transform;
- cameraParent.transform.localPosition = new Vector3(0, 0, -scaledFocusDistance);
- cameraParent.transform.localRotation = Quaternion.identity;
+ if (mode == G3DCameraMode.MULTIVIEW)
+ {
+ return;
+ }
+ headtrackingConnection.shiftViewToLeft();
}
- if (cameras == null)
+ ///
+ /// Shifts views to the right. This does not shift the cameras. This shifts the views you see on the display.
+ /// Only works in headtracking mode, in multiview use viewOffset.
+ ///
+ public void shiftViewToRight()
{
- cameras = new List();
- for (int i = 0; i < MAX_CAMERAS; i++)
+ if (mode == G3DCameraMode.MULTIVIEW)
{
- cameras.Add(new GameObject(CAMERA_NAME_PREFIX + i).AddComponent());
- cameras[i].transform.SetParent(cameraParent.transform, true);
- cameras[i].gameObject.SetActive(false);
- cameras[i].transform.localRotation = Quaternion.identity;
- cameras[i].clearFlags = mainCamera.clearFlags;
- cameras[i].backgroundColor = mainCamera.backgroundColor;
- cameras[i].targetDisplay = mainCamera.targetDisplay;
-
- cameras[i].depth = mainCamera.depth - 1; // make sure the main camera renders on top of the secondary cameras
-#if G3D_HDRP
- cameras[i].gameObject.AddComponent();
-#endif
+ return;
}
+ headtrackingConnection.shiftViewToRight();
}
- }
- private int getCameraCountFromCalibrationFile()
- {
- if (calibrationFile == null)
+ public void toggleTestFrame()
{
- Debug.LogError(
- "No calibration file set. Please set a calibration file. Using default values."
- );
- return 2;
+ showTestFrame = !showTestFrame;
}
- // TODO do not recreate the calibration provider every time
- // This gets called every frame in UpdateCameraCountBasedOnMode
- CalibrationProvider calibration = CalibrationProvider.getFromString(calibrationFile.text);
- return getCameraCountFromCalibrationFile(calibration);
- }
-
- private int getCameraCountFromCalibrationFile(CalibrationProvider calibration)
- {
- int NativeViewcount = calibration.getInt("NativeViewcount");
- return NativeViewcount;
- }
-
- private Vector2Int getDisplayResolutionFromCalibrationFile()
- {
- if (calibrationFile == null)
+ public void toggleHeadTracking()
{
- Debug.LogError(
- "No calibration file set. Please set a calibration file. Using default values."
- );
- return new Vector2Int(1920, 1080);
+ if (mode == G3DCameraMode.MULTIVIEW)
+ {
+ return;
+ }
+ headtrackingConnection.toggleHeadTracking();
}
- CalibrationProvider calibration = CalibrationProvider.getFromString(calibrationFile.text);
- int HorizontalResolution = calibration.getInt("HorizontalResolution");
- int VerticalResolution = calibration.getInt("VerticalResolution");
- return new Vector2Int(HorizontalResolution, VerticalResolution);
- }
-
- private void initLibrary()
- {
- string applicationName = Application.productName;
- if (string.IsNullOrEmpty(applicationName))
+ public G3DShaderParameters GetShaderParameters()
{
- applicationName = "Unity";
+ lock (shaderLock)
+ {
+ return shaderParameters;
+ }
}
- var invalids = System.IO.Path.GetInvalidFileNameChars();
- applicationName = String
- .Join("_", applicationName.Split(invalids, StringSplitOptions.RemoveEmptyEntries))
- .TrimEnd('.');
- applicationName = applicationName + "_G3D_Config.ini";
- try
+ public void setShaderParameters(G3DShaderParameters parameters)
{
- bool useHimaxD2XXDevices = true;
- bool useHimaxRP2040Devices = true;
- bool usePmdFlexxDevices = true;
-
- libInterface = LibInterface.Instance;
- libInterface.init(
- calibrationPath,
- Application.persistentDataPath,
- applicationName,
- this,
- this,
- this,
- debugMessages,
- useHimaxD2XXDevices,
- useHimaxRP2040Devices,
- usePmdFlexxDevices
- );
+ lock (shaderLock)
+ {
+ shaderParameters = parameters;
+ }
}
- catch (Exception e)
+
+ ///
+ /// Set the cameras FOV to the fov calculated from the configuration file.
+ /// Sets the filed of view to the natural field of view the display actually covers in your field of vision if you sit at the recommended distance.
+ ///
+ public void setCameraFOVToDisplayFOV()
{
- Debug.LogError("Failed to initialize library: " + e.Message);
- return;
+ // set to display FOV from configuration file
+ mainCamera.fieldOfView = displayFOV;
}
- // set initial values
- // intialize head position at focus distance from focus plane
- headPosition = new HeadPosition
- {
- headDetected = false,
- imagePosIsValid = false,
- imagePosX = 0,
- imagePosY = 0,
- worldPosX = 0.0,
- worldPosY = 0.0,
- worldPosZ = -focusDistance
- };
- filteredHeadPosition = new HeadPosition
+ ///
+ /// Set the focus distance to the native focus distance of the dispaly. Native focus distance is the distance the viewer has to be from the display for the 3d effect to look best.
+ ///
+ public void setFocusDistanceToDisplay()
{
- headDetected = false,
- imagePosIsValid = false,
- imagePosX = 0,
- imagePosY = 0,
- worldPosX = 0.0,
- worldPosY = 0.0,
- worldPosZ = -focusDistance
- };
-
- if (usePositionFiltering())
- {
- try
+ if (configurationFile == null)
{
- libInterface.initializePositionFilter(
- headPositionFilter.x,
- headPositionFilter.y,
- headPositionFilter.z
+ Debug.LogError(
+ "No configuration file set. Please set a configuration file. Using default values."
);
+ updateFocusDistance(0.7f);
+ return;
}
- catch (Exception e)
- {
- Debug.LogError("Failed to initialize position filter: " + e.Message);
- }
- }
- }
- private void deinitLibrary()
- {
- if (libInterface == null || !libInterface.isInitialized())
- {
- return;
+ ConfigurationProvider configuration = ConfigurationProvider.getFromString(
+ configurationFile.text
+ );
+ int BasicWorkingDistanceMM = configuration.getInt("BasicWorkingDistanceMM");
+ float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f;
+ updateFocusDistance(BasicWorkingDistanceMeter);
}
- try
+ ///
+ /// If no value or NAN is passed the focus distance will not be updated, but the focus plane object position will be updated
+ ///
+ ///
+ public void updateFocusDistance(float newFocusDistance = float.NaN)
{
- libInterface.stopHeadTracking();
- libInterface.unregisterHeadPositionChangedCallback(this);
- libInterface.unregisterShaderParametersChangedCallback(this);
- libInterface.unregisterMessageCallback(this);
- libInterface.deinit();
- }
- catch (Exception e)
- {
- Debug.Log(e);
+ if (!float.IsNaN(newFocusDistance))
+ {
+ focusDistance = newFocusDistance;
+ }
+ // only run this code if not in editor mode
+ if (Application.isPlaying)
+ {
+ // update focus plane distance
+ if (focusPlaneObject != null)
+ {
+ focusPlaneObject.transform.localPosition = new Vector3(0, 0, focusDistance);
+ }
+ if (cameraParent != null)
+ {
+ cameraParent.transform.localPosition = new Vector3(
+ 0,
+ 0,
+ -focusDistWithDollyZoom
+ );
+ }
+ }
}
- }
- private void reinitializeShader()
- {
- if (mode == G3DCameraMode.MULTIVIEW)
+ private void InitMainCamera()
{
- material = new Material(Shader.Find("G3D/AutostereoMultiview"));
- }
- else
- {
- material = new Material(Shader.Find("G3D/Autostereo"));
+ if (mainCamera != null)
+ {
+ return;
+ }
+ mainCamera = GetComponent();
+ mainCamCullingMask = mainCamera.cullingMask;
+ originalMainClearFlags = mainCamera.clearFlags;
}
- }
-#if G3D_URP
- private void OnEnable()
- {
- RenderPipelineManager.beginCameraRendering += OnBeginCamera;
- }
+ private void loadMultiviewViewSeparationFromConfiguration(
+ ConfigurationProvider configuration
+ )
+ {
+ if (mode != G3DCameraMode.MULTIVIEW)
+ {
+ return;
+ }
- private void OnDisable()
- {
- RenderPipelineManager.beginCameraRendering -= OnBeginCamera;
- }
+ int BasicWorkingDistanceMM = configuration.getInt("BasicWorkingDistanceMM");
+ int NativeViewcount = configuration.getInt("NativeViewcount");
+ float ApertureAngle = 14.0f;
+ try
+ {
+ ApertureAngle = configuration.getFloat("ApertureAngle");
+ }
+ catch (Exception e)
+ {
+ Debug.LogWarning(e.Message);
+ }
- private void OnBeginCamera(ScriptableRenderContext context, Camera cam)
- {
- // Use the EnqueuePass method to inject a custom render pass
- cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass);
+ float BasicWorkingDistanceMeter = BasicWorkingDistanceMM / 1000.0f;
+ float halfZoneOpeningAngleRad = ApertureAngle * Mathf.Deg2Rad / 2.0f;
+ float halfWidthZoneAtbasicDistance =
+ Mathf.Tan(halfZoneOpeningAngleRad) * BasicWorkingDistanceMeter;
- if (mainCamera.GetUniversalAdditionalCameraData().renderPostProcessing)
- {
- for (int i = 0; i < MAX_CAMERAS; i++)
+ // calculate eye separation/ view separation
+ if (mode == G3DCameraMode.MULTIVIEW)
{
- cameras[i].GetUniversalAdditionalCameraData().renderPostProcessing = true;
+ viewSeparation = halfWidthZoneAtbasicDistance * 2 / NativeViewcount;
}
}
- }
-#endif
- #endregion
-
- #region Updates
- void Update()
- {
- // update the shader parameters (only in diorama mode)
- if (mode == G3DCameraMode.DIORAMA)
+ ///
+ /// Initializes the cameras and their parents.
+ /// if cameras are already initialized, this function only returns the focus plane.
+ ///
+ ///
+ /// The focus plane game object.
+ ///
+ private void initCamerasAndParents()
{
- libInterface.calculateShaderParameters(latencyCorrectionMode);
- lock (shaderLock)
+ if (focusPlaneObject == null)
+ {
+ focusPlaneObject = new GameObject("focus plane center");
+ focusPlaneObject.transform.parent = transform;
+ focusPlaneObject.transform.localPosition = new Vector3(0, 0, focusDistance);
+ focusPlaneObject.transform.localRotation = Quaternion.identity;
+ }
+
+ //initialize cameras
+ if (cameraParent == null)
{
- shaderParameters = libInterface.getCurrentShaderParameters();
+ cameraParent = new GameObject("g3dcams");
+ cameraParent.transform.parent = focusPlaneObject.transform;
+ cameraParent.transform.localPosition = new Vector3(0, 0, -focusDistance);
+ cameraParent.transform.localRotation = Quaternion.identity;
+ }
+
+ if (cameras == null)
+ {
+ cameras = new List();
+ for (int i = 0; i < MAX_CAMERAS; i++)
+ {
+ cameras.Add(new GameObject(CAMERA_NAME_PREFIX + i).AddComponent());
+
+ copyCameraSettings(mainCamera, cameras[i]);
+
+ cameras[i].transform.SetParent(cameraParent.transform, true);
+ cameras[i].gameObject.SetActive(false);
+ cameras[i].transform.localRotation = Quaternion.identity;
+ }
}
}
- bool cameraCountChanged = updateCameraCountBasedOnMode();
- updateCameras();
- updateShaderParameters();
+ private int getCameraCountFromConfigurationFile()
+ {
+ if (configurationFile == null)
+ {
+ Debug.LogError(
+ "No configuration file set. Please set a configuration file. Using default values."
+ );
+ return 2;
+ }
+
+ // TODO do not recreate the configuration provider every time
+ // This gets called every frame in UpdateCameraCountBasedOnMode
+ ConfigurationProvider configuration = ConfigurationProvider.getFromString(
+ configurationFile.text
+ );
+ return getCameraCountFromConfigurationFile(configuration);
+ }
- bool screenSizeChanged = false;
- if (windowResized() || windowMoved())
+ private int getCameraCountFromConfigurationFile(ConfigurationProvider configuration)
{
- updateScreenViewportProperties();
- screenSizeChanged = true;
+ int NativeViewcount = configuration.getInt("NativeViewcount");
+ return NativeViewcount;
}
- if (
- screenSizeChanged
- || cameraCountChanged
- || oldRenderResolutionScale != renderResolutionScale
- )
+ private Vector2Int getDisplayResolutionFromConfigurationFile()
{
- oldRenderResolutionScale = renderResolutionScale;
- updateShaderRenderTextures();
+ if (configurationFile == null)
+ {
+ Debug.LogError(
+ "No configuration file set. Please set a configuration file. Using default values."
+ );
+ return new Vector2Int(1920, 1080);
+ }
+
+ ConfigurationProvider configuration = ConfigurationProvider.getFromString(
+ configurationFile.text
+ );
+ int HorizontalResolution = configuration.getInt("HorizontalResolution");
+ int VerticalResolution = configuration.getInt("VerticalResolution");
+ return new Vector2Int(HorizontalResolution, VerticalResolution);
}
- }
- private void updateScreenViewportProperties()
- {
- Vector2Int displayResolution = getDisplayResolutionFromCalibrationFile();
- if (mode == G3DCameraMode.MULTIVIEW)
+ private void reinitializeShader()
{
- shaderParameters.screenWidth = displayResolution.x;
- shaderParameters.screenHeight = displayResolution.y;
- shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x;
- shaderParameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height;
+ if (mode == G3DCameraMode.MULTIVIEW)
+ {
+ material = new Material(Shader.Find("G3D/AutostereoMultiview"));
+ }
+ else
+ {
+ material = new Material(Shader.Find("G3D/Autostereo"));
+ }
}
- else
+ #endregion
+
+ #region Updates
+ void Update()
{
- try
+ bool windowMovedLastFrame = windowMoved();
+ bool windowResizedLastFrame = windowResized();
+
+ if (mainCamera.enabled == false)
{
- // This is the size of the entire monitor screen
- libInterface.setScreenSize(displayResolution.x, displayResolution.y);
+ // enable all cameras if main camera is disabled
+ for (int i = 0; i < internalCameraCount; i++)
+ {
+ cameras[i].gameObject.SetActive(false);
+ }
+ mainCamInactiveLastFrame = true;
+ return;
+ }
- // this refers to the window in which the 3D effect is rendered (including eg windows top window menu)
- libInterface.setWindowSize(Screen.width, Screen.height);
- libInterface.setWindowPosition(
- Screen.mainWindowPosition.x,
- Screen.mainWindowPosition.y
- );
+ bool recreatedRenderTextures = false;
- // This refers to the actual viewport in which the 3D effect is rendered
- libInterface.setViewportSize(Screen.width, Screen.height);
- libInterface.setViewportOffset(0, 0);
+ if (mainCamInactiveLastFrame)
+ {
+ // recreate shader render textures when main camera was inactive last frame
+ recreatedRenderTextures = true;
}
- catch (Exception e)
+
+ mainCamInactiveLastFrame = false;
+
+ // update the shader parameters (only in headtracking mode)
+ if (mode == G3DCameraMode.HEADTRACKING)
{
- Debug.LogError("Failed to update screen viewport properties: " + e.Message);
+ headtrackingConnection.calculateShaderParameters();
}
- }
- // this parameter is used in the shader to invert the y axis
- material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height);
- }
+ bool cameraCountChanged = updateCameraCountBasedOnMode();
+ updateCameras();
+ updateShaderParameters();
- private void updateShaderParameters()
- {
- lock (shaderLock)
- {
- material?.SetInt(
- shaderHandles.leftViewportPosition,
- shaderParameters.leftViewportPosition
- );
- material?.SetInt(
- shaderHandles.bottomViewportPosition,
- shaderParameters.bottomViewportPosition
- );
- material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth);
- material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight);
- material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount);
- material?.SetInt(
- shaderHandles.angleRatioNumerator,
- shaderParameters.angleRatioNumerator
- );
- material?.SetInt(
- shaderHandles.angleRatioDenominator,
- shaderParameters.angleRatioDenominator
- );
- material?.SetInt(
- shaderHandles.leftLensOrientation,
- shaderParameters.leftLensOrientation
- );
- material?.SetInt(shaderHandles.mstart, shaderParameters.mstart);
-
- // test frame and stripe
- material?.SetInt(shaderHandles.showTestFrame, showTestFrame ? 1 : 0);
- material?.SetInt(shaderHandles.showTestStripe, shaderParameters.showTestStripe);
-
- material?.SetInt(shaderHandles.testGapWidth, shaderParameters.testGapWidth);
- material?.SetInt(shaderHandles.track, shaderParameters.track);
- material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount);
- material?.SetInt(shaderHandles.hviews1, shaderParameters.hviews1);
- material?.SetInt(shaderHandles.hviews2, shaderParameters.hviews2);
- material?.SetInt(shaderHandles.blur, shaderParameters.blur);
- material?.SetInt(shaderHandles.blackBorder, shaderParameters.blackBorder);
- material?.SetInt(shaderHandles.blackSpace, shaderParameters.blackSpace);
- material?.SetInt(shaderHandles.bls, shaderParameters.bls);
- material?.SetInt(shaderHandles.ble, shaderParameters.ble);
- material?.SetInt(shaderHandles.brs, shaderParameters.brs);
- material?.SetInt(shaderHandles.bre, shaderParameters.bre);
- material?.SetInt(shaderHandles.zCorrectionValue, shaderParameters.zCorrectionValue);
- material?.SetInt(shaderHandles.zCompensationValue, shaderParameters.zCompensationValue);
- material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout);
-
- material?.SetInt(Shader.PropertyToID("cameraCount"), internalCameraCount);
-
- material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0);
+ if (windowResizedLastFrame || windowMovedLastFrame)
+ {
+ updateScreenViewportProperties();
+ }
- if (mode == G3DCameraMode.MULTIVIEW)
+ if (windowResizedLastFrame)
{
- material.SetInt(Shader.PropertyToID("indexMapLength"), indexMap.currentMap.Length);
- material.SetFloatArray(
- Shader.PropertyToID("index_map"),
- indexMap.getPaddedIndexMapArray()
- );
+ recreatedRenderTextures = true;
+ }
- material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset);
+ if (cameraCountChanged || oldRenderResolutionScale != renderResolutionScale)
+ {
+ oldRenderResolutionScale = renderResolutionScale;
+ recreatedRenderTextures = true;
}
- else
+
+ if (recreatedRenderTextures)
{
- if (invertViewsInDiorama)
- {
- material.SetInt(Shader.PropertyToID("invertViews"), 1);
- }
- else
- {
- material.SetInt(Shader.PropertyToID("invertViews"), 0);
- }
+ updateRenderTextures();
}
}
- }
- void updateCameras()
- {
- Vector3 targetPosition = new Vector3(0, 0, -scaledFocusDistance);
- ; // position for the camera center (base position from which all other cameras are offset)
- float targetViewSeparation = 0.0f;
+ private void updateFocusPlane()
+ {
+ if (focusPlaneObject != null)
+ {
+ focusPlaneObject.transform.localPosition = new Vector3(0, 0, focusDistance);
+ }
+ }
- // calculate the camera center position and eye separation if head tracking and the diorama effect are enabled
- if (mode == G3DCameraMode.DIORAMA)
+ private void updateScreenViewportProperties()
{
- HeadPosition headPosition;
- if (usePositionFiltering())
+ Vector2Int displayResolution = getDisplayResolutionFromConfigurationFile();
+ if (mode == G3DCameraMode.MULTIVIEW)
{
- headPosition = getFilteredHeadPosition();
+ shaderParameters.screenWidth = displayResolution.x;
+ shaderParameters.screenHeight = displayResolution.y;
+ shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x;
+ shaderParameters.bottomViewportPosition =
+ Screen.mainWindowPosition.y + Screen.height;
}
else
{
- headPosition = getHeadPosition();
+ headtrackingConnection.updateScreenViewportProperties(displayResolution);
}
- headtrackingHandler.handleHeadTrackingState(
- ref headPosition,
- ref targetPosition,
- ref targetViewSeparation,
- scaledViewSeparation,
- scaledFocusDistance,
- cameraParent.transform.localPosition
- );
+ // this parameter is used in the shader to invert the y axis
+ material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height);
+ material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width);
}
- else if (mode == G3DCameraMode.MULTIVIEW)
+
+ private void updateShaderParameters()
{
- targetViewSeparation = scaledViewSeparation;
- }
+ lock (shaderLock)
+ {
+ material?.SetInt(
+ shaderHandles.leftViewportPosition,
+ shaderParameters.leftViewportPosition
+ );
+ material?.SetInt(
+ shaderHandles.bottomViewportPosition,
+ shaderParameters.bottomViewportPosition
+ );
+ material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth);
+ material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight);
+ material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount);
+ material?.SetInt(
+ shaderHandles.angleRatioNumerator,
+ shaderParameters.angleRatioNumerator
+ );
+ material?.SetInt(
+ shaderHandles.angleRatioDenominator,
+ shaderParameters.angleRatioDenominator
+ );
+ material?.SetInt(
+ shaderHandles.leftLensOrientation,
+ shaderParameters.leftLensOrientation
+ );
+ material?.SetInt(shaderHandles.mstart, shaderParameters.mstart);
+
+ // test frame and stripe
+ material?.SetInt(shaderHandles.showTestFrame, showTestFrame ? 1 : 0);
+ material?.SetInt(shaderHandles.showTestStripe, shaderParameters.showTestStripe);
+
+ material?.SetInt(shaderHandles.testGapWidth, shaderParameters.testGapWidth);
+ material?.SetInt(shaderHandles.track, shaderParameters.track);
+ material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount);
+ material?.SetInt(shaderHandles.hviews1, shaderParameters.hviews1);
+ material?.SetInt(shaderHandles.hviews2, shaderParameters.hviews2);
+ material?.SetInt(shaderHandles.blur, shaderParameters.blur);
+ material?.SetInt(shaderHandles.blackBorder, shaderParameters.blackBorder);
+ material?.SetInt(shaderHandles.blackSpace, shaderParameters.blackSpace);
+ material?.SetInt(shaderHandles.bls, shaderParameters.bls);
+ material?.SetInt(shaderHandles.ble, shaderParameters.ble);
+ material?.SetInt(shaderHandles.brs, shaderParameters.brs);
+ material?.SetInt(shaderHandles.bre, shaderParameters.bre);
+ material?.SetInt(shaderHandles.zCorrectionValue, shaderParameters.zCorrectionValue);
+ material?.SetInt(
+ shaderHandles.zCompensationValue,
+ shaderParameters.zCompensationValue
+ );
+ material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout);
- cameraParent.transform.localPosition = targetPosition;
+ material?.SetInt(Shader.PropertyToID("cameraCount"), internalCameraCount);
- float horizontalOffset = targetPosition.x;
- float verticalOffset = targetPosition.y;
+ material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0);
- float currentFocusDistance = -cameraParent.transform.localPosition.z;
- float dollyZoomOffset = currentFocusDistance - currentFocusDistance * dollyZoom;
+ if (mode == G3DCameraMode.MULTIVIEW)
+ {
+ material.SetInt(
+ Shader.PropertyToID("indexMapLength"),
+ indexMap.currentMap.Length
+ );
+ material.SetFloatArray(
+ Shader.PropertyToID("index_map"),
+ indexMap.getPaddedIndexMapArray()
+ );
+
+ material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset);
+ }
+ else
+ {
+ material.SetInt(
+ Shader.PropertyToID("invertViews"),
+ invertViewsInHeadtracking ? 1 : 0
+ );
+ }
+ material?.SetInt(Shader.PropertyToID("mosaic_rows"), 4);
+ material?.SetInt(Shader.PropertyToID("mosaic_columns"), 4);
+ }
+ }
- float focusDistanceWithDollyZoom = currentFocusDistance - dollyZoomOffset;
- mainCamera.fieldOfView =
- 2
- * Mathf.Atan(scaledHalfCameraWidthAtStart / focusDistanceWithDollyZoom)
- * Mathf.Rad2Deg;
+ private void updateCameras()
+ {
+ Vector3 targetPosition = new Vector3(0, 0, -focusDistWithDollyZoom); // position for the camera center (base position from which all other cameras are offset)
+ float targetViewSeparation = 0.0f;
- // set the camera parent position to the focus distance
- cameraParent.transform.localPosition = new Vector3(
- horizontalOffset,
- verticalOffset,
- -currentFocusDistance
- );
+ // calculate the camera center position and eye separation if head tracking and the headtracking effect are enabled
+ if (mode == G3DCameraMode.HEADTRACKING)
+ {
+ headtrackingConnection.handleHeadTrackingState(
+ ref targetPosition,
+ ref targetViewSeparation,
+ scaledViewSeparation,
+ focusDistWithDollyZoom
+ );
+ }
+ else if (mode == G3DCameraMode.MULTIVIEW)
+ {
+ targetViewSeparation = scaledViewSeparation;
+ }
- //calculate camera positions and matrices
- for (int i = 0; i < internalCameraCount; i++)
- {
- var camera = cameras[i];
- //copy any changes to the main camera
- camera.fieldOfView = mainCamera.fieldOfView;
- camera.farClipPlane = mainCamera.farClipPlane;
- camera.nearClipPlane = mainCamera.nearClipPlane;
- camera.projectionMatrix = mainCamera.projectionMatrix;
- camera.transform.localRotation = cameraParent.transform.localRotation;
-#if G3D_URP
- camera.GetUniversalAdditionalCameraData().antialiasing = antialiasingMode;
-#endif
-#if G3D_HDRP
- HDAdditionalCameraData hdAdditionalCameraData =
- camera.gameObject.GetComponent();
- if (hdAdditionalCameraData != null)
- hdAdditionalCameraData.antialiasing = antialiasingMode;
-#endif
+ cameraParent.transform.localPosition = targetPosition;
- float localCameraOffset = calculateCameraOffset(
- i,
- targetViewSeparation,
- internalCameraCount
- );
+ float horizontalOffset = targetPosition.x;
+ float verticalOffset = targetPosition.y;
- // apply new projection matrix
- Matrix4x4 projMatrix = calculateCameraProjectionMatrix(
- localCameraOffset,
+ float currentFocusDistance = -cameraParent.transform.localPosition.z;
+
+ // set the camera parent position to the focus distance
+ cameraParent.transform.localPosition = new Vector3(
horizontalOffset,
verticalOffset,
- focusDistanceWithDollyZoom,
- camera.projectionMatrix
+ -currentFocusDistance
);
- camera.projectionMatrix = projMatrix;
+ //calculate camera positions and matrices
+ for (int i = 0; i < internalCameraCount; i++)
+ {
+ var camera = cameras[i];
+ copyCameraSettings(mainCamera, camera, cameraParent.transform.localRotation);
+ camera.fieldOfView = calcNewFOV(currentFocusDistance);
+
+ float localCameraOffset = calculateCameraOffset(
+ i,
+ targetViewSeparation,
+ internalCameraCount
+ );
- camera.transform.localPosition = new Vector3(localCameraOffset, 0, 0);
+ // apply new projection matrix
+ camera.projectionMatrix = calculateCameraProjectionMatrix(
+ localCameraOffset + horizontalOffset,
+ verticalOffset,
+ focusDistWithDollyZoom,
+ camera.projectionMatrix
+ );
- camera.gameObject.SetActive(true);
- }
+ camera.transform.localPosition = new Vector3(localCameraOffset, 0, 0);
- //disable all the other cameras, we are not using them with this cameracount
- for (int i = internalCameraCount; i < MAX_CAMERAS; i++)
- {
- cameras[i].gameObject.SetActive(false);
- }
- }
+ // enable all cameras
+ camera.gameObject.SetActive(true);
+ }
- ///
- /// Sets the camera count to two if we are in diorama mode. Sets it to the maximum amount of views the display is capable of if we are in multiview mode.
- ///
- /// true if camera count was changed.
- private bool updateCameraCountBasedOnMode()
- {
- int previousCameraCount = internalCameraCount;
- if (mode == G3DCameraMode.DIORAMA)
- {
- internalCameraCount = 2;
- }
- else if (mode == G3DCameraMode.MULTIVIEW)
- {
- internalCameraCount = getCameraCountFromCalibrationFile();
- if (internalCameraCount > MAX_CAMERAS)
+ //disable all the other cameras, we are not using
+ for (int i = internalCameraCount; i < MAX_CAMERAS; i++)
{
- internalCameraCount = MAX_CAMERAS;
+ cameras[i].gameObject.SetActive(false);
}
}
- if (internalCameraCount != previousCameraCount)
+ ///
+ /// Sets the camera count to two if we are in headtracking mode. Sets it to the maximum amount of views the display is capable of if we are in multiview mode.
+ ///
+ /// true if camera count was changed.
+ private bool updateCameraCountBasedOnMode()
{
- return true;
- }
+ int previousCameraCount = internalCameraCount;
+ if (mode == G3DCameraMode.HEADTRACKING)
+ {
+ internalCameraCount = 2;
+ }
+ else if (mode == G3DCameraMode.MULTIVIEW)
+ {
+ internalCameraCount = getCameraCountFromConfigurationFile();
+ if (internalCameraCount > MAX_CAMERAS)
+ {
+ internalCameraCount = MAX_CAMERAS;
+ }
+ }
- return false;
- }
+ if (internalCameraCount != previousCameraCount)
+ {
+ return true;
+ }
- public void updateShaderRenderTextures()
- {
- if (material == null)
- return;
- if (cameras == null)
- return;
+ return false;
+ }
+
+ ///
+ /// copies relevant camera settings from the source camera to the destination camera.
+ /// Ensures that destination camera is setup correctly.
+ ///
+ /// The source camera from which to copy settings.
+ /// The parent transform of the destination camera.
+ /// The destination camera to which settings will be copied.
+ private void copyCameraSettings(
+ Camera source,
+ Camera destination,
+ Quaternion destLocalRotation = default(Quaternion)
+ )
+ {
+ RenderTexture currentTargetTexture = destination.targetTexture;
+ destination.CopyFrom(source);
+ destination.targetTexture = currentTargetTexture; // ensure that the target texture is not overwritten
+ destination.transform.localRotation = destLocalRotation;
+ destination.cullingMask = mainCamCullingMask;
+ destination.clearFlags = originalMainClearFlags;
+ destination.depth = source.depth - 1; // make sure the main camera renders on top of the secondary cameras
+#if G3D_HDRP
+ HDAdditionalCameraData sourceHDData = source.GetComponent();
+ HDAdditionalCameraData destinationHDData =
+ destination.GetComponent();
+ if (destinationHDData == null)
+ {
+ destinationHDData = destination.gameObject.AddComponent();
+ }
+ if (sourceHDData != null)
+ {
+ sourceHDData.CopyTo(destinationHDData);
+ }
+#endif
+#if G3D_URP
+ UniversalAdditionalCameraData sourceURPData =
+ source.GetComponent();
+ UniversalAdditionalCameraData destinationURPData =
+ destination.GetComponent();
+ if (destinationURPData == null)
+ {
+ destinationURPData =
+ destination.gameObject.AddComponent();
+ }
- //prevent any memory leaks
- for (int i = 0; i < MAX_CAMERAS; i++)
- cameras[i].targetTexture?.Release();
+ if (sourceURPData != null)
+ {
+ Helpers.copyToCameraTarget(sourceURPData, destinationURPData);
+ }
- RenderTexture[] renderTextures = new RenderTexture[internalCameraCount];
+ destination.GetUniversalAdditionalCameraData().antialiasing = antialiasingMode;
+#endif
+ }
- //set only those we need
- for (int i = 0; i < internalCameraCount; i++)
+ ///
+ /// adds rendertextres for the cameras to the shader and sets them as target textures for the cameras.
+ ///
+ ///
+ ///
+ ///
+ /// only used if view generation is turned on. used to specify the texture name (left, right, middle)
+ private void addRenderTextureToCamera(
+ RenderTexture[] renderTextures,
+ int renderTextureIndex,
+ int cameraIndex,
+ string texNameInShader = "texture"
+ )
{
int width = Screen.width;
int height = Screen.height;
- width = (int)(width * ((float)renderResolutionScale / 100f));
- height = (int)(height * ((float)renderResolutionScale / 100f));
+ width = (int)(width * (renderResolutionScale / 100f));
+ height = (int)(height * (renderResolutionScale / 100f));
- renderTextures[i] = new RenderTexture(width, height, 0)
+ renderTextures[renderTextureIndex] = new RenderTexture(width, height, 0)
{
format = RenderTextureFormat.ARGB32,
depthStencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.D16_UNorm,
};
- cameras[i].targetTexture = renderTextures[i];
- material.SetTexture("texture" + i, renderTextures[i], RenderTextureSubElement.Color);
- }
- }
-
- private bool windowResized()
- {
- var window_dim = new Vector2Int(Screen.width, Screen.height);
- if (cachedWindowSize != window_dim)
- {
- cachedWindowSize = window_dim;
- return true;
- }
- return false;
- }
-
- private bool windowMoved()
- {
- var window_pos = new Vector2Int(Screen.mainWindowPosition.x, Screen.mainWindowPosition.y);
- if (cachedWindowPosition != window_pos)
- {
- cachedWindowPosition = window_pos;
- return true;
- }
- return false;
- }
-
- ///
- ///
- ///
- /// Offset (along the x axis) of this camera compared to zero position.
- /// general offset (x axis) of the "zero position" compared to start position due to head tracking.
- /// general offset (y axis) of the "zero position" compared to start position due to head tracking.
- /// general offset (z axis) of the "zero position" compared to start position due to head tracking.
- ///
- ///
- private Matrix4x4 calculateCameraProjectionMatrix(
- float localCameraOffset,
- float horizontalOffset,
- float verticalOffset,
- float focusDistance,
- Matrix4x4 mainCamProjectionMatrix
- )
- {
- // horizontal obliqueness
- float horizontalObl = -(localCameraOffset + horizontalOffset) / focusDistance;
- float vertObl = -verticalOffset / focusDistance;
-
- // focus distance is in view space. Writing directly into projection matrix would require focus distance to be in projection space
- Matrix4x4 shearMatrix = Matrix4x4.identity;
- shearMatrix[0, 2] = horizontalObl;
- shearMatrix[1, 2] = vertObl;
- // apply new projection matrix
- return mainCamProjectionMatrix * shearMatrix;
- }
-
- // This function only does something when you use the SRP render pipeline.
- // when using either URP or HRDP image combination is handled in the respective renderpasses.
- // URP -> G3DUrpScriptableRenderPass.cs
- // HDRP -> G3DHDRPCustomPass.cs
- void OnRenderImage(RenderTexture source, RenderTexture destination)
- {
- // This is where the material and shader are applied to the camera image.
- //legacy support (no URP or HDRP)
-#if G3D_HDRP || URP
-#else
- if (material == null)
- Graphics.Blit(source, destination);
- else
- Graphics.Blit(source, destination, material);
-#endif
- }
- #endregion
-
- ///
- /// always use this method to get the current head position.
- /// NEVER access headPosition directly, as it is updated in a different thread.
- ///
- ///
- ///
- public HeadPosition getHeadPosition()
- {
- lock (headPosLock)
- {
- return headPosition;
+ cameras[cameraIndex].targetTexture = renderTextures[renderTextureIndex];
+ material.SetTexture(
+ "texture" + renderTextureIndex,
+ renderTextures[renderTextureIndex],
+ RenderTextureSubElement.Color
+ );
}
- }
- ///
- /// always use this method to get the smoothed head position.
- /// NEVER access headPosition directly, as it is updated in a different thread.
- ///
- ///
- ///
- public HeadPosition getFilteredHeadPosition()
- {
- lock (headPosLock)
+ private bool windowResized()
{
- return filteredHeadPosition;
+ var window_dim = new Vector2Int(Screen.width, Screen.height);
+ if (cachedWindowSize != window_dim)
+ {
+ cachedWindowSize = window_dim;
+ return true;
+ }
+ return false;
}
- }
- #region callback handling
- void ITNewHeadPositionCallback.NewHeadPositionCallback(
- bool headDetected,
- bool imagePosIsValid,
- int imagePosX,
- int imagePosY,
- double worldPosX,
- double worldPosY,
- double worldPosZ
- )
- {
- lock (headPosLock)
+ private bool windowMoved()
{
- string logEntry =
- DateTime.Now.ToString("HH:mm::ss.fff")
- + ";"
- + worldPosX
- + ";"
- + worldPosY
- + ";"
- + worldPosZ
- + ";"
- + headDetected
- + ";"
- + imagePosIsValid
- + ";";
-
- headPosition.headDetected = headDetected;
- headPosition.imagePosIsValid = imagePosIsValid;
-
- int millimeterToMeter = 1000;
-
- Vector3 headPos = new Vector3(
- (float)-worldPosX / millimeterToMeter,
- (float)worldPosY / millimeterToMeter,
- (float)-worldPosZ / millimeterToMeter
+ var window_pos = new Vector2Int(
+ Screen.mainWindowPosition.x,
+ Screen.mainWindowPosition.y
);
-
- int scaleFactorInt = (int)sceneScaleFactor * (int)headTrackingScale;
- float scaleFactor = sceneScaleFactor * headTrackingScale;
-
- headPosition.imagePosX = imagePosX / (int)millimeterToMeter * scaleFactorInt;
- headPosition.imagePosY = imagePosY / (int)millimeterToMeter * scaleFactorInt;
- headPosition.worldPosX = headPos.x * scaleFactor;
- headPosition.worldPosY = headPos.y * scaleFactor;
- headPosition.worldPosZ = headPos.z * sceneScaleFactor;
-
- if (usePositionFiltering())
+ if (cachedWindowPosition != window_pos)
{
- double filteredPositionX;
- double filteredPositionY;
- double filteredPositionZ;
-
- if (headDetected)
- {
- try
- {
- libInterface.applyPositionFilter(
- worldPosX,
- worldPosY,
- worldPosZ,
- out filteredPositionX,
- out filteredPositionY,
- out filteredPositionZ
- );
-
- filteredHeadPosition.worldPosX =
- -filteredPositionX / millimeterToMeter * scaleFactor;
- filteredHeadPosition.worldPosY =
- filteredPositionY / millimeterToMeter * scaleFactor;
- filteredHeadPosition.worldPosZ =
- -filteredPositionZ / millimeterToMeter * sceneScaleFactor;
- }
- catch (Exception e)
- {
- Debug.LogError("Failed to apply position filter: " + e.Message);
- }
- }
-
- filteredHeadPosition.headDetected = headDetected;
- filteredHeadPosition.imagePosIsValid = imagePosIsValid;
-
- logEntry +=
- filteredHeadPosition.worldPosX
- + ";"
- + filteredHeadPosition.worldPosY
- + ";"
- + filteredHeadPosition.worldPosZ
- + ";";
+ cachedWindowPosition = window_pos;
+ return true;
}
-
- headPositionLog.Enqueue(logEntry);
- }
- }
-
- void ITNewErrorMessageCallback.NewErrorMessageCallback(
- EMessageSeverity severity,
- string sender,
- string caption,
- string cause,
- string remedy
- )
- {
- string messageText = formatErrorMessage(caption, cause, remedy);
- switch (severity)
+ return false;
+ }
+
+ ///
+ ///
+ ///
+ /// Offset (along the x axis) of this camera compared to zero position.
+ /// general offset (x axis) of the "zero position" compared to start position due to head tracking.
+ /// general offset (y axis) of the "zero position" compared to start position due to head tracking.
+ /// general offset (z axis) of the "zero position" compared to start position due to head tracking.
+ ///
+ ///
+ private Matrix4x4 calculateCameraProjectionMatrix(
+ float horizontalOffset,
+ float verticalOffset,
+ float focusDistance,
+ Matrix4x4 mainCamProjectionMatrix
+ )
{
- case EMessageSeverity.MS_EXCEPTION:
- Debug.LogError(messageText);
- break;
- case EMessageSeverity.MS_ERROR:
- Debug.LogError(messageText);
- break;
- case EMessageSeverity.MS_WARNING:
- Debug.LogWarning(messageText);
- break;
- case EMessageSeverity.MS_INFO:
-
- Debug.Log(messageText);
- break;
- default:
- Debug.Log(messageText);
- break;
- }
- }
+ // horizontal obliqueness
+ float horizontalObl = -horizontalOffset / focusDistance;
+ float vertObl = -verticalOffset / focusDistance;
- ///
- /// The shader parameters contain everything necessary for the shader to render the 3D effect.
- /// These are updated every time a new head position is received.
- /// They do not update the head position itself.
- ///
- ///
- void ITNewShaderParametersCallback.NewShaderParametersCallback(
- G3DShaderParameters shaderParameters
- )
- {
- lock (shaderLock)
- {
- this.shaderParameters = shaderParameters;
+ // focus distance is in view space. Writing directly into projection matrix would require focus distance to be in projection space
+ Matrix4x4 shearMatrix = Matrix4x4.identity;
+ shearMatrix[0, 2] = horizontalObl;
+ shearMatrix[1, 2] = vertObl;
+ // apply new projection matrix
+ return mainCamProjectionMatrix * shearMatrix;
}
- }
- private string formatErrorMessage(string caption, string cause, string remedy)
- {
- string messageText = caption + ": " + cause;
-
- if (string.IsNullOrEmpty(remedy) == false)
+ // This function only does something when you use the SRP render pipeline.
+ // when using either URP or HRDP image combination is handled in the respective renderpasses.
+ // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs
+ // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs
+ void OnRenderImage(RenderTexture source, RenderTexture destination)
{
- messageText = messageText + "\n" + remedy;
+ // This is where the material and shader are applied to the camera image.
+ //legacy support (no URP or HDRP)
+#if G3D_HDRP || URP
+#else
+ if (material == null)
+ Graphics.Blit(source, destination);
+ else
+ Graphics.Blit(source, destination, material);
+#endif
}
+ #endregion
- return messageText;
- }
- #endregion
-
- #region Debugging
+ #region Debugging
#if UNITY_EDITOR
- void OnDrawGizmos()
- {
- if (!showGizmos)
- {
- return;
- }
-
- if (enabled == false)
+ void OnDrawGizmos()
{
- // do not run this code if the script is not enabled
- return;
- }
+ if (!showGizmos)
+ {
+ return;
+ }
- if (mainCamera == null)
- {
- mainCamera = GetComponent();
- if (mainCamera == null)
+ if (enabled == false)
{
- Debug.LogError(
- "No main camera found. Please add a camera to the G3DCamera object."
- );
+ // do not run this code if the script is not enabled
return;
}
- }
- float dollyZoomFactor = scaledFocusDistance - scaledFocusDistance * dollyZoom;
+ if (mainCamera == null)
+ {
+ InitMainCamera();
+ if (mainCamera == null)
+ {
+ Debug.LogError(
+ "No main camera found. Please add a camera to the G3DCamera object."
+ );
+ return;
+ }
+ }
- float tmpHalfCameraWidthAtStart =
- Mathf.Tan(baseFieldOfView * Mathf.Deg2Rad / 2) * scaledFocusDistance;
+ Vector3 basePosition = new Vector3(0, 0, focusDistance);
+ float dollyDifference = focusDistance - focusDistWithDollyZoom;
- float focusDistanceWithDollyZoom = scaledFocusDistance - dollyZoomFactor;
- float tmpFieldOfView =
- 2 * Mathf.Atan(tmpHalfCameraWidthAtStart / focusDistanceWithDollyZoom) * Mathf.Rad2Deg;
- float fieldOfViewWithoutDolly =
- 2 * Mathf.Atan(tmpHalfCameraWidthAtStart / scaledFocusDistance) * Mathf.Rad2Deg;
+ Vector3 position;
+ Gizmos.color = new Color(0, 0, 1, 0.75F);
+ Gizmos.matrix = transform.localToWorldMatrix;
+ // draw camera position spheres
+ for (int i = 0; i < internalCameraCount; i++)
+ {
+ float localCameraOffset = calculateCameraOffset(
+ i,
+ scaledViewSeparation,
+ internalCameraCount
+ );
+ Vector3 camPos = basePosition - new Vector3(0, 0, focusDistWithDollyZoom);
+ camPos += new Vector3(1, 0, 0) * localCameraOffset;
+ Gizmos.DrawSphere(camPos, 0.01f * gizmoSize);
+ }
- Vector3 basePosition = new Vector3(0, 0, focusDistanceWithDollyZoom);
+ // draw camera frustums
+ Gizmos.color = new Color(0, 0, 1, 1); // set color to one wo improve visibility
+ for (int i = 0; i < internalCameraCount; i++)
+ {
+ float localCameraOffset = calculateCameraOffset(
+ i,
+ scaledViewSeparation,
+ internalCameraCount
+ );
- Vector3 position;
- // draw eye separation
- Gizmos.color = new Color(0, 0, 1, 0.75F);
- Gizmos.matrix = transform.localToWorldMatrix;
- // draw camera position spheres
- for (int i = 0; i < internalCameraCount; i++)
- {
- float localCameraOffset = calculateCameraOffset(
- i,
- scaledViewSeparation,
- internalCameraCount
- );
- Vector3 camPos = basePosition - new Vector3(0, 0, focusDistanceWithDollyZoom);
- camPos += new Vector3(1, 0, 0) * localCameraOffset;
- Gizmos.DrawSphere(camPos, 0.2f * gizmoSize * sceneScaleFactor);
- }
+ position = transform.position + transform.right * localCameraOffset;
+ // apply dolly zoom to position
+ position += transform.forward * dollyDifference;
- // draw camera frustums
- Gizmos.color = new Color(0, 0, 1, 1); // set color to one wo improve visibility
- for (int i = 0; i < internalCameraCount; i++)
- {
- float localCameraOffset = calculateCameraOffset(
- i,
- scaledViewSeparation,
- internalCameraCount
- );
- position = transform.position + transform.right * localCameraOffset;
+ // apply new projection matrix
+ Matrix4x4 localProjectionMatrix = Matrix4x4.TRS(
+ position,
+ transform.rotation,
+ Vector3.one
+ );
+ Matrix4x4 projMatrix = calculateCameraProjectionMatrix(
+ localCameraOffset,
+ 0,
+ focusDistWithDollyZoom,
+ localProjectionMatrix
+ );
- // apply new projection matrix
- Matrix4x4 localProjectionMatrix = Matrix4x4.TRS(
- position,
- transform.rotation,
- Vector3.one
- );
- Matrix4x4 projMatrix = calculateCameraProjectionMatrix(
- localCameraOffset,
- 0,
- 0,
- focusDistanceWithDollyZoom,
- localProjectionMatrix
- );
+ Gizmos.matrix = projMatrix;
- Gizmos.matrix = projMatrix;
+ Gizmos.DrawFrustum(
+ Vector3.zero,
+ calcNewFOV(),
+ mainCamera.farClipPlane,
+ mainCamera.nearClipPlane,
+ mainCamera.aspect
+ );
+ }
- Gizmos.DrawFrustum(
- Vector3.zero,
- tmpFieldOfView,
- mainCamera.farClipPlane,
- mainCamera.nearClipPlane,
- mainCamera.aspect
- );
+ Gizmos.matrix = transform.localToWorldMatrix;
+ drawFocusPlane(mainCamera.fieldOfView, focusDistance);
}
- Gizmos.matrix = transform.localToWorldMatrix;
-
- // draw focus plane
- Gizmos.color = new Color(0, 0, 1, 0.25F);
- position = new Vector3(0, 0, 1) * focusDistanceWithDollyZoom;
- float frustumWidth =
- Mathf.Tan(fieldOfViewWithoutDolly * Mathf.Deg2Rad / 2) * scaledFocusDistance * 2;
- float frustumHeight =
- Mathf.Tan(
- Camera.VerticalToHorizontalFieldOfView(fieldOfViewWithoutDolly, mainCamera.aspect)
- * Mathf.Deg2Rad
- / 2
- )
- * scaledFocusDistance
- * 2;
- Gizmos.DrawCube(position, new Vector3(frustumHeight, frustumWidth, 0.001f));
- }
-#endif
- #endregion
-
- private float calculateCameraOffset(
- int currentCamera,
- float targetEyeSeparation,
- int tmpCameraCount
- )
- {
- int currentView = -tmpCameraCount / 2 + currentCamera;
- if (tmpCameraCount % 2 == 0 && currentView >= 0)
+ private void drawFocusPlane(float FOV, float focusDist)
{
- currentView += 1;
+ Gizmos.color = new Color(0, 0, 1, 0.25F);
+ Vector3 position = new Vector3(0, 0, focusDist);
+ float frustumWidth = Mathf.Tan(FOV * Mathf.Deg2Rad / 2) * focusDist * 2;
+ float frustumHeight =
+ Mathf.Tan(
+ Camera.VerticalToHorizontalFieldOfView(FOV, mainCamera.aspect)
+ * Mathf.Deg2Rad
+ / 2
+ )
+ * focusDist
+ * 2;
+ Gizmos.DrawCube(position, new Vector3(frustumHeight, frustumWidth, 0.001f));
}
- float offset = currentView * targetEyeSeparation;
-
- // when the camera count is even, one camera is placed half the eye separation to the right of the center
- // same for the other to the left
- // therefore we need to add the correction term to the offset to get the correct position
- if (tmpCameraCount % 2 == 0)
+ private float calcNewFOV()
{
- // subtract half of the eye separation to get the correct offset
- float correctionTerm = targetEyeSeparation / 2;
- if (currentView > 0)
- {
- correctionTerm *= -1;
- }
- return (offset + correctionTerm) * -1;
+ return calcNewFOV(focusDistWithDollyZoom);
}
+#endif
- int flip = mirrorViews ? 1 : -1;
-
- return offset * flip;
- }
-
- ///
- /// Returns false if all values of the position filter are set to zero.
- ///
- ///
- private bool usePositionFiltering()
- {
- return headPositionFilter.x != 0 || headPositionFilter.y != 0 || headPositionFilter.z != 0;
- }
-
- public void logCameraPositionsToFile()
- {
- System.IO.StreamWriter writer = new System.IO.StreamWriter(
- Application.dataPath + "/HeadPositionLog.csv",
- false
- );
- writer.WriteLine(
- "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z"
- );
- string[] headPoitionLogArray = headPositionLog.ToArray();
- for (int i = 0; i < headPoitionLogArray.Length; i++)
+ private float calcNewFOV(float actualFocusDistance)
{
- writer.WriteLine(headPoitionLogArray[i]);
- }
- writer.Close();
- }
+ float halfFOVRad = mainCamera.fieldOfView * Mathf.Deg2Rad / 2;
+ float a = Mathf.Tan(halfFOVRad) * focusDistance;
- public void shiftViewToLeft()
- {
- if (mode == G3DCameraMode.MULTIVIEW)
- {
- return;
+ float newHalfFOVRad = Mathf.Atan(a / actualFocusDistance);
+ return newHalfFOVRad * Mathf.Rad2Deg * 2;
}
- try
- {
- libInterface.shiftViewToLeft();
- }
- catch (Exception e)
- {
- Debug.LogError("Failed to shift view to left: " + e.Message);
- }
- }
+ #endregion
- public void shiftViewToRight()
- {
- if (mode == G3DCameraMode.MULTIVIEW)
- {
- return;
- }
- try
- {
- libInterface.shiftViewToRight();
- }
- catch (Exception e)
+ private float calculateCameraOffset(
+ int currentCamera,
+ float targetEyeSeparation,
+ int tmpCameraCount
+ )
{
- Debug.LogError("Failed to shift view to right: " + e.Message);
- }
- }
+ int currentView = -tmpCameraCount / 2 + currentCamera;
+ if (tmpCameraCount % 2 == 0 && currentView >= 0)
+ {
+ currentView += 1;
+ }
- public void toggleTestFrame()
- {
- showTestFrame = !showTestFrame;
- }
+ // it is used to scale the offset to the ends for a mosaic texture where the middle textures are missing
+ float offset = currentView * targetEyeSeparation;
- public void toggleHeadTracking()
- {
- if (mode == G3DCameraMode.MULTIVIEW)
- {
- return;
- }
- Debug.Log("Toggling head tracking status");
- if (libInterface == null || !libInterface.isInitialized())
- {
- return;
- }
-
- try
- {
- HeadTrackingStatus headtrackingHandler = libInterface.getHeadTrackingStatus();
- if (headtrackingHandler.hasTrackingDevice)
+ // when the camera count is even, one camera is placed half the eye separation to the right of the center
+ // same for the other to the left
+ // therefore we need to add the correction term to the offset to get the correct position
+ if (tmpCameraCount % 2 == 0)
{
- if (!headtrackingHandler.isTrackingActive)
+ // subtract half of the eye separation to get the correct offset
+ float correctionTerm = targetEyeSeparation / 2;
+ if (currentView > 0)
{
- libInterface.startHeadTracking();
- }
- else
- {
- libInterface.stopHeadTracking();
+ correctionTerm *= -1;
}
+ offset = offset + correctionTerm;
}
- }
- catch (Exception e)
- {
- Debug.LogError("Failed to toggle head tracking status: " + e.Message);
- }
- }
- public G3DShaderParameters GetShaderParameters()
- {
- lock (shaderLock)
- {
- return shaderParameters;
+ int flip = mirrorViews ? 1 : -1;
+
+ return offset * flip;
}
}
}
diff --git a/G3DCameraMosaicMultiview.cs b/G3DCameraMosaicMultiview.cs
index 68a6b9c..a6a1b0e 100644
--- a/G3DCameraMosaicMultiview.cs
+++ b/G3DCameraMosaicMultiview.cs
@@ -14,568 +14,601 @@
#if G3D_URP
using UnityEngine.Rendering.Universal;
#endif
-
-public enum MosaicMode
-{
- Image,
- Video,
- RenderTexture
-}
-
-///
-/// Replaces the image the camera this script is attached to sees with the rendertexture.
-/// The texture should contain a mosaic image with several views.
-///
-/// IMPORTANT: This script must not be attached to a camera already using a G3D camera script.
-///
-[RequireComponent(typeof(Camera))]
-public class G3DCameraMosaicMultiview : MonoBehaviour
+namespace G3D
{
- #region Calibration
- [Tooltip("Drop the calibration file for the display you want to use here.")]
- public TextAsset calibrationFile;
-
- [Min(1)]
- public int mosaicRowCount = 3;
-
- [Min(1)]
- public int mosaicColumnCount = 3;
-
- [Tooltip(
- "If enabled, the mosaic dimensions will be extracted from the filename. E.g. video.mosaic.3x3\nONLY WORKS FOR IMAGE AND VIDEO MODES"
- )]
- public bool dimensionsFromFilename = false;
-
- [Tooltip(
- "Does not check if the amount of HQ views specified in the calibration file fits the provided mosaic."
- )]
- public bool useHQViews = false;
-
- [Space(10)]
- [Tooltip(
- "Where the views start to yoyo in the index map. Index map contains the order of views."
- )]
- [Range(0.0f, 1.0f)]
- public float indexMapYoyoStart = 0.0f;
-
- [Tooltip("Inverts the entire index map. Index map contains the order of views.")]
- public bool invertIndexMap = false;
-
- [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")]
- public bool invertIndexMapIndices = false;
-
- [Space(10)]
- #endregion
+ public enum MosaicMode
+ {
+ Image,
+ Video,
+ RenderTexture
+ }
///
- /// Shifts the individual views to the left or right by the specified number of views.
+ /// Replaces the image the camera this script is attached to sees with the rendertexture.
+ /// The texture should contain a mosaic image with several views.
+ ///
+ /// IMPORTANT: This script must not be attached to a camera already using a G3D camera script.
///
- [Tooltip("Shifts the individual views to the left or right by the specified number of views.")]
- public int viewOffset = 0;
-
- public MosaicMode mosaicMode = MosaicMode.RenderTexture;
- public RenderTexture renderTexture;
- public Texture2D image;
-
- public VideoClip videoClip;
-
- #region 3D Effect settings
- [Header("3D Effect settings")]
- [Tooltip("If set to true, the views will be flipped horizontally.")]
- public bool mirrorViews = false;
- #endregion
-
-
- #region Private variables
- private PreviousValues previousValues = new PreviousValues();
- private IndexMap indexMap = IndexMap.Instance;
- private Camera mainCamera;
- private Material material;
+ [RequireComponent(typeof(Camera))]
+ public class G3DCameraMosaicMultiview : MonoBehaviour
+ {
+ [Tooltip("Drop the configuration file for the display you want to use here.")]
+ public TextAsset configurationFile;
+
+ [Min(1)]
+ ///
+ /// The number of rows in the mosaic.
+ ///
+ public int mosaicRowCount = 3;
+
+ [Min(1)]
+ ///
+ /// The number of columns in the mosaic.
+ ///
+ public int mosaicColumnCount = 3;
+
+ [Tooltip(
+ "If enabled, the mosaic dimensions will be extracted from the filename. E.g. video.mosaic.3x3\nONLY WORKS FOR IMAGE AND VIDEO MODES"
+ )]
+ ///
+ /// If enabled, the mosaic dimensions will be extracted from the filename. E.g. video.mosaic.3x3
+ /// ONLY WORKS FOR IMAGE AND VIDEO MODES
+ ///
+ public bool dimensionsFromFilename = false;
+
+ [Tooltip(
+ "Use HQ Views. Does not check if the amount of HQ views specified in the configuration file fits the provided mosaic."
+ )]
+ ///
+ /// Use HQ Views. Does not check if the amount of HQ views specified in the configuration file fits the provided mosaic.
+ ///
+ public bool useHQViews = false;
+
+ [Space(10)]
+ [Tooltip(
+ "Where the views start to yoyo in the index map. Index map contains the order of views."
+ )]
+ [Range(0.0f, 1.0f)]
+ ///
+ /// Where the views start to yoyo in the index map in percent. Index map contains the order of views.
+ /// [0, 1, 2, 3, 4, 5, 6, 7] with yoyo start at 50% would become [6, 5, 4, 3, 4, 5, 6]
+ ///
+ public float indexMapYoyoStart = 0.0f;
+
+ [Tooltip("Inverts the entire index map. Index map contains the order of views.")]
+ ///
+ /// Inverts the entire index map. Index map contains the order of views.
+ /// [0, 1, 2, 3, 4, 5, 6, 7] would become [7, 6, 5, 4, 3, 2, 1, 0]
+ ///
+ public bool invertIndexMap = false;
+
+ [Tooltip("Inverts the indices in the index map. Index map contains the order of views.")]
+ ///
+ /// Inverts the individual indices in the index map. Index map contains the order of views.
+ /// [6, 5, 4, 3, 4, 5, 6] would become [0, 1, 2, 3, 2, 1, 0]
+ ///
+ public bool invertIndexMapIndices = false;
+
+ ///
+ /// Shifts the individual views to the left or right by the specified number of views.
+ /// This does not shift the cameras. This shifts the views you see on the display.
+ ///
+ [Tooltip(
+ "Shifts the individual views to the left or right by the specified number of views."
+ )]
+ public int viewOffset = 0;
+
+ public MosaicMode mosaicMode = MosaicMode.RenderTexture;
+
+ ///
+ /// Render Texture used for rendertexture mosaic mode.
+ /// In video mode this gets overwritten by this script with the provided image/ video texture.
+ ///
+ public RenderTexture renderTexture;
+ public Texture2D image;
+
+ public VideoClip videoClip;
+
+ [Tooltip("If set to true, the views will be flipped horizontally.")]
+ public bool mirrorViews = false;
+
+ #region Private variables
+ private IndexMap indexMap = IndexMap.Instance;
+ private Camera mainCamera;
+ private Material material;
#if G3D_HDRP
- private G3DHDRPCustomPass customPass;
+ private G3D.RenderPipeline.HDRP.CustomPass customPass;
#endif
#if G3D_URP
- private G3DUrpScriptableRenderPass customPass;
+ private G3D.RenderPipeline.URP.ScriptableRP customPass;
#endif
- private ShaderHandles shaderHandles;
- private G3DShaderParameters shaderParameters;
+ private ShaderHandles shaderHandles;
+ private G3DShaderParameters shaderParameters;
- private Vector2Int cachedWindowPosition;
- private Vector2Int cachedWindowSize;
+ private Vector2Int cachedWindowPosition;
+ private Vector2Int cachedWindowSize;
- public VideoPlayer internalVideoPlayer { get; private set; }
+ public VideoPlayer internalVideoPlayer { get; private set; }
- #endregion
+ #endregion
- #region Initialization
- void Start()
- {
- mainCamera = GetComponent();
- mainCamera.cullingMask = 0; //disable rendering of the main camera
- mainCamera.clearFlags = CameraClearFlags.Color;
+ #region Initialization
+ void Start()
+ {
+ mainCamera = GetComponent();
+ mainCamera.cullingMask = 0; //disable rendering of the main camera
+ mainCamera.clearFlags = CameraClearFlags.Color;
- //initialize cameras
+ //initialize cameras
- shaderHandles = new ShaderHandles()
- {
- leftViewportPosition = Shader.PropertyToID("v_pos_x"),
- bottomViewportPosition = Shader.PropertyToID("v_pos_y"),
- screenHeight = Shader.PropertyToID("s_height"),
- nativeViewCount = Shader.PropertyToID("nativeViewCount"),
- angleRatioNumerator = Shader.PropertyToID("zwinkel"),
- angleRatioDenominator = Shader.PropertyToID("nwinkel"),
- leftLensOrientation = Shader.PropertyToID("isleft"),
- BGRPixelLayout = Shader.PropertyToID("isBGR"),
- hqViewCount = Shader.PropertyToID("hqview"),
- mstart = Shader.PropertyToID("mstart"),
- };
-
- // This has to be done after the cameras are updated
- cachedWindowPosition = new Vector2Int(
- Screen.mainWindowPosition.x,
- Screen.mainWindowPosition.y
- );
- cachedWindowSize = new Vector2Int(Screen.width, Screen.height);
+ shaderHandles = new ShaderHandles();
+ shaderHandles.init();
+
+ // This has to be done after the cameras are updated
+ cachedWindowPosition = new Vector2Int(
+ Screen.mainWindowPosition.x,
+ Screen.mainWindowPosition.y
+ );
+ cachedWindowSize = new Vector2Int(Screen.width, Screen.height);
#if G3D_HDRP
- // init fullscreen postprocessing for hd render pipeline
- var customPassVolume = gameObject.AddComponent();
- customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess;
- customPassVolume.isGlobal = true;
- // Make the volume invisible in the inspector
- customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave;
- customPass = customPassVolume.AddPassOfType(typeof(G3DHDRPCustomPass)) as G3DHDRPCustomPass;
- customPass.fullscreenPassMaterial = material;
- customPass.materialPassName = "G3DFullScreen3D";
+ // init fullscreen postprocessing for hd render pipeline
+ var customPassVolume = gameObject.AddComponent();
+ customPassVolume.injectionPoint = CustomPassInjectionPoint.AfterPostProcess;
+ customPassVolume.isGlobal = true;
+ // Make the volume invisible in the inspector
+ customPassVolume.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave;
+ customPass =
+ customPassVolume.AddPassOfType(typeof(G3D.RenderPipeline.HDRP.CustomPass))
+ as G3D.RenderPipeline.HDRP.CustomPass;
+ customPass.fullscreenPassMaterial = material;
+ customPass.materialPassName = "G3DFullScreen3D";
#endif
#if G3D_URP
- customPass = new G3DUrpScriptableRenderPass(material);
+ customPass = new G3D.RenderPipeline.URP.ScriptableRP(material);
#endif
- // Do this last to ensure custom passes are already set up
- CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString(
- calibrationFile.text
- );
- shaderParameters = defaultCalibrationProvider.getShaderParameters();
- setupTextureMode();
- reinitializeShader();
+ // Do this last to ensure custom passes are already set up
+ ConfigurationProvider defaultConfigurationProvider =
+ ConfigurationProvider.getFromString(configurationFile.text);
+ shaderParameters = defaultConfigurationProvider.getShaderParameters();
+ setupTextureMode();
+ reinitializeShader();
- previousValues.init();
+ if (dimensionsFromFilename)
+ {
+ extractDimensionsFromFile();
+ }
- if (dimensionsFromFilename)
- {
- extractDimensionsFromFile();
+ updateIndexMap();
}
- updateIndexMap();
- }
-
- private void updateIndexMap()
- {
- int availableViews = shaderParameters.nativeViewCount;
- if (useHQViews)
+#if G3D_URP
+ private void OnEnable()
{
- availableViews = shaderParameters.hqViewCount;
+ RenderPipelineManager.beginCameraRendering += OnBeginCamera;
}
- indexMap.UpdateIndexMap(
- availableViews,
- mosaicColumnCount * mosaicRowCount,
- indexMapYoyoStart,
- invertIndexMap,
- invertIndexMapIndices
- );
- }
-
- private void extractDimensionsFromFile()
- {
- string name = "";
- switch (mosaicMode)
+ private void OnDisable()
{
- case MosaicMode.Image:
- if (image != null)
- {
- name = image.name;
- }
- break;
- case MosaicMode.Video:
- if (videoClip != null)
- {
- name = videoClip.name;
- }
- break;
- case MosaicMode.RenderTexture:
- // cannot extract dimensions from render texture
- return;
+ RenderPipelineManager.beginCameraRendering -= OnBeginCamera;
}
- dimensionsFromString(name, out mosaicRowCount, out mosaicColumnCount);
- }
-
- private void dimensionsFromString(string name, out int rows, out int columns)
- {
- rows = 1;
- columns = 1;
- string[] parts = name.Split('.');
- if (parts.Length < 3 || parts[1] != "mosaic")
+ private void OnBeginCamera(ScriptableRenderContext context, Camera cam)
{
- Debug.LogError("Invalid mosaic video file name format: " + name);
- return;
+ // Use the EnqueuePass method to inject a custom render pass
+ cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass);
}
+#endif
- string rowsStr = parts[2];
- string[] tmp = rowsStr.Split('x');
- if (tmp.Length != 2)
- {
- Debug.LogError("Invalid mosaic video rows format: " + rowsStr);
- return;
- }
- string columnsStr = tmp[0]; // e.g. 3x3 -> 3
- string rowsStrOnly = tmp[1]; // e.g. 3x3 -> 3
- if (!int.TryParse(columnsStr, out columns) || !int.TryParse(rowsStrOnly, out rows))
- {
- Debug.LogError("Could not parse mosaic video rows and columns from: " + rowsStr);
- return;
- }
- }
+ #endregion
- private void setupTextureMode()
- {
- switch (mosaicMode)
- {
- case MosaicMode.RenderTexture:
- // nothing to do here, render texture is already assigned
- break;
- case MosaicMode.Video:
- setupVideoPlayer();
- break;
- case MosaicMode.Image:
- // nothing to do here, image is already assigned
- break;
- }
- }
+ #region Updates
- private void setupVideoPlayer()
- {
- if (internalVideoPlayer == null)
+ void Update()
{
- internalVideoPlayer = gameObject.AddComponent();
- internalVideoPlayer.playOnAwake = true;
- internalVideoPlayer.isLooping = true;
+ updateShaderParameters();
+
+ if (windowResized() || windowMoved())
+ {
+ updateScreenViewportProperties();
+ }
}
- internalVideoPlayer.renderMode = VideoRenderMode.RenderTexture;
- if (renderTexture == null)
+
+ public void updateIndexMap()
{
- renderTexture = new RenderTexture(1920, 1080, 0);
+ int availableViews = shaderParameters.nativeViewCount;
+ if (useHQViews)
+ {
+ availableViews = shaderParameters.hqViewCount;
+ }
+
+ indexMap.UpdateIndexMap(
+ availableViews,
+ mosaicColumnCount * mosaicRowCount,
+ indexMapYoyoStart,
+ invertIndexMap,
+ invertIndexMapIndices
+ );
}
- internalVideoPlayer.targetTexture = renderTexture;
- internalVideoPlayer.clip = videoClip;
- internalVideoPlayer.Play();
- }
- private void setCorrectMosaicTexture()
- {
- switch (mosaicMode)
+ public string indexMapToString()
{
- case MosaicMode.RenderTexture:
- material.SetTexture("mosaictexture", renderTexture, RenderTextureSubElement.Color);
- break;
- case MosaicMode.Video:
- material.SetTexture("mosaictexture", renderTexture, RenderTextureSubElement.Color);
- break;
- case MosaicMode.Image:
- material.SetTexture("mosaictexture", image);
- break;
+ return indexMap.currentMapToString();
}
- }
- public void reinitializeShader()
- {
- material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic"));
- setCorrectMosaicTexture();
+ public void reinitializeShader()
+ {
+ material = new Material(Shader.Find("G3D/AutostereoMultiviewMosaic"));
+ setCorrectMosaicTexture();
- updateScreenViewportProperties();
- updateShaderParameters();
+ updateScreenViewportProperties();
+ updateShaderParameters();
#if G3D_HDRP
- customPass.fullscreenPassMaterial = material;
-#endif
-#if G3D_URP
- customPass.updateMaterial(material);
+ customPass.fullscreenPassMaterial = material;
#endif
- }
-
#if G3D_URP
- private void OnEnable()
- {
- RenderPipelineManager.beginCameraRendering += OnBeginCamera;
- }
-
- private void OnDisable()
- {
- RenderPipelineManager.beginCameraRendering -= OnBeginCamera;
- }
-
- private void OnBeginCamera(ScriptableRenderContext context, Camera cam)
- {
- // Use the EnqueuePass method to inject a custom render pass
- cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(customPass);
- }
+ customPass.updateMaterial(material);
#endif
-
- #endregion
-
- #region Updates
-
- ///
- /// OnValidate gets called every time the script is changed in the editor.
- /// This is used to react to changes made to the parameters.
- ///
- void OnValidate()
- {
- if (isActiveAndEnabled == false)
- {
- // do not run this code if the script is not enabled
- return;
}
- if (calibrationFile != previousValues.calibrationFile)
+ ///
+ /// Updates the shader parameters based on the provided configuration file.
+ ///
+ ///
+ public void updateShaderFromConfigurationFile(TextAsset configurationFile)
{
- previousValues.calibrationFile = calibrationFile;
- updateShaderFromCalibrationFile();
+ if (configurationFile == null || configurationFile.text == "")
+ {
+ return;
+ }
+ this.configurationFile = configurationFile;
+ updateShaderFromConfigurationFile();
}
- if (
- previousValues.indexMapYoyoStart != indexMapYoyoStart
- || previousValues.invertIndexMap != invertIndexMap
- || previousValues.invertIndexMapIndices != invertIndexMapIndices
- )
+ ///
+ /// Updates the shader parameters based on the configuration file already set.
+ ///
+ public void updateShaderFromConfigurationFile()
{
- previousValues.indexMapYoyoStart = indexMapYoyoStart;
- previousValues.invertIndexMap = invertIndexMap;
- previousValues.invertIndexMapIndices = invertIndexMapIndices;
+ if (configurationFile == null || configurationFile.text == "")
+ {
+ return;
+ }
- indexMap.UpdateIndexMap(
- shaderParameters.nativeViewCount,
- mosaicColumnCount * mosaicRowCount,
- indexMapYoyoStart,
- invertIndexMap,
- invertIndexMapIndices
+ ConfigurationProvider calibrationProvider = ConfigurationProvider.getFromString(
+ configurationFile.text
);
+ shaderParameters = calibrationProvider.getShaderParameters();
}
- }
- public void updateShaderFromCalibrationFile(TextAsset calibrationFile)
- {
- if (calibrationFile == null || calibrationFile.text == "")
+ ///
+ /// The provided file uri has to be a display configuration ini file.
+ ///
+ ///
+ public void UpdateShaderParametersFromURI(string uri)
{
- return;
+ if (uri == null || uri == "")
+ {
+ return;
+ }
+
+ try
+ {
+ ConfigurationProvider defaultConfigurationProvider =
+ ConfigurationProvider.getFromURI(
+ uri,
+ (ConfigurationProvider provider) =>
+ {
+ shaderParameters = provider.getShaderParameters();
+ updateShaderParameters();
+ return 0;
+ }
+ );
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to update shader parameters from uri: " + e.Message);
+ }
}
- this.calibrationFile = calibrationFile;
- updateShaderFromCalibrationFile();
- }
- public void updateShaderFromCalibrationFile()
- {
- if (calibrationFile == null || calibrationFile.text == "")
+ ///
+ /// The provided file path has to be a display configuration ini file.
+ ///
+ ///
+ public void UpdateShaderParametersFromFile(string filePath)
{
- return;
+ if (filePath == null || filePath == "" || filePath.EndsWith(".ini") == false)
+ {
+ return;
+ }
+
+ try
+ {
+ ConfigurationProvider defaultConfigurationProvider =
+ ConfigurationProvider.getFromConfigFile(filePath);
+ shaderParameters = defaultConfigurationProvider.getShaderParameters();
+ updateShaderParameters();
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to update shader parameters from file: " + e.Message);
+ }
}
- CalibrationProvider calibrationProvider = CalibrationProvider.getFromString(
- calibrationFile.text
- );
- shaderParameters = calibrationProvider.getShaderParameters();
- }
-
- void Update()
- {
- updateShaderParameters();
-
- if (windowResized() || windowMoved())
+ ///
+ /// The provided string has to be a display configuration ini file.
+ ///
+ ///
+ public void UpdateShaderParametersFromINIString(string iniFile)
{
- updateScreenViewportProperties();
+ if (iniFile == null || iniFile == "")
+ {
+ return;
+ }
+
+ try
+ {
+ ConfigurationProvider defaultConfigurationProvider =
+ ConfigurationProvider.getFromString(iniFile);
+ shaderParameters = defaultConfigurationProvider.getShaderParameters();
+ updateShaderParameters();
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to update shader parameters from json: " + e.Message);
+ }
}
- }
- private void updateScreenViewportProperties()
- {
- try
+ ///
+ /// Sets the dimensions of the mosaic based on the filename. The filename has to be in the format "name.mosaic.{columns}x{rows}.ext". E.g. "video.mosaic.3x3.mp4" would set the mosaic to have 3 columns and 3 rows.
+ ///
+ ///
+ public void setDimensionsFromFilename(string filename)
{
- shaderParameters.screenHeight = Screen.height;
- shaderParameters.screenWidth = Screen.width;
- shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x;
- shaderParameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height;
+ dimensionsFromString(filename, out mosaicRowCount, out mosaicColumnCount);
}
- catch (Exception e)
- {
- Debug.LogError("Failed to update screen viewport properties: " + e.Message);
- }
-
- // this parameter is used in the shader to invert the y axis
- material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height);
- }
- private void updateShaderParameters()
- {
- material?.SetInt(shaderHandles.leftViewportPosition, shaderParameters.leftViewportPosition);
- material?.SetInt(
- shaderHandles.bottomViewportPosition,
- shaderParameters.bottomViewportPosition
- );
- material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight);
- material?.SetInt(shaderHandles.angleRatioNumerator, shaderParameters.angleRatioNumerator);
- material?.SetInt(
- shaderHandles.angleRatioDenominator,
- shaderParameters.angleRatioDenominator
- );
- material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount);
- material?.SetInt(shaderHandles.leftLensOrientation, shaderParameters.leftLensOrientation);
- material?.SetInt(shaderHandles.showTestFrame, 0);
- material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount);
- material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout);
- material?.SetInt(shaderHandles.mstart, shaderParameters.mstart);
-
- int cameraCount = mosaicColumnCount * mosaicRowCount;
- int shaderMaxCount = shaderParameters.nativeViewCount;
- if (cameraCount > shaderMaxCount)
+ private void extractDimensionsFromFile()
{
- cameraCount = shaderMaxCount;
+ string name = "";
+ switch (mosaicMode)
+ {
+ case MosaicMode.Image:
+ if (image != null)
+ {
+ name = image.name;
+ }
+ break;
+ case MosaicMode.Video:
+ if (videoClip != null)
+ {
+ name = videoClip.name;
+ }
+ break;
+ case MosaicMode.RenderTexture:
+ // cannot extract dimensions from render texture
+ return;
+ }
+ dimensionsFromString(name, out mosaicRowCount, out mosaicColumnCount);
}
- material?.SetInt(Shader.PropertyToID("cameraCount"), cameraCount);
-
- material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0);
-
- material?.SetInt(Shader.PropertyToID("mosaic_rows"), mosaicRowCount);
- material?.SetInt(Shader.PropertyToID("mosaic_columns"), mosaicColumnCount);
-
- material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset);
-
- material.SetInt(Shader.PropertyToID("indexMapLength"), indexMap.currentMap.Length);
- material.SetFloatArray(Shader.PropertyToID("index_map"), indexMap.getPaddedIndexMapArray());
+ private void dimensionsFromString(string name, out int rows, out int columns)
+ {
+ rows = 1;
+ columns = 1;
- material?.SetInt(Shader.PropertyToID("use_hq_views"), useHQViews ? 1 : 0);
- }
+ string[] parts = name.Split('.');
+ if (parts.Length < 3 || parts[1] != "mosaic")
+ {
+ Debug.LogError("Invalid mosaic video file name format: " + name);
+ return;
+ }
- private bool windowResized()
- {
- var window_dim = new Vector2Int(Screen.width, Screen.height);
- if (cachedWindowSize != window_dim)
- {
- cachedWindowSize = window_dim;
- return true;
+ string rowsStr = parts[2];
+ string[] tmp = rowsStr.Split('x');
+ if (tmp.Length != 2)
+ {
+ Debug.LogError("Invalid mosaic video rows format: " + rowsStr);
+ return;
+ }
+ string columnsStr = tmp[0]; // e.g. 3x3 -> 3
+ string rowsStrOnly = tmp[1]; // e.g. 3x3 -> 3
+ if (!int.TryParse(columnsStr, out columns) || !int.TryParse(rowsStrOnly, out rows))
+ {
+ Debug.LogError("Could not parse mosaic video rows and columns from: " + rowsStr);
+ return;
+ }
}
- return false;
- }
- private bool windowMoved()
- {
- var window_pos = new Vector2Int(Screen.mainWindowPosition.x, Screen.mainWindowPosition.y);
- if (cachedWindowPosition != window_pos)
+ private void setupTextureMode()
{
- cachedWindowPosition = window_pos;
- return true;
+ switch (mosaicMode)
+ {
+ case MosaicMode.RenderTexture:
+ // nothing to do here, render texture is already assigned
+ break;
+ case MosaicMode.Video:
+ setupVideoPlayer();
+ break;
+ case MosaicMode.Image:
+ // nothing to do here, image is already assigned
+ break;
+ }
}
- return false;
- }
- // This function only does something when you use the SRP render pipeline.
- // when using either URP or HRDP image combination is handled in the respective renderpasses.
- // URP -> G3DUrpScriptableRenderPass.cs
- // HDRP -> G3DHDRPCustomPass.cs
- void OnRenderImage(RenderTexture source, RenderTexture destination)
- {
- // This is where the material and shader are applied to the camera image.
- //legacy support (no URP or HDRP)
-#if G3D_HDRP || URP
-#else
- if (material == null)
- Graphics.Blit(source, destination);
- else
- Graphics.Blit(source, destination, material);
-#endif
- }
- #endregion
-
- ///
- /// The provided file uri has to be a display calibration ini file.
- ///
- ///
- public void UpdateShaderParametersFromURI(string uri)
- {
- if (uri == null || uri == "")
+ private void setupVideoPlayer()
{
- return;
+ if (internalVideoPlayer == null)
+ {
+ internalVideoPlayer = gameObject.AddComponent();
+ internalVideoPlayer.playOnAwake = true;
+ internalVideoPlayer.isLooping = true;
+ }
+ internalVideoPlayer.renderMode = VideoRenderMode.RenderTexture;
+ if (renderTexture == null)
+ {
+ renderTexture = new RenderTexture(1920, 1080, 0);
+ }
+ internalVideoPlayer.targetTexture = renderTexture;
+ internalVideoPlayer.clip = videoClip;
+ internalVideoPlayer.Play();
}
- try
+ private void setCorrectMosaicTexture()
{
- CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromURI(
- uri,
- (CalibrationProvider provider) =>
- {
- shaderParameters = provider.getShaderParameters();
- updateShaderParameters();
- return 0;
- }
- );
+ switch (mosaicMode)
+ {
+ case MosaicMode.RenderTexture:
+ material.SetTexture(
+ "mosaictexture",
+ renderTexture,
+ RenderTextureSubElement.Color
+ );
+ material.SetTexture(
+ "_colorMosaic",
+ renderTexture,
+ RenderTextureSubElement.Color
+ );
+ break;
+ case MosaicMode.Video:
+ material.SetTexture(
+ "mosaictexture",
+ renderTexture,
+ RenderTextureSubElement.Color
+ );
+ material.SetTexture(
+ "_colorMosaic",
+ renderTexture,
+ RenderTextureSubElement.Color
+ );
+ break;
+ case MosaicMode.Image:
+ material.SetTexture("mosaictexture", image);
+ material.SetTexture("_colorMosaic", image);
+ break;
+ }
}
- catch (Exception e)
- {
- Debug.LogError("Failed to update shader parameters from uri: " + e.Message);
- }
- }
- ///
- /// The provided file path has to be a display calibration ini file.
- ///
- ///
- public void UpdateShaderParametersFromFile(string filePath)
- {
- if (filePath == null || filePath == "" || filePath.EndsWith(".ini") == false)
+ private void updateScreenViewportProperties()
{
- return;
+ try
+ {
+ shaderParameters.screenHeight = Screen.height;
+ shaderParameters.screenWidth = Screen.width;
+ shaderParameters.leftViewportPosition = Screen.mainWindowPosition.x;
+ shaderParameters.bottomViewportPosition =
+ Screen.mainWindowPosition.y + Screen.height;
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to update screen viewport properties: " + e.Message);
+ }
+
+ // this parameter is used in the shader to invert the y axis
+ material?.SetInt(Shader.PropertyToID("viewportHeight"), Screen.height);
+ material?.SetInt(Shader.PropertyToID("viewportWidth"), Screen.width);
}
- try
+ private void updateShaderParameters()
{
- CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromConfigFile(
- filePath
+ material?.SetInt(
+ shaderHandles.leftViewportPosition,
+ shaderParameters.leftViewportPosition
);
- shaderParameters = defaultCalibrationProvider.getShaderParameters();
- updateShaderParameters();
- }
- catch (Exception e)
- {
- Debug.LogError("Failed to update shader parameters from file: " + e.Message);
+ material?.SetInt(
+ shaderHandles.bottomViewportPosition,
+ shaderParameters.bottomViewportPosition
+ );
+ material?.SetInt(shaderHandles.screenHeight, shaderParameters.screenHeight);
+ material?.SetInt(shaderHandles.screenWidth, shaderParameters.screenWidth);
+ material?.SetInt(
+ shaderHandles.angleRatioNumerator,
+ shaderParameters.angleRatioNumerator
+ );
+ material?.SetInt(
+ shaderHandles.angleRatioDenominator,
+ shaderParameters.angleRatioDenominator
+ );
+ material?.SetInt(shaderHandles.nativeViewCount, shaderParameters.nativeViewCount);
+ material?.SetInt(
+ shaderHandles.leftLensOrientation,
+ shaderParameters.leftLensOrientation
+ );
+ material?.SetInt(shaderHandles.showTestFrame, 0);
+ material?.SetInt(shaderHandles.hqViewCount, shaderParameters.hqViewCount);
+ material?.SetInt(shaderHandles.BGRPixelLayout, shaderParameters.BGRPixelLayout);
+ material?.SetInt(shaderHandles.mstart, shaderParameters.mstart);
+
+ int cameraCount = mosaicColumnCount * mosaicRowCount;
+ int shaderMaxCount = shaderParameters.nativeViewCount;
+ if (cameraCount > shaderMaxCount)
+ {
+ cameraCount = shaderMaxCount;
+ }
+
+ material?.SetInt(Shader.PropertyToID("cameraCount"), cameraCount);
+
+ material?.SetInt(Shader.PropertyToID("mirror"), mirrorViews ? 1 : 0);
+
+ material?.SetInt(Shader.PropertyToID("mosaic_rows"), mosaicRowCount);
+ material?.SetInt(Shader.PropertyToID("mosaic_columns"), mosaicColumnCount);
+
+ material?.SetInt(Shader.PropertyToID("viewOffset"), viewOffset);
+
+ material.SetInt(Shader.PropertyToID("indexMapLength"), indexMap.currentMap.Length);
+ material.SetFloatArray(
+ Shader.PropertyToID("index_map"),
+ indexMap.getPaddedIndexMapArray()
+ );
+
+ material?.SetInt(Shader.PropertyToID("use_hq_views"), useHQViews ? 1 : 0);
}
- }
- ///
- /// The provided string has to be a display calibration ini file.
- ///
- ///
- public void UpdateShaderParametersFromINIString(string iniFile)
- {
- if (iniFile == null || iniFile == "")
+ private bool windowResized()
{
- return;
+ var window_dim = new Vector2Int(Screen.width, Screen.height);
+ if (cachedWindowSize != window_dim)
+ {
+ cachedWindowSize = window_dim;
+ return true;
+ }
+ return false;
}
- try
+ private bool windowMoved()
{
- CalibrationProvider defaultCalibrationProvider = CalibrationProvider.getFromString(
- iniFile
+ var window_pos = new Vector2Int(
+ Screen.mainWindowPosition.x,
+ Screen.mainWindowPosition.y
);
- shaderParameters = defaultCalibrationProvider.getShaderParameters();
- updateShaderParameters();
+ if (cachedWindowPosition != window_pos)
+ {
+ cachedWindowPosition = window_pos;
+ return true;
+ }
+ return false;
}
- catch (Exception e)
+
+ // This function only does something when you use the SRP render pipeline.
+ // when using either URP or HRDP image combination is handled in the respective renderpasses.
+ // URP -> G3D.RenderPipeline.URP.ScriptableRP.cs
+ // HDRP -> G3D.RenderPipeline.HDRP.CustomPass.cs
+ void OnRenderImage(RenderTexture source, RenderTexture destination)
{
- Debug.LogError("Failed to update shader parameters from json: " + e.Message);
+ // This is where the material and shader are applied to the camera image.
+ //legacy support (no URP or HDRP)
+#if G3D_HDRP || URP
+#else
+ if (material == null)
+ Graphics.Blit(source, destination);
+ else
+ Graphics.Blit(source, destination, material);
+#endif
}
+ #endregion
}
}
diff --git a/Multiview Display Calibrations/Display_43-Inch_N8D.txt.meta b/Multiview Display Calibrations/Display_43-Inch_N8D.txt.meta
deleted file mode 100644
index a8db8ed..0000000
--- a/Multiview Display Calibrations/Display_43-Inch_N8D.txt.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: cca51193da5633a429db9e6a48fe47a6
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Multiview Display Calibrations/Display_65-Inch_17D.txt.meta b/Multiview Display Calibrations/Display_65-Inch_17D.txt.meta
deleted file mode 100644
index e75f198..0000000
--- a/Multiview Display Calibrations/Display_65-Inch_17D.txt.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 2dcfd0cb4bc2bfa4a8eec4dd6afd99d9
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Multiview Display Calibrations/Holobox_13-3-Inch_S3D.txt b/Multiview Display Calibrations/Holobox_13-3-Inch_S3D.txt
deleted file mode 100644
index b9e44b4..0000000
--- a/Multiview Display Calibrations/Holobox_13-3-Inch_S3D.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-
-[MonitorConfiguration]
-Name=PM241UHD
-AssociatedHeadTrackingDevice=PmdFlexx2#0430-9,40A-1,AC8-5,044
-SerialNumber=22Q4A00606
-PhysicalSizeInch=13.300000
-DistanceSensorToPanelCenterMM=112.500000
-HorizontalResolution=3840
-VerticalResolution=2160
-ConfigCode=S3D
-NativeViewcount=7
-AngleRatioNumerator=4
-AngleRatioDenominator=5
-LeftLensOrientation=1
-isBGR=false
-MShiftDefault=0
-ZShiftDefault=0
-BlackBorderDefault=0
-BlackSpaceDefault=0
-MaxAllowedHorizontalAngle=0.000000
-MaxAllowedVerticalAngle=0.000000
-MinAllowedDistanceMM=0.000000
-MaxAllowedDistanceMM=0.000000
-BasicWorkingDistanceMM=682
-MinWorkingDistanceMM=432
-MaxWorkingDistanceMM=811
-ZoneWidthAtMinWorkingDistance=389
-ZoneWidthAtMaxWorkingDistance=-3312
-LatencyCorrectionTimeShift=90.000000
-zCorrectionFunctionFactor=9.743771
-zCorrectionFunctionBase=0.538800
-ViewOffsetAtBaseDistance=8
-ZCompensationValueAtMinimumWorkingDistance=19
-ZCompensationValueAtMaximumWorkingDistance=-2
-ApertureAngle=12.95
diff --git a/Multiview Display Calibrations/Holobox_13-3-Inch_S3D.txt.meta b/Multiview Display Calibrations/Holobox_13-3-Inch_S3D.txt.meta
deleted file mode 100644
index 4a67803..0000000
--- a/Multiview Display Calibrations/Holobox_13-3-Inch_S3D.txt.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 342c6b5fc21fa0f468a829ea957e02c0
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Multiview Display Calibrations/Holobox_27-Inch_17Q.txt.meta b/Multiview Display Calibrations/Holobox_27-Inch_17Q.txt.meta
deleted file mode 100644
index 32fe843..0000000
--- a/Multiview Display Calibrations/Holobox_27-Inch_17Q.txt.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 20dada593864a5d48952c171b5494ce0
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/README.md b/README.md
index 6a51444..03c5146 100644
--- a/README.md
+++ b/README.md
@@ -18,9 +18,9 @@ The created autostereo cameras all shift their FOV such that they overlap on the
This plugin creates helper gizmos in the scene view to visualize the focus plane and the camera positions. The size of the gizmos can be adjusted in the script parameters. The spheres next to the main camera represent the base position of the autostereo cameras. The blue plane represents the focus plane. The blue view frustums represents the FOV of the autostereo cameras.
-When using diorama mode the autostereo cameras follow the same movement pattern relative to the focus plane, as the head performs infront of the display. In addition the FOV of the cameras is adjusted such that they always overlap on the focus plane.
+When using headtracking mode the autostereo cameras follow the same movement pattern relative to the focus plane, as the head performs infront of the display. In addition the FOV of the cameras is adjusted such that they always overlap on the focus plane.
-Example for shifted FOV in diorama mode:
+Example for shifted FOV in headtracking mode:
| | |
| -------------------------------------------- | -------------------------------------------- |
|  |  |
@@ -34,18 +34,21 @@ Example for shifted FOV in diorama mode:
# Parameters
-| Name | Description |
-| ----------------------- | --------------------------------------------------------------------------------- |
-| Mode | Switch between Diorama and Multiview modes. |
-| Calibration file | The calibration file used to calibrate multiview mode. |
-| Scene scale factor | Scales display calibration to fit larger scenes. |
-| Dolly zoom | Mimics a dolly zoom effect. |
-| View offset scale | Scales the view disparity (e.g pushes 3d cameras closer together/ further apart). |
-| Render resolution scale | Size of the individual autostereo camera render textures in percent. |
+| Name | Description |
+| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
+| Mode | Switch between headtracking and Multiview modes. |
+| Calibration file | The configuration file used to calibrate multiview mode. |
+| Scene scale factor | Scales display configuration to fit larger scenes. |
+| Dolly zoom | Mimics a dolly zoom effect. |
+| View offset scale | Scales the view disparity (e.g pushes 3d cameras closer together/ further apart). |
+| Render resolution scale | Size of the individual autostereo camera render textures in percent. |
+| Generate Views | If set to true, only the outer most and the middle view will be rendered. The rest is generated based on these. |
+| Fill holes | If set to true, small holes in the generated views will be filled. |
+| Hole filling radius | How far around the missing pixels the algorithm searches for a usable pixel. Larger values result in better results but take longer. |
### Calibration file
-This takes an ini file (the same ini files as can be found in the displays calibration folder) that contains the calibration data for the display. This is only needed in multiview mode. In diorama mode the calibration files are read from the default calibration folder of 3D Global. You can still provide a calibration file in diorama mode. It will only be used for drawing the helper gizmos in the scene view. During play it will not be used (in diorama mode).
+This takes an ini file (the same ini files as can be found in the displays configuration folder) that contains the configuration data for the display. This is only needed in multiview mode. In headtracking mode the configuration files are read from the default configuration folder of 3D Global. You can still provide a configuration file in headtracking mode. It will only be used for drawing the helper gizmos in the scene view. During play it will not be used (in headtracking mode).
Unfortunatly for now unity does not support "\*.ini" files as TextAsset. Therefore you have to rename the file extension to "\*.txt" to be able to use it in Unity.
@@ -62,7 +65,7 @@ Mimics a dolly zoom effect by scaling the camera position and field of view. Thi
| Name | Description |
| ------------------- | ----------------------------------------------------------------------------- |
| Mirror views | Mirrors the individual views horizontally (e.g. needed for Holobox displays). |
-| Head tracking scale | Scales the strength of head tracking. (Only shown in diorama mode.) |
+| Head tracking scale | Scales the strength of head tracking. (Only shown in headtracking mode.) |
| Show gizmos | Render helper gizmos in scene. |
| Gizmo size | Size of the gizmos. |
@@ -74,22 +77,34 @@ This value can be used to increase performance by reducing the resolution of the
## Modes explained
-### Diorama mode
+### Headtracking mode
This mode uses a head tracking camera integrated into a 3d Global display to adjust the view based on the viewer's head position. It only works properly if the display has a head tracking camera.
-#### Diorama Calibration files
+#### Headtracking Calibration files
-It requires the displays calibration files to be copied to "C:\Users\Public\Documents\3D Global\calibrations" (Windows). Each individual physical display has its own calibration files. These can be found on the display. The calibration for one display is contained in a folder named after the used head tracking camera (e.g "HimaxD2XX#DK0HLRO3"). Inside this folder are two files (one ini file and one image file). Copy this entire folder to the calibration folder mentioned earlier.
+It requires the displays configuration files to be copied to "C:\Users\Public\Documents\3D Global\calibrations" (Windows). Each individual physical display has its own configuration files. These can be found on the display. The configuration for one display is contained in a folder named after the used head tracking camera (e.g "HimaxD2XX#DK0HLRO3"). Inside this folder are two files (one ini file and one image file). Copy this entire folder to the configuration folder mentioned earlier.
### Multiview mode
-In this mode the plugin renders multiple views without any head tracking and spreads them equally accross the displays available views. This mode works on all 3D Global displays. This mode only requires general calibration information contained in the calibration.ini file. Therefore you can reuse the same calibration file on multiple displays of the same type.
+In this mode the plugin renders multiple views without any head tracking and spreads them equally accross the displays available views. This mode works on all 3D Global displays. This mode only requires general configuration information contained in the configuration.ini file. Therefore you can reuse the same configuration file on multiple displays of the same type.
# Performance
As each view only shows a subset of the displays pixels, you can use the "Render resolution scale" parameter to reduce the resolution of the individual camera render textures. This can significantly increase performance while barely impacting quality.
+# View Generation
+
+When this is enabled only the outer most and the middle view are rendered. The other views are generated based on these two views.
+
+## Anti Aliasing
+
+Only FXAA and SMAA work when view generation is enabled. When the cameras anti aliasing is set to TAA no anti aliasing is applied to the rendered views.
+
+## Bloom
+
+Bloom does not work correctly when view generation is enabled. Bloom results in weird halo like effects around objects in the generated views. It is recommended to disable bloom when using view generation.
+
# Switching render pipelines
If you switch between render pipelines (e.g. from URP to built-in) you need to ensure the old render pipeline package is removed from the project. Otherwise this plugin will show wrong behavior.
@@ -105,4 +120,4 @@ Reimporting this package also updates the "Scripting define symbols" automatical
If you have more than one render pipeline installed this plugin cant know which one is the active one and might not work correctly. Removing the unused render pipeline package resolves this issue. Currently the plugin only checks the installed render pipeline after installation. So if you switch the render pipeline you have to remove the plugin and reimport it.
-Switching mode during playback (multiview to diorama and vice versa) is currently not supported. You have to stop the playback and start it again to switch between modes.
+Switching mode during playback (multiview to headtracking and vice versa) is currently not supported. You have to stop the playback and start it again to switch between modes.
diff --git a/Resources/G3DBlit.shader b/Resources/G3DBlit.shader
new file mode 100644
index 0000000..6237f75
--- /dev/null
+++ b/Resources/G3DBlit.shader
@@ -0,0 +1,38 @@
+Shader "G3D/G3DBlit"
+{
+ SubShader
+ {
+ PackageRequirements
+ {
+ "com.unity.render-pipelines.high-definition": "unity=2021.3"
+ }
+ Tags { "RenderType"="Opaque" "RenderPipeline" = "HDRenderPipeline"}
+
+ Pass
+ {
+ Name "G3DBlit"
+
+ ZWrite Off
+ ZTest Always
+ Blend Off
+ Cull Off
+
+ HLSLPROGRAM
+ #include "G3DHLSLShaderBasics.hlsl"
+ #include "G3DHLSLCommonFunctions.hlsl"
+
+ #pragma vertex vert
+ #pragma fragment fragHDRP
+
+ // 0 = left camera, 1 = right camera
+ Texture2D _mainTex;
+ SamplerState sampler_mainTex;
+
+ float4 fragHDRP (v2f i) : SV_Target
+ {
+ return _mainTex.Sample(sampler_mainTex, i.uv); // sample the main texture
+ }
+ ENDHLSL
+ }
+ }
+}
\ No newline at end of file
diff --git a/Resources/G3DBlit.shader.meta b/Resources/G3DBlit.shader.meta
new file mode 100644
index 0000000..18825d4
--- /dev/null
+++ b/Resources/G3DBlit.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b0c81b24f2002d9468f2f132574c55ae
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ nonModifiableTextures: []
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Resources/G3DCameraInspector.uxml b/Resources/G3DCameraInspector.uxml
index c5589d8..9c4c395 100644
--- a/Resources/G3DCameraInspector.uxml
+++ b/Resources/G3DCameraInspector.uxml
@@ -1,27 +1,33 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Resources/G3DHLSLCommonFunctions.hlsl b/Resources/G3DHLSLCommonFunctions.hlsl
new file mode 100644
index 0000000..6ada76f
--- /dev/null
+++ b/Resources/G3DHLSLCommonFunctions.hlsl
@@ -0,0 +1,40 @@
+float2 calculateUVForMosaic(uint viewIndex, float2 fullScreenUV, uint mosaic_rows = 4, uint mosaic_columns = 4) {
+ viewIndex = max(0, viewIndex);
+ uint xAxis = viewIndex % mosaic_columns;
+ uint yAxis = viewIndex / mosaic_columns;
+ // invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left)
+ // The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity.
+ yAxis = mosaic_rows - 1 - yAxis;
+ int2 moasicIndex = int2(xAxis, yAxis);
+ float2 scaledUV = float2(fullScreenUV.x / mosaic_columns, fullScreenUV.y / mosaic_rows);
+ float2 cellSize = float2(1.0 / mosaic_columns, 1.0 / mosaic_rows);
+ return scaledUV + cellSize * moasicIndex;
+}
+
+
+// the text coords of the original left and right view are from 0 - 1
+// the tex coords of the texel we are currently rendering are also from 0 - 1
+// but we want to create a grid of views, so we need to transform the tex coords
+// to the grid size.
+// basically we want to figure out in which grid cell the current texel is, then convert the texel coords to the grid cell coords.
+// example assuming a grid size of 3x3:
+// original tex coords: 0.8, 0.5
+// step 1: transform the tex coords to the grid size by multiplying with grid size
+// -> e.g. original x coord 0.8 turns to 0.8 * 3 = 2.4
+// step 2: figure out the grid cell by taking the integer part of the transformed tex coords
+// -> e.g. 2.4 turns to 2
+// step 3: subtract the integer part from the transformed tex coords to get the texel coords in the grid cell
+// -> e.g. 2.4 - 2 = 0.4 -> final texel coords in the grid cell are 0.4, 0.5
+float2 getCellCoordinates(float2 uv, int2 gridSize) {
+ // flip y coordiate to have cell index 0 in upper left corner
+ return float2(uv.x, 1.0 - uv.y) * float2(gridSize);
+}
+
+uint getViewIndex(float2 cellCoordinates, int2 gridSize) {
+ return uint(cellCoordinates.x) + gridSize.x * uint(cellCoordinates.y);
+}
+
+float2 getCellTexCoords(float2 cellCoordinates) {
+ float2 uv = frac(cellCoordinates);
+ return float2(uv.x, 1.0 - uv.y);
+}
diff --git a/Resources/G3DHLSLCommonFunctions.hlsl.meta b/Resources/G3DHLSLCommonFunctions.hlsl.meta
new file mode 100644
index 0000000..3655b91
--- /dev/null
+++ b/Resources/G3DHLSLCommonFunctions.hlsl.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 0bd9fd86aa17fd3488c5bce2a26b8204
+ShaderIncludeImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Resources/G3DHLSLShaderBasics.hlsl b/Resources/G3DHLSLShaderBasics.hlsl
new file mode 100644
index 0000000..e2d3a72
--- /dev/null
+++ b/Resources/G3DHLSLShaderBasics.hlsl
@@ -0,0 +1,25 @@
+#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl"
+#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
+
+struct VertAttributes
+{
+ uint vertexID : SV_VertexID;
+ UNITY_VERTEX_INPUT_INSTANCE_ID
+};
+
+struct v2f
+{
+ float2 uv : TEXCOORD0;
+ float4 screenPos : SV_POSITION;
+};
+
+v2f vert(VertAttributes input)
+{
+ v2f output;
+ UNITY_SETUP_INSTANCE_ID(input);
+ output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
+ output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID);
+
+ return output;
+
+}
\ No newline at end of file
diff --git a/Resources/G3DHLSLShaderBasics.hlsl.meta b/Resources/G3DHLSLShaderBasics.hlsl.meta
new file mode 100644
index 0000000..b9dd37e
--- /dev/null
+++ b/Resources/G3DHLSLShaderBasics.hlsl.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 7e489c663c157834ab53e28c0f32e201
+ShaderIncludeImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Resources/G3DMosaicCameraInspector.uxml b/Resources/G3DMosaicCameraInspector.uxml
index 82a2f11..8750321 100644
--- a/Resources/G3DMosaicCameraInspector.uxml
+++ b/Resources/G3DMosaicCameraInspector.uxml
@@ -1,5 +1,5 @@
-
+
@@ -17,6 +17,9 @@
+
+
+
diff --git a/Resources/G3DShader.shader b/Resources/G3DShader.shader
index ad09c5c..d064ff4 100644
--- a/Resources/G3DShader.shader
+++ b/Resources/G3DShader.shader
@@ -33,13 +33,13 @@ Shader "G3D/Autostereo"
// The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity.
int yScreenCoords = int(i.screenPos.y) + v_pos_y; // transform y position from viewport to screen coordinates
if (isleft == 0) {
- yScreenCoords = s_height - yScreenCoords ; // invertieren für rechts geneigte Linse
+ yScreenCoords = s_height - yScreenCoords; // invertieren für rechts geneigte Linse
}
int yw = int(yScreenCoords * zwinkel) / nwinkel; // Winkelberechnung für die Renderberechnung
//Start native Renderberechnung
int sr = (xScreenCoords * 3) + yw;
- int3 xwert = int3(sr + 0, sr + 1, sr + 2) % nativeViewCount; // #### nativeViewCount->lt03
+ uint3 xwert = int3(sr + 0, sr + 1, sr + 2) % nativeViewCount; // #### nativeViewCount->lt03
// int3 xwert = modiv3g3d( int3(sr + 0, sr + 1, sr + 2), nativeViewCount); // #### nativeViewCount->lt03
// Start HQ-Renderberechnung inklusive Z-Korrektur
@@ -64,7 +64,7 @@ Shader "G3D/Autostereo"
tr2d = track;
}
- int3 mtmp = ((hviews1 - xwert) * nwinkel) + hqwert + track + mstart + zwert;
+ uint3 mtmp = ((hviews1 - xwert) * nwinkel) + hqwert + track + mstart + zwert;
xwert = hviews1 - (mtmp % hqview);
// xwert = hviews1 - modiv3g3d(mtmp, hqview);
@@ -76,8 +76,9 @@ Shader "G3D/Autostereo"
// hier wird der Farbwert des Views aus der Textur geholt und die Ausblendung realisisert
- float4 colorLeft = sampleFromView((1 + invertViews) % 2, uvCoords); // Pixeldaten linkes Bild
- float4 colorRight = sampleFromView((0 + invertViews) % 2, uvCoords); // Pixeldaten rechtes Bild
+ uint inverViewsUint = uint(invertViews);
+ float4 colorLeft = sampleFromView((1 + inverViewsUint) % 2, uvCoords); // Pixeldaten linkes Bild
+ float4 colorRight = sampleFromView((0 + inverViewsUint) % 2, uvCoords); // Pixeldaten rechtes Bild
float cor=0.0, cog=0.0, cob=0.0;
@@ -157,7 +158,6 @@ Shader "G3D/Autostereo"
{
v2f output;
UNITY_SETUP_INSTANCE_ID(input);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID);
@@ -204,7 +204,6 @@ Shader "G3D/Autostereo"
{
v2f output;
UNITY_SETUP_INSTANCE_ID(input);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID);
diff --git a/Resources/G3DShaderMultiview.shader b/Resources/G3DShaderMultiview.shader
index 60481b3..8581152 100644
--- a/Resources/G3DShaderMultiview.shader
+++ b/Resources/G3DShaderMultiview.shader
@@ -151,7 +151,6 @@ Shader "G3D/AutostereoMultiview"
{
v2f output;
UNITY_SETUP_INSTANCE_ID(input);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID);
@@ -198,7 +197,6 @@ Shader "G3D/AutostereoMultiview"
{
v2f output;
UNITY_SETUP_INSTANCE_ID(input);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID);
diff --git a/Resources/G3DShaderMultiviewMosaic.shader b/Resources/G3DShaderMultiviewMosaic.shader
index 5da69da..ef914bb 100644
--- a/Resources/G3DShaderMultiviewMosaic.shader
+++ b/Resources/G3DShaderMultiviewMosaic.shader
@@ -7,18 +7,19 @@ Shader "G3D/AutostereoMultiviewMosaic"
#include "G3D_ShaderBasics.hlsl"
// mosaic video parameters
- int mosaic_rows = 1; // number of rows in the mosaic
- int mosaic_columns = 1; // number of columns in the mosaic
+ uint mosaic_rows = 1; // number of rows in the mosaic
+ uint mosaic_columns = 1; // number of columns in the mosaic
- Texture2D mosaictexture;
- SamplerState samplermosaictexture;
+
+ Texture2D _colorMosaic;
+ SamplerState sampler_colorMosaic;
int map(int x, int in_min, int in_max, int out_min, int out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
- float2 calculateUVForMosaic(int viewIndex, float2 startingUV) {
+ float2 calculateUVForMosaic(uint viewIndex, float2 startingUV) {
if(viewIndex < 0 )
{
viewIndex = 0;
@@ -27,8 +28,8 @@ Shader "G3D/AutostereoMultiviewMosaic"
{
viewIndex = map(viewIndex, 0, nativeViewCount - 1, 0, mosaic_rows * mosaic_columns - 1);
}
- int xAxis = viewIndex % mosaic_columns;
- int yAxis = viewIndex / mosaic_columns;
+ uint xAxis = viewIndex % mosaic_columns;
+ uint yAxis = viewIndex / mosaic_columns;
// invert y axis to account for different coordinate systems between Unity and OpenGL (OpenGL has origin at bottom left)
// The shader was written for OpenGL, so we need to invert the y axis to make it work in Unity.
yAxis = mosaic_rows - 1 - yAxis;
@@ -65,17 +66,17 @@ Shader "G3D/AutostereoMultiviewMosaic"
// 250 corresponds to a black view
if(viewIndices.x != 250) {
float2 mappedUVCoords = calculateUVForMosaic(viewIndices.x, uvCoords);
- float4 tmpColorX = mosaictexture.Sample(samplermosaictexture, mappedUVCoords);
+ float4 tmpColorX = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords);
color.x = tmpColorX.x;
}
if(viewIndices.y != 250) {
float2 mappedUVCoords = calculateUVForMosaic(viewIndices.y, uvCoords);
- float4 tmpColorY = mosaictexture.Sample(samplermosaictexture, mappedUVCoords);
+ float4 tmpColorY = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords);
color.y = tmpColorY.y;
}
if(viewIndices.z != 250) {
float2 mappedUVCoords = calculateUVForMosaic(viewIndices.z, uvCoords);
- float4 tmpColorZ = mosaictexture.Sample(samplermosaictexture, mappedUVCoords);
+ float4 tmpColorZ = _colorMosaic.Sample(sampler_colorMosaic, mappedUVCoords);
color.z = tmpColorZ.z;
}
}
@@ -119,7 +120,6 @@ Shader "G3D/AutostereoMultiviewMosaic"
{
v2f output;
UNITY_SETUP_INSTANCE_ID(input);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID);
@@ -166,7 +166,6 @@ Shader "G3D/AutostereoMultiviewMosaic"
{
v2f output;
UNITY_SETUP_INSTANCE_ID(input);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.uv = GetFullScreenTriangleTexCoord(input.vertexID);
output.screenPos = GetFullScreenTriangleVertexPosition(input.vertexID);
diff --git a/Resources/G3D_ShaderBasics.hlsl b/Resources/G3D_ShaderBasics.hlsl
index 9c90521..b91fa1d 100644
--- a/Resources/G3D_ShaderBasics.hlsl
+++ b/Resources/G3D_ShaderBasics.hlsl
@@ -1,49 +1,49 @@
-int nativeViewCount; // Anzahl nativer Views
-int zwinkel; // Winkelzähler
-int nwinkel; // Winkelnenner
+uint nativeViewCount; // Anzahl nativer Views
+uint zwinkel; // Winkelzähler
+uint nwinkel; // Winkelnenner
int isleft; // links(1) oder rechts(0) geneigtes Lentikular
-int test; // Rot/Schwarz (1)ein, (0)aus
-int stest; // Streifen Rot/Schwarz (1)ein, (0)aus
-int testgap; // Breite der Lücke im Testbild
-int track; // Trackingshift
-int mstart; // Viewshift permanent Offset
-int hqview; // hqViewCount
-int hviews1; // hqview - 1
-int hviews2; // hqview / 2
-
-int bls; // black left start (start and end points of left and right "eye" window)
-int ble; // black left end
-int brs; // black right start
-int bre; // black right end
-
-int s_height; // screen height
-int v_pos_x; // horizontal viewport position
-int v_pos_y; // vertical viewport position
-int tvx; // zCorrectionValue
-int zkom; // zCompensationValue, kompensiert den Shift der durch die Z-Korrektur entsteht
+uint test; // Rot/Schwarz (1)ein, (0)aus
+uint stest; // Streifen Rot/Schwarz (1)ein, (0)aus
+uint testgap; // Breite der Lücke im Testbild
+uint track; // Trackingshift
+uint mstart; // Viewshift permanent Offset
+uint hqview; // hqViewCount
+uint hviews1; // hqview - 1
+uint hviews2; // hqview / 2
+
+uint bls; // black left start (start and end points of left and right "eye" window)
+uint ble; // black left end
+uint brs; // black right start
+uint bre; // black right end
+
+uint s_height; // screen height
+uint v_pos_x; // horizontal viewport position
+uint v_pos_y; // vertical viewport position
+uint tvx; // zCorrectionValue
+uint zkom; // zCompensationValue, kompensiert den Shift der durch die Z-Korrektur entsteht
// This shader was originally implemented for OpenGL, so we need to invert the y axis to make it work in Unity.
// to do this we need the actual viewport height
-int viewportHeight;
+uint viewportHeight;
int mirror; // 1: mirror from left to right, 0: no mirror
// unused parameters -> only here for so that this shader overlaps with the multiview shader
// amount of render targets
-int cameraCount;
-int isBGR; // 0 = RGB, 1 = BGR
+uint cameraCount;
+uint isBGR; // 0 = RGB, 1 = BGR
// unused parameters
-int bborder; // blackBorder schwarz verblendung zwischen den views?
-int bspace; // blackSpace
-int s_width; // screen width
-int blur; // je größer der Wert umso mehr wird verwischt 0-1000 sinnvoll
+uint bborder; // blackBorder schwarz verblendung zwischen den views?
+uint bspace; // blackSpace
+uint s_width; // screen width
+uint blur; // je größer der Wert umso mehr wird verwischt 0-1000 sinnvoll
-int use_hq_views; // 1: use hq views, 0: use native views
+uint use_hq_views; // 1: use hq views, 0: use native views
float index_map[256];
-int indexMapLength;
+uint indexMapLength;
struct v2f
{
@@ -51,11 +51,11 @@ struct v2f
float4 screenPos : SV_POSITION;
};
-int viewOffset = 0;
+uint viewOffset = 0;
-int finalizeViewIndex(int viewIndex)
+uint finalizeViewIndex(uint viewIndex)
{
- int result = viewIndex + viewOffset;
+ uint result = viewIndex + viewOffset;
if(use_hq_views == 1)
{
@@ -103,7 +103,7 @@ int3 getHQViewIndices(float2 screenPos)
float numHQViews = float(hqview);
int direction = isleft == 1 ? 1 : -1;
- int vc = nativeViewCount * nwinkel;
+ uint vc = nativeViewCount * nwinkel;
int stride = nwinkel;
float angle = float(zwinkel);
diff --git a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta
index b09c2a1..5d36653 100644
--- a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta
+++ b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.dll.meta
@@ -1,2 +1,27 @@
fileFormatVersion: 2
-guid: 00e77c5faaa540a419efd61fd32a5318
\ No newline at end of file
+guid: 00e77c5faaa540a419efd61fd32a5318
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta
index f999223..f272ad0 100644
--- a/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta
+++ b/Runtime/HeadTrackingLib/G3D_HeadTrackingLibrary_c.lib.meta
@@ -1,2 +1,27 @@
fileFormatVersion: 2
-guid: 103214c26ba3e8d44b0d9c8b3d746260
\ No newline at end of file
+guid: 103214c26ba3e8d44b0d9c8b3d746260
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/HeadTrackingLib/ftd2xx.dll.meta b/Runtime/HeadTrackingLib/ftd2xx.dll.meta
index aa783c5..ffd3168 100644
--- a/Runtime/HeadTrackingLib/ftd2xx.dll.meta
+++ b/Runtime/HeadTrackingLib/ftd2xx.dll.meta
@@ -1,2 +1,27 @@
fileFormatVersion: 2
-guid: 248b6bdb18cf378429710d798a04db7a
\ No newline at end of file
+guid: 248b6bdb18cf378429710d798a04db7a
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/HeadTrackingLib/opengl32sw.dll.meta b/Runtime/HeadTrackingLib/opengl32sw.dll.meta
index b74acf4..90ac4b7 100644
--- a/Runtime/HeadTrackingLib/opengl32sw.dll.meta
+++ b/Runtime/HeadTrackingLib/opengl32sw.dll.meta
@@ -1,2 +1,27 @@
fileFormatVersion: 2
-guid: dc294affd701dc44d803c218a19c820e
\ No newline at end of file
+guid: dc294affd701dc44d803c218a19c820e
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator.meta b/Runtime/ViewMapGenerator.meta
new file mode 100644
index 0000000..4864c46
--- /dev/null
+++ b/Runtime/ViewMapGenerator.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4f7808ea8eee504499536b10dd674af4
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator/bin.meta b/Runtime/ViewMapGenerator/bin.meta
new file mode 100644
index 0000000..922e4b3
--- /dev/null
+++ b/Runtime/ViewMapGenerator/bin.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0089e43dfb50a5f418ff7a2802499f90
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator/bin/viewmap_generator.dll b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll
new file mode 100644
index 0000000..89f78f9
--- /dev/null
+++ b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c6dbdf8a31407f844402c9c0e1a071a9635c06087ba2c69f245371e516b107aa
+size 32768
diff --git a/Runtime/ViewMapGenerator/bin/viewmap_generator.dll.meta b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll.meta
new file mode 100644
index 0000000..adfed63
--- /dev/null
+++ b/Runtime/ViewMapGenerator/bin/viewmap_generator.dll.meta
@@ -0,0 +1,27 @@
+fileFormatVersion: 2
+guid: 07bf56d292d711943a7c9ad03b155e9d
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator/include.meta b/Runtime/ViewMapGenerator/include.meta
new file mode 100644
index 0000000..3967c96
--- /dev/null
+++ b/Runtime/ViewMapGenerator/include.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 778c9bbae97e79c43935385a2a409311
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorLib.h b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h
new file mode 100644
index 0000000..d39fa39
--- /dev/null
+++ b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h
@@ -0,0 +1,94 @@
+#ifndef VIEWMAP_GENERATOR_H
+#define VIEWMAP_GENERATOR_H
+
+#if defined(_WIN32) && defined(VIEWMAP_GENERATOR_DLL)
+#ifdef viewmap_generator_EXPORTS
+#define VIEWMAP_GENERATOR_API __declspec(dllexport)
+#else
+#define VIEWMAP_GENERATOR_API __declspec(dllimport)
+#endif
+#else
+#define VIEWMAP_GENERATOR_API
+#endif
+
+#include "G3DMonitorTypes.h"
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ typedef enum G3DMonitor_Error {
+ G3DMONITOR_SUCCESS, // no error
+ G3DMONITOR_ERROR, // general error, call Error_GetLastError for details
+ G3DMONITOR_INVALID_PARAMETER, // invalid parameter (e.g. invalid instance)
+ G3DMONITOR_BUFFER_TOO_SMALL, // buffer is too small
+ G3DMONITOR_NOT_IMPLEMENTED, // function is not implemented
+ } G3DMonitor_Error;
+
+#define ERRORINTRO \
+ try \
+ {
+
+#define ERROROUTRO \
+ return G3DMONITOR_SUCCESS; \
+ } \
+ catch (const std::exception& e) \
+ { \
+ Error_SetLastError(e.what()); \
+ } \
+ catch (...) \
+ { \
+ Error_SetLastError("Unexpected exception"); \
+ } \
+ return G3DMONITOR_ERROR;
+
+#define G3DMONITOR_CALLTYPE /* default */
+
+ // error handling
+ VIEWMAP_GENERATOR_API void G3DMONITOR_CALLTYPE Error_SetLastError(const char* ErrorText);
+ VIEWMAP_GENERATOR_API bool G3DMONITOR_CALLTYPE Error_GetLastError(char* ErrorText, size_t* Size);
+
+ // monitor instance handling
+ VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_Create(
+ uint32_t PixelCountX, // number of pixels in x (used for full view map dimension)
+ uint32_t PixelCountY, // number of pixels in y (used for full view map dimension)
+ uint16_t ViewCount, // view count (if 0, 3d is not supported)
+ uint32_t LensWidth, // lens width (in number of pixel)
+ int32_t LensAngleCounter, // angle counter (including sign)
+ bool ViewOrderInverted, // if view order is inverted (left and right mirrored)
+ bool Rotated, // if monitor is rotated by 90° (not supported by deprecated algorithm)
+ bool FullPixel, // if only full pixel available (lens is always over full pixel; no sub pixel addressable)
+ bool BGRMode, // if red and blue channel are exchanged (display rotated by 180°)
+ void** Monitor);
+ VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_Destroy(void* Monitor);
+
+ // calculate view map from given parameters
+ // is result = G3DMONITOR_SUCCESS, view map is valid
+ // do not forget to free memory of view map, if result is G3DMONITOR_SUCCESS (calling G3DMonitor_FreeViewMap)
+ VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_BuildViewMap(
+ void* Monitor, // monitor instance (created with G3DMonitor_Create)
+ bool HQMode, // hq mode (use all views up to 250; in non-hq mode, number of views is limited to number of base views)
+ G3DViewMapAlignment ViewAlignment, // view alignment
+ G3DViewMapZeroPixel ViewZeroPixel, // zero sub pixel, if view alignment is vma_DefinedZero; z is color to use (0=red, 1=green, 2=blue)
+ bool EnlargeViewMapX, // if to repeat shrinked viewmap to full monitor resolution
+ bool EnlargeViewMapY, // if to repeat shrinked viewmap to full monitor resolution
+ bool AViewMapIsBGR, // if colors aligned blue, green, red (instead red, green, blue; e.g. used for windows bitmap)
+ bool AViewMapInvertY, // if y order is inverted (bottom line is last in memory (like windows bitmap))
+ int8_t AViewMapLinePadding, // line padding (<0=automatic for windows bitmap, 0=off, 1..n = define number of fill bytes)
+ uint8_t* ResultViewCount, // number of views used in view map (will be <= 250 in any case since 251..255 are reserved)
+ uint32_t* ResultViewMapWidth, // dimension of viewmap in pixel in x
+ uint32_t* ResultViewMapHeight, // dimension of viewmap in pixel in y
+ uint32_t* ResultViewMapSize, // size of view map (in bytes)
+ uint32_t* ResultViewMapScanLineSize, // size of single line of view map (in bytes)
+ uint8_t** ResultViewMap // result view map (memory is allocated and filled automatically; memory size of view map is ResultViewMapWidth*ResultViewMapHeight*3 (format is RGB))
+ );
+
+ // destroy view map memory and set pointer null
+ VIEWMAP_GENERATOR_API G3DMonitor_Error G3DMONITOR_CALLTYPE G3DMonitor_FreeViewMap(uint8_t** ViewMap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // VIEWMAP_GENERATOR_H
diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorLib.h.meta b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h.meta
new file mode 100644
index 0000000..fa68d37
--- /dev/null
+++ b/Runtime/ViewMapGenerator/include/G3DMonitorLib.h.meta
@@ -0,0 +1,27 @@
+fileFormatVersion: 2
+guid: 81088ae9a0a484b49bbbb16b3b3c4925
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h
new file mode 100644
index 0000000..c2fcabb
--- /dev/null
+++ b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h
@@ -0,0 +1,54 @@
+#ifndef G3DMONITORTYPES_H
+#define G3DMONITORTYPES_H
+
+#include
+#include
+
+// alignment of view map
+typedef enum G3DViewMapAlignment {
+ vma_Default, // use calculated view indizes (first left sub pixel use last view index (or 0, if view order is inverted)
+ vma_DefinedZero, // defined sub pixel will get view index 0
+ vma_CompatibleDeprecated, // compatible to deprecated algorithm (if screen is left, left pixel of last row has highest view number; if screen is right, left pixel of first row has highest view number)
+ vma_CompatibleMPV // compatible to mpv (left red subpixel in second row from top will use view 0; since there are multiple implementations, this may not work as used version of mpv; mpv use different calculation, if map is calculated shader!)
+} G3DViewMapAlignment;
+
+// zero sub pixel
+typedef struct G3DViewMapZeroPixel {
+ uint32_t x;
+ uint32_t y;
+ uint32_t z;
+} G3DViewMapZeroPixel;
+
+// pixel colors
+typedef enum G3DPixelColorChannel {
+ pc_Red,
+ pc_Green,
+ pc_Blue,
+ pc_NumColors
+} G3DPixelColorChannel;
+
+// 2d point
+typedef struct G3DPoint2DF {
+ float x;
+ float y;
+
+ G3DPoint2DF()
+ {
+ x = 0;
+ y = 0;
+ }
+
+ G3DPoint2DF(float ax, float ay)
+ {
+ x = ax;
+ y = ay;
+ }
+} G3DPoint2DF;
+
+// 2d point for pixel
+typedef struct G3DPixelPointF {
+ G3DPoint2DF& operator[](G3DPixelColorChannel i) { return data[i]; }
+ G3DPoint2DF data[pc_NumColors];
+} G3DPixelPointF;
+
+#endif // G3DMONITORTYPES_H
diff --git a/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h.meta b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h.meta
new file mode 100644
index 0000000..69cb002
--- /dev/null
+++ b/Runtime/ViewMapGenerator/include/G3DMonitorTypes.h.meta
@@ -0,0 +1,27 @@
+fileFormatVersion: 2
+guid: f8d0e289de452c045a13f833d3669c4b
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator/lib.meta b/Runtime/ViewMapGenerator/lib.meta
new file mode 100644
index 0000000..f704004
--- /dev/null
+++ b/Runtime/ViewMapGenerator/lib.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ab4811b56f0b3bc42a34f0516deefc16
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/ViewMapGenerator/lib/viewmap_generator.lib b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib
new file mode 100644
index 0000000..703f7bd
--- /dev/null
+++ b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f58b866825a1c6402a696d8bd717edeaf7a640674749ddb25e6e1abe9cf67a0f
+size 3122
diff --git a/Runtime/ViewMapGenerator/lib/viewmap_generator.lib.meta b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib.meta
new file mode 100644
index 0000000..d5bd308
--- /dev/null
+++ b/Runtime/ViewMapGenerator/lib/viewmap_generator.lib.meta
@@ -0,0 +1,27 @@
+fileFormatVersion: 2
+guid: 41c889b4543a73649aec930c891adf19
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/CalibrationProvider.cs b/Scripts/CalibrationProvider.cs
deleted file mode 100644
index 4dc3c9d..0000000
--- a/Scripts/CalibrationProvider.cs
+++ /dev/null
@@ -1,279 +0,0 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using IniParser;
-using IniParser.Model;
-using IniParser.Parser;
-using UnityEngine;
-using UnityEngine.Networking;
-
-public class CalibrationProvider
-{
- private IniData iniData;
-
- private CalibrationProvider() { }
-
- ///
- ///
- ///
- ///
- /// int return parameter can be ignored
- ///
- public static CalibrationProvider getFromURI(
- string uri,
- Func callback
- )
- {
- CalibrationProvider provider = new CalibrationProvider();
- if (uri == null || uri.Length == 0)
- {
- return provider;
- }
-
- UnityWebRequest webRequest = UnityWebRequest.Get(uri);
- UnityWebRequestAsyncOperation asyncOperation = webRequest.SendWebRequest();
-
- asyncOperation.completed += (op) =>
- {
- if (!webRequest.isDone && webRequest.result != UnityWebRequest.Result.Success)
- {
- Debug.LogError(webRequest.error);
- return;
- }
-
- string calibrationData = webRequest.downloadHandler.text;
-
- IniDataParser parser = new IniDataParser();
- provider.iniData = parser.Parse(calibrationData);
-
- callback(provider);
- };
-
- return provider;
- }
-
- public static CalibrationProvider getFromConfigFile(string calibrationFile)
- {
- CalibrationProvider provider = new CalibrationProvider();
- if (calibrationFile == null || !File.Exists(calibrationFile))
- {
- return provider;
- }
- FileIniDataParser parser = new FileIniDataParser();
- provider.iniData = parser.ReadFile(calibrationFile);
- return provider;
- }
-
- public static CalibrationProvider getFromString(string calibrationData)
- {
- CalibrationProvider provider = new CalibrationProvider();
- if (calibrationData == null || calibrationData.Length == 0)
- {
- return provider;
- }
- IniDataParser parser = new IniDataParser();
- provider.iniData = parser.Parse(calibrationData);
- return provider;
- }
-
- ///
- ///
- ///
- ///
- public G3DShaderParameters getShaderParameters()
- {
- G3DShaderParameters parameters = new G3DShaderParameters();
-
- // display parameters
-#if UNITY_IOS
- parameters.screenWidth = readOrDefault("HorizontalResolution", Screen.width);
- parameters.screenHeight = readOrDefault("VerticalResolution", Screen.height);
- parameters.leftViewportPosition = 0; //< The left position of the viewport in screen coordinates
- parameters.bottomViewportPosition = 0; //< The bottom position of the viewport in screen coordinates
-#else
- DisplayInfo mainDisplayInfo = Screen.mainWindowDisplayInfo;
- parameters.screenWidth = readOrDefault("HorizontalResolution", mainDisplayInfo.width);
- parameters.screenHeight = readOrDefault("VerticalResolution", mainDisplayInfo.height);
- parameters.leftViewportPosition = Screen.mainWindowPosition.x; //< The left position of the viewport in screen coordinates
- parameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height; //< The bottom position of the viewport in screen coordinates
-#endif
-
- // default values are those i got from the head tracking library when no camera was connected
- parameters.nativeViewCount = getInt("NativeViewcount");
- parameters.angleRatioNumerator = getInt("AngleRatioNumerator");
- parameters.angleRatioDenominator = getInt("AngleRatioDenominator");
- parameters.leftLensOrientation = getInt("LeftLensOrientation");
- parameters.BGRPixelLayout = getBool("isBGR") ? 1 : 0;
- parameters.blackBorder = getInt("BlackBorderDefault");
- parameters.blackSpace = getInt("BlackSpaceDefault");
-
- parameters.showTestFrame = 0;
- parameters.showTestStripe = 0;
- parameters.testGapWidth = 0;
-
- parameters.blur = 0; // this is based on a guess
-
- parameters.mstart = 0; // set to zero for no viewOffset
- parameters.track = 0; // set to zero for no tracking shift
-
- // lens parameters
- // This is a total guess as to how this parameter is calculated.
- parameters.hqViewCount = parameters.nativeViewCount * parameters.angleRatioDenominator;
- parameters.hviews1 = parameters.hqViewCount - 1;
- parameters.hviews2 = parameters.hqViewCount / 2;
-
- // bls and ble correspond to the start and end of the left eye view "window"
- // brs and bre correspond to the start and end of the right eye view "window"
- // my guess: the left eye takes half the views, and the right eye takes the other half
- parameters.bls = 0;
- parameters.ble = parameters.hviews2;
- parameters.brs = parameters.hviews2;
- parameters.bre = parameters.hqViewCount - 1;
-
- parameters.zCompensationValue = 0; // i got this value from the library when no camera was connected
- parameters.zCorrectionValue = 13248; // i got this value from the library when no camera was connected
-
- return parameters;
- }
-
- ///
- /// Returns the value of key from the ini file, or defaultValue if the key is not found or an error occurs.
- ///
- ///
- ///
- ///
- private int readOrDefault(string key, int defaultValue)
- {
- if (iniData == null)
- {
- return defaultValue;
- }
-
- try
- {
- string value = null;
- iniData.TryGetKey("MonitorConfiguration." + key, out value);
-
- if (value == null)
- {
- return defaultValue;
- }
- int number;
- if (int.TryParse(value, out number))
- {
- return number;
- }
-
- float floatNumber;
- if (float.TryParse(value, out floatNumber))
- {
- return (int)floatNumber;
- }
-
- bool boolValue;
- if (bool.TryParse(value, out boolValue))
- {
- return boolValue ? 1 : 0;
- }
-
- return defaultValue;
- }
- catch (System.Exception e)
- {
- Debug.LogWarning(e.Message);
- return defaultValue;
- }
- }
-
- public int getInt(string key)
- {
- if (iniData == null)
- {
- throw new System.Exception("iniData is null");
- }
-
- string value;
- iniData.TryGetKey("MonitorConfiguration." + key, out value);
-
- if (value == null)
- {
- throw new System.Exception("Key not found: " + key);
- }
- int number;
- if (int.TryParse(value, out number))
- {
- return number;
- }
- throw new System.Exception("Error reading ini file. Value is not an int: " + value);
- }
-
- public float getFloat(string key)
- {
- if (iniData == null)
- {
- throw new System.Exception("iniData is null");
- }
-
- string value;
- iniData.TryGetKey("MonitorConfiguration." + key, out value);
-
- if (value == null)
- {
- throw new System.Exception("Key not found: " + key);
- }
- float number;
- if (
- float.TryParse(
- value,
- System.Globalization.NumberStyles.Float,
- System.Globalization.CultureInfo.InvariantCulture,
- out number
- )
- )
- {
- return number;
- }
-
- throw new System.Exception("Error reading ini file. Value is not a float: " + value);
- }
-
- public string getString(string key)
- {
- if (iniData == null)
- {
- throw new System.Exception("iniData is null");
- }
-
- string value;
- iniData.TryGetKey("MonitorConfiguration." + key, out value);
-
- if (value == null)
- {
- throw new System.Exception("Key not found: " + key);
- }
-
- return value;
- }
-
- public bool getBool(string key)
- {
- if (iniData == null)
- {
- throw new System.Exception("iniData is null");
- }
-
- string value = null;
- iniData.TryGetKey("MonitorConfiguration." + key, out value);
-
- if (value == null)
- {
- throw new System.Exception("Key not found: " + key);
- }
- bool boolValue;
- if (bool.TryParse(value, out boolValue))
- {
- return boolValue;
- }
- throw new System.Exception("Error reading ini file. Value is not a bool: " + value);
- }
-}
diff --git a/Scripts/ConfigurationProvider.cs b/Scripts/ConfigurationProvider.cs
new file mode 100644
index 0000000..a711b22
--- /dev/null
+++ b/Scripts/ConfigurationProvider.cs
@@ -0,0 +1,282 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using IniParser;
+using IniParser.Model;
+using IniParser.Parser;
+using UnityEngine;
+using UnityEngine.Networking;
+
+namespace G3D
+{
+ public class ConfigurationProvider
+ {
+ private IniData iniData;
+
+ private ConfigurationProvider() { }
+
+ ///
+ ///
+ ///
+ ///
+ /// int return parameter can be ignored
+ ///
+ public static ConfigurationProvider getFromURI(
+ string uri,
+ Func callback
+ )
+ {
+ ConfigurationProvider provider = new ConfigurationProvider();
+ if (uri == null || uri.Length == 0)
+ {
+ return provider;
+ }
+
+ UnityWebRequest webRequest = UnityWebRequest.Get(uri);
+ UnityWebRequestAsyncOperation asyncOperation = webRequest.SendWebRequest();
+
+ asyncOperation.completed += (op) =>
+ {
+ if (!webRequest.isDone && webRequest.result != UnityWebRequest.Result.Success)
+ {
+ Debug.LogError(webRequest.error);
+ return;
+ }
+
+ string configurationData = webRequest.downloadHandler.text;
+
+ IniDataParser parser = new IniDataParser();
+ provider.iniData = parser.Parse(configurationData);
+
+ callback(provider);
+ };
+
+ return provider;
+ }
+
+ public static ConfigurationProvider getFromConfigFile(string configurationFile)
+ {
+ ConfigurationProvider provider = new ConfigurationProvider();
+ if (configurationFile == null || !File.Exists(configurationFile))
+ {
+ return provider;
+ }
+ FileIniDataParser parser = new FileIniDataParser();
+ provider.iniData = parser.ReadFile(configurationFile);
+ return provider;
+ }
+
+ public static ConfigurationProvider getFromString(string configurationData)
+ {
+ ConfigurationProvider provider = new ConfigurationProvider();
+ if (configurationData == null || configurationData.Length == 0)
+ {
+ return provider;
+ }
+ IniDataParser parser = new IniDataParser();
+ provider.iniData = parser.Parse(configurationData);
+ return provider;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public G3DShaderParameters getShaderParameters()
+ {
+ G3DShaderParameters parameters = new G3DShaderParameters();
+
+ // display parameters
+#if UNITY_IOS
+ parameters.screenWidth = readOrDefault("HorizontalResolution", Screen.width);
+ parameters.screenHeight = readOrDefault("VerticalResolution", Screen.height);
+ parameters.leftViewportPosition = 0; //< The left position of the viewport in screen coordinates
+ parameters.bottomViewportPosition = 0; //< The bottom position of the viewport in screen coordinates
+#else
+ DisplayInfo mainDisplayInfo = Screen.mainWindowDisplayInfo;
+ parameters.screenWidth = readOrDefault("HorizontalResolution", mainDisplayInfo.width);
+ parameters.screenHeight = readOrDefault("VerticalResolution", mainDisplayInfo.height);
+ parameters.leftViewportPosition = Screen.mainWindowPosition.x; //< The left position of the viewport in screen coordinates
+ parameters.bottomViewportPosition = Screen.mainWindowPosition.y + Screen.height; //< The bottom position of the viewport in screen coordinates
+#endif
+
+ // default values are those i got from the head tracking library when no camera was connected
+ parameters.nativeViewCount = getInt("NativeViewcount");
+ parameters.angleRatioNumerator = getInt("AngleRatioNumerator");
+ parameters.angleRatioDenominator = getInt("AngleRatioDenominator");
+ parameters.leftLensOrientation = getInt("LeftLensOrientation");
+ parameters.BGRPixelLayout = getBool("isBGR") ? 1 : 0;
+ parameters.blackBorder = getInt("BlackBorderDefault");
+ parameters.blackSpace = getInt("BlackSpaceDefault");
+
+ parameters.showTestFrame = 0;
+ parameters.showTestStripe = 0;
+ parameters.testGapWidth = 0;
+
+ parameters.blur = 0; // this is based on a guess
+
+ parameters.mstart = 0; // set to zero for no viewOffset
+ parameters.track = 0; // set to zero for no tracking shift
+
+ // lens parameters
+ // This is a total guess as to how this parameter is calculated.
+ parameters.hqViewCount = parameters.nativeViewCount * parameters.angleRatioDenominator;
+ parameters.hviews1 = parameters.hqViewCount - 1;
+ parameters.hviews2 = parameters.hqViewCount / 2;
+
+ // bls and ble correspond to the start and end of the left eye view "window"
+ // brs and bre correspond to the start and end of the right eye view "window"
+ // my guess: the left eye takes half the views, and the right eye takes the other half
+ parameters.bls = 0;
+ parameters.ble = parameters.hviews2;
+ parameters.brs = parameters.hviews2;
+ parameters.bre = parameters.hqViewCount - 1;
+
+ parameters.zCompensationValue = 0; // i got this value from the library when no camera was connected
+ parameters.zCorrectionValue = 13248; // i got this value from the library when no camera was connected
+
+ return parameters;
+ }
+
+ ///
+ /// Returns the value of key from the ini file, or defaultValue if the key is not found or an error occurs.
+ ///
+ ///
+ ///
+ ///
+ private int readOrDefault(string key, int defaultValue)
+ {
+ if (iniData == null)
+ {
+ return defaultValue;
+ }
+
+ try
+ {
+ string value = null;
+ iniData.TryGetKey("MonitorConfiguration." + key, out value);
+
+ if (value == null)
+ {
+ return defaultValue;
+ }
+ int number;
+ if (int.TryParse(value, out number))
+ {
+ return number;
+ }
+
+ float floatNumber;
+ if (float.TryParse(value, out floatNumber))
+ {
+ return (int)floatNumber;
+ }
+
+ bool boolValue;
+ if (bool.TryParse(value, out boolValue))
+ {
+ return boolValue ? 1 : 0;
+ }
+
+ return defaultValue;
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogWarning(e.Message);
+ return defaultValue;
+ }
+ }
+
+ public int getInt(string key)
+ {
+ if (iniData == null)
+ {
+ throw new System.Exception("iniData is null");
+ }
+
+ string value;
+ iniData.TryGetKey("MonitorConfiguration." + key, out value);
+
+ if (value == null)
+ {
+ throw new System.Exception("Key not found: " + key);
+ }
+ int number;
+ if (int.TryParse(value, out number))
+ {
+ return number;
+ }
+ throw new System.Exception("Error reading ini file. Value is not an int: " + value);
+ }
+
+ public float getFloat(string key)
+ {
+ if (iniData == null)
+ {
+ throw new System.Exception("iniData is null");
+ }
+
+ string value;
+ iniData.TryGetKey("MonitorConfiguration." + key, out value);
+
+ if (value == null)
+ {
+ throw new System.Exception("Key not found: " + key);
+ }
+ float number;
+ if (
+ float.TryParse(
+ value,
+ System.Globalization.NumberStyles.Float,
+ System.Globalization.CultureInfo.InvariantCulture,
+ out number
+ )
+ )
+ {
+ return number;
+ }
+
+ throw new System.Exception("Error reading ini file. Value is not a float: " + value);
+ }
+
+ public string getString(string key)
+ {
+ if (iniData == null)
+ {
+ throw new System.Exception("iniData is null");
+ }
+
+ string value;
+ iniData.TryGetKey("MonitorConfiguration." + key, out value);
+
+ if (value == null)
+ {
+ throw new System.Exception("Key not found: " + key);
+ }
+
+ return value;
+ }
+
+ public bool getBool(string key)
+ {
+ if (iniData == null)
+ {
+ throw new System.Exception("iniData is null");
+ }
+
+ string value = null;
+ iniData.TryGetKey("MonitorConfiguration." + key, out value);
+
+ if (value == null)
+ {
+ throw new System.Exception("Key not found: " + key);
+ }
+ bool boolValue;
+ if (bool.TryParse(value, out boolValue))
+ {
+ return boolValue;
+ }
+ throw new System.Exception("Error reading ini file. Value is not a bool: " + value);
+ }
+ }
+}
diff --git a/Scripts/CalibrationProvider.cs.meta b/Scripts/ConfigurationProvider.cs.meta
similarity index 100%
rename from Scripts/CalibrationProvider.cs.meta
rename to Scripts/ConfigurationProvider.cs.meta
diff --git a/Scripts/G3DHDRPCustomPass.cs b/Scripts/G3DHDRPCustomPass.cs
deleted file mode 100644
index e83ee84..0000000
--- a/Scripts/G3DHDRPCustomPass.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-#if G3D_HDRP
-using UnityEngine;
-using UnityEngine.Rendering;
-using UnityEngine.Rendering.HighDefinition;
-
-internal class G3DHDRPCustomPass : FullScreenCustomPass
-{
- protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { }
-
- protected override void Execute(CustomPassContext ctx)
- {
- var camera = ctx.hdCamera.camera;
- if (isG3DMainCamera(camera))
- {
- CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None);
- CoreUtils.DrawFullScreen(
- ctx.cmd,
- fullscreenPassMaterial,
- ctx.propertyBlock,
- shaderPassId: 0
- );
- }
- }
-
- ///
- /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set.
- /// If so returns true, otherwise returns false.
- ///
- ///
- ///
- static bool isG3DMainCamera(Camera camera)
- {
- if (camera.cameraType != CameraType.Game)
- return false;
- bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera);
- bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled; // only do something if our component is enabled
-
- bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent(
- out var mosaicCamera
- );
- bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled; // same check if it is a mosaic camera
-
- if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled)
- return false;
-
- // skip all cameras created by the G3D Camera script
- if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX))
- {
- return false;
- }
-
- return true;
- }
-
- protected override void Cleanup() { }
-}
-#endif
diff --git a/Scripts/G3DUrpScriptableRenderPass.cs b/Scripts/G3DUrpScriptableRenderPass.cs
deleted file mode 100644
index 5c3b4bd..0000000
--- a/Scripts/G3DUrpScriptableRenderPass.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-#if G3D_URP
-using Unity.Burst;
-using UnityEngine;
-using UnityEngine.Rendering;
-using UnityEngine.Rendering.RenderGraphModule;
-using UnityEngine.Rendering.Universal;
-
-internal class G3DUrpScriptableRenderPass : ScriptableRenderPass
-{
- Material m_Material;
-
- private class PassData
- {
- internal TextureHandle src;
- internal Camera camera;
- internal Material blitMaterial;
- }
-
- public G3DUrpScriptableRenderPass(Material material)
- {
- m_Material = material;
- renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
- }
-
- [System.Obsolete]
- public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
- {
- ConfigureTarget(renderingData.cameraData.renderer.cameraColorTargetHandle);
- }
-
- public void updateMaterial(Material material)
- {
- m_Material = material;
- }
-
- [System.Obsolete]
- public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
- {
- var camera = renderingData.cameraData.camera;
-
- if (performBlit(camera, m_Material))
- {
- CommandBuffer cmd = CommandBufferPool.Get();
- Blitter.BlitCameraTexture(
- cmd,
- renderingData.cameraData.renderer.cameraColorTargetHandle,
- renderingData.cameraData.renderer.cameraColorTargetHandle,
- m_Material,
- 0
- );
- context.ExecuteCommandBuffer(cmd);
- cmd.Clear();
-
- CommandBufferPool.Release(cmd);
- }
- }
-
- private void InitPassData(
- RenderGraph renderGraph,
- ContextContainer frameData,
- ref PassData passData
- )
- {
- // Fill up the passData with the data needed by the passes
-
- // UniversalResourceData contains all the texture handles used by the renderer, including the active color and depth textures
- // The active color and depth textures are the main color and depth buffers that the camera renders into
- UniversalResourceData resourceData = frameData.Get();
-
- UniversalCameraData cameraData = frameData.Get();
-
- passData.src = resourceData.activeColorTexture;
- passData.camera = cameraData.camera;
- passData.blitMaterial = m_Material;
- }
-
- ///
- /// Checks whether the camera is a G3D camera or a Mosaic Multiview camera and if the blit material has been set.
- /// If so returns true, otherwise returns false.
- ///
- ///
- ///
- static bool performBlit(Camera camera, Material blitMaterial)
- {
- if (camera.cameraType != CameraType.Game)
- return false;
- bool isG3DCamera = camera.gameObject.TryGetComponent(out var g3dCamera);
- bool isG3DCameraEnabled = isG3DCamera && g3dCamera.enabled;
-
- bool isMosaicMultiviewCamera = camera.gameObject.TryGetComponent(
- out var mosaicCamera
- );
- bool isMosaicMultiviewCameraEnabled = isMosaicMultiviewCamera && mosaicCamera.enabled;
-
- if (!isG3DCameraEnabled && !isMosaicMultiviewCameraEnabled)
- return false;
-
- if (blitMaterial == null)
- return false;
-
- // skip all cameras created by the G3D Camera script
- if (camera.name.StartsWith(G3DCamera.CAMERA_NAME_PREFIX))
- {
- return false;
- }
-
- return true;
- }
-
- static void ExecutePass(PassData data, RasterGraphContext context)
- {
- var camera = data.camera;
- if (performBlit(camera, data.blitMaterial))
- {
- // CommandBuffer cmd = CommandBufferPool.Get();
- // Blitter.BlitCameraTexture(cmd, data.src, data.src, data.blitMaterial, 0);
- // Blitter.
- // context.ExecuteCommandBuffer(cmd);
- // cmd.Clear();
- // CommandBufferPool.Release(cmd);
-
- Blitter.BlitTexture(
- context.cmd,
- data.src,
- new Vector4(1, 1, 0, 0),
- data.blitMaterial,
- 0
- );
- }
- }
-
- // This is where the renderGraph handle can be accessed.
- // Each ScriptableRenderPass can use the RenderGraph handle to add multiple render passes to the render graph
- public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
- {
- string passName = "Blit With Material";
-
- // This simple pass copies the active color texture to a new texture using a custom material. This sample is for API demonstrative purposes,
- // so the new texture is not used anywhere else in the frame, you can use the frame debugger to verify its contents.
-
- // add a raster render pass to the render graph, specifying the name and the data type that will be passed to the ExecutePass function
- using (var builder = renderGraph.AddRasterRenderPass(passName, out var passData))
- {
- // Initialize the pass data
- InitPassData(renderGraph, frameData, ref passData);
-
- // // We disable culling for this pass for the demonstrative purpose of this sampe, as normally this pass would be culled,
- // // since the destination texture is not used anywhere else
- builder.AllowPassCulling(false);
-
- // Assign the ExecutePass function to the render pass delegate, which will be called by the render graph when executing the pass
- builder.SetRenderFunc(
- (PassData data, RasterGraphContext context) => ExecutePass(data, context)
- );
- }
- }
-}
-#endif
diff --git a/Scripts/HeadTracking.meta b/Scripts/HeadTracking.meta
new file mode 100644
index 0000000..a7ba442
--- /dev/null
+++ b/Scripts/HeadTracking.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 76f510add19ac1c4ea2b15abf35ef11f
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Exceptions.meta b/Scripts/HeadTracking/Exceptions.meta
similarity index 100%
rename from Scripts/Exceptions.meta
rename to Scripts/HeadTracking/Exceptions.meta
diff --git a/Scripts/Exceptions/G3D_AlreadyInitializedException.cs b/Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs
similarity index 100%
rename from Scripts/Exceptions/G3D_AlreadyInitializedException.cs
rename to Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs
diff --git a/Scripts/Exceptions/G3D_AlreadyInitializedException.cs.meta b/Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs.meta
similarity index 100%
rename from Scripts/Exceptions/G3D_AlreadyInitializedException.cs.meta
rename to Scripts/HeadTracking/Exceptions/G3D_AlreadyInitializedException.cs.meta
diff --git a/Scripts/Exceptions/G3D_IndexOutOfRangeException.cs b/Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs
similarity index 100%
rename from Scripts/Exceptions/G3D_IndexOutOfRangeException.cs
rename to Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs
diff --git a/Scripts/Exceptions/G3D_IndexOutOfRangeException.cs.meta b/Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs.meta
similarity index 100%
rename from Scripts/Exceptions/G3D_IndexOutOfRangeException.cs.meta
rename to Scripts/HeadTracking/Exceptions/G3D_IndexOutOfRangeException.cs.meta
diff --git a/Scripts/Exceptions/G3D_NotInitializedException.cs b/Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs
similarity index 100%
rename from Scripts/Exceptions/G3D_NotInitializedException.cs
rename to Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs
diff --git a/Scripts/Exceptions/G3D_NotInitializedException.cs.meta b/Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs.meta
similarity index 100%
rename from Scripts/Exceptions/G3D_NotInitializedException.cs.meta
rename to Scripts/HeadTracking/Exceptions/G3D_NotInitializedException.cs.meta
diff --git a/Scripts/HeadTracking/HeadTrackingPretender.cs b/Scripts/HeadTracking/HeadTrackingPretender.cs
new file mode 100644
index 0000000..2954e3a
--- /dev/null
+++ b/Scripts/HeadTracking/HeadTrackingPretender.cs
@@ -0,0 +1,39 @@
+using UnityEngine;
+
+namespace G3D
+{
+ ///
+ /// This class can be used to simulate head tracking if no head tracking device is available.
+ /// Simply attach it to a GameObject and move the GameObject around to simulate head tracking.
+ /// The game objects transform will be used to simulate the head position.
+ /// (In meter -> 0.7 z equals 70 centimiter from the head tracking camera)
+ ///
+ public class HeadTrackingPretender : MonoBehaviour
+ {
+ public G3DCamera g3dCamera;
+ public bool headDetected = true;
+
+ public float initialOffsetZ = 0.7f;
+
+ // Start is called before the first frame update
+ void Start() { }
+
+ // Update is called once per frame
+ void Update()
+ {
+ Vector3 headPosition = transform.localPosition;
+ headPosition = -headPosition;
+ headPosition = headPosition * 1000;
+ headPosition.z += initialOffsetZ * 1000;
+ ((ITNewHeadPositionCallback)g3dCamera.headtrackingConnection).NewHeadPositionCallback(
+ headDetected,
+ true,
+ (int)headPosition.x,
+ (int)headPosition.y,
+ headPosition.x,
+ headPosition.y,
+ headPosition.z
+ );
+ }
+ }
+}
diff --git a/Scripts/HeadTrackingPretender.cs.meta b/Scripts/HeadTracking/HeadTrackingPretender.cs.meta
similarity index 100%
rename from Scripts/HeadTrackingPretender.cs.meta
rename to Scripts/HeadTracking/HeadTrackingPretender.cs.meta
diff --git a/Scripts/HeadTracking/HeadtrackingConnection.cs b/Scripts/HeadTracking/HeadtrackingConnection.cs
new file mode 100644
index 0000000..60272fb
--- /dev/null
+++ b/Scripts/HeadTracking/HeadtrackingConnection.cs
@@ -0,0 +1,756 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using UnityEngine;
+
+namespace G3D
+{
+ public class HeadtrackingConnection
+ : ITNewHeadPositionCallback,
+ ITNewShaderParametersCallback,
+ ITNewErrorMessageCallback
+ {
+ public float headLostTimeoutInSec = 3.0f;
+
+ public float transitionDuration = 0.5f;
+
+ public Vector3Int headPositionFilter = new Vector3Int(5, 5, 5);
+ public LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE;
+
+ public float basicWorkingDistance = 1.0f;
+ public float headTrackingSensitivity = 1.0f;
+ private bool debugMessages;
+
+ private Vector3 lastHeadPosition = new Vector3(0, 0, 0);
+
+ private float headLostTimer = 0.0f;
+ private float transitionTime = 0.0f;
+
+ private enum HeadTrackingState
+ {
+ TRACKING,
+ LOST,
+ TRANSITIONTOLOST,
+ TRANSITIONTOTRACKING,
+ LOSTGRACEPERIOD
+ }
+
+ private HeadTrackingState prevHeadTrackingState = HeadTrackingState.LOST;
+
+ private LibInterface libInterface;
+ private string calibrationPath;
+
+ ///
+ /// This struct is used to store the current head position.
+ /// It is updated in a different thread, so always use getHeadPosition() to get the current head position.
+ /// NEVER use headPosition directly.
+ ///
+ private HeadPosition headPosition;
+ private HeadPosition filteredHeadPosition;
+
+ private static object headPosLock = new object();
+
+ private Queue headPositionLog;
+
+ private G3DCamera g3dCamera;
+
+ public HeadtrackingConnection(
+ float basicWorkingDistance,
+ float headTrackingSensitivity,
+ string configurationPathOverwrite,
+ G3DCamera g3dCamera,
+ bool debugMessages = false,
+ Vector3Int headPositionFilter = new Vector3Int(),
+ LatencyCorrectionMode latencyCorrectionMode = LatencyCorrectionMode.LCM_SIMPLE
+ )
+ {
+ lastHeadPosition = new Vector3(0, 0, -basicWorkingDistance);
+ this.basicWorkingDistance = basicWorkingDistance;
+ this.headTrackingSensitivity = headTrackingSensitivity;
+
+ calibrationPath = System.Environment.GetFolderPath(
+ Environment.SpecialFolder.CommonDocuments
+ );
+ calibrationPath = Path.Combine(calibrationPath, "3D Global", "calibrations");
+ if (!string.IsNullOrEmpty(configurationPathOverwrite))
+ {
+ calibrationPath = configurationPathOverwrite;
+ }
+
+ this.debugMessages = debugMessages;
+ this.headPositionFilter = headPositionFilter;
+ this.latencyCorrectionMode = latencyCorrectionMode;
+
+ this.g3dCamera = g3dCamera;
+
+ headPositionLog = new Queue(10000);
+ }
+
+ public void startHeadTracking()
+ {
+ try
+ {
+ libInterface.startHeadTracking();
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to start head tracking: " + e.Message);
+ }
+ }
+
+ public void shiftViewToLeft()
+ {
+ try
+ {
+ libInterface.shiftViewToLeft();
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to shift view to left: " + e.Message);
+ }
+ }
+
+ public void shiftViewToRight()
+ {
+ try
+ {
+ libInterface.shiftViewToRight();
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to shift view to right: " + e.Message);
+ }
+ }
+
+ public void toggleHeadTracking()
+ {
+ if (libInterface == null || !libInterface.isInitialized())
+ {
+ return;
+ }
+
+ try
+ {
+ HeadTrackingStatus headtrackingConnection = libInterface.getHeadTrackingStatus();
+ if (headtrackingConnection.hasTrackingDevice)
+ {
+ if (!headtrackingConnection.isTrackingActive)
+ {
+ libInterface.startHeadTracking();
+ }
+ else
+ {
+ libInterface.stopHeadTracking();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to toggle head tracking status: " + e.Message);
+ }
+ }
+
+ ///
+ /// always use this method to get the current head position.
+ /// NEVER access headPosition directly, as it is updated in a different thread.
+ ///
+ ///
+ ///
+ public HeadPosition getHeadPosition()
+ {
+ HeadPosition currentHeadPosition;
+ lock (headPosLock)
+ {
+ if (usePositionFiltering())
+ {
+ currentHeadPosition = filteredHeadPosition;
+ }
+ else
+ {
+ currentHeadPosition = headPosition;
+ }
+ }
+ return currentHeadPosition;
+ }
+
+ /**
+ * Calculate the new camera center position based on the head tracking.
+ * If head tracking is lost, or the head moves to far away from the tracking camera a grace periope is started.
+ * Afterwards the camera center will be animated back towards the default position.
+ */
+ public void handleHeadTrackingState(
+ ref Vector3 targetPosition,
+ ref float targetViewSeparation,
+ float viewSeparation,
+ float currentFocusDistance
+ )
+ {
+ HeadPosition headPos = getHeadPosition();
+ headPos.worldPosZ = convertWorldPosZToTrackedPosition(
+ (float)headPos.worldPosZ,
+ currentFocusDistance
+ );
+ // get new state
+ HeadTrackingState newState = getNewTrackingState(prevHeadTrackingState, ref headPos);
+
+ prevHeadTrackingState = newState;
+
+ // handle lost state
+ if (newState == HeadTrackingState.LOST)
+ {
+ targetPosition = new Vector3(0, 0, -currentFocusDistance);
+ targetViewSeparation = 0.0f;
+ }
+ // handle tracking state
+ else if (newState == HeadTrackingState.TRACKING)
+ {
+ Vector3 headPositionWorld = new Vector3(
+ (float)headPos.worldPosX,
+ (float)headPos.worldPosY,
+ (float)headPos.worldPosZ
+ );
+
+ targetPosition = headPositionWorld;
+ targetViewSeparation = viewSeparation;
+ lastHeadPosition = targetPosition;
+ }
+ // if lost, start grace period
+ else if (newState == HeadTrackingState.LOSTGRACEPERIOD)
+ {
+ // if we have waited for the timeout
+ if (headLostTimer > headLostTimeoutInSec)
+ {
+ newState = HeadTrackingState.TRANSITIONTOLOST;
+ headLostTimer = 0.0f;
+ transitionTime = 0.0f;
+ }
+ else
+ {
+ headLostTimer += Time.deltaTime;
+ targetPosition = lastHeadPosition;
+ targetViewSeparation = viewSeparation;
+ }
+ }
+ // handle transitions
+ else if (
+ newState == HeadTrackingState.TRANSITIONTOLOST
+ || newState == HeadTrackingState.TRANSITIONTOTRACKING
+ )
+ {
+ // init with values for transition to lost
+ Vector3 originPosition = lastHeadPosition;
+ Vector3 transitionTargetPosition = new Vector3(0, 0, -currentFocusDistance);
+ float transitionViewSeparation = 0.0f;
+ float originSeparation = viewSeparation;
+
+ if (newState == HeadTrackingState.TRANSITIONTOTRACKING)
+ {
+ originPosition = new Vector3(0, 0, -basicWorkingDistance);
+ transitionViewSeparation = viewSeparation;
+ originSeparation = 0.0f;
+
+ if (headPos.headDetected)
+ {
+ Vector3 headPositionWorld = new Vector3(
+ (float)headPos.worldPosX,
+ (float)headPos.worldPosY,
+ (float)headPos.worldPosZ
+ );
+ headPositionWorld.z = convertWorldPosZToTrackedPosition(
+ headPositionWorld.z,
+ currentFocusDistance
+ );
+ transitionTargetPosition = headPositionWorld;
+ }
+ else
+ {
+ // if no head is detected use last known head position
+ transitionTargetPosition = lastHeadPosition;
+ }
+ }
+
+ bool isEndReached = handleTransition(
+ originPosition,
+ transitionTargetPosition,
+ originSeparation,
+ transitionViewSeparation,
+ ref targetPosition,
+ ref targetViewSeparation
+ );
+ if (isEndReached)
+ {
+ transitionTime = 0.0f;
+ // if we have reached the target position, we are no longer in transition
+ if (newState == HeadTrackingState.TRANSITIONTOLOST)
+ {
+ newState = HeadTrackingState.LOST;
+ }
+ else
+ {
+ newState = HeadTrackingState.TRACKING;
+ }
+ }
+ }
+
+ // reset lost timer if we are not in grace period
+ if (newState != HeadTrackingState.LOSTGRACEPERIOD)
+ {
+ headLostTimer = 0.0f;
+ }
+
+ prevHeadTrackingState = newState;
+ }
+
+ public void initLibrary()
+ {
+ string applicationName = Application.productName;
+ if (string.IsNullOrEmpty(applicationName))
+ {
+ applicationName = "Unity";
+ }
+ var invalids = System.IO.Path.GetInvalidFileNameChars();
+ applicationName = String
+ .Join("_", applicationName.Split(invalids, StringSplitOptions.RemoveEmptyEntries))
+ .TrimEnd('.');
+ applicationName = applicationName + "_G3D_Config.ini";
+
+ try
+ {
+ bool useHimaxD2XXDevices = true;
+ bool useHimaxRP2040Devices = true;
+ bool usePmdFlexxDevices = true;
+
+ libInterface = LibInterface.Instance;
+ libInterface.init(
+ calibrationPath,
+ Application.persistentDataPath,
+ applicationName,
+ this,
+ this,
+ this,
+ debugMessages,
+ useHimaxD2XXDevices,
+ useHimaxRP2040Devices,
+ usePmdFlexxDevices
+ );
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to initialize library: " + e.Message);
+ return;
+ }
+
+ // set initial values
+ // intialize head position at focus distance from focus plane
+ headPosition = new HeadPosition
+ {
+ headDetected = false,
+ imagePosIsValid = false,
+ imagePosX = 0,
+ imagePosY = 0,
+ worldPosX = 0.0,
+ worldPosY = 0.0,
+ worldPosZ = -basicWorkingDistance
+ };
+ filteredHeadPosition = new HeadPosition
+ {
+ headDetected = false,
+ imagePosIsValid = false,
+ imagePosX = 0,
+ imagePosY = 0,
+ worldPosX = 0.0,
+ worldPosY = 0.0,
+ worldPosZ = -basicWorkingDistance
+ };
+
+ if (usePositionFiltering())
+ {
+ try
+ {
+ libInterface.initializePositionFilter(
+ headPositionFilter.x,
+ headPositionFilter.y,
+ headPositionFilter.z
+ );
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to initialize position filter: " + e.Message);
+ }
+ }
+ }
+
+ public void deinitLibrary()
+ {
+ if (libInterface == null || !libInterface.isInitialized())
+ {
+ return;
+ }
+
+ try
+ {
+ libInterface.stopHeadTracking();
+ libInterface.unregisterHeadPositionChangedCallback(this);
+ libInterface.unregisterShaderParametersChangedCallback(this);
+ libInterface.unregisterMessageCallback(this);
+ libInterface.deinit();
+ }
+ catch (Exception e)
+ {
+ Debug.Log(e);
+ }
+ }
+
+ public void logCameraPositionsToFile()
+ {
+ StreamWriter writer = new StreamWriter(
+ Application.dataPath + "/HeadPositionLog.csv",
+ false
+ );
+ writer.WriteLine(
+ "Camera update time; Camera X; Camera Y; Camera Z; Head detected; Image position valid; Filtered X; Filtered Y; Filtered Z"
+ );
+ string[] headPoitionLogArray = headPositionLog.ToArray();
+ for (int i = 0; i < headPoitionLogArray.Length; i++)
+ {
+ writer.WriteLine(headPoitionLogArray[i]);
+ }
+ writer.Close();
+ }
+
+ public void calculateShaderParameters()
+ {
+ libInterface.calculateShaderParameters(latencyCorrectionMode);
+ g3dCamera.setShaderParameters(libInterface.getCurrentShaderParameters());
+ }
+
+ public void updateScreenViewportProperties(Vector2Int displayResolution)
+ {
+ try
+ {
+ // This is the size of the entire monitor screen
+ libInterface.setScreenSize(displayResolution.x, displayResolution.y);
+
+ // this refers to the window in which the 3D effect is rendered (including eg windows top window menu)
+ libInterface.setWindowSize(Screen.width, Screen.height);
+ libInterface.setWindowPosition(
+ Screen.mainWindowPosition.x,
+ Screen.mainWindowPosition.y
+ );
+
+ // This refers to the actual viewport in which the 3D effect is rendered
+ libInterface.setViewportSize(Screen.width, Screen.height);
+ libInterface.setViewportOffset(0, 0);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to update screen viewport properties: " + e.Message);
+ }
+ }
+
+ public void setBasicWorkingDistance(float distance)
+ {
+ basicWorkingDistance = distance;
+ }
+
+ ///
+ /// returns true if transition end is reached
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// is transition end reached
+ private bool handleTransition(
+ Vector3 originPosition,
+ Vector3 transitionTargetPosition,
+ float originSeparation,
+ float transitionViewSeparation,
+ ref Vector3 targetPosition,
+ ref float targetViewSeparation
+ )
+ {
+ // interpolate values
+ float transitionPercentage = transitionTime / transitionDuration;
+ transitionTime += Time.deltaTime;
+
+ // set to default position
+ Vector3 interpolatedPosition = Vector3.Lerp(
+ originPosition,
+ transitionTargetPosition,
+ transitionPercentage
+ );
+ float interpolatedEyeSeparation = Mathf.Lerp(
+ originSeparation,
+ transitionViewSeparation,
+ transitionPercentage
+ );
+
+ // check if end is reached (with a small tolerance)
+ if (transitionPercentage + 0.01f < 1.0f)
+ {
+ // apply values
+ targetPosition = interpolatedPosition;
+ targetViewSeparation = interpolatedEyeSeparation;
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Converts the input position (in real world head space) to a position relative to the input focus distance.
+ ///
+ ///
+ ///
+ ///
+ private float convertWorldPosZToTrackedPosition(float worldPosZ, float focusDistance)
+ {
+ // convert from mm to m and apply scale factor
+ float zOffset = basicWorkingDistance + worldPosZ; // worldposZ is negative when in front of the camera, so we add it to the basic working distance
+ float convertedZ = focusDistance - zOffset;
+ return -convertedZ; // invert to be in right coordinate system for camera position
+ }
+
+ private HeadTrackingState getNewTrackingState(
+ HeadTrackingState currentHeadTrackingState,
+ ref HeadPosition headPosition
+ )
+ {
+ HeadTrackingState newState;
+
+ if (headPosition.headDetected)
+ {
+ // if head detected
+ if (currentHeadTrackingState == HeadTrackingState.TRACKING)
+ {
+ newState = HeadTrackingState.TRACKING;
+ }
+ else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD)
+ {
+ newState = HeadTrackingState.TRACKING;
+ }
+ else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING)
+ {
+ newState = HeadTrackingState.TRANSITIONTOTRACKING;
+ }
+ else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST)
+ {
+ newState = HeadTrackingState.TRANSITIONTOLOST;
+ }
+ else //(currentHeadTrackingState == HeadTrackingState.LOST)
+ {
+ newState = HeadTrackingState.TRANSITIONTOTRACKING;
+ }
+ }
+ else
+ {
+ // if head not detected
+ if (currentHeadTrackingState == HeadTrackingState.TRACKING)
+ {
+ newState = HeadTrackingState.LOSTGRACEPERIOD;
+ }
+ else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD)
+ {
+ newState = HeadTrackingState.LOSTGRACEPERIOD;
+ }
+ else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING)
+ {
+ newState = HeadTrackingState.TRANSITIONTOTRACKING;
+ }
+ else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST)
+ {
+ newState = HeadTrackingState.TRANSITIONTOLOST;
+ }
+ else //(currentHeadTrackingState == HeadTrackingState.LOST)
+ {
+ newState = HeadTrackingState.LOST;
+ }
+ }
+ return newState;
+ }
+
+ private string headTrackingStateToString()
+ {
+ switch (prevHeadTrackingState)
+ {
+ case HeadTrackingState.TRACKING:
+ return "TRACKING";
+ case HeadTrackingState.LOST:
+ return "LOST";
+ case HeadTrackingState.LOSTGRACEPERIOD:
+ return "LOSTGRACEPERIOD";
+ case HeadTrackingState.TRANSITIONTOLOST:
+ return "TRANSITIONTOLOST";
+ case HeadTrackingState.TRANSITIONTOTRACKING:
+ return "TRANSITIONTOTRACKING";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ #region callback handling
+ void ITNewHeadPositionCallback.NewHeadPositionCallback(
+ bool headDetected,
+ bool imagePosIsValid,
+ int imagePosX,
+ int imagePosY,
+ double worldPosX,
+ double worldPosY,
+ double worldPosZ
+ )
+ {
+ lock (headPosLock)
+ {
+ string logEntry =
+ DateTime.Now.ToString("HH:mm::ss.fff")
+ + ";"
+ + worldPosX
+ + ";"
+ + worldPosY
+ + ";"
+ + worldPosZ
+ + ";"
+ + headDetected
+ + ";"
+ + imagePosIsValid
+ + ";";
+
+ headPosition.headDetected = headDetected;
+ headPosition.imagePosIsValid = imagePosIsValid;
+
+ int millimeterToMeter = 1000;
+
+ Vector3 headPos = new Vector3(
+ (float)-worldPosX / millimeterToMeter,
+ (float)worldPosY / millimeterToMeter,
+ (float)-worldPosZ / millimeterToMeter
+ );
+
+ int scaleFactorInt = (int)headTrackingSensitivity;
+
+ headPosition.imagePosX = imagePosX / (int)millimeterToMeter * scaleFactorInt;
+ headPosition.imagePosY = imagePosY / (int)millimeterToMeter * scaleFactorInt;
+ headPosition.worldPosX = headPos.x * headTrackingSensitivity;
+ headPosition.worldPosY = headPos.y * headTrackingSensitivity;
+ headPosition.worldPosZ = headPos.z * headTrackingSensitivity;
+
+ if (usePositionFiltering())
+ {
+ double filteredPositionX;
+ double filteredPositionY;
+ double filteredPositionZ;
+
+ if (headDetected)
+ {
+ try
+ {
+ libInterface.applyPositionFilter(
+ worldPosX,
+ worldPosY,
+ worldPosZ,
+ out filteredPositionX,
+ out filteredPositionY,
+ out filteredPositionZ
+ );
+
+ filteredHeadPosition.worldPosX =
+ -filteredPositionX / millimeterToMeter * headTrackingSensitivity;
+ filteredHeadPosition.worldPosY =
+ filteredPositionY / millimeterToMeter * headTrackingSensitivity;
+ filteredHeadPosition.worldPosZ =
+ -filteredPositionZ / millimeterToMeter * headTrackingSensitivity;
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Failed to apply position filter: " + e.Message);
+ }
+ }
+
+ filteredHeadPosition.headDetected = headDetected;
+ filteredHeadPosition.imagePosIsValid = imagePosIsValid;
+
+ logEntry +=
+ filteredHeadPosition.worldPosX
+ + ";"
+ + filteredHeadPosition.worldPosY
+ + ";"
+ + filteredHeadPosition.worldPosZ
+ + ";";
+ }
+
+ headPositionLog.Enqueue(logEntry);
+ }
+ }
+
+ void ITNewErrorMessageCallback.NewErrorMessageCallback(
+ EMessageSeverity severity,
+ string sender,
+ string caption,
+ string cause,
+ string remedy
+ )
+ {
+ string messageText = formatErrorMessage(caption, cause, remedy);
+ switch (severity)
+ {
+ case EMessageSeverity.MS_EXCEPTION:
+ Debug.LogError(messageText);
+ break;
+ case EMessageSeverity.MS_ERROR:
+ Debug.LogError(messageText);
+ break;
+ case EMessageSeverity.MS_WARNING:
+ Debug.LogWarning(messageText);
+ break;
+ case EMessageSeverity.MS_INFO:
+
+ Debug.Log(messageText);
+ break;
+ default:
+ Debug.Log(messageText);
+ break;
+ }
+ }
+
+ ///
+ /// The shader parameters contain everything necessary for the shader to render the 3D effect.
+ /// These are updated every time a new head position is received.
+ /// They do not update the head position itself.
+ ///
+ ///
+ void ITNewShaderParametersCallback.NewShaderParametersCallback(
+ G3DShaderParameters shaderParameters
+ )
+ {
+ g3dCamera.setShaderParameters(shaderParameters);
+ }
+
+ private string formatErrorMessage(string caption, string cause, string remedy)
+ {
+ string messageText = caption + ": " + cause;
+
+ if (string.IsNullOrEmpty(remedy) == false)
+ {
+ messageText = messageText + "\n" + remedy;
+ }
+
+ return messageText;
+ }
+ #endregion
+
+ ///
+ /// Returns false if all values of the position filter are set to zero.
+ ///
+ ///
+ private bool usePositionFiltering()
+ {
+ return headPositionFilter.x != 0
+ || headPositionFilter.y != 0
+ || headPositionFilter.z != 0;
+ }
+ }
+}
diff --git a/Scripts/HeadTracking/HeadtrackingConnection.cs.meta b/Scripts/HeadTracking/HeadtrackingConnection.cs.meta
new file mode 100644
index 0000000..f474a10
--- /dev/null
+++ b/Scripts/HeadTracking/HeadtrackingConnection.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 14b0027f55e52ab43b31007b4669e221
\ No newline at end of file
diff --git a/Scripts/Interfaces.meta b/Scripts/HeadTracking/Interfaces.meta
similarity index 100%
rename from Scripts/Interfaces.meta
rename to Scripts/HeadTracking/Interfaces.meta
diff --git a/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs
new file mode 100644
index 0000000..829d503
--- /dev/null
+++ b/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs
@@ -0,0 +1,21 @@
+namespace G3D
+{
+ public delegate void TNewErrorMessageCallback(
+ EMessageSeverity severity,
+ string sender,
+ string caption,
+ string cause,
+ string remedy
+ );
+
+ public interface ITNewErrorMessageCallback
+ {
+ public void NewErrorMessageCallback(
+ EMessageSeverity severity,
+ string sender,
+ string caption,
+ string cause,
+ string remedy
+ );
+ }
+}
diff --git a/Scripts/Interfaces/ITNewErrorMessageCallback.cs.meta b/Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs.meta
similarity index 100%
rename from Scripts/Interfaces/ITNewErrorMessageCallback.cs.meta
rename to Scripts/HeadTracking/Interfaces/ITNewErrorMessageCallback.cs.meta
diff --git a/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs
new file mode 100644
index 0000000..8f7fb2b
--- /dev/null
+++ b/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs
@@ -0,0 +1,25 @@
+namespace G3D
+{
+ public delegate void TNewHeadPositionCallback(
+ bool headDetected,
+ bool imagePosIsValid,
+ int imagePosX,
+ int imagePosY,
+ double worldPosX,
+ double worldPosY,
+ double worldPosZ
+ );
+
+ public interface ITNewHeadPositionCallback
+ {
+ public void NewHeadPositionCallback(
+ bool headDetected,
+ bool imagePosIsValid,
+ int imagePosX,
+ int imagePosY,
+ double worldPosX,
+ double worldPosY,
+ double worldPosZ
+ );
+ }
+}
diff --git a/Scripts/Interfaces/ITNewHeadPositionCallback.cs.meta b/Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs.meta
similarity index 100%
rename from Scripts/Interfaces/ITNewHeadPositionCallback.cs.meta
rename to Scripts/HeadTracking/Interfaces/ITNewHeadPositionCallback.cs.meta
diff --git a/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs b/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs
new file mode 100644
index 0000000..c8d267a
--- /dev/null
+++ b/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs
@@ -0,0 +1,9 @@
+namespace G3D
+{
+ public delegate void TNewShaderParametersCallback(G3DShaderParameters shaderParameters);
+
+ public interface ITNewShaderParametersCallback
+ {
+ public void NewShaderParametersCallback(G3DShaderParameters shaderParameters);
+ }
+}
diff --git a/Scripts/Interfaces/ITNewShaderParametersCallback.cs.meta b/Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs.meta
similarity index 100%
rename from Scripts/Interfaces/ITNewShaderParametersCallback.cs.meta
rename to Scripts/HeadTracking/Interfaces/ITNewShaderParametersCallback.cs.meta
diff --git a/Scripts/HeadTracking/LibInterface.cs b/Scripts/HeadTracking/LibInterface.cs
new file mode 100644
index 0000000..d6ac765
--- /dev/null
+++ b/Scripts/HeadTracking/LibInterface.cs
@@ -0,0 +1,2207 @@
+using System;
+using System.Runtime.InteropServices;
+using UnityEngine;
+
+namespace G3D
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void TNewShaderParametersCallbackInternal(
+ in G3DShaderParameters shaderParameters,
+ IntPtr listener
+ );
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void TNewHeadPositionCallbackInternal(
+ int headDetected,
+ int imagePosIsValid,
+ int imagePosX,
+ int imagePosY,
+ double worldPosX,
+ double worldPosY,
+ double worldPosZ,
+ IntPtr listener
+ );
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void TNewErrorMessageCallbackInternal(
+ EMessageSeverity severity,
+ IntPtr sender,
+ IntPtr caption,
+ IntPtr cause,
+ IntPtr remedy,
+ IntPtr listener
+ );
+
+ public enum EMessageSeverity
+ {
+ MS_INFO = 1,
+ MS_WARNING,
+ MS_ERROR,
+ MS_EXCEPTION
+ };
+
+ public enum LatencyCorrectionMode
+ {
+ LCM_SIMPLE = 0, //< Simple or No LatencyCorrection (position prediction) - without timestamp from render event / paintGL
+ LCM_EXTENDED = 1, //< Extended prediction - considering timestamp from render event / paintGL
+ }
+
+ public struct G3DShaderParameters
+ {
+ // Viewport properties
+ public int leftViewportPosition; //< The left position of the viewport in screen coordinates
+ public int bottomViewportPosition; //< The bottom position of the viewport in screen coordinates
+
+ // Monitor properties
+ public int screenWidth; //< The screen width in pixels
+ public int screenHeight; //< The screen height in pixels
+
+ public int nativeViewCount; // OLD: viewcount
+ public int angleRatioNumerator; // OLD: zwinkel
+ public int angleRatioDenominator; // OLD: nwinkel
+ public int leftLensOrientation; // OLD: isleft
+ public int BGRPixelLayout; // OLD: isbgr
+
+ public int mstart; // TODO: rename to viewOffset
+ public int showTestFrame; // OLD: test
+ public int showTestStripe; // OLD: stest
+ public int testGapWidth; // OLD: testgap
+ public int track;
+ public int hqViewCount; // OLD: hqview
+ public int hviews1;
+ public int hviews2;
+ public int blur;
+ public int blackBorder; // OLD: bborder
+ public int blackSpace; // OLD: bspace
+ public int bls;
+ public int ble;
+ public int brs;
+ public int bre;
+
+ public int zCorrectionValue; // OLD: tvx
+ public int zCompensationValue; // OLD: zkom
+ };
+
+ public struct HeadTrackingStatus
+ {
+ public bool hasTrackingDevice;
+ public bool isTrackingActive;
+ }
+
+ public sealed class LibInterface
+ {
+ public bool logToConsole = true;
+ private TNewHeadPositionCallbackInternal newHeadPosCppCallback;
+ private TNewShaderParametersCallbackInternal newShaderparamsCppCallback;
+ TNewErrorMessageCallbackInternal newErrorMessagesCppCallback;
+
+ private static readonly LibInterface internalInstance = new LibInterface();
+
+ // Explicit static constructor to tell C# compiler
+ // not to mark type as beforefieldinit
+ static LibInterface() { }
+
+ private LibInterface()
+ {
+ newHeadPosCppCallback = new TNewHeadPositionCallbackInternal(
+ TranslateNewHeadPositionCallback
+ );
+
+ newShaderparamsCppCallback = new TNewShaderParametersCallbackInternal(
+ TranslateShaderParametersCallback
+ );
+
+ newErrorMessagesCppCallback = new TNewErrorMessageCallbackInternal(
+ TranslateNewErrorMessageCallback
+ );
+ }
+
+ public static LibInterface Instance
+ {
+ get { return internalInstance; }
+ }
+
+ private bool initialized = false;
+
+ ///
+ /// Initializes the G3D head tracking library. If it is already initialized, this function only registers the callbacks.
+ /// If the library is already initialized, this function will not set the useHimaxD2XXDevice and usePmdFlexxDevice Flag, neither the logToConsole flag.
+ ///
+ /// This function has to be called before any other function of this class is called.
+ ///
+ /// This function does not start the head tracking. To start the head tracking, call startHeadTracking().
+ ///
+ /// You do not have to call the destructor manually. It is called, when the last reference to the singeleton is removed.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void init(
+ string calibrationPath,
+ string configPath,
+ string configFileName,
+ in ITNewHeadPositionCallback newHeadInferfaceInstance,
+ in ITNewShaderParametersCallback shaderInterfaceInstance,
+ in ITNewErrorMessageCallback errorInterfaceInstance,
+ bool logToConsole = true,
+ bool useHimaxD2XXDevice = true,
+ bool useHimaxRP2040Device = true,
+ bool usePmdFlexxDevice = true
+ )
+ {
+ // register callbacks in case the library is already initialized
+ // afterwards, return
+ if (initialized)
+ {
+ try
+ {
+ registerHeadPositionChangedCallback(newHeadInferfaceInstance);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+ try
+ {
+ registerShaderParametersChangedCallback(shaderInterfaceInstance);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+ try
+ {
+ registerMessageCallback(errorInterfaceInstance);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+
+ Debug.Log("G3D head tracking library is already initialized.");
+ return;
+ }
+
+ this.logToConsole = logToConsole;
+
+ if (calibrationPath == "" || configPath == "" || configFileName == "")
+ {
+ throw new System.Exception(
+ "G3D head tracking library: Configuration paths have to be set. Aborting initialization."
+ );
+ }
+
+ try
+ {
+ initLibrary();
+ }
+ catch (G3D_AlreadyInitializedException)
+ {
+ Debug.Log("G3D head tracking library has already been initialized.");
+ }
+ catch (Exception e)
+ {
+ throw e;
+ }
+
+ try
+ {
+ setCalibrationPath(calibrationPath);
+ setConfigPath(configPath);
+ setConfigFileName(configFileName);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ deinitLibrary();
+ throw new System.Exception(
+ "G3D head tracking library: an error occured when setting the calibration paths. Aborting initialization"
+ );
+ }
+
+ // register callbacks
+ try
+ {
+ registerHeadPositionChangedCallback(newHeadInferfaceInstance);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+ try
+ {
+ registerShaderParametersChangedCallback(shaderInterfaceInstance);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+ try
+ {
+ registerMessageCallback(errorInterfaceInstance);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+
+ try
+ {
+ if (useHimaxD2XXDevice)
+ {
+ useHimaxD2XXDevices();
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+ try
+ {
+ if (useHimaxRP2040Device)
+ {
+ useHimaxRP2040Devices();
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+ try
+ {
+ if (usePmdFlexxDevice)
+ {
+ usePmdFlexxDevices();
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+
+ try
+ {
+ initHeadTracking();
+ }
+ catch (G3D_AlreadyInitializedException)
+ {
+ Debug.Log("G3D head tracking has already been initialized.");
+ }
+ catch (Exception e)
+ {
+ throw e;
+ }
+ Debug.Log("tracking device count " + getHeadTrackingDeviceCount());
+
+ initialized = true;
+ }
+
+ public void deinit()
+ {
+ // this is in its own try catch to continue deinit if this fails
+ try
+ {
+ stopHeadTracking();
+ }
+ catch (Exception e)
+ {
+ // only log this if actually initialized.
+ // otherwise this was probably called from an uninitialized state and the exception reflects that.
+ if (initialized)
+ {
+ Debug.LogError(e.ToString());
+ }
+ }
+
+ // this is in its own try catch to continue deinit if this fails
+ try
+ {
+ deinitHeadTracking();
+ }
+ catch (Exception e)
+ {
+ if (initialized)
+ {
+ Debug.LogError(e.ToString());
+ }
+ }
+
+ // this is in its own try catch to continue deinit if this fails
+ try
+ {
+ deinitLibrary();
+ }
+ catch (Exception e)
+ {
+ if (initialized)
+ {
+ Debug.LogError(e.ToString());
+ }
+ }
+
+ Debug.Log("destroy library");
+
+ initialized = false;
+ }
+
+ ~LibInterface()
+ {
+ deinit();
+ }
+
+ public bool isInitialized()
+ {
+ return initialized;
+ }
+
+ private void initLibrary()
+ {
+ int result = LibInterfaceCpp.initLibrary();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library initialization successfull.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when initializing the library."
+ );
+ }
+ }
+
+ private void deinitLibrary()
+ {
+ int result = LibInterfaceCpp.deinitLibrary();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library deinitialization successfull.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when initializing the library."
+ );
+ }
+ }
+
+ public void setCalibrationPath(string calibrationPath)
+ {
+ byte[] calibPathBytes = System.Text.Encoding.UTF8.GetBytes(calibrationPath + "\0");
+ int result = LibInterfaceCpp.setCalibrationPath(calibPathBytes);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setCalibrationPath success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: calibration path already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the calibration path."
+ );
+ }
+ }
+
+ public void setConfigPath(string configPath)
+ {
+ byte[] configPathBytes = System.Text.Encoding.UTF8.GetBytes(configPath + "\0");
+ int result = LibInterfaceCpp.setConfigPath(configPathBytes);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setConfigPath success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: config path already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the config path."
+ );
+ }
+ }
+
+ public void setConfigFileName(string configFileName)
+ {
+ byte[] configFileNameBytes = System.Text.Encoding.UTF8.GetBytes(configFileName + "\0");
+ int result = LibInterfaceCpp.setConfigFileName(configFileNameBytes);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setConfigFileName success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: config file name already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the config file name."
+ );
+ }
+ }
+
+ ///
+ ///
+ ///
+ /// (has device, is tracking)
+ ///
+ ///
+ ///
+ ///
+ public bool currentHeadTrackingDeviceHasValidCalibration()
+ {
+ bool isValid;
+ int result = LibInterfaceCpp.currentHeadTrackingDeviceHasValidCalibration(out isValid);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: currentHeadTrackingDeviceHasValidCalibration success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when trying toi check if the current devide has a valid calibration."
+ );
+ }
+
+ return isValid;
+ }
+
+ // ------------------------------------------------
+
+ ///
+ /// headDetected: 0 = no head detected, 1 >= head detected
+ /// imagePosIsValid: 0 = invalid, 1 >= valid
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void TranslateNewHeadPositionCallback(
+ int headDetected,
+ int imagePosIsValid,
+ int imagePosX,
+ int imagePosY,
+ double worldPosX,
+ double worldPosY,
+ double worldPosZ,
+ IntPtr listener
+ )
+ {
+ try
+ {
+ // translate intptr to interface instance
+ // call interface instance callback
+ GCHandle gch = GCHandle.FromIntPtr(listener);
+ ITNewHeadPositionCallback interfaceInstance = (ITNewHeadPositionCallback)gch.Target;
+
+ interfaceInstance.NewHeadPositionCallback(
+ headDetected != 0,
+ imagePosIsValid != 0,
+ imagePosX,
+ imagePosY,
+ worldPosX,
+ worldPosY,
+ worldPosZ
+ );
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e.ToString());
+ }
+ }
+
+ public void registerHeadPositionChangedCallback(
+ in ITNewHeadPositionCallback inferfaceInstance
+ )
+ {
+ GCHandle gch = GCHandle.Alloc(inferfaceInstance);
+
+ int result = LibInterfaceCpp.registerHeadPositionChangedCallback(
+ GCHandle.ToIntPtr(gch),
+ newHeadPosCppCallback
+ );
+ // TODO figure out if this results in memory leaks
+ // gch.Free();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: registerHeadPositionChangedCallback success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: this callback has already been registered."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when registering the head position changed callback."
+ );
+ }
+ }
+
+ public void unregisterHeadPositionChangedCallback(
+ in ITNewHeadPositionCallback inferfaceInstance
+ )
+ {
+ GCHandle gch = GCHandle.Alloc(inferfaceInstance);
+ int result = LibInterfaceCpp.unregisterHeadPositionChangedCallback(
+ GCHandle.ToIntPtr(gch)
+ );
+ gch.Free();
+
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: unregisterHeadPositionChangedCallback success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: this callback has already been unregistered."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library: callback not registered.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when unregistering the head position changed callback."
+ );
+ }
+ }
+
+ private void TranslateShaderParametersCallback(
+ in G3DShaderParameters shaderParameters,
+ IntPtr listener
+ )
+ {
+ try
+ {
+ // translate intptr to interface instance
+ // call interface instance callback
+ GCHandle gch = GCHandle.FromIntPtr(listener);
+ ITNewShaderParametersCallback interfaceInstance = (ITNewShaderParametersCallback)
+ gch.Target;
+
+ interfaceInstance.NewShaderParametersCallback(shaderParameters);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e.ToString());
+ }
+ }
+
+ public void registerShaderParametersChangedCallback(
+ in ITNewShaderParametersCallback interfaceInstance
+ )
+ {
+ GCHandle gch = GCHandle.Alloc(interfaceInstance);
+
+ int result = LibInterfaceCpp.registerShaderParametersChangedCallback(
+ GCHandle.ToIntPtr(gch),
+ newShaderparamsCppCallback
+ );
+ // TODO figure out if this results in memory leaks
+ // gch.Free();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: registerShaderParametersChangedCallback success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: this callback has already been registered."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when registering the shader parameters changed callback."
+ );
+ }
+ }
+
+ public void unregisterShaderParametersChangedCallback(
+ in ITNewShaderParametersCallback interfaceInstance
+ )
+ {
+ GCHandle gch = GCHandle.Alloc(interfaceInstance);
+ int result = LibInterfaceCpp.unregisterShaderParametersChangedCallback(
+ GCHandle.ToIntPtr(gch)
+ );
+ gch.Free();
+
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: unregisterShaderParametersChangedCallback success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: this callback has already been unregistered."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library: callback not registered.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when unregistering the shader parameters changed callback."
+ );
+ }
+ }
+
+ private void TranslateNewErrorMessageCallback(
+ EMessageSeverity severity,
+ IntPtr sender,
+ IntPtr caption,
+ IntPtr cause,
+ IntPtr remedy,
+ IntPtr listener
+ )
+ {
+ try
+ {
+ // translate intptr to interface instance
+ // call interface instance callback
+ GCHandle gch = GCHandle.FromIntPtr(listener);
+ ITNewErrorMessageCallback interfaceInstance = (ITNewErrorMessageCallback)gch.Target;
+
+ string senderString = Marshal.PtrToStringUTF8(sender);
+ string captionString = Marshal.PtrToStringUTF8(caption);
+ string causeString = Marshal.PtrToStringUTF8(cause);
+ string remedyString = Marshal.PtrToStringUTF8(remedy);
+
+ interfaceInstance.NewErrorMessageCallback(
+ severity,
+ senderString,
+ captionString,
+ causeString,
+ remedyString
+ );
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e.ToString());
+ }
+ }
+
+ public void registerMessageCallback(in ITNewErrorMessageCallback inferfaceInstance)
+ {
+ GCHandle gch = GCHandle.Alloc(inferfaceInstance);
+
+ int result = LibInterfaceCpp.registerMessageCallback(
+ GCHandle.ToIntPtr(gch),
+ newErrorMessagesCppCallback
+ );
+ // TODO figure out if this results in memory leaks
+ // gch.Free();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: registerMessageCallback success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: this callback has already been registered."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when registering the message callback."
+ );
+ }
+ }
+
+ public void unregisterMessageCallback(in ITNewErrorMessageCallback inferfaceInstance)
+ {
+ GCHandle gch = GCHandle.Alloc(inferfaceInstance);
+ int result = LibInterfaceCpp.unregisterMessageCallback(GCHandle.ToIntPtr(gch));
+ gch.Free();
+
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: unregisterMessageCallback success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: this callback has already been unregistered."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library: callback not registered.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when unregistering the message callback."
+ );
+ }
+ }
+
+ // ------------------------------------------------
+
+ public void useHimaxD2XXDevices()
+ {
+ int result = LibInterfaceCpp.useHimaxD2XXDevices();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: useHimaxD2XXDevices success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when using Himax D2XX devices."
+ );
+ }
+ }
+
+ public void useHimaxRP2040Devices()
+ {
+ int result = LibInterfaceCpp.useHimaxRP2040Devices();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: useHimaxRP2040Devices success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when using Himax RP2040 devices."
+ );
+ }
+ }
+
+ public void usePmdFlexxDevices()
+ {
+ int result = LibInterfaceCpp.usePmdFlexxDevices();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: usePmdFlexxDevices success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when using PMD Flexx devices."
+ );
+ }
+ }
+
+ public void initHeadTracking()
+ {
+ int result = LibInterfaceCpp.initHeadTracking();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: initHeadTracking success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: head tracking already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException(
+ "G3D library: head tracking not initialized."
+ );
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when initializing head tracking."
+ );
+ }
+ }
+
+ public void deinitHeadTracking()
+ {
+ int result = LibInterfaceCpp.deinitHeadTracking();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: deinitHeadTracking success.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: head tracking already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException(
+ "G3D library: head tracking not initialized."
+ );
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when deinitializing head tracking."
+ );
+ }
+ }
+
+ // ------------------------------------------------
+
+ public int getHeadTrackingDeviceCount()
+ {
+ int deviceCount;
+ int result = LibInterfaceCpp.getHeadTrackingDeviceCount(out deviceCount);
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: getHeadTrackingDeviceCount success. Device count: " + deviceCount
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: head tracking device count already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the head tracking device count."
+ );
+ }
+
+ return deviceCount;
+ }
+
+ public string getHeadTrackingDeviceName(int deviceNumber)
+ {
+ IntPtr deviceNamePtr;
+ int result = LibInterfaceCpp.getHeadTrackingDeviceName(deviceNumber, out deviceNamePtr);
+ string deviceName = Marshal.PtrToStringUTF8(deviceNamePtr);
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: getHeadTrackingDeviceName success. Device with index "
+ + deviceNumber
+ + "has name: "
+ + deviceName
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: head tracking device name already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: the provided deviceNumber is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the head tracking device name."
+ );
+ }
+
+ return deviceName;
+ }
+
+ public string getCurrentHeadTrackingDevice()
+ {
+ IntPtr device;
+ int result = LibInterfaceCpp.getCurrentHeadTrackingDevice(out device);
+ string deviceName = Marshal.PtrToStringUTF8(device);
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: getCurrentHeadTrackingDevice success. Current device name: "
+ + deviceName
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: current head tracking device already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the current head tracking device."
+ );
+ }
+
+ return deviceName;
+ }
+
+ public void setCurrentHeadTrackingDevice(string device)
+ {
+ byte[] deviceNameBytes = System.Text.Encoding.UTF8.GetBytes(device);
+ int result = LibInterfaceCpp.setCurrentHeadTrackingDevice(deviceNameBytes);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setCurrentHeadTrackingDevice success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library: current head tracking device already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the current head tracking device."
+ );
+ }
+ }
+
+ ///
+ ///
+ ///
+ /// (horizontal resolution, vertical resolution)
+ ///
+ ///
+ ///
+ ///
+ public Tuple getHeadTrackingDeviceResolution()
+ {
+ int horizontalResolution;
+ int verticalResolution;
+ int result = LibInterfaceCpp.getHeadTrackingDeviceResolution(
+ out horizontalResolution,
+ out verticalResolution
+ );
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: getHeadTrackingDeviceResolution success. Horizontal resolution: "
+ + horizontalResolution
+ + ", vertical resolution: "
+ + verticalResolution
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the head tracking device resolution."
+ );
+ }
+
+ return new Tuple(horizontalResolution, verticalResolution);
+ }
+
+ // ------------------------------------------------
+
+ public int getFirstValidCalibrationMatrixCol()
+ {
+ int value;
+ int result = LibInterfaceCpp.getFirstValidCalibrationMatrixCol(out value);
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: getFirstValidCalibrationMatrixCol success. Value: " + value
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the first valid calibration matrix column."
+ );
+ }
+
+ return value;
+ }
+
+ public int getLastValidCalibrationMatrixCol()
+ {
+ int value;
+ int result = LibInterfaceCpp.getLastValidCalibrationMatrixCol(out value);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: getLastValidCalibrationMatrixCol success. Value: " + value);
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the last valid calibration matrix column."
+ );
+ }
+
+ return value;
+ }
+
+ public int getFirstValidCalibrationMatrixRow()
+ {
+ int value;
+ int result = LibInterfaceCpp.getFirstValidCalibrationMatrixRow(out value);
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: getFirstValidCalibrationMatrixRow success. Value: " + value
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the first valid calibration matrix row."
+ );
+ }
+
+ return value;
+ }
+
+ public int getLastValidCalibrationMatrixRow()
+ {
+ int value;
+ int result = LibInterfaceCpp.getLastValidCalibrationMatrixRow(out value);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: getLastValidCalibrationMatrixRow success. Value: " + value);
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the last valid calibration matrix row."
+ );
+ }
+
+ return value;
+ }
+
+ // ------------------------------------------------
+
+ public void startHeadTracking()
+ {
+ int result = LibInterfaceCpp.startHeadTracking();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: head tracking started successfully.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already started.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException(
+ "G3D library: head tracking not initialized."
+ );
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when starting head tracking."
+ );
+ }
+ }
+
+ public void stopHeadTracking()
+ {
+ int result = LibInterfaceCpp.stopHeadTracking();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: head tracking stopped successfully.");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException(
+ "G3D library: head tracking already stopped."
+ );
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when stopping head tracking."
+ );
+ }
+ }
+
+ ///
+ ///
+ ///
+ /// (has device, is tracking)
+ ///
+ ///
+ ///
+ ///
+ public HeadTrackingStatus getHeadTrackingStatus()
+ {
+ bool hasDevice;
+ bool isTracking;
+ int result = LibInterfaceCpp.getHeadTrackingStatus(out hasDevice, out isTracking);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: getHeadTrackingStatus success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the head tracking status."
+ );
+ }
+
+ HeadTrackingStatus headTrackingStatus = new HeadTrackingStatus
+ {
+ hasTrackingDevice = hasDevice,
+ isTrackingActive = isTracking
+ };
+
+ return headTrackingStatus;
+ }
+
+ // ------------------------------------------------
+
+ public G3DShaderParameters getCurrentShaderParameters()
+ {
+ G3DShaderParameters parameters;
+ int result = LibInterfaceCpp.getCurrentShaderParameters(out parameters);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: getCurrentShaderParameters success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when getting the current shader parameters."
+ );
+ }
+
+ return parameters;
+ }
+
+ public void setWindowPosition(int left, int top)
+ {
+ int result = LibInterfaceCpp.setWindowPosition(left, top);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setWindowPosition success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the window position."
+ );
+ }
+ }
+
+ public void setWindowSize(int width, int height)
+ {
+ int result = LibInterfaceCpp.setWindowSize(width, height);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setWindowSize success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the window size."
+ );
+ }
+ }
+
+ public void setViewportOffset(int left, int top)
+ {
+ int result = LibInterfaceCpp.setViewportOffset(left, top);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setViewportOffset success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the viewport offset."
+ );
+ }
+ }
+
+ public void setViewportSize(int width, int height)
+ {
+ int result = LibInterfaceCpp.setViewportSize(width, height);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setViewportSize success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the viewport size."
+ );
+ }
+ }
+
+ public void setScreenSize(int width, int height)
+ {
+ int result = LibInterfaceCpp.setScreenSize(width, height);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: setScreenSize success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when setting the screen dimensions."
+ );
+ }
+ }
+
+ // ------------------------------------------------
+
+ public void shiftViewToLeft()
+ {
+ int result = LibInterfaceCpp.shiftViewToLeft();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: shiftViewToLeft success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when shifting the view to the left."
+ );
+ }
+ }
+
+ public void shiftViewToRight()
+ {
+ int result = LibInterfaceCpp.shiftViewToRight();
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: shiftViewToRight success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException("G3D library already initialized.");
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException("G3D library not initialized.");
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library: an unknown error occurred when shifting the view to the right."
+ );
+ }
+ }
+
+ // ------------------------------------------------
+
+ // extern G3D_UNIVERSAL_HEADTRACKING_LIBRARY_EXPORT int initializePositionFilter(const int lenX, const int lenY, const int lenZ);
+ public void initializePositionFilter(int lenX, int lenY, int lenZ)
+ {
+ int result = LibInterfaceCpp.initializePositionFilter(lenX, lenY, lenZ);
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: initializePositionFilter success. Filter set to ("
+ + lenX
+ + ","
+ + lenY
+ + ","
+ + lenZ
+ + ","
+ + ")"
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library initializePositionFilter: position filter already initialized."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException(
+ "G3D library initializePositionFilter: not initialized."
+ );
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library initializePositionFilter: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library initializePositionFilter: an unknown error occurred when shifting the view to the right."
+ );
+ }
+ }
+
+ // extern G3D_UNIVERSAL_HEADTRACKING_LIBRARY_EXPORT int applyPositionFilter(const double posX, const double posY, const double posZ,
+ // double* filteredX, double* filteredY, double* filteredZ);
+ public void applyPositionFilter(
+ double posX,
+ double posY,
+ double posZ,
+ out double filteredX,
+ out double filteredY,
+ out double filteredZ
+ )
+ {
+ int result = LibInterfaceCpp.applyPositionFilter(
+ posX,
+ posY,
+ posZ,
+ out filteredX,
+ out filteredY,
+ out filteredZ
+ );
+ if (logToConsole)
+ {
+ Debug.Log(
+ "G3D library: applyPositionFilter success. Filtered position: ()"
+ + filteredX
+ + ", "
+ + filteredY
+ + ", "
+ + filteredZ
+ + "); Unfiltered Position: ("
+ + posX
+ + ", "
+ + posY
+ + ", "
+ + posZ
+ + ")"
+ );
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library applyPositionFilter: library already initialized error."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException(
+ "G3D library applyPositionFilter: position filter not initialized."
+ );
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library applyPositionFilter: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library applyPositionFilter: an unknown error occurred when getting the head tracking device resolution."
+ );
+ }
+ }
+
+ public void calculateShaderParameters(LatencyCorrectionMode latencyCorrectionMode)
+ {
+ int result = LibInterfaceCpp.calculateShaderParameters(latencyCorrectionMode);
+ if (logToConsole)
+ {
+ Debug.Log("G3D library: calculateShaderParameters success");
+ }
+ if (result == -100)
+ {
+ throw new G3D_AlreadyInitializedException(
+ "G3D library calculateShaderParameters: library already initialized error."
+ );
+ }
+ if (result == -101)
+ {
+ throw new G3D_NotInitializedException(
+ "G3D library calculateShaderParameters: library not initialized."
+ );
+ }
+ if (result == -102)
+ {
+ throw new G3D_IndexOutOfRangeException(
+ "G3D library calculateShaderParameters: a provided index is out of range."
+ );
+ }
+ if (result == -200)
+ {
+ throw new Exception(
+ "G3D library calculateShaderParameters: an unknown error occurred when calculating the shader parameters."
+ );
+ }
+ }
+ }
+
+ ///
+ /// This class provides the raw C interface to the G3D Universal Head Tracking Library.
+ ///
+ internal static class LibInterfaceCpp
+ {
+ // Error codes
+ // const int E_OK = 0;
+ // const int E_INITIALIZED_ALREADY = -100;
+ // const int E_NOT_INITIALIZED = -101;
+ // const int E_INDEX_OUT_OF_RANGE = -102;
+ // const int E_EXCEPTION_ERROR = -200;
+
+ //function definitions
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?initLibrary@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int initLibrary();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?deinitLibrary@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int deinitLibrary();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setCalibrationPath@G3D_HTL@@YAHPEBD@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setCalibrationPath(byte[] path);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setConfigPath@G3D_HTL@@YAHPEBD@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setConfigPath(byte[] path);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setConfigFileName@G3D_HTL@@YAHPEBD@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setConfigFileName(byte[] path);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?currentHeadTrackingDeviceHasValidCalibration@G3D_HTL@@YAHPEA_N@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int currentHeadTrackingDeviceHasValidCalibration(
+ [MarshalAs(UnmanagedType.U1)] out bool isValid
+ );
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?registerHeadPositionChangedCallback@G3D_HTL@@YAHPEAXP6AXHHHHNNN0@Z@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int registerHeadPositionChangedCallback(
+ IntPtr listener,
+ TNewHeadPositionCallbackInternal callback
+ );
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?unregisterHeadPositionChangedCallback@G3D_HTL@@YAHPEAX@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int unregisterHeadPositionChangedCallback(IntPtr listener);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?registerShaderParametersChangedCallback@G3D_HTL@@YAHPEAXP6AXPEBUCG3DShaderParameters@@0@Z@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int registerShaderParametersChangedCallback(
+ IntPtr listener,
+ TNewShaderParametersCallbackInternal callback
+ );
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?unregisterShaderParametersChangedCallback@G3D_HTL@@YAHPEAX@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int unregisterShaderParametersChangedCallback(IntPtr listener);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?registerMessageCallback@G3D_HTL@@YAHPEAXP6AXW4EMessageSeverity@@PEBD2220@Z@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int registerMessageCallback(
+ IntPtr listener,
+ TNewErrorMessageCallbackInternal callback
+ );
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?unregisterMessageCallback@G3D_HTL@@YAHPEAX@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int unregisterMessageCallback(IntPtr listener);
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?useHimaxD2XXDevices@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int useHimaxD2XXDevices();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?useHimaxRP2040Devices@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int useHimaxRP2040Devices();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?usePmdFlexxDevices@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int usePmdFlexxDevices();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?initHeadTracking@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int initHeadTracking();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?deinitHeadTracking@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int deinitHeadTracking();
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getHeadTrackingDeviceCount@G3D_HTL@@YAHPEAH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getHeadTrackingDeviceCount(out int deviceCount);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getHeadTrackingDeviceName@G3D_HTL@@YAHHPEAPEAD@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getHeadTrackingDeviceName(int deviceNumber, out IntPtr deviceName);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getCurrentHeadTrackingDevice@G3D_HTL@@YAHPEAPEAD@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getCurrentHeadTrackingDevice(out IntPtr deviceName);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setCurrentHeadTrackingDevice@G3D_HTL@@YAHPEBD@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setCurrentHeadTrackingDevice(in byte[] device);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getHeadTrackingDeviceResolution@G3D_HTL@@YAHPEAH0@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getHeadTrackingDeviceResolution(
+ out int horizontalResolution,
+ out int verticalResolution
+ );
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getFirstValidCalibrationMatrixCol@G3D_HTL@@YAHPEAH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getFirstValidCalibrationMatrixCol(out int value);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getLastValidCalibrationMatrixCol@G3D_HTL@@YAHPEAH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getLastValidCalibrationMatrixCol(out int value);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getFirstValidCalibrationMatrixRow@G3D_HTL@@YAHPEAH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getFirstValidCalibrationMatrixRow(out int value);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getLastValidCalibrationMatrixRow@G3D_HTL@@YAHPEAH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getLastValidCalibrationMatrixRow(out int value);
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?startHeadTracking@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int startHeadTracking();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?stopHeadTracking@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int stopHeadTracking();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getHeadTrackingStatus@G3D_HTL@@YAHPEA_N0@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getHeadTrackingStatus(
+ [MarshalAs(UnmanagedType.U1)] out bool hasDevice,
+ [MarshalAs(UnmanagedType.U1)] out bool isTracking
+ );
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?getCurrentShaderParameters@G3D_HTL@@YAHPEAUCG3DShaderParameters@@@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int getCurrentShaderParameters(
+ out G3DShaderParameters shaderParameters
+ );
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setWindowPosition@G3D_HTL@@YAHHH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setWindowPosition(int left, int top);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setWindowSize@G3D_HTL@@YAHHH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setWindowSize(int width, int height);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setViewportOffset@G3D_HTL@@YAHHH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setViewportOffset(int left, int top);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setViewportSize@G3D_HTL@@YAHHH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setViewportSize(int width, int height);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?setScreenSize@G3D_HTL@@YAHHH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int setScreenSize(int width, int height);
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?shiftViewToLeft@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int shiftViewToLeft();
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?shiftViewToRight@G3D_HTL@@YAHXZ",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int shiftViewToRight();
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?initializePositionFilter@G3D_HTL@@YAHHHH@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int initializePositionFilter(int lenX, int lenY, int lenZ);
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?applyPositionFilter@G3D_HTL@@YAHNNNPEAN00@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int applyPositionFilter(
+ double posX,
+ double posY,
+ double posZ,
+ out double filteredX,
+ out double filteredY,
+ out double filteredZ
+ );
+
+ // ------------------------------------------------
+
+ [DllImport(
+ "G3D_HeadTrackingLibrary_c.dll",
+ EntryPoint = "?calculateShaderParameters@G3D_HTL@@YAHW4LatencyCorrectionMode@1@@Z",
+ CallingConvention = CallingConvention.Cdecl
+ )]
+ public static extern int calculateShaderParameters(
+ LatencyCorrectionMode latencyCorrectionMode
+ );
+ }
+}
diff --git a/Scripts/LibInterface.cs.meta b/Scripts/HeadTracking/LibInterface.cs.meta
similarity index 100%
rename from Scripts/LibInterface.cs.meta
rename to Scripts/HeadTracking/LibInterface.cs.meta
diff --git a/Scripts/HeadTrackingPretender.cs b/Scripts/HeadTrackingPretender.cs
deleted file mode 100644
index 05de906..0000000
--- a/Scripts/HeadTrackingPretender.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using UnityEngine;
-
-///
-/// This class can be used to simulate head tracking if no head tracking device is available.
-/// Simply attach it to a GameObject and move the GameObject around to simulate head tracking.
-/// The game objects transform will be used to simulate the head position.
-/// (In meter -> 0.7 z equals 70 centimiter from the head tracking camera)
-///
-public class HeadTrackingPretender : MonoBehaviour
-{
- public G3DCamera g3dCamera;
- public bool headDetected = true;
-
- public float initialOffsetZ = 0.7f;
-
- // Start is called before the first frame update
- void Start() { }
-
- // Update is called once per frame
- void Update()
- {
- Vector3 headPosition = transform.localPosition;
- headPosition = -headPosition;
- headPosition = headPosition * 1000;
- headPosition.z += initialOffsetZ * 1000;
- ((ITNewHeadPositionCallback)g3dCamera).NewHeadPositionCallback(
- headDetected,
- true,
- (int)headPosition.x,
- (int)headPosition.y,
- headPosition.x,
- headPosition.y,
- headPosition.z
- );
- }
-}
diff --git a/Scripts/HeadtrackingHandler.cs b/Scripts/HeadtrackingHandler.cs
deleted file mode 100644
index 7bb0f13..0000000
--- a/Scripts/HeadtrackingHandler.cs
+++ /dev/null
@@ -1,277 +0,0 @@
-using UnityEngine;
-
-public class HeadtrackingHandler
-{
- [Tooltip("Time it takes till the reset animation starts in seconds.")]
- public float headLostTimeoutInSec = 3.0f;
-
- [Tooltip("Reset animation duratuion in seconds.")]
- public float transitionDuration = 0.5f;
-
- private Vector3 lastHeadPosition = new Vector3(0, 0, 0);
-
- private float headLostTimer = 0.0f;
- private float transitionTime = 0.0f;
-
- private enum HeadTrackingState
- {
- TRACKING,
- LOST,
- TRANSITIONTOLOST,
- TRANSITIONTOTRACKING,
- LOSTGRACEPERIOD
- }
-
- private HeadTrackingState prevHeadTrackingState = HeadTrackingState.LOST;
-
- public HeadtrackingHandler(float focusDistance)
- {
- lastHeadPosition = new Vector3(0, 0, -focusDistance);
- }
-
- /**
- * Calculate the new camera center position based on the head tracking.
- * If head tracking is lost, or the head moves to far away from the tracking camera a grace periope is started.
- * Afterwards the camera center will be animated back towards the default position.
- */
- public void handleHeadTrackingState(
- ref HeadPosition headPosition,
- ref Vector3 targetPosition,
- ref float targetViewSeparation,
- float viewSeparation,
- float focusDistance,
- Vector3 cameraParentLocalPosition
- )
- {
- // get new state
- HeadTrackingState newState = getNewStateState(prevHeadTrackingState, ref headPosition);
-
- prevHeadTrackingState = newState;
-
- // handle lost state
- if (newState == HeadTrackingState.LOST)
- {
- targetPosition = new Vector3(0, 0, -focusDistance);
- targetViewSeparation = 0.0f;
- }
- // handle tracking state
- else if (newState == HeadTrackingState.TRACKING)
- {
- Vector3 headPositionWorld = new Vector3(
- (float)headPosition.worldPosX,
- (float)headPosition.worldPosY,
- (float)headPosition.worldPosZ
- );
-
- targetPosition = headPositionWorld;
- targetViewSeparation = viewSeparation;
- lastHeadPosition = targetPosition;
- }
- // if lost, start grace period
- else if (newState == HeadTrackingState.LOSTGRACEPERIOD)
- {
- // if we have waited for the timeout
- if (headLostTimer > headLostTimeoutInSec)
- {
- newState = HeadTrackingState.TRANSITIONTOLOST;
- headLostTimer = 0.0f;
- transitionTime = 0.0f;
- }
- else
- {
- headLostTimer += Time.deltaTime;
- targetPosition = lastHeadPosition;
- targetViewSeparation = viewSeparation;
- }
- }
- // handle transitions
- else if (
- newState == HeadTrackingState.TRANSITIONTOLOST
- || newState == HeadTrackingState.TRANSITIONTOTRACKING
- )
- {
- // init with values for transition to lost
- Vector3 originPosition = lastHeadPosition;
- Vector3 transitionTargetPosition = new Vector3(0, 0, -focusDistance);
- float transitionViewSeparation = 0.0f;
- float originSeparation = viewSeparation;
-
- if (newState == HeadTrackingState.TRANSITIONTOTRACKING)
- {
- originPosition = new Vector3(0, 0, -focusDistance);
- transitionViewSeparation = viewSeparation;
- originSeparation = 0.0f;
-
- if (headPosition.headDetected)
- {
- Vector3 headPositionWorld = new Vector3(
- (float)headPosition.worldPosX,
- (float)headPosition.worldPosY,
- (float)headPosition.worldPosZ
- );
- transitionTargetPosition = headPositionWorld;
- }
- else
- {
- // if no head is detected use last known head position
- transitionTargetPosition = lastHeadPosition;
- }
- }
-
- bool isEndReached = handleTransition(
- originPosition,
- transitionTargetPosition,
- originSeparation,
- transitionViewSeparation,
- ref targetPosition,
- ref targetViewSeparation
- );
- if (isEndReached)
- {
- transitionTime = 0.0f;
- // if we have reached the target position, we are no longer in transition
- if (newState == HeadTrackingState.TRANSITIONTOLOST)
- {
- newState = HeadTrackingState.LOST;
- }
- else
- {
- newState = HeadTrackingState.TRACKING;
- }
- }
- }
-
- // reset lost timer if we are not in grace period
- if (newState != HeadTrackingState.LOSTGRACEPERIOD)
- {
- headLostTimer = 0.0f;
- }
-
- prevHeadTrackingState = newState;
- }
-
- ///
- /// returns true if transition end is reached
- ///
- ///
- ///
- ///
- ///
- ///
- /// is transition end reached
- private bool handleTransition(
- Vector3 originPosition,
- Vector3 transitionTargetPosition,
- float originSeparation,
- float transitionViewSeparation,
- ref Vector3 targetPosition,
- ref float targetViewSeparation
- )
- {
- // interpolate values
- float transitionPercentage = transitionTime / transitionDuration;
- transitionTime += Time.deltaTime;
-
- // set to default position
- Vector3 interpolatedPosition = Vector3.Lerp(
- originPosition,
- transitionTargetPosition,
- transitionPercentage
- );
- float interpolatedEyeSeparation = Mathf.Lerp(
- originSeparation,
- transitionViewSeparation,
- transitionPercentage
- );
-
- // check if end is reached (with a small tolerance)
- if (transitionPercentage + 0.01f < 1.0f)
- {
- // apply values
- targetPosition = interpolatedPosition;
- targetViewSeparation = interpolatedEyeSeparation;
- return false;
- }
- else
- {
- return true;
- }
- }
-
- private HeadTrackingState getNewStateState(
- HeadTrackingState currentHeadTrackingState,
- ref HeadPosition headPosition
- )
- {
- HeadTrackingState newState;
-
- if (headPosition.headDetected)
- {
- // if head detected
- if (currentHeadTrackingState == HeadTrackingState.TRACKING)
- {
- newState = HeadTrackingState.TRACKING;
- }
- else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD)
- {
- newState = HeadTrackingState.TRACKING;
- }
- else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING)
- {
- newState = HeadTrackingState.TRANSITIONTOTRACKING;
- }
- else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST)
- {
- newState = HeadTrackingState.TRANSITIONTOLOST;
- }
- else //(currentHeadTrackingState == HeadTrackingState.LOST)
- {
- newState = HeadTrackingState.TRANSITIONTOTRACKING;
- }
- }
- else
- {
- // if head not detected
- if (currentHeadTrackingState == HeadTrackingState.TRACKING)
- {
- newState = HeadTrackingState.LOSTGRACEPERIOD;
- }
- else if (currentHeadTrackingState == HeadTrackingState.LOSTGRACEPERIOD)
- {
- newState = HeadTrackingState.LOSTGRACEPERIOD;
- }
- else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOTRACKING)
- {
- newState = HeadTrackingState.TRANSITIONTOTRACKING;
- }
- else if (currentHeadTrackingState == HeadTrackingState.TRANSITIONTOLOST)
- {
- newState = HeadTrackingState.TRANSITIONTOLOST;
- }
- else //(currentHeadTrackingState == HeadTrackingState.LOST)
- {
- newState = HeadTrackingState.LOST;
- }
- }
- return newState;
- }
-
- private string headTrackingStateToString()
- {
- switch (prevHeadTrackingState)
- {
- case HeadTrackingState.TRACKING:
- return "TRACKING";
- case HeadTrackingState.LOST:
- return "LOST";
- case HeadTrackingState.LOSTGRACEPERIOD:
- return "LOSTGRACEPERIOD";
- case HeadTrackingState.TRANSITIONTOLOST:
- return "TRANSITIONTOLOST";
- case HeadTrackingState.TRANSITIONTOTRACKING:
- return "TRANSITIONTOTRACKING";
- default:
- return "UNKNOWN";
- }
- }
-}
diff --git a/Scripts/HeadtrackingHandler.cs.meta b/Scripts/HeadtrackingHandler.cs.meta
deleted file mode 100644
index 7740871..0000000
--- a/Scripts/HeadtrackingHandler.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 6e134d4f9564d2e4380aa95380f61a15
\ No newline at end of file
diff --git a/Scripts/IndexMap.cs b/Scripts/IndexMap.cs
index a6f8eaa..8ba04ea 100644
--- a/Scripts/IndexMap.cs
+++ b/Scripts/IndexMap.cs
@@ -1,953 +1,1010 @@
using System;
using System.Collections.Generic;
-public class IndexMap
+namespace G3D
{
- private static readonly IndexMap internalInstance = new IndexMap();
-
- // Explicit static constructor to tell C# compiler
- // not to mark type as beforefieldinit
- static IndexMap()
+ public class IndexMap
{
- internalInstance.generateDefaultMaps();
- }
+ private static readonly IndexMap internalInstance = new IndexMap();
- public static IndexMap Instance
- {
- get { return internalInstance; }
- }
+ // Explicit static constructor to tell C# compiler
+ // not to mark type as beforefieldinit
+ static IndexMap()
+ {
+ internalInstance.generateDefaultMaps();
+ }
- private int BLANK_VIEW = 250; // corresponds to a black view
- public Dictionary maps;
+ public static IndexMap Instance
+ {
+ get { return internalInstance; }
+ }
- public int[] currentMap = new int[] { 0 };
+ private int BLANK_VIEW = 250; // corresponds to a black view
+ public Dictionary maps;
- public void generateDefaultMaps()
- {
- maps = new Dictionary();
-
- string name = "A";
- int[] mapArray = new int[35];
- generate_indexmap(ref mapArray, mapArray.Length, 2, 0.0f, 1.0f, false);
- maps[name] = mapArray;
-
- name = "B";
- mapArray = new int[35];
- generate_indexmap(ref mapArray, mapArray.Length, 6, 0.0f, 1.0f, false);
- maps[name] = mapArray;
-
- name = "C";
- mapArray = new int[35];
- generate_indexmap(ref mapArray, mapArray.Length, 8, 0.0f, 1.0f, false);
- maps[name] = mapArray;
-
- name = "D";
- mapArray = new int[35];
- generate_indexmap(ref mapArray, mapArray.Length, 9, 0.0f, 1.0f, false);
- maps[name] = mapArray;
-
- name = "E";
- mapArray = new int[35];
- generate_indexmap(ref mapArray, mapArray.Length, 16, 0.0f, 1.0f, false);
- maps[name] = mapArray;
-
- name = "S1D";
- maps.Add(
- name,
- new int[]
- {
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 250,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250
- }
- );
+ public int[] currentMap = new int[] { 0 };
- name = "S2D";
- maps.Add(
- name,
- new int[]
- {
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 250,
- 250,
- 250,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250
- }
- );
+ public void generateDefaultMaps()
+ {
+ maps = new Dictionary();
+
+ string name = "A";
+ int[] mapArray = new int[35];
+ generate_indexmap(ref mapArray, mapArray.Length, 2, 0.0f, 1.0f, false);
+ maps[name] = mapArray;
+
+ name = "B";
+ mapArray = new int[35];
+ generate_indexmap(ref mapArray, mapArray.Length, 6, 0.0f, 1.0f, false);
+ maps[name] = mapArray;
+
+ name = "C";
+ mapArray = new int[35];
+ generate_indexmap(ref mapArray, mapArray.Length, 8, 0.0f, 1.0f, false);
+ maps[name] = mapArray;
+
+ name = "D";
+ mapArray = new int[35];
+ generate_indexmap(ref mapArray, mapArray.Length, 9, 0.0f, 1.0f, false);
+ maps[name] = mapArray;
+
+ name = "E";
+ mapArray = new int[35];
+ generate_indexmap(ref mapArray, mapArray.Length, 16, 0.0f, 1.0f, false);
+ maps[name] = mapArray;
+
+ name = "S1D";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 250,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250
+ }
+ );
- name = "S3D";
- maps.Add(
- name,
- new int[]
- {
- 250,
- 250,
- 250,
- 250,
- 250,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 250,
- 250,
- 250,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 250,
- 250,
- 250,
- 250,
- 250
- }
- );
+ name = "S2D";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 250,
+ 250,
+ 250,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250
+ }
+ );
- name = "S4D";
- maps.Add(
- name,
- new int[]
- {
- 250,
- 250,
- 250,
- 250,
- 250,
- 250,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 250,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 250,
- 250,
- 250,
- 250,
- 250,
- 250
- }
- );
+ name = "S3D";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 250,
+ 250,
+ 250,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250
+ }
+ );
- name = "S6D";
- maps.Add(
- name,
- new int[]
- {
- 250,
- 250,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 250,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 250,
- 250
- }
- );
+ name = "S4D";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 250,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250,
+ 250
+ }
+ );
- name = "05A";
- maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 });
+ name = "S6D";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 250,
+ 250,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 250,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 250,
+ 250
+ }
+ );
- name = "05C";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
+ name = "05A";
+ maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 });
- name = "05N";
- maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 });
+ name = "05C";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
- name = "05O";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
+ name = "05N";
+ maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 });
- name = "05P";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
+ name = "05O";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
- name = "05Q";
- maps.Add(
- name,
- new int[] { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4 }
- );
+ name = "05P";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
- name = "05R";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
+ name = "05Q";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4
+ }
+ );
- name = "07A";
- maps.Add(name, new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6 });
+ name = "05R";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4 });
- name = "07D";
- maps.Add(
- name,
- new int[]
- {
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 1,
- 1,
- 1,
- 2,
- 2,
- 2,
- 2,
- 2,
- 3,
- 3,
- 3,
- 3,
- 3,
- 4,
- 4,
- 4,
- 4,
- 4,
- 5,
- 5,
- 5,
- 5,
- 5,
- 6,
- 6,
- 6,
- 6,
- 6
- }
- );
+ name = "07A";
+ maps.Add(
+ name,
+ new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6 }
+ );
- name = "08A";
- maps.Add(
- name,
- new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 }
- );
+ name = "07D";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6
+ }
+ );
- name = "08B";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
+ name = "08A";
+ maps.Add(
+ name,
+ new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 }
+ );
- name = "08D";
- maps.Add(
- name,
- new int[]
- {
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 1,
- 1,
- 1,
- 2,
- 2,
- 2,
- 2,
- 2,
- 3,
- 3,
- 3,
- 3,
- 3,
- 4,
- 4,
- 4,
- 4,
- 4,
- 5,
- 5,
- 5,
- 5,
- 5,
- 6,
- 6,
- 6,
- 6,
- 6,
- 7,
- 7,
- 7,
- 7,
- 7
- }
- );
+ name = "08B";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
- name = "08O";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
+ name = "08D";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7
+ }
+ );
- name = "08Q";
- maps.Add(
- name,
- new int[]
- {
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 1,
- 1,
- 1,
- 2,
- 2,
- 2,
- 2,
- 2,
- 3,
- 3,
- 3,
- 3,
- 3,
- 4,
- 4,
- 4,
- 4,
- 4,
- 5,
- 5,
- 5,
- 5,
- 5,
- 6,
- 6,
- 6,
- 6,
- 6,
- 7,
- 7,
- 7,
- 7,
- 7
- }
- );
+ name = "08O";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
- name = "120views";
- maps.Add(
- name,
- new int[]
- {
- 0,
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 9,
- 10,
- 11,
- 12,
- 13,
- 14,
- 15,
- 16,
- 17,
- 18,
- 19,
- 20,
- 21,
- 22,
- 23,
- 24,
- 25,
- 26,
- 27,
- 28,
- 29,
- 30,
- 31,
- 32,
- 33,
- 34,
- 35,
- 36,
- 37,
- 38,
- 39,
- 40,
- 41,
- 42,
- 43,
- 44,
- 45,
- 46,
- 47,
- 48,
- 49,
- 50,
- 51,
- 52,
- 53,
- 54,
- 55,
- 56,
- 57,
- 58,
- 59,
- 60,
- 61,
- 62,
- 63,
- 64,
- 65,
- 66,
- 67,
- 68,
- 69,
- 70,
- 71,
- 72,
- 73,
- 74,
- 75,
- 76,
- 77,
- 78,
- 79,
- 80,
- 81,
- 82,
- 83,
- 84,
- 85,
- 86,
- 87,
- 88,
- 89,
- 90,
- 91,
- 92,
- 93,
- 94,
- 95,
- 96,
- 97,
- 98,
- 99,
- 100,
- 101,
- 102,
- 103,
- 104,
- 105,
- 106,
- 107,
- 108,
- 109,
- 110,
- 111,
- 112,
- 113,
- 114,
- 115,
- 116,
- 117,
- 118,
- 119
- }
- );
+ name = "08Q";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 4,
+ 5,
+ 5,
+ 5,
+ 5,
+ 5,
+ 6,
+ 6,
+ 6,
+ 6,
+ 6,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7
+ }
+ );
- name = "A8A";
- maps.Add(
- name,
- new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 }
- );
+ name = "120views";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 31,
+ 32,
+ 33,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 45,
+ 46,
+ 47,
+ 48,
+ 49,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 55,
+ 56,
+ 57,
+ 58,
+ 59,
+ 60,
+ 61,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 79,
+ 80,
+ 81,
+ 82,
+ 83,
+ 84,
+ 85,
+ 86,
+ 87,
+ 88,
+ 89,
+ 90,
+ 91,
+ 92,
+ 93,
+ 94,
+ 95,
+ 96,
+ 97,
+ 98,
+ 99,
+ 100,
+ 101,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 108,
+ 109,
+ 110,
+ 111,
+ 112,
+ 113,
+ 114,
+ 115,
+ 116,
+ 117,
+ 118,
+ 119
+ }
+ );
- name = "A8B";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
+ name = "A8A";
+ maps.Add(
+ name,
+ new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7 }
+ );
- name = "A8O";
- maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
+ name = "A8B";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
- name = "IPRO_CUS01";
- maps.Add(
- name,
- new int[]
- {
- 0,
- 77,
- 0,
- 77,
- 0,
- 77,
- 0,
- 77,
- 77,
- 77,
- 77,
- 77,
- 1,
- 77,
- 1,
- 77,
- 1,
- 77,
- 1,
- 77,
- 77,
- 77,
- 77,
- 77
- }
- ); // 77 == blank? if so: 77 = 250!
- }
+ name = "A8O";
+ maps.Add(name, new int[] { 0, 1, 2, 3, 4, 5, 6, 7 });
- private void generate_indexmap(
- ref int[] indexmap,
- int availableViews,
- int contentViews,
- float delimiterViews,
- float yoyoStart,
- bool invert,
- bool invertIndices = false
- )
- {
- // sanitize inputs
- yoyoStart = Math.Clamp(yoyoStart, 0.0f, 1.0f);
- delimiterViews = Math.Clamp(delimiterViews, 0.0f, 1.0f);
+ name = "IPRO_CUS01";
+ maps.Add(
+ name,
+ new int[]
+ {
+ 0,
+ 77,
+ 0,
+ 77,
+ 0,
+ 77,
+ 0,
+ 77,
+ 77,
+ 77,
+ 77,
+ 77,
+ 1,
+ 77,
+ 1,
+ 77,
+ 1,
+ 77,
+ 1,
+ 77,
+ 77,
+ 77,
+ 77,
+ 77
+ }
+ ); // 77 == blank? if so: 77 = 250!
+ }
- if (availableViews <= 1 || contentViews == 1)
+ ///
+ /// Updates the internal index map stored in "currentMap".
+ /// Also returns the generated index map.
+ /// Creates a new index map and adds it to the internal dictionary with the given key.
+ /// DANGEROUS: if the key already exists, the existing map will not be overwritten
+ /// if the parameter overwrite is false!
+ ///
+ /// key to the internal map.
+ /// number of available views on the display. has to be larger than 0
+ /// number of views your content has. has to be larger than 0
+ /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0
+ /// if true, the final index map will be inverted
+ /// if true, the internal map in the internal dictionary will be overwritten
+ ///
+ public int[] UpdateIndexMapFromKey(
+ string key,
+ int availableViews,
+ int contentViews,
+ float yoyoStart,
+ bool invert,
+ bool invertIndices = false,
+ bool overwrite = false
+ )
{
- for (int j = 0; j < availableViews; j++)
- indexmap[j] = 0;
- return;
+ if (maps.ContainsKey(key) && !overwrite)
+ {
+ // TODO: also check for parameters
+ // key found, return the loaded map
+ currentMap = maps[key];
+ }
+ else
+ {
+ // key not found -> generate a map and return a pointer to that one
+ int[] mapArray = GetIndexMapTemp(
+ availableViews,
+ contentViews,
+ yoyoStart,
+ invert,
+ invertIndices
+ );
+ maps[key] = mapArray;
+ currentMap = mapArray;
+ }
+ return currentMap;
}
- if (yoyoStart < 1.0f)
+ ///
+ /// Updates the internal index map stored in "currentMap".
+ /// Also returns the generated index map.
+ ///
+ /// number of available views on the display. has to be larger than 0
+ /// number of views your content has. has to be larger than 0
+ /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0
+ /// if true, the final index map will be inverted
+ /// if true, the internal map in the internal dictionary will be overwritten
+ ///
+ public int[] UpdateIndexMap(
+ int availableViews,
+ int contentViews,
+ float yoyoStart,
+ bool invert,
+ bool invertIndices = false
+ )
{
- int nonInvertedCount = (int)(availableViews * yoyoStart);
- int[] indexmapNonInvertedPart = new int[nonInvertedCount];
- int[] indexmapInvertedPart = new int[availableViews - nonInvertedCount];
-
- generateAutomaticDistribution(
- ref indexmapNonInvertedPart,
- nonInvertedCount,
- contentViews,
- 0.0f,
- false
- );
- generateAutomaticDistribution(
- ref indexmapInvertedPart,
- availableViews - nonInvertedCount,
+ int[] mapArray = GetIndexMapTemp(
+ availableViews,
contentViews,
- 0.0f,
- true
- );
- Array.Copy(indexmapNonInvertedPart, 0, indexmap, 0, nonInvertedCount);
- Array.Copy(
- indexmapInvertedPart,
- 0,
- indexmap,
- nonInvertedCount,
- availableViews - nonInvertedCount
+ yoyoStart,
+ invert,
+ invertIndices
);
+ currentMap = mapArray;
+ return currentMap;
}
- else
+
+ ///
+ /// Creates a new index map without storing it in the internal dictionary.
+ ///
+ /// number of available views on the display. has to be larger than 0
+ /// number of views your content has. has to be larger than 0
+ /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0
+ /// if true, the final index map will be inverted
+ ///
+ public int[] GetIndexMapTemp(
+ int availableViews,
+ int contentViews,
+ float yoyoStart,
+ bool invert,
+ bool invertIndices = false
+ )
{
- generateAutomaticDistribution(
- ref indexmap,
+ int[] mapArray = new int[availableViews];
+ generate_indexmap(
+ ref mapArray,
availableViews,
contentViews,
- delimiterViews,
- invert
+ 0,
+ yoyoStart,
+ invert,
+ invertIndices
);
+ return mapArray;
}
- if (invert)
+ public float[] convertToFloatArray(in int[] intArray)
{
- int x = 0;
- int y = availableViews - 1;
- int tmp;
- while (x < y)
+ float[] floatArray = new float[intArray.Length];
+ for (int i = 0; i < intArray.Length; i++)
{
- tmp = indexmap[x];
- indexmap[x] = indexmap[y];
- indexmap[y] = tmp;
- x++;
- y--;
+ floatArray[i] = intArray[i];
}
+ return floatArray;
}
- if (invertIndices)
+ public float[] currentMapAsFloatArray()
{
- for (int i = 0; i < indexmap.Length; i++)
+ return convertToFloatArray(currentMap);
+ }
+
+ public float[] getPaddedIndexMapArray()
+ {
+ float[] paddedArray = new float[256];
+ float[] indexMapArray = currentMapAsFloatArray();
+ for (int i = 0; i < paddedArray.Length; i++)
{
- if (indexmap[i] != BLANK_VIEW)
+ if (i < indexMapArray.Length)
+ {
+ paddedArray[i] = indexMapArray[i];
+ }
+ else
{
- indexmap[i] = contentViews - 1 - indexmap[i];
+ paddedArray[i] = 0.0f;
}
}
+ return paddedArray;
}
- }
-
- private void generateAutomaticDistribution(
- ref int[] indexmap,
- int availableViews,
- int contentViews,
- float delimiterViews,
- bool invert
- )
- {
- if (indexmap.Length == 0)
- return;
-
- int i;
- // automatic distribution
- if (contentViews == 2)
- {
- i = generateTwoContentViews(ref indexmap, availableViews, delimiterViews);
- }
- else
- {
- i = generateMoreThanTwoContentViews(
- ref indexmap,
- availableViews,
- contentViews,
- delimiterViews
- );
- }
- for (int j = i; j < availableViews; j++)
- indexmap[j] = 0;
- if (invert)
+ ///
+ /// This function is for displaying in inspector
+ ///
+ ///
+ public string currentMapToString()
{
- int x = 0;
- int y = availableViews - 1;
- int tmp;
- while (x < y)
+ string mapString = "";
+ int counter = 0;
+ for (int i = 0; i < currentMap.Length; i++)
{
- tmp = indexmap[x];
- indexmap[x] = indexmap[y];
- indexmap[y] = tmp;
- x++;
- y--;
+ int index = currentMap[i];
+ mapString += index.ToString();
+ if (i < currentMap.Length - 1)
+ mapString += ", ";
+ counter++;
+ if (counter >= 17)
+ {
+ mapString += "\n";
+ counter = 0;
+ }
}
+ return currentMap != null ? mapString : "Index map not initialized.";
}
- if (delimiterViews == 0.0f)
+ private void generate_indexmap(
+ ref int[] indexmap,
+ int availableViews,
+ int contentViews,
+ float delimiterViews,
+ float yoyoStart,
+ bool invert,
+ bool invertIndices = false
+ )
{
- if (indexmap[0] == BLANK_VIEW)
- indexmap[0] = 0;
- for (int x = 1; x < availableViews; x++)
+ // sanitize inputs
+ yoyoStart = Math.Clamp(yoyoStart, 0.0f, 1.0f);
+ delimiterViews = Math.Clamp(delimiterViews, 0.0f, 1.0f);
+
+ if (availableViews <= 1 || contentViews == 1)
{
- if (indexmap[x] == BLANK_VIEW)
- indexmap[x] = indexmap[x - 1];
+ for (int j = 0; j < availableViews; j++)
+ indexmap[j] = 0;
+ return;
}
- }
- }
-
- private int generateTwoContentViews(
- ref int[] indexmap,
- int availableViews,
- float delimiterViews
- )
- {
- int i = 0;
- float eyeAreaSpace = (1.0f - delimiterViews) / 2;
-
- int delimViews = (int)(availableViews * delimiterViews);
- int eyeViews = (int)(availableViews * eyeAreaSpace);
- int startDelimViews = (availableViews - delimViews - eyeViews * 2) / 2;
- int endDelimViews = availableViews - delimViews - eyeViews * 2 - startDelimViews;
-
- for (; startDelimViews > 0; startDelimViews--)
- indexmap[i++] = BLANK_VIEW;
- for (int j = 0; j < eyeViews; j++)
- indexmap[i++] = 1;
- for (; delimViews > 0; delimViews--)
- indexmap[i++] = BLANK_VIEW;
- for (int j = 0; j < eyeViews; j++)
- indexmap[i++] = 0;
- for (; endDelimViews > 0; endDelimViews--)
- indexmap[i++] = BLANK_VIEW;
-
- return i;
- }
- private int generateMoreThanTwoContentViews(
- ref int[] indexmap,
- int availableViews,
- int contentViews,
- float delimiterViews
- )
- {
- int i = 0;
- int delimViewsStart = (int)(availableViews * delimiterViews / 2);
- int delimViewsEnd = (int)(availableViews * delimiterViews / 2);
- int spacePerView = (availableViews - delimViewsStart - delimViewsEnd) / contentViews;
- int spaceLeftover =
- availableViews - delimViewsStart - delimViewsEnd - spacePerView * contentViews;
-
- for (; delimViewsStart > 0; delimViewsStart--)
- indexmap[i++] = BLANK_VIEW;
- for (int viewIndex = 0; viewIndex < contentViews; viewIndex++)
- {
- int offset = 0;
- if (spaceLeftover > 0)
+ if (yoyoStart < 1.0f)
{
- offset = 1;
- spaceLeftover--;
+ int nonInvertedCount = (int)(availableViews * yoyoStart);
+ int[] indexmapNonInvertedPart = new int[nonInvertedCount];
+ int[] indexmapInvertedPart = new int[availableViews - nonInvertedCount];
+
+ generateAutomaticDistribution(
+ ref indexmapNonInvertedPart,
+ nonInvertedCount,
+ contentViews,
+ 0.0f,
+ false
+ );
+ generateAutomaticDistribution(
+ ref indexmapInvertedPart,
+ availableViews - nonInvertedCount,
+ contentViews,
+ 0.0f,
+ true
+ );
+ Array.Copy(indexmapNonInvertedPart, 0, indexmap, 0, nonInvertedCount);
+ Array.Copy(
+ indexmapInvertedPart,
+ 0,
+ indexmap,
+ nonInvertedCount,
+ availableViews - nonInvertedCount
+ );
}
- for (int currentSpace = spacePerView + offset; currentSpace > 0; currentSpace--)
+ else
{
- indexmap[i++] = (int)(contentViews - 1 - viewIndex);
+ generateAutomaticDistribution(
+ ref indexmap,
+ availableViews,
+ contentViews,
+ delimiterViews,
+ invert
+ );
}
- }
- for (; delimViewsEnd > 0; delimViewsEnd--)
- indexmap[i++] = BLANK_VIEW;
- return i;
- }
+ if (invert)
+ {
+ int x = 0;
+ int y = availableViews - 1;
+ int tmp;
+ while (x < y)
+ {
+ tmp = indexmap[x];
+ indexmap[x] = indexmap[y];
+ indexmap[y] = tmp;
+ x++;
+ y--;
+ }
+ }
- ///
- /// Updates the internal index map stored in "currentMap".
- /// Also returns the generated index map.
- /// Creates a new index map and adds it to the internal dictionary with the given key.
- /// DANGEROUS: if the key already exists, the existing map will not be overwritten
- /// if the parameter overwrite is false!
- ///
- /// key to the internal map.
- /// number of available views on the display. has to be larger than 0
- /// number of views your content has. has to be larger than 0
- /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0
- /// if true, the final index map will be inverted
- /// if true, the internal map in the internal dictionary will be overwritten
- ///
- public int[] UpdateIndexMapFromKey(
- string key,
- int availableViews,
- int contentViews,
- float yoyoStart,
- bool invert,
- bool invertIndices = false,
- bool overwrite = false
- )
- {
- if (maps.ContainsKey(key) && !overwrite)
- {
- // TODO: also check for parameters
- // key found, return the loaded map
- currentMap = maps[key];
+ if (invertIndices)
+ {
+ for (int i = 0; i < indexmap.Length; i++)
+ {
+ if (indexmap[i] != BLANK_VIEW)
+ {
+ indexmap[i] = contentViews - 1 - indexmap[i];
+ }
+ }
+ }
}
- else
+
+ private void generateAutomaticDistribution(
+ ref int[] indexmap,
+ int availableViews,
+ int contentViews,
+ float delimiterViews,
+ bool invert
+ )
{
- // key not found -> generate a map and return a pointer to that one
- int[] mapArray = GetIndexMapTemp(
- availableViews,
- contentViews,
- yoyoStart,
- invert,
- invertIndices
- );
- maps[key] = mapArray;
- currentMap = mapArray;
- }
- return currentMap;
- }
+ if (indexmap.Length == 0)
+ return;
- ///
- /// Updates the internal index map stored in "currentMap".
- /// Also returns the generated index map.
- ///
- /// number of available views on the display. has to be larger than 0
- /// number of views your content has. has to be larger than 0
- /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0
- /// if true, the final index map will be inverted
- /// if true, the internal map in the internal dictionary will be overwritten
- ///
- public int[] UpdateIndexMap(
- int availableViews,
- int contentViews,
- float yoyoStart,
- bool invert,
- bool invertIndices = false
- )
- {
- int[] mapArray = GetIndexMapTemp(
- availableViews,
- contentViews,
- yoyoStart,
- invert,
- invertIndices
- );
- currentMap = mapArray;
- return currentMap;
- }
+ int i;
+ // automatic distribution
+ if (contentViews == 2)
+ {
+ i = generateTwoContentViews(ref indexmap, availableViews, delimiterViews);
+ }
+ else
+ {
+ i = generateMoreThanTwoContentViews(
+ ref indexmap,
+ availableViews,
+ contentViews,
+ delimiterViews
+ );
+ }
+ for (int j = i; j < availableViews; j++)
+ indexmap[j] = 0;
- ///
- /// Creates a new index map without storing it in the internal dictionary.
- ///
- /// number of available views on the display. has to be larger than 0
- /// number of views your content has. has to be larger than 0
- /// set the percentage where the inversion will start (e.g. 0, 1,2,3,2,1,0); will be clamped between 0.0 and 1.0
- /// if true, the final index map will be inverted
- ///
- public int[] GetIndexMapTemp(
- int availableViews,
- int contentViews,
- float yoyoStart,
- bool invert,
- bool invertIndices = false
- )
- {
- int[] mapArray = new int[availableViews];
- generate_indexmap(
- ref mapArray,
- availableViews,
- contentViews,
- 0,
- yoyoStart,
- invert,
- invertIndices
- );
- return mapArray;
- }
+ if (invert)
+ {
+ int x = 0;
+ int y = availableViews - 1;
+ int tmp;
+ while (x < y)
+ {
+ tmp = indexmap[x];
+ indexmap[x] = indexmap[y];
+ indexmap[y] = tmp;
+ x++;
+ y--;
+ }
+ }
- public float[] convertToFloatArray(in int[] intArray)
- {
- float[] floatArray = new float[intArray.Length];
- for (int i = 0; i < intArray.Length; i++)
- {
- floatArray[i] = intArray[i];
+ if (delimiterViews == 0.0f)
+ {
+ if (indexmap[0] == BLANK_VIEW)
+ indexmap[0] = 0;
+ for (int x = 1; x < availableViews; x++)
+ {
+ if (indexmap[x] == BLANK_VIEW)
+ indexmap[x] = indexmap[x - 1];
+ }
+ }
}
- return floatArray;
- }
- public float[] currentMapAsFloatArray()
- {
- return convertToFloatArray(currentMap);
- }
+ private int generateTwoContentViews(
+ ref int[] indexmap,
+ int availableViews,
+ float delimiterViews
+ )
+ {
+ int i = 0;
+ float eyeAreaSpace = (1.0f - delimiterViews) / 2;
+
+ int delimViews = (int)(availableViews * delimiterViews);
+ int eyeViews = (int)(availableViews * eyeAreaSpace);
+ int startDelimViews = (availableViews - delimViews - eyeViews * 2) / 2;
+ int endDelimViews = availableViews - delimViews - eyeViews * 2 - startDelimViews;
+
+ for (; startDelimViews > 0; startDelimViews--)
+ indexmap[i++] = BLANK_VIEW;
+ for (int j = 0; j < eyeViews; j++)
+ indexmap[i++] = 1;
+ for (; delimViews > 0; delimViews--)
+ indexmap[i++] = BLANK_VIEW;
+ for (int j = 0; j < eyeViews; j++)
+ indexmap[i++] = 0;
+ for (; endDelimViews > 0; endDelimViews--)
+ indexmap[i++] = BLANK_VIEW;
+
+ return i;
+ }
- public float[] getPaddedIndexMapArray()
- {
- float[] paddedArray = new float[256];
- float[] indexMapArray = currentMapAsFloatArray();
- for (int i = 0; i < paddedArray.Length; i++)
+ private int generateMoreThanTwoContentViews(
+ ref int[] indexmap,
+ int availableViews,
+ int contentViews,
+ float delimiterViews
+ )
{
- if (i < indexMapArray.Length)
+ int i = 0;
+ int delimViewsStart = (int)(availableViews * delimiterViews / 2);
+ int delimViewsEnd = (int)(availableViews * delimiterViews / 2);
+ int spacePerView = (availableViews - delimViewsStart - delimViewsEnd) / contentViews;
+ int spaceLeftover =
+ availableViews - delimViewsStart - delimViewsEnd - spacePerView * contentViews;
+
+ for (; delimViewsStart > 0; delimViewsStart--)
+ indexmap[i++] = BLANK_VIEW;
+ for (int viewIndex = 0; viewIndex < contentViews; viewIndex++)
{
- paddedArray[i] = indexMapArray[i];
- }
- else
- {
- paddedArray[i] = 0.0f;
+ int offset = 0;
+ if (spaceLeftover > 0)
+ {
+ offset = 1;
+ spaceLeftover--;
+ }
+ for (int currentSpace = spacePerView + offset; currentSpace > 0; currentSpace--)
+ {
+ indexmap[i++] = (int)(contentViews - 1 - viewIndex);
+ }
}
+ for (; delimViewsEnd > 0; delimViewsEnd--)
+ indexmap[i++] = BLANK_VIEW;
+
+ return i;
}
- return paddedArray;
}
}
diff --git a/Scripts/InspectorG3DCamera.cs b/Scripts/InspectorG3DCamera.cs
index c0f3a80..2479fac 100644
--- a/Scripts/InspectorG3DCamera.cs
+++ b/Scripts/InspectorG3DCamera.cs
@@ -5,92 +5,197 @@
using UnityEngine;
using UnityEngine.UIElements;
-[CustomEditor(typeof(G3DCamera))]
-public class InspectorG3DCamera : Editor
+namespace G3D
{
- public VisualTreeAsset inspectorXML;
+ [CustomEditor(typeof(G3DCamera))]
+ public class InspectorG3DCamera : Editor
+ {
+ public VisualTreeAsset inspectorXML;
- private PropertyField modeField;
+ private PropertyField modeField;
+ private PropertyField configurationFileField;
+ private PropertyField indexMapYoyoStartField;
+ private PropertyField invertIndexMapField;
+ private PropertyField invertIndexMapIndicesField;
- private PropertyField calibrationFileField;
- private PropertyField headtrackingScaleField;
+ private PropertyField headtrackingScaleField;
- private PropertyField viewOffsetField;
+ private PropertyField viewOffsetField;
+ private PropertyField focusDistanceField;
+ private PropertyField dollyZoomField;
+ private Button setCameraFOVButton;
- private Label calibFolderLabel;
- private Label DioramaCalibFileInfo;
+ private Label calibFolderLabel;
+ private Label HeadtrackingCalibFileInfo;
- private static bool isAdvancedSettingsVisible = false;
- private Foldout advancedSettingsFoldout;
+ private static bool isAdvancedSettingsVisible = false;
+ private Foldout advancedSettingsFoldout;
- public override VisualElement CreateInspectorGUI()
- {
- G3DCamera camera = (G3DCamera)target;
+ private Label IndexMap;
+
+ ///
+ /// This is used to hack the Unity Inspector.
+ /// Unity triggers a change event when the Inspector is first displayed, but it doesn't provide the new value in that event, so we have to store the last config file to detect when it actually changes.
+ ///
+ private TextAsset lastConfigFile;
+
+ public override VisualElement CreateInspectorGUI()
+ {
+ G3DCamera camera = (G3DCamera)target;
+ // Create a new VisualElement to be the root of our Inspector UI.
+ VisualElement mainInspector = new VisualElement();
+ // Load the UXML file.
+ inspectorXML = AssetDatabase.LoadAssetAtPath(
+ "Packages/com.3dglobal.core/Resources/G3DCameraInspector.uxml"
+ );
+
+ // Instantiate the UXML.
+ mainInspector = inspectorXML.Instantiate();
+
+ // Find the PropertyField in the Inspector XML.
+ modeField = mainInspector.Q("mode");
+
+ advancedSettingsFoldout = mainInspector.Q("AdvancedSettings");
+ advancedSettingsFoldout.value = isAdvancedSettingsVisible;
+
+ IndexMap = mainInspector.Q