[SOLVED] VR Launch title and icon in SteamVR

Interesting, there is no troubleshooting:vr catagory

Hello,
I am wondering (banging my head against the wall), how do I set the title and icon for when an application loads in vr in SteamVR? Right now it is just the default pinkish/purple icon with the title ‘java’.

I’m not even sure if it is part of the OpenVR library, or part of Steam’s. Or even what that is called. Googling has been insightful (just trying to figure out how I would do it in general, not even in LWJGL/JME).

Any ideas?

Thanks,
Trevor

1 Like

Just checked Steam’s developer portal for VR settings of a game, but the splash art was nowhere to be seen. So I’d guess it’s a OpenVR thing? Will do some more research and will let you know if I find out anything.

1 Like

Thanks!
All of my research keeps pointing me to here:
IVROverlay::SetOverlayTexture · ValveSoftware/openvr Wiki (github.com)

But I do not think that is what I am looking for. I did notice here though it looks like they are using the overlay, but it was not clear to me if that was inside the app or external during the loading phase.
openvr/SteamVR_LoadLevel.cs at master · ValveSoftware/openvr (github.com)

I think there must be a parameter to pass into the OpenVR API during initialization, that would not be a call to it like the set overlay. But I do not know at this point.

EDIT: Another interesting link: Unity native VR/XR: Scene Loading, Async, Custom Loading Screen - Unity Forum

The term “overlay” in OpenVR is usually meant as an application that renders over another application. Kinda like programs that render twitch chat over your controllers while playing games.

I found this, it looks like something more on the right track: https://github.com/ValveSoftware/openvr/blob/0eb70325a4ea3edca2a0ff8aefa631f2529e6eb7/headers/openvr.h#L2357

It is from EVRApplicationProperty Is there a way we can directly pass into these properties using these IDs?

This looks promising, now we need to find the function required to set those values. Might not be possible with jme-vr as it is though, IIRC calling most OpenVR functions needs references to some native objects for which I’m not sure whether jme-vr exposes them or not.
Tomorrow I can try going through my unfinished old attempt at a proper OpenVR implementation for jme to see whether I find any clues there.

1 Like

Sweet, thank you for the help. I will keep looking as well. I am amazed at the lack of documentation for OpenVR.

I hope LWJGL provides a OpenXR binding soon. Supposed to happen after the 3.2.4 release but that’s been delayed for a while now

These are the LWJGL links in the code to that. I still have not found how to set it, but it looks like I can get the property.

The sooner the better :slight_smile: Once that binding is released, I’d suggest we redo the entire jme-vr library, as it’s very much designed with 2016 era VR in mind where every manufacturer had their own API that needed to be supported. That abstraction layer made it impossible for me to figure out where to fit OpenVR’s newer action based input API, so I ended up just trying to rewrite entire OpenVR support. From what I’m hearing, the concept of this API has carried over to OpenXR, so something will need to be done regarding that.

After some searching I found this https://gist.github.com/krzys-h/98464aa2f4a1ad814358f8f078111366

Turns out the name and image need to be set in a .vrmanifest file which will then need to be registered by calling VRApplications.VRApplications_AddApplicationManifest() in LWJGL. Couldn’t find any documentation even mentioning how to make these manifest files, but found 2 of them in the SteamVR install directory itself. They’re basically just json files which can be easily opened with any text editor and you could use those as an example by which you can make your own manifest.

Sadly don’t have my Index set up at the moment, so can’t test it myself.

1 Like

Oh, interesting. That makes sense from what I was reading yesterday then. I was looking in Fallout4VR install directory and did not find anything, but I did not check any other install dir.

I have my Vive Pro setup and running right now, and a jme vr test project. I will see if I can get this working.

Here is some cpp form OpenVR-InputEmulator/main.cpp at master · matzman666/OpenVR-InputEmulator (github.com):

