Skip to content

Commit

Permalink
Implement all axes and buttons for mixed reality input controllers. A…
Browse files Browse the repository at this point in the history
…dd controller test app to view real-time controller data. Fix base64 image decoding to execute synchronously (fixes datguivr). Implement dispatchEvent.
  • Loading branch information
Almost-Done committed May 15, 2019
1 parent fabebaf commit 2002eac
Show file tree
Hide file tree
Showing 23 changed files with 6,855 additions and 88 deletions.
36 changes: 27 additions & 9 deletions core/dom-events.cpp
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
#include "dom-events.h"
#include "host-interfaces.h"
#include "include/holojs/private/chakra.h"
#include "resource-management/resource-manager.h"
#include "include/holojs/private/script-host-utilities.h"
#include "resource-management/resource-manager.h"

using namespace HoloJs::Interfaces;
using namespace HoloJs::ResourceManagement;
using namespace std;

long DOMEventRegistration::initialize()
{
RETURN_IF_FAILED(ScriptHostUtilities::ProjectFunction(
L"addEventListener", L"eventRegistration", staticAddEventListener, this, &m_addEventListenerFunction));
RETURN_IF_FAILED(ScriptHostUtilities::ProjectFunction(
L"removeEventListener", L"eventRegistration", staticRemoveEventListener, this, &m_removeEventListenerFunction));
JS_PROJECTION_REGISTER(L"eventRegistration", L"addEventListener", addEventListener);
JS_PROJECTION_REGISTER(L"eventRegistration", L"removeEventListener", removeEventListener);
JS_PROJECTION_REGISTER(L"eventRegistration", L"dispatchEvent", dispatchEvent);

return S_OK;
}

JsValueRef DOMEventRegistration::addEventListener(_In_ JsValueRef callee,
_In_ JsValueRef* arguments,
_In_ unsigned short argumentCount)
JsValueRef DOMEventRegistration::_addEventListener(_In_ JsValueRef* arguments,
_In_ unsigned short argumentCount,
_In_ JsValueRef callee)
{
RETURN_INVALID_REF_IF_FALSE(argumentCount == 4);

Expand All @@ -36,7 +35,9 @@ JsValueRef DOMEventRegistration::addEventListener(_In_ JsValueRef callee,
return JS_INVALID_REFERENCE;
}

JsValueRef DOMEventRegistration::removeEventListener(_In_ JsValueRef* arguments, _In_ unsigned short argumentCount)
JsValueRef DOMEventRegistration::_removeEventListener(_In_ JsValueRef* arguments,
_In_ unsigned short argumentCount,
_In_ JsValueRef callee)
{
RETURN_INVALID_REF_IF_FALSE(argumentCount == 4);

Expand All @@ -51,3 +52,20 @@ JsValueRef DOMEventRegistration::removeEventListener(_In_ JsValueRef* arguments,

return nullptr;
}

JsValueRef DOMEventRegistration::_dispatchEvent(_In_ JsValueRef* arguments,
_In_ unsigned short argumentCount)
{
RETURN_INVALID_REF_IF_FALSE(argumentCount == 4);

auto element = ResourceManager::externalToEventsInterface(arguments[1]);
RETURN_INVALID_REF_IF_NULL(element);

wstring eventName;
RETURN_INVALID_REF_IF_FAILED(ScriptHostUtilities::GetString(arguments[2], eventName));

JsValueRef customEvent = arguments[3];
element->invokeEventListeners(eventName, customEvent);

return nullptr;
}
25 changes: 3 additions & 22 deletions core/dom-events.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,9 @@ class DOMEventRegistration {
long initialize();

private:
JsValueRef m_addEventListenerFunction;
static JsValueRef CHAKRA_CALLBACK staticAddEventListener(_In_ JsValueRef callee,
_In_ bool isConstructCall,
_In_ JsValueRef *arguments,
_In_ unsigned short argumentCount,
_In_ PVOID callbackData)
{
return reinterpret_cast<DOMEventRegistration *>(callbackData)
->addEventListener(callee, arguments, argumentCount);
}
JsValueRef addEventListener(_In_ JsValueRef callee, _In_ JsValueRef *arguments, _In_ unsigned short argumentCount);

JsValueRef m_removeEventListenerFunction;
static JsValueRef CHAKRA_CALLBACK staticRemoveEventListener(_In_ JsValueRef callee,
_In_ bool isConstructCall,
_In_ JsValueRef *arguments,
_In_ unsigned short argumentCount,
_In_ PVOID callbackData)
{
return reinterpret_cast<DOMEventRegistration *>(callbackData)->removeEventListener(arguments, argumentCount);
}
JsValueRef removeEventListener(_In_ JsValueRef *arguments, _In_ unsigned short argumentCount);
JS_PROJECTION_WITH_CONTEXT_DEFINE(DOMEventRegistration, addEventListener)
JS_PROJECTION_WITH_CONTEXT_DEFINE(DOMEventRegistration, removeEventListener)
JS_PROJECTION_DEFINE(DOMEventRegistration, dispatchEvent)
};

} // namespace Interfaces
Expand Down
15 changes: 8 additions & 7 deletions core/event-target.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "include/holojs/private/chakra.h"
#include "include/holojs/private/event-target.h"
#include "include/holojs/private/chakra.h"
#include "include/holojs/private/error-handling.h"
#include <array>

Expand Down Expand Up @@ -68,7 +68,8 @@ long EventTarget::invokeEventListeners(const wstring& eventName)
return S_OK;
}

