override protected void Render() { Lock = true; int mainRetry = 5; ClientContext context; do { context = new ClientContext("com.bivrost360.desktopplayer"); Thread.Sleep(50); }while (context == null && mainRetry-- > 0); DisplayConfig displayConfig = null; for (int retry = 0; retry < 12; retry++) { if (abort) { context.Dispose(); Lock = false; return; } displayConfig = context.GetDisplayConfig(); if (displayConfig != null) { int contextRetry = 0; do { context.update(); contextRetry++; if (abort) { context.Dispose(); Lock = false; return; } Thread.Sleep(1); } while (!displayConfig.CheckDisplayStartup() || contextRetry < 300); if (displayConfig.CheckDisplayStartup()) { break; } } } if (displayConfig == null) { context.Dispose(); Lock = false; return; } var numDisplayInputs = displayConfig.GetNumDisplayInputs(); if (numDisplayInputs != 1) { context.Dispose(); Lock = false; return; } var displayDimensions = displayConfig.GetDisplayDimensions(0); var numViewers = displayConfig.GetNumViewers(); if (numViewers != 1) { context.Dispose(); Lock = false; return; } var form = new RenderForm("BIVROST - OSVR"); form.Width = displayDimensions.Width; form.Height = displayDimensions.Height; form.ShowInTaskbar = false; var desc = new SwapChainDescription() { BufferCount = 1, ModeDescription = new ModeDescription(displayDimensions.Width, displayDimensions.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm), IsWindowed = true, OutputHandle = form.Handle, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput }; SwapChain swapChain; // Create DirectX drawing device. //SharpDX.Direct3D11.Device device = new Device(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.BgraSupport, new SharpDX.Direct3D.FeatureLevel[] { SharpDX.Direct3D.FeatureLevel.Level_10_0 }); Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport, desc, out _device, out swapChain); // Create DirectX Graphics Interface factory, used to create the swap chain. Factory factory = swapChain.GetParent <Factory>(); factory.MakeWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAll); form.FormBorderStyle = FormBorderStyle.None; form.TopMost = true; DeviceContext immediateContext = _device.ImmediateContext; using (SharpDX.DXGI.Device2 dxgiDevice = _device.QueryInterface <SharpDX.DXGI.Device2>()) { //var bounds = dxgiDevice.Adapter.Outputs[1].Description.DesktopBounds; //form.DesktopBounds = new System.Drawing.Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height); //dxgiDevice.Adapter.Outputs.ToList().ForEach(o => //{ // if (o.Description.DeviceName.EndsWith("2")) // { // swapChain.SetFullscreenState(true, o); // } //}); Rectangle bounds; if (Features.IsDebug) { log.Info("OSVR: available screens: " + string.Join("\n", dxgiDevice.Adapter.Outputs.ToList().ConvertAll(o => o.Description.DeviceName + " (" + o.Description.DesktopBounds + ")"))); } if (Logic.Instance.settings.OSVRScreen == ScreenSelection.Autodetect) { // start with last screen Output output = dxgiDevice.Adapter.Outputs[dxgiDevice.Adapter.Outputs.Length - 1]; // but something resembling a HDK 1.4 (1920x1080) will be better foreach (var o in dxgiDevice.Adapter.Outputs) { var b = o.Description.DesktopBounds; if (b.Width == 1920 && b.Height == 1080) { log.Info("OSVR: found a 1920x1080 candidate for a HDK 1.4"); output = o; } } // and something resembling a HDK 2.0 (2160x1200) will be even more better foreach (var o in dxgiDevice.Adapter.Outputs) { var b = o.Description.DesktopBounds; if (b.Width == 2160 && b.Height == 1200) { log.Info("OSVR: found a 2160x1200 candidate for a HDK 2.0"); output = o; } } bounds = output.Description.DesktopBounds; log.Info($"OSVR: guessed output ({bounds})"); } else { int osvrScreen = (int)Logic.Instance.settings.OSVRScreen; if (osvrScreen >= dxgiDevice.Adapter.Outputs.Length) { osvrScreen = dxgiDevice.Adapter.Outputs.Length - 1; } bounds = dxgiDevice.Adapter.Outputs[osvrScreen].Description.DesktopBounds; log.Info($"OSVR: selected output #{osvrScreen} ({bounds})"); } form.DesktopBounds = new System.Drawing.Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height); if (dxgiDevice.Adapter.Outputs.Length <= 1) { Logic.Notify("Only one screen is active. Press Control+S to stop the movie if needed."); } } // Create a depth buffer, using the same width and height as the back buffer. Texture2DDescription depthBufferDescription = new Texture2DDescription() { Format = Format.D32_Float, ArraySize = 1, MipLevels = 1, Width = displayDimensions.Width, Height = displayDimensions.Height, SampleDescription = new SampleDescription(1, 0), Usage = ResourceUsage.Default, BindFlags = BindFlags.DepthStencil, CpuAccessFlags = CpuAccessFlags.None, OptionFlags = ResourceOptionFlags.None }; // Retrieve the DXGI device, in order to set the maximum frame latency. using (SharpDX.DXGI.Device1 dxgiDevice = _device.QueryInterface <SharpDX.DXGI.Device1>()) dxgiDevice.MaximumFrameLatency = 1; using (_gd = SharpDX.Toolkit.Graphics.GraphicsDevice.New(_device)) using (customEffectL = GetCustomEffect(_gd)) using (customEffectR = GetCustomEffect(_gd)) //using (var primitive = GraphicTools.CreateGeometry(_projection, _gd)) using (vrui = new VRUI(_device, _gd)) using (Texture2D depthBuffer = new Texture2D(_device, depthBufferDescription)) using (DepthStencilView depthView = new DepthStencilView(_device, depthBuffer)) using (Texture2D backBuffer = Texture2D.FromSwapChain <Texture2D>(swapChain, 0)) using (RenderTargetView renderView = new RenderTargetView(_device, backBuffer)) { //primitive = GraphicTools.CreateGeometry(Projection, _gd); DateTime startTime = DateTime.Now; Vector3 position = new Vector3(0, 0, -1); #region Render loop DateTime lastTime = DateTime.Now; float deltaTime = 0; immediateContext.OutputMerger.SetTargets(depthView, renderView); form.GotFocus += (s, e) => OnGotFocus(); bool first = true; RenderLoop.Run(form, () => { if (abort) { form.Close(); return; } if (first) { // Start with default background SetDefaultScene(); OnGotFocus(); first = false; } UpdateContentIfRequested(); context.update(); float timeSinceStart = (float)(DateTime.Now - startTime).TotalSeconds; deltaTime = (float)(DateTime.Now - lastTime).TotalSeconds; lastTime = DateTime.Now; immediateContext.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); immediateContext.ClearRenderTargetView(renderView, Color.Black); uint viewer = 0; for (int eyeIndex = 0; eyeIndex < 2; eyeIndex++) { var numEyes = displayConfig.GetNumEyesForViewer(viewer); var viewerPose = displayConfig.GetViewerPose(viewer); for (byte eye = 0; eye < numEyes; eye++) { uint numSurfaces = displayConfig.GetNumSurfacesForViewerEye(viewer, eye); Pose3 viewerEyePose = displayConfig.GetViewerEyePose(viewer, eye); Matrix44f viewerEyeMatrixf = displayConfig.GetViewerEyeViewMatrixf(viewer, eye, MatrixConventionsFlags.Default); uint surface = 0; OSVR.ClientKit.Viewport viewport = displayConfig.GetRelativeViewportForViewerEyeSurface(viewer, eye, surface); Matrix44f projectionf = displayConfig.GetProjectionMatrixForViewerEyeSurfacef(viewer, eye, surface, 0.001f, 1000.0f, MatrixConventionsFlags.Default); ProjectionClippingPlanes projectionClippingPlanes = displayConfig.GetViewerEyeSurfaceProjectionClippingPlanes(viewer, eye, surface); ViewportF vp = new ViewportF(viewport.Left, viewport.Bottom, viewport.Width, viewport.Height); immediateContext.Rasterizer.SetViewport(vp); Vector3 lookPosition = viewerEyePose.translation.ToVector3(); SharpDX.Quaternion lookRotation = viewerEyePose.rotation.ToQuaternion(); Matrix rotationMatrix = Matrix.RotationQuaternion(lookRotation); Vector3 lookUp = Vector3.Transform(new Vector3(0, 1, 0), rotationMatrix).ToVector3(); Vector3 lookAt = Vector3.Transform(new Vector3(0, 0, -1), rotationMatrix).ToVector3(); Matrix viewMatrix = Matrix.LookAtRH(lookPosition, lookPosition + lookAt, lookUp); Matrix projectionMatrix = projectionf.ToMatrix(); Matrix worldMatrix = Matrix.Translation(lookPosition); Matrix MVP = worldMatrix * viewMatrix * projectionMatrix; customEffectL.Parameters["WorldViewProj"].SetValue(MVP); customEffectR.Parameters["WorldViewProj"].SetValue(MVP); lock (localCritical) { if (eye == 0) { primitive?.Draw(customEffectL); } if (eye == 1) { primitive?.Draw(customEffectR); } } // reset UI position every frame if it is not visible if (vrui.isUIHidden) { vrui.SetWorldPosition(viewMatrix.Forward, lookPosition, true); } if (eye == 0) { lookRotation.Invert(); ProvideLook?.Invoke(lookPosition, lookRotation, OSVRFOV); } vrui.Draw(Media, currentTime, Duration); vrui.Render(deltaTime, viewMatrix, projectionMatrix, lookPosition, ShouldShowVRUI); } } swapChain.Present(0, PresentFlags.None); }); #endregion //debugWindow.Stop(); waitForRendererStop.Set(); //swapChain.SetFullscreenState(false, null); immediateContext.ClearState(); immediateContext.Flush(); immediateContext.Dispose(); swapChain.Dispose(); factory.Dispose(); //swapChain.Dispose(); // Disposing the device, before the hmd, will cause the hmd to fail when disposing. // Disposing the device, after the hmd, will cause the dispose of the device to fail. // It looks as if the hmd steals ownership of the device and destroys it, when it's shutting down. // device.Dispose(); base._device.Dispose(); //hmd.Dispose(); //oculus.Dispose(); displayConfig.Dispose(); context.Dispose(); } Lock = false; }
protected override void Render() { EnsureDllsLoaded(); Lock = true; EVRInitError initError = EVRInitError.None; Valve.VR.OpenVR.Init(ref initError); if (initError != EVRInitError.None) { throw new Exception("OpenVR init error " + initError + ": " + Valve.VR.OpenVR.GetStringForHmdError(initError)); } CVRSystem hmd = Valve.VR.OpenVR.System; CVRCompositor compositor = Valve.VR.OpenVR.Compositor; uint targetWidth = 0, targetHeight = 0; hmd.GetRecommendedRenderTargetSize(ref targetWidth, ref targetHeight); float sceneWidth = (float)targetWidth; float sceneHeight = (float)targetHeight; float l_left = 0.0f, l_right = 0.0f, l_top = 0.0f, l_bottom = 0.0f; hmd.GetProjectionRaw(EVREye.Eye_Left, ref l_left, ref l_right, ref l_top, ref l_bottom); float r_left = 0.0f, r_right = 0.0f, r_top = 0.0f, r_bottom = 0.0f; hmd.GetProjectionRaw(EVREye.Eye_Right, ref r_left, ref r_right, ref r_top, ref r_bottom); Vector2 tanHalfFov = new Vector2( Math.Max(Math.Max(-l_left, l_right), Math.Max(-r_left, r_right)), Math.Max(Math.Max(-l_top, l_bottom), Math.Max(-r_top, r_bottom)) ); VRTextureBounds_t[] textureBounds = new VRTextureBounds_t[2]; textureBounds[0].uMin = 0.5f + 0.5f * l_left / tanHalfFov.X; textureBounds[0].uMax = 0.5f + 0.5f * l_right / tanHalfFov.X; textureBounds[0].vMin = 0.5f - 0.5f * l_bottom / tanHalfFov.Y; textureBounds[0].vMax = 0.5f - 0.5f * l_top / tanHalfFov.Y; textureBounds[1].uMin = 0.5f + 0.5f * r_left / tanHalfFov.X; textureBounds[1].uMax = 0.5f + 0.5f * r_right / tanHalfFov.X; textureBounds[1].vMin = 0.5f - 0.5f * r_bottom / tanHalfFov.Y; textureBounds[1].vMax = 0.5f - 0.5f * r_top / tanHalfFov.Y; float aspect = tanHalfFov.X / tanHalfFov.Y; float lookFov = (float)(2.0f * Math.Atan(tanHalfFov.Y) * 180 / Math.PI); Texture2DDescription eyeTextureDescription = new Texture2DDescription() { Format = Format.R8G8B8A8_UNorm_SRgb, ArraySize = 1, MipLevels = 1, Width = (int)targetWidth, Height = (int)targetHeight, SampleDescription = new SampleDescription(1, 0), Usage = ResourceUsage.Default, BindFlags = BindFlags.RenderTarget, CpuAccessFlags = CpuAccessFlags.None, OptionFlags = ResourceOptionFlags.None }; Texture2DDescription eyeDepthTextureDescription = new Texture2DDescription() { Format = Format.D32_Float_S8X24_UInt, ArraySize = 1, MipLevels = 1, Width = (int)targetWidth, Height = (int)targetHeight, SampleDescription = new SampleDescription(1, 0), Usage = ResourceUsage.Default, BindFlags = BindFlags.DepthStencil, CpuAccessFlags = CpuAccessFlags.None, OptionFlags = ResourceOptionFlags.None }; ///// CUBE //var cubeEffect = new SharpDX.Toolkit.Graphics.BasicEffect(_gd) //{ // //PreferPerPixelLighting = false, // //TextureEnabled = false, // LightingEnabled = true, // //DiffuseColor = new Vector4(0.5f,0.5f,0.5f,1f), // Sampler = _gd.SamplerStates.AnisotropicClamp //}; //cubeEffect.EnableDefaultLighting(); //var cube = SharpDX.Toolkit.Graphics.GeometricPrimitive.Teapot.New(_gd, 1, 8, false); ///// END using (_device = new Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport, new FeatureLevel[] { FeatureLevel.Level_10_0 })) using (DeviceContext context = _device.ImmediateContext) using (_gd = SharpDX.Toolkit.Graphics.GraphicsDevice.New(_device)) //using (SharpDX.Toolkit.Graphics.GeometricPrimitive primitive = GraphicTools.CreateGeometry(_projection, _gd, false)) using (customEffectL = GetCustomEffect(_gd)) using (customEffectR = GetCustomEffect(_gd)) using (Texture2D leftEye = new Texture2D(_device, eyeTextureDescription)) using (RenderTargetView leftEyeView = new RenderTargetView(_device, leftEye)) using (Texture2D leftEyeDepth = new Texture2D(_device, eyeDepthTextureDescription)) using (DepthStencilView leftEyeDepthView = new DepthStencilView(_device, leftEyeDepth)) using (Texture2D rightEye = new Texture2D(_device, eyeTextureDescription)) using (RenderTargetView rightEyeView = new RenderTargetView(_device, rightEye)) using (Texture2D rightEyeDepth = new Texture2D(_device, eyeDepthTextureDescription)) using (DepthStencilView rightEyeDepthView = new DepthStencilView(_device, rightEyeDepth)) using (vrui = new VRUI(_device, _gd)) { //primitive = GraphicTools.CreateGeometry(Projection, _gd, false); Stopwatch stopwatch = new Stopwatch(); Texture_t leftEyeTex = new Texture_t() { eColorSpace = EColorSpace.Gamma, eType = EGraphicsAPIConvention.API_DirectX, handle = leftEye.NativePointer }; Texture_t rightEyeTex = new Texture_t() { eColorSpace = EColorSpace.Gamma, eType = EGraphicsAPIConvention.API_DirectX, handle = rightEye.NativePointer }; TrackedDevicePose_t[] renderPoseArray = new TrackedDevicePose_t[16]; TrackedDevicePose_t[] gamePoseArray = new TrackedDevicePose_t[16]; TrackedDevicePose_t pose = new TrackedDevicePose_t(); try { // Start with default background SetDefaultScene(); while (!abort) { UpdateContentIfRequested(); float deltaTime = (float)stopwatch.Elapsed.TotalSeconds; stopwatch.Restart(); compositor.WaitGetPoses(renderPoseArray, gamePoseArray); if (renderPoseArray[Valve.VR.OpenVR.k_unTrackedDeviceIndex_Hmd].bPoseIsValid) { pose = gamePoseArray[Valve.VR.OpenVR.k_unTrackedDeviceIndex_Hmd]; } foreach (EVREye eye in new EVREye[] { EVREye.Eye_Left, EVREye.Eye_Right }) { DepthStencilView currentEyeDepthView = (eye == EVREye.Eye_Left) ? leftEyeDepthView : rightEyeDepthView; RenderTargetView currentEyeView = (eye == EVREye.Eye_Left) ? leftEyeView : rightEyeView; // Setup targets and viewport for rendering context.OutputMerger.SetTargets(currentEyeDepthView, currentEyeView); context.ClearDepthStencilView(currentEyeDepthView, DepthStencilClearFlags.Depth, 1.0f, 0); context.ClearRenderTargetView(currentEyeView, Color.Black); context.Rasterizer.SetViewport(new Viewport(0, 0, (int)targetWidth, (int)targetHeight, 0.0f, 1.0f)); Quaternion lookRotation; // = pose.mDeviceToAbsoluteTracking.GetRotation(); Vector3 lookPosition; // = pose.mDeviceToAbsoluteTracking.GetPosition(); Vector3 scale; Matrix eyePose = hmd.GetEyeToHeadTransform(eye).RebuildTRSMatrix() * pose.mDeviceToAbsoluteTracking.RebuildTRSMatrix(); eyePose.Decompose(out scale, out lookRotation, out lookPosition); Matrix rotationMatrix = Matrix.RotationQuaternion(lookRotation); Vector3 lookUp = Vector3.Transform(Vector3.Up, rotationMatrix).ToVector3(); Vector3 lookAt = Vector3.Transform(Vector3.ForwardRH, rotationMatrix).ToVector3(); //Console.WriteLine($"OpenVR {eye} up: {lookUp:00.00} at: {lookAt:00.00} position:{lookPosition:00.00}"); Matrix viewMatrix = Matrix.LookAtRH(lookPosition, lookPosition + lookAt, lookUp); Matrix pm1 = Matrix.PerspectiveFovLH(lookFov * ((float)Math.PI / 180f), aspect, 0.001f, 100.0f); //Matrix pm2 = hmd.GetProjectionMatrix(eye, 0.001f, 100f, EGraphicsAPIConvention.API_DirectX).ToProjMatrix(); Matrix projectionMatrix = pm1; Matrix worldMatrix = Matrix.Translation(lookPosition); if (Logic.Instance.settings.OpenVRReverse) { worldMatrix = Matrix.RotationY(180) * worldMatrix; } Matrix MVP = worldMatrix * viewMatrix * projectionMatrix; customEffectL.Parameters["WorldViewProj"].SetValue(MVP); customEffectR.Parameters["WorldViewProj"].SetValue(MVP); lock (localCritical) { if (eye == EVREye.Eye_Left) { primitive?.Draw(customEffectL); } if (eye == EVREye.Eye_Right) { primitive?.Draw(customEffectR); } } if (eye == EVREye.Eye_Left) { Vector3 fixedLookPosition = Vector3.Transform(lookPosition, MVP).ToVector3(); Vector3 fixedLookAt = Vector3.Transform(lookPosition + Vector3.ForwardLH, MVP).ToVector3() - fixedLookPosition; Vector3 fixedLookUp = Vector3.Transform(lookPosition + Vector3.Up, MVP).ToVector3() - fixedLookPosition; fixedLookAt.Normalize(); fixedLookUp.Normalize(); Quaternion fixedLookRotation = Quaternion.LookAtRH(Vector3.Zero, fixedLookAt, fixedLookUp); //fixedLookRotation.X *= -1; //fixedLookRotation.Y *= -1; fixedLookRotation.Z *= -1; //if(eye == EVREye.Eye_Left) // fixedLookRotation.Invert(); //Mirror effect of rotation along z axis by flipping x and y elements of the quaternion. //http://stackoverflow.com/a/32482386/785171 //float t = fixedLookRotation.X; //fixedLookRotation.X = fixedLookRotation.Y; //fixedLookRotation.Y = t; //fixedLookRotation = new Quaternion(1, 0, 0, 0) * fixedLookRotation; //fixedLookRotation = new Quaternion(0, 0, 1, 0) * fixedLookRotation; LoggerManager.Publish("openvr.forward", fixedLookAt); LoggerManager.Publish("openvr.up", fixedLookUp); // TODO: normalize ProvideLook?.Invoke(fixedLookPosition, fixedLookRotation, lookFov); } // reset UI position every frame if it is not visible if (vrui.isUIHidden) { vrui.SetWorldPosition(viewMatrix.Forward, lookPosition, false); } vrui.Draw(Media, currentTime, Duration); vrui.Render(deltaTime, viewMatrix, projectionMatrix, lookPosition, ShouldShowVRUI); //// controllers: //cubeEffect.View = viewMatrix; //cubeEffect.Projection = projectionMatrix; //for (uint controller = 1 /*skip hmd*/; controller < Valve.VR.OpenVR.k_unMaxTrackedDeviceCount; controller++) //{ // VRControllerState_t controllerState = default(VRControllerState_t); // //var controllerPose = renderPoseArray[controller]; // //if (hmd.GetControllerState(controller, ref controllerState)) { // Vector3 pos = renderPoseArray[controller].mDeviceToAbsoluteTracking.GetPosition(); // Quaternion rot = renderPoseArray[controller].mDeviceToAbsoluteTracking.GetRotation(); // rot = rot * new Quaternion(0, 1, 0, 0); // float s = controllerState.ulButtonPressed > 0 ? 0.5f : 0.1f; // cubeEffect.World = Matrix.Scaling(s) * Matrix.RotationQuaternion(rot) * Matrix.Translation(pos); // cube.Draw(cubeEffect); // //} //} } // RENDER TO HMD EVRCompositorError errorLeft = compositor.Submit( EVREye.Eye_Left, ref leftEyeTex, ref textureBounds[0], EVRSubmitFlags.Submit_Default ); EVRCompositorError errorRight = compositor.Submit( EVREye.Eye_Right, ref rightEyeTex, ref textureBounds[1], EVRSubmitFlags.Submit_Default ); if (errorLeft != EVRCompositorError.None) { throw new HeadsetError("VR Compositor failure (left): " + errorLeft); } if (errorRight != EVRCompositorError.None) { throw new HeadsetError("VR Compositor failure (right): " + errorRight); } } ; } finally { Valve.VR.OpenVR.Shutdown(); primitive?.Dispose(); context.ClearState(); context.Flush(); } } }