void installManifest(bool cleaninstall = false) {
	auto manifestQPath = QDir::cleanPath(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("manifest.vrmanifest"));
	if (QFile::exists(manifestQPath)) {
		bool alreadyInstalled = false;
		if (vr::VRApplications()->IsApplicationInstalled(inputemulator::OverlayController::applicationKey)) {
			if (cleaninstall) {
				char buffer[1024];
				auto appError = vr::VRApplicationError_None;
				vr::VRApplications()->GetApplicationPropertyString(inputemulator::OverlayController::applicationKey, vr::VRApplicationProperty_WorkingDirectory_String, buffer, 1024, &appError);
				if (appError == vr::VRApplicationError_None) {
					auto oldManifestQPath = QDir::cleanPath(QDir(buffer).absoluteFilePath("manifest.vrmanifest"));
					if (oldManifestQPath.compare(manifestQPath, Qt::CaseInsensitive) != 0) {
						vr::VRApplications()->RemoveApplicationManifest(QDir::toNativeSeparators(oldManifestQPath).toStdString().c_str());
					} else {
						alreadyInstalled = true;
					}
				}
			} else {
				alreadyInstalled = true;
			}
		}
		auto apperror = vr::VRApplications()->AddApplicationManifest(QDir::toNativeSeparators(manifestQPath).toStdString().c_str());
		if (apperror != vr::VRApplicationError_None) {
			throw std::runtime_error(std::string("Could not add application manifest: ") + std::string(vr::VRApplications()->GetApplicationsErrorNameFromEnum(apperror)));
		} else if (!alreadyInstalled || cleaninstall) {
			auto apperror = vr::VRApplications()->SetApplicationAutoLaunch(inputemulator::OverlayController::applicationKey, true);
			if (apperror != vr::VRApplicationError_None) {
				throw std::runtime_error(std::string("Could not set auto start: ") + std::string(vr::VRApplications()->GetApplicationsErrorNameFromEnum(apperror)));
			}
		}
	} else {
		throw std::runtime_error(std::string("Could not find application manifest: ") + manifestQPath.toStdString());
	}
}

void removeManifest() {
	auto manifestQPath = QDir::cleanPath(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("manifest.vrmanifest"));
	if (QFile::exists(manifestQPath)) {
		if (vr::VRApplications()->IsApplicationInstalled(inputemulator::OverlayController::applicationKey)) {
			vr::VRApplications()->RemoveApplicationManifest(QDir::toNativeSeparators(manifestQPath).toStdString().c_str());
		}
	} else {
		throw std::runtime_error(std::string("Could not find application manifest: ") + manifestQPath.toStdString());
	}
}

And their manifest:

OK, so here is my progress:

private static void registerVr() {
        //Register our manifest with SteamVR
        File manifest = new File("C:\\Users\\Trevor\\Desktop\\jme-tests\\src\\main\\resources\\outside.vrmanifest"); //Path to manifest
        if (manifest.exists()) {
            String path = manifest.getAbsolutePath(); //Must be absolute path

            //The bool is for temporary or permanent registration
            int error = VRApplications.VRApplications_AddApplicationManifest(path, false);

            //Check if we had an error
            if (error != VR.EVRApplicationError_VRApplicationError_None) {
                //OK, we had an error, what was it
                switch (error) {
                    case VR.EVRApplicationError_VRApplicationError_AppKeyAlreadyExists:
                        //If it already exists, we don't care, that is fine for now
                        //TODO: Other programs check if the registered manifest path is correct,
                        // and update it if not correct
                        logger.fine("VR Application already installed");
                        break;
                    default:
                        //Something else is wrong, we should report it
                        String reason = VRApplications.VRApplications_GetApplicationsErrorNameFromEnum(error);
                        logger.severe("Failed to register VR Manifest: " + reason);
                }
            } else {
                logger.info("VR Application installed");
            }
        } else {
            logger.warning("No VR Manifest found, but VR enabled!");
        }
    }
{
	"source" : "builtin",
	"applications": [{
		"app_key": "io.tlf.vr.test",
		"launch_type": "binary",
		"binary_path_windows": "java",
		"arguments": "-jar C:\\Users\\Trevor\\Desktop\\jme-tests\\build\\libs\\jme-tests-0.0.0.jar",
        "image_path": "Textures/vr_banner.png",
		"strings": {
			"en_us": {
				"name": "Trevor's VR Test",
				"description": "Testing VR Systems with JME"
			}
		}
	}]
}

It says it is registered, every time I launch the app, and it does not show up in my library, or even load the correct name or icon…

It is almost like Steam will not let anything else be visible other than steam apps. But I do not know because there are no errors.

Still not working, I feel like I have hit a dead end, but I will keep trying.
Here is my current test code:

