mirror of
https://github.com/overte-org/community-apps.git
synced 2025-04-08 07:22:45 +02:00
Add the "Light Bulb Generator" app
This adds the "Light Bulb Generator" application: To generate rapidly a visible glowing light source with its light beam.
This commit is contained in:
parent
897ca71ad5
commit
62b79e6fc8
8 changed files with 509 additions and 2 deletions
BIN
applications/lightBulb/FiraSans-SemiBold.ttf
Normal file
BIN
applications/lightBulb/FiraSans-SemiBold.ttf
Normal file
Binary file not shown.
33
applications/lightBulb/LIGHT_BULB.json
Normal file
33
applications/lightBulb/LIGHT_BULB.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"DataVersion": 0,
|
||||
"Entities": [
|
||||
{
|
||||
"type": "Sphere",
|
||||
"name": "LIGHT_BULB",
|
||||
"locked": false,
|
||||
"dimensions": {
|
||||
"x": 0.06,
|
||||
"y": 0.06,
|
||||
"z": 0.06
|
||||
},
|
||||
"rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"grab": {
|
||||
"grabbable": false
|
||||
},
|
||||
"color": {
|
||||
"red": 255,
|
||||
"green": 255,
|
||||
"blue": 255
|
||||
},
|
||||
"userData": "{\"lightIntensity\":5,\"lightSpotCutOff\": 30,\"lightRange\": 10}",
|
||||
"script": "https://aleziakurdis.github.io/lightBulb/lightBulb.js"
|
||||
}
|
||||
],
|
||||
"Id": "{5cbb14bf-ca6c-4aa9-bcfa-1a53c8438669}",
|
||||
"Version": 133
|
||||
}
|
143
applications/lightBulb/app-lightBulb.js
Normal file
143
applications/lightBulb/app-lightBulb.js
Normal file
|
@ -0,0 +1,143 @@
|
|||
"use strict";
|
||||
//
|
||||
// app-lightBulb.js
|
||||
//
|
||||
// Created by Alezia Kurdis, April 23rd 2022.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Application to generate light bulbs.
|
||||
//
|
||||
// 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-lightBulb.js";
|
||||
var ROOT = Script.resolvePath('').split(jsMainFileName)[0];
|
||||
|
||||
var intensity = 5;
|
||||
var angle = 30;
|
||||
var range = 10;
|
||||
var wOrH = "white";
|
||||
var hue = 30;
|
||||
|
||||
var APP_NAME = "BULB";
|
||||
var APP_URL = ROOT + "lightBulb.html";
|
||||
var APP_ICON_INACTIVE = ROOT + "icon_inactive.png";
|
||||
var APP_ICON_ACTIVE = ROOT + "icon_active.png";
|
||||
var LIGHT_BULB_SCRIPT = ROOT + "lightBulb.js";
|
||||
var appStatus = false;
|
||||
|
||||
var channel = "com.overte.app.lightBulb";
|
||||
var timestamp = 0;
|
||||
var INTERCALL_DELAY = 200; //0.3 sec
|
||||
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
tablet.screenChanged.connect(onScreenChanged);
|
||||
|
||||
var button = tablet.addButton({
|
||||
text: APP_NAME,
|
||||
icon: APP_ICON_INACTIVE,
|
||||
activeIcon: APP_ICON_ACTIVE
|
||||
});
|
||||
|
||||
|
||||
function clicked(){
|
||||
if (appStatus === true) {
|
||||
tablet.webEventReceived.disconnect(onAppWebEventReceived);
|
||||
tablet.gotoHomeScreen();
|
||||
appStatus = false;
|
||||
}else{
|
||||
tablet.gotoWebScreen(APP_URL + "?woh=" + wOrH + "&hue=" + hue + "&int=" + intensity + "&ang=" + angle + "&ran=" + range);
|
||||
tablet.webEventReceived.connect(onAppWebEventReceived);
|
||||
appStatus = true;
|
||||
}
|
||||
|
||||
button.editProperties({
|
||||
isActive: appStatus
|
||||
});
|
||||
}
|
||||
|
||||
button.clicked.connect(clicked);
|
||||
|
||||
|
||||
function onAppWebEventReceived(message) {
|
||||
var d = new Date();
|
||||
var n = d.getTime();
|
||||
|
||||
var messageObj = JSON.parse(message);
|
||||
if (messageObj.channel === channel) {
|
||||
if (messageObj.action === "GEN_LIGHT_BULB" && (n - timestamp) > INTERCALL_DELAY) {
|
||||
d = new Date();
|
||||
timestamp = d.getTime();
|
||||
|
||||
intensity = messageObj.lightIntensity;
|
||||
angle = messageObj.lightSpotCutOff;
|
||||
range = messageObj.lightRange;
|
||||
wOrH = messageObj.colorMode;
|
||||
hue = messageObj.hue;
|
||||
|
||||
generatelightBulb(messageObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onScreenChanged(type, url) {
|
||||
if (type == "Web" && url.indexOf(APP_URL) != -1) {
|
||||
appStatus = true;
|
||||
} else {
|
||||
appStatus = false;
|
||||
}
|
||||
|
||||
button.editProperties({
|
||||
isActive: appStatus
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
|
||||
if (appStatus) {
|
||||
tablet.gotoHomeScreen();
|
||||
tablet.webEventReceived.disconnect(onMoreAppWebEventReceived);
|
||||
}
|
||||
|
||||
tablet.screenChanged.disconnect(onScreenChanged);
|
||||
tablet.removeButton(button);
|
||||
}
|
||||
|
||||
function generatelightBulb(data) {
|
||||
|
||||
var id = Entities.addEntity({
|
||||
"type": "Sphere",
|
||||
"name": "LIGHT_BULB",
|
||||
"locked": false,
|
||||
"dimensions": {
|
||||
"x": 0.06,
|
||||
"y": 0.06,
|
||||
"z": 0.06
|
||||
},
|
||||
"rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"position": Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -2 })),
|
||||
"grab": {
|
||||
"grabbable": false
|
||||
},
|
||||
"color": {
|
||||
"red": data.color.red,
|
||||
"green": data.color.green,
|
||||
"blue": data.color.blue
|
||||
},
|
||||
"userData": "{\"lightIntensity\":" + data.lightIntensity + ",\"lightSpotCutOff\": " + data.lightSpotCutOff + ",\"lightRange\": " + data.lightRange + "}",
|
||||
"script": LIGHT_BULB_SCRIPT
|
||||
}, "domain");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
}());
|
BIN
applications/lightBulb/icon_active.png
Normal file
BIN
applications/lightBulb/icon_active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
applications/lightBulb/icon_inactive.png
Normal file
BIN
applications/lightBulb/icon_inactive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
211
applications/lightBulb/lightBulb.html
Normal file
211
applications/lightBulb/lightBulb.html
Normal file
|
@ -0,0 +1,211 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
lightBulb.html
|
||||
|
||||
Created by Alezia Kurdis, April 23rd 2022.
|
||||
Copyright 2022 Overte e.V.
|
||||
|
||||
HTML UI for an application to generate light bulbs.
|
||||
|
||||
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>
|
||||
//Parameters
|
||||
|
||||
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 channel = "com.overte.app.lightBulb";
|
||||
|
||||
//Paths
|
||||
var thisPageName = "lightBulb.html";
|
||||
var currentPath = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
||||
var ROOTPATH = currentPath.replace(thisPageName, "");
|
||||
|
||||
</script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: FiraSans-SemiBold;
|
||||
src: url(FiraSans-SemiBold.ttf);
|
||||
}
|
||||
|
||||
body {
|
||||
background: #363842;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 18px;
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
#colorPreview {
|
||||
background: #ffffff;
|
||||
border: 1px solid #000000;
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>LIGHT BULB GENERATOR</h1><hr>
|
||||
Color: <br>
|
||||
<table><tr><td>
|
||||
<div id = "colorPreview"></div>
|
||||
</td><td>
|
||||
<input type = "radio" id="colorWhite" name="colorType" onclick="setColor();" value = "white" checked>White<br>
|
||||
<input type = "radio" id="colorHue" name="colorType" onclick="setColor();" value = "hue">Hue <input id="hue" type="number" size = "5" value="30" onchange='setColor();'>°
|
||||
</td></tr></table>
|
||||
<br><br>
|
||||
Intensity: <input id="intensity" type="number" size = "5" value="5" onchange='setColor();'><br><br>
|
||||
Beam angle: <input id="beamAngle" type="number" size = "5" value="30" onchange='setColor();'>°<br><br>
|
||||
Range: <input id="range" type="number" size = "5" value="10" onchange='setColor();'>m<br><br><br>
|
||||
|
||||
<button onClick='genLightBulb();'>GENERATE A LIGHT BULB</button>
|
||||
|
||||
<script>
|
||||
function setColor() {
|
||||
var color;
|
||||
|
||||
var hue = parseInt(document.getElementById("hue").value, 10)/360;
|
||||
if (document.getElementById("colorWhite").checked) {
|
||||
color = hslToRgb(hue, 1, 1);
|
||||
} else {
|
||||
color = hslToRgb(hue, 1, 0.5);
|
||||
}
|
||||
var h = parseInt(document.getElementById("hue").value,10);
|
||||
if (h >= 720 || h < -720) {
|
||||
document.getElementById("hue").value = 0;
|
||||
} else if (h < 0) {
|
||||
document.getElementById("hue").value = h + 360;
|
||||
} else if (h >= 360) {
|
||||
document.getElementById("hue").value = h - 360;
|
||||
}
|
||||
|
||||
document.getElementById("colorPreview").style.backgroundColor = "rgb(" + color[0] + ", " + color[1] + ", " + color[2] + ")";
|
||||
|
||||
var i = parseInt(document.getElementById("intensity").value,10);
|
||||
if (i < 0) {
|
||||
document.getElementById("intensity").value = 0;
|
||||
}
|
||||
|
||||
var a = parseInt(document.getElementById("beamAngle").value,10);
|
||||
if (a < 0) {
|
||||
document.getElementById("beamAngle").value = 0;
|
||||
}
|
||||
if (a >= 180) {
|
||||
document.getElementById("beamAngle").value = 180;
|
||||
}
|
||||
|
||||
var r = parseInt(document.getElementById("range").value,10);
|
||||
if (r < 0) {
|
||||
document.getElementById("range").value = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function genLightBulb(){
|
||||
var hue = parseInt(document.getElementById("hue").value, 10)/360;
|
||||
var colorMode = "white";
|
||||
if (document.getElementById("colorWhite").checked) {
|
||||
color = hslToRgb(hue, 1, 1);
|
||||
} else {
|
||||
color = hslToRgb(hue, 1, 0.6);
|
||||
colorMode = "hue";
|
||||
}
|
||||
var lightColor = {"red": color[0], "green": color[1], "blue": color[2]};
|
||||
|
||||
var intensity = parseInt(document.getElementById("intensity").value,10);
|
||||
var beamAngle = parseInt(document.getElementById("beamAngle").value,10);
|
||||
var range = parseInt(document.getElementById("range").value,10);
|
||||
|
||||
var data = {
|
||||
"action": "GEN_LIGHT_BULB",
|
||||
"channel": channel,
|
||||
"color": lightColor,
|
||||
"lightIntensity": intensity,
|
||||
"lightSpotCutOff": beamAngle,
|
||||
"lightRange": range,
|
||||
"colorMode": colorMode,
|
||||
"hue": parseInt(document.getElementById("hue").value, 10)
|
||||
};
|
||||
|
||||
EventBridge.emitWebEvent(JSON.stringify(data));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*
|
||||
* @param {number} h The hue
|
||||
* @param {number} s The saturation
|
||||
* @param {number} l The lightness
|
||||
* @return {Array} The RGB representation
|
||||
*/
|
||||
function hslToRgb(h, s, l){
|
||||
var r, g, b;
|
||||
|
||||
if(s == 0){
|
||||
r = g = b = l; // achromatic
|
||||
}else{
|
||||
var hue2rgb = function hue2rgb(p, q, t){
|
||||
if(t < 0) t += 1;
|
||||
if(t > 1) t -= 1;
|
||||
if(t < 1/6) return p + (q - p) * 6 * t;
|
||||
if(t < 1/2) return q;
|
||||
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1/3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1/3);
|
||||
}
|
||||
|
||||
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||
}
|
||||
|
||||
//Reading paramaters and apply them
|
||||
var parameter = findGetParameter("woh");
|
||||
if(parameter === null){parameter = "white";}
|
||||
if (parameter === "white") {
|
||||
document.getElementById("colorWhite").checked = true;
|
||||
document.getElementById("colorHue").checked = false;
|
||||
} else {
|
||||
document.getElementById("colorWhite").checked = false;
|
||||
document.getElementById("colorHue").checked = true;
|
||||
}
|
||||
|
||||
parameter = findGetParameter("hue");
|
||||
document.getElementById("hue").value = parseInt(parameter, 10);
|
||||
|
||||
parameter = findGetParameter("int");
|
||||
document.getElementById("intensity").value = parseInt(parameter, 10);
|
||||
|
||||
parameter = findGetParameter("ang");
|
||||
document.getElementById("beamAngle").value = parseInt(parameter, 10);
|
||||
|
||||
parameter = findGetParameter("ran");
|
||||
document.getElementById("range").value = parseInt(parameter, 10);
|
||||
setColor();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
111
applications/lightBulb/lightBulb.js
Normal file
111
applications/lightBulb/lightBulb.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
"use strict";
|
||||
//
|
||||
// lightBulb.js
|
||||
//
|
||||
// Created by Alezia Kurdis, July 14th, 2021.
|
||||
// Copyright 2022 Overte e.V.
|
||||
//
|
||||
// Turn a shape to a light bulb.
|
||||
//
|
||||
// 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 ROOT = Script.resolvePath('').split("lightBulb.js")[0];
|
||||
var DEG_TO_RAD = Math.PI/180;
|
||||
var lightID = Uuid.NULL;
|
||||
var materialID = Uuid.NULL;
|
||||
|
||||
this.preload = function(entityID) {
|
||||
|
||||
var properties = Entities.getEntityProperties(entityID, ["renderWithZones", "color", "userData"]);
|
||||
var renderWithZones = properties.renderWithZones;
|
||||
var color = properties.color;
|
||||
var userData = JSON.parse(properties.userData);
|
||||
|
||||
var lightIntensity = userData.lightIntensity ? userData.lightIntensity : 5;
|
||||
var lightSpotCutOff = userData.lightSpotCutOff ? userData.lightSpotCutOff : 30;
|
||||
var lightRange = userData.lightRange ? userData.lightRange : 10;
|
||||
|
||||
|
||||
|
||||
lightID = Entities.addEntity({
|
||||
"parentID": entityID,
|
||||
"renderWithZones": renderWithZones,
|
||||
"locked": false,
|
||||
"localPosition": {"x": 0.0, "y": 0.0, "z": 0.0},
|
||||
"localRotation": {
|
||||
"x": -0.7071,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"w": 0.7071
|
||||
},
|
||||
"name": "bulb-light",
|
||||
"grab": {
|
||||
"grabbable": false
|
||||
},
|
||||
"type": "Light",
|
||||
"dimensions": {
|
||||
"x": lightRange * 2,
|
||||
"y": lightRange * 2,
|
||||
"z": lightRange * 2
|
||||
},
|
||||
"color": color,
|
||||
"intensity": lightIntensity,
|
||||
"falloffRadius": 1,
|
||||
"isSpotlight": true,
|
||||
"visible": true,
|
||||
"exponent": 1,
|
||||
"cutoff": lightSpotCutOff
|
||||
},"local");
|
||||
|
||||
var sumColorCompnent = (color.red/255) +(color.green/255) +(color.blue/255);
|
||||
if (sumColorCompnent === 0) {
|
||||
sumColorCompnent = 0.001;
|
||||
}
|
||||
var bloomFactor = 9 / sumColorCompnent;
|
||||
|
||||
var materialContent = {
|
||||
"materialVersion": 1,
|
||||
"materials": [
|
||||
{
|
||||
"name": "bulb",
|
||||
"albedo": [1, 1, 1],
|
||||
"metallic": 1,
|
||||
"roughness": 1,
|
||||
"opacity": 1,
|
||||
"emissive": [(color.red/255) * bloomFactor, (color.green/255) * bloomFactor, (color.blue/255) * bloomFactor],
|
||||
"scattering": 0,
|
||||
"unlit": false,
|
||||
"cullFaceMode": "CULL_NONE",
|
||||
"model": "hifi_pbr"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
materialID = Entities.addEntity({
|
||||
"type": "Material",
|
||||
"parentID": entityID,
|
||||
"renderWithZones": renderWithZones,
|
||||
"locked": false,
|
||||
"localPosition": {"x": 0.0, "y": 0.2, "z": 0.0},
|
||||
"name": "bulb-material",
|
||||
"materialURL": "materialData",
|
||||
"priority": 1,
|
||||
"materialData": JSON.stringify(materialContent)
|
||||
},"local");
|
||||
|
||||
};
|
||||
|
||||
this.unload = function(entityID) {
|
||||
//Termination proces
|
||||
if (lightID !== Uuid.NULL) {
|
||||
Entities.deleteEntity(lightID);
|
||||
}
|
||||
if (materialID !== Uuid.NULL) {
|
||||
Entities.deleteEntity(materialID);
|
||||
}
|
||||
};
|
||||
|
||||
})
|
|
@ -67,7 +67,7 @@ var metadata = { "applications":
|
|||
"isActive": true,
|
||||
"directory": "radar",
|
||||
"name": "Radar",
|
||||
"description": "Show where people are and teleport in the domain. <a href='http://ctrlaltstudio.com/vircadia/radar' target=`_blank`>More info...</a>",
|
||||
"description": "Show where people are and teleport in the domain.",
|
||||
"jsfile": "radar/radar.js",
|
||||
"icon": "radar/assets/radar-i.svg",
|
||||
"caption": "RADAR"
|
||||
|
@ -85,7 +85,7 @@ var metadata = { "applications":
|
|||
"isActive": true,
|
||||
"directory": "nametags",
|
||||
"name": "Nametags",
|
||||
"description": "Display users' display names above their heads. <a href='http://ctrlaltstudio.com/vircadia/nametags' target=`_blank`>More info...</a>",
|
||||
"description": "Display users' display names above their heads.",
|
||||
"jsfile": "nametags/nametags.js",
|
||||
"icon": "nametags/assets/nametags-i.svg",
|
||||
"caption": "NAMETAGS"
|
||||
|
@ -116,6 +116,15 @@ var metadata = { "applications":
|
|||
"jsfile": "odometer/odometer.js",
|
||||
"icon": "odometer/appicon_i.png",
|
||||
"caption": "ODOMETER"
|
||||
},
|
||||
{
|
||||
"isActive": true,
|
||||
"directory": "lightBulb",
|
||||
"name": "Light Bulb Generator",
|
||||
"description": "Generate rapidly a visible glowing light source with its light beam.",
|
||||
"jsfile": "lightBulb/app-lightBulb.js",
|
||||
"icon": "lightBulb/icon_inactive.png",
|
||||
"caption": "BULB"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue