Added 3d closet demo, cleaned up menus.

This commit is contained in:
Michael Mainguy 2024-02-02 15:36:47 -06:00
parent 27167f4e47
commit 2ef5379a3b
47 changed files with 800 additions and 4743 deletions

View File

@ -3,6 +3,9 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1" name="viewport"/> <meta content="width=device-width, initial-scale=1" name="viewport"/>
<link href="/assets/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
<link href="/assets/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
<link href="/assets/favicon-96x96.png" rel="icon" sizes="96x96" type="image/png">
<title>Deep Diagram</title> <title>Deep Diagram</title>
<style> <style>
body { body {
@ -11,6 +14,9 @@
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
aspect-ratio: auto; aspect-ratio: auto;
font-family: Roboto, sans-serif;
font-size: large;
color: #4444ee;
} }
.scene { .scene {
@ -20,100 +26,221 @@
} }
.webButton {
position: fixed;
bottom: 50px;
left: 20px;
background: rgba(51, 51, 51, .7);
display: none;
width: 80px;
height: 50px;
z-index: 10;
}
.webMenu {
display: none;
position: fixed;
bottom: 50px;
left: 20px;
width: 50%;
height: 50%;
background: rgba(51, 51, 51, .9);
color: #fff;
z-index: 9;
}
.webMenu input {
background: rgba(51, 51, 51, .9);
}
.webMenu label, .webMenu input {
color: #fff;
}
.webMenu ul {
list-style-type: none;
padding: 0px;
margin: 0px;
}
.webMenu li label {
display: inline-block;
width: 200px;
}
.webMenu li {
margin: 5px;
}
#gameCanvas { #gameCanvas {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
background: transparent;
} }
#questLaunch {
position: fixed; div.overlay {
top: 30px; position: absolute;
right: 30px;
padding: 10px;
background: #000; background: #000;
color: #fff; left: 50%;
font-size: 1.5em; top: 50%;
z-index: 100; transform: translate(-50%, -50%);
z-index: 12;
width: 320px;
height: 300px;
}
div.overlay div {
background-color: #000000;
color: #FFD700;
padding: 15px 25px;
text-align: center;
text-decoration: none;
cursor: pointer;
border: none;
}
div.overlay div a {
display: inline-block;
text-decoration: none;
border-color: #FFD700;
border-style: solid;
border-width: 1px;
padding: 10px;
width: 200px;
}
div.overlay div a:visited, div.overlay div a:link {
color: white;
}
div.overlay div a:hover {
background-color: #FFD700;
color: #000000;
}
div.overlay div a {
}
div.overlay input {
display: inline-block;
margin: 10px auto;
text-decoration: none;
border-color: #FFD700;
border-style: solid;
border-width: 1px;
padding: 10px;
width: 200px;
}
div#create {
left: 50%;
top: 60%;
transform: translate(-50%, -50%);
z-index: 12;
width: 320px;
height: 240px;
border: 3px inset #FFD700;
display: none;
}
div.overlay div a.cancel {
font-size: small;
font-weight: lighter;
font-style: italic;
background-color: #222211;
color: #EEC755;
}
div.overlay div a.cancel:hover {
background-color: #EEC700;
color: #000000;
} }
#download { #download {
position: fixed; display: none;
z-index: 11;
width: 200px;
height: 20px;
position: absolute;
bottom: 50px;
left: 16px;
padding: 10px;
background: #000;
color: #fff;
} }
#main.mini {
left: 100px;
top: 200px;
width: 160px;
}
#main.mini img, #tutorial img {
width: 160px;
height: 60px;
}
#main.mini div a, #tutorial div a {
width: 80px;
}
#tutorial h1 {
font-size: x-large;
font-weight: bolder;
text-align: center;
color: #F9F9E9;
}
#tutorial {
z-index: 15;
left: 100px;
top: 560px;
width: 160px;
height: 210px;
}
#closekey, #closekey a:active, #closekey a:visited, #closekey a:link {
position: relative;
color: #ffffff;
}
#loadingGrid {
z-index: -1;
width: 100%;
height: 100%;
}
</style> </style>
<link as="script" href="/newRelic.js" rel="preload"> <link as="script" href="/newRelic.js" rel="preload">
<script src="/newRelic.js"></script> <script src="/newRelic.js"></script>
<meta content="width=device-width, initial-scale=1, height=device-height" name="viewport"> <meta content="width=device-width, initial-scale=1, height=device-height" name="viewport">
<meta content="An immersive vr diagramming experience based on a-frame and webxr" name="description"> <meta content="An immersive vr diagramming experience based using webxr" name="description">
<link href="/assets/favicon-16x16.png" rel=icon sizes="16x16" type="image/png">
<link href="/assets/favicon-32x32.png" rel=icon sizes="32x32" type="image/png">
<link href="/assets/favicon-96x96.png" rel=icon sizes="96x96" type="image/png">
<link href="/manifest.webmanifest" rel="manifest"/> <link href="/manifest.webmanifest" rel="manifest"/>
<script src='/niceware.js'></script> <!--<script src='/niceware.js'></script>-->
</head> </head>
<body> <body>
<div class="overlay" id="tutorial">
<h1>Help</h1>
<div id="desktopTutorial"><a href="#" id="desktopLink">Desktop</a></div>
<div id="questTutorial"><a href="#" id="questLink">Quest</a></div>
</div>
<div id="download"><a href="#" id="downloadLink">Download Model</a></div> <div class="overlay mini" id="main">
<img height="120" src="/assets/ddd.svg" width="320">
<div id="startCreate"><a href="#" id="startCreateLink">Start</a></div>
<div id="download"><a href="#" id="downloadLink">Download Model</a></div>
</div>
<div class="overlay" id="create">
<div><input id="createName" placeholder="Enter a name for your diagram" type="text"></div>
<div><input id="createPassword" placeholder="Enter a password (optional)" type="text"></div>
<div><a href="#" id="createActionLink">Create</a></div>
<div><a class="cancel" href="#" id="cancelCreateLink">Cancel</a></div>
</div>
<div class="overlay" id="keyboardHelp">
<div id="closekey"><a href="#">X</a></div>
<img height="240" src="/assets/textures/keyboardhelp2.jpg" width="480">
<img height="240" src="/assets/textures/mousehelp.jpg" width="180">
</div>
<img id="loadingGrid" src="/assets/grid3.jpg"/>
<script> <script>
const create = document.querySelector('#startCreateLink');
if (create) {
create.addEventListener('click', function (evt) {
evt.preventDefault();
document.querySelector('#main').style.display = 'none';
document.querySelector('#create').style.display = 'block';
});
}
const cancel = document.querySelector('#cancelCreateLink');
if (cancel) {
cancel.addEventListener('click', function (evt) {
evt.preventDefault();
document.querySelector('#main').style.display = 'block';
document.querySelector('#create').style.display = 'none';
});
}
const close = document.querySelector('#closekey a');
if (close) {
close.addEventListener('click', function (evt) {
evt.preventDefault();
document.querySelector('#keyboardHelp').style.display = 'none';
});
}
const desktopTutorial = document.querySelector('#desktopLink');
if (desktopTutorial) {
desktopTutorial.addEventListener('click', function (evt) {
evt.preventDefault();
// document.querySelector('#tutorial').style.display = 'none';
document.querySelector('#keyboardHelp').style.display = 'block';
});
}
const createAction = document.querySelector('#createActionLink');
if (createAction) {
createAction.addEventListener('click', function (evt) {
evt.preventDefault();
const value = document.querySelector('#createName').value;
if (value && value.length > 4) {
document.location.href = '/db/' + value;
} else {
window.alert('Name must be longer than 4 characters');
}
});
}
/* /*
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
var SpeechGrammarList = SpeechGrammarList || window.webkitSpeechGrammarList var SpeechGrammarList = SpeechGrammarList || window.webkitSpeechGrammarList
@ -141,42 +268,27 @@
<div class="scene"> <div class="scene">
<canvas id="gameCanvas"></canvas> <canvas id="gameCanvas"></canvas>
</div> </div>
<div class="webButton">
<button id="menuButton">Open Menu</button>
</div>
<div class="webMenu">
<ul>
<li>
<label for="newRelicAccount">New Relic Account</label>
<input id="newRelicAccount" type="text" value="">
</li>
<li>
<label for="newRelicClient">New Relic Client</label>
<input id="newRelicClient" type="text" value="">
</li>
</ul>
<button id="webButton">Save</button>
</div>
<script>
const button = document.querySelector('#menuButton');
if (button) {
button.addEventListener('click', (e) => {
const menu = document.querySelector('.webMenu');
//const button = document.querySelector('#webButton');
if (menu.style.display === 'block') {
menu.style.display = 'none';
button.innerText = "Open Menu";
} else {
menu.style.display = 'block';
button.innerText = "Close Menu";
}
});
}
</script>
</body> </body>
<style>
#keyboardHelp {
display: none;
width: 665px;
height: 312px;
}
#keyboardHelp .button {
background-color: white;
width: 16px;
height: 16px;
display: inline-block;
text-align: center;
color: #000000;
}
#keyboardHelp div {
background: transparent;
}
</style>
</html> </html>

View File

@ -28,8 +28,6 @@
"events": "^3.3.0", "events": "^3.3.0",
"@typed-mxgraph/typed-mxgraph": "^1.0.8", "@typed-mxgraph/typed-mxgraph": "^1.0.8",
"@types/node": "^18.14.0", "@types/node": "^18.14.0",
"dexie": "^3.2.4",
"dexie-observable": "^4.0.1-beta.13",
"earcut": "^2.2.4", "earcut": "^2.2.4",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"@types/file-saver": "^2.0.6", "@types/file-saver": "^2.0.6",
@ -37,15 +35,11 @@
"loglevel": "^1.8.1", "loglevel": "^1.8.1",
"mxgraph": "^4.2.2", "mxgraph": "^4.2.2",
"niceware": "^4.0.0", "niceware": "^4.0.0",
"p2p-data-channel": "^1.10.7",
"pouchdb": "^8.0.1", "pouchdb": "^8.0.1",
"pouchdb-find": "^7.2.2", "pouchdb-find": "^7.2.2",
"query-string": "^8.1.0", "query-string": "^8.1.0",
"recordrtc": "^5.6.2",
"ring-client-api": "11.7.7",
"round": "^2.0.1", "round": "^2.0.1",
"uuid": "^9.0.0", "uuid": "^9.0.0"
"material-components-web": "^11.0.0"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^4.9.5", "typescript": "^4.9.5",

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

BIN
public/assets/Android.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

43
public/assets/ddd.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/assets/grid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
public/assets/grid2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 KiB

BIN
public/assets/grid3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 KiB

BIN
public/assets/iPhone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -1,63 +0,0 @@
{
"name": "Chart 1",
"series": [
{
"name": "series 1",
"values": [
{
"id": "1",
"start": "2015-01-01",
"end": "2015-12-31",
"value": 1
},
{
"id": "2",
"start": "2016-01-01",
"end": "2016-12-31",
"value": 2
},
{
"id": "3",
"start": "2017-01-01",
"end": "2017-12-31",
"value": 3
},
{
"id": "4",
"start": "2018-01-01",
"end": "2018-12-31",
"value": 4
}
]
},
{
"name": "series 2",
"values": [
{
"id": "1",
"start": "2015-01-01",
"end": "2015-12-31",
"value": 5
},
{
"id": "2",
"start": "2016-01-01",
"end": "2016-12-31",
"value": 6
},
{
"id": "3",
"start": "2017-01-01",
"end": "2017-12-31",
"value": 7
},
{
"id": "4",
"start": "2018-01-01",
"end": "2018-12-31",
"value": 8
}
]
}
]
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -9,14 +9,14 @@
"description": "Immersive diagraming tool to dig into deeper meaning behind your ideas", "description": "Immersive diagraming tool to dig into deeper meaning behind your ideas",
"icons": [ "icons": [
{ {
"src": "/assets/android-icon-192x192.png", "src": "/Android.png",
"sizes": "192x192", "sizes": "196x196",
"type": "image/png", "type": "image/png",
"purpose": "any" "purpose": "any"
}, },
{ {
"src": "/assets/icon-512x512.png", "src": "/iPhone.png",
"sizes": "512x512", "sizes": "180x180",
"type": "image/png" "type": "image/png"
} }
], ],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 KiB

View File

@ -1,10 +1,6 @@
const VERSION = '0'; const VERSION = '0';
const CACHE = "pwabuilder-offline"; const CACHE = "pwabuilder-offline";
const PRECACHE_ASSETS = [ const PRECACHE_ASSETS = []
'/grass1.jpeg',
'/loading-loading-forever.gif',
'/outdoor_field2.jpeg'
]
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js'); importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js');
self.addEventListener("message", (event) => { self.addEventListener("message", (event) => {

View File

@ -7,56 +7,60 @@ import {
PhysicsShapeType, PhysicsShapeType,
Scene, Scene,
TransformNode, TransformNode,
Vector3 Vector3,
WebXRDefaultExperience
} from "@babylonjs/core"; } from "@babylonjs/core";
import {buildStandardMaterial} from "../../materials/functions/buildStandardMaterial"; import {buildStandardMaterial} from "../../materials/functions/buildStandardMaterial";
export function buildRig(scene: Scene): Mesh { export function buildRig(scene: Scene, xr: WebXRDefaultExperience): Mesh {
const rigMesh = MeshBuilder.CreateCylinder("platform", {diameter: .5, height: 1.6}, scene); const rigMesh = MeshBuilder.CreateCylinder("platform", {diameter: .5, height: .2}, scene);
const cameratransform = new TransformNode("cameraTransform", scene); const cameratransform = new TransformNode("cameraTransform", scene);
cameratransform.parent = rigMesh; cameratransform.parent = rigMesh;
cameratransform.position = new Vector3(0, -.8, 0); xr.baseExperience.onInitialXRPoseSetObservable.add((state) => {
xr.baseExperience.camera.parent = cameratransform;
xr.baseExperience.camera.position = new Vector3(0, 0, 0);
});
for (const cam of scene.cameras) { for (const cam of scene.cameras) {
cam.parent = cameratransform; cam.parent = cameratransform;
console.log(cam.absoluteRotation); if (cam.getClassName() == "FreeCamera") {
//cameratransform.position = new Vector3(0, 1.6, 0);
//cam.position.set(0, 1.6, 0);
} else {
//cameratransform.position = new Vector3(0, 1.6, 0);
//cam.position.set(0, 0, 0);
}
} }
scene.onActiveCameraChanged.add(() => {
scene.onActiveCameraChanged.add((s) => { for (const cam of scene.cameras) {
cam.parent = cameratransform;
if (cam.getClassName() == "FreeCamera") {
//cameratransform.position = new Vector3(0, 1.6, 0);
//cam.position.set(0, 1.6, 0);
} else {
//cameratransform.position = new Vector3(0, 0, 0);
//cam.position.set(0, 0, 0);
}
}
cameratransform.rotation.set(0, Math.PI, 0); cameratransform.rotation.set(0, Math.PI, 0);
s.activeCamera.parent = cameratransform; //s.activeCamera.parent = cameratransform;
}); });
rigMesh.material = buildStandardMaterial("rigMaterial", scene, "#2222ff"); rigMesh.material = buildStandardMaterial("rigMaterial", scene, "#2222ff");
rigMesh.setAbsolutePosition(new Vector3(0, .01, 3)); rigMesh.setAbsolutePosition(new Vector3(0, .01, 3));
const home = new AxesViewer(scene, .5); rigMesh.isPickable = false;
new AxesViewer(scene, .25);
rigMesh.lookAt(new Vector3(0, 0.01, 0)); rigMesh.lookAt(new Vector3(0, 0.01, 0));
rigMesh.visibility = 0; rigMesh.visibility = 1;
const rigAggregate = const rigAggregate =
new PhysicsAggregate( new PhysicsAggregate(
rigMesh, rigMesh,
PhysicsShapeType.CYLINDER, PhysicsShapeType.CYLINDER,
{friction: 0, center: Vector3.Zero(), mass: 50, restitution: .1}, {friction: 0, center: Vector3.Zero(), mass: 50, restitution: .01},
scene); scene);
/*const rightFoot = MeshBuilder.CreateBox("rightFoot", {width: .1, height: .1, depth: .2}, scene);
const rightFootAggregate =
new PhysicsAggregate(
rightFoot,
PhysicsShapeType.BOX,
{ friction: 0, center: Vector3.Zero(), radius: .2, pointA: new Vector3(0, 0, 0),
pointB: new Vector3(0, 1.5, 0), mass: 50, restitution: .1},
scene);
rightFootAggregate.body.setMotionType(PhysicsMotionType.ANIMATED);
rightFoot.parent= rigAggregate.transformNode;
rightFoot.material = rigMaterial;
rightFoot.position.y=.05;
rightFoot.position.x=.2;
rightFoot.position.z= 2;
*/
rigAggregate.body.setMotionType(PhysicsMotionType.DYNAMIC); rigAggregate.body.setMotionType(PhysicsMotionType.DYNAMIC);
return rigMesh; return rigMesh;
} }