package io.tlf.jme.test;

import com.jme3.app.*;
import com.jme3.app.state.AppState;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.vr.openvr.OpenVR;
import com.jme3.material.Material;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.CartoonSSAO;
import com.jme3.post.FilterPostProcessor;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
import com.jme3.util.SkyFactory;
import com.jme3.util.VRGUIPositioningMode;
import org.lwjgl.openvr.VRApplications;

import java.io.File;
import java.util.logging.Logger;

public class VRTest extends SimpleApplication {

    private static final Logger logger = Logger.getLogger(VRTest.class.getName());

    Spatial observer;
    boolean moveForward, moveBackwards, rotateLeft, rotateRight;
    Material mat;
    Node mainScene;

    VRAppState vrAppState = null;

    public VRTest(AppState... initialStates) {
        super(initialStates);

        vrAppState = getStateManager().getState(VRAppState.class);
    }


    @Override
    public void simpleInitApp() {

        logger.info("Updating asset manager with " + System.getProperty("user.dir"));
        getAssetManager().registerLocator(System.getProperty("user.dir") + File.separator + "assets", FileLocator.class);

        mainScene = new Node("scene");
        observer = new Node("observer");

        Spatial sky = SkyFactory.createSky(getAssetManager(), "Textures/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
        rootNode.attachChild(sky);



        // make the floor according to the size of our play area
        Geometry floor = new Geometry("floor", new Box(1f, 1f, 1f));

        Vector2f playArea = vrAppState.getVREnvironment().getVRBounds().getPlaySize();
        if (playArea == null) {
            // no play area, use default size & height
            floor.setLocalScale(2f, 0.5f, 2f);
            floor.move(0f, -1.5f, 0f);
        } else {
            // cube model is actually 2x as big, cut it down to proper playArea size with * 0.5
            floor.setLocalScale(playArea.x * 0.5f, 0.5f, playArea.y * 0.5f);
            floor.move(0f, -0.5f, 0f);
        }

        mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        floor.setMaterial(mat);
        rootNode.attachChild(floor);


        // test any positioning mode here (defaults to AUTO_CAM_ALL)
        vrAppState.getVRGUIManager().setPositioningMode(VRGUIPositioningMode.AUTO_OBSERVER_ALL);
        vrAppState.getVRGUIManager().setGuiScale(0.4f);

        observer.setLocalTranslation(new Vector3f(0.0f, 0.0f, 0.0f));

        vrAppState.setObserver(observer);
        mainScene.attachChild(observer);
        rootNode.attachChild(mainScene);

        initInputs();

        getInputManager().setCursorVisible(true);
    }


    private void initInputs() {
        InputManager inputManager = getInputManager();
        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping("incShift", new KeyTrigger(KeyInput.KEY_Q));
        inputManager.addMapping("decShift", new KeyTrigger(KeyInput.KEY_E));
        inputManager.addMapping("forward", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("filter", new KeyTrigger(KeyInput.KEY_F));
        inputManager.addMapping("dumpImages", new KeyTrigger(KeyInput.KEY_I));
        inputManager.addMapping("exit", new KeyTrigger(KeyInput.KEY_ESCAPE));

        ActionListener acl = new ActionListener() {

            public void onAction(String name, boolean keyPressed, float tpf) {
                if (name.equals("incShift") && keyPressed) {
                    vrAppState.getVRGUIManager().adjustGuiDistance(-0.1f);
                } else if (name.equals("decShift") && keyPressed) {
                    vrAppState.getVRGUIManager().adjustGuiDistance(0.1f);
                } else if (name.equals("filter") && keyPressed) {
                    // adding filters in realtime
                    CartoonSSAO cartfilt = new CartoonSSAO(vrAppState.isInstanceRendering());
                    FilterPostProcessor fpp = new FilterPostProcessor(getAssetManager());
                    fpp.addFilter(cartfilt);
                    getViewPort().addProcessor(fpp);
                    // filters added to main viewport during runtime,
                    // move them into VR processing
                    // (won't do anything if not in VR mode)
                    vrAppState.moveScreenProcessingToVR();
                }
                if (name.equals("toggle")) {
                    vrAppState.getVRGUIManager().positionGui();
                }
                if (name.equals("forward")) {
                    if (keyPressed) {
                        moveForward = true;
                    } else {
                        moveForward = false;
                    }
                } else if (name.equals("back")) {
                    if (keyPressed) {
                        moveBackwards = true;
                    } else {
                        moveBackwards = false;
                    }
                } else if (name.equals("dumpImages")) {
                    ((OpenVR) vrAppState.getVRHardware()).getCompositor().CompositorDumpImages.apply();
                } else if (name.equals("left")) {
                    if (keyPressed) {
                        rotateLeft = true;
                    } else {
                        rotateLeft = false;
                    }
                } else if (name.equals("right")) {
                    if (keyPressed) {
                        rotateRight = true;
                    } else {
                        rotateRight = false;
                    }
                } else if (name.equals("exit")) {
                    stop(true);
                    System.exit(0);
                }


            }
        };
        inputManager.addListener(acl, "forward");
        inputManager.addListener(acl, "back");
        inputManager.addListener(acl, "left");
        inputManager.addListener(acl, "right");
        inputManager.addListener(acl, "toggle");
        inputManager.addListener(acl, "incShift");
        inputManager.addListener(acl, "decShift");
        inputManager.addListener(acl, "filter");
        inputManager.addListener(acl, "dumpImages");
        inputManager.addListener(acl, "exit");
    }

    @Override
    public void simpleUpdate(float tpf) {

    }


    private static void registerVr() {
        //Register our manifest with SteamVR
        File manifest = new File("C:\\Users\\Trevor\\Desktop\\jme-tests\\src\\main\\resources\\outside.vrmanifest"); //Path to manifest
        if (manifest.exists()) {
            String path = manifest.getAbsolutePath(); //Must be absolute path

            String appKey = "io.tlf.vr.test";
            int error;

            //The bool is for temporary or permanent registration
            //error = VRApplications.VRApplications_RemoveApplicationManifest(path);
            //logger.info("Unregister VR Manifest: " + VRApplications.VRApplications_GetApplicationsErrorNameFromEnum(error));
            if (!VRApplications.VRApplications_IsApplicationInstalled(appKey)) {
                error = VRApplications.VRApplications_AddApplicationManifest(path, false);
                logger.info("Register VR Manifest: " + VRApplications.VRApplications_GetApplicationsErrorNameFromEnum(error));
            } else {
                logger.info("VR App already installed");
            }
            long pid = ProcessHandle.current().pid();
            logger.info("PID = " + pid);
            error = VRApplications.VRApplications_IdentifyApplication((int) pid, appKey);
            logger.info("Set VR Manifest: " + VRApplications.VRApplications_GetApplicationsErrorNameFromEnum(error));
        } else {
            logger.warning("No VR Manifest found, but VR enabled!");
        }

        //List apps
        /* This throws a native error...
        int count = VRApplications.VRApplications_GetApplicationCount();
        logger.info("Running apps: " + count);
        for (int i = 0; i < count; i++) {
            ByteBuffer buffer = ByteBuffer.allocate(VR.k_unMaxActionSetNameLength);
            int error = VRApplications.VRApplications_GetApplicationKeyByIndex(i, buffer);
            String name = new String(buffer.array());
            logger.info("Running app: " + name);
        }
         */
    }

    /**
     * Create a {@link VRAppState VR app state} and use a Simple application that use it.<br>
     * The recommended procedure is:<br>
     * <ul>
     * <li>Create some {@link AppSettings AppSettings} with VR related parameters.
     * <li>Instanciate the {@link VRAppState VRAppState} attached to the settings.
     * <li>Instanciate your {@link Application Application}.
     * <li>Attach the settings to the application.
     * <li>Start the application.
     * </ul>
     *
     * @param args not used
     */
    public static void main(String[] args) {

        // Prepare settings for VR rendering.
        // It is recommended to share same settings between the VR app state and the application.
        AppSettings settings = new AppSettings(true);

        settings.put(VRConstants.SETTING_VRAPI, VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE); // The VR api to use (need to be present on the system)
        settings.put(VRConstants.SETTING_DISABLE_VR, false);          // Enable VR
        settings.put(VRConstants.SETTING_ENABLE_MIRROR_WINDOW, true); // Enable Mirror rendering oh the screen (disable to be faster)
        settings.put(VRConstants.SETTING_VR_FORCE, false);            // Not forcing VR rendering if no VR system is found.
        settings.put(VRConstants.SETTING_FLIP_EYES, false);           // Is the HMD eyes have to be inverted.
        settings.put(VRConstants.SETTING_DEFAULT_FOV, 108f);          // The default field Of View (FOV)
        settings.put(VRConstants.SETTING_DEFAULT_ASPECT_RATIO, 1f);   // The default aspect ratio.

        settings.setRenderer(AppSettings.LWJGL_OPENGL32); // Setting the renderer. OpenGL 3 is needed if you're using Instance Rendering.

        // The VR Environment.
        // This object is the interface between the JMonkey world (Application, AppState, ...) and the VR specific stuff.
        VREnvironment environment = new VREnvironment(settings);
        environment.initialize();

        registerVr();

        settings.put(VRConstants.SETTING_NO_GUI, false);              // enable gui.
        settings.put(VRConstants.SETTING_GUI_OVERDRAW, true);         // show gui even if it is behind things.
        settings.put(VRConstants.SETTING_GUI_CURVED_SURFACE, true);   // Curve the mesh that is displaying the GUI

        // Checking if the VR environment is well initialized
        // (access to the underlying VR system is effective, VR devices are detected).
        if (environment.isInitialized()) {
            environment.setSettings(settings);
            // Initialise VR AppState with the VR environment.
            VRAppState vrAppState = new VRAppState(settings, environment);

            // Create the sample application with the VRAppState attached.
            // There is no constraint on the Application type.
            SimpleApplication test = new VRTest(vrAppState);
            test.setShowSettings(false);

            // Sharing settings between app state and application is recommended.
            test.setSettings(settings);

            // Starting the application.
            test.start();
        } else {
            logger.severe("Cannot start VR sample application as VR system is not initialized (see log for details)");
        }
    }
}
{
  "source": "builtin",
  "applications": [
    {
      "app_key": "io.tlf.vr.test",
      "launch_type": "binary",
      "binary_path_windows": "java",
      "arguments": "-jar C:\\Users\\Trevor\\Desktop\\jme-tests\\build\\libs\\jme-tests-0.0.0.jar",
      "image_path": "Textures/vr_banner.png",
      "strings": {
        "en_us": {
          "name": "Trevor's VR Test",
          "description": "Testing VR Systems with JME"
        }
      }
    }
  ]
}

From the output, it says it registered, but then cannot find the app:

Sep 27, 2020 11:21:56 AM com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVR initialize
INFO: Model Number : VIVE_Pro MV
Sep 27, 2020 11:21:56 AM com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVR initialize
INFO: Serial Number: LHR-A9D4E09A
Sep 27, 2020 11:21:56 AM com.jme3.input.vr.lwjgl_openvr.LWJGLOpenVR initialize
INFO: Initializing OpenVR system [SUCCESS]
Sep 27, 2020 11:21:56 AM io.tlf.jme.test.VRTest registerVr
INFO: Register VR Manifest: VRApplicationError_None
Sep 27, 2020 11:21:56 AM io.tlf.jme.test.VRTest registerVr
INFO: PID = 26436
Sep 27, 2020 11:21:56 AM io.tlf.jme.test.VRTest registerVr
INFO: Set VR Manifest: VRApplicationError_UnknownApplication
Sep 27, 2020 11:21:56 AM com.jme3.system.JmeDesktopSystem initialize

GOT IT!
OK, the exe path must be absolute, or relative to the manifest. You cannot use paths mapped with the path variable, aka java

{
  "source": "builtin",
  "applications": [
    {
      "app_key": "io.tlf.vr.test",
      "launch_type": "binary",
      "binary_path_windows": "C:\\Program Files\\Java\\jdk-11.0.2\\bin\\java.exe",
      "binary_path_linux": "java",
      "binary_path_linux_arm": "java",
      "binary_path_osx": "java",
      "arguments": "-jar C:\\Users\\Trevor\\Desktop\\jme-tests\\build\\libs\\jme-tests-0.0.0.jar",
      "image_path": "Textures/vr_banner.png",
      "action_manifest_path" : "none",
      "strings": {
        "en_us": {
          "name": "Trevor's VR Test",
          "description": "Testing VR Systems with JME"
        }
      }
    }
  ]
}

For debugging, use the SteamVR web console:
image
It was a great help.

OK, I am on the right track. I will do some more testing, and get this polished up.

EDIT: I’m not sure how to set the vertical image, other apps seem to have a way to do so. But I am happy with this progress so far.

3 Likes

OK, here is the working registration code:

private void registerVr() {
        File manifest = new File(VR_MANIFEST); //Path to manifest
        if (manifest.exists()) {
            String path = manifest.getAbsolutePath(); //Must be absolute path
            int error;
            if (!VRApplications.VRApplications_IsApplicationInstalled(VR_APP_KEY)) {
                error = VRApplications.VRApplications_AddApplicationManifest(path, false);
                LOGGER.info("Register VR Manifest: " + VRApplications.VRApplications_GetApplicationsErrorNameFromEnum(error));
            } else {
                LOGGER.info("VR App already installed");
            }

            //Now that we are registered, we set that the manifest goes to the current app
            long pid = ProcessHandle.current().pid();
            LOGGER.finest("VR PID = " + pid);
            error = VRApplications.VRApplications_IdentifyApplication((int) pid, VR_APP_KEY);
            LOGGER.info("Set VR Manifest: " + VRApplications.VRApplications_GetApplicationsErrorNameFromEnum(error));
        } else {
            LOGGER.severe("VR Manifest not found, but VR is enabled");
        }
    }

Take note, I have a two constants here, VR_MANIFEST is a path (absolute or relative, does not matter) to the .vrmanifest file. VR_APP_KEY is the app_key in the manifest, these must match.

Here is a sample manifest:

{
	"source" : "builtin",
	"applications": [{
		"app_key": "io.tlf.example.vr",
		"launch_type": "binary",
		"binary_path_windows": "OutsideClient.exe",
		"arguments": "",
        "image_path": "assets/Textures/VR/vr_banner.png",
		"action_manifest_path" : "none",
		"strings": {
			"en_us": {
				"name": "Outside",
				"description": "The Outside Engine"
			}
		}
	}]
}

Take note in my manifest, I am using the exe wrapper for my application. You do not need to do this, but if not, you will need to use an absolute path to java.exe and the arguments for launching your jar. The image path is relative to the manifest, not your application. Again, the app key must match what is in the constant in your program. This app key must be unique to your application.

Why do I run VRApplications.VRApplications_IdentifyApplication?
Answer: because otherwise it will take a program restart for the manifest to go into effect.

The documentation says that you can pass in 0 for the PID, why did you get the PID and pass it in?
Answer: Because the documentation is wrong, Unity discovered this, you need the PID.

What is the best size for the banner image?
Answer: According to Unity 460x215 px.

When do I run this in my code?
Answer: AFTER the VREnvironment has been initialized!!! (It will throw a native error otherwise)

If you have any other questions, please feel free to ask.

@grizeldi I also found a bug in the jme implementation for VRInput.

Should be:
controllerName = VRSystem.VRSystem_GetStringTrackedDeviceProperty(i, VR.ETrackedDeviceProperty_Prop_ModelNumber_String, error);

As the code stands right now, it just always returns lighthouse for me. I do not know if this is always the case, but for the Vive Pro and Vive Controllers and Vive Trackers it is true. I tested my trackers just to make sure.
By changing it, I get the actual model name:

INFO:   Tracked controller 3/64 VIVE Tracker Pro MV (HTC) attached.
INFO:   Tracked controller 6/64 VIVE Controller Pro MV (HTC) attached.
INFO:   Tracked controller 7/64 VIVE Controller Pro MV (HTC) attached.

I am marking this solved, and I hope it can be of use to someone in the future.

1 Like

The entirety of jme-vr will probably need a rewrite at some point and input is one of the main reasons for it. jme-vr was designed back in the early days of VR where every manufacturer had their own API. But once the things advanced enough that some APIs added new features others didn’t (looking at you, index finger tracking), there just isn’t a way to fit these new features into the design of jme-vr. And the fact that there is a bunch of unfinished code in there doesn’t help.

I’m really hoping OpenXR gets the industry wide manufacturer adoption we were promised. Once/if that happens, we’d only need to support a single API and that’s probably when the rewrite should happen.

3 Likes

It’s already supported by all major manufacturers. We’re waiting on lwjgl bindings, unless someone here wants to help spasi out and contribute that to them