long EventTarget::invokeEventListeners(const std::wstring& eventName, JsValueRef argument) {
long EventTarget::invokeEventListeners(const std::wstring& eventName, JsValueRef argument)
{
JsValueRef result;

auto handlersForEventName = m_handlerMap.find(eventName);
Expand All @@ -77,15 +78,15 @@ long EventTarget::invokeEventListeners(const std::wstring& eventName, JsValueRef
array<JsValueRef, 2> allArguments;
allArguments[0] = handler.second;
allArguments[1] = argument;
HANDLE_EXCEPTION_IF_JS_ERROR(JsCallFunction(handler.first, allArguments.data(), static_cast<unsigned short>(allArguments.size()), &result));
HANDLE_EXCEPTION_IF_JS_ERROR(JsCallFunction(
handler.first, allArguments.data(), static_cast<unsigned short>(allArguments.size()), &result));
}
}

return S_OK;
}

long EventTarget::invokeEventListeners(const std::wstring& eventName,
const vector<JsValueRef>& arguments)
long EventTarget::invokeEventListeners(const std::wstring& eventName, const vector<JsValueRef>& arguments)
{
JsValueRef result;

Expand All @@ -95,8 +96,8 @@ long EventTarget::invokeEventListeners(const std::wstring& eventName,
vector<JsValueRef> allArguments;
allArguments.push_back(handler.second);
allArguments.insert(allArguments.end(), arguments.begin(), arguments.end());
HANDLE_EXCEPTION_IF_JS_ERROR(
JsCallFunction(handler.first, allArguments.data(), static_cast<unsigned short>(allArguments.size()), &result));
HANDLE_EXCEPTION_IF_JS_ERROR(JsCallFunction(
handler.first, allArguments.data(), static_cast<unsigned short>(allArguments.size()), &result));
}
}

Expand Down
13 changes: 13 additions & 0 deletions core/include/holojs/private/chakra.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ typedef unsigned char* ChakraBytePtr;
} \
JsValueRef _##name(JsValueRef* arguments, unsigned short argumentCount);

#define JS_PROJECTION_WITH_CONTEXT_DEFINE(iface, name) \
JsValueRef m_##name = JS_INVALID_REFERENCE; \
static JsValueRef CHAKRA_CALLBACK static_##name(JsValueRef callee, \
bool isConstructCall, \
JsValueRef* arguments, \
unsigned short argumentCount, \
PVOID callbackData) \
{ \
return reinterpret_cast<##iface*>(callbackData)->_##name(arguments, argumentCount, callee); \
} \
JsValueRef _##name(JsValueRef* arguments, unsigned short argumentCount, JsValueRef callee);


