Ambiera Forum

Discussions, Help and Support.

Ambiera Forum > CopperCube > Help with CopperCube
Node.js Bridge2 OpenVR

Noobski
Guest
Quote
2024-03-09 22:05:54

So, Hypothetically speaking, say if i were to have converted the OpenVR SDK with bindings to Node.js and i have the full bridge with all necessary dependencies, is there anyone out there in this comunity that knows to to program interactions with an sdk? / I have everything but i just lack the knowledge to program the interaction through my node.js server at the moment so im searching for help and insight. The plan is to give the community SteamVR support on windows .exe target. If you want you can see my other post for WebGl WebXR support.


Guest
Guest
Quote
2024-03-10 00:03:03

Ask OkeOke!


okeoke
Registered User
Quote
2024-03-10 10:33:37

Have you really coded something or just found a publicly available repohttps://github.com/node-xr/node-...

Also how does it go with ordering peasant models? Do you already have them?

First of all, I haven't ever coded for vr, so I'm not quite sure I understand it properly, and I'm too lazy to figure it out.

Open vr repo states that the whole SDK contains of driver and application API, which is a C++ library. https://github.com/ValveSoftware/openvr/wiki/API-Documentation. You don't interact with SDK, you're adding this C++ library to your project as an internal dependency.

Same page with documentation contains the following description:
One real-world example of an application is a game engine like Unity. Unity calls OpenVR API to get the position and orientation of any attached VR headset and apply them to the Main Camera. Unity then sends the camera image to OpenVR. OpenVR does some operations on the image and then displays it to the real headset screen.


In case of node js bindings, it's just the way of calling C++ code from node js environment, described here: https://medium.com/@koistya/how-to-call-c-c-code-from-node-js-86a773033892.

Basically, you're running an application which interacts with vr headset in as a separate process. The way to establish communication between processes is called IPC. There are different ways on how IPC could be organized, but since coppercube is a closed source engine, there are two things you can do with its js bindings: communicate via http, which will not work because of latency, and communication with file system.

You can use filesystem to pass user input from node js app, but I have no idea how to pass picture back to node js, and I don't think it is possible. You might use some other tool for that.


Noobski
Guest
Quote
2024-03-11 03:48:22

So i had no idea that there was one already out i will have to check that out. But yes, i have built something up. I created a C++ node.js addon i dont remember all the steps i took because it was so much but i made the addon in visual studios and compiled it with all the dependencies, i ended up with a bunch of c++ header files with calls for node.js and i had the openvr sdk aswell created a bindings.gyp file and compiled the addon with everything in the projects directory, there was a lot more steps but forgot them and ended up with a openvr.node binary file. now at this point i believe i now have everything, getting the binary was the last step. Now all thats left is to code a node.js server that can communicate with the .exe client and the binarary by having the correct calls and responses set up, then the binary will interface with the actual openvr sdk. // on another note for the webgl i have gotten it to start a xr session and force the popup to ask permission to access a vr headset and select it but im stuck there it just keeps displaying on the webpage but i did get it to actually start steamvr one time when i checked for headset, and it showed the steam vr popup for the hmd and controllers and the xr session started but didnt move to the headset. i believe it may be because i dont have the proper settings done to my actual coppercube game like cameras and so forth as well as when i check developer console a lot of the functions for the xr session or undefined meaning i need to further the code and add all the logic for the xr session like projectionmatrix and , those kinda things


Noobski
Guest
Quote
2024-03-11 04:11:24

So, yeah i wish i had known about that git link you just wrote that would have saved me so much time you dont even understand. but so that git link one was good but it didnt finish the last two steps, i went ahead and downloaded it and installed nan and other dependencies and then used g++ to compile all of it and ended up with the same result as i did on the one i made. in like 5mins is all it took me using the git link you posted. for mines i started from scratch and it took me roughly two weeks to complete it because i kept running into errors and had to problem solve. so yeah we're still at the same place i am in possession of everything we need to give our coppercube games steamvr support we just need to come together and figure out how to code a nod.js server to interact with it. im already talking to chatgpt and trying to gain us some ground> Ill keep everyone posted and okeoke once i figure out how to zip it all up i shoot you a copy if you want to get in on it too.


