Added stl person to toolbox.

This commit is contained in:
Michael Mainguy 2024-06-17 11:33:24 -05:00
parent d08e86e92f
commit e0d85a6a3d
14 changed files with 109 additions and 57 deletions

Binary file not shown.

View File

@ -96,7 +96,6 @@ async function start() {
conn.connection.sendUTF('{ "type": "error", "netAddr": "' + hash + '" }');
});
logger.info((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.', connections.length);
});
setInterval(() => {
const message = `{ "count": ${connections.size} }`

View File

@ -27,12 +27,12 @@ export class DiagramManager {
private _moving: number = 10;
private _i: number = 0;
constructor() {
constructor(readyObservable: Observable<boolean>) {
this._me = getMe();
this._scene = DefaultScene.Scene;
this._config = new AppConfig();
this._controllers = new Controllers();
this._diagramMenuManager = new DiagramMenuManager(this.onDiagramEventObservable, this._controllers, this._config);
this._diagramMenuManager = new DiagramMenuManager(this.onDiagramEventObservable, this._controllers, this._config, readyObservable);
this._diagramEntityActionManager = buildEntityActionManager(this._controllers);
this.onDiagramEventObservable.add(this.onDiagramEvent, DiagramEventObserverMask.FROM_DB, true, this);
@ -97,6 +97,7 @@ export class DiagramManager {
});
this._logger.debug("DiagramManager constructed");
}
public get actionManager(): AbstractActionManager {
@ -140,7 +141,7 @@ export class DiagramManager {
private onDiagramEvent(event: DiagramEvent) {
let diagramObject = this._diagramObjects.get(event.entity.id);
let diagramObject = this._diagramObjects.get(event?.entity?.id);
switch (event.type) {
case DiagramEventType.ADD:
if (diagramObject) {
@ -160,7 +161,7 @@ export class DiagramManager {
if (diagramObject) {
diagramObject.dispose();
}
this._diagramObjects.delete(event.entity.id);
this._diagramObjects.delete(event?.entity?.id);
break;
case DiagramEventType.MODIFY:
this._logger.debug(event);

View File

@ -26,7 +26,7 @@ export class DiagramMenuManager {
private _logger = log.getLogger('DiagramMenuManager');
private _connectionPreview: ConnectionPreview;
constructor(notifier: Observable<DiagramEvent>, controllers: Controllers, config: AppConfig) {
constructor(notifier: Observable<DiagramEvent>, controllers: Controllers, config: AppConfig, readyObservable: Observable<boolean>) {
this._scene = DefaultScene.Scene;
@ -38,7 +38,7 @@ export class DiagramMenuManager {
const event = {type: DiagramEventType.MODIFY, entity: {id: evt.id, text: evt.text}}
this._notifier.notifyObservers(event, DiagramEventObserverMask.FROM_DB);
});
this.toolbox = new Toolbox();
this.toolbox = new Toolbox(readyObservable);
this.scaleMenu = new ScaleMenu2(this._notifier);
if (viewOnly()) {
this.toolbox.handleMesh.setEnabled(false);

View File

@ -62,6 +62,7 @@ function createNewInstanceIfNecessary(entity: DiagramEntity, scene: Scene): Abst
case DiagramTemplates.CYLINDER:
case DiagramTemplates.CONE:
case DiagramTemplates.PLANE:
case DiagramTemplates.PERSON:
const toolMesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color);
if (toolMesh && !oldMesh) {
newMesh = new InstancedMesh(entity.id, (toolMesh as Mesh));

View File

@ -30,6 +30,7 @@ export enum DiagramTemplates {
CONE = "#cone-template",
IMAGE = "#image-template",
PLANE = "#plane-template",
PERSON = "#person-template"
}
export type DiagramEvent = {

View File

@ -28,6 +28,8 @@ export class PouchdbPersistenceManager {
private _encryption = new Encryption();
private _encKey = null;
private _diagramManager: DiagramManager;
private _salt: string;
private _failCount: number = 0;
constructor() {
document.addEventListener('passwordset', (evt) => {
@ -37,7 +39,7 @@ export class PouchdbPersistenceManager {
this._logger.debug('Initialized');
});
}
console.log(evt);
this._logger.debug(evt);
});
}
@ -45,6 +47,14 @@ export class PouchdbPersistenceManager {
this._diagramManager = diagramManager;
diagramManager.onDiagramEventObservable.add((evt) => {
this._logger.debug(evt);
if (!evt?.entity) {
this._logger.warn('no entity');
return;
}
if (!evt?.entity?.id) {
this._logger.warn('no entity id');
return;
}
switch (evt.type) {
case DiagramEventType.REMOVE:
this.remove(evt.entity.id);
@ -105,6 +115,9 @@ export class PouchdbPersistenceManager {
this._logger.warn('CONFLICTS!', doc._conflicts);
}
if (this._encKey) {
if (!doc.encrypted) {
this._logger.warn("current local doc is not encrypted, encrypting");
}
await this._encryption.encryptObject(entity);
const newDoc = {
_id: doc._id,
@ -114,6 +127,9 @@ export class PouchdbPersistenceManager {
this.db.put(newDoc)
} else {
if (doc) {
if (doc.encrypted) {
this._logger.error("current local doc is encrypted, but encryption key is missing... saving in plaintext");
}
const newDoc = {_id: doc._id, _rev: doc._rev, ...entity};
this.db.put(newDoc);
} else {
@ -125,6 +141,10 @@ export class PouchdbPersistenceManager {
if (err.status == 404) {
try {
if (this._encKey) {
if (!this._encryption.ready) {
this._logger.error('Encryption not ready, there is a potential problem when this happens, we will generate a new salt which may cause data loss and/or slowness');
await this._encryption.setPassword(this._encKey);
}
await this._encryption.encryptObject(entity);
const newDoc = {
_id: entity.id,
@ -132,13 +152,16 @@ export class PouchdbPersistenceManager {
}
this.db.put(newDoc);
} else {
this._logger.info('no encryption key, saving in plaintext');
const newEntity = {_id: entity.id, ...entity};
this.db.put(newEntity);
}
} catch (err2) {
this._logger.error("Unable to save document");
this._logger.error(err2);
}
} else {
this._logger.error("Unknown error with document get from db");
this._logger.error(err);
}
}
@ -155,20 +178,27 @@ export class PouchdbPersistenceManager {
try {
const doc = await this.db.get('metadata');
if (doc.encrypted) {
if (!this._salt && doc.encrypted.salt) {
this._logger.warn('Missing Salt');
this._salt = doc.encrypted.salt;
}
if (!this._encKey) {
const promptPassword = new CustomEvent('promptpassword', {detail: 'Please enter password'});
document.dispatchEvent(promptPassword);
return false;
}
if (!this._encryption.ready) {
this._logger.warn("Encryption not ready, setting password");
await this._encryption.setPassword(this._encKey, doc.encrypted.salt);
}
const decrypted = await this._encryption.decryptToObject(doc.encrypted.encrypted, doc.encrypted.iv);
if (decrypted.friendly) {
this._logger.info("Storing Document friendly name in local storage, decrypted");
localStorage.setItem(current, decrypted.friendly);
}
} else {
if (doc && doc.friendly) {
this._logger.info("Storing Document friendly name in local storage");
localStorage.setItem(current, doc.friendly);
}
if (doc && doc.camera) {
@ -192,7 +222,7 @@ export class PouchdbPersistenceManager {
await this.db.put(newDoc);
}
} else {
this._logger.debug('no friendly name found');
this._logger.warn('no friendly name found');
}
}
}
@ -225,12 +255,9 @@ export class PouchdbPersistenceManager {
}
private async sendLocalDataToScene() {
let salt = null;
const clear = localStorage.getItem('clearLocal');
try {
const all = await this.db.allDocs({include_docs: true});
for (const dbEntity of all.rows) {
this._logger.debug(dbEntity.doc);
@ -265,8 +292,14 @@ export class PouchdbPersistenceManager {
switch (err.message) {
case 'WebCrypto_DecryptionFailure: ':
case 'Invalid data type!':
const promptPassword = new CustomEvent('promptpassword', {detail: 'Please enter password'});
document.dispatchEvent(promptPassword);
this._failCount++;
if (this._failCount < 5) {
const promptPassword = new CustomEvent('promptpassword', {detail: 'Please enter password'});
document.dispatchEvent(promptPassword);
} else {
this._logger.error('Too many decryption failures, Ignoring... This may compromise your data security');
window.alert('Too many decryption failures, Ignoring... This may compromise your data security');
}
}
this._logger.error(err);
}

View File

@ -94,15 +94,15 @@ export class Presence {
this._logger.warn('user not found', data.user);
const newUser = MeshBuilder.CreateDisc(data.user.id, {radius: 0.3}, scene);
const node = new TransformNode(data.netAddr, scene);
const material = new StandardMaterial(data.user.id + 'mat', scene);
material.diffuseColor = new Color3(0, 0, 1);
material.backFaceCulling = false;
newUser.material = material;
newUser.parent = node;
newUser.rotation.x = Math.PI / 2;
newUser.position.y = 0.01;
node.position = xyztovec(data.user.base.position);
node.rotation = xyztovec(data.user.base.rotation);
}
}
this._logger.debug('user update', data.user);

View File

@ -12,7 +12,7 @@ import {enumKeys} from "../../util/functions/enumKeys";
import {ToolType} from "../types/toolType";
import {buildTool} from "./buildTool";
export function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number, toolMap: Map<string, AbstractMesh>): Node {
export async function buildColor(color: Color3, scene: Scene, parent: TransformNode, index: number, toolMap: Map<string, AbstractMesh>): Promise<Node> {
const width = .1;
const height = .1;
const material = new StandardMaterial("material-" + color.toHexString(), scene);
@ -38,7 +38,7 @@ export function buildColor(color: Color3, scene: Scene, parent: TransformNode, i
let i = 0;
const tools = [];
for (const tool of enumKeys(ToolType)) {
const newItem = buildTool(ToolType[tool], colorBoxMesh, material);
const newItem = await buildTool(ToolType[tool], colorBoxMesh, material);
if (newItem) {
//buildColorPicker(scene, color, newItem, material, i, colorChangeObservable);
newItem.position = new Vector3(calculatePosition(++i), .1, 0);

View File

@ -1,11 +1,13 @@
import {ToolType} from "../types/toolType";
import {Mesh, MeshBuilder, Scene} from "@babylonjs/core";
import {Mesh, MeshBuilder, Scene, SceneLoader} from "@babylonjs/core";
import {DefaultScene} from "../../defaultScene";
const detail = {
tesselation: 16,
subdivisions: 5
}
export function buildMesh(type: ToolType, toolname: string, scene: Scene): Mesh {
export async function buildMesh(type: ToolType, toolname: string, scene: Scene): Promise<Mesh> {
switch (type) {
case ToolType.BOX:
return MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}, scene);
@ -34,7 +36,11 @@ export function buildMesh(type: ToolType, toolname: string, scene: Scene): Mesh
diameterBottom: 1,
tessellation: detail.tesselation
}, scene);
case ToolType.PERSON:
const result = await SceneLoader.ImportMeshAsync(null, '/assets/models/', 'person.stl', DefaultScene.Scene);
result.meshes[0].id = toolname;
result.meshes[0].name = toolname;
return result.meshes[0] as Mesh;
case ToolType.PLANE:
return MeshBuilder.CreatePlane(toolname, {width: 1, height: 1}, scene);

View File

@ -4,7 +4,7 @@ import {buildMesh} from "./buildMesh";
const WIDGET_SIZE = .1;
export function buildTool(tool: ToolType, colorParent: AbstractMesh, material: Material) {
export async function buildTool(tool: ToolType, colorParent: AbstractMesh, material: Material) {
let id = "ID";
let color = "#000000";
switch (material.getClassName()) {
@ -21,7 +21,7 @@ export function buildTool(tool: ToolType, colorParent: AbstractMesh, material: M
}
const newItem = buildMesh(tool, `tool-${id}`, colorParent.getScene());
const newItem = await buildMesh(tool, `tool-${id}`, colorParent.getScene());
if (!newItem) {
return null;
}

View File

@ -1,4 +1,4 @@
import {AbstractMesh, Color3, InstancedMesh, Node, Scene, TransformNode, Vector3} from "@babylonjs/core";
import {AbstractMesh, Color3, InstancedMesh, Node, Observable, Scene, TransformNode, Vector3} from "@babylonjs/core";
import {buildColor} from "./functions/buildColor";
import log from "loglevel";
import {Handle} from "../objects/handle";
@ -19,13 +19,16 @@ export class Toolbox {
private readonly _handle: Handle;
private readonly _scene: Scene;
constructor() {
constructor(readyObservable: Observable<boolean>) {
this._scene = DefaultScene.Scene;
this._toolboxBaseNode = new TransformNode("toolbox", this._scene);
this._handle = new Handle(this._toolboxBaseNode, 'Toolbox');
this._toolboxBaseNode.position.y = .2;
this._toolboxBaseNode.scaling = new Vector3(0.6, 0.6, 0.6);
this.buildToolbox();
this.buildToolbox().then(() => {
readyObservable.notifyObservers(true);
this._logger.info('Toolbox built');
});
Toolbox._instance = this;
}
private index = 0;
@ -46,9 +49,9 @@ export class Toolbox {
return this._tools.has(mesh.id);
}
private buildToolbox() {
private async buildToolbox() {
this.setupPointerObservable();
this.buildColorPicker();
await this.buildColorPicker();
if (this._toolboxBaseNode.parent) {
const platform = this._scene.getMeshById("platform");
if (platform) {
@ -94,10 +97,10 @@ export class Toolbox {
node.isEnabled(false) == true
};
private buildColorPicker() {
private async buildColorPicker() {
let initial = true;
for (const c of colors) {
const cnode = buildColor(Color3.FromHexString(c), this._scene, this._toolboxBaseNode, this.index++, this._tools);
const cnode = await buildColor(Color3.FromHexString(c), this._scene, this._toolboxBaseNode, this.index++, this._tools);
if (initial) {
initial = false;
for (const id of cnode.metadata.tools) {

View File

@ -5,4 +5,5 @@ export enum ToolType {
CONE = "#cone-template",
PLANE = "#plane-template",
OBJECT = "#object-template",
PERSON = "#person-template"
}

View File

@ -1,4 +1,4 @@
import {Color3, Engine, FreeCamera, Scene, Vector3, WebGPUEngine} from "@babylonjs/core";
import {Color3, Engine, FreeCamera, Observable, Scene, Vector3, WebGPUEngine} from "@babylonjs/core";
import '@babylonjs/loaders';
import {DiagramManager} from "./diagram/diagramManager";
import log, {Logger} from "loglevel";
@ -16,7 +16,7 @@ import {Introduction} from "./tutorial/introduction";
const webGpu = false;
log.setLevel('error', false);
log.setLevel('debug', false);
const canvas = (document.querySelector('#gameCanvas') as HTMLCanvasElement);
export class VrApp {
@ -30,7 +30,39 @@ export class VrApp {
});
}
public async initialize(scene: Scene) {
//const mesh = SceneLoader.ImportMesh(null, '/assets/models/', 'person.stl', DefaultScene.Scene);
setMainCamera(scene);
const spinner = new Spinner();
spinner.show();
const diagramReadyObservable = new Observable<boolean>();
const diagramManager = new DiagramManager(diagramReadyObservable);
diagramReadyObservable.add((ready) => {
if (ready) {
initDb(diagramManager);
} else {
this.logger.error('DiagramManager not ready');
}
});
initEnvironment(diagramManager, spinner);
const gamepadManager = new GamepadManager(scene);
addSceneInspector();
//const camMenu = new CameraMenu(scene);
const el = document.querySelector('#download');
if (el) {
el.addEventListener('click', () => {
exportGltf();
})
}
if (!localStorage.getItem('tutorialCompleted')) {
this.logger.info('Starting tutorial');
const intro = new Introduction();
}
this.logger.info('Render loop started');
}
private async initializeEngine() {
let engine: WebGPUEngine | Engine = null;
if (webGpu) {
engine = new WebGPUEngine(canvas);
@ -56,28 +88,6 @@ export class VrApp {
});
}
public async initialize(scene: Scene) {
setMainCamera(scene);
const spinner = new Spinner();
spinner.show();
const diagramManager = new DiagramManager();
await initDb(diagramManager);
initEnvironment(diagramManager, spinner);
const gamepadManager = new GamepadManager(scene);
addSceneInspector();
//const camMenu = new CameraMenu(scene);
const el = document.querySelector('#download');
if (el) {
el.addEventListener('click', () => {
exportGltf();
})
}
if (!localStorage.getItem('tutorialCompleted')) {
this.logger.info('Starting tutorial');
const intro = new Introduction();
}
this.logger.info('Render loop started');
}
}
@ -95,10 +105,7 @@ async function initDb(diagramManager: DiagramManager) {
const db = new PouchdbPersistenceManager();
//const userManager = new UserManager(db.onUserObservable);
db.setDiagramManager(diagramManager);
await db.initialize();
}
function initEnvironment(diagramManager: DiagramManager, spinner: Spinner) {