You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1252 lines
50 KiB
1252 lines
50 KiB
/****************************************************************************
|
|
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos.com
|
|
|
|
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 "XRInterface.h"
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <functional>
|
|
#include <unordered_map>
|
|
#include "android/AndroidPlatform.h"
|
|
#include "base/Log.h"
|
|
#include "base/Macros.h"
|
|
#include "base/StringUtil.h"
|
|
#include "bindings/event/EventDispatcher.h"
|
|
#include "cocos-version.h"
|
|
#include "cocos/bindings/jswrapper/SeApi.h"
|
|
#include "core/scene-graph/Node.h"
|
|
#include "java/jni/JniHelper.h"
|
|
#include "platform/interfaces/modules/ISystemWindow.h"
|
|
#include "platform/interfaces/modules/ISystemWindowManager.h"
|
|
#include "renderer/GFXDeviceManager.h"
|
|
#include "scene/Camera.h"
|
|
#include "scene/RenderWindow.h"
|
|
#ifdef CC_USE_VULKAN
|
|
#include "gfx-vulkan/VKDevice.h"
|
|
#endif
|
|
#ifdef CC_USE_GLES3
|
|
#include "gfx-gles-common/gles3w.h"
|
|
#include "gfx-gles3/GLES3Device.h"
|
|
#include "renderer/gfx-gles3/GLES3GPUObjects.h"
|
|
#endif
|
|
|
|
#if CC_USE_XR
|
|
#include "Xr.h"
|
|
#endif
|
|
#include "application/ApplicationManager.h"
|
|
#include "base/threading/MessageQueue.h"
|
|
#include "platform/Image.h"
|
|
|
|
// print log
|
|
const bool IS_ENABLE_XR_LOG = false;
|
|
|
|
namespace cc {
|
|
const static ccstd::unordered_map<xr::XRClick::Type, StickKeyCode> CLICK_TYPE_TO_KEY_CODE = {
|
|
{xr::XRClick::Type::MENU, StickKeyCode::MENU},
|
|
{xr::XRClick::Type::TRIGGER_LEFT, StickKeyCode::TRIGGER_LEFT},
|
|
{xr::XRClick::Type::SHOULDER_LEFT, StickKeyCode::L1},
|
|
{xr::XRClick::Type::THUMBSTICK_LEFT, StickKeyCode::L3},
|
|
{xr::XRClick::Type::X, StickKeyCode::Y},
|
|
{xr::XRClick::Type::Y, StickKeyCode::X},
|
|
{xr::XRClick::Type::TRIGGER_RIGHT, StickKeyCode::TRIGGER_RIGHT},
|
|
{xr::XRClick::Type::SHOULDER_RIGHT, StickKeyCode::R1},
|
|
{xr::XRClick::Type::THUMBSTICK_RIGHT, StickKeyCode::R3},
|
|
{xr::XRClick::Type::A, StickKeyCode::B},
|
|
{xr::XRClick::Type::B, StickKeyCode::A},
|
|
{xr::XRClick::Type::HOME, StickKeyCode::UNDEFINE},
|
|
{xr::XRClick::Type::START, StickKeyCode::START},
|
|
{xr::XRClick::Type::DPAD_DOWN, StickKeyCode::Y},
|
|
{xr::XRClick::Type::DPAD_UP, StickKeyCode::Y},
|
|
{xr::XRClick::Type::DPAD_LEFT, StickKeyCode::X},
|
|
{xr::XRClick::Type::DPAD_RIGHT, StickKeyCode::X}};
|
|
|
|
const static ccstd::unordered_map<xr::XRGrab::Type, StickAxisCode> GRAB_TYPE_TO_AXIS_CODE = {
|
|
{xr::XRGrab::Type::TRIGGER_LEFT, StickAxisCode::L2},
|
|
{xr::XRGrab::Type::TRIGGER_RIGHT, StickAxisCode::R2},
|
|
{xr::XRGrab::Type::GRIP_LEFT, StickAxisCode::LEFT_GRIP},
|
|
{xr::XRGrab::Type::GRIP_RIGHT, StickAxisCode::RIGHT_GRIP},
|
|
};
|
|
|
|
const static ccstd::unordered_map<xr::XRTouch::Type, StickTouchCode> TOUCH_TYPE_TO_AXIS_CODE = {
|
|
{xr::XRTouch::Type::TOUCH_A, StickTouchCode::A},
|
|
{xr::XRTouch::Type::TOUCH_B, StickTouchCode::B},
|
|
{xr::XRTouch::Type::TOUCH_X, StickTouchCode::X},
|
|
{xr::XRTouch::Type::TOUCH_Y, StickTouchCode::Y},
|
|
{xr::XRTouch::Type::TOUCH_TRIGGER_LEFT, StickTouchCode::LEFT_TRIGGER},
|
|
{xr::XRTouch::Type::TOUCH_TRIGGER_RIGHT, StickTouchCode::RIGHT_TRIGGER},
|
|
{xr::XRTouch::Type::TOUCH_THUMBSTICK_LEFT, StickTouchCode::LEFT_THUMBSTICK},
|
|
{xr::XRTouch::Type::TOUCH_THUMBSTICK_RIGHT, StickTouchCode::RIGHT_THUMBSTICK},
|
|
};
|
|
|
|
void XRInterface::dispatchGamepadEventInternal(const xr::XRControllerEvent &xrControllerEvent) {
|
|
if (xrControllerEvent.xrControllerInfos.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto *controllerInfo = ccnew ControllerInfo();
|
|
if (!controllerInfo) {
|
|
return;
|
|
}
|
|
|
|
size_t length = xrControllerEvent.xrControllerInfos.size();
|
|
for (size_t i = 0; i < length; i++) {
|
|
switch (xrControllerEvent.xrControllerInfos.at(i)->getXREventType()) {
|
|
case xr::XREventType::CLICK: {
|
|
auto *xrClick = static_cast<xr::XRClick *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
if(CLICK_TYPE_TO_KEY_CODE.count(xrClick->type) > 0) {
|
|
StickKeyCode stickKeyCode = CLICK_TYPE_TO_KEY_CODE.at(xrClick->type);
|
|
|
|
switch (xrClick->type) {
|
|
case xr::XRClick::Type::MENU:
|
|
case xr::XRClick::Type::TRIGGER_LEFT:
|
|
case xr::XRClick::Type::SHOULDER_LEFT:
|
|
case xr::XRClick::Type::THUMBSTICK_LEFT:
|
|
case xr::XRClick::Type::X:
|
|
case xr::XRClick::Type::Y:
|
|
case xr::XRClick::Type::TRIGGER_RIGHT:
|
|
case xr::XRClick::Type::SHOULDER_RIGHT:
|
|
case xr::XRClick::Type::THUMBSTICK_RIGHT:
|
|
case xr::XRClick::Type::A:
|
|
case xr::XRClick::Type::B:
|
|
case xr::XRClick::Type::START: {
|
|
controllerInfo->buttonInfos.emplace_back(ControllerInfo::ButtonInfo(stickKeyCode, xrClick->isPress));
|
|
break;
|
|
}
|
|
case xr::XRClick::Type::HOME: {
|
|
CC_LOG_INFO("[XRInterface] dispatchGamepadEventInternal exit when home click in rokid.");
|
|
#if CC_USE_XR
|
|
xr::XrEntry::getInstance()->destroyXrInstance();
|
|
xr::XrEntry::destroyInstance();
|
|
_isXrEntryInstanceValid = false;
|
|
#endif
|
|
CC_CURRENT_APPLICATION_SAFE()->close();
|
|
break;
|
|
}
|
|
case xr::XRClick::Type::DPAD_UP:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::Y, xrClick->isPress ? 1.F : 0.F));
|
|
break;
|
|
case xr::XRClick::Type::DPAD_DOWN:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::Y, xrClick->isPress ? -1.F : 0.F));
|
|
break;
|
|
case xr::XRClick::Type::DPAD_LEFT:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::X, xrClick->isPress ? -1.F : 0.F));
|
|
break;
|
|
case xr::XRClick::Type::DPAD_RIGHT:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::X, xrClick->isPress ? 1.F : 0.F));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} break;
|
|
case xr::XREventType::STICK: {
|
|
auto *xrStick = static_cast<xr::XRStick *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
switch (xrStick->type) {
|
|
case xr::XRStick::Type::STICK_LEFT:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_X, xrStick->x));
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_Y, xrStick->y));
|
|
break;
|
|
case xr::XRStick::Type::STICK_RIGHT:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_X, xrStick->x));
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_Y, xrStick->y));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} break;
|
|
case xr::XREventType::GRAB: {
|
|
auto *xrGrab = static_cast<xr::XRGrab *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
if(GRAB_TYPE_TO_AXIS_CODE.count(xrGrab->type) > 0) {
|
|
StickAxisCode stickAxisCode = GRAB_TYPE_TO_AXIS_CODE.at(xrGrab->type);
|
|
switch (xrGrab->type) {
|
|
case xr::XRGrab::Type::TRIGGER_LEFT:
|
|
case xr::XRGrab::Type::TRIGGER_RIGHT: {
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(stickAxisCode, xrGrab->value));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
controllerInfo->napdId = 0; // xr only one gamepad connection
|
|
_controllerEvent.controllerInfos.emplace_back(controllerInfo);
|
|
_controllerEvent.type = ControllerEvent::Type::GAMEPAD;
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
if (!controllerInfo->buttonInfos.empty()) {
|
|
for (const auto &btnInfo : controllerInfo->buttonInfos) {
|
|
_xrRemotePreviewManager->sendControllerKeyInfo(btnInfo);
|
|
}
|
|
}
|
|
|
|
if (!controllerInfo->axisInfos.empty()) {
|
|
for (const auto &axisInfo : controllerInfo->axisInfos) {
|
|
_xrRemotePreviewManager->sendControllerKeyInfo(axisInfo);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
events::Controller::broadcast(_controllerEvent);
|
|
_controllerEvent.type = ControllerEvent::Type::UNKNOWN;
|
|
_controllerEvent.controllerInfos.clear();
|
|
}
|
|
|
|
void XRInterface::dispatchHandleEventInternal(const xr::XRControllerEvent &xrControllerEvent) {
|
|
if (xrControllerEvent.xrControllerInfos.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto *controllerInfo = ccnew ControllerInfo();
|
|
if (!controllerInfo) {
|
|
return;
|
|
}
|
|
|
|
size_t length = xrControllerEvent.xrControllerInfos.size();
|
|
se::AutoHandleScope scope;
|
|
uint32_t poseIndex = 0;
|
|
for (size_t i = 0; i < length; i++) {
|
|
switch (xrControllerEvent.xrControllerInfos.at(i)->getXREventType()) {
|
|
case xr::XREventType::CLICK: {
|
|
auto *xrClick = static_cast<xr::XRClick *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
if(CLICK_TYPE_TO_KEY_CODE.count(xrClick->type) > 0) {
|
|
StickKeyCode stickKeyCode = CLICK_TYPE_TO_KEY_CODE.at(xrClick->type);
|
|
switch (xrClick->type) {
|
|
case xr::XRClick::Type::MENU: {
|
|
#if !XR_OEM_SEED
|
|
controllerInfo->buttonInfos.emplace_back(ControllerInfo::ButtonInfo(StickKeyCode::MENU, xrClick->isPress));
|
|
#else
|
|
CC_LOG_INFO("[XRInterface] exit when menu click in seed.");
|
|
CC_CURRENT_APPLICATION_SAFE()->close();
|
|
#endif
|
|
break;
|
|
}
|
|
case xr::XRClick::Type::TRIGGER_LEFT:
|
|
case xr::XRClick::Type::THUMBSTICK_LEFT:
|
|
case xr::XRClick::Type::X:
|
|
case xr::XRClick::Type::Y:
|
|
case xr::XRClick::Type::TRIGGER_RIGHT:
|
|
case xr::XRClick::Type::THUMBSTICK_RIGHT:
|
|
case xr::XRClick::Type::A:
|
|
case xr::XRClick::Type::B:
|
|
case xr::XRClick::Type::START: {
|
|
controllerInfo->buttonInfos.emplace_back(ControllerInfo::ButtonInfo(stickKeyCode, xrClick->isPress));
|
|
break;
|
|
}
|
|
case xr::XRClick::Type::HOME: {
|
|
CC_LOG_INFO("[XRInterface] dispatchHandleEventInternal exit when home click in rokid.");
|
|
#if CC_USE_XR
|
|
xr::XrEntry::getInstance()->destroyXrInstance();
|
|
xr::XrEntry::destroyInstance();
|
|
_isXrEntryInstanceValid = false;
|
|
#endif
|
|
CC_CURRENT_APPLICATION_SAFE()->close();
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} break;
|
|
case xr::XREventType::STICK: {
|
|
auto *xrStick = static_cast<xr::XRStick *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
switch (xrStick->type) {
|
|
case xr::XRStick::Type::STICK_LEFT:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_X, xrStick->x));
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::LEFT_STICK_Y, xrStick->y));
|
|
break;
|
|
case xr::XRStick::Type::STICK_RIGHT:
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_X, xrStick->x));
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(StickAxisCode::RIGHT_STICK_Y, xrStick->y));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} break;
|
|
case xr::XREventType::GRAB: {
|
|
auto *xrGrab = static_cast<xr::XRGrab *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
if(GRAB_TYPE_TO_AXIS_CODE.count(xrGrab->type) > 0) {
|
|
StickAxisCode stickAxisCode = GRAB_TYPE_TO_AXIS_CODE.at(xrGrab->type);
|
|
controllerInfo->axisInfos.emplace_back(ControllerInfo::AxisInfo(stickAxisCode, xrGrab->value));
|
|
}
|
|
} break;
|
|
case xr::XREventType::TOUCH: {
|
|
auto *xrTouch = static_cast<xr::XRTouch *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
if(TOUCH_TYPE_TO_AXIS_CODE.count(xrTouch->type) > 0) {
|
|
StickTouchCode stickTouchCode = TOUCH_TYPE_TO_AXIS_CODE.at(xrTouch->type);
|
|
controllerInfo->touchInfos.emplace_back(ControllerInfo::TouchInfo(stickTouchCode, xrTouch->value));
|
|
}
|
|
break;
|
|
}
|
|
case xr::XREventType::POSE: {
|
|
auto *xrPose = static_cast<xr::XRPose *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
switch (xrPose->type) {
|
|
case xr::XRPose::Type::HAND_LEFT:
|
|
case xr::XRPose::Type::HAND_RIGHT:
|
|
case xr::XRPose::Type::AIM_LEFT:
|
|
case xr::XRPose::Type::AIM_RIGHT: {
|
|
auto *jsPose = se::Object::createPlainObject();
|
|
jsPose->setProperty("code", se::Value(static_cast<int>(xrPose->type)));
|
|
jsPose->setProperty("x", se::Value(xrPose->px));
|
|
jsPose->setProperty("y", se::Value(xrPose->py));
|
|
jsPose->setProperty("z", se::Value(xrPose->pz));
|
|
jsPose->setProperty("quaternionX", se::Value(xrPose->qx));
|
|
jsPose->setProperty("quaternionY", se::Value(xrPose->qy));
|
|
jsPose->setProperty("quaternionZ", se::Value(xrPose->qz));
|
|
jsPose->setProperty("quaternionW", se::Value(xrPose->qw));
|
|
if (!_jsPoseEventArray) {
|
|
_jsPoseEventArray = se::Object::createArrayObject(0);
|
|
_jsPoseEventArray->root();
|
|
}
|
|
_jsPoseEventArray->setArrayElement(poseIndex, se::Value(jsPose));
|
|
poseIndex++;
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (poseIndex > 0) {
|
|
_jsPoseEventArray->setProperty("length", se::Value(poseIndex));
|
|
se::ValueArray args;
|
|
args.emplace_back(se::Value(_jsPoseEventArray));
|
|
EventDispatcher::doDispatchJsEvent("onHandlePoseInput", args);
|
|
}
|
|
|
|
if (!controllerInfo->buttonInfos.empty() || !controllerInfo->axisInfos.empty() || !controllerInfo->touchInfos.empty()) {
|
|
controllerInfo->napdId = 0; // xr only one handle connection
|
|
_controllerEvent.controllerInfos.emplace_back(controllerInfo);
|
|
_controllerEvent.type = ControllerEvent::Type::HANDLE;
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
if (!controllerInfo->buttonInfos.empty()) {
|
|
for (const auto &btnInfo : controllerInfo->buttonInfos) {
|
|
_xrRemotePreviewManager->sendControllerKeyInfo(btnInfo);
|
|
}
|
|
}
|
|
|
|
if (!controllerInfo->axisInfos.empty()) {
|
|
for (const auto &axisInfo : controllerInfo->axisInfos) {
|
|
_xrRemotePreviewManager->sendControllerKeyInfo(axisInfo);
|
|
}
|
|
}
|
|
|
|
if (!controllerInfo->touchInfos.empty()) {
|
|
for (const auto &touchInfo : controllerInfo->touchInfos) {
|
|
_xrRemotePreviewManager->sendControllerKeyInfo(touchInfo);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
events::Controller::broadcast(_controllerEvent);
|
|
_controllerEvent.type = ControllerEvent::Type::UNKNOWN;
|
|
_controllerEvent.controllerInfos.clear();
|
|
} else {
|
|
CC_SAFE_DELETE(controllerInfo)
|
|
}
|
|
}
|
|
|
|
void XRInterface::dispatchHMDEventInternal(const xr::XRControllerEvent &xrControllerEvent) {
|
|
if (xrControllerEvent.xrControllerInfos.empty()) {
|
|
return;
|
|
}
|
|
|
|
size_t length = xrControllerEvent.xrControllerInfos.size();
|
|
se::AutoHandleScope scope;
|
|
uint32_t poseIndex = 0;
|
|
for (size_t i = 0; i < length; i++) {
|
|
if (xrControllerEvent.xrControllerInfos.at(i)->getXREventType() == xr::XREventType::POSE) {
|
|
auto *xrPose = static_cast<xr::XRPose *>(xrControllerEvent.xrControllerInfos.at(i).get());
|
|
switch (xrPose->type) {
|
|
case xr::XRPose::Type::VIEW_LEFT:
|
|
case xr::XRPose::Type::VIEW_RIGHT:
|
|
case xr::XRPose::Type::HEAD_MIDDLE: {
|
|
auto *jsPose = se::Object::createPlainObject();
|
|
jsPose->setProperty("code", se::Value(static_cast<int>(xrPose->type)));
|
|
jsPose->setProperty("x", se::Value(xrPose->px));
|
|
jsPose->setProperty("y", se::Value(xrPose->py));
|
|
jsPose->setProperty("z", se::Value(xrPose->pz));
|
|
jsPose->setProperty("quaternionX", se::Value(xrPose->qx));
|
|
jsPose->setProperty("quaternionY", se::Value(xrPose->qy));
|
|
jsPose->setProperty("quaternionZ", se::Value(xrPose->qz));
|
|
jsPose->setProperty("quaternionW", se::Value(xrPose->qw));
|
|
if (!_jsPoseEventArray) {
|
|
_jsPoseEventArray = se::Object::createArrayObject(0);
|
|
_jsPoseEventArray->root();
|
|
}
|
|
_jsPoseEventArray->setArrayElement(poseIndex, se::Value(jsPose));
|
|
poseIndex++;
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (poseIndex > 0) {
|
|
_jsPoseEventArray->setProperty("length", se::Value(poseIndex));
|
|
se::ValueArray args;
|
|
args.emplace_back(se::Value(_jsPoseEventArray));
|
|
EventDispatcher::doDispatchJsEvent("onHMDPoseInput", args);
|
|
}
|
|
}
|
|
|
|
xr::XRVendor XRInterface::getVendor() {
|
|
#if CC_USE_XR
|
|
return static_cast<xr::XRVendor>(xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::DEVICE_VENDOR).getInt());
|
|
#else
|
|
return xr::XRVendor::MONADO;
|
|
#endif
|
|
}
|
|
|
|
xr::XRConfigValue XRInterface::getXRConfig(xr::XRConfigKey key) {
|
|
#if CC_USE_XR
|
|
return xr::XrEntry::getInstance()->getXRConfig(key);
|
|
#else
|
|
CC_UNUSED_PARAM(key);
|
|
cc::xr::XRConfigValue configValue;
|
|
return configValue;
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::setXRConfig(xr::XRConfigKey key, xr::XRConfigValue value) {
|
|
#if CC_USE_XR
|
|
CC_LOG_INFO("[XR] setConfigParameterI %d", key);
|
|
xr::XrEntry::getInstance()->setXRConfig(key, value);
|
|
#else
|
|
CC_UNUSED_PARAM(key);
|
|
CC_UNUSED_PARAM(value);
|
|
#endif
|
|
}
|
|
|
|
uint32_t XRInterface::getRuntimeVersion() {
|
|
#if CC_USE_XR
|
|
return xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::RUNTIME_VERSION).getInt();
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::initialize(void *javaVM, void *activity) {
|
|
#if CC_USE_XR
|
|
CC_LOG_INFO("[XR] initialize vm.%p,aty.%p | %d", javaVM, activity, (int)gettid());
|
|
_isXrEntryInstanceValid = true;
|
|
xr::XrEntry::getInstance()->initPlatformData(javaVM, activity);
|
|
xr::XrEntry::getInstance()->setGamepadCallback(std::bind(&XRInterface::dispatchGamepadEventInternal, this, std::placeholders::_1));
|
|
xr::XrEntry::getInstance()->setHandleCallback(std::bind(&XRInterface::dispatchHandleEventInternal, this, std::placeholders::_1));
|
|
xr::XrEntry::getInstance()->setHMDCallback(std::bind(&XRInterface::dispatchHMDEventInternal, this, std::placeholders::_1));
|
|
xr::XrEntry::getInstance()->setXRConfig(xr::XRConfigKey::LOGIC_THREAD_ID, static_cast<int>(gettid()));
|
|
xr::XrEntry::getInstance()->setXRConfig(xr::XRConfigKey::ENGINE_VERSION, COCOS_VERSION);
|
|
xr::XrEntry::getInstance()->setXRConfigCallback([this](xr::XRConfigKey key, xr::XRConfigValue value) {
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("XRConfigCallback.%d", key);
|
|
if (key == xr::XRConfigKey::RENDER_EYE_FRAME_LEFT || key == xr::XRConfigKey::RENDER_EYE_FRAME_RIGHT) {
|
|
if (value.getInt() == 0) {
|
|
this->beginRenderEyeFrame(key == xr::XRConfigKey::RENDER_EYE_FRAME_LEFT ? 0 : 1);
|
|
}
|
|
|
|
if (value.getInt() == 1) {
|
|
this->endRenderEyeFrame(key == xr::XRConfigKey::RENDER_EYE_FRAME_LEFT ? 0 : 1);
|
|
}
|
|
} else if (key == xr::XRConfigKey::IMAGE_TRACKING_CANDIDATEIMAGE && value.isString()) {
|
|
if (!_gThreadPool) {
|
|
_gThreadPool = LegacyThreadPool::newSingleThreadPool();
|
|
}
|
|
|
|
std::string imageInfo = value.getString();
|
|
_gThreadPool->pushTask([imageInfo, this](int /*tid*/) {
|
|
this->loadImageTrackingData(imageInfo);
|
|
});
|
|
} else if (key == xr::XRConfigKey::ASYNC_LOAD_ASSETS_IMAGE && value.isString()) {
|
|
std::string imagePath = value.getString();
|
|
if (imagePath.length() == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!_gThreadPool) {
|
|
_gThreadPool = LegacyThreadPool::newSingleThreadPool();
|
|
}
|
|
_gThreadPool->pushTask([imagePath, this](int /*tid*/) {
|
|
this->asyncLoadAssetsImage(imagePath);
|
|
});
|
|
} else if (key == xr::XRConfigKey::ASYNC_LOAD_ASSETS_IMAGE && value.isInt()) {
|
|
_isFlipPixelY = value.getInt() == static_cast<int>(gfx::API::GLES3);
|
|
} else if (key == xr::XRConfigKey::TS_EVENT_CALLBACK) {
|
|
se::AutoHandleScope scope;
|
|
se::ValueArray args;
|
|
args.emplace_back(se::Value(value.getString()));
|
|
EventDispatcher::doDispatchJsEvent("onXREvent", args);
|
|
}
|
|
});
|
|
#if XR_OEM_PICO
|
|
std::string graphicsApiName = GraphicsApiOpenglES;
|
|
#if CC_USE_VULKAN
|
|
graphicsApiName = GraphicsApiVulkan_1_1;
|
|
#endif
|
|
xr::XrEntry::getInstance()->createXrInstance(graphicsApiName.c_str());
|
|
#endif
|
|
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
_xrRemotePreviewManager = new XRRemotePreviewManager();
|
|
#endif
|
|
#else
|
|
CC_UNUSED_PARAM(javaVM);
|
|
CC_UNUSED_PARAM(activity);
|
|
#endif
|
|
}
|
|
|
|
// render thread lifecycle
|
|
void XRInterface::onRenderPause() {
|
|
#if CC_USE_XR
|
|
if (!_renderPaused) {
|
|
_renderPaused = true;
|
|
_renderResumed = false;
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
_xrRemotePreviewManager->pause();
|
|
}
|
|
#endif
|
|
CC_LOG_INFO("[XR] onRenderPause");
|
|
xr::XrEntry::getInstance()->pauseXrInstance();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::onRenderResume() {
|
|
#if CC_USE_XR
|
|
if (!_renderResumed) {
|
|
_renderResumed = true;
|
|
_renderPaused = false;
|
|
CC_LOG_INFO("[XR] onRenderResume");
|
|
xr::XrEntry::getInstance()->resumeXrInstance();
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
_xrRemotePreviewManager->resume();
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::onRenderDestroy() {
|
|
#if CC_USE_XR
|
|
CC_LOG_INFO("[XR] onRenderDestroy");
|
|
xr::XrEntry::getInstance()->destroyXrInstance();
|
|
xr::XrEntry::destroyInstance();
|
|
_isXrEntryInstanceValid = false;
|
|
if (_jsPoseEventArray != nullptr) {
|
|
_jsPoseEventArray->unroot();
|
|
_jsPoseEventArray->decRef();
|
|
_jsPoseEventArray = nullptr;
|
|
}
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
_xrRemotePreviewManager->stop();
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
// render thread lifecycle
|
|
|
|
// gfx
|
|
void XRInterface::preGFXDeviceInitialize(gfx::API gfxApi) {
|
|
#if CC_USE_XR
|
|
CC_LOG_INFO("[XR] preGFXDeviceInitialize.api.%d | Multi Thread.%d", gfxApi, gfx::DeviceAgent::getInstance() ? 1 : 0);
|
|
setXRConfig(xr::XRConfigKey::MULTITHREAD_MODE, gfx::DeviceAgent::getInstance() != nullptr);
|
|
xr::XrEntry::getInstance()->setXRConfig(xr::XRConfigKey::RENDER_THREAD_ID, static_cast<int>(gettid()));
|
|
|
|
if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
|
|
#if !XR_OEM_PICO
|
|
std::string graphicsApiName = gfxApi == gfx::API::GLES3 ? GraphicsApiOpenglES : GraphicsApiVulkan_1_1;
|
|
xr::XrEntry::getInstance()->createXrInstance(graphicsApiName.c_str());
|
|
#endif
|
|
}
|
|
#else
|
|
CC_UNUSED_PARAM(gfxApi);
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::postGFXDeviceInitialize(gfx::API gfxApi) {
|
|
#if CC_USE_XR
|
|
CC_LOG_INFO("[XR] postGFXDeviceInitialize.api.%d", gfxApi);
|
|
if (gfxApi == gfx::API::GLES3) {
|
|
#if CC_USE_GLES3
|
|
xr::XrEntry::getInstance()->initXrSession(_gles3wLoadFuncProc,
|
|
_gles3GPUContext->eglDisplay,
|
|
_gles3GPUContext->eglConfig,
|
|
_gles3GPUContext->eglDefaultContext);
|
|
#endif
|
|
} else if (gfxApi == gfx::API::VULKAN) {
|
|
#if CC_USE_VULKAN
|
|
int vkQueueFamilyIndex = xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::VK_QUEUE_FAMILY_INDEX).getInt();
|
|
cc::xr::XrEntry::getInstance()->initXrSession(_vkInstance, _vkPhysicalDevice, _vkDevice, vkQueueFamilyIndex);
|
|
#endif
|
|
}
|
|
#else
|
|
CC_UNUSED_PARAM(gfxApi);
|
|
#endif
|
|
}
|
|
|
|
const xr::XRSwapchain &XRInterface::doGFXDeviceAcquire(gfx::API gfxApi) {
|
|
#if CC_USE_XR
|
|
// CC_LOG_INFO("[XR] doGFXDeviceAcquire.api.%d", gfxApi);
|
|
if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
|
|
return xr::XrEntry::getInstance()->acquireXrSwapchain(static_cast<uint32_t>(gfxApi));
|
|
}
|
|
#else
|
|
CC_UNUSED_PARAM(gfxApi);
|
|
#endif
|
|
return _acquireSwapchain;
|
|
}
|
|
|
|
bool XRInterface::isGFXDeviceNeedsPresent(gfx::API gfxApi) {
|
|
CC_UNUSED_PARAM(gfxApi);
|
|
#if CC_USE_XR
|
|
// CC_LOG_INFO("[XR] isGFXDeviceNeedsPresent.api.%d", gfxApi);
|
|
// if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
|
|
// }
|
|
return xr::XrEntry::getInstance()->getXRConfig(cc::xr::XRConfigKey::PRESENT_ENABLE).getBool();
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::postGFXDevicePresent(gfx::API gfxApi) {
|
|
#if CC_USE_XR
|
|
// CC_LOG_INFO("[XR] postGFXDevicePresent.api.%d", gfxApi);
|
|
if (gfxApi == gfx::API::GLES3 || gfxApi == gfx::API::VULKAN) {
|
|
}
|
|
#else
|
|
CC_UNUSED_PARAM(gfxApi);
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::createXRSwapchains() {
|
|
#if CC_USE_XR
|
|
CC_LOG_INFO("[XR] createXRSwapchains");
|
|
if (gfx::DeviceAgent::getInstance()) {
|
|
ENQUEUE_MESSAGE_0(gfx::DeviceAgent::getInstance()->getMessageQueue(),
|
|
CreateXRSwapchains,
|
|
{
|
|
CC_LOG_INFO("[XR] [RT] initXrSwapchains");
|
|
xr::XrEntry::getInstance()->setXRConfig(xr::XRConfigKey::RENDER_THREAD_ID, (int)gettid());
|
|
JniHelper::getEnv();
|
|
xr::XrEntry::getInstance()->initXrSwapchains();
|
|
})
|
|
} else {
|
|
xr::XrEntry::getInstance()->initXrSwapchains();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const std::vector<cc::xr::XRSwapchain> &XRInterface::getXRSwapchains() {
|
|
#if CC_USE_XR
|
|
CC_LOG_INFO("[XR] getXRSwapchains");
|
|
if (_xrSwapchains.empty()) {
|
|
_xrSwapchains = xr::XrEntry::getInstance()->getCocosXrSwapchains();
|
|
}
|
|
#endif
|
|
return _xrSwapchains;
|
|
}
|
|
|
|
gfx::Format XRInterface::getXRSwapchainFormat() {
|
|
#if CC_USE_XR
|
|
int swapchainFormat = xr::XrEntry::getInstance()->getXRConfig(xr::XRConfigKey::SWAPCHAIN_FORMAT).getInt();
|
|
#if CC_USE_GLES3
|
|
if (swapchainFormat == GL_SRGB_ALPHA_EXT) {
|
|
return gfx::Format::SRGB8_A8;
|
|
}
|
|
|
|
if (swapchainFormat == GL_RGBA8) {
|
|
return gfx::Format::RGBA8;
|
|
}
|
|
|
|
if (swapchainFormat == GL_BGRA8_EXT) {
|
|
return gfx::Format::BGRA8;
|
|
}
|
|
#endif
|
|
|
|
#if CC_USE_VULKAN
|
|
if (swapchainFormat == VK_FORMAT_R8G8B8A8_SRGB) {
|
|
return gfx::Format::SRGB8_A8;
|
|
}
|
|
|
|
if (swapchainFormat == VK_FORMAT_R8G8B8A8_UNORM) {
|
|
return gfx::Format::RGBA8;
|
|
}
|
|
|
|
if (swapchainFormat == VK_FORMAT_B8G8R8A8_UNORM) {
|
|
return gfx::Format::BGRA8;
|
|
}
|
|
#endif
|
|
#endif
|
|
return gfx::Format::BGRA8;
|
|
}
|
|
|
|
void XRInterface::updateXRSwapchainTypedID(uint32_t typedID) {
|
|
#if CC_USE_XR
|
|
#if XR_OEM_HUAWEIVR
|
|
_eglSurfaceTypeMap[typedID] = EGLSurfaceType::WINDOW;
|
|
#else
|
|
_eglSurfaceTypeMap[typedID] = EGLSurfaceType::PBUFFER;
|
|
#endif
|
|
#else
|
|
CC_UNUSED_PARAM(typedID);
|
|
#endif
|
|
}
|
|
// gfx
|
|
|
|
// vulkan
|
|
#ifdef CC_USE_VULKAN
|
|
uint32_t XRInterface::getXRVkApiVersion(uint32_t engineVkApiVersion) {
|
|
#if CC_USE_XR
|
|
return xr::XrEntry::getInstance()->getXrVkApiVersion(engineVkApiVersion);
|
|
#else
|
|
return engineVkApiVersion;
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::initializeVulkanData(const PFN_vkGetInstanceProcAddr &addr) {
|
|
_vkGetInstanceProcAddr = addr;
|
|
}
|
|
|
|
VkInstance XRInterface::createXRVulkanInstance(const VkInstanceCreateInfo &instInfo) {
|
|
#if CC_USE_XR
|
|
_vkInstance = xr::XrEntry::getInstance()->xrVkCreateInstance(instInfo, _vkGetInstanceProcAddr);
|
|
_vkPhysicalDevice = xr::XrEntry::getInstance()->getXrVkGraphicsDevice(_vkInstance);
|
|
return _vkInstance;
|
|
#else
|
|
CC_UNUSED_PARAM(instInfo);
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
VkDevice XRInterface::createXRVulkanDevice(const VkDeviceCreateInfo *deviceInfo) {
|
|
#if CC_USE_XR
|
|
VK_CHECK(xr::XrEntry::getInstance()->xrVkCreateDevice(deviceInfo, _vkGetInstanceProcAddr, _vkPhysicalDevice, &_vkDevice));
|
|
#else
|
|
CC_UNUSED_PARAM(deviceInfo);
|
|
#endif
|
|
return _vkDevice;
|
|
}
|
|
|
|
VkPhysicalDevice XRInterface::getXRVulkanGraphicsDevice() {
|
|
return _vkPhysicalDevice;
|
|
}
|
|
|
|
void XRInterface::getXRSwapchainVkImages(std::vector<VkImage> &vkImages, uint32_t eye) {
|
|
#if CC_USE_XR
|
|
xr::XrEntry::getInstance()->getSwapchainImages(vkImages, eye);
|
|
#else
|
|
CC_UNUSED_PARAM(vkImages);
|
|
CC_UNUSED_PARAM(eye);
|
|
#endif
|
|
}
|
|
#endif
|
|
// vulkan
|
|
|
|
// gles
|
|
#ifdef CC_USE_GLES3
|
|
void XRInterface::initializeGLESData(PFNGLES3WLOADPROC gles3wLoadFuncProc, gfx::GLES3GPUContext *gpuContext) {
|
|
#if CC_USE_XR
|
|
_gles3wLoadFuncProc = gles3wLoadFuncProc;
|
|
_gles3GPUContext = gpuContext;
|
|
void *eglDisplay = gpuContext->eglDisplay;
|
|
void *eglConfig = gpuContext->eglConfig;
|
|
void *eglDefaultContext = gpuContext->eglDefaultContext;
|
|
CC_LOG_INFO("[XR] initializeGLESData.egl.%p/%p/%p", eglDisplay, eglConfig, eglDefaultContext);
|
|
#else
|
|
CC_UNUSED_PARAM(gles3wLoadFuncProc);
|
|
CC_UNUSED_PARAM(gpuContext);
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::attachGLESFramebufferTexture2D() {
|
|
#if CC_USE_XR
|
|
xr::XrEntry::getInstance()->attachXrFramebufferTexture2D();
|
|
#endif
|
|
}
|
|
|
|
EGLSurfaceType XRInterface::acquireEGLSurfaceType(uint32_t typedID) {
|
|
#if CC_USE_XR
|
|
if (_eglSurfaceTypeMap.count(typedID) > 0) {
|
|
return _eglSurfaceTypeMap[typedID];
|
|
}
|
|
#else
|
|
CC_UNUSED_PARAM(typedID);
|
|
#endif
|
|
return EGLSurfaceType::WINDOW;
|
|
}
|
|
#endif
|
|
// gles
|
|
|
|
// stereo render loop
|
|
bool XRInterface::platformLoopStart() {
|
|
#if CC_USE_XR
|
|
// CC_LOG_INFO("[XR] platformLoopStart");
|
|
if (!_isXrEntryInstanceValid) {
|
|
return false;
|
|
}
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager && !_xrRemotePreviewManager->isStarted()) {
|
|
_xrRemotePreviewManager->start();
|
|
}
|
|
#endif
|
|
return xr::XrEntry::getInstance()->platformLoopStart();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool XRInterface::beginRenderFrame() {
|
|
#if CC_USE_XR
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] beginRenderFrame.%d", _committedFrame);
|
|
_isEnabledEyeRenderJsCallback = xr::XrEntry::getInstance()->getXRConfig(xr::XRConfigKey::EYE_RENDER_JS_CALLBACK).getBool();
|
|
if (gfx::DeviceAgent::getInstance()) {
|
|
static uint64_t frameId = 0;
|
|
frameId++;
|
|
if (_committedFrame) {
|
|
gfx::DeviceAgent::getInstance()->presentWait();
|
|
_committedFrame = false;
|
|
}
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] beginRenderFrame waitFrame.%lld", frameId);
|
|
xr::XrEntry::getInstance()->waitFrame();
|
|
ENQUEUE_MESSAGE_1(gfx::DeviceAgent::getInstance()->getMessageQueue(),
|
|
BeginRenderFrame, frameId, frameId,
|
|
{
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] beginRenderFrame.%lld", frameId);
|
|
xr::XrEntry::getInstance()->frameStart();
|
|
})
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
_xrRemotePreviewManager->tick();
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
_xrRemotePreviewManager->tick();
|
|
}
|
|
#endif
|
|
return xr::XrEntry::getInstance()->frameStart();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool XRInterface::isRenderAllowable() {
|
|
#if CC_USE_XR
|
|
return xr::XrEntry::getInstance()->isRenderAllowable();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool XRInterface::beginRenderEyeFrame(uint32_t eye) {
|
|
#if CC_USE_XR
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] beginRenderEyeFrame %d", eye);
|
|
if (_isEnabledEyeRenderJsCallback) {
|
|
se::AutoHandleScope scope;
|
|
se::ValueArray args;
|
|
args.emplace_back(se::Value(eye));
|
|
EventDispatcher::doDispatchJsEvent("onXREyeRenderBegin", args);
|
|
}
|
|
if (gfx::DeviceAgent::getInstance()) {
|
|
ENQUEUE_MESSAGE_1(gfx::DeviceAgent::getInstance()->getMessageQueue(),
|
|
BeginRenderEyeFrame, eye, eye,
|
|
{
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] beginRenderEyeFrame %d", eye);
|
|
xr::XrEntry::getInstance()->renderLoopStart(eye);
|
|
})
|
|
} else {
|
|
xr::XrEntry::getInstance()->renderLoopStart(static_cast<int>(eye));
|
|
}
|
|
#else
|
|
CC_UNUSED_PARAM(eye);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool XRInterface::endRenderEyeFrame(uint32_t eye) {
|
|
#if CC_USE_XR
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] endRenderEyeFrame %d", eye);
|
|
if (_isEnabledEyeRenderJsCallback) {
|
|
se::AutoHandleScope scope;
|
|
se::ValueArray args;
|
|
args.emplace_back(se::Value(eye));
|
|
EventDispatcher::doDispatchJsEvent("onXREyeRenderEnd", args);
|
|
}
|
|
if (gfx::DeviceAgent::getInstance()) {
|
|
ENQUEUE_MESSAGE_1(gfx::DeviceAgent::getInstance()->getMessageQueue(),
|
|
EndRenderEyeFrame, eye, eye,
|
|
{
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] endRenderEyeFrame %d", eye);
|
|
xr::XrEntry::getInstance()->renderLoopEnd(eye);
|
|
})
|
|
} else {
|
|
xr::XrEntry::getInstance()->renderLoopEnd(static_cast<int>(eye));
|
|
}
|
|
#else
|
|
CC_UNUSED_PARAM(eye);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool XRInterface::endRenderFrame() {
|
|
#if CC_USE_XR
|
|
if (IS_ENABLE_XR_LOG) {
|
|
CC_LOG_INFO("[XR] endRenderFrame.%d",
|
|
cc::ApplicationManager::getInstance()->getCurrentAppSafe()->getEngine()->getTotalFrames());
|
|
}
|
|
|
|
if (gfx::DeviceAgent::getInstance()) {
|
|
ENQUEUE_MESSAGE_0(gfx::DeviceAgent::getInstance()->getMessageQueue(),
|
|
EndRenderFrame,
|
|
{
|
|
xr::XrEntry::getInstance()->frameEnd();
|
|
gfx::DeviceAgent::getInstance()->presentSignal();
|
|
if (IS_ENABLE_XR_LOG) CC_LOG_INFO("[XR] [RT] presentSignal endRenderFrame.%d",
|
|
cc::ApplicationManager::getInstance()->getCurrentAppSafe()->getEngine()->getTotalFrames());
|
|
})
|
|
_committedFrame = true;
|
|
// CC_LOG_INFO("[XR] endRenderFrame pass presentWait errno %d", errno);
|
|
} else {
|
|
xr::XrEntry::getInstance()->frameEnd();
|
|
#if CC_USE_XR_REMOTE_PREVIEW
|
|
if (_xrRemotePreviewManager) {
|
|
_xrRemotePreviewManager->tick();
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool XRInterface::platformLoopEnd() {
|
|
#if CC_USE_XR
|
|
if (!_isXrEntryInstanceValid) {
|
|
return false;
|
|
}
|
|
return xr::XrEntry::getInstance()->platformLoopEnd();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
// stereo render loop
|
|
|
|
ccstd::vector<float> XRInterface::getHMDViewPosition(uint32_t eye, int trackingType) {
|
|
#if CC_USE_XR
|
|
return xr::XrEntry::getInstance()->getHMDViewPosition(eye, trackingType);
|
|
#else
|
|
CC_UNUSED_PARAM(eye);
|
|
CC_UNUSED_PARAM(trackingType);
|
|
ccstd::vector<float> res;
|
|
res.reserve(3);
|
|
return res;
|
|
#endif
|
|
}
|
|
|
|
ccstd::vector<float> XRInterface::getXREyeFov(uint32_t eye) {
|
|
#if CC_USE_XR
|
|
return xr::XrEntry::getInstance()->getEyeFov(eye);
|
|
#else
|
|
CC_UNUSED_PARAM(eye);
|
|
ccstd::vector<float> res;
|
|
res.reserve(4);
|
|
return res;
|
|
#endif
|
|
}
|
|
|
|
ccstd::vector<float> XRInterface::getXRViewProjectionData(uint32_t eye, float near, float far) {
|
|
#if CC_USE_XR
|
|
return xr::XrEntry::getInstance()->computeViewProjection(eye, near, far, 1.0F);
|
|
#else
|
|
CC_UNUSED_PARAM(eye);
|
|
CC_UNUSED_PARAM(near);
|
|
CC_UNUSED_PARAM(far);
|
|
ccstd::vector<float> res;
|
|
res.reserve(16);
|
|
return res;
|
|
#endif
|
|
}
|
|
|
|
// renderwindow
|
|
xr::XREye XRInterface::getXREyeByRenderWindow(void *window) {
|
|
if (!window) {
|
|
return xr::XREye::NONE;
|
|
}
|
|
if (_xrWindowMap.count(window) > 0) {
|
|
return _xrWindowMap[window];
|
|
}
|
|
return xr::XREye::NONE;
|
|
}
|
|
|
|
void XRInterface::bindXREyeWithRenderWindow(void *window, xr::XREye eye) {
|
|
if (_xrWindowMap.count(window) > 0) {
|
|
_xrWindowMap[window] = eye;
|
|
} else {
|
|
_xrWindowMap.emplace(std::make_pair(window, eye));
|
|
}
|
|
}
|
|
|
|
void XRInterface::loadImageTrackingData(const std::string &imageInfo) {
|
|
// name|@assets/TrackingImage_SpacesTown.png|0.18|0.26
|
|
ccstd::vector<ccstd::string> segments = StringUtil::split(imageInfo, "|");
|
|
std::string imageName = segments.at(0);
|
|
std::string imagePath = segments.at(1);
|
|
auto physicalSizeX = static_cast<float>(atof(segments.at(2).c_str()));
|
|
auto physicalSizeY = static_cast<float>(atof(segments.at(3).c_str()));
|
|
auto *spaceTownImage = new Image();
|
|
spaceTownImage->addRef();
|
|
bool res = spaceTownImage->initWithImageFile(imagePath);
|
|
if (!res) {
|
|
CC_LOG_ERROR("[XRInterface] loadImageTrackingData init failed, %s!!!", imageInfo.c_str());
|
|
return;
|
|
}
|
|
uint32_t imageWidth = spaceTownImage->getWidth();
|
|
uint32_t imageHeight = spaceTownImage->getHeight();
|
|
const uint32_t bufferSize = imageWidth * imageHeight * 3;
|
|
auto *buffer = new uint8_t[bufferSize];
|
|
for (unsigned int j = 0; j < imageHeight; ++j) {
|
|
for (unsigned int i = 0; i < imageWidth; ++i) {
|
|
const unsigned int pixel = i + j * imageWidth;
|
|
const unsigned int pixelFlip = i + (imageHeight - j - 1) * imageWidth;
|
|
|
|
const uint8_t *originalPixel = &spaceTownImage->getData()[static_cast<size_t>(pixel * 4)];
|
|
uint8_t *convertedPixel = &buffer[static_cast<size_t>(pixelFlip * 3)];
|
|
convertedPixel[0] = originalPixel[0];
|
|
convertedPixel[1] = originalPixel[1];
|
|
convertedPixel[2] = originalPixel[2];
|
|
}
|
|
}
|
|
spaceTownImage->release();
|
|
|
|
auto app = CC_CURRENT_APPLICATION();
|
|
if (!app) {
|
|
CC_LOG_ERROR("[XRInterface] loadImageTrackingData callback failed, application not exist!!!");
|
|
return;
|
|
}
|
|
auto engine = app->getEngine();
|
|
CC_ASSERT_NOT_NULL(engine);
|
|
engine->getScheduler()->performFunctionInCocosThread([=]() {
|
|
xr::XRTrackingImageData candidateImage;
|
|
candidateImage.friendlyName = imageName;
|
|
candidateImage.bufferSize = bufferSize;
|
|
candidateImage.buffer = buffer;
|
|
candidateImage.pixelSizeWidth = imageWidth;
|
|
candidateImage.pixelSizeHeight = imageHeight;
|
|
candidateImage.physicalWidth = physicalSizeX;
|
|
candidateImage.physicalHeight = physicalSizeY;
|
|
setXRConfig(xr::XRConfigKey::IMAGE_TRACKING_CANDIDATEIMAGE, static_cast<void *>(&candidateImage));
|
|
});
|
|
}
|
|
|
|
void XRInterface::handleAppCommand(int appCmd) {
|
|
#if CC_USE_XR
|
|
setXRConfig(xr::XRConfigKey::APP_COMMAND, appCmd);
|
|
#else
|
|
CC_UNUSED_PARAM(appCmd);
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::adaptOrthographicMatrix(cc::scene::Camera *camera, const ccstd::array<float, 4> &preTransform, Mat4 &proj, Mat4 &view) {
|
|
#if CC_USE_XR
|
|
const float orthoHeight = camera->getOrthoHeight();
|
|
const float near = camera->getNearClip();
|
|
const float far = camera->getFarClip();
|
|
const float aspect = camera->getAspect();
|
|
auto *swapchain = camera->getWindow()->getSwapchain();
|
|
const auto &orientation = swapchain ? swapchain->getSurfaceTransform() : gfx::SurfaceTransform::IDENTITY;
|
|
cc::xr::XREye wndXREye = getXREyeByRenderWindow(camera->getWindow());
|
|
const auto &xrFov = getXREyeFov(static_cast<uint32_t>(wndXREye));
|
|
const float ipdValue = getXRConfig(xr::XRConfigKey::DEVICE_IPD).getFloat();
|
|
const float projectionSignY = gfx::Device::getInstance()->getCapabilities().clipSpaceSignY;
|
|
Mat4 orthoProj;
|
|
auto *systemWindow = CC_GET_MAIN_SYSTEM_WINDOW();
|
|
const float deviceAspect = systemWindow ? (systemWindow->getViewSize().width / systemWindow->getViewSize().height) : 1.777F;
|
|
// device's fov may be asymmetry
|
|
float radioLeft = 1.0F;
|
|
float radioRight = 1.0F;
|
|
if (wndXREye == xr::XREye::LEFT) {
|
|
radioLeft = fabs(tanf(xrFov[0])) / fabs(tanf(xrFov[1]));
|
|
} else if (wndXREye == xr::XREye::RIGHT) {
|
|
radioRight = fabs(tanf(xrFov[1])) / fabs(tanf(xrFov[0]));
|
|
}
|
|
|
|
const float x = orthoHeight * deviceAspect;
|
|
const float y = orthoHeight;
|
|
Mat4::createOrthographicOffCenter(-x * radioLeft, x * radioRight, -y, y, near, far,
|
|
gfx::Device::getInstance()->getCapabilities().clipSpaceMinZ, projectionSignY,
|
|
static_cast<int>(orientation), &orthoProj);
|
|
// keep scale to [-1, 1] only use offset
|
|
orthoProj.m[0] = preTransform[0] / x;
|
|
orthoProj.m[5] = preTransform[3] * projectionSignY / y;
|
|
|
|
Mat4 eyeCameraProj;
|
|
const auto &projFloat = getXRViewProjectionData(static_cast<uint32_t>(wndXREye), 0.001F, 1000.0F);
|
|
std::memcpy(eyeCameraProj.m, projFloat.data(), sizeof(float) * 16);
|
|
Vec4 point{ipdValue * 0.5F, 0.0F, -1.0F, 1.0F};
|
|
eyeCameraProj.transformVector(&point);
|
|
|
|
Mat4 objectTranslateMat;
|
|
// point in [-1,1] convert to [-aspect,aspect]
|
|
if (wndXREye == xr::XREye::LEFT) {
|
|
objectTranslateMat.translate(point.x * orthoHeight * 0.5F * aspect, 0.0F, 0.0F);
|
|
} else if (wndXREye == xr::XREye::RIGHT) {
|
|
objectTranslateMat.translate(-point.x * orthoHeight * 0.5F * aspect, 0.0F, 0.0F);
|
|
}
|
|
// update view matrix
|
|
view = camera->getNode()->getWorldMatrix().getInversed();
|
|
Mat4 scaleMat{};
|
|
scaleMat.scale(camera->getNode()->getWorldScale());
|
|
// remove scale
|
|
Mat4::multiply(scaleMat, view, &view);
|
|
Mat4::multiply(objectTranslateMat, view, &view);
|
|
|
|
// fit width, scale deviceAspect to aspect
|
|
const float ndcXScale = getXRConfig(xr::XRConfigKey::SPLIT_AR_GLASSES).getBool() ? (1.0F + point.x * 0.5F) : 1.0F;
|
|
const float ndcYScale = aspect / deviceAspect;
|
|
Mat4 ndcScaleMat;
|
|
ndcScaleMat.scale(ndcXScale, ndcYScale, 1.0F);
|
|
|
|
proj = ndcScaleMat * orthoProj;
|
|
#else
|
|
CC_UNUSED_PARAM(camera);
|
|
CC_UNUSED_PARAM(preTransform);
|
|
CC_UNUSED_PARAM(proj);
|
|
CC_UNUSED_PARAM(view);
|
|
#endif
|
|
}
|
|
|
|
void XRInterface::asyncLoadAssetsImage(const std::string &imagePath) {
|
|
auto *assetsImage = new Image();
|
|
assetsImage->addRef();
|
|
bool res = assetsImage->initWithImageFile(imagePath);
|
|
if (!res) {
|
|
CC_LOG_ERROR("[XRInterface] async load assets image init failed, %s!!!", imagePath.c_str());
|
|
assetsImage->release();
|
|
return;
|
|
}
|
|
uint32_t imageWidth = assetsImage->getWidth();
|
|
uint32_t imageHeight = assetsImage->getHeight();
|
|
uint32_t bufferSize = assetsImage->getDataLen();
|
|
uint8_t *buffer = nullptr;
|
|
if (assetsImage->getRenderFormat() == gfx::Format::RGB8) {
|
|
// convert to rgba8
|
|
bufferSize = imageWidth * imageHeight * 4;
|
|
buffer = new uint8_t[bufferSize];
|
|
for (uint32_t y = 0; y < imageHeight; y++) {
|
|
for (uint32_t x = 0; x < imageWidth; x++) {
|
|
const unsigned int pixel = x + y * imageWidth;
|
|
const unsigned int pixelFlip = x + (imageHeight - y - 1) * imageWidth;
|
|
const uint8_t *originalPixel = &assetsImage->getData()[static_cast<size_t>(pixel * 3)];
|
|
uint8_t *convertedPixel = nullptr;
|
|
if (_isFlipPixelY) {
|
|
convertedPixel = &buffer[static_cast<size_t>(pixelFlip * 4)];
|
|
} else {
|
|
convertedPixel = &buffer[static_cast<size_t>(pixel * 4)];
|
|
}
|
|
convertedPixel[0] = originalPixel[0];
|
|
convertedPixel[1] = originalPixel[1];
|
|
convertedPixel[2] = originalPixel[2];
|
|
convertedPixel[3] = 255.0F;
|
|
}
|
|
}
|
|
} else {
|
|
buffer = new uint8_t[bufferSize];
|
|
if (_isFlipPixelY) {
|
|
for (uint32_t y = 0; y < imageHeight; y++) {
|
|
for (uint32_t x = 0; x < imageWidth; x++) {
|
|
const unsigned int pixel = x + y * imageWidth;
|
|
const unsigned int pixelFlip = x + (imageHeight - y - 1) * imageWidth;
|
|
const uint8_t *originalPixel = &assetsImage->getData()[static_cast<size_t>(pixel * 4)];
|
|
uint8_t *convertedPixel = &buffer[static_cast<size_t>(pixelFlip * 4)];
|
|
convertedPixel[0] = originalPixel[0];
|
|
convertedPixel[1] = originalPixel[1];
|
|
convertedPixel[2] = originalPixel[2];
|
|
convertedPixel[3] = originalPixel[3];
|
|
}
|
|
}
|
|
} else {
|
|
memcpy(buffer, assetsImage->getData(), bufferSize);
|
|
}
|
|
}
|
|
auto app = CC_CURRENT_APPLICATION();
|
|
if (!app) {
|
|
CC_LOG_ERROR("[XRInterface] loadAssetsImage callback failed, application not exist!!!");
|
|
return;
|
|
}
|
|
auto engine = app->getEngine();
|
|
CC_ASSERT_NOT_NULL(engine);
|
|
engine->getScheduler()->performFunctionInCocosThread([=]() {
|
|
auto *imageData = new xr::XRTrackingImageData();
|
|
imageData->friendlyName = imagePath;
|
|
imageData->bufferSize = bufferSize;
|
|
imageData->buffer = buffer;
|
|
imageData->pixelSizeWidth = imageWidth;
|
|
imageData->pixelSizeHeight = imageHeight;
|
|
if (!this->getXRConfig(xr::XRConfigKey::ASYNC_LOAD_ASSETS_IMAGE_RESULTS).getPointer()) {
|
|
auto *imagesMapPtr = new std::unordered_map<std::string, void *>();
|
|
(*imagesMapPtr).emplace(std::make_pair(imagePath, static_cast<void *>(imageData)));
|
|
this->setXRConfig(xr::XRConfigKey::ASYNC_LOAD_ASSETS_IMAGE_RESULTS, static_cast<void *>(imagesMapPtr));
|
|
} else {
|
|
auto *imagesMapPtr = static_cast<std::unordered_map<std::string,
|
|
void *> *>(getXRConfig(xr::XRConfigKey::ASYNC_LOAD_ASSETS_IMAGE_RESULTS).getPointer());
|
|
(*imagesMapPtr).emplace(std::make_pair(imagePath, static_cast<void *>(imageData)));
|
|
}
|
|
});
|
|
assetsImage->release();
|
|
}
|
|
|
|
#if CC_USE_XR
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_com_cocos_lib_xr_CocosXRApi_onAdbCmd(JNIEnv * /*env*/, jobject /*thiz*/, jstring key, jstring value) {
|
|
auto cmdKey = cc::JniHelper::jstring2string(key);
|
|
auto cmdValue = cc::JniHelper::jstring2string(value);
|
|
if (IS_ENABLE_XR_LOG) {
|
|
CC_LOG_INFO("CocosXRApi_onAdbCmd_%s_%s", cmdKey.c_str(), cmdValue.c_str());
|
|
}
|
|
cc::xr::XrEntry::getInstance()->setXRConfig(cc::xr::XRConfigKey::ADB_COMMAND, cmdKey.append(":").append(cmdValue));
|
|
}
|
|
|
|
extern "C" JNIEXPORT void JNICALL Java_com_cocos_lib_xr_CocosXRApi_onActivityLifecycleCallback(JNIEnv * /*env*/, jobject /*thiz*/, jint type, jstring activityClassName) {
|
|
auto name = cc::JniHelper::jstring2string(activityClassName);
|
|
if (IS_ENABLE_XR_LOG) {
|
|
CC_LOG_INFO("CocosXRApi_onActivityLifecycleCallback_%d_%s", type, name.c_str());
|
|
}
|
|
cc::xr::XrEntry::getInstance()->setXRConfig(cc::xr::XRConfigKey::ACTIVITY_LIFECYCLE, type);
|
|
cc::xr::XrEntry::getInstance()->setXRConfig(cc::xr::XRConfigKey::ACTIVITY_LIFECYCLE, name);
|
|
}
|
|
|
|
#endif
|
|
} // namespace cc
|
|
|