experiments with glb objects and data based diagrams.
This commit is contained in:
parent
dcd5a8835c
commit
a1adbf5bd9
@ -109,9 +109,8 @@
|
|||||||
<script src='/niceware.js'></script>
|
<script src='/niceware.js'></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!--<div id="questLaunch"><a href="https://www.oculus.com/open_url/?url=https://www.deepdiagram.com/" target="_blank">Launch
|
|
||||||
On Quest</a>
|
<!--
|
||||||
</div>
|
|
||||||
<div id="download"><a href="#" id="downloadLink">Download Model</a></div>
|
<div id="download"><a href="#" id="downloadLink">Download Model</a></div>
|
||||||
-->
|
-->
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
BIN
public/assets/models/model.glb
Normal file
BIN
public/assets/models/model.glb
Normal file
Binary file not shown.
63
public/data/data.json
Normal file
63
public/data/data.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@ export class Rigplatform {
|
|||||||
private readonly xr: WebXRDefaultExperience;
|
private readonly xr: WebXRDefaultExperience;
|
||||||
private yRotation: number = 0;
|
private yRotation: number = 0;
|
||||||
public rigMesh: Mesh;
|
public rigMesh: Mesh;
|
||||||
|
public flyMode: boolean = true;
|
||||||
private turning: boolean = false;
|
private turning: boolean = false;
|
||||||
private velocity: Vector3 = Vector3.Zero();
|
private velocity: Vector3 = Vector3.Zero();
|
||||||
private turnVelocity: number = 0;
|
private turnVelocity: number = 0;
|
||||||
@ -43,6 +43,7 @@ export class Rigplatform {
|
|||||||
this.fixRotation();
|
this.fixRotation();
|
||||||
this.initializeControllers();
|
this.initializeControllers();
|
||||||
this.registerVelocityObserver();
|
this.registerVelocityObserver();
|
||||||
|
|
||||||
}
|
}
|
||||||
public forwardback(val: number) {
|
public forwardback(val: number) {
|
||||||
this.velocity.z = (val * this.velocityArray[this.velocityIndex])*-1;
|
this.velocity.z = (val * this.velocityArray[this.velocityIndex])*-1;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {DiagramEntity} 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 {TextLabel} from "../../objects/textLabel";
|
||||||
@ -13,9 +13,19 @@ export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene):
|
|||||||
logger.error("buildMeshFromDiagramEntity: entity is null");
|
logger.error("buildMeshFromDiagramEntity: entity is null");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!entity.id) {
|
switch (entity.type) {
|
||||||
entity.id = "id" + uuidv4();
|
case DiagramEntityType.USER:
|
||||||
|
logger.debug("buildMeshFromDiagramEntity: entity is user");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
generateId(entity);
|
||||||
|
|
||||||
|
const newMesh: AbstractMesh = createNewInstanceIfNecessary(entity, scene);
|
||||||
|
return mapMetadata(entity, newMesh, scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNewInstanceIfNecessary(entity: DiagramEntity, scene: Scene): AbstractMesh {
|
||||||
const oldMesh: AbstractMesh = scene.getMeshById(entity.id);
|
const oldMesh: AbstractMesh = scene.getMeshById(entity.id);
|
||||||
let newMesh: AbstractMesh;
|
let newMesh: AbstractMesh;
|
||||||
if (oldMesh) {
|
if (oldMesh) {
|
||||||
@ -33,10 +43,20 @@ export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene):
|
|||||||
newMesh.metadata = {template: entity.template, exportable: true, tool: false};
|
newMesh.metadata = {template: entity.template, exportable: true, tool: false};
|
||||||
} else {
|
} else {
|
||||||
logger.warn('no tool mesh found for ' + entity.template + "-" + entity.color);
|
logger.warn('no tool mesh found for ' + entity.template + "-" + entity.color);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return newMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateId(entity: DiagramEntity) {
|
||||||
|
if (!entity.id) {
|
||||||
|
entity.id = "id" + uuidv4();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapMetadata(entity: DiagramEntity, newMesh: AbstractMesh, scene: Scene): AbstractMesh {
|
||||||
if (newMesh) {
|
if (newMesh) {
|
||||||
if (entity.position) {
|
if (entity.position) {
|
||||||
newMesh.position = xyztovec(entity.position);
|
newMesh.position = xyztovec(entity.position);
|
||||||
@ -73,8 +93,6 @@ export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene):
|
|||||||
}
|
}
|
||||||
return newMesh;
|
return newMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function xyztovec(xyz: { x, y, z }): Vector3 {
|
function xyztovec(xyz: { x, y, z }): Vector3 {
|
||||||
return new Vector3(xyz.x, xyz.y, xyz.z);
|
return new Vector3(xyz.x, xyz.y, xyz.z);
|
||||||
}
|
}
|
||||||
@ -25,7 +25,15 @@ export function toDiagramEntity(mesh: AbstractMesh): DiagramEntity {
|
|||||||
entity.to = mesh?.metadata?.to;
|
entity.to = mesh?.metadata?.to;
|
||||||
entity.scale = vectoxys(mesh.scaling);
|
entity.scale = vectoxys(mesh.scaling);
|
||||||
if (mesh.material) {
|
if (mesh.material) {
|
||||||
entity.color = (mesh.material as any).diffuseColor.toHexString();
|
switch (mesh.material.getClassName()) {
|
||||||
|
case "StandardMaterial":
|
||||||
|
entity.color = (mesh.material as any).diffuseColor.toHexString();
|
||||||
|
break;
|
||||||
|
case "PBRMaterial":
|
||||||
|
entity.color = (mesh.material as any).albedoColor.toHexString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (entity.template != "#object-template") {
|
if (entity.template != "#object-template") {
|
||||||
logger.error("toDiagramEntity: mesh.material is null");
|
logger.error("toDiagramEntity: mesh.material is null");
|
||||||
|
|||||||
@ -13,6 +13,10 @@ export enum DiagramEventType {
|
|||||||
RESET
|
RESET
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DiagramEntityType {
|
||||||
|
USER = "user"
|
||||||
|
}
|
||||||
|
|
||||||
export enum DiagramEventMask {
|
export enum DiagramEventMask {
|
||||||
LOCAL = 1,
|
LOCAL = 1,
|
||||||
REMOTE = 2,
|
REMOTE = 2,
|
||||||
@ -37,6 +41,7 @@ export type DiagramEntity = {
|
|||||||
position?: { x: number, y: number, z: number };
|
position?: { x: number, y: number, z: number };
|
||||||
rotation?: { x: number, y: number, z: number };
|
rotation?: { x: number, y: number, z: number };
|
||||||
template?: string;
|
template?: string;
|
||||||
|
type?: DiagramEntityType;
|
||||||
text?: string;
|
text?: string;
|
||||||
scale?: { x: number, y: number, z: number };
|
scale?: { x: number, y: number, z: number };
|
||||||
parent?: string;
|
parent?: string;
|
||||||
|
|||||||
87
src/objects/avatar.ts
Normal file
87
src/objects/avatar.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
AssetContainer,
|
||||||
|
Mesh,
|
||||||
|
MeshBuilder,
|
||||||
|
Observable,
|
||||||
|
PhysicsAggregate,
|
||||||
|
PhysicsMotionType,
|
||||||
|
PhysicsShapeType,
|
||||||
|
Scene,
|
||||||
|
SceneLoader,
|
||||||
|
Vector3
|
||||||
|
} from "@babylonjs/core";
|
||||||
|
|
||||||
|
export function buildAvatar(scene: Scene) {
|
||||||
|
const objectObservable = new Observable<AssetContainer>();
|
||||||
|
objectObservable.add((container) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const data = container.instantiateModelsToScene(undefined, false, {doNotInstantiate: true});
|
||||||
|
const mesh = (data.rootNodes[0] as Mesh);
|
||||||
|
const body = scene.getMeshByName("Clone of avaturn_body");
|
||||||
|
/*body.simplify(
|
||||||
|
[
|
||||||
|
{quality: .7, distance: 5},
|
||||||
|
{quality: .1, distance: 10},
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
SimplificationType.QUADRATIC,
|
||||||
|
function() {
|
||||||
|
const skel = scene.getSkeletonById("Clone of Armature");
|
||||||
|
body.parent.getChildMeshes().forEach((m) => {
|
||||||
|
if (m.name.indexOf("ecimated") > -1) {
|
||||||
|
m.skeleton = skel;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log("simplification done");
|
||||||
|
}
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const bounds = mesh.getHierarchyBoundingVectors(true);
|
||||||
|
console.log(bounds);
|
||||||
|
const size = bounds.max.subtract(bounds.min);
|
||||||
|
const top = MeshBuilder.CreateBox("container", {width: size.x, height: size.y, depth: size.z}, scene);
|
||||||
|
top.position.y = 1.6;
|
||||||
|
top.metadata = {grabbable: true};
|
||||||
|
mesh.parent = top;
|
||||||
|
mesh.position.y = -size.y / 2;
|
||||||
|
top.position = new Vector3(-.6, size.y / 2 + 1, 0);
|
||||||
|
|
||||||
|
//top.scaling = new Vector3(.1, .1, .1);
|
||||||
|
top.visibility = 0;
|
||||||
|
|
||||||
|
//top.physicsBody = new PhysicsBody(top, PhysicsMotionType.DYNAMIC, false, this.scene);
|
||||||
|
|
||||||
|
data.animationGroups[0].play(true);
|
||||||
|
|
||||||
|
|
||||||
|
const physicsAggregate = new PhysicsAggregate(top,
|
||||||
|
PhysicsShapeType.BOX, {mass: 100, restitution: .02, friction: .3}, scene);
|
||||||
|
physicsAggregate.body.setMotionType(PhysicsMotionType.DYNAMIC);
|
||||||
|
physicsAggregate.body.setGravityFactor(1);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
//physicsAggregate.body.setAngularDamping(.5);
|
||||||
|
|
||||||
|
//top.physicsBody.setLinearVelocity(Vector3.Up().scale(100));
|
||||||
|
//mesh.parent = top.physicsBody.transformNode;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
loadObject(scene, objectObservable);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadObject(scene: Scene, observable: Observable<AssetContainer>) {
|
||||||
|
SceneLoader.LoadAssetContainer("/assets/models/",
|
||||||
|
"model.glb",
|
||||||
|
scene,
|
||||||
|
(container: AssetContainer) => {
|
||||||
|
observable.notifyObservers(container);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@ export class TextLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Set font
|
//Set font
|
||||||
const height = 0.125;
|
const height = 0.05;
|
||||||
const font_size = 24;
|
const font_size = 24;
|
||||||
const font = "bold " + font_size + "px Arial";
|
const font = "bold " + font_size + "px Arial";
|
||||||
//Set height for dynamic texture
|
//Set height for dynamic texture
|
||||||
@ -65,10 +65,11 @@ export class TextLabel {
|
|||||||
|
|
||||||
const yOffset = mesh.getBoundingInfo().boundingSphere.maximum.y;
|
const yOffset = mesh.getBoundingInfo().boundingSphere.maximum.y;
|
||||||
plane.parent = mesh;
|
plane.parent = mesh;
|
||||||
plane.position.y = yOffset + height;
|
|
||||||
//plane.scaling.y = mesh.scaling.y;
|
plane.scaling.y = (1 / mesh.scaling.y);
|
||||||
//plane.scaling.x = mesh.scaling.x;
|
plane.scaling.x = (1 / mesh.scaling.x);
|
||||||
//plane.scaling.z = mesh.scaling.z;
|
plane.scaling.z = (1 / mesh.scaling.z);
|
||||||
|
plane.position.y = yOffset + (height * plane.scaling.y);
|
||||||
return plane;
|
return plane;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,15 +1,21 @@
|
|||||||
import {Color3, MeshBuilder, Observable, Scene, StandardMaterial, TransformNode, Vector3} from "@babylonjs/core";
|
import {Color3, MeshBuilder, Observable, PBRMaterial, Scene, TransformNode, Vector3} from "@babylonjs/core";
|
||||||
import {enumKeys} from "../../util/functions/enumKeys";
|
import {enumKeys} from "../../util/functions/enumKeys";
|
||||||
import {ToolType} from "../types/toolType";
|
import {ToolType} from "../types/toolType";
|
||||||
import {buildTool} from "./buildTool";
|
import {buildTool} from "./buildTool";
|
||||||
import {AdvancedDynamicTexture, ColorPicker} from "@babylonjs/gui";
|
import {AdvancedDynamicTexture, ColorPicker} from "@babylonjs/gui";
|
||||||
|
import {MarbleProceduralTexture} from "@babylonjs/procedural-textures";
|
||||||
|
|
||||||
export function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number,
|
export function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number,
|
||||||
colorChangeObservable: Observable<{ oldColor: string, newColor: string }>) {
|
colorChangeObservable: Observable<{ oldColor: string, newColor: string }>) {
|
||||||
const width = 1;
|
const width = 1;
|
||||||
const depth = .2;
|
const depth = .2;
|
||||||
const material = new StandardMaterial("material-" + color.toHexString(), scene);
|
const material = new PBRMaterial("material-" + color.toHexString(), scene);
|
||||||
material.diffuseColor = color;
|
|
||||||
|
//const material = new StandardMaterial("material-" + color.toHexString(), scene);
|
||||||
|
material.albedoColor = color;
|
||||||
|
material.metallic = 1;
|
||||||
|
material.bumpTexture = new MarbleProceduralTexture("marble", 1024, scene);
|
||||||
|
material.bumpTexture.level = 5;
|
||||||
const mesh = MeshBuilder.CreateBox("toolbox-color-" + color.toHexString(), {
|
const mesh = MeshBuilder.CreateBox("toolbox-color-" + color.toHexString(), {
|
||||||
width: width,
|
width: width,
|
||||||
height: .01,
|
height: .01,
|
||||||
@ -42,9 +48,9 @@ export function buildColor(color: Color3, scene: Scene, parent: TransformNode, i
|
|||||||
colorPicker.scaleX = 5;
|
colorPicker.scaleX = 5;
|
||||||
colorPicker.value = color;
|
colorPicker.value = color;
|
||||||
colorPicker.onValueChangedObservable.add((value) => {
|
colorPicker.onValueChangedObservable.add((value) => {
|
||||||
const oldColor = material.diffuseColor.clone();
|
const oldColor = material.albedoColor.clone();
|
||||||
const newColor = value.clone();
|
const newColor = value.clone();
|
||||||
material.diffuseColor = newColor;
|
material.albedoColor = newColor;
|
||||||
const newColorHex = newColor.toHexString();
|
const newColorHex = newColor.toHexString();
|
||||||
material.id = "material-" + newColorHex;
|
material.id = "material-" + newColorHex;
|
||||||
material.name = "material-" + newColorHex;
|
material.name = "material-" + newColorHex;
|
||||||
|
|||||||
@ -1,11 +1,23 @@
|
|||||||
import {AbstractMesh, Color3, InstancedMesh, StandardMaterial, Vector3} from "@babylonjs/core";
|
import {AbstractMesh, Color3, InstancedMesh, PBRMaterial, StandardMaterial, Vector3} from "@babylonjs/core";
|
||||||
import {ToolType} from "../types/toolType";
|
import {ToolType} from "../types/toolType";
|
||||||
import {buildMesh} from "./buildMesh";
|
import {buildMesh} from "./buildMesh";
|
||||||
|
|
||||||
const WIDGET_SIZE = .1;
|
const WIDGET_SIZE = .1;
|
||||||
|
|
||||||
export function buildTool(tool: ToolType, parent: AbstractMesh) {
|
export function buildTool(tool: ToolType, parent: AbstractMesh) {
|
||||||
const id = toolId(tool, (parent.material as StandardMaterial).diffuseColor);
|
let id = "ID";
|
||||||
|
switch (parent.material.getClassName()) {
|
||||||
|
|
||||||
|
case "StandardMaterial":
|
||||||
|
id = toolId(tool, (parent.material as StandardMaterial).diffuseColor);
|
||||||
|
break;
|
||||||
|
case "PBRMaterial":
|
||||||
|
id = toolId(tool, (parent.material as PBRMaterial).albedoColor);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.logger.warn("buildTool: parent.material is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const newItem = buildMesh(tool, `tool-${id}`, parent.getScene());
|
const newItem = buildMesh(tool, `tool-${id}`, parent.getScene());
|
||||||
if (!newItem) {
|
if (!newItem) {
|
||||||
|
|||||||
@ -1,14 +1,4 @@
|
|||||||
import {
|
import {AssetContainer, Color3, Mesh, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core";
|
||||||
AssetContainer,
|
|
||||||
Color3,
|
|
||||||
Mesh,
|
|
||||||
MeshBuilder,
|
|
||||||
Observable,
|
|
||||||
Scene,
|
|
||||||
SceneLoader,
|
|
||||||
TransformNode,
|
|
||||||
Vector3
|
|
||||||
} from "@babylonjs/core";
|
|
||||||
|
|
||||||
import {Button3D, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui";
|
import {Button3D, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui";
|
||||||
import {ControllerEventType, Controllers} from "../controllers/controllers";
|
import {ControllerEventType, Controllers} from "../controllers/controllers";
|
||||||
@ -81,25 +71,8 @@ export class Toolbox {
|
|||||||
this.addPanel.node.scaling = new Vector3(.1, .1, .1);
|
this.addPanel.node.scaling = new Vector3(.1, .1, .1);
|
||||||
this.addPanel.position = new Vector3(-.25, 0, 0);
|
this.addPanel.position = new Vector3(-.25, 0, 0);
|
||||||
//@TODO: move this somewhere else, just to prototype loading objects.
|
//@TODO: move this somewhere else, just to prototype loading objects.
|
||||||
//loadObject(this.scene, this.objectObservable);
|
|
||||||
this.objectObservable.add((container) => {
|
|
||||||
this.logger.debug("loaded object");
|
|
||||||
const data = container.instantiateModelsToScene(undefined, false, {doNotInstantiate: true});
|
|
||||||
const mesh = (data.rootNodes[0] as Mesh);
|
|
||||||
const bounds = data.rootNodes[0].getHierarchyBoundingVectors(true);
|
|
||||||
console.log(bounds);
|
|
||||||
const size = bounds.max.subtract(bounds.min);
|
|
||||||
const top = MeshBuilder.CreateBox("container", {width: size.x, height: size.y, depth: size.z}, this.scene);
|
|
||||||
top.position.y = 1.5;
|
|
||||||
top.metadata = {template: "#object-template", grabbable: true, tool: true};
|
|
||||||
mesh.parent = top;
|
|
||||||
mesh.position.y = -size.y / 2;
|
|
||||||
top.position = new Vector3(-.6, .2, 0);
|
|
||||||
//top.scaling = new Vector3(.1, .1, .1);
|
|
||||||
top.visibility = 0;
|
|
||||||
console.log(data.rootNodes.length);
|
|
||||||
|
|
||||||
});
|
|
||||||
addButton.onPointerClickObservable.add(() => {
|
addButton.onPointerClickObservable.add(() => {
|
||||||
buildColor(Color3.Random(), this.scene, this.node, this.index++, this.colorChangeObservable);
|
buildColor(Color3.Random(), this.scene, this.node, this.index++, this.colorChangeObservable);
|
||||||
});
|
});
|
||||||
@ -111,16 +84,7 @@ export class Toolbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadObject(scene: Scene, observable: Observable<AssetContainer>) {
|
|
||||||
SceneLoader.LoadAssetContainer("/assets/models/",
|
|
||||||
"server_racking_system.glb",
|
|
||||||
scene,
|
|
||||||
(container: AssetContainer) => {
|
|
||||||
observable.notifyObservers(container);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
function createButton(): Button3D {
|
function createButton(): Button3D {
|
||||||
const addButton = new Button3D("add-button");
|
const addButton = new Button3D("add-button");
|
||||||
const text = new TextBlock("add-button-text", "Add Color");
|
const text = new TextBlock("add-button-text", "Add Color");
|
||||||
|
|||||||
@ -103,7 +103,7 @@ export class CustomEnvironment {
|
|||||||
|
|
||||||
ground.material = groundMaterial;
|
ground.material = groundMaterial;
|
||||||
new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, scene);
|
new PhysicsAggregate(ground, PhysicsShapeType.BOX, {mass: 0}, scene);
|
||||||
|
//buildAvatar(scene);
|
||||||
return ground;
|
return ground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
16
src/util/functions/buildQuestLink.ts
Normal file
16
src/util/functions/buildQuestLink.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export function buildQuestLink() {
|
||||||
|
/*
|
||||||
|
<div id="questLaunch"><a href="https://www.oculus.com/open_url/?url=https://www.deepdiagram.com/" target="_blank">Launch
|
||||||
|
On Quest</a>
|
||||||
|
</div>
|
||||||
|
*/
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.id = "questLaunch";
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = "https://www.oculus.com/open_url/?url=" + window.location.href;
|
||||||
|
a.target = "_blank";
|
||||||
|
a.innerText = "Launch On Quest";
|
||||||
|
div.appendChild(a);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
|
||||||
|
}
|
||||||
6
src/visualization/chartDataType.ts
Normal file
6
src/visualization/chartDataType.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {TimeseriesType} from "./timeseriesType";
|
||||||
|
|
||||||
|
export type ChartDataType = {
|
||||||
|
name: string;
|
||||||
|
series: TimeseriesType[];
|
||||||
|
}
|
||||||
22
src/visualization/functions/buildContainer.ts
Normal file
22
src/visualization/functions/buildContainer.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import {Color3, Mesh, MeshBuilder, StandardMaterial, TransformNode, Vector3} from "@babylonjs/core";
|
||||||
|
|
||||||
|
export function buildContainer(parent: TransformNode, size: Vector3) {
|
||||||
|
const scene = parent.getScene();
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const material = new StandardMaterial("container-material", scene);
|
||||||
|
material.diffuseColor = new Color3(.5, .5, .5);
|
||||||
|
|
||||||
|
const floor = MeshBuilder.CreatePlane("floor", {width: size.x, height: size.z, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||||
|
const left = MeshBuilder.CreatePlane("left", {width: size.z, height: size.y, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||||
|
const back = MeshBuilder.CreatePlane("back", {width: size.x, height: size.y, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||||
|
[floor, left, back].forEach((mesh) => {
|
||||||
|
mesh.material = material;
|
||||||
|
mesh.parent = parent;
|
||||||
|
});
|
||||||
|
left.position = new Vector3(size.x / 2, size.y / 2, 0);
|
||||||
|
back.position = new Vector3(0, size.y / 2, -size.z / 2);
|
||||||
|
left.rotation.y = Math.PI / 2;
|
||||||
|
floor.rotation.x = Math.PI / 2;
|
||||||
|
}
|
||||||
17
src/visualization/functions/buildLabel.ts
Normal file
17
src/visualization/functions/buildLabel.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import {AbstractMesh, Mesh, MeshBuilder, TransformNode} from "@babylonjs/core";
|
||||||
|
|
||||||
|
const LABEL_HEIGHT = .1;
|
||||||
|
|
||||||
|
export function buildLabel(name: string, parent: TransformNode, labelWidth: number): void {
|
||||||
|
const scene = parent.getScene();
|
||||||
|
const seriesLabel = MeshBuilder.CreatePlane(name + "-label", {
|
||||||
|
width: labelWidth,
|
||||||
|
height: LABEL_HEIGHT,
|
||||||
|
sideOrientation: Mesh.DOUBLESIDE
|
||||||
|
}, scene);
|
||||||
|
seriesLabel.parent = parent;
|
||||||
|
seriesLabel.position.z = LABEL_HEIGHT / 2;
|
||||||
|
seriesLabel.position.y = .5;
|
||||||
|
seriesLabel.rotation.x = Math.PI / 2;
|
||||||
|
seriesLabel.material = (parent as AbstractMesh).material;
|
||||||
|
}
|
||||||
62
src/visualization/functions/buildSeries.ts
Normal file
62
src/visualization/functions/buildSeries.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
AbstractMesh,
|
||||||
|
Color3,
|
||||||
|
InstancedMesh,
|
||||||
|
Mesh,
|
||||||
|
MeshBuilder,
|
||||||
|
StandardMaterial,
|
||||||
|
TransformNode,
|
||||||
|
Vector3
|
||||||
|
} from "@babylonjs/core";
|
||||||
|
import {buildLabel} from "./buildLabel";
|
||||||
|
import {TimeseriesType} from "../timeseriesType";
|
||||||
|
|
||||||
|
export function buildSeries(parent: TransformNode, series: TimeseriesType, index: number, count: number, scale: number): void {
|
||||||
|
const scene = parent.getScene();
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const labelWidth = 1 / count * .8;
|
||||||
|
const material = new StandardMaterial(series.name + "-material", scene);
|
||||||
|
const seriesMesh = MeshBuilder.CreatePlane(series.name, {width: labelWidth, height: 1, sideOrientation: Mesh.DOUBLESIDE}, scene);
|
||||||
|
seriesMesh.material = material;
|
||||||
|
seriesMesh.parent = parent;
|
||||||
|
seriesMesh.position.x = .5 - (index / count) - (1 / count / 2);
|
||||||
|
seriesMesh.position.y = .001;
|
||||||
|
seriesMesh.rotation.x = Math.PI / 2;
|
||||||
|
material.diffuseColor = Color3.Random();
|
||||||
|
buildLabel(series.name, seriesMesh, labelWidth);
|
||||||
|
buildValues(seriesMesh, series, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildValues(parent: TransformNode, series: TimeseriesType, scale: number): void {
|
||||||
|
const scene = parent.getScene();
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const valueCount = series.values.length;
|
||||||
|
const valueStep = 1 / valueCount;
|
||||||
|
let lastValue = null;
|
||||||
|
const linepoints: Vector3[] = [];
|
||||||
|
const basepoint = MeshBuilder.CreatePlane(series.name + "-value-base", {height: .003, width: .01}, scene);
|
||||||
|
|
||||||
|
basepoint.parent = parent;
|
||||||
|
basepoint.material = (parent as AbstractMesh).material;
|
||||||
|
basepoint.visibility = .5;
|
||||||
|
series.values.forEach((value, index) => {
|
||||||
|
//const point = MeshBuilder.CreateSphere(series.name + "-value-" + index, {diameter: .01}, scene);
|
||||||
|
const point = new InstancedMesh(series.name + "-value-" + index, basepoint);
|
||||||
|
point.parent = parent;
|
||||||
|
//point.rotation.x = Math.PI/2;
|
||||||
|
point.position.z = .5 - index * valueStep;
|
||||||
|
point.position.y = value.value * scale;
|
||||||
|
|
||||||
|
point.billboardMode = Mesh.BILLBOARDMODE_Y;
|
||||||
|
linepoints.push(point.position.clone());
|
||||||
|
});
|
||||||
|
const line = MeshBuilder.CreateLines(series.name + "-line", {points: linepoints}, scene);
|
||||||
|
line.color = ((parent as AbstractMesh).material as StandardMaterial).diffuseColor;
|
||||||
|
line.parent = parent;
|
||||||
|
line.isPickable = false;
|
||||||
|
line.rotation.x = -Math.PI / 2;
|
||||||
|
}
|
||||||
58
src/visualization/functions/genData.ts
Normal file
58
src/visualization/functions/genData.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import {ChartDataType} from "../chartDataType";
|
||||||
|
import {TimepointType, TimeseriesType} from "../timeseriesType";
|
||||||
|
|
||||||
|
export function genData(): ChartDataType {
|
||||||
|
return {
|
||||||
|
name: "test",
|
||||||
|
series: [
|
||||||
|
testDataSeries("series1"),
|
||||||
|
testDataSeries("series2"),
|
||||||
|
testDataSeries("series3"),
|
||||||
|
testDataSeries("series4"),
|
||||||
|
testDataSeries("series5"),
|
||||||
|
testDataSeries("series6"),
|
||||||
|
testDataSeries("series7"),
|
||||||
|
testDataSeries("series8"),
|
||||||
|
testDataSeries("series9"),
|
||||||
|
testDataSeries("series10"),
|
||||||
|
testDataSeries("series11"),
|
||||||
|
testDataSeries("series12"),
|
||||||
|
testDataSeries("series13"),
|
||||||
|
testDataSeries("series14"),
|
||||||
|
testDataSeries("series15"),
|
||||||
|
testDataSeries("series16"),
|
||||||
|
testDataSeries("series17"),
|
||||||
|
testDataSeries("series18"),
|
||||||
|
testDataSeries("series19"),
|
||||||
|
testDataSeries("series20"),
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDataSeries(name: string): TimeseriesType {
|
||||||
|
const data = {
|
||||||
|
name: name,
|
||||||
|
values: getValues()
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValues(): TimepointType[] {
|
||||||
|
const data: TimepointType[] = [];
|
||||||
|
const count = 150;
|
||||||
|
let val = Math.random() * 50 + 25;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
let rand = (Math.random() * 4 - 2);
|
||||||
|
if (Math.abs(rand) < .15) {
|
||||||
|
rand = (Math.random() * 10 - 5);
|
||||||
|
}
|
||||||
|
data.push({
|
||||||
|
start: i,
|
||||||
|
end: i + 1,
|
||||||
|
value: val += rand
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
46
src/visualization/timeseries.ts
Normal file
46
src/visualization/timeseries.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import {TransformNode, Vector3} from "@babylonjs/core";
|
||||||
|
import {TimeseriesType} from "./timeseriesType";
|
||||||
|
import {buildContainer} from "./functions/buildContainer";
|
||||||
|
import {ChartDataType} from "./chartDataType";
|
||||||
|
import {buildSeries} from "./functions/buildSeries";
|
||||||
|
|
||||||
|
export class Timeseries {
|
||||||
|
private parent: TransformNode;
|
||||||
|
private data: ChartDataType;
|
||||||
|
private size: Vector3 = new Vector3(1, 1, 1);
|
||||||
|
|
||||||
|
constructor(parent: TransformNode) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.parent.onDisposeObservable.add(() => {
|
||||||
|
this.parent = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public setData(data: ChartDataType) {
|
||||||
|
this.data = data;
|
||||||
|
if (this.parent) {
|
||||||
|
this.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchData(url: string) {
|
||||||
|
fetch(url)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
this.setData((data as ChartDataType));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
const scene = this.parent.getScene();
|
||||||
|
if (!scene || !this.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buildContainer(this.parent, this.size);
|
||||||
|
const seriesCount = this.data.series.length;
|
||||||
|
this.data.series.forEach((series: TimeseriesType, index: number) => {
|
||||||
|
buildSeries(this.parent, series, index, seriesCount, .01);
|
||||||
|
});
|
||||||
|
this.parent.scaling = new Vector3(5, 5, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/visualization/timeseriesType.ts
Normal file
10
src/visualization/timeseriesType.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export type TimeseriesType = {
|
||||||
|
name: string;
|
||||||
|
values: TimepointType[];
|
||||||
|
}
|
||||||
|
export type TimepointType = {
|
||||||
|
id?: string,
|
||||||
|
start?: string | number;
|
||||||
|
end: string | number;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
21
src/vrApp.ts
21
src/vrApp.ts
@ -13,6 +13,7 @@ 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 {Introduction} from "./tutorial/introduction";
|
||||||
|
import {buildQuestLink} from "./util/functions/buildQuestLink";
|
||||||
|
|
||||||
export class VrApp {
|
export class VrApp {
|
||||||
private scene: Scene;
|
private scene: Scene;
|
||||||
@ -63,11 +64,12 @@ export class VrApp {
|
|||||||
|
|
||||||
await db.initialize();
|
await db.initialize();
|
||||||
|
|
||||||
const environment = new CustomEnvironment(scene, "default", config);
|
|
||||||
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));
|
||||||
scene.setActiveCameraByName("Main Camera");
|
scene.setActiveCameraByName("Main Camera");
|
||||||
|
const environment = new CustomEnvironment(scene, "default", config);
|
||||||
environment.groundMeshObservable.add((ground) => {
|
environment.groundMeshObservable.add((ground) => {
|
||||||
groundMeshObserver(ground, scene, diagramManager, controllers, spinner);
|
groundMeshObserver(ground, scene, diagramManager, controllers, spinner);
|
||||||
}, -1, false, this);
|
}, -1, false, this);
|
||||||
@ -103,13 +105,24 @@ export class VrApp {
|
|||||||
*/
|
*/
|
||||||
addSceneInspector(scene);
|
addSceneInspector(scene);
|
||||||
const mainMenu = new MainMenu(scene);
|
const mainMenu = new MainMenu(scene);
|
||||||
//const zero = MeshBuilder.CreateSphere('target', {diameter: 1.6}, scene);
|
|
||||||
// zero.position = new Vector3(0, .8, 0);
|
/*
|
||||||
|
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);
|
//const newRelic = new NewRelicQuery(scene);
|
||||||
//newRelic.getSales();
|
//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;
|
||||||
this.engine.runRenderLoop(() => {
|
this.engine.runRenderLoop(() => {
|
||||||
this.scene.render();
|
this.scene.render();
|
||||||
|
if (i++ % 60 == 0) {
|
||||||
|
// console.log(this.engine.getFps());
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
this.logger.info('Render loop started');
|
this.logger.info('Render loop started');
|
||||||
|
|
||||||
@ -123,7 +136,7 @@ export class VrApp {
|
|||||||
const vrApp = new VrApp();
|
const vrApp = new VrApp();
|
||||||
const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement);
|
const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement);
|
||||||
vrApp.initialize(canvas).then(() => {
|
vrApp.initialize(canvas).then(() => {
|
||||||
|
buildQuestLink();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user