#define JS_PROJECTION_REGISTER(namespaceName, scriptName, nativeName) \
RETURN_IF_FAILED( \
ScriptHostUtilities::ProjectFunction(scriptName, namespaceName, static_##nativeName, this, &m_##nativeName));
Binary file modified core/scripts/gamepad.js
Binary file not shown.
Binary file modified core/scripts/window.js
Binary file not shown.
200 changes: 200 additions & 0 deletions sample-apps/appcode/controller-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
var camera, scene, renderer;

function ControllersStatus() {
this.connectedCount = 0;
this.thumbPressed = false;
this.triggerPressed = false;
this.triggerValue = 0;
this.gripPressed = false;
this.menuPressed = false;
this.thumbpadTouched = false;
this.thumbpadPressed = false;
this.thumbstickAxisX = 0;
this.thumbstickAxisY = 0;
this.thumbpadAxisX = 0;
this.thumbpadAxisY = 0;
}

var controllersStatus = new ControllersStatus();
const controllerColorOn = 0xDB3236;
const controllerColorOff = 0xF4C20D;

function init() {
let canvas = document.createElement('canvasvr');

renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(render);

window.addEventListener('resize', onWindowResize, false);

camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.2, 1000);
camera.position.set(0, 1, 0);

scene = new THREE.Scene();

// Enter immersive mode if available
navigator.getVRDisplays().then(
function (value) {
if (value.length > 0) {
renderer.vr.enabled = true;
renderer.vr.setDevice(value[0]);
value[0].requestPresent([{ source: renderer.domElement }]);
}
});

const gazeInput = window.dat.GUIVR.addInputObject(camera);
scene.add(gazeInput.cursor);

let mainGui = window.dat.GUIVR.create('Controllers');
mainGui.add(controllersStatus, 'connectedCount').listen();
mainGui.add(controllersStatus, 'thumbPressed').listen();
mainGui.add(controllersStatus, 'triggerPressed').listen();
mainGui.add(controllersStatus, 'triggerValue').min(0).max(1).step(0.05).listen();
mainGui.add(controllersStatus, 'gripPressed').listen();
mainGui.add(controllersStatus, 'menuPressed').listen();

mainGui.add(controllersStatus, 'thumbpadTouched').listen();
mainGui.add(controllersStatus, 'thumbpadPressed').listen();

mainGui.add(controllersStatus, 'thumbpadAxisX').min(-1).max(1).step(0.05).listen();
mainGui.add(controllersStatus, 'thumbpadAxisY').min(-1).max(1).step(0.05).listen();

mainGui.add(controllersStatus, 'thumbstickAxisX').min(-1).max(1).step(0.05).listen();
mainGui.add(controllersStatus, 'thumbstickAxisY').min(-1).max(1).step(0.05).listen();

mainGui.position.z = -1;
mainGui.position.y = 1.2;
scene.add(mainGui);

//THREE.VRController.verbosity = 0.5;
}

function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

function render() {
THREE.VRController.update();
renderer.render(scene, camera);
}

window.addEventListener('vr controller connected', function (event) {

var controller = event.detail;
scene.add(controller);

// Update controller count
controllersStatus.connectedCount++;

controller.standingMatrix = renderer.vr.getStandingMatrix();
controller.head = window.camera;

let controllerMaterial = new THREE.MeshBasicMaterial({
color: controllerColorOff
});

let controllerMesh = new THREE.Mesh(
new THREE.CylinderGeometry(0.005, 0.05, 0.1, 6),
controllerMaterial
)

let handleMesh = new THREE.Mesh(
new THREE.BoxGeometry(0.03, 0.01, 0.03),
controllerMaterial
);

controllerMesh.rotation.x = -Math.PI / 2;
handleMesh.position.y = -0.05;
controllerMesh.add(handleMesh);
controller.userData.mesh = controllerMesh;// So we can change the color later.
controller.add(controllerMesh);

// Allow this controller to interact with DAT GUI.
var guiInputHelper = window.dat.GUIVR.addInputObject(controller)
scene.add(guiInputHelper);

// Handle button events to update the UI
controller.addEventListener('primary press began', function (event) {
event.target.userData.mesh.material.color.setHex(controllerColorOn);
guiInputHelper.pressed(true);
});

controller.addEventListener('primary press ended', function (event) {
event.target.userData.mesh.material.color.setHex(controllerColorOff);
guiInputHelper.pressed(false);
});


controller.addEventListener('thumbstick press began', function (event) {
controllersStatus.thumbPressed = true;
});

controller.addEventListener('thumbstick press ended', function (event) {
controllersStatus.thumbPressed = false;
});

controller.addEventListener('trigger press began', function (event) {
controllersStatus.triggerPressed = true;
});

controller.addEventListener('trigger press ended', function (event) {
controllersStatus.triggerPressed = false;
});

controller.addEventListener('trigger value changed', function (event) {
controllersStatus.triggerValue = event.value;
});

controller.addEventListener('grip press began', function (event) {
controllersStatus.gripPressed = true;
});

controller.addEventListener('grip press ended', function (event) {
controllersStatus.gripPressed = false;
});

controller.addEventListener('menu press began', function (event) {
controllersStatus.menuPressed = true;
});

controller.addEventListener('menu press ended', function (event) {
controllersStatus.menuPressed = false;
});

controller.addEventListener('thumbpad touch began', function (event) {
controllersStatus.thumbpadTouched = true;
});

controller.addEventListener('thumbpad touch ended', function (event) {
controllersStatus.thumbpadTouched = false;
});

controller.addEventListener('thumbpad press began', function (event) {
controllersStatus.thumbpadPressed = true;
});

controller.addEventListener('thumbpad press ended', function (event) {
controllersStatus.thumbpadPressed = false;
});

controller.addEventListener('thumbpad axes changed', function (event) {
controllersStatus.thumbpadAxisX = event.axes[0];
controllersStatus.thumbpadAxisY = event.axes[1];
});

controller.addEventListener('thumbstick axes changed', function (event) {
controllersStatus.thumbstickAxisX = event.axes[0];
controllersStatus.thumbstickAxisY = event.axes[1];
});

controller.addEventListener('disconnected', function (event) {
controller.parent.remove(controller);
controllersStatus.connectedCount--;
});
})

init();
1 change: 1 addition & 0 deletions sample-apps/controller-view.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"scripts":["threejs/three.js", "threejs/VRController.js", "threejs/datguivr.js","appcode/controller-view.js"],"resources":[],"name":"controller test app","XsrFileName":"controller-view.json"}
Loading

0 comments on commit 2002eac

Please sign in to comment.