mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-05-09 02:59:48 +02:00
merge upstream/master into andrew/isentropic
This commit is contained in:
commit
ba6e66f169
12 changed files with 118 additions and 465 deletions
|
@ -22,9 +22,7 @@ Script.include([
|
||||||
"libraries/progressDialog.js",
|
"libraries/progressDialog.js",
|
||||||
|
|
||||||
"libraries/entitySelectionTool.js",
|
"libraries/entitySelectionTool.js",
|
||||||
"libraries/ModelImporter.js",
|
|
||||||
|
|
||||||
"libraries/ExportMenu.js",
|
|
||||||
"libraries/ToolTip.js",
|
"libraries/ToolTip.js",
|
||||||
|
|
||||||
"libraries/entityPropertyDialogBox.js",
|
"libraries/entityPropertyDialogBox.js",
|
||||||
|
@ -35,7 +33,6 @@ Script.include([
|
||||||
|
|
||||||
var selectionDisplay = SelectionDisplay;
|
var selectionDisplay = SelectionDisplay;
|
||||||
var selectionManager = SelectionManager;
|
var selectionManager = SelectionManager;
|
||||||
var modelImporter = new ModelImporter();
|
|
||||||
var entityPropertyDialogBox = EntityPropertyDialogBox;
|
var entityPropertyDialogBox = EntityPropertyDialogBox;
|
||||||
|
|
||||||
var cameraManager = new CameraManager();
|
var cameraManager = new CameraManager();
|
||||||
|
@ -420,8 +417,6 @@ var toolBar = (function () {
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
|
||||||
var exportMenu = null;
|
|
||||||
|
|
||||||
function isLocked(properties) {
|
function isLocked(properties) {
|
||||||
// special case to lock the ground plane model in hq.
|
// special case to lock the ground plane model in hq.
|
||||||
if (location.hostname == "hq.highfidelity.io" &&
|
if (location.hostname == "hq.highfidelity.io" &&
|
||||||
|
@ -694,17 +689,16 @@ function setupModelMenus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Entity List...", shortcutKey: "CTRL+META+L", afterItem: "Models" });
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Entity List...", shortcutKey: "CTRL+META+L", afterItem: "Models" });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Entity List..." });
|
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
|
||||||
afterItem: "Paste Models", isCheckable: true, isChecked: true });
|
afterItem: "Entity List...", isCheckable: true, isChecked: true });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
|
||||||
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
|
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
||||||
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
||||||
|
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" });
|
||||||
|
|
||||||
|
|
||||||
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, afterItem: MENU_INSPECT_TOOL_ENABLED,
|
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, afterItem: MENU_INSPECT_TOOL_ENABLED,
|
||||||
|
@ -725,14 +719,13 @@ function cleanupModelMenus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.removeMenuItem("Edit", "Entity List...");
|
Menu.removeMenuItem("Edit", "Entity List...");
|
||||||
Menu.removeMenuItem("Edit", "Paste Models");
|
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
||||||
|
|
||||||
Menu.removeSeparator("File", "Models");
|
Menu.removeSeparator("File", "Models");
|
||||||
Menu.removeMenuItem("File", "Export Models");
|
Menu.removeMenuItem("File", "Export Entities");
|
||||||
Menu.removeMenuItem("File", "Import Models");
|
Menu.removeMenuItem("File", "Import Entities");
|
||||||
|
|
||||||
Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
|
Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
|
||||||
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
|
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
|
||||||
|
@ -747,11 +740,7 @@ Script.scriptEnding.connect(function() {
|
||||||
toolBar.cleanup();
|
toolBar.cleanup();
|
||||||
cleanupModelMenus();
|
cleanupModelMenus();
|
||||||
tooltip.cleanup();
|
tooltip.cleanup();
|
||||||
modelImporter.cleanup();
|
|
||||||
selectionDisplay.cleanup();
|
selectionDisplay.cleanup();
|
||||||
if (exportMenu) {
|
|
||||||
exportMenu.close();
|
|
||||||
}
|
|
||||||
Entities.setLightsArePickable(originalLightsArePickable);
|
Entities.setLightsArePickable(originalLightsArePickable);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -793,18 +782,41 @@ function handeMenuEvent(menuItem) {
|
||||||
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
|
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
|
||||||
} else if (menuItem == "Delete") {
|
} else if (menuItem == "Delete") {
|
||||||
deleteSelectedEntities();
|
deleteSelectedEntities();
|
||||||
} else if (menuItem == "Paste Models") {
|
} else if (menuItem == "Export Entities") {
|
||||||
modelImporter.paste();
|
if (!selectionManager.hasSelection()) {
|
||||||
} else if (menuItem == "Export Models") {
|
Window.alert("No entities have been selected.");
|
||||||
if (!exportMenu) {
|
} else {
|
||||||
exportMenu = new ExportMenu({
|
var filename = "models__" + Window.location.hostname + "__.svo";
|
||||||
onClose: function () {
|
filename = Window.save("Select where to save", filename, "*.svo")
|
||||||
exportMenu = null;
|
if (filename) {
|
||||||
|
var success = Clipboard.exportEntities(filename, selectionManager.selections);
|
||||||
|
if (!success) {
|
||||||
|
Window.alert("Export failed.");
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
} else if (menuItem == "Import Entities") {
|
||||||
|
var filename = Window.browse("Select models to import", "", "*.svo")
|
||||||
|
if (filename) {
|
||||||
|
var success = Clipboard.importEntities(filename);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE;
|
||||||
|
var direction = Quat.getFront(Camera.orientation);
|
||||||
|
var offset = Vec3.multiply(distance, direction);
|
||||||
|
var position = Vec3.sum(Camera.position, offset);
|
||||||
|
|
||||||
|
position.x = Math.max(0, position.x);
|
||||||
|
position.y = Math.max(0, position.y);
|
||||||
|
position.z = Math.max(0, position.z);
|
||||||
|
|
||||||
|
var pastedEntityIDs = Clipboard.pasteEntities(position);
|
||||||
|
|
||||||
|
selectionManager.setSelections(pastedEntityIDs);
|
||||||
|
} else {
|
||||||
|
Window.alert("There was an error importing the entity file.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (menuItem == "Import Models") {
|
|
||||||
modelImporter.doImport();
|
|
||||||
} else if (menuItem == "Entity List...") {
|
} else if (menuItem == "Entity List...") {
|
||||||
entityListTool.toggleVisible();
|
entityListTool.toggleVisible();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,224 +0,0 @@
|
||||||
//
|
|
||||||
// ExportMenu.js
|
|
||||||
// examples/libraries
|
|
||||||
//
|
|
||||||
// Copyright 2014 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
|
|
||||||
//
|
|
||||||
|
|
||||||
ExportMenu = function (opts) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var windowDimensions = Controller.getViewportDimensions();
|
|
||||||
var pos = { x: windowDimensions.x / 2, y: windowDimensions.y - 100 };
|
|
||||||
|
|
||||||
this._onClose = opts.onClose || function () { };
|
|
||||||
this._position = { x: 0.0, y: 0.0, z: 0.0 };
|
|
||||||
this._scale = 1.0;
|
|
||||||
|
|
||||||
var minScale = 1;
|
|
||||||
var maxScale = 32768;
|
|
||||||
var titleWidth = 120;
|
|
||||||
var locationWidth = 100;
|
|
||||||
var scaleWidth = 144;
|
|
||||||
var exportWidth = 100;
|
|
||||||
var cancelWidth = 100;
|
|
||||||
var margin = 4;
|
|
||||||
var height = 30;
|
|
||||||
var outerHeight = height + (2 * margin);
|
|
||||||
var buttonColor = { red: 128, green: 128, blue: 128 };
|
|
||||||
|
|
||||||
var SCALE_MINUS = scaleWidth * 40.0 / 100.0;
|
|
||||||
var SCALE_PLUS = scaleWidth * 63.0 / 100.0;
|
|
||||||
|
|
||||||
var fullWidth = locationWidth + scaleWidth + exportWidth + cancelWidth + (2 * margin);
|
|
||||||
var offset = fullWidth / 2;
|
|
||||||
pos.x -= offset;
|
|
||||||
|
|
||||||
var background = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x,
|
|
||||||
y: pos.y,
|
|
||||||
opacity: 1,
|
|
||||||
width: fullWidth,
|
|
||||||
height: outerHeight,
|
|
||||||
backgroundColor: { red: 200, green: 200, blue: 200 },
|
|
||||||
text: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
var titleText = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x,
|
|
||||||
y: pos.y - height,
|
|
||||||
font: { size: 14 },
|
|
||||||
width: titleWidth,
|
|
||||||
height: height,
|
|
||||||
backgroundColor: { red: 255, green: 255, blue: 255 },
|
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
|
||||||
text: "Export Models"
|
|
||||||
});
|
|
||||||
|
|
||||||
var locationButton = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x + margin,
|
|
||||||
y: pos.y + margin,
|
|
||||||
width: locationWidth,
|
|
||||||
height: height,
|
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
|
||||||
text: "0, 0, 0",
|
|
||||||
});
|
|
||||||
var scaleOverlay = Overlays.addOverlay("image", {
|
|
||||||
x: pos.x + margin + locationWidth,
|
|
||||||
y: pos.y + margin,
|
|
||||||
width: scaleWidth,
|
|
||||||
height: height,
|
|
||||||
subImage: { x: 0, y: 3, width: 144, height: height },
|
|
||||||
imageURL: toolIconUrl + "voxel-size-selector.svg",
|
|
||||||
alpha: 0.9,
|
|
||||||
});
|
|
||||||
var scaleViewWidth = 40;
|
|
||||||
var scaleView = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x + margin + locationWidth + SCALE_MINUS,
|
|
||||||
y: pos.y + margin,
|
|
||||||
width: scaleViewWidth,
|
|
||||||
height: height,
|
|
||||||
backgroundAlpha: 0.0,
|
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
|
||||||
text: "1"
|
|
||||||
});
|
|
||||||
var exportButton = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x + margin + locationWidth + scaleWidth,
|
|
||||||
y: pos.y + margin,
|
|
||||||
width: exportWidth,
|
|
||||||
height: height,
|
|
||||||
color: { red: 0, green: 255, blue: 255 },
|
|
||||||
text: "Export"
|
|
||||||
});
|
|
||||||
var cancelButton = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x + margin + locationWidth + scaleWidth + exportWidth,
|
|
||||||
y: pos.y + margin,
|
|
||||||
width: cancelWidth,
|
|
||||||
height: height,
|
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
|
||||||
text: "Cancel"
|
|
||||||
});
|
|
||||||
|
|
||||||
var voxelPreview = Overlays.addOverlay("cube", {
|
|
||||||
position: { x: 0, y: 0, z: 0 },
|
|
||||||
size: this._scale,
|
|
||||||
color: { red: 255, green: 255, blue: 0 },
|
|
||||||
alpha: 1,
|
|
||||||
solid: false,
|
|
||||||
visible: true,
|
|
||||||
lineWidth: 4
|
|
||||||
});
|
|
||||||
|
|
||||||
this.parsePosition = function (str) {
|
|
||||||
var parts = str.split(',');
|
|
||||||
if (parts.length == 3) {
|
|
||||||
var x = parseFloat(parts[0]);
|
|
||||||
var y = parseFloat(parts[1]);
|
|
||||||
var z = parseFloat(parts[2]);
|
|
||||||
if (isFinite(x) && isFinite(y) && isFinite(z)) {
|
|
||||||
return { x: x, y: y, z: z };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.showPositionPrompt = function () {
|
|
||||||
var positionStr = self._position.x + ", " + self._position.y + ", " + self._position.z;
|
|
||||||
while (1) {
|
|
||||||
positionStr = Window.prompt("Position to export form:", positionStr);
|
|
||||||
if (positionStr == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var position = self.parsePosition(positionStr);
|
|
||||||
if (position != null) {
|
|
||||||
self.setPosition(position.x, position.y, position.z);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Window.alert("The position you entered was invalid.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setScale = function (scale) {
|
|
||||||
self._scale = Math.min(maxScale, Math.max(minScale, scale));
|
|
||||||
Overlays.editOverlay(scaleView, { text: self._scale });
|
|
||||||
Overlays.editOverlay(voxelPreview, { size: self._scale });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.decreaseScale = function () {
|
|
||||||
self.setScale(self._scale /= 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.increaseScale = function () {
|
|
||||||
self.setScale(self._scale *= 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.exportEntities = function() {
|
|
||||||
var x = self._position.x;
|
|
||||||
var y = self._position.y;
|
|
||||||
var z = self._position.z;
|
|
||||||
var s = self._scale;
|
|
||||||
var filename = "models__" + Window.location.hostname + "__" + x + "_" + y + "_" + z + "_" + s + "__.svo";
|
|
||||||
filename = Window.save("Select where to save", filename, "*.svo")
|
|
||||||
if (filename) {
|
|
||||||
var success = Clipboard.exportEntities(filename, x, y, z, s);
|
|
||||||
if (!success) {
|
|
||||||
Window.alert("Export failed: no models found in selected area.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getPosition = function () {
|
|
||||||
return self._position;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setPosition = function (x, y, z) {
|
|
||||||
self._position = { x: x, y: y, z: z };
|
|
||||||
var positionStr = x + ", " + y + ", " + z;
|
|
||||||
Overlays.editOverlay(locationButton, { text: positionStr });
|
|
||||||
Overlays.editOverlay(voxelPreview, { position: self._position });
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
this.mouseReleaseEvent = function (event) {
|
|
||||||
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
|
||||||
|
|
||||||
if (clickedOverlay == locationButton) {
|
|
||||||
self.showPositionPrompt();
|
|
||||||
} else if (clickedOverlay == exportButton) {
|
|
||||||
self.exportEntities();
|
|
||||||
} else if (clickedOverlay == cancelButton) {
|
|
||||||
self.close();
|
|
||||||
} else if (clickedOverlay == scaleOverlay) {
|
|
||||||
var x = event.x - pos.x - margin - locationWidth;
|
|
||||||
print(x);
|
|
||||||
if (x < SCALE_MINUS) {
|
|
||||||
self.decreaseScale();
|
|
||||||
} else if (x > SCALE_PLUS) {
|
|
||||||
self.increaseScale();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.close = function () {
|
|
||||||
this.cleanup();
|
|
||||||
this._onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.cleanup = function () {
|
|
||||||
Overlays.deleteOverlay(background);
|
|
||||||
Overlays.deleteOverlay(titleText);
|
|
||||||
Overlays.deleteOverlay(locationButton);
|
|
||||||
Overlays.deleteOverlay(exportButton);
|
|
||||||
Overlays.deleteOverlay(cancelButton);
|
|
||||||
Overlays.deleteOverlay(voxelPreview);
|
|
||||||
Overlays.deleteOverlay(scaleOverlay);
|
|
||||||
Overlays.deleteOverlay(scaleView);
|
|
||||||
};
|
|
||||||
|
|
||||||
print("CONNECTING!");
|
|
||||||
Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent);
|
|
||||||
};
|
|
|
@ -1,200 +0,0 @@
|
||||||
//
|
|
||||||
// ModelImporter.js
|
|
||||||
// examples/libraries
|
|
||||||
//
|
|
||||||
// Copyright 2014 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
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
ModelImporter = function (opts) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var windowDimensions = Controller.getViewportDimensions();
|
|
||||||
|
|
||||||
var height = 30;
|
|
||||||
var margin = 4;
|
|
||||||
var outerHeight = height + (2 * margin);
|
|
||||||
var titleWidth = 120;
|
|
||||||
var cancelWidth = 100;
|
|
||||||
var fullWidth = titleWidth + cancelWidth + (2 * margin);
|
|
||||||
|
|
||||||
var localModels = Overlays.addOverlay("localmodels", {
|
|
||||||
position: { x: 1, y: 1, z: 1 },
|
|
||||||
scale: 1,
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
var importScale = 1;
|
|
||||||
var importBoundaries = Overlays.addOverlay("cube", {
|
|
||||||
position: { x: 0, y: 0, z: 0 },
|
|
||||||
size: 1,
|
|
||||||
color: { red: 128, blue: 128, green: 128 },
|
|
||||||
lineWidth: 4,
|
|
||||||
solid: false,
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
|
|
||||||
var pos = { x: windowDimensions.x / 2 - (fullWidth / 2), y: windowDimensions.y - 100 };
|
|
||||||
|
|
||||||
var background = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x,
|
|
||||||
y: pos.y,
|
|
||||||
opacity: 1,
|
|
||||||
width: fullWidth,
|
|
||||||
height: outerHeight,
|
|
||||||
backgroundColor: { red: 200, green: 200, blue: 200 },
|
|
||||||
visible: false,
|
|
||||||
text: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
var titleText = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x + margin,
|
|
||||||
y: pos.y + margin,
|
|
||||||
font: { size: 14 },
|
|
||||||
width: titleWidth,
|
|
||||||
height: height,
|
|
||||||
backgroundColor: { red: 255, green: 255, blue: 255 },
|
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
|
||||||
visible: false,
|
|
||||||
text: "Import Models"
|
|
||||||
});
|
|
||||||
var cancelButton = Overlays.addOverlay("text", {
|
|
||||||
x: pos.x + margin + titleWidth,
|
|
||||||
y: pos.y + margin,
|
|
||||||
width: cancelWidth,
|
|
||||||
height: height,
|
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
|
||||||
visible: false,
|
|
||||||
text: "Close"
|
|
||||||
});
|
|
||||||
this._importing = false;
|
|
||||||
|
|
||||||
this.setImportVisible = function (visible) {
|
|
||||||
Overlays.editOverlay(importBoundaries, { visible: visible });
|
|
||||||
Overlays.editOverlay(localModels, { visible: visible });
|
|
||||||
Overlays.editOverlay(cancelButton, { visible: visible });
|
|
||||||
Overlays.editOverlay(titleText, { visible: visible });
|
|
||||||
Overlays.editOverlay(background, { visible: visible });
|
|
||||||
};
|
|
||||||
|
|
||||||
var importPosition = { x: 0, y: 0, z: 0 };
|
|
||||||
this.moveImport = function (position) {
|
|
||||||
importPosition = position;
|
|
||||||
Overlays.editOverlay(localModels, {
|
|
||||||
position: { x: importPosition.x, y: importPosition.y, z: importPosition.z }
|
|
||||||
});
|
|
||||||
Overlays.editOverlay(importBoundaries, {
|
|
||||||
position: { x: importPosition.x, y: importPosition.y, z: importPosition.z }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mouseMoveEvent = function (event) {
|
|
||||||
if (self._importing) {
|
|
||||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
|
||||||
var intersection = Entities.findRayIntersection(pickRay);
|
|
||||||
|
|
||||||
var distance = 2;// * self._scale;
|
|
||||||
|
|
||||||
if (false) {//intersection.intersects) {
|
|
||||||
var intersectionDistance = Vec3.length(Vec3.subtract(pickRay.origin, intersection.intersection));
|
|
||||||
if (intersectionDistance < distance) {
|
|
||||||
distance = intersectionDistance * 0.99;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetPosition = {
|
|
||||||
x: pickRay.origin.x + (pickRay.direction.x * distance),
|
|
||||||
y: pickRay.origin.y + (pickRay.direction.y * distance),
|
|
||||||
z: pickRay.origin.z + (pickRay.direction.z * distance)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (targetPosition.x < 0) targetPosition.x = 0;
|
|
||||||
if (targetPosition.y < 0) targetPosition.y = 0;
|
|
||||||
if (targetPosition.z < 0) targetPosition.z = 0;
|
|
||||||
|
|
||||||
var nudgeFactor = 1;
|
|
||||||
var newPosition = {
|
|
||||||
x: Math.floor(targetPosition.x / nudgeFactor) * nudgeFactor,
|
|
||||||
y: Math.floor(targetPosition.y / nudgeFactor) * nudgeFactor,
|
|
||||||
z: Math.floor(targetPosition.z / nudgeFactor) * nudgeFactor
|
|
||||||
}
|
|
||||||
|
|
||||||
self.moveImport(newPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mouseReleaseEvent = function (event) {
|
|
||||||
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
|
||||||
|
|
||||||
if (clickedOverlay == cancelButton) {
|
|
||||||
self._importing = false;
|
|
||||||
self.setImportVisible(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Would prefer to use {4} for the coords, but it would only capture the last digit.
|
|
||||||
var fileRegex = /__(.+)__(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)_(\d+(?:\.\d+)?)__/;
|
|
||||||
this.doImport = function () {
|
|
||||||
if (!self._importing) {
|
|
||||||
var filename = Window.browse("Select models to import", "", "*.svo")
|
|
||||||
if (filename) {
|
|
||||||
parts = fileRegex.exec(filename);
|
|
||||||
if (parts == null) {
|
|
||||||
Window.alert("The file you selected does not contain source domain or location information");
|
|
||||||
} else {
|
|
||||||
var hostname = parts[1];
|
|
||||||
var x = parts[2];
|
|
||||||
var y = parts[3];
|
|
||||||
var z = parts[4];
|
|
||||||
var s = parts[5];
|
|
||||||
importScale = s;
|
|
||||||
if (hostname != location.hostname) {
|
|
||||||
if (!Window.confirm(("These models were not originally exported from this domain. Continue?"))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (Window.confirm(("Would you like to import back to the source location?"))) {
|
|
||||||
var success = Clipboard.importEntities(filename);
|
|
||||||
if (success) {
|
|
||||||
Clipboard.pasteEntities(x, y, z, 1);
|
|
||||||
} else {
|
|
||||||
Window.alert("There was an error importing the entity file.");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var success = Clipboard.importEntities(filename);
|
|
||||||
if (success) {
|
|
||||||
self._importing = true;
|
|
||||||
self.setImportVisible(true);
|
|
||||||
Overlays.editOverlay(importBoundaries, { size: s });
|
|
||||||
} else {
|
|
||||||
Window.alert("There was an error importing the entity file.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.paste = function () {
|
|
||||||
if (self._importing) {
|
|
||||||
// self._importing = false;
|
|
||||||
// self.setImportVisible(false);
|
|
||||||
Clipboard.pasteEntities(importPosition.x, importPosition.y, importPosition.z, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cleanup = function () {
|
|
||||||
Overlays.deleteOverlay(localModels);
|
|
||||||
Overlays.deleteOverlay(importBoundaries);
|
|
||||||
Overlays.deleteOverlay(cancelButton);
|
|
||||||
Overlays.deleteOverlay(titleText);
|
|
||||||
Overlays.deleteOverlay(background);
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent);
|
|
||||||
Controller.mouseMoveEvent.connect(this.mouseMoveEvent);
|
|
||||||
};
|
|
|
@ -772,13 +772,10 @@ void Application::paintGL() {
|
||||||
|
|
||||||
DependencyManager::get<GlowEffect>()->render();
|
DependencyManager::get<GlowEffect>()->render();
|
||||||
|
|
||||||
{
|
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
|
||||||
PerformanceTimer perfTimer("renderOverlay");
|
PerformanceTimer perfTimer("renderOverlay");
|
||||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
|
||||||
_applicationOverlay.renderOverlay(true);
|
_applicationOverlay.renderOverlay(true);
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
|
_applicationOverlay.displayOverlayTexture();
|
||||||
_applicationOverlay.displayOverlayTexture();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1633,6 +1630,47 @@ void Application::setActiveFaceTracker() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
|
||||||
|
QVector<EntityItem*> entities;
|
||||||
|
|
||||||
|
auto entityTree = _entities.getTree();
|
||||||
|
EntityTree exportTree;
|
||||||
|
|
||||||
|
glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||||
|
for (auto entityID : entityIDs) {
|
||||||
|
auto entityItem = entityTree->findEntityByEntityItemID(entityID);
|
||||||
|
if (!entityItem) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto properties = entityItem->getProperties();
|
||||||
|
auto position = properties.getPosition();
|
||||||
|
|
||||||
|
root.x = glm::min(root.x, position.x);
|
||||||
|
root.y = glm::min(root.y, position.y);
|
||||||
|
root.z = glm::min(root.z, position.z);
|
||||||
|
|
||||||
|
entities << entityItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entities.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto entityItem : entities) {
|
||||||
|
auto properties = entityItem->getProperties();
|
||||||
|
|
||||||
|
properties.setPosition(properties.getPosition() - root);
|
||||||
|
exportTree.addEntity(entityItem->getEntityItemID(), properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportTree.writeToSVOFile(filename.toLocal8Bit().constData());
|
||||||
|
|
||||||
|
// restore the main window's active state
|
||||||
|
_window->activateWindow();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
|
bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
|
||||||
QVector<EntityItem*> entities;
|
QVector<EntityItem*> entities;
|
||||||
_entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
|
_entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
|
||||||
|
@ -1682,8 +1720,8 @@ bool Application::importEntities(const QString& filename) {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::pasteEntities(float x, float y, float z) {
|
QVector<EntityItemID> Application::pasteEntities(float x, float y, float z) {
|
||||||
_entityClipboard.sendEntities(&_entityEditSender, _entities.getTree(), x, y, z);
|
return _entityClipboard.sendEntities(&_entityEditSender, _entities.getTree(), x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::initDisplay() {
|
void Application::initDisplay() {
|
||||||
|
|
|
@ -323,7 +323,8 @@ public slots:
|
||||||
void nodeKilled(SharedNodePointer node);
|
void nodeKilled(SharedNodePointer node);
|
||||||
void packetSent(quint64 length);
|
void packetSent(quint64 length);
|
||||||
|
|
||||||
void pasteEntities(float x, float y, float z);
|
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
||||||
|
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
|
||||||
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||||
bool importEntities(const QString& filename);
|
bool importEntities(const QString& filename);
|
||||||
|
|
||||||
|
|
|
@ -311,8 +311,18 @@ void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
||||||
}
|
}
|
||||||
|
|
||||||
// mixamo blendshapes
|
// mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will
|
||||||
if (!mapping.contains(BLENDSHAPE_FIELD) && geometry.applicationName == "mixamo.com") {
|
// be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file
|
||||||
|
bool likelyMixamoFile = geometry.applicationName == "mixamo.com" ||
|
||||||
|
(geometry.blendshapeChannelNames.contains("BrowsDown_Left") &&
|
||||||
|
geometry.blendshapeChannelNames.contains("BrowsDown_Right") &&
|
||||||
|
geometry.blendshapeChannelNames.contains("MouthOpen") &&
|
||||||
|
geometry.blendshapeChannelNames.contains("TongueUp") &&
|
||||||
|
geometry.blendshapeChannelNames.contains("MouthWhistle_NarrowAdjust_Left") &&
|
||||||
|
geometry.blendshapeChannelNames.contains("NoseScrunch_Left") &&
|
||||||
|
geometry.blendshapeChannelNames.contains("Squint_Right"));
|
||||||
|
|
||||||
|
if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) {
|
||||||
QVariantHash blendshapes;
|
QVariantHash blendshapes;
|
||||||
blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);
|
blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);
|
||||||
blendshapes.insertMulti("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0);
|
blendshapes.insertMulti("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0);
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
ClipboardScriptingInterface::ClipboardScriptingInterface() {
|
ClipboardScriptingInterface::ClipboardScriptingInterface() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
|
||||||
|
return Application::getInstance()->exportEntities(filename, entityIDs);
|
||||||
|
}
|
||||||
|
|
||||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
|
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
|
||||||
return Application::getInstance()->exportEntities(filename, x, y, z, s);
|
return Application::getInstance()->exportEntities(filename, x, y, z, s);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +26,6 @@ bool ClipboardScriptingInterface::importEntities(const QString& filename) {
|
||||||
return Application::getInstance()->importEntities(filename);
|
return Application::getInstance()->importEntities(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClipboardScriptingInterface::pasteEntities(float x, float y, float z, float s) {
|
QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position) {
|
||||||
Application::getInstance()->pasteEntities(x, y, z);
|
return Application::getInstance()->pasteEntities(position.x, position.y, position.z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,9 @@ signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool importEntities(const QString& filename);
|
bool importEntities(const QString& filename);
|
||||||
|
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
|
||||||
bool exportEntities(const QString& filename, float x, float y, float z, float s);
|
bool exportEntities(const QString& filename, float x, float y, float z, float s);
|
||||||
void pasteEntities(float x, float y, float z, float s);
|
QVector<EntityItemID> pasteEntities(glm::vec3 position);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ClipboardScriptingInterface_h
|
#endif // hifi_ClipboardScriptingInterface_h
|
||||||
|
|
|
@ -997,13 +997,17 @@ void EntityTree::pruneTree() {
|
||||||
recurseTreeWithOperator(&theOperator);
|
recurseTreeWithOperator(&theOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z) {
|
QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z) {
|
||||||
SendEntitiesOperationArgs args;
|
SendEntitiesOperationArgs args;
|
||||||
args.packetSender = packetSender;
|
args.packetSender = packetSender;
|
||||||
args.localTree = localTree;
|
args.localTree = localTree;
|
||||||
args.root = glm::vec3(x, y, z);
|
args.root = glm::vec3(x, y, z);
|
||||||
|
QVector<EntityItemID> newEntityIDs;
|
||||||
|
args.newEntityIDs = &newEntityIDs;
|
||||||
recurseTreeWithOperation(sendEntitiesOperation, &args);
|
recurseTreeWithOperation(sendEntitiesOperation, &args);
|
||||||
packetSender->releaseQueuedMessages();
|
packetSender->releaseQueuedMessages();
|
||||||
|
|
||||||
|
return newEntityIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) {
|
bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) {
|
||||||
|
@ -1013,6 +1017,7 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
|
||||||
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
|
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
|
||||||
for (int i = 0; i < entities.size(); i++) {
|
for (int i = 0; i < entities.size(); i++) {
|
||||||
EntityItemID newID(NEW_ENTITY, EntityItemID::getNextCreatorTokenID(), false);
|
EntityItemID newID(NEW_ENTITY, EntityItemID::getNextCreatorTokenID(), false);
|
||||||
|
args->newEntityIDs->append(newID);
|
||||||
EntityItemProperties properties = entities[i]->getProperties();
|
EntityItemProperties properties = entities[i]->getProperties();
|
||||||
properties.setPosition(properties.getPosition() + args->root);
|
properties.setPosition(properties.getPosition() + args->root);
|
||||||
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
|
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
|
||||||
|
|
|
@ -39,6 +39,7 @@ public:
|
||||||
glm::vec3 root;
|
glm::vec3 root;
|
||||||
EntityTree* localTree;
|
EntityTree* localTree;
|
||||||
EntityEditPacketSender* packetSender;
|
EntityEditPacketSender* packetSender;
|
||||||
|
QVector<EntityItemID>* newEntityIDs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,7 +147,7 @@ public:
|
||||||
virtual void dumpTree();
|
virtual void dumpTree();
|
||||||
virtual void pruneTree();
|
virtual void pruneTree();
|
||||||
|
|
||||||
void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
|
QVector<EntityItemID> sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
|
||||||
|
|
||||||
void entityChanged(EntityItem* entity);
|
void entityChanged(EntityItem* entity);
|
||||||
|
|
||||||
|
|
|
@ -1266,6 +1266,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
|
||||||
QVector<QString> humanIKJointIDs(humanIKJointNames.size());
|
QVector<QString> humanIKJointIDs(humanIKJointNames.size());
|
||||||
|
|
||||||
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
|
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
|
||||||
|
|
||||||
QMultiHash<QByteArray, WeightedIndex> blendshapeIndices;
|
QMultiHash<QByteArray, WeightedIndex> blendshapeIndices;
|
||||||
for (int i = 0;; i++) {
|
for (int i = 0;; i++) {
|
||||||
QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i];
|
QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i];
|
||||||
|
@ -1720,12 +1721,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
|
||||||
|
|
||||||
} else if (object.properties.last() == "BlendShapeChannel") {
|
} else if (object.properties.last() == "BlendShapeChannel") {
|
||||||
QByteArray name = object.properties.at(1).toByteArray();
|
QByteArray name = object.properties.at(1).toByteArray();
|
||||||
|
|
||||||
name = name.left(name.indexOf('\0'));
|
name = name.left(name.indexOf('\0'));
|
||||||
if (!blendshapeIndices.contains(name)) {
|
if (!blendshapeIndices.contains(name)) {
|
||||||
// try everything after the dot
|
// try everything after the dot
|
||||||
name = name.mid(name.lastIndexOf('.') + 1);
|
name = name.mid(name.lastIndexOf('.') + 1);
|
||||||
}
|
}
|
||||||
QString id = getID(object.properties);
|
QString id = getID(object.properties);
|
||||||
|
geometry.blendshapeChannelNames << name;
|
||||||
foreach (const WeightedIndex& index, blendshapeIndices.values(name)) {
|
foreach (const WeightedIndex& index, blendshapeIndices.values(name)) {
|
||||||
blendshapeChannelIndices.insert(id, index);
|
blendshapeChannelIndices.insert(id, index);
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,6 +257,8 @@ public:
|
||||||
|
|
||||||
/// given a meshIndex this will return the name of the model that mesh belongs to if known
|
/// given a meshIndex this will return the name of the model that mesh belongs to if known
|
||||||
QString getModelNameOfMesh(int meshIndex) const;
|
QString getModelNameOfMesh(int meshIndex) const;
|
||||||
|
|
||||||
|
QList<QString> blendshapeChannelNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(FBXGeometry)
|
Q_DECLARE_METATYPE(FBXGeometry)
|
||||||
|
|
Loading…
Reference in a new issue