//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "platform/input/oculusVR/oculusVRDevice.h" #include "platform/platformInput.h" #include "core/module.h" #include "console/engineAPI.h" #include "T3D/gameBase/gameConnection.h" MODULE_BEGIN( OculusVRDevice ) MODULE_INIT_AFTER( InputEventManager ) MODULE_SHUTDOWN_BEFORE( InputEventManager ) MODULE_INIT { OculusVRDevice::staticInit(); ManagedSingleton< OculusVRDevice >::createSingleton(); if(OculusVRDevice::smEnableDevice) { OCULUSVRDEV->enable(); } // Register the device with the Input Event Manager INPUTMGR->registerDevice(OCULUSVRDEV); } MODULE_SHUTDOWN { INPUTMGR->unregisterDevice(OCULUSVRDEV); ManagedSingleton< OculusVRDevice >::deleteSingleton(); } MODULE_END; //----------------------------------------------------------------------------- // OculusVRDevice //----------------------------------------------------------------------------- bool OculusVRDevice::smEnableDevice = true; bool OculusVRDevice::smSimulateHMD = true; bool OculusVRDevice::smGenerateAngleAxisRotationEvents = true; bool OculusVRDevice::smGenerateEulerRotationEvents = false; bool OculusVRDevice::smGenerateRotationAsAxisEvents = false; F32 OculusVRDevice::smMaximumAxisAngle = 25.0f; bool OculusVRDevice::smGenerateWholeFrameEvents = false; OculusVRDevice::OculusVRDevice() { // From IInputDevice dStrcpy(mName, "oculusvr"); mDeviceType = INPUTMGR->getNextDeviceType(); // mEnabled = false; mActive = false; // We don't current support scaling of the input texture. The graphics pipeline will // need to be modified for this. mScaleInputTexture = false; mDeviceManager = NULL; mListener = NULL; buildCodeTable(); } OculusVRDevice::~OculusVRDevice() { cleanUp(); } void OculusVRDevice::staticInit() { Con::addVariable("pref::OculusVR::EnableDevice", TypeBool, &smEnableDevice, "@brief If true, the Oculus VR device will be enabled, if present.\n\n" "@ingroup Game"); Con::addVariable("OculusVR::GenerateAngleAxisRotationEvents", TypeBool, &smGenerateAngleAxisRotationEvents, "@brief If true, broadcast sensor rotation events as angled axis.\n\n" "@ingroup Game"); Con::addVariable("OculusVR::GenerateEulerRotationEvents", TypeBool, &smGenerateEulerRotationEvents, "@brief If true, broadcast sensor rotation events as Euler angles about the X, Y and Z axis.\n\n" "@ingroup Game"); Con::addVariable("OculusVR::GenerateRotationAsAxisEvents", TypeBool, &smGenerateRotationAsAxisEvents, "@brief If true, broadcast sensor rotation as axis events.\n\n" "@ingroup Game"); Con::addVariable("OculusVR::MaximumAxisAngle", TypeF32, &smMaximumAxisAngle, "@brief The maximum sensor angle when used as an axis event as measured from a vector pointing straight up (in degrees).\n\n" "Should range from 0 to 90 degrees.\n\n" "@ingroup Game"); Con::addVariable("OculusVR::GenerateWholeFrameEvents", TypeBool, &smGenerateWholeFrameEvents, "@brief Indicates that a whole frame event should be generated and frames should be buffered.\n\n" "@ingroup Game"); } void OculusVRDevice::cleanUp() { disable(); } void OculusVRDevice::buildCodeTable() { // Build the sensor device code table OculusVRSensorDevice::buildCodeTable(); } void OculusVRDevice::addHMDDevice(OVR::HMDDevice* hmd) { if(!hmd) return; OVR::HMDInfo hmdInfo; if(!hmd->GetDeviceInfo(&hmdInfo)) return; OculusVRHMDDevice* hmdd = new OculusVRHMDDevice(); hmdd->set(hmd, hmdInfo, mScaleInputTexture); mHMDDevices.push_back(hmdd); Con::printf(" HMD found: %s by %s [v%d]", hmdInfo.ProductName, hmdInfo.Manufacturer, hmdInfo.Version); } void OculusVRDevice::createSimulatedHMD() { OculusVRHMDDevice* hmdd = new OculusVRHMDDevice(); hmdd->createSimulation(OculusVRHMDDevice::ST_RIFT_PREVIEW, mScaleInputTexture); mHMDDevices.push_back(hmdd); Con::printf(" HMD simulated: %s by %s [v%d]", hmdd->getProductName(), hmdd->getManufacturer(), hmdd->getVersion()); } void OculusVRDevice::addSensorDevice(OVR::SensorDevice* sensor) { if(!sensor) return; OVR::SensorInfo sensorInfo; if(!sensor->GetDeviceInfo(&sensorInfo)) return; OculusVRSensorDevice* sensord = new OculusVRSensorDevice(); sensord->set(sensor, sensorInfo, mSensorDevices.size()); mSensorDevices.push_back(sensord); Con::printf(" Sensor found: %s by %s [v%d] %s", sensorInfo.ProductName, sensorInfo.Manufacturer, sensorInfo.Version, sensorInfo.SerialNumber); } void OculusVRDevice::createSimulatedSensor() { OculusVRSensorDevice* sensord = new OculusVRSensorDevice(); sensord->createSimulation(OculusVRSensorDevice::ST_RIFT_PREVIEW, mSensorDevices.size()); mSensorDevices.push_back(sensord); Con::printf(" Sensor simulated: %s by %s [v%d] %s", sensord->getProductName(), sensord->getManufacturer(), sensord->getVersion(), sensord->getSerialNumber()); } bool OculusVRDevice::enable() { // Start off with disabling the device if it is already enabled disable(); Con::printf("Oculus VR Device Init:"); OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); if(OVR::System::IsInitialized()) { mEnabled = true; // Create the OVR device manager mDeviceManager = OVR::DeviceManager::Create(); if(!mDeviceManager) { if(smSimulateHMD) { Con::printf(" Could not create a HMD device manager. Simulating a HMD."); Con::printf(" "); createSimulatedHMD(); createSimulatedSensor(); setActive(true); return true; } else { Con::printf(" Could not create a HMD device manager."); Con::printf(" "); mEnabled = false; OVR::System::Destroy(); return false; } } // Provide a message listener // NOTE: Commented out as non-functional in 0.1.2 //mListener = new DeviceListener(this); //mDeviceManager->SetMessageHandler(mListener); // Enumerate HMDs and pick the first one OVR::HMDDevice* hmd = mDeviceManager->EnumerateDevices().CreateDevice(); if(hmd) { // Add the HMD to our list addHMDDevice(hmd); // Detect and add any sensor on the HMD OVR::SensorDevice* sensor = hmd->GetSensor(); if(sensor) { addSensorDevice(sensor); } else { Con::printf(" No sensor device on HMD."); } setActive(true); } else { if(smSimulateHMD) { Con::printf(" Could not enumerate a HMD device. Simulating a HMD."); createSimulatedHMD(); createSimulatedSensor(); setActive(true); } else { Con::printf(" Could not enumerate a HMD device."); } } } Con::printf(" "); return false; } void OculusVRDevice::disable() { for(U32 i=0; iRelease(); mDeviceManager = NULL; } if(mEnabled) { OVR::System::Destroy(); } if(mListener) { delete mListener; mListener = NULL; } setActive(false); mEnabled = false; } bool OculusVRDevice::process() { if(!mEnabled) return false; if(!getActive()) return false; //Build the maximum axis angle to be passed into the sensor process() F32 maxAxisRadius = mSin(mDegToRad(smMaximumAxisAngle)); // Process each sensor for(U32 i=0; iprocess(mDeviceType, smGenerateAngleAxisRotationEvents, smGenerateEulerRotationEvents, smGenerateRotationAsAxisEvents, maxAxisRadius); } return true; } //----------------------------------------------------------------------------- bool OculusVRDevice::providesYFOV() const { if(!mHMDDevices.size()) return false; return true; } F32 OculusVRDevice::getYFOV() const { if(!mHMDDevices.size()) return 0.0f; const OculusVRHMDDevice* hmd = getHMDDevice(0); if(!hmd) return 0.0f; return hmd->getYFOV(); } bool OculusVRDevice::providesEyeOffset() const { if(!mHMDDevices.size()) return false; return true; } const Point3F& OculusVRDevice::getEyeOffset() const { if(!mHMDDevices.size()) return Point3F::Zero; const OculusVRHMDDevice* hmd = getHMDDevice(0); if(!hmd) return Point3F::Zero; return hmd->getEyeWorldOffset(); } bool OculusVRDevice::providesProjectionOffset() const { if(!mHMDDevices.size()) return false; return true; } const Point2F& OculusVRDevice::getProjectionOffset() const { if(!mHMDDevices.size()) return Point2F::Zero; const OculusVRHMDDevice* hmd = getHMDDevice(0); if(!hmd) return Point2F::Zero; return hmd->getProjectionCenterOffset(); } //----------------------------------------------------------------------------- const OculusVRHMDDevice* OculusVRDevice::getHMDDevice(U32 index) const { if(index >= mHMDDevices.size()) return NULL; return mHMDDevices[index]; } //----------------------------------------------------------------------------- const OculusVRSensorDevice* OculusVRDevice::getSensorDevice(U32 index) const { if(index >= mSensorDevices.size()) return NULL; return mSensorDevices[index]; } EulerF OculusVRDevice::getSensorEulerRotation(U32 index) { if(index >= mSensorDevices.size()) return Point3F::Zero; return mSensorDevices[index]->getEulerRotation(); } F32 OculusVRDevice::getSensorPredictionTime(U32 index) { const OculusVRSensorDevice* sensor = getSensorDevice(index); if(!sensor || !sensor->isValid()) return 0.0f; return sensor->getPredictionTime(); } void OculusVRDevice::setSensorPredictionTime(U32 index, F32 dt) { if(index >= mSensorDevices.size()) return; OculusVRSensorDevice* sensor = mSensorDevices[index]; if(!sensor->isValid()) return; sensor->setPredictionTime(dt); } void OculusVRDevice::setAllSensorPredictionTime(F32 dt) { for(U32 i=0; isetPredictionTime(dt); } } void OculusVRDevice::resetAllSensors() { // Reset each sensor for(U32 i=0; ireset(); } } //----------------------------------------------------------------------------- void OculusVRDevice::DeviceListener::OnMessage(const OVR::Message& msg) { switch(msg.Type) { case OVR::Message_DeviceAdded: { const OVR::MessageDeviceStatus* status = static_cast(&msg); Con::printf("OVR: Device added of type: %d", status->Handle.GetType()); } break; case OVR::Message_DeviceRemoved: Con::printf("OVR: Device removed of type: %d", msg.pDevice->GetType()); break; default: break; } } //----------------------------------------------------------------------------- DefineEngineFunction(isOculusVRDeviceActive, bool, (),, "@brief Used to determine if the Oculus VR input device is active\n\n" "The Oculus VR device is considered active when the library has been " "initialized and either a real of simulated HMD is present.\n\n" "@return True if the Oculus VR input device is active.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return false; } return OCULUSVRDEV->getActive(); } //----------------------------------------------------------------------------- DefineEngineFunction(setOVRHMDAsGameConnectionDisplayDevice, bool, (GameConnection* conn),, "@brief Sets the first HMD to be a GameConnection's display device\n\n" "@param conn The GameConnection to set.\n" "@return True if the GameConnection display device was set.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { Con::errorf("setOVRHMDAsGameConnectionDisplayDevice(): No Oculus VR Device present."); return false; } if(!conn) { Con::errorf("setOVRHMDAsGameConnectionDisplayDevice(): Invalid GameConnection."); return false; } conn->setDisplayDevice(OCULUSVRDEV); return true; } //----------------------------------------------------------------------------- DefineEngineFunction(getOVRHMDCount, S32, (),, "@brief Get the number of HMD devices that are currently connected.\n\n" "@return The number of Oculus VR HMD devices that are currently connected.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return 0; } return OCULUSVRDEV->getHMDCount(); } DefineEngineFunction(isOVRHMDSimulated, bool, (S32 index),, "@brief Determines if the requested OVR HMD is simulated or real.\n\n" "@param index The HMD index.\n" "@return True if the HMD is simulated.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return true; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return true; } return hmd->isSimulated(); } DefineEngineFunction(getOVRHMDProductName, const char*, (S32 index),, "@brief Retrieves the HMD product name.\n\n" "@param index The HMD index.\n" "@return The name of the HMD product.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return ""; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return ""; } return hmd->getProductName(); } DefineEngineFunction(getOVRHMDManufacturer, const char*, (S32 index),, "@brief Retrieves the HMD manufacturer name.\n\n" "@param index The HMD index.\n" "@return The manufacturer of the HMD product.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return ""; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return ""; } return hmd->getManufacturer(); } DefineEngineFunction(getOVRHMDVersion, S32, (S32 index),, "@brief Retrieves the HMD version number.\n\n" "@param index The HMD index.\n" "@return The version number of the HMD product.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return -1; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return -1; } return hmd->getVersion(); } DefineEngineFunction(getOVRHMDDisplayDeviceName, const char*, (S32 index),, "@brief Windows display device name used in EnumDisplaySettings/CreateDC.\n\n" "@param index The HMD index.\n" "@return The name of the HMD display device, if any.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return ""; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return ""; } return hmd->getDisplayDeviceName(); } DefineEngineFunction(getOVRHMDResolution, Point2I, (S32 index),, "@brief Provides the OVR HMD screen resolution.\n\n" "@param index The HMD index.\n" "@return A two component string with the screen's resolution.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return Point2I(1280, 800); } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return Point2I(1280, 800); } return hmd->getResolution(); } DefineEngineFunction(getOVRHMDDistortionCoefficients, String, (S32 index),, "@brief Provides the OVR HMD distortion coefficients.\n\n" "@param index The HMD index.\n" "@return A four component string with the distortion coefficients.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return "0 0 0 0"; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return "0 0 0 0"; } const Point4F& k = hmd->getKDistortion(); char buf[256]; dSprintf(buf, 256, "%g %g %g %g", k.x, k.y, k.z, k.w); return buf; } DefineEngineFunction(getOVRHMDEyeXOffsets, Point2F, (S32 index),, "@brief Provides the OVR HMD eye x offsets in uv coordinates.\n\n" "@param index The HMD index.\n" "@return A two component string with the left and right eye x offsets.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return Point2F(0.5, 0.5); } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return Point2F(0.5, 0.5); } // X component is left, Y component is right const Point2F& offset = hmd->getEyeUVOffset(); return offset; } DefineEngineFunction(getOVRHMDXCenterOffset, F32, (S32 index),, "@brief Provides the OVR HMD calculated XCenterOffset.\n\n" "@param index The HMD index.\n" "@return The calculated XCenterOffset.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return 0.0f; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return 0.0f; } F32 offset = hmd->getCenterOffset(); return offset; } DefineEngineFunction(getOVRHMDDistortionScale, F32, (S32 index),, "@brief Provides the OVR HMD calculated distortion scale.\n\n" "@param index The HMD index.\n" "@return The calculated distortion scale.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return 1.0f; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return 1.0f; } F32 scale = hmd->getDistortionScale(); return scale; } DefineEngineFunction(getOVRHMDYFOV, F32, (S32 index),, "@brief Provides the OVR HMD calculated Y FOV.\n\n" "@param index The HMD index.\n" "@return The calculated Y FOV.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return 1.0f; } const OculusVRHMDDevice* hmd = OCULUSVRDEV->getHMDDevice(index); if(!hmd) { return 1.0f; } F32 fov = hmd->getYFOV(); return mRadToDeg(fov); } //----------------------------------------------------------------------------- DefineEngineFunction(getOVRSensorCount, S32, (),, "@brief Get the number of sensor devices that are currently connected.\n\n" "@return The number of Oculus VR sensor devices that are currently connected.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return 0; } return OCULUSVRDEV->getSensorCount(); } DefineEngineFunction(getOVRSensorEulerRotation, Point3F, (S32 index),, "@brief Get the Euler rotation values for the given sensor index.\n\n" "@param index The sensor index.\n" "@return The Euler rotation values of the Oculus VR sensor, in degrees.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return Point3F::Zero; } EulerF rot = OCULUSVRDEV->getSensorEulerRotation(index); return Point3F(mRadToDeg(rot.x), mRadToDeg(rot.y), mRadToDeg(rot.z)); } DefineEngineFunction(getOVRSensorPredictionTime, F32, (S32 index),, "@brief Get the prediction time set for the given sensor index.\n\n" "@param index The sensor index.\n" "@return The prediction time of the Oculus VR sensor, given in seconds.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return 0; } return OCULUSVRDEV->getSensorPredictionTime(index); } DefineEngineFunction(setSensorPredictionTime, void, (S32 index, F32 dt),, "@brief Set the prediction time set for the given sensor index.\n\n" "@param index The sensor index.\n" "@param dt The prediction time to set given in seconds. Setting to 0 disables prediction.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return; } OCULUSVRDEV->setSensorPredictionTime(index, dt); } DefineEngineFunction(setAllSensorPredictionTime, void, (F32 dt),, "@brief Set the prediction time set for all sensors.\n\n" "@param dt The prediction time to set given in seconds. Setting to 0 disables prediction.\n" "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return; } OCULUSVRDEV->setAllSensorPredictionTime(dt); } DefineEngineFunction(ovrResetAllSensors, void, (),, "@brief Resets all Oculus VR sensors.\n\n" "This resets all sensor orientations such that their 'normal' rotation " "is defined when this function is called. This defines an HMD's forwards " "and up direction, for example." "@ingroup Game") { if(!ManagedSingleton::instanceOrNull()) { return; } OCULUSVRDEV->resetAllSensors(); }