mirror of
https://github.com/AleziaKurdis/Overte-community-apps.git
synced 2025-04-06 07:33:22 +02:00
Add "Exploration Survival Kit" application
The kit includes Compass and a rezzable flashlight for VR exploration.
This commit is contained in:
parent
8e5edf4218
commit
e3468d3258
14 changed files with 488 additions and 0 deletions
|
@ -170,6 +170,15 @@ var metadata = { "applications":
|
|||
"jsfile": "tabletCam/tabletCam_app.js",
|
||||
"icon": "tabletCam/appIcons/snap-pro-i.svg",
|
||||
"caption": "SNAP-PRO"
|
||||
},
|
||||
{
|
||||
"isActive": true,
|
||||
"directory": "survivalKit",
|
||||
"name": "Exploration Survival Kit",
|
||||
"description": "Necessary tools for exploration: a COMPASS and a VR FLASHLIGHT.",
|
||||
"jsfile": "survivalKit/app-survivalKit.js",
|
||||
"icon": "survivalKit/icon_inactive.png",
|
||||
"caption": "SURVIVAL"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
BIN
applications/survivalKit/FiraSans-SemiBold.ttf
Normal file
BIN
applications/survivalKit/FiraSans-SemiBold.ttf
Normal file
Binary file not shown.
214
applications/survivalKit/app-survivalKit.js
Normal file
214
applications/survivalKit/app-survivalKit.js
Normal file
|
@ -0,0 +1,214 @@
|
|||
"use strict";
|
||||
//
|
||||
// app-survivalKit.js
|
||||
//
|
||||
// Created by Alezia Kurdis, December 29th 2022.
|
||||
// Copyright 2022, Overte e.V.
|
||||
//
|
||||
// Survival kit for virtual worlds exploration in Overte.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
(function() {
|
||||
var jsMainFileName = "app-survivalKit.js";
|
||||
var ROOT = Script.resolvePath('').split(jsMainFileName)[0];
|
||||
|
||||
var APP_NAME = "SURVIVAL";
|
||||
var APP_URL = ROOT + "survivalKit.html";
|
||||
var APP_ICON_INACTIVE = ROOT + "icon_inactive.png";
|
||||
var APP_ICON_ACTIVE = ROOT + "icon_active.png";
|
||||
var appStatus = false;
|
||||
|
||||
var channel = "org.overte.app.survivalKit";
|
||||
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
var UPDATE_TIMER_INTERVAL = 100; // 0.1 sec
|
||||
var processTimer = 0;
|
||||
|
||||
var flashLightID = Uuid.NULL;
|
||||
var flashLightLightID = Uuid.NULL;
|
||||
var filter = "WHITE";
|
||||
|
||||
var timestamp = 0;
|
||||
var INTERCALL_DELAY = 200; //0.3 sec
|
||||
|
||||
var FLASHLIGHT_NAME = "%%!!Survival.Kit.vr.flashlight!!%%";
|
||||
|
||||
tablet.screenChanged.connect(onScreenChanged);
|
||||
|
||||
var button = tablet.addButton({
|
||||
text: APP_NAME,
|
||||
icon: APP_ICON_INACTIVE,
|
||||
activeIcon: APP_ICON_ACTIVE
|
||||
});
|
||||
|
||||
var lightColor = {
|
||||
"WHITE": {"red": 255, "green": 250, "blue": 230},
|
||||
"AMBER": {"red": 255, "green": 145, "blue": 0},
|
||||
"RED": {"red": 255, "green": 0, "blue": 0},
|
||||
"BLUE": {"red": 0, "green": 119, "blue": 255}
|
||||
};
|
||||
|
||||
function clicked(){
|
||||
if (appStatus === true) {
|
||||
tablet.webEventReceived.disconnect(onMoreAppWebEventReceived);
|
||||
tablet.gotoHomeScreen();
|
||||
Script.update.disconnect(myTimer);
|
||||
appStatus = false;
|
||||
}else{
|
||||
Script.update.connect(myTimer);
|
||||
var isFlashlightActive = "OFF";
|
||||
if (flashLightID !== Uuid.NULL) {
|
||||
isFlashlightActive = "ON";
|
||||
}
|
||||
var url = APP_URL + "?flashlight=" + isFlashlightActive + "&filter=" + filter;
|
||||
tablet.gotoWebScreen(url);
|
||||
tablet.webEventReceived.connect(onMoreAppWebEventReceived);
|
||||
appStatus = true;
|
||||
}
|
||||
|
||||
if (flashLightID === Uuid.NULL) {
|
||||
button.editProperties({
|
||||
isActive: appStatus
|
||||
});
|
||||
} else {
|
||||
button.editProperties({
|
||||
isActive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
button.clicked.connect(clicked);
|
||||
|
||||
|
||||
function onMoreAppWebEventReceived(message) {
|
||||
var d = new Date();
|
||||
var n = d.getTime();
|
||||
|
||||
if (typeof message === "string") {
|
||||
eventObj = JSON.parse(message);
|
||||
if (eventObj.channel === channel) {
|
||||
if (eventObj.action === "UPDATE_FLASHLIGHT_ACTIVATION" && (n - timestamp) > INTERCALL_DELAY) {
|
||||
d = new Date();
|
||||
timestamp = d.getTime();
|
||||
if (eventObj.isActive === true) {
|
||||
createFlashLight();
|
||||
} else {
|
||||
clearFlashLight();
|
||||
}
|
||||
} else if (eventObj.action === "UPDATE_FLASHLIGHT_FILTER" && (n - timestamp) > INTERCALL_DELAY) {
|
||||
d = new Date();
|
||||
timestamp = d.getTime();
|
||||
filter = eventObj.filter;
|
||||
updateFlashLightFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onScreenChanged(type, url) {
|
||||
if (type === "Web" && url.indexOf(APP_URL) !== -1) {
|
||||
appStatus = true;
|
||||
Script.update.connect(myTimer);
|
||||
} else {
|
||||
appStatus = false;
|
||||
Script.update.disconnect(myTimer);
|
||||
}
|
||||
|
||||
if (flashLightID === Uuid.NULL) {
|
||||
button.editProperties({
|
||||
isActive: appStatus
|
||||
});
|
||||
} else {
|
||||
button.editProperties({
|
||||
isActive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function myTimer(deltaTime) {
|
||||
var today = new Date();
|
||||
if ((today.getTime() - processTimer) > UPDATE_TIMER_INTERVAL ) {
|
||||
var azimuth = "" + (2000 - Math.floor(2000 * ((Quat.safeEulerAngles(MyAvatar.orientation).y + 180) / 360)));
|
||||
var dataToUi = {
|
||||
"channel": channel,
|
||||
"action": "UPDATE_AZIMUTH",
|
||||
"azimuth": azimuth
|
||||
};
|
||||
tablet.emitScriptEvent(JSON.stringify(dataToUi));
|
||||
|
||||
today = new Date();
|
||||
processTimer = today.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
function createFlashLight() {
|
||||
var entityIDs = Entities.findEntitiesByName(FLASHLIGHT_NAME, MyAvatar.position, 100, false);
|
||||
entityIDs.forEach(function (currentEntityID) {
|
||||
var currentEntityOwner = Entities.getEntityProperties(currentEntityID, ['owningAvatarID']).owningAvatarID;
|
||||
if (currentEntityOwner === MyAvatar.sessionUUID && currentEntityID !== flashLightID) {
|
||||
Entities.deleteEntity(currentEntityID);
|
||||
}
|
||||
});
|
||||
|
||||
if (flashLightID === Uuid.NULL) {
|
||||
flashLightID = Entities.addEntity({
|
||||
"type": "Model",
|
||||
"modelURL": ROOT + "vrFlashLight_" + filter + ".fst",
|
||||
"name": FLASHLIGHT_NAME,
|
||||
"lifetime": 28800,
|
||||
"position": Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 })),
|
||||
"shapeType": "cylinder-z",
|
||||
"grab": {
|
||||
"grabbable": true
|
||||
},
|
||||
"rotation": MyAvatar.orientation
|
||||
}, "avatar");
|
||||
|
||||
flashLightLightID = Entities.addEntity({
|
||||
"parentID": flashLightID,
|
||||
"type": "Light",
|
||||
"name": "Flashlight-Light",
|
||||
"dimensions": {"x": 192.8363, "y": 192.8363, "z": 300},
|
||||
"color": lightColor[filter],
|
||||
"intensity": 15,
|
||||
"falloffRadius": 1,
|
||||
"isSpotlight": true,
|
||||
"exponent": 1,
|
||||
"cutoff": 40,
|
||||
"localPosition": {"x": 0, "y": 0, "z": -0.18}
|
||||
}, "avatar");
|
||||
}
|
||||
}
|
||||
|
||||
function updateFlashLightFilter() {
|
||||
if (flashLightID !== Uuid.NULL) {
|
||||
Entities.editEntity(flashLightID, {"modelURL": ROOT + "vrFlashLight_" + filter + ".fst"});
|
||||
Entities.editEntity(flashLightLightID, {"color": lightColor[filter]});
|
||||
}
|
||||
}
|
||||
|
||||
function clearFlashLight() {
|
||||
if (flashLightID !== Uuid.NULL) {
|
||||
Entities.deleteEntity(flashLightID);
|
||||
flashLightID = Uuid.NULL;
|
||||
flashLightLightID = Uuid.NULL;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
|
||||
if (appStatus) {
|
||||
tablet.gotoHomeScreen();
|
||||
tablet.webEventReceived.disconnect(onMoreAppWebEventReceived);
|
||||
}
|
||||
clearFlashLight();
|
||||
tablet.screenChanged.disconnect(onScreenChanged);
|
||||
tablet.removeButton(button);
|
||||
Script.update.disconnect(myTimer);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
}());
|
BIN
applications/survivalKit/compassStrip.jpg
Normal file
BIN
applications/survivalKit/compassStrip.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
BIN
applications/survivalKit/flashlight.png
Normal file
BIN
applications/survivalKit/flashlight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
BIN
applications/survivalKit/icon_active.png
Normal file
BIN
applications/survivalKit/icon_active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
applications/survivalKit/icon_inactive.png
Normal file
BIN
applications/survivalKit/icon_inactive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
253
applications/survivalKit/survivalKit.html
Normal file
253
applications/survivalKit/survivalKit.html
Normal file
|
@ -0,0 +1,253 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
survivalKit.html
|
||||
|
||||
Created by Alezia Kurdis, December 29th 2022.
|
||||
Copyright 2022, Overte e.V.
|
||||
|
||||
Survival kit for virtual worlds exploration in Overte. UI.
|
||||
|
||||
Distributed under the Apache License, Version 2.0.
|
||||
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
var channel = "org.overte.app.survivalKit";
|
||||
var isFlashLightActive = false;
|
||||
var flashLightFilter = "WHITE";
|
||||
|
||||
function findGetParameter(parameterName) {
|
||||
var result = null,
|
||||
tmp = [];
|
||||
var items = location.search.substr(1).split("&");
|
||||
for (var index = 0; index < items.length; index++) {
|
||||
tmp = items[index].split("=");
|
||||
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var flashlight = findGetParameter("flashlight");
|
||||
if (flashlight === null) {
|
||||
isFlashLightActive = false;
|
||||
} else {
|
||||
if (flashlight === "ON") {
|
||||
isFlashLightActive = true;
|
||||
} else {
|
||||
isFlashLightActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
var filter = findGetParameter("filter");
|
||||
if (filter === null) {
|
||||
flashLightFilter = "WHITE";
|
||||
} else {
|
||||
if (filter === "WHITE" || filter === "AMBER" || filter === "RED" || filter === "BLUE") {
|
||||
flashLightFilter = filter;
|
||||
} else {
|
||||
flashLightFilter = "WHITE";
|
||||
}
|
||||
}
|
||||
|
||||
var thisPageName = "survivalKit.html";
|
||||
var currentPath = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
||||
var ROOTPATH = currentPath.replace(thisPageName, "");
|
||||
|
||||
EventBridge.scriptEventReceived.connect(function(message){
|
||||
var messageObj = JSON.parse(message);
|
||||
if (messageObj.channel === channel) {
|
||||
if (messageObj.action === "UPDATE_AZIMUTH") {
|
||||
var azimuth = messageObj.azimuth;
|
||||
compassBox.style.objectPosition = "-" + azimuth + "px 0px";
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function activeFlashLight() {
|
||||
isFlashLightActive = document.getElementById("vrFlashlightActivation").checked;
|
||||
|
||||
var dataObj = {
|
||||
"channel": channel,
|
||||
"action": "UPDATE_FLASHLIGHT_ACTIVATION",
|
||||
"isActive": isFlashLightActive
|
||||
};
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify(dataObj));
|
||||
}
|
||||
|
||||
function setFlashLightFilter() {
|
||||
var filters = document.getElementsByName('lightFilter');
|
||||
for(var i = 0; i < filters.length; i++){
|
||||
if(filters[i].checked){
|
||||
flashLightFilter = filters[i].value;
|
||||
}
|
||||
}
|
||||
var dataObj = {
|
||||
"channel": channel,
|
||||
"action": "UPDATE_FLASHLIGHT_FILTER",
|
||||
"filter": flashLightFilter
|
||||
};
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify(dataObj));
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: FiraSans-SemiBold;
|
||||
src: url(FiraSans-SemiBold.ttf);
|
||||
}
|
||||
body {
|
||||
background: #4f5161;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
#compass {
|
||||
width: 400px;
|
||||
height: 60px;
|
||||
overflow:hidden;
|
||||
border-style: inset;
|
||||
border-color: #75788f;
|
||||
object-fit: none;
|
||||
object-position: 0px 0px;
|
||||
}
|
||||
div.compassControl {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 0px;
|
||||
}
|
||||
div.compassPointer {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 24px;
|
||||
color: #660000;
|
||||
padding: 0px;
|
||||
}
|
||||
h1 {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 24px;
|
||||
color: #e8d8b0;
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #363636;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #0ee32a;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 0px #0ee32a;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>COMPASS:</h1>
|
||||
<div class = "compassControl">
|
||||
<div class = "compassPointer">▼</div>
|
||||
<img id="compass">
|
||||
<div class = "compassPointer">▲</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<h1>VR FLASHLIGHT:</h1>
|
||||
<table><tr><td style="text-align: center;">
|
||||
<label class="switch">
|
||||
<input id="vrFlashlightActivation" type="checkbox" onClick="activeFlashLight();">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</td><td style="text-align: center;">
|
||||
<img src = "flashlight.png" style = "width: 300px;">
|
||||
</td></tr></table>
|
||||
<div style="margin-left: 60px;">
|
||||
<input type="radio" id="lightFilter_white" name="lightFilter" value="WHITE" onClick="setFlashLightFilter();">
|
||||
<label for="WHITE"><font style="color:#FFFFFF;">White</font></label><br>
|
||||
<input type="radio" id="lightFilter_amber" name="lightFilter" value="AMBER" onClick="setFlashLightFilter();">
|
||||
<label for="AMBER"><font style="color:#ffa42e;">Amber</font></label><br>
|
||||
<input type="radio" id="lightFilter_red" name="lightFilter" value="RED" onClick="setFlashLightFilter();">
|
||||
<label for="RED"><font style="color:#ff2121;">Red</font></label><br>
|
||||
<input type="radio" id="lightFilter_blue" name="lightFilter" value="BLUE" onClick="setFlashLightFilter();">
|
||||
<label for="BLUE"><font style="color:#3094ff;">Ultra Blue</font></label>
|
||||
</div>
|
||||
<script>
|
||||
var compassBox = document.getElementById("compass");
|
||||
compassBox.style.objectPosition = "0px 0px";
|
||||
compassBox.src = ROOTPATH + "compassStrip.jpg";
|
||||
|
||||
document.getElementById("vrFlashlightActivation").checked = isFlashLightActive;
|
||||
|
||||
switch(flashLightFilter) {
|
||||
case "WHITE":
|
||||
document.getElementById("lightFilter_white").checked = true;
|
||||
break;
|
||||
case "AMBER":
|
||||
document.getElementById("lightFilter_amber").checked = true;
|
||||
break;
|
||||
case "RED":
|
||||
document.getElementById("lightFilter_red").checked = true;
|
||||
break;
|
||||
case "BLUE":
|
||||
document.getElementById("lightFilter_blue").checked = true;
|
||||
break;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
BIN
applications/survivalKit/vrFlashLight.fbx
Normal file
BIN
applications/survivalKit/vrFlashLight.fbx
Normal file
Binary file not shown.
3
applications/survivalKit/vrFlashLight_AMBER.fst
Normal file
3
applications/survivalKit/vrFlashLight_AMBER.fst
Normal file
|
@ -0,0 +1,3 @@
|
|||
name = vrFlashLight_AMBER
|
||||
filename = vrFlashLight.fbx
|
||||
materialMap = [{"mat::LIGHT": {"materials":[{ "name": "LIGHT", "albedo": [1,1,1], "roughness": 0.9, "metallic": 0.01, "emissive": [ 2.66, 1.96109, 0.82407], "cullFaceMode": "CULL_BACK", "defaultFallthrough": true}]}}]
|
3
applications/survivalKit/vrFlashLight_BLUE.fst
Normal file
3
applications/survivalKit/vrFlashLight_BLUE.fst
Normal file
|
@ -0,0 +1,3 @@
|
|||
name = vrFlashLight_BLUE
|
||||
filename = vrFlashLight.fbx
|
||||
materialMap = [{"mat::LIGHT": {"materials":[{ "name": "LIGHT", "albedo": [1,1,1], "roughness": 0.9, "metallic": 0.01, "emissive": [ 0.69133, 1.28066, 2.89], "cullFaceMode": "CULL_BACK", "defaultFallthrough": true}]}}]
|
3
applications/survivalKit/vrFlashLight_RED.fst
Normal file
3
applications/survivalKit/vrFlashLight_RED.fst
Normal file
|
@ -0,0 +1,3 @@
|
|||
name = vrFlashLight_RED
|
||||
filename = vrFlashLight.fbx
|
||||
materialMap = [{"mat::LIGHT": {"materials":[{ "name": "LIGHT", "albedo": [1,1,1], "roughness": 0.9, "metallic": 0.01, "emissive": [2.73, 0, 0], "cullFaceMode": "CULL_BACK", "defaultFallthrough": true}]}}]
|
3
applications/survivalKit/vrFlashLight_WHITE.fst
Normal file
3
applications/survivalKit/vrFlashLight_WHITE.fst
Normal file
|
@ -0,0 +1,3 @@
|
|||
name = vrFlashLight_WHITE
|
||||
filename = vrFlashLight.fbx
|
||||
materialMap = [{"mat::LIGHT": {"materials":[{ "name": "LIGHT", "albedo": [1,1,1], "roughness": 0.9, "metallic": 0.01, "emissive": [ 2.13, 2.013, 1.6789], "cullFaceMode": "CULL_BACK", "defaultFallthrough": true}]}}]
|
BIN
applications/survivalKit/vrFlashLight_normal.jpg
Normal file
BIN
applications/survivalKit/vrFlashLight_normal.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 189 KiB |
Loading…
Reference in a new issue