hadoken
Guest
Quote
2024-03-11 08:23:11

Thank you for bringing this topic to the table @Noobski, CopperCube VR support for popular headsets, WebXR and Steam VR would be much appreciated and desirable for the future.


Noobski
Guest
Quote
2024-03-11 13:58:18

Need insight, this is my nan init method according to my node module: /// inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType );
NAN_METHOD(VR_Init);
//so how would i set this part in my server: try {
// Use your openvr.node addon here to initialize OpenVR
const openvr = require('./public/openvr.node');
// Add your OpenVR initialization logic using 'openvr' module
const vrSystem = openvr.VR_Init(openvr.EVRApplicationType.Scene);
if (vrSystem) {
console.log('OpenVR initialization successful!');
res.status(200).send('OpenVR initialization successful!'); //im getting error on server side when it recieves the get /init-openvr from client when it gets to the .Scene part it says error reading "Scene". i know i didnt set it up correctly anyone know how i would write that part according to how my module states the nan method for it?


Noobski
Guest
Quote
2024-03-11 14:08:39

Im so close i just need to figure out how to properly structure my server.js. im going to post my openvr.h node.js nan methods and my current server.js so someone can help me structure it. openvr.h: ifndef NODE_OPENVR_H
define NODE_OPENVR_H

include <nan.h>

/// inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType );
NAN_METHOD(VR_Init);

/// inline void VR_Shutdown();
NAN_METHOD(VR_Shutdown);

/// VR_INTERFACE bool VR_CALLTYPE VR_IsHmdPresent();
NAN_METHOD(VR_IsHmdPresent);

/// VR_INTERFACE bool VR_CALLTYPE VR_IsRuntimeInstalled();
NAN_METHOD(VR_IsRuntimeInstalled);

/// VR_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();
NAN_METHOD(VR_RuntimePath);

/// VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsSymbol( EVRInitError error );
NAN_METHOD(VR_GetVRInitErrorAsSymbol);

/// VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsEnglishDescription( EVRInitError error );
NAN_METHOD(VR_GetVRInitErrorAsEnglishDescription);

/// VR_INTERFACE void *VR_CALLTYPE VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError );
// Not implemented.

/// VR_INTERFACE bool VR_CALLTYPE VR_IsInterfaceVersionValid( const char *pchInterfaceVersion );
// Not implemented.

/// VR_INTERFACE uint32_t VR_CALLTYPE VR_GetInitToken();
NAN_METHOD(VR_GetInitToken);

endif // NODE_OPENVR_H


Noobski
Guest
Quote
2024-03-11 14:09:54

current server.js // server.js
const express = require('express');
const path = require('path');
const nan = require('nan');

const app = express();
const port = 3000;

// Serve static files (including openvr.node)
app.use(express.static(path.join(__dirname, 'public')));

// Initialize OpenVR when receiving a GET request
app.get('/init-openvr', (req, res) => {
console.log('Received /init-openvr request');

try {
// Use your openvr.node addon here to initialize OpenVR
const openvr = require('./public/openvr.node');
// Add your OpenVR initialization logic using 'openvr' module
const vrSystem = openvr.VR_Init(openvr.EVRApplicationType.Scene);
if (vrSystem) {
console.log('OpenVR initialization successful!');
res.status(200).send('OpenVR initialization successful!');
} else {
console.error('Failed to initialize OpenVR');
res.status(500).send('Failed to initialize OpenVR');
}
} catch (error) {
console.error('Error loading openvr.node:', error);
res.status(500).send('Internal Server Error');
}
});

// Start the server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`);
});


Noobski
Guest
Quote
2024-03-11 14:13:00

As stated before i got my client to send the get /init-openvr and my server recieves it but gets error trying to initiate saying cannot read the ".Scene" part which is the last part before it will actually initialize. i dont have that line in the server.js written properly thats what i currently need help with. Im still a beginner so idk how my nan method is telling me to really write it out in the server.