View File

@ -31,7 +31,7 @@ export class Rigplatform {
this.diagramManager = diagramManager; this.diagramManager = diagramManager;
this.controllers = controllers; this.controllers = controllers;
this.xr = xr; this.xr = xr;
this.rigMesh = buildRig(scene); this.rigMesh = buildRig(scene, xr);
this.fixRotation(); this.fixRotation();
this.initializeControllers(); this.initializeControllers();
@ -163,7 +163,7 @@ export class Rigplatform {
} }
break; break;
} }
this.xr.baseExperience.camera.position = new Vector3(0, 1.6, 0); //this.xr.baseExperience.camera.position = new Vector3(0, 0, 0);
if (controller) { if (controller) {
controller.setRig(this); controller.setRig(this);
} }

View File

@ -1,4 +1,4 @@
import {AbstractMesh, MeshBuilder, Scene} from "@babylonjs/core"; import {AbstractMesh, KeyboardEventTypes, MeshBuilder, Scene} from "@babylonjs/core";
import {Rigplatform} from "./rigplatform"; import {Rigplatform} from "./rigplatform";
import {ControllerEventType, Controllers} from "./controllers"; import {ControllerEventType, Controllers} from "./controllers";
import {DiagramManager} from "../diagram/diagramManager"; import {DiagramManager} from "../diagram/diagramManager";
@ -40,20 +40,38 @@ export class WebController {
this.scene.onKeyboardObservable.add((kbInfo) => { this.scene.onKeyboardObservable.add((kbInfo) => {
this.logger.debug(kbInfo); this.logger.debug(kbInfo);
const canvas = document.querySelector('#gameCanvas');
if (canvas && kbInfo.event.target != canvas) {
return;
}
if (kbInfo.type == KeyboardEventTypes.KEYUP) {
this.rig.turn(0);
}
if (kbInfo.type == 1) { if (kbInfo.type == 1) {
switch (kbInfo.event.key) { switch (kbInfo.event.key) {
case "ArrowUp": case "ArrowUp":
case "w":
this.rig.forwardback(-this.speed); this.rig.forwardback(-this.speed);
break; break;
case "ArrowDown": case "ArrowDown":
case "s":
this.rig.forwardback(this.speed); this.rig.forwardback(this.speed);
break; break;
case "ArrowLeft": case "ArrowLeft":
case "a":
this.rig.leftright(-this.speed); this.rig.leftright(-this.speed);
break; break;
case "A":
this.rig.turn(-this.speed);
break;
case "ArrowRight": case "ArrowRight":
case "d":
this.rig.leftright(this.speed); this.rig.leftright(this.speed);
break; break;
case "D":
this.rig.turn(this.speed);
break;
case "]": case "]":
this.speed *= 1.5; this.speed *= 1.5;
break; break;
@ -70,6 +88,7 @@ export class WebController {
} }
break; break;
default: default:
this.logger.debug(kbInfo.event); this.logger.debug(kbInfo.event);
} }

View File

@ -1,7 +1,7 @@
import {DiagramEntity, DiagramEntityType} from "../types/diagramEntity"; import {DiagramEntity, DiagramEntityType} from "../types/diagramEntity";
import {AbstractMesh, InstancedMesh, Mesh, Quaternion, Scene, Vector3} from "@babylonjs/core"; import {AbstractMesh, InstancedMesh, Mesh, Quaternion, Scene, Vector3} from "@babylonjs/core";
import {DiagramConnection} from "../diagramConnection"; import {DiagramConnection} from "../diagramConnection";
import {TextLabel} from "../../objects/textLabel"; import {updateTextNode} from "../../util/functions/updateTextNode";
import log from "loglevel"; import log from "loglevel";
import {v4 as uuidv4} from 'uuid'; import {v4 as uuidv4} from 'uuid';
import {buildStandardMaterial} from "../../materials/functions/buildStandardMaterial"; import {buildStandardMaterial} from "../../materials/functions/buildStandardMaterial";
@ -80,7 +80,7 @@ function mapMetadata(entity: DiagramEntity, newMesh: AbstractMesh, scene: Scene)
} }
if (entity.text) { if (entity.text) {
newMesh.metadata.text = entity.text; newMesh.metadata.text = entity.text;
TextLabel.updateTextNode(newMesh, entity.text); updateTextNode(newMesh, entity.text);
} }
if (entity.from) { if (entity.from) {
newMesh.metadata.from = entity.from; newMesh.metadata.from = entity.from;

View File

@ -2,7 +2,7 @@ import {DiagramEvent, DiagramEventType} from "../types/diagramEntity";
import log from "loglevel"; import log from "loglevel";
import {applyPhysics} from "./diagramShapePhysics"; import {applyPhysics} from "./diagramShapePhysics";
import {ActionManager, PhysicsMotionType, Scene} from "@babylonjs/core"; import {ActionManager, PhysicsMotionType, Scene} from "@babylonjs/core";
import {TextLabel} from "../../objects/textLabel"; import {updateTextNode} from "../../util/functions/updateTextNode";
import {Toolbox} from "../../toolbox/toolbox"; import {Toolbox} from "../../toolbox/toolbox";
import {DiaSounds} from "../../util/diaSounds"; import {DiaSounds} from "../../util/diaSounds";
@ -51,7 +51,7 @@ export function diagramEventHandler(event: DiagramEvent,
break; break;
case DiagramEventType.DROP: case DiagramEventType.DROP:
if (isDiagramEntity(mesh) && (mesh.metadata.template.indexOf('#') > -1)) { if (isDiagramEntity(mesh) && (mesh.metadata.template.indexOf('#') > -1)) {
TextLabel.updateTextNode(mesh, entity.text); updateTextNode(mesh, entity.text);
} }
break; break;
case DiagramEventType.ADD: case DiagramEventType.ADD:

View File

@ -271,9 +271,8 @@ export class PouchdbPersistenceManager {
private async beginSync(remoteDbName: string) { private async beginSync(remoteDbName: string) {
try { try {
//const remoteDbName = "db1"; //const remoteDbName = "db1";
const remoteUserName = "user1"; const remoteUserName = remoteDbName;
const password = "password"; const password = "password";
const dbs = await axios.get(import.meta.env.VITE_SYNCDB_ENDPOINT + '_all_dbs'); const dbs = await axios.get(import.meta.env.VITE_SYNCDB_ENDPOINT + '_all_dbs');
if (dbs.data.indexOf(remoteDbName) == -1) { if (dbs.data.indexOf(remoteDbName) == -1) {

View File

@ -18,7 +18,7 @@ import {DiagramEvent, DiagramEventType} from "../diagram/types/diagramEntity";
import log from "loglevel"; import log from "loglevel";
import {InputTextView} from "../information/inputTextView"; import {InputTextView} from "../information/inputTextView";
import {DiaSounds} from "../util/diaSounds"; import {DiaSounds} from "../util/diaSounds";
import {TextLabel} from "../objects/textLabel"; import {updateTextNode} from "../util/functions/updateTextNode";
import {DiagramConnection} from "../diagram/diagramConnection"; import {DiagramConnection} from "../diagram/diagramConnection";
import {toDiagramEntity} from "../diagram/functions/toDiagramEntity"; import {toDiagramEntity} from "../diagram/functions/toDiagramEntity";
@ -29,6 +29,7 @@ import {SoccerMenu} from "../soccer/soccerMenu";
import {CameraMenu} from "./cameraMenu"; import {CameraMenu} from "./cameraMenu";
import {exportGltf} from "../util/functions/exportGltf"; import {exportGltf} from "../util/functions/exportGltf";
import {isDiagramEntity} from "../diagram/functions/isDiagramEntity"; import {isDiagramEntity} from "../diagram/functions/isDiagramEntity";
import {ScaleMenu} from "./scaleMenu";
export class EditMenu extends AbstractMenu { export class EditMenu extends AbstractMenu {
private state: EditMenuState = EditMenuState.NONE; private state: EditMenuState = EditMenuState.NONE;
@ -161,8 +162,10 @@ export class EditMenu extends AbstractMenu {
this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1); this.diagramManager.onDiagramEventObservable.notifyObservers(event, -1);
} }
private scaleMenu: ScaleMenu;
constructor(scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager, controllers: Controllers) { constructor(scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager, controllers: Controllers) {
super(scene, xr, controllers); super(scene, xr, controllers);
//this.scaleMenu = new ScaleMenu(this.scene, this.xr, this.controllers);
this.sounds = new DiaSounds(scene); this.sounds = new DiaSounds(scene);
this.diagramManager = diagramManager; this.diagramManager = diagramManager;
this.gizmoManager = new GizmoManager(scene); this.gizmoManager = new GizmoManager(scene);
@ -177,16 +180,16 @@ export class EditMenu extends AbstractMenu {
panel.columns = 4; panel.columns = 4;
this.manager.addControl(panel); this.manager.addControl(panel);
panel.addControl(this.makeButton("Cameras", "camera")); //panel.addControl(this.makeButton("Cameras", "camera"));
panel.addControl(this.makeButton("Modify", "modify")); //panel.addControl(this.makeButton("Modify", "modify"));
panel.addControl(this.makeButton("Remove", "remove")); panel.addControl(this.makeButton("Remove", "remove"));
panel.addControl(this.makeButton("Add Label", "label")); panel.addControl(this.makeButton("Label", "label"));
panel.addControl(this.makeButton("Copy", "copy")); panel.addControl(this.makeButton("Copy", "copy"));
panel.addControl(this.makeButton("Connect", "connect")); panel.addControl(this.makeButton("Connect", "connect"));
panel.addControl(this.makeButton("Export GLTF", "exportgltf")); //panel.addControl(this.makeButton("Export GLTF", "exportgltf"));
panel.addControl(this.makeButton("Recolor", "recolor")); //panel.addControl(this.makeButton("Recolor", "recolor"));
panel.addControl(this.makeButton("New Relic", "newrelic")); //panel.addControl(this.makeButton("New Relic", "newrelic"));
panel.addControl(this.makeButton("Soccer", "soccer")); //panel.addControl(this.makeButton("Soccer", "soccer"));
//panel.addControl(this.makeButton("Add Ring Cameras", "addRingCameras")); //panel.addControl(this.makeButton("Add Ring Cameras", "addRingCameras"));
this.manager.controlScaling = .1; this.manager.controlScaling = .1;
this.scene.onPointerObservable.add((pointerInfo) => { this.scene.onPointerObservable.add((pointerInfo) => {
@ -240,7 +243,7 @@ export class EditMenu extends AbstractMenu {
textInput.show(); textInput.show();
textInput.onTextObservable.addOnce((value) => { textInput.onTextObservable.addOnce((value) => {
this.persist(mesh, value.text); this.persist(mesh, value.text);
TextLabel.updateTextNode(mesh, value.text); updateTextNode(mesh, value.text);
}); });
@ -267,19 +270,7 @@ export class EditMenu extends AbstractMenu {
private modifyMesh(mesh: AbstractMesh) { private modifyMesh(mesh: AbstractMesh) {
if (isDiagramEntity(mesh) && if (isDiagramEntity(mesh) &&
mesh.parent?.parent?.id != "toolbox") { mesh.parent?.parent?.id != "toolbox") {
if (this.gizmoManager.gizmos.boundingBoxGizmo.attachedMesh?.id == mesh.id) { this.scaleMenu.changeMesh(mesh);
this.gizmoManager.gizmos.boundingBoxGizmo.attachedMesh = null;
} else {
this.gizmoManager.attachToMesh(mesh);
this.gizmoManager.gizmos.boundingBoxGizmo.onScaleBoxDragObservable.add(() => {
this.diagramManager.onDiagramEventObservable.notifyObservers({
type: DiagramEventType.MODIFY,
entity: toDiagramEntity(mesh),
}, -1
)
this.logger.debug(mesh.scaling);
});
}
} }
} }

108
src/menus/scaleMenu.ts Normal file
View File

@ -0,0 +1,108 @@
import {AbstractMesh, Scene, TransformNode, Vector3, WebXRDefaultExperience} from "@babylonjs/core";
import {Controllers} from "../controllers/controllers";
import {DiaSounds} from "../util/diaSounds";
import {AbstractMenu} from "./abstractMenu";
import {GUI3DManager, Slider3D} from "@babylonjs/gui";
export class ScaleMenu extends AbstractMenu {
private sounds: DiaSounds;
private mesh: AbstractMesh;
private xSlider: Slider3D;
private ySlider: Slider3D;
private zSlider: Slider3D;
private transformNode: TransformNode;
private xTransformNode: TransformNode;
private yTransformNode: TransformNode;
private zTransformNode: TransformNode;
constructor(scene: Scene, xr: WebXRDefaultExperience, controllers: Controllers) {
super(scene, xr, controllers);
this.sounds = new DiaSounds(scene);
this.transformNode = new TransformNode("scaleMenu", scene);
this.xTransformNode = new TransformNode("xTransformNode", scene);
this.xTransformNode.parent = this.transformNode;
this.yTransformNode = new TransformNode("yTransformNode", scene);
this.yTransformNode.parent = this.transformNode;
this.zTransformNode = new TransformNode("zTransformNode", scene);
this.zTransformNode.parent = this.transformNode;
//super.createHandle(this.transformNode);
this.transformNode.position.y = 0;
this.transformNode.position.z = 0;
this.transformNode.position.x = 0;
this.buildMenu();
//this.transformNode.position.y = 2;
}
public changeMesh(mesh: AbstractMesh) {
this.mesh = mesh;
this.xSlider.value = mesh.scaling.x;
this.ySlider.value = mesh.scaling.y;
this.zSlider.value = mesh.scaling.z;
const two = new Vector3(2, 2, 2);
this.transformNode.position = this.mesh.absolutePosition.clone();
this.transformNode.rotation = this.mesh.absoluteRotationQuaternion.toEulerAngles();
}
private buildMenu() {
const manager = new GUI3DManager(this.scene);
//manager.rootContainer.position.y = 2;
//manager.rootContainer.node.position.y = 2;
this.xSlider = new Slider3D("xslider");
this.ySlider = new Slider3D("xslider");
this.zSlider = new Slider3D("xslider");
manager.addControl(this.xSlider);
manager.addControl(this.ySlider);
manager.addControl(this.zSlider);
this.xSlider.linkToTransformNode(this.xTransformNode);
this.ySlider.linkToTransformNode(this.yTransformNode);
this.zSlider.linkToTransformNode(this.zTransformNode);
this.xTransformNode.position = new Vector3(0, 0, .6);
this.xTransformNode.rotation.y = Math.PI;
this.yTransformNode.position = new Vector3(.6, .6, .6);
this.yTransformNode.rotation.z = Math.PI / 2;
this.zTransformNode.position = new Vector3(.6, .6, 0);
this.zTransformNode.rotation.y = Math.PI / 2;
setValues(this.xSlider);
setValues(this.ySlider);
setValues(this.zSlider);
this.xSlider.onValueChangedObservable.add((value) => {
if (this.mesh) {
this.mesh.scaling.x = value;
}
});
this.ySlider.onValueChangedObservable.add((value) => {
if (this.mesh) {
this.mesh.scaling.y = value;
}
});
this.zSlider.onValueChangedObservable.add((value) => {
if (this.mesh) {
this.mesh.scaling.z = value;
}
});
this.transformNode.scaling.x = .5;
this.transformNode.scaling.y = .5;
this.transformNode.scaling.z = .5;
}
}
function setValues(slider: Slider3D) {
slider.minimum = .1;
slider.maximum = 1;
slider.step = .1;
slider.value = .1;
}

View File

@ -1,75 +0,0 @@
import {AbstractMesh, DynamicTexture, Material, MeshBuilder, StandardMaterial} from "@babylonjs/core";
import log from "loglevel";
export class TextLabel {
private static logger: log.Logger = log.getLogger('TextLabel');
public static updateTextNode(mesh: AbstractMesh, text: string) {
if (!mesh) {
this.logger.error("updateTextNode: mesh is null");
return null;
}
const textNodes = mesh.getChildren((node) => {
return node.metadata?.label == true;
});
if (textNodes && textNodes.length > 0) {
textNodes.forEach((node) => {
node.dispose(false, true);
});
}
if (!text) {
return null;
}
//Set font
const height = 0.05;
const font_size = 24;
const font = "bold " + font_size + "px Arial";
//Set height for dynamic texture
const DTHeight = 1.5 * font_size; //or set as wished
//Calc Ratio
const ratio = height / DTHeight;
//Use a temporary dynamic texture to calculate the length of the text on the dynamic texture canvas
const temp = new DynamicTexture("DynamicTexture", 32, mesh.getScene());
const tmpctx = temp.getContext();
tmpctx.font = font;
const DTWidth = tmpctx.measureText(text).width + 8;
//Calculate width the plane has to be
const planeWidth = DTWidth * ratio;
//Create dynamic texture and write the text
const dynamicTexture = new DynamicTexture("DynamicTexture", {
width: DTWidth,
height: DTHeight
}, mesh.getScene(), false);
const mat = new StandardMaterial("mat", mesh.getScene());
mat.diffuseTexture = dynamicTexture;
//mat.emissiveColor = Color3.White();
dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
//Create plane and set dynamic texture as material
//const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene());
const plane1 = this.createPlane(mat, mesh, text, planeWidth, height);
const plane2 = this.createPlane(mat, mesh, text, planeWidth, height);
plane2.rotation.y = Math.PI;
}
private static createPlane(mat: Material, mesh: AbstractMesh, text: string, planeWidth: number, height: number): AbstractMesh {
const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene());
plane.material = mat;
//plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
plane.metadata = {exportable: true, label: true};
const yOffset = mesh.getBoundingInfo().boundingSphere.maximum.y;
plane.parent = mesh;
plane.scaling.y = (1 / mesh.scaling.y);
plane.scaling.x = (1 / mesh.scaling.x);
plane.scaling.z = (1 / mesh.scaling.z);
plane.position.y = yOffset + (height * plane.scaling.y);
return plane;
}
}

View File

@ -45,7 +45,7 @@ export class Ball {
private buildBall() { private buildBall() {
SceneLoader.ImportMesh(null, "/assets/models/", "ball.gltf", this.scene, SceneLoader.ImportMesh(null, "/assets/models/", "ball.gltf", this.scene,
(meshes, particleSystems, skeletons, animationGroups) => { (meshes) => {
this.logger.debug('ball loaded'); this.logger.debug('ball loaded');
this.mesh = meshes[0]; this.mesh = meshes[0];
this.parent = MeshBuilder.CreateSphere("ballParent", {diameter: .17}, this.scene); this.parent = MeshBuilder.CreateSphere("ballParent", {diameter: .17}, this.scene);

View File

@ -11,6 +11,10 @@ export function buildColor(color: Color3, scene: Scene, parent: TransformNode, i
//const material = new PBRMaterial("material-" + color.toHexString(), scene); //const material = new PBRMaterial("material-" + color.toHexString(), scene);
const material = new StandardMaterial("material-" + color.toHexString(), scene); const material = new StandardMaterial("material-" + color.toHexString(), scene);
material.diffuseColor = color; material.diffuseColor = color;
material.roughness = 1;
material.specularPower = 1;
//material.emissiveColor = color;
//const material = new StandardMaterial("material-" + color.toHexString(), scene); //const material = new StandardMaterial("material-" + color.toHexString(), scene);
//material.albedoColor = color; //material.albedoColor = color;
//material.metallic = 1; //material.metallic = 1;

View File

@ -7,16 +7,19 @@ export function buildMesh(type: ToolType, toolname: string, scene: Scene): Mesh
return MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}, scene); return MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}, scene);
case ToolType.SPHERE: case ToolType.SPHERE:
return MeshBuilder.CreateSphere(toolname, {diameter: 1}, scene); return MeshBuilder.CreateIcoSphere(toolname, {subdivisions: 5, radius: .5}, scene);
//return MeshBuilder.CreateSphere(toolname, {diameter: 1}, scene);
case ToolType.CYLINDER: case ToolType.CYLINDER:
return MeshBuilder.CreateCylinder(toolname, {height: 1, diameter: 1}, scene); return MeshBuilder.CreateCylinder(toolname, {height: 1, diameter: 1, subdivisions: 1, tessellation: 12}, scene);
case ToolType.CONE: case ToolType.CONE:
return MeshBuilder.CreateCylinder(toolname, { return MeshBuilder.CreateCylinder(toolname, {
diameterTop: 0, diameterTop: 0,
subdivisions: 1,
height: 1, height: 1,
diameterBottom: 1 diameterBottom: 1,
tessellation: 12
}, scene); }, scene);
case ToolType.PLANE: case ToolType.PLANE:

View File

@ -17,7 +17,6 @@ import {AppConfig} from "../util/appConfig";
import Hls from "hls.js"; import Hls from "hls.js";
import log, {Logger} from "loglevel"; import log, {Logger} from "loglevel";
export class Introduction { export class Introduction {
private readonly scene: Scene; private readonly scene: Scene;
private manager: GUI3DManager; private manager: GUI3DManager;

24
src/tutorial/tutorial.ts Normal file
View File

@ -0,0 +1,24 @@
import {AppConfig} from "../util/appConfig";
import {Scene} from "@babylonjs/core";
export class Tutorial {
private scene: Scene;
private config: AppConfig;
constructor(scene: Scene, config: AppConfig) {
this.scene = scene;
this.config = config;
const advance = document.querySelector('#advanceLink');
if (advance) {
advance.addEventListener('click', () => {
this.advance();
});
}
console.log('Tutorial');
}
private advance() {
window.alert('here');
}
}

View File

@ -1,20 +1,24 @@
import { import {
Color3,
GroundMesh, GroundMesh,
HemisphericLight, HemisphericLight,
Material,
MeshBuilder, MeshBuilder,
Observable, Observable,
PBRMaterial, PBRMaterial,
PhotoDome,
PhysicsAggregate, PhysicsAggregate,
PhysicsShapeType, PhysicsShapeType,
PointsCloudSystem,
Scene, Scene,
Sound, Sound,
Texture, Texture,
TransformNode,
Vector3 Vector3
} from "@babylonjs/core"; } from "@babylonjs/core";
import {CustomPhysics} from "./customPhysics"; import {CustomPhysics} from "./customPhysics";
import {DiaSounds} from "./diaSounds"; import {DiaSounds} from "./diaSounds";
import {AppConfig} from "./appConfig"; import {AppConfig} from "./appConfig";
import {GridMaterial} from "@babylonjs/materials";
export class CustomEnvironment { export class CustomEnvironment {
@ -25,8 +29,13 @@ export class CustomEnvironment {
constructor(scene: Scene, name: string = "default", config: AppConfig) { constructor(scene: Scene, name: string = "default", config: AppConfig) {
this.scene = scene; this.scene = scene;
this.name = name; this.name = name;
new HemisphericLight("light1", new Vector3(1, 1, 0), scene); const loading = document.querySelector('#loadingGrid');
new HemisphericLight("light2", new Vector3(-1, 1, 0), scene); if (loading) {
loading.remove();
}
const light = new HemisphericLight("light1", new Vector3(.1, 1, 0), scene);
light.groundColor = new Color3(.1, .1, .1)
light.intensity = .6;
const physics = new CustomPhysics(this.scene, config); const physics = new CustomPhysics(this.scene, config);
physics physics
@ -35,15 +44,11 @@ export class CustomEnvironment {
const ground = this.createGround(); const ground = this.createGround();
this._groundMeshObservable.notifyObservers(ground); this._groundMeshObservable.notifyObservers(ground);
}); });
}
private initSounds() {
const photo = new PhotoDome('sky',
'/assets/textures/outdoor_field4.jpeg', {},
scene);
try { try {
const sounds = new DiaSounds(scene); const sounds = new DiaSounds(this.scene);
window.setTimeout((sound) => { window.setTimeout((sound) => {
sound.play() sound.play()
}, 2000, sounds.background); }, 2000, sounds.background);
@ -72,20 +77,97 @@ export class CustomEnvironment {
} catch (error) { } catch (error) {
} }
} }
public get groundMeshObservable() { public get groundMeshObservable() {
return this._groundMeshObservable; return this._groundMeshObservable;
} }
private createGround() { private createGround() {
const scene = this.scene; const scene = this.scene;
const ground: GroundMesh = MeshBuilder.CreateGround("ground", {
width: 20,
height: 20,
subdivisions: 1
}, scene);
createPoints(scene, 20, 20);
ground.material = createGridMaterial(scene, Color3.FromHexString("#aaffaa"), Color3.FromHexString("#111511"));
const color1 = Color3.FromHexString("#ff9999");
const color2 = Color3.FromHexString("#221111");
const color3 = Color3.FromHexString("#9999ff");
const color4 = Color3.FromHexString("#111115");
this.createWall(new Vector3(0, 10, 10), new Vector3(0, 0, 0), color3, color4);
this.createWall(new Vector3(0, 10, -10), new Vector3(0, Math.PI, 0), color3, color4);
this.createWall(new Vector3(10, 10, 0), new Vector3(0, Math.PI / 2, 0), color1, color2);
this.createWall(new Vector3(-10, 10, 0), new Vector3(0, -Math.PI / 2, 0), color1, color2);
new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, scene);
//buildAvatar(scene);
return ground;
}
private createWall(position: Vector3, rotation: Vector3, color1: Color3, color2: Color3) {
const scene = this.scene;
const wall = MeshBuilder.CreatePlane("wall", {width: 20, height: 20}, scene);
wall.position = position;
wall.rotation = rotation;
wall.material = createGridMaterial(scene, color1, color2);
return wall;
}
}
async function createPoints(scene: Scene, divisions: number = 10, scale: number = 80) {
const half = .5;
const increment = 1 / divisions;
let x = -half;
let y = -half;
let z = -half;
const baseTransform = new TransformNode("baseTransform", scene);
baseTransform.scaling = new Vector3(scale, scale, scale);
baseTransform.position = new Vector3(0, scale / 2, 0);
const pcs = new PointsCloudSystem("pcs", 1, scene);
pcs.addPoints((divisions + 1) ** 3, function (particle) {
particle.position.x = x;
particle.position.y = y;
particle.position.z = z;
x += increment;
if (x > half) {
x = -half
y += increment;
if (y > half) {
y = -half;
z += increment;
if (z > half) {
}
}
}
});
const mesh = await pcs.buildMeshAsync();
mesh.visibility = .5;
mesh.parent = baseTransform;
}
function createGridMaterial(scene: Scene, lineColor: Color3, mainColor: Color3): Material {
const material = new GridMaterial("gridMaterial", scene);
material.minorUnitVisibility = .1;
material.gridRatio = .1;
material.majorUnitFrequency = 10;
material.mainColor = mainColor;
material.lineColor = lineColor;
return material;
}
function createGrassGround(scene: Scene): Material {
const groundMaterial = new PBRMaterial("groundMaterial", scene); const groundMaterial = new PBRMaterial("groundMaterial", scene);
const gText = new Texture("/assets/textures/grass1.jpeg", scene); const gText = new Texture("/assets/textures/grass1.jpeg", scene);
gText.uScale = 30; gText.uScale = 10;
gText.vScale = 30; gText.vScale = 10;
groundMaterial.albedoTexture = gText; groundMaterial.albedoTexture = gText;
groundMaterial.metallic = 0; groundMaterial.metallic = 0;
groundMaterial.roughness = 1; groundMaterial.roughness = 1;
@ -94,16 +176,5 @@ export class CustomEnvironment {
grassBump.vScale = 20; grassBump.vScale = 20;
groundMaterial.bumpTexture = groundMaterial.bumpTexture =
grassBump; grassBump;
return groundMaterial;
const ground: GroundMesh = MeshBuilder.CreateGround("ground", {
width: 100,
height: 100,
subdivisions: 1
}, scene);
ground.material = groundMaterial;
new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, scene);
//buildAvatar(scene);
return ground;
}
} }

View File

@ -11,6 +11,9 @@ export function buildQuestLink() {
a.target = "_blank"; a.target = "_blank";
a.innerText = "Launch On Quest"; a.innerText = "Launch On Quest";
div.appendChild(a); div.appendChild(a);
document.body.appendChild(div); const main = document.querySelector('#main');
if (main) {
//main.appendChild(div);
}
} }

View File

@ -0,0 +1,69 @@
import {
AssetContainer,
Color3,
Mesh,
MeshBuilder,
Scalar,
Scene,
SceneLoader,
SpotLight,
StandardMaterial,
Vector3
} from "@babylonjs/core";
export function createCloset(scene: Scene) {
const width = 1.8;
const height = 2.4;
const material = new StandardMaterial("closet", scene);
material.maxSimultaneousLights = 10;
material.diffuseColor = Color3.FromHexString("#ffffee");
const back = MeshBuilder.CreatePlane("back", {sideOrientation: Mesh.DOUBLESIDE, width: width, height: height}, scene);
back.position.z = -.9 / 2;
back.material = material;
back.position.y = height / 2;
const left = MeshBuilder.CreatePlane("back", {sideOrientation: Mesh.DOUBLESIDE, width: .9, height: height}, scene);
const left2 = MeshBuilder.CreatePlane("back", {sideOrientation: Mesh.DOUBLESIDE, width: .15, height: height}, scene);
left2.position.z = .9 / 2;
left2.position.x = -width / 2 + .15 / 2;
left2.position.y = height / 2;
left.material = material;
left2.material = material;
const right = MeshBuilder.CreatePlane("back", {sideOrientation: Mesh.DOUBLESIDE, width: .9, height: height}, scene);
const right2 = MeshBuilder.CreatePlane("back", {sideOrientation: Mesh.DOUBLESIDE, width: .15, height: height}, scene);
right2.position.x = width / 2 - .15 / 2;
right2.position.y = height / 2;
right2.position.z = .9 / 2;
right2.material = material
right.material = material;
left.position.y = height / 2;
right.position.y = height / 2;
left.position.x = width / 2;
right.position.x = -width / 2;
left.rotation.y = Math.PI / 2;
right.rotation.y = Math.PI / 2;
const front = MeshBuilder.CreatePlane("back", {sideOrientation: Mesh.DOUBLESIDE, width: width - .3, height: .35}, scene);
front.material = material;
front.position.y = height - .35 / 2;
front.position.z = .9 / 2;
const width2 = width * .9;
for (let i = 0; i < 5; i++) {
const l = Scalar.Lerp(-width2 / 2, width2 / 2, (width / 5) * i);
const light = new SpotLight("light", new Vector3(l, 2, (.9 / 2) - .01),
new Vector3(0, 0, -1), Math.PI / 1.5, 5, scene);
light.intensity = .3;
}
SceneLoader.LoadAssetContainer("/assets/textures/washer/LG Trom Wash Tower Object Collection_2color/LG Trom Wash Tower_white/", "LG Trom Wash.gltf", scene,
(container: AssetContainer) => {
const model = container.instantiateModelsToScene(undefined, false, {doNotInstantiate: true});
const node = model.rootNodes[0];
node.scaling.scaleInPlace(.00098);
node.position.x -= .42;
const bounds = node.getHierarchyBoundingVectors(true);
console.log((bounds.max.x - bounds.min.x) * 39.37);
console.log('here');
});
}

View File

@ -20,6 +20,9 @@ export async function groundMeshObserver(ground, scene, diagramManager, controll
} }
}); });
xr.baseExperience.onInitialXRPoseSetObservable.add((camera) => {
//camera.position = new Vector3(0, -1.6, 0);
});
if (spinner) { if (spinner) {
spinner.hide(); spinner.hide();
@ -30,9 +33,12 @@ export async function groundMeshObserver(ground, scene, diagramManager, controll
this.logger.debug(ev); this.logger.debug(ev);
}); });
}); });
xr.baseExperience.onStateChangedObservable.add((state) => { xr.baseExperience.onStateChangedObservable.add((state) => {
if (state == WebXRState.IN_XR) { switch (state) {
case WebXRState.IN_XR:
scene.audioEnabled = true; scene.audioEnabled = true;
//xr.baseExperience.camera.position = new Vector3(0, 1.6, 0); //xr.baseExperience.camera.position = new Vector3(0, 1.6, 0);
//xr.baseExperience.camera.setTarget(new Vector3(0, 1.6, 3)); //xr.baseExperience.camera.setTarget(new Vector3(0, 1.6, 3));
window.addEventListener(('pa-button-state-change'), (event: any) => { window.addEventListener(('pa-button-state-change'), (event: any) => {
@ -40,7 +46,10 @@ export async function groundMeshObserver(ground, scene, diagramManager, controll
log.debug('App', event.detail); log.debug('App', event.detail);
} }
}); });
break;
} }
}); });
import('../../controllers/rigplatform').then((rigmodule) => { import('../../controllers/rigplatform').then((rigmodule) => {
const rig = new rigmodule.Rigplatform(scene, xr, diagramManager, controllers); const rig = new rigmodule.Rigplatform(scene, xr, diagramManager, controllers);
@ -58,8 +67,6 @@ export async function groundMeshObserver(ground, scene, diagramManager, controll
} }
}); });
const config = new ConfigMenu(scene, xr, controllers, diagramManager.config); const config = new ConfigMenu(scene, xr, controllers, diagramManager.config);
const webController = new WebController(scene, rig, diagramManager, controllers); const webController = new WebController(scene, rig, diagramManager, controllers);
}); });
} }

View File

@ -0,0 +1,74 @@
import {AbstractMesh, DynamicTexture, Material, MeshBuilder, StandardMaterial} from "@babylonjs/core";
import log from "loglevel";
const textLogger: log.Logger = log.getLogger('TextLabel');
export function updateTextNode(mesh: AbstractMesh, text: string) {
if (!mesh) {
textLogger.error("updateTextNode: mesh is null");
return null;
}
const textNodes = mesh.getChildren((node) => {
return node.metadata?.label == true;
});
if (textNodes && textNodes.length > 0) {
textNodes.forEach((node) => {
node.dispose(false, true);
});
}
if (!text) {
return null;
}
//Set font
const height = 0.05;
const font_size = 24;
const font = "bold " + font_size + "px Arial";
//Set height for dynamic texture
const DTHeight = 1.5 * font_size; //or set as wished
//Calc Ratio
const ratio = height / DTHeight;
//Use a temporary dynamic texture to calculate the length of the text on the dynamic texture canvas
const temp = new DynamicTexture("DynamicTexture", 32, mesh.getScene());
const tmpctx = temp.getContext();
tmpctx.font = font;
const DTWidth = tmpctx.measureText(text).width + 8;
//Calculate width the plane has to be
const planeWidth = DTWidth * ratio;
//Create dynamic texture and write the text
const dynamicTexture = new DynamicTexture("DynamicTexture", {
width: DTWidth,
height: DTHeight
}, mesh.getScene(), false);
const mat = new StandardMaterial("mat", mesh.getScene());
mat.diffuseTexture = dynamicTexture;
//mat.emissiveColor = Color3.White();
dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
//Create plane and set dynamic texture as material
//const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene());
const plane1 = createPlane(mat, mesh, text, planeWidth, height);
const plane2 = createPlane(mat, mesh, text, planeWidth, height);
plane2.rotation.y = Math.PI;
}
function createPlane(mat: Material, mesh: AbstractMesh, text: string, planeWidth: number, height: number): AbstractMesh {
const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene());
plane.material = mat;
//plane.billboardMode = Mesh.BILLBOARDMODE_ALL;
plane.metadata = {exportable: true, label: true};
const yOffset = mesh.getBoundingInfo().boundingSphere.maximum.y;
plane.parent = mesh;
plane.scaling.y = (1 / mesh.scaling.y);
plane.scaling.x = (1 / mesh.scaling.x);
plane.scaling.z = (1 / mesh.scaling.z);
plane.position.y = yOffset + (height * plane.scaling.y);
return plane;
}

View File

@ -19,7 +19,6 @@ export class Spinner {
constructor(scene: Scene) { constructor(scene: Scene) {
this.scene = scene; this.scene = scene;
this.build(); this.build();
} }
public show() { public show() {
@ -33,7 +32,7 @@ export class Spinner {
} }
private build() { private build() {
const spinner: AbstractMesh = MeshBuilder.CreateSphere("spinner", {diameter: 1}, this.scene); const spinner: AbstractMesh = MeshBuilder.CreateSphere("spinner", {diameter: 2}, this.scene);
const material = new StandardMaterial("spinner", this.scene); const material = new StandardMaterial("spinner", this.scene);
const text = new DynamicTexture("spinner", {width: 1024, height: 1024}, this.scene, false); const text = new DynamicTexture("spinner", {width: 1024, height: 1024}, this.scene, false);
text.drawText("Please Wait", 250, 500, "bold 150px Segoe UI", "white", "transparent", true, true); text.drawText("Please Wait", 250, 500, "bold 150px Segoe UI", "white", "transparent", true, true);
@ -82,6 +81,8 @@ export class Spinner {
particleSystem.emitter = spinner; particleSystem.emitter = spinner;
particleSystem.parent = spinner; particleSystem.parent = spinner;
spinner.position.y = 1; spinner.position.y = 1;
spinner.position.z = 6;
this.spinner = spinner; this.spinner = spinner;
this.spinner.setEnabled(false); this.spinner.setEnabled(false);
this.particleSystem = particleSystem; this.particleSystem = particleSystem;

View File

@ -12,9 +12,10 @@ import {PouchdbPersistenceManager} from "./integration/pouchdbPersistenceManager
import {addSceneInspector} from "./util/functions/sceneInspctor"; import {addSceneInspector} from "./util/functions/sceneInspctor";
import {groundMeshObserver} from "./util/functions/groundMeshObserver"; import {groundMeshObserver} from "./util/functions/groundMeshObserver";
import {MainMenu} from "./menus/mainMenu"; import {MainMenu} from "./menus/mainMenu";
import {Introduction} from "./tutorial/introduction";
import {buildQuestLink} from "./util/functions/buildQuestLink"; import {buildQuestLink} from "./util/functions/buildQuestLink";
import {exportGltf} from "./util/functions/exportGltf"; import {exportGltf} from "./util/functions/exportGltf";
import {Tutorial} from "./tutorial/tutorial";
export class VrApp { export class VrApp {
private scene: Scene; private scene: Scene;
@ -23,16 +24,11 @@ export class VrApp {
private logger: Logger = log.getLogger('App'); private logger: Logger = log.getLogger('App');
constructor() { constructor() {
log.setDefaultLevel('warn'); log.setDefaultLevel('warn');
log.getLogger('App').setLevel('debug'); log.getLogger('App').setLevel('debug');
log.getLogger('DiagramManager').setLevel('debug'); log.getLogger('DiagramManager').setLevel('debug');
const canvas = document.querySelector('#gameCanvas'); const canvas = document.querySelector('#gameCanvas');
this.logger.debug('App', 'gameCanvas created'); this.logger.debug('App', 'gameCanvas created');
} }
public async initialize(canvas: HTMLCanvasElement) { public async initialize(canvas: HTMLCanvasElement) {
@ -46,6 +42,7 @@ export class VrApp {
const spinner = new Spinner(scene); const spinner = new Spinner(scene);
spinner.show(); spinner.show();
const config = new AppConfig(); const config = new AppConfig();
const tutorial = new Tutorial(scene, config);
const controllers = new Controllers(); const controllers = new Controllers();
const toolbox = new Toolbox(scene, controllers); const toolbox = new Toolbox(scene, controllers);
const diagramManager = new DiagramManager(scene, controllers, toolbox, config); const diagramManager = new DiagramManager(scene, controllers, toolbox, config);
@ -54,18 +51,16 @@ export class VrApp {
db.setDiagramManager(diagramManager); db.setDiagramManager(diagramManager);
db.configObserver.add((newConfig) => { db.configObserver.add((newConfig) => {
if (!newConfig.demoCompleted) { if (!newConfig.demoCompleted) {
const demo = new Introduction(scene, config); const main = document.querySelector('#main');
} else {
const create = document.querySelector('#create');
} }
config.onConfigChangedObservable.notifyObservers(newConfig, 1); config.onConfigChangedObservable.notifyObservers(newConfig, 1);
}); });
config.onConfigChangedObservable.add((newConfig) => { config.onConfigChangedObservable.add((newConfig) => {
db.setConfig(newConfig); db.setConfig(newConfig);
}, 2, false, this); }, 2, false, this);
await db.initialize(); await db.initialize();
const camera: FreeCamera = new FreeCamera("Main Camera", const camera: FreeCamera = new FreeCamera("Main Camera",
new Vector3(0, 1.6, 0), scene); new Vector3(0, 1.6, 0), scene);
//camera.setTarget(new Vector3(0, 1.6, -3)); //camera.setTarget(new Vector3(0, 1.6, -3));
@ -112,15 +107,6 @@ export class VrApp {
exportGltf(scene); exportGltf(scene);
}) })
} }
/*
const base = new TransformNode("chart");
base.position.y = .25;
base.position.z = -5;
const chart = new Timeseries(base);
chart.setData(genData());
*/
//const newRelic = new NewRelicQuery(scene);
//newRelic.getSales();
this.logger.info('keydown event listener added, use Ctrl+Shift+Alt+I to toggle debug layer'); this.logger.info('keydown event listener added, use Ctrl+Shift+Alt+I to toggle debug layer');
let i = 0; let i = 0;
this.engine.runRenderLoop(() => { this.engine.runRenderLoop(() => {