"use strict"; // // androidControls.js // // Created by keeshii on September 26th, 2023. // Copyright 2022-2023 Overte e.V. // // This script read touch screen events and triggers mouse events. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // (function () { Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var DISPATCHER_TOUCH_PROPERTIES = ["id", "position", "rotation", "dimensions", "registrationPoint"]; var TAP_DELAY = 300; function AndroidControls() { this.onTouchStartFn = null; this.onTouchEndFn = null; this.touchStartTime = 0; } AndroidControls.prototype.intersectsOverlay = function (intersection) { if (intersection && intersection.intersects && intersection.overlayID) { return true; } return false; }; AndroidControls.prototype.intersectsEntity = function (intersection) { if (intersection && intersection.intersects && intersection.entityID) { return true; } return false; }; AndroidControls.prototype.findRayIntersection = function (pickRay) { // Check 3D overlays and entities. Argument is an object with origin and direction. var overlayRayIntersection = Overlays.findRayIntersection(pickRay); var entityRayIntersection = Entities.findRayIntersection(pickRay, true); var isOverlayInters = this.intersectsOverlay(overlayRayIntersection); var isEntityInters = this.intersectsEntity(entityRayIntersection); if (isOverlayInters && (!isEntityInters || overlayRayIntersection.distance < entityRayIntersection.distance)) { return {type: 'overlay', obj: overlayRayIntersection}; } else if (isEntityInters) { return {type: 'entity', obj: entityRayIntersection}; } return false; }; AndroidControls.prototype.createEventProperties = function (entityId, info, eventType) { var pointerEvent = { type: eventType, id: 1, pos2D: {x: 0, y: 0}, pos3D: info.obj.intersection, normal: info.obj.surfaceNormal, direction: info.obj.direction, button: "Primary", isPrimaryButton: true, isLeftButton: true, isPrimaryHeld: eventType === 'Press', isSecondaryHeld: false, isTertiaryHeld: false, keyboardModifiers: 0 }; var properties = Entities.getEntityProperties(entityId, DISPATCHER_TOUCH_PROPERTIES); if (properties.id === entityId) { pointerEvent.pos2D = info.type === "entity" ? projectOntoEntityXYPlane(entityId, info.obj.intersection, properties) : projectOntoOverlayXYPlane(entityId, info.obj.intersection, properties); } return pointerEvent; }; AndroidControls.prototype.triggerClick = function (event) { var info = this.findRayIntersection(Camera.computePickRay(event.x, event.y)); if (!info) { return; } var entityId = info.type === "entity" ? info.obj.entityID : info.obj.overlayID; var pressEvent = this.createEventProperties(entityId, info, 'Press'); var releaseEvent = this.createEventProperties(entityId, info, 'Release'); Entities.sendMousePressOnEntity(entityId, pressEvent); Entities.sendClickDownOnEntity(entityId, pressEvent); Script.setTimeout(function () { Entities.sendMouseReleaseOnEntity(entityId, releaseEvent); Entities.sendClickReleaseOnEntity(entityId, releaseEvent); }, 75); }; AndroidControls.prototype.onTouchStart = function (_event) { this.touchStartTime = Date.now(); }; AndroidControls.prototype.onTouchEnd = function (event) { var now = Date.now(); if (now - this.touchStartTime < TAP_DELAY) { this.triggerClick(event); } this.touchStartTime = 0; }; AndroidControls.prototype.init = function () { var self = this; this.onTouchStartFn = function (ev) { self.onTouchStart(ev); }; this.onTouchEndFn = function (ev) { self.onTouchEnd(ev); }; Controller.touchBeginEvent.connect(this.onTouchStartFn); Controller.touchEndEvent.connect(this.onTouchEndFn); }; AndroidControls.prototype.ending = function () { if (this.onTouchStartFn) { Controller.touchBeginEvent.disconnect(this.onTouchStartFn); } if (this.onTouchEndFn) { Controller.touchEndEvent.disconnect(this.onTouchEndFn); } this.touchStartTime = 0; this.onTouchStartFn = null; this.onTouchEndFn = null; }; var androidControls = new AndroidControls(); Script.scriptEnding.connect(function () { androidControls.ending(); }); androidControls.init(); module.exports = androidControls; }());