overte/scripts/system/create/modules/entityShapeVisualizer.js
2019-06-25 16:31:21 -07:00

257 lines
7.8 KiB
JavaScript

"use strict";
// entityShapeVisualizer.js
//
// Created by Thijs Wenker on 1/11/19
//
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var SHAPETYPE_TO_SHAPE = {
"box": "Cube",
"ellipsoid": "Sphere",
"cylinder-y": "Cylinder",
};
var REQUESTED_ENTITY_SHAPE_PROPERTIES = [
'type', 'shapeType', 'compoundShapeURL', 'localDimensions'
];
function getEntityShapePropertiesForType(properties) {
switch (properties.type) {
case "Zone":
if (SHAPETYPE_TO_SHAPE[properties.shapeType]) {
return {
type: "Shape",
shape: SHAPETYPE_TO_SHAPE[properties.shapeType],
localDimensions: properties.localDimensions
};
} else if (properties.shapeType === "compound") {
return {
type: "Model",
modelURL: properties.compoundShapeURL,
localDimensions: properties.localDimensions
};
} else if (properties.shapeType === "sphere") {
var sphereDiameter = Math.max(properties.localDimensions.x, properties.localDimensions.y,
properties.localDimensions.z);
return {
type: "Sphere",
modelURL: properties.compoundShapeURL,
localDimensions: {x: sphereDiameter, y: sphereDiameter, z: sphereDiameter}
};
}
break;
}
// Default properties
return {
type: "Shape",
shape: "Cube",
localDimensions: properties.localDimensions
};
}
function deepEqual(a, b) {
if (a === b) {
return true;
}
if (typeof(a) !== "object" || typeof(b) !== "object") {
return false;
}
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (var property in a) {
if (!a.hasOwnProperty(property)) {
continue;
}
if (!b.hasOwnProperty(property)) {
return false;
}
if (!deepEqual(a[property], b[property])) {
return false;
}
}
return true;
}
/**
* Returns an array of property names which are different in comparison.
* @param propertiesA
* @param propertiesB
* @returns {Array} - array of different property names
*/
function compareEntityProperties(propertiesA, propertiesB) {
var differentProperties = [],
property;
for (property in propertiesA) {
if (!propertiesA.hasOwnProperty(property)) {
continue;
}
if (!propertiesB.hasOwnProperty(property) || !deepEqual(propertiesA[property], propertiesB[property])) {
differentProperties.push(property);
}
}
for (property in propertiesB) {
if (!propertiesB.hasOwnProperty(property)) {
continue;
}
if (!propertiesA.hasOwnProperty(property)) {
differentProperties.push(property);
}
}
return differentProperties;
}
function deepCopy(v) {
return JSON.parse(JSON.stringify(v));
}
function EntityShape(entityID) {
this.entityID = entityID;
var propertiesForType = getEntityShapePropertiesForType(Entities.getEntityProperties(entityID, REQUESTED_ENTITY_SHAPE_PROPERTIES));
this.previousPropertiesForType = propertiesForType;
this.initialize(propertiesForType);
}
EntityShape.prototype = {
initialize: function(properties) {
// Create new instance of JS object:
var overlayProperties = deepCopy(properties);
overlayProperties.localPosition = Vec3.ZERO;
overlayProperties.localRotation = Quat.IDENTITY;
overlayProperties.canCastShadows = false;
overlayProperties.parentID = this.entityID;
overlayProperties.collisionless = true;
overlayProperties.ignorePickIntersection = true;
this.entity = Entities.addEntity(overlayProperties, "local");
var PROJECTED_MATERIALS = false;
this.materialEntity = Entities.addEntity({
type: "Material",
localPosition: Vec3.ZERO,
localRotation: Quat.IDENTITY,
localDimensions: properties.localDimensions,
parentID: this.entity,
priority: 1,
materialMappingMode: PROJECTED_MATERIALS ? "projected" : "uv",
materialURL: Script.resolvePath("../../assets/images/materials/GridPattern.json"),
ignorePickIntersection: true
}, "local");
},
update: function() {
var propertiesForType = getEntityShapePropertiesForType(Entities.getEntityProperties(this.entityID, REQUESTED_ENTITY_SHAPE_PROPERTIES));
var difference = compareEntityProperties(propertiesForType, this.previousPropertiesForType);
if (deepEqual(difference, ['localDimensions'])) {
this.previousPropertiesForType = propertiesForType;
Entities.editEntity(this.entity, {
localDimensions: propertiesForType.localDimensions,
});
} else if (difference.length > 0) {
this.previousPropertiesForType = propertiesForType;
this.clear();
this.initialize(propertiesForType);
}
},
clear: function() {
Entities.deleteEntity(this.materialEntity);
Entities.deleteEntity(this.entity);
}
};
function EntityShapeVisualizer(visualizedTypes) {
this.acceptedEntities = [];
this.ignoredEntities = [];
this.entityShapes = {};
this.visualizedTypes = visualizedTypes;
}
EntityShapeVisualizer.prototype = {
addEntity: function(entityID) {
if (this.entityShapes[entityID]) {
return;
}
this.entityShapes[entityID] = new EntityShape(entityID);
},
updateEntity: function(entityID) {
if (!this.entityShapes[entityID]) {
return;
}
this.entityShapes[entityID].update();
},
removeEntity: function(entityID) {
if (!this.entityShapes[entityID]) {
return;
}
this.entityShapes[entityID].clear();
delete this.entityShapes[entityID];
},
cleanup: function() {
Object.keys(this.entityShapes).forEach(function(entityID) {
this.entityShapes[entityID].clear();
}, this);
this.entityShapes = {};
},
setEntities: function(entities) {
var qualifiedEntities = entities.filter(function(entityID) {
if (this.acceptedEntities.indexOf(entityID) !== -1) {
return true;
}
if (this.ignoredEntities.indexOf(entityID) !== -1) {
return false;
}
if (this.visualizedTypes.indexOf(Entities.getEntityProperties(entityID, "type").type) !== -1) {
this.acceptedEntities.push(entityID);
return true;
}
this.ignoredEntities.push(entityID);
return false;
}, this);
var newEntries = [];
var updateEntries = [];
var currentEntries = Object.keys(this.entityShapes);
qualifiedEntities.forEach(function(entityID) {
if (currentEntries.indexOf(entityID) !== -1) {
updateEntries.push(entityID);
} else {
newEntries.push(entityID);
}
});
var deleteEntries = currentEntries.filter(function(entityID) {
return updateEntries.indexOf(entityID) === -1;
});
deleteEntries.forEach(function(entityID) {
this.removeEntity(entityID);
}, this);
updateEntries.forEach(function(entityID) {
this.updateEntity(entityID);
}, this);
newEntries.forEach(function(entityID) {
this.addEntity(entityID);
}, this);
}
};
module.exports = EntityShapeVisualizer;