Noobski
Guest
Quote
2024-03-11 14:13:57

And your welcome @hadoken for bringing this up. I know we all want and need this.


Noobski
Guest
Quote
2024-03-11 14:50:25

this is the modules bindings for the node.js may help: include "ivrsystem.h"
include "openvr.h"

include <nan.h>

void Initialize(
v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context
) {
Nan::Set(
exports,
Nan::New("VR_Init").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_Init)).ToLocalChecked()
);
Nan::Set(
exports,
Nan::New("VR_Shutdown").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_Shutdown)).ToLocalChecked()
);
Nan::Set(
exports,
Nan::New("VR_IsHmdPresent").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_IsHmdPresent)).ToLocalChecked()
);
Nan::Set(
exports,
Nan::New("VR_IsRuntimeInstalled").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_IsRuntimeInstalled)).ToLocalChecked()
);
Nan::Set(
exports,
Nan::New("VR_RuntimePath").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_RuntimePath)).ToLocalChecked()
);
Nan::Set(
exports,
Nan::New("VR_GetVRInitErrorAsSymbol").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_GetVRInitErrorAsSymbol)).ToLocalChecked()
);
Nan::Set(
exports,
Nan::New("VR_GetVRInitErrorAsEnglishDescription").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_GetVRInitErrorAsEnglishDescription)).ToLocalChecked()
);
Nan::Set(
exports,
Nan::New("VR_GetInitToken").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(VR_GetInitToken)).ToLocalChecked()
);
IVRSystem::Init(exports);
}

NODE_MODULE(openvr, Initialize);


Noobski
Guest
Quote
2024-03-11 16:18:01

this is the openvr.ccp: // inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType );
NAN_METHOD(VR_Init)
{
if (info.Length() != 1)
{
Nan::ThrowError("Wrong number of arguments.");
return;
}

if (!info[0]->IsNumber())
{
Nan::ThrowTypeError("Argument[0] must be a number (EVRApplicationType).");
return;
}

uint32_t applicationType = Nan::To<int32_t>(info[0]).ToChecked();
// TODO: is there a better way to do this?
constexpr uint32_t applicationTypeMax = vr::VRApplication_Max;
if (applicationType >= applicationTypeMax)
{
Nan::ThrowTypeError("Argument[0] was out of enum range (EVRApplicationType).");
return;
}

// Perform the actual wrapped call.
vr::EVRInitError error;
vr::IVRSystem *system = vr::VR_Init(
&error,
static_cast<vr::EVRApplicationType>(applicationType)
);

// If the VR system failed to initialize, immediately raise a node exception.
if (system == nullptr)
{
Nan::ThrowError(vr::VR_GetVRInitErrorAsEnglishDescription(error));
return;
}

// Wrap the resulting system in the correct wrapper and return it.
auto result = IVRSystem::NewInstance(system);
info.GetReturnValue().Set(result);
}

//=============================================================================
// inline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType );
NAN_METHOD(VR_Shutdown)
{
if (info.Length() != 0)
{
Nan::ThrowError("Wrong number of arguments.");
return;
}

vr::VR_Shutdown();
}

//=============================================================================
/// VR_INTERFACE bool VR_CALLTYPE VR_IsHmdPresent();
NAN_METHOD(VR_IsHmdPresent)
{
if (info.Length() != 0)
{
Nan::ThrowError("Wrong number of arguments.");
return;
}

const auto result = vr::VR_IsHmdPresent();
info.GetReturnValue().Set(Nan::New<Boolean>(result));
}

//=============================================================================
/// VR_INTERFACE bool VR_CALLTYPE VR_IsRuntimeInstalled();
NAN_METHOD(VR_IsRuntimeInstalled)
{
if (info.Length() != 0)
{
Nan::ThrowError("Wrong number of arguments.");
return;
}

const auto result = vr::VR_IsRuntimeInstalled();
info.GetReturnValue().Set(Nan::New<Boolean>(result));
}

