@@ -50,8 +50,6 @@ import graphics.scenery.controls.TrackerInput
5050import graphics.scenery.primitives.*
5151import graphics.scenery.proteins.Protein
5252import graphics.scenery.proteins.RibbonDiagram
53- import graphics.scenery.utils.ExtractsNatives
54- import graphics.scenery.utils.ExtractsNatives.Companion.getPlatform
5553import graphics.scenery.utils.LogbackUtils
5654import graphics.scenery.utils.SceneryPanel
5755import graphics.scenery.utils.Statistics
@@ -84,8 +82,8 @@ import net.imglib2.type.numeric.RealType
8482import net.imglib2.type.numeric.integer.UnsignedByteType
8583import net.imglib2.view.Views
8684import org.joml.Quaternionf
85+ import org.joml.Vector2f
8786import org.joml.Vector3f
88- import org.joml.Vector4f
8987import org.scijava.Context
9088import org.scijava.`object`.ObjectService
9189import org.scijava.display.Display
@@ -100,8 +98,6 @@ import org.scijava.service.SciJavaService
10098import org.scijava.thread.ThreadService
10199import org.scijava.util.ColorRGB
102100import org.scijava.util.Colors
103- import org.scijava.util.VersionUtils
104- import sc.iview.commands.demo.animation.ParticleDemo
105101import sc.iview.commands.edit.InspectorInteractiveCommand
106102import sc.iview.event.NodeActivatedEvent
107103import sc.iview.event.NodeAddedEvent
@@ -131,12 +127,10 @@ import java.util.function.Predicate
131127import java.util.stream.Collectors
132128import kotlin.collections.ArrayList
133129import kotlin.collections.HashMap
134- import kotlin.collections.LinkedHashMap
135130import kotlin.concurrent.thread
136131import javax.swing.JOptionPane
137132import kotlin.math.cos
138133import kotlin.math.sin
139- import kotlin.system.measureTimeMillis
140134
141135/* *
142136 * Main SciView class.
@@ -1689,22 +1683,28 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
16891683 }
16901684
16911685 private var originalFOV = camera?.fov
1686+ private var originalWinSize = getWindowSize()
16921687
16931688 /* *
1694- * Enable VR rendering
1689+ * Enable or disable VR rendering. Automatically stores the original controls and FOV and restores them
1690+ * after VR is toggled off again.
1691+ * @param resizeWindow changes the window resolution to match the stereo rendering of the selected headset.
1692+ * @param resolutionScale Factor that allows changing the VR resolution
16951693 */
1696- fun toggleVRRendering () {
1694+ fun toggleVRRendering (resizeWindow : Boolean = true, resolutionScale : Float = 1f ) {
16971695 var renderer = renderer ? : return
16981696
16991697 // Save camera's original settings if we switch from 2D to VR
17001698 if (! vrActive) {
17011699 originalFOV = camera?.fov
1700+ originalWinSize = getWindowSize()
17021701 }
17031702
17041703 // If turning off VR, store the controls state before deactivating
17051704 if (vrActive) {
17061705 // We're about to turn off VR
17071706 controls.stashControls()
1707+ setWindowSize(originalWinSize.first, originalWinSize.second)
17081708 }
17091709
17101710 vrActive = ! vrActive
@@ -1721,6 +1721,20 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
17211721 if (hmd.initializedAndWorking()) {
17221722 hub.add(SceneryElement .HMDInput , hmd)
17231723 ti = hmd
1724+ // Disable the sidebar if it was still open
1725+ if ((mainWindow as SwingMainWindow ).sidebarOpen) {
1726+ toggleSidebar()
1727+ }
1728+ if (resizeWindow) {
1729+ val perEyeResolution = hmd.getRenderTargetSize()
1730+ // Recommended resolution is about x1.33 larger than the actual headset resolution
1731+ // due to distortion compensation.
1732+ // Too high resolution gets in the way of volume rendering, so we scale it down a bit again
1733+ setWindowSize(
1734+ (perEyeResolution.x * 2f / 1.33f * resolutionScale).toInt(),
1735+ (perEyeResolution.y / 1.33f * resolutionScale).toInt()
1736+ )
1737+ }
17241738 } else {
17251739 logger.warn(" Could not initialise VR headset, just activating stereo rendering." )
17261740 }
@@ -1916,6 +1930,57 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
19161930 println (scijavaContext!! .serviceIndex)
19171931 }
19181932
1933+ /* *
1934+ * Set the window dimensions of the sciview rendering window.
1935+ * This is essential for VR headsets that require specific resolutions.
1936+ *
1937+ * @param width The desired width of the window in pixels
1938+ * @param height The desired height of the window in pixels
1939+ * @return true if the window was successfully resized, false otherwise
1940+ */
1941+ fun setWindowSize (width : Int , height : Int ): Boolean {
1942+ if (width <= 0 || height <= 0 ) {
1943+ log.error(" Window dimensions must be positive: width=$width , height=$height " )
1944+ return false
1945+ }
1946+
1947+ try {
1948+ // Update internal dimensions
1949+ windowWidth = width
1950+ windowHeight = height
1951+
1952+ // Update the main window frame if it exists
1953+ if (mainWindow is SwingMainWindow ) {
1954+ val swingWindow = mainWindow as SwingMainWindow
1955+ val scale = getScenerySettings().get(" Renderer.SurfaceScale" ) ? : Vector2f (1f )
1956+ val scaledWidth = (width / scale.x()).toInt()
1957+ val scaleHeight = (height / scale.y()).toInt()
1958+ // We need to scale the swing window with taking the surface scale into account
1959+ swingWindow.frame.setSize(scaledWidth, scaleHeight)
1960+
1961+ // Update the renderer dimensions
1962+ // TODO Is this even needed? Since outdated semaphores will trigger a swapchain recreation anyway
1963+ renderer?.reshape(width, height)
1964+ }
1965+
1966+ log.info(" Window resized to ${width} x${height} " )
1967+ return true
1968+ } catch (e: Exception ) {
1969+ log.error(" Failed to resize window: ${e.message} " )
1970+ e.printStackTrace()
1971+ return false
1972+ }
1973+ }
1974+
1975+ /* *
1976+ * Get the current window dimensions.
1977+ *
1978+ * @return a Pair containing the width and height of the window
1979+ */
1980+ fun getWindowSize (): Pair <Int , Int > {
1981+ return Pair (windowWidth, windowHeight)
1982+ }
1983+
19191984 /* *
19201985 * Return the color table corresponding to the [lutName]
19211986 * @param lutName a String represening an ImageJ style LUT name, like Fire.lut
@@ -2002,6 +2067,26 @@ class SciView : SceneryBase, CalibratedRealInterval<CalibratedAxis> {
20022067 val sciViewService = context.service(SciViewService ::class .java)
20032068 return sciViewService.orCreateActiveSciView
20042069 }
2070+
2071+ /* *
2072+ * Static launching method with custom window dimensions
2073+ *
2074+ * @param width The desired width of the window in pixels
2075+ * @param height The desired height of the window in pixels
2076+ * @return a newly created SciView with specified dimensions
2077+ */
2078+ @JvmStatic
2079+ @Throws(Exception ::class )
2080+ fun create (width : Int , height : Int ): SciView {
2081+ xinitThreads()
2082+ val context = Context (ImageJService ::class .java, SciJavaService ::class .java, SCIFIOService ::class .java)
2083+ val objectService = context.service(ObjectService ::class .java)
2084+ objectService.addObject(Utils .SciviewStandalone ())
2085+ val sciViewService = context.service(SciViewService ::class .java)
2086+ val sciView = sciViewService.orCreateActiveSciView
2087+ sciView.setWindowSize(width, height)
2088+ return sciView
2089+ }
20052090
20062091 /* *
20072092 * Static launching method
0 commit comments