//=============================================================================
/// VR_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();
NAN_METHOD(VR_RuntimePath)
{
if (info.Length() != 0)
{
Nan::ThrowError("Wrong number of arguments.");
return;
}

const char *result = vr::VR_RuntimePath();
info.GetReturnValue().Set(Nan::New<String>(result).ToLocalChecked());
}

//=============================================================================
/// VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsSymbol( EVRInitError error );
NAN_METHOD(VR_GetVRInitErrorAsSymbol)
{
if (info.Length() != 1)
{
Nan::ThrowError("Wrong number of arguments.");
return;
}

if (!info[0]->IsNumber())
{
Nan::ThrowTypeError("Argument[0] must be a number (EVRInitError).");
return;
}

uint32_t nError = Nan::To<int32_t>(info[0]).ToChecked();
vr::EVRInitError eError = static_cast<vr::EVRInitError>(nError);
const char *result = vr::VR_GetVRInitErrorAsSymbol(eError);
info.GetReturnValue().Set(Nan::New<String>(result).ToLocalChecked());
}

//=============================================================================
/// VR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsEnglishDescription( EVRInitError error );
NAN_METHOD(VR_GetVRInitErrorAsEnglishDescription)
{
if (info.Length() != 1)
{
Nan::ThrowError("Wrong number of arguments.");
return;
}

if (!info[0]->IsNumber())
{



okeoke
Registered User
Quote
2024-03-12 16:25:08

Ok, that's sort of cute. I don't understand what was the point of showing off and lying about writing binding for open vr? Tbh, it looks like you have no idea what you're doing.

Try the following:
1. Install node js version 18.17.1 (mb higher will also work dunno)
2. Install steam vr
3. Install git and add it to path environment variable
4. Create a folder on your hard drive, call it whatever you want;
5. Open command line and cd to the folder
6. Execute "git clone --recursive https://github.com/node-xr/node-openvr.git" - this will clone node-openvr package, and also download required dependencies;
7. From cmd "cd node-openvr"
8. Run "npm i" - this will run node-gyp, which is used to build node-openvr and also download requires node_modules
9. Cd back "cd .."
10. Run "npm init -y" - this will create package.json and lock files
11. Run "npm i express" to install express
12. Run "npm i ./node-openvr" to install open-vr module from local file system
13. Create index.js or server js file, or name it whatever you like, and paste:
const express = require('express');
const openvr = require('openvr');

const app = express();
const port = 3000;

// Initialize OpenVR when receiving a GET request
app.get('/init-openvr', (req, res) => {
console.log('Received /init-openvr request');

try {
const vrSystem = openvr.VR_Init(openvr.EVRApplicationType.Scene);
if (vrSystem) {
console.log('OpenVR initialization successful!');
res.status(200).send('OpenVR initialization successful!');
} else {
console.error('Failed to initialize OpenVR');
res.status(500).send('Failed to initialize OpenVR');
}
} catch (error) {
console.error('Error loading openvr.node:', error);
res.status(500).send('Internal Server Error');
}
});

// Start the server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`);
});

14. Run node index.js (or server.js, or whatever you called the file)

I don't have a vt headset, so there is no way for me to try it completely, but for me it opens vr application, and asks me to connect the device, so I suppose it works.


okeoke
Registered User
Quote
2024-03-12 16:31:46

Also, if I were doing that, I would do two things:
1. Remove express and use file system as IPC. This means save file to the file system from node and read it from CopperCube, and vice versa. CopperCube http client will give you 50 ms delay on every call, even if your server is running on localhost;
2. Think about how you would build node js app to .exe - probably, it's not that simple as you think.


Create reply:


Posted by: (you are not logged in)


Enter the missing letter in: "Interna?ional" (you are not logged in)


Text:

 

  

Possible Codes


Feature Code
Link [url] www.example.com [/url]
Bold [b]bold text[/b]
Image [img]http://www.example.com/image.jpg[/img]
Quote [quote]quoted text[/quote]
Code [code]source code[/code]

Emoticons


   






Copyright© Ambiera e.U. all rights reserved.
Privacy Policy | Terms and Conditions | Imprint | Contact