mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 17:52:26 +02:00
Merge remote-tracking branch 'upstream/master' into oculus_old_renderer
This commit is contained in:
commit
320cfa7c9e
87 changed files with 1754 additions and 1010 deletions
|
@ -183,4 +183,4 @@ endif ()
|
|||
|
||||
if (ANDROID OR DESKTOP_GVR)
|
||||
add_subdirectory(gvr-interface)
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
|
@ -92,7 +92,6 @@ function checkControllerSide(whichSide) {
|
|||
Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0]));
|
||||
linearVelocity = averageLinearVelocity[0];
|
||||
angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(LEFT_TIP));
|
||||
angularVelocity = Vec3.multiply(180.0 / Math.PI, angularVelocity);
|
||||
} else {
|
||||
BUTTON_FWD = RIGHT_BUTTON_FWD;
|
||||
BUTTON_3 = RIGHT_BUTTON_3;
|
||||
|
@ -104,7 +103,6 @@ function checkControllerSide(whichSide) {
|
|||
Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1]));
|
||||
linearVelocity = averageLinearVelocity[1];
|
||||
angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP));
|
||||
angularVelocity = Vec3.multiply(180.0 / Math.PI, angularVelocity);
|
||||
handMessage = "RIGHT";
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,9 @@ function shootDice(position, velocity) {
|
|||
position: position,
|
||||
velocity: velocity,
|
||||
rotation: Quat.fromPitchYawRollDegrees(Math.random() * 360, Math.random() * 360, Math.random() * 360),
|
||||
angularVelocity: { x: Math.random() * 100, y: Math.random() * 100, z: Math.random() * 100 },
|
||||
// NOTE: angularVelocity is in radians/sec
|
||||
var maxAngularSpeed = Math.PI;
|
||||
angularVelocity: { x: Math.random() * maxAngularSpeed, y: Math.random() * maxAngularSpeed, z: Math.random() * maxAngularSpeed },
|
||||
lifetime: LIFETIME,
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
shapeType: "box",
|
||||
|
@ -108,4 +110,4 @@ function scriptEnding() {
|
|||
|
||||
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
|
@ -22,9 +22,7 @@ Script.include([
|
|||
"libraries/progressDialog.js",
|
||||
|
||||
"libraries/entitySelectionTool.js",
|
||||
"libraries/ModelImporter.js",
|
||||
|
||||
"libraries/ExportMenu.js",
|
||||
"libraries/ToolTip.js",
|
||||
|
||||
"libraries/entityPropertyDialogBox.js",
|
||||
|
@ -35,7 +33,6 @@ Script.include([
|
|||
|
||||
var selectionDisplay = SelectionDisplay;
|
||||
var selectionManager = SelectionManager;
|
||||
var modelImporter = new ModelImporter();
|
||||
var entityPropertyDialogBox = EntityPropertyDialogBox;
|
||||
|
||||
var cameraManager = new CameraManager();
|
||||
|
@ -420,8 +417,6 @@ var toolBar = (function () {
|
|||
}());
|
||||
|
||||
|
||||
var exportMenu = null;
|
||||
|
||||
function isLocked(properties) {
|
||||
// special case to lock the ground plane model in hq.
|
||||
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: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Entity List..." });
|
||||
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",
|
||||
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
||||
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
||||
|
||||
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: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "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,
|
||||
|
@ -725,14 +719,13 @@ function cleanupModelMenus() {
|
|||
}
|
||||
|
||||
Menu.removeMenuItem("Edit", "Entity List...");
|
||||
Menu.removeMenuItem("Edit", "Paste Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
||||
|
||||
Menu.removeSeparator("File", "Models");
|
||||
Menu.removeMenuItem("File", "Export Models");
|
||||
Menu.removeMenuItem("File", "Import Models");
|
||||
Menu.removeMenuItem("File", "Export Entities");
|
||||
Menu.removeMenuItem("File", "Import Entities");
|
||||
|
||||
Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
|
||||
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
|
||||
|
@ -747,11 +740,7 @@ Script.scriptEnding.connect(function() {
|
|||
toolBar.cleanup();
|
||||
cleanupModelMenus();
|
||||
tooltip.cleanup();
|
||||
modelImporter.cleanup();
|
||||
selectionDisplay.cleanup();
|
||||
if (exportMenu) {
|
||||
exportMenu.close();
|
||||
}
|
||||
Entities.setLightsArePickable(originalLightsArePickable);
|
||||
});
|
||||
|
||||
|
@ -793,18 +782,41 @@ function handeMenuEvent(menuItem) {
|
|||
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
|
||||
} else if (menuItem == "Delete") {
|
||||
deleteSelectedEntities();
|
||||
} else if (menuItem == "Paste Models") {
|
||||
modelImporter.paste();
|
||||
} else if (menuItem == "Export Models") {
|
||||
if (!exportMenu) {
|
||||
exportMenu = new ExportMenu({
|
||||
onClose: function () {
|
||||
exportMenu = null;
|
||||
} else if (menuItem == "Export Entities") {
|
||||
if (!selectionManager.hasSelection()) {
|
||||
Window.alert("No entities have been selected.");
|
||||
} else {
|
||||
var filename = "models__" + Window.location.hostname + "__.svo";
|
||||
filename = Window.save("Select where to save", filename, "*.svo")
|
||||
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...") {
|
||||
entityListTool.toggleVisible();
|
||||
}
|
||||
|
|
|
@ -11,11 +11,13 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
|
||||
var lightProperties = {
|
||||
type: "Light",
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
dimensions: { x: 1000, y: 1000, z: 1000 },
|
||||
angularVelocity: { x: 0, y: 10, z: 0 },
|
||||
angularVelocity: { x: 0, y: 10 * DEGREES_TO_RADIANS, z: 0 },
|
||||
angularDamping: 0,
|
||||
|
||||
isSpotlight: true,
|
||||
|
|
16
examples/hmdDefaults.js
Normal file
16
examples/hmdDefaults.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// hmdDefaults.js
|
||||
// examples
|
||||
//
|
||||
// Created by David Rowe on 6 Mar 2015.
|
||||
// Copyright 2015 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
|
||||
//
|
||||
|
||||
Script.load("progress.js");
|
||||
Script.load("lobby.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("controllers/oculus/goTo.js");
|
||||
//Script.load("scripts.js"); // Not created yet
|
|
@ -2,6 +2,10 @@
|
|||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<script>
|
||||
var PI = 3.14159265358979;
|
||||
var DEGREES_TO_RADIANS = PI / 180.0;
|
||||
var RADIANS_TO_DEGREES = 180.0 / PI;
|
||||
|
||||
function enableChildren(el, selector) {
|
||||
els = el.querySelectorAll(selector);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
|
@ -58,6 +62,22 @@
|
|||
}
|
||||
};
|
||||
|
||||
function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
|
||||
return function() {
|
||||
var data = {
|
||||
type: "update",
|
||||
properties: {
|
||||
}
|
||||
};
|
||||
data.properties[property] = {
|
||||
x: elX.value * multiplier,
|
||||
y: elY.value * multiplier,
|
||||
z: elZ.value * multiplier,
|
||||
};
|
||||
EventBridge.emitWebEvent(JSON.stringify(data));
|
||||
}
|
||||
};
|
||||
|
||||
function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) {
|
||||
return function() {
|
||||
var data = {
|
||||
|
@ -227,9 +247,9 @@
|
|||
elLinearVelocityZ.value = properties.velocity.z.toFixed(2);
|
||||
elLinearDamping.value = properties.damping.toFixed(2);
|
||||
|
||||
elAngularVelocityX.value = properties.angularVelocity.x.toFixed(2);
|
||||
elAngularVelocityY.value = properties.angularVelocity.y.toFixed(2);
|
||||
elAngularVelocityZ.value = properties.angularVelocity.z.toFixed(2);
|
||||
elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(2);
|
||||
elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(2);
|
||||
elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(2);
|
||||
elAngularDamping.value = properties.angularDamping.toFixed(2);
|
||||
|
||||
elGravityX.value = properties.gravity.x.toFixed(2);
|
||||
|
@ -352,8 +372,8 @@
|
|||
elLinearVelocityZ.addEventListener('change', velocityChangeFunction);
|
||||
elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping'));
|
||||
|
||||
var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunction(
|
||||
'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ);
|
||||
var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier(
|
||||
'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS);
|
||||
elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction);
|
||||
elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction);
|
||||
elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction);
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -11,6 +11,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
var RADIANS_TO_DEGREES = 180.0 / Math.PI;
|
||||
|
||||
EntityPropertyDialogBox = (function () {
|
||||
var that = {};
|
||||
|
||||
|
@ -146,11 +149,12 @@ EntityPropertyDialogBox = (function () {
|
|||
index++;
|
||||
array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Pitch:", value: properties.angularVelocity.x.toFixed(decimals) });
|
||||
// NOTE: angular velocity is in radians/sec but we display degrees/sec for users
|
||||
array.push({ label: "Angular Pitch:", value: (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Yaw:", value: properties.angularVelocity.y.toFixed(decimals) });
|
||||
array.push({ label: "Angular Yaw:", value: (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Roll:", value: properties.angularVelocity.z.toFixed(decimals) });
|
||||
array.push({ label: "Angular Roll:", value: (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Damping:", value: properties.angularDamping.toFixed(decimals) });
|
||||
index++;
|
||||
|
@ -343,9 +347,10 @@ EntityPropertyDialogBox = (function () {
|
|||
properties.velocity.z = array[index++].value;
|
||||
properties.damping = array[index++].value;
|
||||
|
||||
properties.angularVelocity.x = array[index++].value;
|
||||
properties.angularVelocity.y = array[index++].value;
|
||||
properties.angularVelocity.z = array[index++].value;
|
||||
// NOTE: angular velocity is in radians/sec but we display degrees/sec for users
|
||||
properties.angularVelocity.x = array[index++].value * DEGREES_TO_RADIANS;
|
||||
properties.angularVelocity.y = array[index++].value * DEGREES_TO_RADIANS;
|
||||
properties.angularVelocity.z = array[index++].value * DEGREES_TO_RADIANS;
|
||||
properties.angularDamping = array[index++].value;
|
||||
|
||||
properties.gravity.x = array[index++].value;
|
||||
|
|
|
@ -35,13 +35,15 @@ var NUM_INITIAL_PARTICLES = 200;
|
|||
var PARTICLE_MIN_SIZE = 0.50;
|
||||
var PARTICLE_MAX_SIZE = 1.50;
|
||||
var INITIAL_VELOCITY = 5.0;
|
||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
|
||||
var planets = [];
|
||||
var particles = [];
|
||||
|
||||
// Create planets that will extert gravity on test particles
|
||||
for (var i = 0; i < planetTypes.length; i++) {
|
||||
var rotationalVelocity = 10 + Math.random() * 60;
|
||||
// NOTE: rotationalVelocity is in radians/sec
|
||||
var rotationalVelocity = (10 + Math.random() * 60) * DEGREES_TO_RADIANS;
|
||||
var position = { x: planetTypes[i].x, y: planetTypes[i].y, z: planetTypes[i].z };
|
||||
position = Vec3.multiply(MAX_RANGE / 2, position);
|
||||
position = Vec3.sum(center, position);
|
||||
|
@ -118,4 +120,4 @@ function update(deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
|
@ -20,6 +20,8 @@ var GRAVITY = -1.0;
|
|||
var LIFETIME = 600;
|
||||
var DAMPING = 0.50;
|
||||
|
||||
var TWO_PI = 2.0 * Math.PI;
|
||||
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(SCALE * 3.0, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var floor = Entities.addEntity(
|
||||
|
@ -122,7 +124,8 @@ var spinner = Entities.addEntity(
|
|||
position: center,
|
||||
dimensions: { x: SCALE / 1.5, y: SCALE / 3.0, z: SCALE / 8.0 },
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
angularVelocity: { x: 0, y: 360, z: 0 },
|
||||
// NOTE: angularVelocity is in radians/sec
|
||||
angularVelocity: { x: 0, y: TWO_PI, z: 0 },
|
||||
angularDamping: 0.0,
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
|
@ -179,4 +182,4 @@ function scriptEnding() {
|
|||
}
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.update.connect(update);
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -849,4 +849,4 @@ function animateAvatar(deltaTime, speed) {
|
|||
MyAvatar.setJointData(jointName, Quat.fromVec3Degrees(jointRotations));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@
|
|||
devour all we've written and make<br>
|
||||
suggestions where necessary.<br>
|
||||
Documentation is always at<br>
|
||||
<a href="http://docs.highfidelity.io/"><b>docs.highfidelity.io</b></a>
|
||||
<a href="http://docs.highfidelity.com/"><b>docs.highfidelity.com</b></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid-unit padding-block"></div>
|
||||
|
@ -187,4 +187,4 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -141,7 +141,7 @@ using namespace std;
|
|||
static unsigned STARFIELD_NUM_STARS = 50000;
|
||||
static unsigned STARFIELD_SEED = 1;
|
||||
|
||||
const qint64 MAXIMUM_CACHE_SIZE = 10737418240; // 10GB
|
||||
const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB
|
||||
|
||||
static QTimer* locationUpdateTimer = NULL;
|
||||
static QTimer* balanceUpdateTimer = NULL;
|
||||
|
@ -151,7 +151,7 @@ static QTimer* billboardPacketTimer = NULL;
|
|||
static QTimer* checkFPStimer = NULL;
|
||||
static QTimer* idleTimer = NULL;
|
||||
|
||||
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
|
||||
const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml";
|
||||
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
||||
|
||||
const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
|
||||
|
@ -445,7 +445,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
|
||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
||||
networkAccessManager.setCache(cache);
|
||||
|
||||
|
||||
ResourceCache::setRequestLimit(3);
|
||||
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
@ -490,6 +490,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
connect(nodeList.data(), SIGNAL(dataReceived(const quint8, const int)),
|
||||
bandwidthRecorder.data(), SLOT(updateInboundData(const quint8, const int)));
|
||||
|
||||
connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
|
||||
this, &Application::checkSkeleton, Qt::QueuedConnection);
|
||||
|
||||
// check first run...
|
||||
if (_firstRun.get()) {
|
||||
qDebug() << "This is a first run...";
|
||||
|
@ -772,13 +775,10 @@ void Application::paintGL() {
|
|||
|
||||
DependencyManager::get<GlowEffect>()->render();
|
||||
|
||||
{
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
|
||||
PerformanceTimer perfTimer("renderOverlay");
|
||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
||||
_applicationOverlay.renderOverlay(true);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
|
||||
_applicationOverlay.displayOverlayTexture();
|
||||
}
|
||||
_applicationOverlay.displayOverlayTexture();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1633,10 +1633,50 @@ void Application::setActiveFaceTracker() {
|
|||
#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) {
|
||||
QVector<EntityItem*> entities;
|
||||
_entities.getTree()->findEntities(AACube(glm::vec3(x / (float)TREE_SCALE,
|
||||
y / (float)TREE_SCALE, z / (float)TREE_SCALE), scale / (float)TREE_SCALE), entities);
|
||||
_entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
|
||||
|
||||
if (entities.size() > 0) {
|
||||
glm::vec3 root(x, y, z);
|
||||
|
@ -1683,8 +1723,8 @@ bool Application::importEntities(const QString& filename) {
|
|||
return success;
|
||||
}
|
||||
|
||||
void Application::pasteEntities(float x, float y, float z) {
|
||||
_entityClipboard.sendEntities(&_entityEditSender, _entities.getTree(), x, y, z);
|
||||
QVector<EntityItemID> Application::pasteEntities(float x, float y, float z) {
|
||||
return _entityClipboard.sendEntities(&_entityEditSender, _entities.getTree(), x, y, z);
|
||||
}
|
||||
|
||||
void Application::initDisplay() {
|
||||
|
@ -2263,7 +2303,6 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
serverBounds.scale(TREE_SCALE);
|
||||
|
||||
ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
|
||||
|
||||
|
@ -2327,7 +2366,6 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
serverBounds.scale(TREE_SCALE);
|
||||
|
||||
ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
|
||||
if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
|
||||
|
@ -4006,3 +4044,19 @@ void Application::notifyPacketVersionMismatch() {
|
|||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::checkSkeleton() {
|
||||
if (_myAvatar->getSkeletonModel().isActive() && !_myAvatar->getSkeletonModel().hasSkeleton()) {
|
||||
qDebug() << "MyAvatar model has no skeleton";
|
||||
|
||||
QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded...";
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(message);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.exec();
|
||||
|
||||
_myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL);
|
||||
_myAvatar->sendIdentityPacket();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -323,7 +323,8 @@ public slots:
|
|||
void nodeKilled(SharedNodePointer node);
|
||||
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 importEntities(const QString& filename);
|
||||
|
||||
|
@ -584,6 +585,8 @@ private:
|
|||
QTimer _settingsTimer;
|
||||
|
||||
GLCanvas* _glWidget = new GLCanvas(); // our GLCanvas has a couple extra features
|
||||
|
||||
void checkSkeleton();
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -436,6 +436,8 @@ Menu::Menu() {
|
|||
SLOT(disable(bool)));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0,
|
||||
dialogsManager.data(), SLOT(cachesSizeDialog()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
|
||||
QMenu* timingMenu = developerMenu->addMenu("Timing and Stats");
|
||||
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
|
|
|
@ -125,7 +125,7 @@ namespace MenuOption {
|
|||
const QString BookmarkLocation = "Bookmark Location";
|
||||
const QString Bookmarks = "Bookmarks";
|
||||
const QString CascadedShadows = "Cascaded";
|
||||
const QString CachesSize = "Caches Size";
|
||||
const QString CachesSize = "RAM Caches Size";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ChatCircling = "Chat Circling";
|
||||
const QString CollideAsRagdoll = "Collide With Self (Ragdoll)";
|
||||
|
@ -143,6 +143,7 @@ namespace MenuOption {
|
|||
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
||||
const QString DisableLightEntities = "Disable Light Entities";
|
||||
const QString DisableNackPackets = "Disable NACK Packets";
|
||||
const QString DiskCacheEditor = "Disk Cache Editor";
|
||||
const QString DisplayHands = "Show Hand Info";
|
||||
const QString DisplayHandTargets = "Show Hand Targets";
|
||||
const QString DisplayModelBounds = "Display Model Bounds";
|
||||
|
|
|
@ -177,6 +177,24 @@ bool ModelUploader::zip() {
|
|||
}
|
||||
QByteArray fbxContents = fbx.readAll();
|
||||
FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
|
||||
|
||||
#if 0 /// Temporarily remove this check until CtrlAltDavid can come up with a fix.
|
||||
// Make sure that a skeleton model has a skeleton
|
||||
if (_modelType == SKELETON_MODEL) {
|
||||
if (geometry.rootJointIndex == -1) {
|
||||
|
||||
QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled.";
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Model Upload");
|
||||
msgBox.setText(message);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// make sure we have some basic mappings
|
||||
populateBasicMapping(mapping, filename, geometry);
|
||||
|
@ -311,8 +329,20 @@ void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename
|
|||
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
||||
}
|
||||
|
||||
// mixamo blendshapes
|
||||
if (!mapping.contains(BLENDSHAPE_FIELD) && geometry.applicationName == "mixamo.com") {
|
||||
// mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will
|
||||
// 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("Facial_Blends") &&
|
||||
geometry.blendshapeChannelNames.contains("BrowsDown_Right") &&
|
||||
geometry.blendshapeChannelNames.contains("MouthOpen") &&
|
||||
geometry.blendshapeChannelNames.contains("Blink_Left") &&
|
||||
geometry.blendshapeChannelNames.contains("Blink_Right") &&
|
||||
geometry.blendshapeChannelNames.contains("Squint_Right"));
|
||||
|
||||
qDebug() << "likelyMixamoFile:" << likelyMixamoFile;
|
||||
qDebug() << "geometry.blendshapeChannelNames:" << geometry.blendshapeChannelNames;
|
||||
|
||||
if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) {
|
||||
QVariantHash blendshapes;
|
||||
blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);
|
||||
blendshapes.insertMulti("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0);
|
||||
|
|
|
@ -21,7 +21,6 @@ ModelReferential::ModelReferential(Referential* referential, EntityTree* tree, A
|
|||
{
|
||||
_translation = referential->getTranslation();
|
||||
_rotation = referential->getRotation();
|
||||
_scale = referential->getScale();
|
||||
unpackExtraData(reinterpret_cast<unsigned char*>(referential->getExtraData().data()),
|
||||
referential->getExtraData().size());
|
||||
|
||||
|
@ -32,9 +31,9 @@ ModelReferential::ModelReferential(Referential* referential, EntityTree* tree, A
|
|||
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
if (item != NULL) {
|
||||
_refScale = item->getLargestDimension();
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_refRotation = item->getRotation();
|
||||
_refPosition = item->getPosition() * (float)TREE_SCALE;
|
||||
_refPosition = item->getPosition();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -51,14 +50,13 @@ ModelReferential::ModelReferential(const QUuid& entityID, EntityTree* tree, Avat
|
|||
return;
|
||||
}
|
||||
|
||||
_refScale = item->getLargestDimension();
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_refRotation = item->getRotation();
|
||||
_refPosition = item->getPosition() * (float)TREE_SCALE;
|
||||
_refPosition = item->getPosition();
|
||||
|
||||
glm::quat refInvRot = glm::inverse(_refRotation);
|
||||
_scale = _avatar->getTargetScale() / _refScale;
|
||||
_rotation = refInvRot * _avatar->getOrientation();
|
||||
_translation = refInvRot * (avatar->getPosition() - _refPosition) / _refScale;
|
||||
_translation = refInvRot * (avatar->getPosition() - _refPosition);
|
||||
}
|
||||
|
||||
void ModelReferential::update() {
|
||||
|
@ -68,9 +66,10 @@ void ModelReferential::update() {
|
|||
}
|
||||
|
||||
bool somethingChanged = false;
|
||||
if (item->getLargestDimension() != _refScale) {
|
||||
_refScale = item->getLargestDimension();
|
||||
_avatar->setTargetScale(_refScale * _scale, true);
|
||||
if (item->getDimensions() != _lastRefDimension) {
|
||||
glm::vec3 oldDimension = _lastRefDimension;
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_translation *= _lastRefDimension / oldDimension;
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (item->getRotation() != _refRotation) {
|
||||
|
@ -80,7 +79,7 @@ void ModelReferential::update() {
|
|||
}
|
||||
if (item->getPosition() != _refPosition || somethingChanged) {
|
||||
_refPosition = item->getPosition();
|
||||
_avatar->setPosition(_refPosition * (float)TREE_SCALE + _refRotation * (_translation * _refScale), true);
|
||||
_avatar->setPosition(_refPosition + _refRotation * _translation, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +107,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
|
|||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
|
||||
_refScale = item->getLargestDimension();
|
||||
_lastRefDimension = item->getDimensions();
|
||||
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
}
|
||||
|
@ -128,14 +127,14 @@ JointReferential::JointReferential(uint32_t jointIndex, const QUuid& entityID, E
|
|||
return;
|
||||
}
|
||||
|
||||
_refScale = item->getLargestDimension();
|
||||
_lastRefDimension = item->getDimensions();
|
||||
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
|
||||
glm::quat refInvRot = glm::inverse(_refRotation);
|
||||
_scale = _avatar->getTargetScale() / _refScale;
|
||||
_rotation = refInvRot * _avatar->getOrientation();
|
||||
_translation = refInvRot * (avatar->getPosition() - _refPosition) / _refScale;
|
||||
// BUG! _refPosition is in domain units, but avatar is in meters
|
||||
_translation = refInvRot * (avatar->getPosition() - _refPosition);
|
||||
}
|
||||
|
||||
void JointReferential::update() {
|
||||
|
@ -146,9 +145,10 @@ void JointReferential::update() {
|
|||
}
|
||||
|
||||
bool somethingChanged = false;
|
||||
if (item->getLargestDimension() != _refScale) {
|
||||
_refScale = item->getLargestDimension();
|
||||
_avatar->setTargetScale(_refScale * _scale, true);
|
||||
if (item->getDimensions() != _lastRefDimension) {
|
||||
glm::vec3 oldDimension = _lastRefDimension;
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_translation *= _lastRefDimension / oldDimension;
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (item->getRotation() != _refRotation) {
|
||||
|
@ -158,7 +158,7 @@ void JointReferential::update() {
|
|||
}
|
||||
if (item->getPosition() != _refPosition || somethingChanged) {
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
_avatar->setPosition(_refPosition + _refRotation * (_translation * _refScale), true);
|
||||
_avatar->setPosition(_refPosition + _refRotation * _translation, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,4 +45,4 @@ protected:
|
|||
uint32_t _jointIndex;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelReferential_h
|
||||
#endif // hifi_ModelReferential_h
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
@ -195,6 +194,12 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
PerformanceTimer perfTimer("skeleton");
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
}
|
||||
|
||||
if (!_skeletonModel.hasSkeleton()) {
|
||||
// All the simulation that can be done has been done
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
simulateAttachments(deltaTime);
|
||||
|
|
|
@ -81,6 +81,8 @@ void SkeletonModel::setJointStates(QVector<JointState> states) {
|
|||
if (_enableShapes) {
|
||||
buildShapes();
|
||||
}
|
||||
|
||||
emit skeletonLoaded();
|
||||
}
|
||||
|
||||
const float PALM_PRIORITY = DEFAULT_PRIORITY;
|
||||
|
@ -721,7 +723,8 @@ void SkeletonModel::buildShapes() {
|
|||
}
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (geometry.joints.isEmpty()) {
|
||||
if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) {
|
||||
// rootJointIndex == -1 if the avatar model has no skeleton
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1006,3 +1009,6 @@ void SkeletonModel::renderJointCollisionShapes(float alpha) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool SkeletonModel::hasSkeleton() {
|
||||
return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false;
|
||||
}
|
||||
|
|
|
@ -123,7 +123,13 @@ public:
|
|||
void resetShapePositionsToDefaultPose(); // DEBUG method
|
||||
|
||||
void renderRagdoll();
|
||||
|
||||
|
||||
bool hasSkeleton();
|
||||
|
||||
signals:
|
||||
|
||||
void skeletonLoaded();
|
||||
|
||||
protected:
|
||||
|
||||
void buildShapes();
|
||||
|
|
|
@ -42,7 +42,7 @@ void OctreeFade::render() {
|
|||
|
||||
glDisable(GL_LIGHTING);
|
||||
glPushMatrix();
|
||||
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||
glScalef(1.0f, 1.0f, 1.0f);
|
||||
glTranslatef(voxelDetails.x + voxelDetails.s * 0.5f,
|
||||
voxelDetails.y + voxelDetails.s * 0.5f,
|
||||
voxelDetails.z + voxelDetails.s * 0.5f);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//
|
||||
//Merge branch 'master' of ssh://github.com/highfidelity/hifi into isentropic/
|
||||
// OctreePacketProcessor.cpp
|
||||
// interface/src/octree
|
||||
//
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
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) {
|
||||
return Application::getInstance()->exportEntities(filename, x, y, z, s);
|
||||
}
|
||||
|
@ -22,6 +26,6 @@ bool ClipboardScriptingInterface::importEntities(const QString& filename) {
|
|||
return Application::getInstance()->importEntities(filename);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::pasteEntities(float x, float y, float z, float s) {
|
||||
Application::getInstance()->pasteEntities(x, y, z);
|
||||
QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position) {
|
||||
return Application::getInstance()->pasteEntities(position.x, position.y, position.z);
|
||||
}
|
||||
|
|
|
@ -23,8 +23,9 @@ signals:
|
|||
|
||||
public slots:
|
||||
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);
|
||||
void pasteEntities(float x, float y, float z, float s);
|
||||
QVector<EntityItemID> pasteEntities(glm::vec3 position);
|
||||
};
|
||||
|
||||
#endif // hifi_ClipboardScriptingInterface_h
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "AttachmentsDialog.h"
|
||||
#include "BandwidthDialog.h"
|
||||
#include "CachesSizeDialog.h"
|
||||
#include "DiskCacheEditor.h"
|
||||
#include "HMDToolsDialog.h"
|
||||
#include "LodToolsDialog.h"
|
||||
#include "LoginDialog.h"
|
||||
|
@ -37,6 +38,11 @@ void DialogsManager::toggleAddressBar() {
|
|||
}
|
||||
}
|
||||
|
||||
void DialogsManager::toggleDiskCacheEditor() {
|
||||
maybeCreateDialog(_diskCacheEditor);
|
||||
_diskCacheEditor->toggle();
|
||||
}
|
||||
|
||||
void DialogsManager::toggleLoginDialog() {
|
||||
maybeCreateDialog(_loginDialog);
|
||||
_loginDialog->toggleQAction();
|
||||
|
@ -61,7 +67,6 @@ void DialogsManager::octreeStatsDetails() {
|
|||
}
|
||||
|
||||
void DialogsManager::cachesSizeDialog() {
|
||||
qDebug() << "Caches size:" << _cachesSizeDialog.isNull();
|
||||
if (!_cachesSizeDialog) {
|
||||
maybeCreateDialog(_cachesSizeDialog);
|
||||
|
||||
|
|
|
@ -24,8 +24,9 @@ class QAction;
|
|||
class AddressBarDialog;
|
||||
class AnimationsDialog;
|
||||
class AttachmentsDialog;
|
||||
class CachesSizeDialog;
|
||||
class BandwidthDialog;
|
||||
class CachesSizeDialog;
|
||||
class DiskCacheEditor;
|
||||
class LodToolsDialog;
|
||||
class LoginDialog;
|
||||
class OctreeStatsDialog;
|
||||
|
@ -45,6 +46,7 @@ public:
|
|||
|
||||
public slots:
|
||||
void toggleAddressBar();
|
||||
void toggleDiskCacheEditor();
|
||||
void toggleLoginDialog();
|
||||
void showLoginDialog();
|
||||
void octreeStatsDetails();
|
||||
|
@ -73,7 +75,7 @@ private:
|
|||
member = new T(parent);
|
||||
Q_CHECK_PTR(member);
|
||||
|
||||
if (_hmdToolsDialog) {
|
||||
if (_hmdToolsDialog && member->windowHandle()) {
|
||||
_hmdToolsDialog->watchWindow(member->windowHandle());
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +86,7 @@ private:
|
|||
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||
QPointer<BandwidthDialog> _bandwidthDialog;
|
||||
QPointer<CachesSizeDialog> _cachesSizeDialog;
|
||||
QPointer<DiskCacheEditor> _diskCacheEditor;
|
||||
QPointer<QMessageBox> _ircInfoBox;
|
||||
QPointer<HMDToolsDialog> _hmdToolsDialog;
|
||||
QPointer<LodToolsDialog> _lodToolsDialog;
|
||||
|
|
150
interface/src/ui/DiskCacheEditor.cpp
Normal file
150
interface/src/ui/DiskCacheEditor.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// DiskCacheEditor.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 3/4/15.
|
||||
// Copyright 2015 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
|
||||
//
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "DiskCacheEditor.h"
|
||||
|
||||
DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) {
|
||||
|
||||
}
|
||||
|
||||
QWindow* DiskCacheEditor::windowHandle() {
|
||||
return (_dialog) ? _dialog->windowHandle() : nullptr;
|
||||
}
|
||||
|
||||
void DiskCacheEditor::toggle() {
|
||||
qDebug() << "DiskCacheEditor::toggle()";
|
||||
if (!_dialog) {
|
||||
makeDialog();
|
||||
}
|
||||
|
||||
if (!_dialog->isActiveWindow()) {
|
||||
_dialog->show();
|
||||
_dialog->raise();
|
||||
_dialog->activateWindow();
|
||||
} else {
|
||||
_dialog->close();
|
||||
}
|
||||
}
|
||||
|
||||
void DiskCacheEditor::makeDialog() {
|
||||
_dialog = new QDialog(static_cast<QWidget*>(parent()));
|
||||
Q_CHECK_PTR(_dialog);
|
||||
_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
_dialog->setWindowTitle("Disk Cache Editor");
|
||||
|
||||
QGridLayout* layout = new QGridLayout(_dialog);
|
||||
Q_CHECK_PTR(layout);
|
||||
_dialog->setLayout(layout);
|
||||
|
||||
|
||||
QLabel* path = new QLabel("Path : ", _dialog);
|
||||
Q_CHECK_PTR(path);
|
||||
path->setAlignment(Qt::AlignRight);
|
||||
layout->addWidget(path, 0, 0);
|
||||
|
||||
QLabel* size = new QLabel("Current Size : ", _dialog);
|
||||
Q_CHECK_PTR(size);
|
||||
size->setAlignment(Qt::AlignRight);
|
||||
layout->addWidget(size, 1, 0);
|
||||
|
||||
QLabel* maxSize = new QLabel("Max Size : ", _dialog);
|
||||
Q_CHECK_PTR(maxSize);
|
||||
maxSize->setAlignment(Qt::AlignRight);
|
||||
layout->addWidget(maxSize, 2, 0);
|
||||
|
||||
|
||||
_path = new QLabel(_dialog);
|
||||
Q_CHECK_PTR(_path);
|
||||
_path->setAlignment(Qt::AlignLeft);
|
||||
layout->addWidget(_path, 0, 1, 1, 3);
|
||||
|
||||
_size = new QLabel(_dialog);
|
||||
Q_CHECK_PTR(_size);
|
||||
_size->setAlignment(Qt::AlignLeft);
|
||||
layout->addWidget(_size, 1, 1, 1, 3);
|
||||
|
||||
_maxSize = new QLabel(_dialog);
|
||||
Q_CHECK_PTR(_maxSize);
|
||||
_maxSize->setAlignment(Qt::AlignLeft);
|
||||
layout->addWidget(_maxSize, 2, 1, 1, 3);
|
||||
|
||||
refresh();
|
||||
|
||||
|
||||
QPushButton* refreshCacheButton = new QPushButton(_dialog);
|
||||
Q_CHECK_PTR(refreshCacheButton);
|
||||
refreshCacheButton->setText("Refresh");
|
||||
refreshCacheButton->setToolTip("Reload the cache stats.");
|
||||
connect(refreshCacheButton, SIGNAL(clicked()), SLOT(refresh()));
|
||||
layout->addWidget(refreshCacheButton, 3, 2);
|
||||
|
||||
QPushButton* clearCacheButton = new QPushButton(_dialog);
|
||||
Q_CHECK_PTR(clearCacheButton);
|
||||
clearCacheButton->setText("Clear");
|
||||
clearCacheButton->setToolTip("Erases the entire content of the disk cache.");
|
||||
connect(clearCacheButton, SIGNAL(clicked()), SLOT(clear()));
|
||||
layout->addWidget(clearCacheButton, 3, 3);
|
||||
}
|
||||
|
||||
void DiskCacheEditor::refresh() {
|
||||
static const std::function<QString(qint64)> stringify = [](qint64 number) {
|
||||
static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB";
|
||||
static const qint64 CHUNK = 1024;
|
||||
QString unit;
|
||||
int i = 0;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (number / CHUNK > 0) {
|
||||
number /= CHUNK;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QString("%0 %1").arg(number).arg(UNITS[i]);
|
||||
};
|
||||
QNetworkDiskCache* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache());
|
||||
|
||||
if (_path) {
|
||||
_path->setText(cache->cacheDirectory());
|
||||
}
|
||||
if (_size) {
|
||||
_size->setText(stringify(cache->cacheSize()));
|
||||
}
|
||||
if (_maxSize) {
|
||||
_maxSize->setText(stringify(cache->maximumCacheSize()));
|
||||
}
|
||||
}
|
||||
|
||||
void DiskCacheEditor::clear() {
|
||||
QMessageBox::StandardButton buttonClicked =
|
||||
QMessageBox::question(_dialog, "Clearing disk cache",
|
||||
"You are about to erase all the content of the disk cache,"
|
||||
"are you sure you want to do that?");
|
||||
if (buttonClicked == QMessageBox::Yes) {
|
||||
QNetworkDiskCache* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache());
|
||||
if (cache) {
|
||||
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
|
||||
cache->clear();
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
}
|
46
interface/src/ui/DiskCacheEditor.h
Normal file
46
interface/src/ui/DiskCacheEditor.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// DiskCacheEditor.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 3/4/15.
|
||||
// Copyright 2015 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
|
||||
//
|
||||
|
||||
#ifndef hifi_DiskCacheEditor_h
|
||||
#define hifi_DiskCacheEditor_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
class QDialog;
|
||||
class QLabel;
|
||||
class QWindow;
|
||||
|
||||
class DiskCacheEditor : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DiskCacheEditor(QWidget* parent = nullptr);
|
||||
|
||||
QWindow* windowHandle();
|
||||
|
||||
public slots:
|
||||
void toggle();
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void makeDialog();
|
||||
|
||||
QPointer<QDialog> _dialog;
|
||||
QPointer<QLabel> _path;
|
||||
QPointer<QLabel> _size;
|
||||
QPointer<QLabel> _maxSize;
|
||||
};
|
||||
|
||||
#endif // hifi_DiskCacheEditor_h
|
|
@ -71,20 +71,19 @@ void NodeBounds::draw() {
|
|||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
serverJurisdictions->unlock();
|
||||
glm::vec3 location(rootDetails.x, rootDetails.y, rootDetails.z);
|
||||
location *= (float)TREE_SCALE;
|
||||
|
||||
AACube serverBounds(location, rootDetails.s * TREE_SCALE);
|
||||
AACube serverBounds(location, rootDetails.s);
|
||||
|
||||
glm::vec3 center = serverBounds.getVertex(BOTTOM_RIGHT_NEAR)
|
||||
+ ((serverBounds.getVertex(TOP_LEFT_FAR) - serverBounds.getVertex(BOTTOM_RIGHT_NEAR)) / 2.0f);
|
||||
|
||||
const float ENTITY_NODE_SCALE = 0.99f;
|
||||
|
||||
float scaleFactor = rootDetails.s * TREE_SCALE;
|
||||
float scaleFactor = rootDetails.s;
|
||||
|
||||
// Scale by 0.92 - 1.00 depending on the scale of the node. This allows smaller nodes to scale in
|
||||
// a bit and not overlap larger nodes.
|
||||
scaleFactor *= 0.92 + (rootDetails.s * 0.08);
|
||||
scaleFactor *= 0.92f + (rootDetails.s * 0.08f);
|
||||
|
||||
// Scale different node types slightly differently because it's common for them to overlap.
|
||||
if (nodeType == NodeType::EntityServer) {
|
||||
|
|
|
@ -273,7 +273,6 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser
|
|||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
serverBounds.scale(TREE_SCALE);
|
||||
serverDetails << " jurisdiction: "
|
||||
<< qPrintable(rootCodeHex)
|
||||
<< " ["
|
||||
|
|
|
@ -76,7 +76,6 @@ int Referential::pack(unsigned char* destinationBuffer) const {
|
|||
|
||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, _translation, 0);
|
||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _rotation);
|
||||
destinationBuffer += packFloatScalarToSignedTwoByteFixed(destinationBuffer, _scale, 0);
|
||||
return destinationBuffer - startPosition;
|
||||
}
|
||||
|
||||
|
@ -91,7 +90,6 @@ int Referential::unpack(const unsigned char* sourceBuffer) {
|
|||
|
||||
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, _translation, 0);
|
||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _rotation);
|
||||
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((const int16_t*) sourceBuffer, &_scale, 0);
|
||||
return sourceBuffer - startPosition;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ public:
|
|||
|
||||
glm::vec3 getTranslation() const { return _translation; }
|
||||
glm::quat getRotation() const { return _rotation; }
|
||||
float getScale() const {return _scale; }
|
||||
QByteArray getExtraData() const { return _extraDataBuffer; }
|
||||
|
||||
virtual void update() {}
|
||||
|
@ -62,14 +61,13 @@ protected:
|
|||
AvatarData* _avatar;
|
||||
QByteArray _extraDataBuffer;
|
||||
|
||||
glm::vec3 _refPosition;
|
||||
glm::quat _refRotation;
|
||||
float _refScale;
|
||||
glm::vec3 _refPosition; // position of object in world-frame
|
||||
glm::quat _refRotation; // rotation of object in world-frame
|
||||
glm::vec3 _lastRefDimension; // dimension of object when _translation was last computed
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
float _scale;
|
||||
glm::vec3 _translation; // offset of avatar in object local-frame
|
||||
glm::quat _rotation; // rotation of avatar in object local-frame
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_Referential_h
|
||||
#endif // hifi_Referential_h
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "RenderableModelEntityItem.h"
|
||||
#include "RenderableSphereEntityItem.h"
|
||||
#include "RenderableTextEntityItem.h"
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
|
||||
|
||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
|
@ -53,6 +54,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory)
|
||||
|
||||
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||
|
@ -96,7 +98,7 @@ void EntityTreeRenderer::init() {
|
|||
|
||||
// make sure our "last avatar position" is something other than our current position, so that on our
|
||||
// first chance, we'll check for enter/leave entity events.
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
|
||||
|
||||
connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);
|
||||
connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::checkAndCallPreload);
|
||||
|
@ -276,10 +278,10 @@ void EntityTreeRenderer::update() {
|
|||
void EntityTreeRenderer::checkEnterLeaveEntities() {
|
||||
if (_tree && !_shuttingDown) {
|
||||
_tree->lockForWrite(); // so that our scripts can do edits if they want
|
||||
glm::vec3 avatarPosition = _viewState->getAvatarPosition() / (float) TREE_SCALE;
|
||||
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
|
||||
|
||||
if (avatarPosition != _lastAvatarPosition) {
|
||||
float radius = 1.0f / (float) TREE_SCALE; // for now, assume 1 meter radius
|
||||
float radius = 1.0f; // for now, assume 1 meter radius
|
||||
QVector<const EntityItem*> foundEntities;
|
||||
QVector<EntityItemID> entitiesContainingAvatar;
|
||||
|
||||
|
@ -341,7 +343,7 @@ void EntityTreeRenderer::leaveAllEntities() {
|
|||
|
||||
// make sure our "last avatar position" is something other than our current position, so that on our
|
||||
// first chance, we'll check for enter/leave entity events.
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
|
||||
_tree->unlock();
|
||||
}
|
||||
}
|
||||
|
@ -416,8 +418,8 @@ const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityI
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::renderElementProxy(EntityTreeElement* entityTreeElement) {
|
||||
glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float) TREE_SCALE;
|
||||
float elementSize = entityTreeElement->getScale() * (float) TREE_SCALE;
|
||||
glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter();
|
||||
float elementSize = entityTreeElement->getScale();
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderWireCube(elementSize, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
@ -478,10 +480,6 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg
|
|||
AACube minCube = entity->getMinimumAACube();
|
||||
AABox entityBox = entity->getAABox();
|
||||
|
||||
maxCube.scale((float) TREE_SCALE);
|
||||
minCube.scale((float) TREE_SCALE);
|
||||
entityBox.scale((float) TREE_SCALE);
|
||||
|
||||
glm::vec3 maxCenter = maxCube.calcCenter();
|
||||
glm::vec3 minCenter = minCube.calcCenter();
|
||||
glm::vec3 entityBoxCenter = entityBox.calcCenter();
|
||||
|
@ -507,9 +505,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg
|
|||
glPopMatrix();
|
||||
|
||||
|
||||
glm::vec3 position = entity->getPosition() * (float) TREE_SCALE;
|
||||
glm::vec3 center = entity->getCenter() * (float) TREE_SCALE;
|
||||
glm::vec3 dimensions = entity->getDimensions() * (float) TREE_SCALE;
|
||||
glm::vec3 position = entity->getPosition();
|
||||
glm::vec3 center = entity->getCenter();
|
||||
glm::vec3 dimensions = entity->getDimensions();
|
||||
glm::quat rotation = entity->getRotation();
|
||||
|
||||
glPushMatrix();
|
||||
|
@ -549,8 +547,6 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
if (entityItem->isVisible()) {
|
||||
// render entityItem
|
||||
AABox entityBox = entityItem->getAABox();
|
||||
|
||||
entityBox.scale(TREE_SCALE);
|
||||
|
||||
// TODO: some entity types (like lights) might want to be rendered even
|
||||
// when they are outside of the view frustum...
|
||||
|
|
|
@ -25,9 +25,9 @@ EntityItem* RenderableBoxEntityItem::factory(const EntityItemID& entityID, const
|
|||
void RenderableBoxEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
|
||||
assert(getType() == EntityTypes::Box);
|
||||
glm::vec3 position = getPositionInMeters();
|
||||
glm::vec3 center = getCenter() * (float)TREE_SCALE;
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
|
|
@ -26,8 +26,8 @@ EntityItem* RenderableLightEntityItem::factory(const EntityItemID& entityID, con
|
|||
void RenderableLightEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableLightEntityItem::render");
|
||||
assert(getType() == EntityTypes::Light);
|
||||
glm::vec3 position = getPositionInMeters();
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
float largestDiameter = glm::max(dimensions.x, dimensions.y, dimensions.z);
|
||||
|
||||
|
|
|
@ -114,9 +114,9 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
|
||||
bool drawAsModel = hasModel();
|
||||
|
||||
glm::vec3 position = getPosition() * (float)TREE_SCALE;
|
||||
float size = getSize() * (float)TREE_SCALE;
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
float size = glm::length(dimensions);
|
||||
|
||||
if (drawAsModel) {
|
||||
remapTextures();
|
||||
|
@ -260,23 +260,10 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
if (!_model) {
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::vec3 originInMeters = origin * (float)TREE_SCALE;
|
||||
QString extraInfo;
|
||||
float localDistance;
|
||||
|
||||
//qDebug() << "RenderableModelEntityItem::findDetailedRayIntersection() precisionPicking:" << precisionPicking;
|
||||
|
||||
bool intersectsModel = _model->findRayIntersectionAgainstSubMeshes(originInMeters, direction,
|
||||
localDistance, face, extraInfo, precisionPicking);
|
||||
|
||||
if (intersectsModel) {
|
||||
// NOTE: findRayIntersectionAgainstSubMeshes() does work in meters, but we're expected to return
|
||||
// results in tree scale.
|
||||
distance = localDistance / (float)TREE_SCALE;
|
||||
}
|
||||
|
||||
return intersectsModel; // we only got here if we intersected our non-aabox
|
||||
QString extraInfo;
|
||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// RenderableParticleEffectEntityItem.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Jason Rickwald on 3/2/15.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <gpu/GPUConfig.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <PerfStat.h>
|
||||
#include <GeometryCache.h>
|
||||
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
|
||||
EntityItem* RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableParticleEffectEntityItem(entityID, properties);
|
||||
}
|
||||
|
||||
RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
ParticleEffectEntityItem(entityItemID, properties) {
|
||||
_cacheID = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render");
|
||||
assert(getType() == EntityTypes::ParticleEffect);
|
||||
float pa_rad = getParticleRadius();
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
glm::vec4 paColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR,
|
||||
getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha());
|
||||
|
||||
// Right now we're just iterating over particles and rendering as a cross of four quads.
|
||||
// This is pretty dumb, it was quick enough to code up. Really, there should be many
|
||||
// rendering modes, including the all-important textured billboards.
|
||||
|
||||
QVector<glm::vec3>* pointVec = new QVector<glm::vec3>(_paCount * VERTS_PER_PARTICLE);
|
||||
quint32 paIter = _paHead;
|
||||
while (_paLife[paIter] > 0.0f) {
|
||||
int j = paIter * XYZ_STRIDE;
|
||||
|
||||
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
|
||||
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
|
||||
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
|
||||
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
|
||||
|
||||
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
|
||||
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2]));
|
||||
pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
|
||||
pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2]));
|
||||
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] - pa_rad));
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] + pa_rad));
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] + pa_rad));
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] - pa_rad));
|
||||
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] + pa_rad));
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] - pa_rad));
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] - pa_rad));
|
||||
pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] + pa_rad));
|
||||
|
||||
paIter = (paIter + 1) % _maxParticles;
|
||||
}
|
||||
|
||||
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, *pointVec, paColor);
|
||||
|
||||
glPushMatrix();
|
||||
glm::vec3 position = getPosition();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::quat rotation = getRotation();
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = getCenter() - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderVertices(gpu::QUADS, _cacheID);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
delete pointVec;
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// RenderableParticleEffectEntityItem.h
|
||||
// interface/src/entities
|
||||
//
|
||||
// Created by Jason Rickwald on 3/2/15.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_RenderableParticleEffectEntityItem_h
|
||||
#define hifi_RenderableParticleEffectEntityItem_h
|
||||
|
||||
#include <ParticleEffectEntityItem.h>
|
||||
|
||||
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
protected:
|
||||
int _cacheID;
|
||||
const int VERTS_PER_PARTICLE = 16;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_RenderableParticleEffectEntityItem_h
|
|
@ -26,9 +26,9 @@ EntityItem* RenderableSphereEntityItem::factory(const EntityItemID& entityID, co
|
|||
void RenderableSphereEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
|
||||
assert(getType() == EntityTypes::Sphere);
|
||||
glm::vec3 position = getPositionInMeters();
|
||||
glm::vec3 center = getCenterInMeters();
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 center = getCenter();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
|
|
@ -30,8 +30,8 @@ EntityItem* RenderableTextEntityItem::factory(const EntityItemID& entityID, cons
|
|||
void RenderableTextEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
|
||||
assert(getType() == EntityTypes::Text);
|
||||
glm::vec3 position = getPositionInMeters();
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::vec3 halfDimensions = dimensions / 2.0f;
|
||||
glm::quat rotation = getRotation();
|
||||
float leftMargin = 0.1f;
|
||||
|
|
|
@ -25,7 +25,7 @@ AddEntityOperator::AddEntityOperator(EntityTree* tree,
|
|||
{
|
||||
// caller must have verified existence of newEntity
|
||||
assert(_newEntity);
|
||||
_newEntityBox = _newEntity->getMaximumAACube().clamp(0.0f, 1.0f);
|
||||
_newEntityBox = _newEntity->getMaximumAACube().clamp(0.0f, (float)TREE_SCALE);
|
||||
}
|
||||
|
||||
bool AddEntityOperator::preRecursion(OctreeElement* element) {
|
||||
|
|
|
@ -60,7 +60,7 @@ bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElement* el
|
|||
// If we don't have an old entity, then we don't contain the entity, otherwise
|
||||
// check the bounds
|
||||
if (_entitiesToDelete.size() > 0) {
|
||||
AACube elementCube = element->getAACube();
|
||||
const AACube& elementCube = element->getAACube();
|
||||
foreach(const EntityToDeleteDetails& details, _entitiesToDelete) {
|
||||
if (elementCube.contains(details.cube)) {
|
||||
containsEntity = true;
|
||||
|
|
|
@ -501,8 +501,12 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
|
||||
dataAt += propertyFlags.getEncodedLength();
|
||||
bytesRead += propertyFlags.getEncodedLength();
|
||||
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_POSITION, glm::vec3, updatePosition);
|
||||
bool useMeters = (args.bitstreamVersion == VERSION_ENTITIES_USE_METERS_AND_RADIANS);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_POSITION, glm::vec3, updatePosition);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_POSITION, glm::vec3, updatePositionInDomainUnits);
|
||||
}
|
||||
|
||||
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
|
||||
|
@ -516,18 +520,31 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
}
|
||||
}
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, setDimensions);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, setDimensions);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, setDimensionsInDomainUnits);
|
||||
}
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY_QUAT_SETTER(PROP_ROTATION, updateRotation);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_DENSITY, float, updateDensity);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_VELOCITY, glm::vec3, updateVelocityInDomainUnits);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravityInDomainUnits);
|
||||
}
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees);
|
||||
}
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
|
||||
|
@ -587,8 +604,7 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
|
|||
}
|
||||
|
||||
float EntityItem::computeMass() const {
|
||||
// NOTE: we group the operations here in and attempt to reduce floating point error.
|
||||
return ((_density * (_volumeMultiplier * _dimensions.x)) * _dimensions.y) * _dimensions.z;
|
||||
return _density * _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z;
|
||||
}
|
||||
|
||||
void EntityItem::setDensity(float density) {
|
||||
|
@ -609,10 +625,7 @@ void EntityItem::setMass(float mass) {
|
|||
// we must protect the density range to help maintain stability of physics simulation
|
||||
// therefore this method might not accept the mass that is supplied.
|
||||
|
||||
// NOTE: when computing the volume we group the _volumeMultiplier (typically a very large number, due
|
||||
// to the TREE_SCALE transformation) with the first dimension component (typically a very small number)
|
||||
// in an attempt to reduce floating point error of the final result.
|
||||
float volume = (_volumeMultiplier * _dimensions.x) * _dimensions.y * _dimensions.z;
|
||||
float volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z;
|
||||
|
||||
// compute new density
|
||||
const float MIN_VOLUME = 1.0e-6f; // 0.001mm^3
|
||||
|
@ -674,41 +687,36 @@ void EntityItem::simulate(const quint64& now) {
|
|||
void EntityItem::simulateKinematicMotion(float timeElapsed) {
|
||||
if (hasAngularVelocity()) {
|
||||
// angular damping
|
||||
glm::vec3 angularVelocity = getAngularVelocity();
|
||||
if (_angularDamping > 0.0f) {
|
||||
angularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
|
||||
_angularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << " angularDamping :" << _angularDamping;
|
||||
qDebug() << " newAngularVelocity:" << angularVelocity;
|
||||
qDebug() << " newAngularVelocity:" << _angularVelocity;
|
||||
#endif
|
||||
setAngularVelocity(angularVelocity);
|
||||
}
|
||||
|
||||
float angularSpeed = glm::length(_angularVelocity);
|
||||
|
||||
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; //
|
||||
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec
|
||||
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
|
||||
if (angularSpeed > 0.0f) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
|
||||
}
|
||||
setAngularVelocity(ENTITY_ITEM_ZERO_VEC3);
|
||||
_angularVelocity = ENTITY_ITEM_ZERO_VEC3;
|
||||
} else {
|
||||
// NOTE: angularSpeed is currently in degrees/sec!!!
|
||||
// TODO: Andrew to convert to radians/sec
|
||||
glm::vec3 angularVelocity = glm::radians(_angularVelocity);
|
||||
// for improved agreement with the way Bullet integrates rotations we use an approximation
|
||||
// and break the integration into bullet-sized substeps
|
||||
glm::quat rotation = getRotation();
|
||||
float dt = timeElapsed;
|
||||
while (dt > PHYSICS_ENGINE_FIXED_SUBSTEP) {
|
||||
glm::quat dQ = computeBulletRotationStep(angularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP);
|
||||
glm::quat dQ = computeBulletRotationStep(_angularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP);
|
||||
rotation = glm::normalize(dQ * rotation);
|
||||
dt -= PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||
}
|
||||
// NOTE: this final partial substep can drift away from a real Bullet simulation however
|
||||
// it only becomes significant for rapidly rotating objects
|
||||
// (e.g. around PI/4 radians per substep, or 7.5 rotations/sec at 60 substeps/sec).
|
||||
glm::quat dQ = computeBulletRotationStep(angularVelocity, dt);
|
||||
glm::quat dQ = computeBulletRotationStep(_angularVelocity, dt);
|
||||
rotation = glm::normalize(dQ * rotation);
|
||||
|
||||
setRotation(rotation);
|
||||
|
@ -753,7 +761,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) {
|
|||
}
|
||||
|
||||
float speed = glm::length(velocity);
|
||||
const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE; // 1mm/sec
|
||||
const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec
|
||||
if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) {
|
||||
setVelocity(ENTITY_ITEM_ZERO_VEC3);
|
||||
if (speed > 0.0f) {
|
||||
|
@ -793,12 +801,12 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
|
||||
properties._type = getType();
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPositionInMeters);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPosition);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensions); // NOTE: radius is obsolete
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(density, getDensity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocityInMeters);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravityInMeters);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript);
|
||||
|
@ -821,12 +829,12 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePositionInMeters); // this will call recalculate collision shape if needed
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, updateDimensionsInMeters); // NOTE: radius is obsolete
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePosition); // this will call recalculate collision shape if needed
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, updateDimensions); // NOTE: radius is obsolete
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(density, updateDensity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocityInMeters);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, updateDamping);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
|
||||
|
@ -888,11 +896,6 @@ void EntityItem::recordCreationTime() {
|
|||
}
|
||||
|
||||
|
||||
// TODO: is this really correct? how do we use size, does it need to handle rotation?
|
||||
float EntityItem::getSize() const {
|
||||
return glm::length(_dimensions);
|
||||
}
|
||||
|
||||
// TODO: doesn't this need to handle rotation?
|
||||
glm::vec3 EntityItem::getCenter() const {
|
||||
return _position + (_dimensions * (glm::vec3(0.5f,0.5f,0.5f) - _registrationPoint));
|
||||
|
@ -951,8 +954,7 @@ AACube EntityItem::getMinimumAACube() const {
|
|||
return AACube(cornerOfCube, longestSide);
|
||||
}
|
||||
|
||||
AABox EntityItem::getAABox() const {
|
||||
|
||||
AABox EntityItem::getAABox() const {
|
||||
// _position represents the position of the registration point.
|
||||
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
|
||||
|
||||
|
@ -967,6 +969,11 @@ AABox EntityItem::getAABox() const {
|
|||
return AABox(rotatedExtentsRelativeToRegistrationPoint);
|
||||
}
|
||||
|
||||
AABox EntityItem::getAABoxInDomainUnits() const {
|
||||
AABox box = getAABox();
|
||||
box.scale(1.0f / (float)TREE_SCALE);
|
||||
return box;
|
||||
}
|
||||
|
||||
// NOTE: This should only be used in cases of old bitstreams which only contain radius data
|
||||
// 0,0,0 --> maxDimension,maxDimension,maxDimension
|
||||
|
@ -992,48 +999,41 @@ void EntityItem::setRadius(float value) {
|
|||
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
|
||||
// ... radius = sqrt(3 x maxDimension ^ 2) / 2.0f;
|
||||
float EntityItem::getRadius() const {
|
||||
float length = glm::length(_dimensions);
|
||||
float radius = length / 2.0f;
|
||||
return radius;
|
||||
return 0.5f * glm::length(_dimensions);
|
||||
}
|
||||
|
||||
void EntityItem::computeShapeInfo(ShapeInfo& info) const {
|
||||
info.setParams(getShapeType(), 0.5f * getDimensionsInMeters());
|
||||
info.setParams(getShapeType(), 0.5f * getDimensions());
|
||||
}
|
||||
|
||||
const float MIN_POSITION_DELTA = 0.0001f;
|
||||
const float MIN_DIMENSIONS_DELTA = 0.0005f;
|
||||
const float MIN_ALIGNMENT_DOT = 0.999999f;
|
||||
const float MIN_VELOCITY_DELTA = 0.01f;
|
||||
const float MIN_DAMPING_DELTA = 0.001f;
|
||||
const float MIN_GRAVITY_DELTA = 0.001f;
|
||||
const float MIN_SPIN_DELTA = 0.0003f;
|
||||
|
||||
void EntityItem::updatePositionInDomainUnits(const glm::vec3& value) {
|
||||
glm::vec3 position = value * (float)TREE_SCALE;
|
||||
updatePosition(position);
|
||||
}
|
||||
|
||||
void EntityItem::updatePosition(const glm::vec3& value) {
|
||||
if (glm::distance(_position, value) * (float)TREE_SCALE > MIN_POSITION_DELTA) {
|
||||
_position = value;
|
||||
if (glm::distance(_position, value) > MIN_POSITION_DELTA) {
|
||||
_position = value;
|
||||
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::updatePositionInMeters(const glm::vec3& value) {
|
||||
glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f);
|
||||
if (glm::distance(_position, position) * (float)TREE_SCALE > MIN_POSITION_DELTA) {
|
||||
_position = position;
|
||||
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
||||
}
|
||||
void EntityItem::updateDimensionsInDomainUnits(const glm::vec3& value) {
|
||||
glm::vec3 dimensions = value * (float)TREE_SCALE;
|
||||
updateDimensions(dimensions);
|
||||
}
|
||||
|
||||
void EntityItem::updateDimensions(const glm::vec3& value) {
|
||||
if (_dimensions != value) {
|
||||
_dimensions = glm::abs(value);
|
||||
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
|
||||
glm::vec3 dimensions = glm::abs(value) / (float) TREE_SCALE;
|
||||
if (_dimensions != dimensions) {
|
||||
_dimensions = dimensions;
|
||||
if (glm::distance(_dimensions, value) > MIN_DIMENSIONS_DELTA) {
|
||||
_dimensions = value;
|
||||
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
|
||||
}
|
||||
}
|
||||
|
@ -1050,10 +1050,7 @@ void EntityItem::updateMass(float mass) {
|
|||
// we must protect the density range to help maintain stability of physics simulation
|
||||
// therefore this method might not accept the mass that is supplied.
|
||||
|
||||
// NOTE: when computing the volume we group the _volumeMultiplier (typically a very large number, due
|
||||
// to the TREE_SCALE transformation) with the first dimension component (typically a very small number)
|
||||
// in an attempt to reduce floating point error of the final result.
|
||||
float volume = (_volumeMultiplier * _dimensions.x) * _dimensions.y * _dimensions.z;
|
||||
float volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z;
|
||||
|
||||
// compute new density
|
||||
float newDensity = _density;
|
||||
|
@ -1072,24 +1069,17 @@ void EntityItem::updateMass(float mass) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::updateVelocity(const glm::vec3& value) {
|
||||
if (glm::distance(_velocity, value) * (float)TREE_SCALE > MIN_VELOCITY_DELTA) {
|
||||
if (glm::length(value) * (float)TREE_SCALE < MIN_VELOCITY_DELTA) {
|
||||
_velocity = ENTITY_ITEM_ZERO_VEC3;
|
||||
} else {
|
||||
_velocity = value;
|
||||
}
|
||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||
}
|
||||
void EntityItem::updateVelocityInDomainUnits(const glm::vec3& value) {
|
||||
glm::vec3 velocity = value * (float)TREE_SCALE;
|
||||
updateVelocity(velocity);
|
||||
}
|
||||
|
||||
void EntityItem::updateVelocityInMeters(const glm::vec3& value) {
|
||||
glm::vec3 velocity = value / (float) TREE_SCALE;
|
||||
if (glm::distance(_velocity, velocity) * (float)TREE_SCALE > MIN_VELOCITY_DELTA) {
|
||||
void EntityItem::updateVelocity(const glm::vec3& value) {
|
||||
if (glm::distance(_velocity, value) > MIN_VELOCITY_DELTA) {
|
||||
if (glm::length(value) < MIN_VELOCITY_DELTA) {
|
||||
_velocity = ENTITY_ITEM_ZERO_VEC3;
|
||||
} else {
|
||||
_velocity = velocity;
|
||||
_velocity = value;
|
||||
}
|
||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||
}
|
||||
|
@ -1102,17 +1092,14 @@ void EntityItem::updateDamping(float value) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::updateGravity(const glm::vec3& value) {
|
||||
if (glm::distance(_gravity, value) * (float)TREE_SCALE > MIN_GRAVITY_DELTA) {
|
||||
_gravity = value;
|
||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||
}
|
||||
void EntityItem::updateGravityInDomainUnits(const glm::vec3& value) {
|
||||
glm::vec3 gravity = value * (float) TREE_SCALE;
|
||||
updateGravity(gravity);
|
||||
}
|
||||
|
||||
void EntityItem::updateGravityInMeters(const glm::vec3& value) {
|
||||
glm::vec3 gravity = value / (float) TREE_SCALE;
|
||||
if ( glm::distance(_gravity, gravity) * (float)TREE_SCALE > MIN_GRAVITY_DELTA) {
|
||||
_gravity = gravity;
|
||||
void EntityItem::updateGravity(const glm::vec3& value) {
|
||||
if ( glm::distance(_gravity, value) > MIN_GRAVITY_DELTA) {
|
||||
_gravity = value;
|
||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class EntityTreeElementExtraEncodeData;
|
|||
|
||||
#define debugTime(T, N) qPrintable(QString("%1 [ %2 ago]").arg(T, 16, 10).arg(formatUsecTime(N - T), 15))
|
||||
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
|
||||
#define debugTreeVector(V) V << "[" << (V * (float)TREE_SCALE) << " in meters ]"
|
||||
#define debugTreeVector(V) V << "[" << V << " in meters ]"
|
||||
|
||||
|
||||
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
||||
|
@ -145,26 +145,27 @@ public:
|
|||
|
||||
// attributes applicable to all entity types
|
||||
EntityTypes::EntityType getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getPositionInMeters() const { return _position * (float) TREE_SCALE; } /// get position in meters
|
||||
glm::vec3 getPositionInDomainUnits() const { return _position / (float)TREE_SCALE; } /// get position in domain scale units (0.0 - 1.0)
|
||||
const glm::vec3& getPosition() const { return _position; } /// get position in meters
|
||||
|
||||
/// set position in domain scale units (0.0 - 1.0)
|
||||
void setPosition(const glm::vec3& value) { _position = value; }
|
||||
void setPositionInMeters(const glm::vec3& value) /// set position in meter units (0.0 - TREE_SCALE)
|
||||
{ setPosition(glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f)); }
|
||||
void setPositionInDomainUnits(const glm::vec3& value)
|
||||
{ setPosition(glm::clamp(value, 0.0f, 1.0f) * (float)TREE_SCALE); }
|
||||
void setPosition(const glm::vec3& value) {
|
||||
_position = value;
|
||||
}
|
||||
|
||||
glm::vec3 getCenter() const; /// calculates center of the entity in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getCenterInMeters() const { return getCenter() * (float) TREE_SCALE; }
|
||||
glm::vec3 getCenterInDomainUnits() const { return getCenter() / (float) TREE_SCALE; }
|
||||
glm::vec3 getCenter() const;
|
||||
|
||||
const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getDimensionsInMeters() const { return _dimensions * (float) TREE_SCALE; } /// get dimensions in meters
|
||||
float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension
|
||||
glm::vec3 getDimensionsInDomainUnits() const { return _dimensions / (float)TREE_SCALE; } /// get dimensions in domain scale units (0.0 - 1.0)
|
||||
const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in meters
|
||||
|
||||
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
|
||||
virtual void setDimensions(const glm::vec3& value) { _dimensions = value; }
|
||||
/// set dimensions in domain scale units (0.0 - 1.0)
|
||||
virtual void setDimensionsInDomainUnits(const glm::vec3& value) { _dimensions = glm::abs(value) * (float)TREE_SCALE; }
|
||||
|
||||
/// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately
|
||||
void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); }
|
||||
/// set dimensions in meter units (0.0 - TREE_SCALE)
|
||||
virtual void setDimensions(const glm::vec3& value) { _dimensions = glm::abs(value); }
|
||||
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||
|
@ -181,16 +182,16 @@ public:
|
|||
|
||||
float getDensity() const { return _density; }
|
||||
|
||||
const glm::vec3& getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second
|
||||
glm::vec3 getVelocityInMeters() const { return _velocity * (float) TREE_SCALE; } /// get velocity in meters
|
||||
void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in domain scale units (0.0-1.0) per second
|
||||
void setVelocityInMeters(const glm::vec3& value) { _velocity = value / (float) TREE_SCALE; } /// velocity in meters
|
||||
glm::vec3 getVelocityInDomainUnits() const { return _velocity / (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second
|
||||
const glm::vec3 getVelocity() const { return _velocity; } /// get velocity in meters
|
||||
void setVelocityInDomainUnits(const glm::vec3& value) { _velocity = value * (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second
|
||||
void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters
|
||||
bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; }
|
||||
|
||||
const glm::vec3& getGravity() const { return _gravity; } /// gravity in domain scale units (0.0-1.0) per second squared
|
||||
glm::vec3 getGravityInMeters() const { return _gravity * (float) TREE_SCALE; } /// get gravity in meters
|
||||
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in domain scale units (0.0-1.0) per second squared
|
||||
void setGravityInMeters(const glm::vec3& value) { _gravity = value / (float) TREE_SCALE; } /// gravity in meters
|
||||
glm::vec3 getGravityInDomainUnits() const { return _gravity / (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared
|
||||
const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters
|
||||
void setGravityInDomainUnits(const glm::vec3& value) { _gravity = value * (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared
|
||||
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters
|
||||
bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; }
|
||||
|
||||
float getDamping() const { return _damping; }
|
||||
|
@ -212,10 +213,10 @@ public:
|
|||
quint64 getExpiry() const;
|
||||
|
||||
// position, size, and bounds related helpers
|
||||
float getSize() const; /// get maximum dimension in domain scale units (0.0 - 1.0)
|
||||
AACube getMaximumAACube() const;
|
||||
AACube getMinimumAACube() const;
|
||||
AABox getAABox() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0)
|
||||
AABox getAABox() const; /// axis aligned bounding box in world-frame (meters)
|
||||
AABox getAABoxInDomainUnits() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0)
|
||||
|
||||
const QString& getScript() const { return _script; }
|
||||
void setScript(const QString& value) { _script = value; }
|
||||
|
@ -250,29 +251,31 @@ public:
|
|||
const QString& getUserData() const { return _userData; }
|
||||
void setUserData(const QString& value) { _userData = value; }
|
||||
|
||||
// TODO: We need to get rid of these users of getRadius()...
|
||||
// TODO: get rid of users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
||||
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
|
||||
virtual bool containsInDomainUnits(const glm::vec3& point) const { return getAABoxInDomainUnits().contains(point); }
|
||||
virtual void computeShapeInfo(ShapeInfo& info) const;
|
||||
|
||||
/// return preferred shape type (actual physical shape may differ)
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
|
||||
|
||||
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
|
||||
void updatePositionInDomainUnits(const glm::vec3& value);
|
||||
void updatePosition(const glm::vec3& value);
|
||||
void updatePositionInMeters(const glm::vec3& value);
|
||||
void updateDimensionsInDomainUnits(const glm::vec3& value);
|
||||
void updateDimensions(const glm::vec3& value);
|
||||
void updateDimensionsInMeters(const glm::vec3& value);
|
||||
void updateRotation(const glm::quat& rotation);
|
||||
void updateDensity(float value);
|
||||
void updateMass(float value);
|
||||
void updateVelocityInDomainUnits(const glm::vec3& value);
|
||||
void updateVelocity(const glm::vec3& value);
|
||||
void updateVelocityInMeters(const glm::vec3& value);
|
||||
void updateDamping(float value);
|
||||
void updateGravityInDomainUnits(const glm::vec3& value);
|
||||
void updateGravity(const glm::vec3& value);
|
||||
void updateGravityInMeters(const glm::vec3& value);
|
||||
void updateAngularVelocity(const glm::vec3& value);
|
||||
void updateAngularVelocityInDegrees(const glm::vec3& value) { updateAngularVelocity(glm::radians(value)); }
|
||||
void updateAngularDamping(float value);
|
||||
void updateIgnoreForCollisions(bool value);
|
||||
void updateCollisionsWillMove(bool value);
|
||||
|
@ -318,11 +321,10 @@ protected:
|
|||
float _glowLevel;
|
||||
float _localRenderAlpha;
|
||||
float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3
|
||||
// NOTE: _volumeMultiplier is used to compute volume:
|
||||
// volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z = m^3
|
||||
// DANGER: due to the size of TREE_SCALE the _volumeMultiplier is always a large number, and therefore
|
||||
// will tend to introduce floating point error. We must keep this in mind when using it.
|
||||
float _volumeMultiplier = (float)TREE_SCALE * (float)TREE_SCALE * (float)TREE_SCALE;
|
||||
// NOTE: _volumeMultiplier is used to allow some mass properties code exist in the EntityItem base class
|
||||
// rather than in all of the derived classes. If we ever collapse these classes to one we could do it a
|
||||
// different way.
|
||||
float _volumeMultiplier = 1.0f;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _gravity;
|
||||
float _damping;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "EntityItemPropertiesDefaults.h"
|
||||
#include "ModelEntityItem.h"
|
||||
#include "TextEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
|
||||
EntityItemProperties::EntityItemProperties() :
|
||||
|
@ -61,6 +62,13 @@ EntityItemProperties::EntityItemProperties() :
|
|||
CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
|
||||
CONSTRUCT_PROPERTY(backgroundColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR),
|
||||
CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE),
|
||||
CONSTRUCT_PROPERTY(maxParticles, ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES),
|
||||
CONSTRUCT_PROPERTY(lifespan, ParticleEffectEntityItem::DEFAULT_LIFESPAN),
|
||||
CONSTRUCT_PROPERTY(emitRate, ParticleEffectEntityItem::DEFAULT_EMIT_RATE),
|
||||
CONSTRUCT_PROPERTY(emitDirection, ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH),
|
||||
CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY),
|
||||
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
|
||||
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
|
@ -228,6 +236,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor);
|
||||
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SHAPE_TYPE, shapeType);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MAX_PARTICLES, maxParticles);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LIFESPAN, lifespan);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_RATE, emitRate);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_DIRECTION, emitDirection);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_STRENGTH, emitStrength);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
@ -282,6 +297,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(textColor, getTextColor());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(backgroundColor, getBackgroundColor());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(shapeType, getShapeTypeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(maxParticles);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(lifespan);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(emitRate);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(emitDirection);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(emitStrength);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(localGravity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius);
|
||||
|
||||
// Sitting properties support
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
|
@ -295,7 +317,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
sittingPoints.setProperty("length", _sittingPoints.size());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable
|
||||
|
||||
AABox aaBox = getAABoxInMeters();
|
||||
AABox aaBox = getAABox();
|
||||
QScriptValue boundingBox = engine->newObject();
|
||||
QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner());
|
||||
QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft());
|
||||
|
@ -355,6 +377,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(textColor, setTextColor);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(backgroundColor, setBackgroundColor);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(shapeType, ShapeType);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(maxParticles, setMaxParticles);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lifespan, setLifespan);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(emitRate, setEmitRate);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(emitDirection, setEmitDirection);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(emitStrength, setEmitStrength);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localGravity, setLocalGravity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(particleRadius, setParticleRadius);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
@ -528,6 +557,16 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, properties.getExponent());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, properties.getCutoff());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::ParticleEffect) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, appendValue, properties.getMaxParticles());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, appendValue, properties.getLifespan());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, appendValue, properties.getEmitRate());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, appendValue, properties.getEmitDirection());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, appendValue, properties.getEmitStrength());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, properties.getLocalGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, properties.getParticleRadius());
|
||||
}
|
||||
}
|
||||
if (propertyCount > 0) {
|
||||
int endOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
@ -746,6 +785,16 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::ParticleEffect) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, float, setMaxParticles);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFESPAN, float, setLifespan);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_STRENGTH, float, setEmitStrength);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCAL_GRAVITY, float, setLocalGravity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
@ -818,18 +867,20 @@ void EntityItemProperties::markAllChanged() {
|
|||
_textColorChanged = true;
|
||||
_backgroundColorChanged = true;
|
||||
_shapeTypeChanged = true;
|
||||
}
|
||||
|
||||
AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const {
|
||||
AACube maxCube = getMaximumAACubeInMeters();
|
||||
maxCube.scale(1.0f / (float)TREE_SCALE);
|
||||
return maxCube;
|
||||
_maxParticlesChanged = true;
|
||||
_lifespanChanged = true;
|
||||
_emitRateChanged = true;
|
||||
_emitDirectionChanged = true;
|
||||
_emitStrengthChanged = true;
|
||||
_localGravityChanged = true;
|
||||
_particleRadiusChanged = true;
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
/// This accounts for the registration point (upon which rotation occurs around).
|
||||
///
|
||||
AACube EntityItemProperties::getMaximumAACubeInMeters() const {
|
||||
AACube EntityItemProperties::getMaximumAACube() const {
|
||||
// * we know that the position is the center of rotation
|
||||
glm::vec3 centerOfRotation = _position; // also where _registration point is
|
||||
|
||||
|
@ -853,7 +904,7 @@ AACube EntityItemProperties::getMaximumAACubeInMeters() const {
|
|||
}
|
||||
|
||||
// The minimum bounding box for the entity.
|
||||
AABox EntityItemProperties::getAABoxInMeters() const {
|
||||
AABox EntityItemProperties::getAABox() const {
|
||||
|
||||
// _position represents the position of the registration point.
|
||||
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
|
||||
|
|
|
@ -83,9 +83,18 @@ enum EntityPropertyList {
|
|||
PROP_ANIMATION_SETTINGS,
|
||||
PROP_USER_DATA,
|
||||
PROP_SHAPE_TYPE,
|
||||
|
||||
// used by ParticleEffect entities
|
||||
PROP_MAX_PARTICLES,
|
||||
PROP_LIFESPAN,
|
||||
PROP_EMIT_RATE,
|
||||
PROP_EMIT_DIRECTION,
|
||||
PROP_EMIT_STRENGTH,
|
||||
PROP_LOCAL_GRAVITY,
|
||||
PROP_PARTICLE_RADIUS,
|
||||
|
||||
// NOTE: add new properties ABOVE this line and then modify PROP_LAST_ITEM below
|
||||
PROP_LAST_ITEM = PROP_SHAPE_TYPE,
|
||||
PROP_LAST_ITEM = PROP_PARTICLE_RADIUS,
|
||||
|
||||
// These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter
|
||||
// since the derived class knows how to interpret it's own properties and knows the types it expects
|
||||
|
@ -110,6 +119,7 @@ class EntityItemProperties {
|
|||
friend class SphereEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class LightEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class TextEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class ParticleEffectEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
public:
|
||||
EntityItemProperties();
|
||||
virtual ~EntityItemProperties();
|
||||
|
@ -129,9 +139,8 @@ public:
|
|||
/// used by EntityScriptingInterface to return EntityItemProperties for unknown models
|
||||
void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; }
|
||||
|
||||
AACube getMaximumAACubeInTreeUnits() const;
|
||||
AACube getMaximumAACubeInMeters() const;
|
||||
AABox getAABoxInMeters() const;
|
||||
AACube getMaximumAACube() const;
|
||||
AABox getAABox() const;
|
||||
|
||||
void debugDump() const;
|
||||
void setLastEdited(quint64 usecTime);
|
||||
|
@ -177,6 +186,13 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor);
|
||||
DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType);
|
||||
DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32);
|
||||
DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float);
|
||||
DEFINE_PROPERTY(PROP_EMIT_RATE, EmitRate, emitRate, float);
|
||||
DEFINE_PROPERTY_REF(PROP_EMIT_DIRECTION, EmitDirection, emitDirection, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_EMIT_STRENGTH, EmitStrength, emitStrength, float);
|
||||
DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float);
|
||||
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
|
||||
|
||||
public:
|
||||
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
|
||||
|
@ -296,6 +312,13 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundColor, backgroundColor, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ShapeType, shapeType, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MaxParticles, maxParticles, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifespan, lifespan, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitRate, emitRate, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitDirection, emitDirection, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitStrength, emitStrength, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalGravity, localGravity, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, "");
|
||||
|
||||
debug << " last edited:" << properties.getLastEdited() << "\n";
|
||||
debug << " edited ago:" << properties.getEditedAgo() << "\n";
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
// creating a new one on the stack so we declare the ZERO_VEC3 constant as an optimization.
|
||||
const glm::vec3 ENTITY_ITEM_ZERO_VEC3(0.0f);
|
||||
|
||||
const glm::vec3 REGULAR_GRAVITY = glm::vec3(0, -9.8f / (float)TREE_SCALE, 0);
|
||||
|
||||
const bool ENTITY_ITEM_DEFAULT_LOCKED = false;
|
||||
const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString("");
|
||||
|
||||
|
@ -37,7 +35,7 @@ const float ENTITY_ITEM_DEFAULT_LIFETIME = ENTITY_ITEM_IMMORTAL_LIFETIME;
|
|||
|
||||
const glm::quat ENTITY_ITEM_DEFAULT_ROTATION;
|
||||
const float ENTITY_ITEM_DEFAULT_WIDTH = 0.1f;
|
||||
const glm::vec3 ENTITY_ITEM_DEFAULT_DIMENSIONS = glm::vec3(ENTITY_ITEM_DEFAULT_WIDTH) / (float)TREE_SCALE;
|
||||
const glm::vec3 ENTITY_ITEM_DEFAULT_DIMENSIONS = glm::vec3(ENTITY_ITEM_DEFAULT_WIDTH);
|
||||
const float ENTITY_ITEM_DEFAULT_VOLUME = ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH * ENTITY_ITEM_DEFAULT_WIDTH;
|
||||
|
||||
const float ENTITY_ITEM_MAX_DENSITY = 10000.0f; // kg/m^3 density of silver
|
||||
|
|
|
@ -182,8 +182,7 @@ EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center
|
|||
EntityItemID result(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false);
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
const EntityItem* closestEntity = _entityTree->findClosestEntity(center/(float)TREE_SCALE,
|
||||
radius/(float)TREE_SCALE);
|
||||
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
|
||||
_entityTree->unlock();
|
||||
if (closestEntity) {
|
||||
result.id = closestEntity->getID();
|
||||
|
@ -207,7 +206,7 @@ QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& ce
|
|||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
QVector<const EntityItem*> entities;
|
||||
_entityTree->findEntities(center/(float)TREE_SCALE, radius/(float)TREE_SCALE, entities);
|
||||
_entityTree->findEntities(center, radius, entities);
|
||||
_entityTree->unlock();
|
||||
|
||||
foreach (const EntityItem* entity, entities) {
|
||||
|
|
|
@ -87,7 +87,7 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
|||
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
||||
PerformanceTimer perfTimer("sortingEntities");
|
||||
MovingEntitiesOperator moveOperator(_entityTree);
|
||||
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
|
||||
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), (float)TREE_SCALE);
|
||||
QSet<EntityItem*>::iterator itemItr = _entitiesToBeSorted.begin();
|
||||
while (itemItr != _entitiesToBeSorted.end()) {
|
||||
EntityItem* entity = *itemItr;
|
||||
|
@ -150,7 +150,7 @@ void EntitySimulation::entityChanged(EntityItem* entity) {
|
|||
bool wasRemoved = false;
|
||||
uint32_t dirtyFlags = entity->getDirtyFlags();
|
||||
if (dirtyFlags & EntityItem::DIRTY_POSITION) {
|
||||
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
|
||||
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), (float)TREE_SCALE);
|
||||
AACube newCube = entity->getMaximumAACube();
|
||||
if (!domainBounds.touches(newCube)) {
|
||||
qDebug() << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
|
|
|
@ -424,8 +424,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData)
|
|||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||
|
||||
glm::vec3 penetration;
|
||||
bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position,
|
||||
args->targetRadius, penetration);
|
||||
bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
|
||||
|
||||
// If this entityTreeElement contains the point, then search it...
|
||||
if (sphereIntersection) {
|
||||
|
@ -475,8 +474,7 @@ public:
|
|||
bool EntityTree::findInSphereOperation(OctreeElement* element, void* extraData) {
|
||||
FindAllNearPointArgs* args = static_cast<FindAllNearPointArgs*>(extraData);
|
||||
glm::vec3 penetration;
|
||||
bool sphereIntersection = element->getAACube().findSpherePenetration(args->position,
|
||||
args->targetRadius, penetration);
|
||||
bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
|
||||
|
||||
// If this element contains the point, then search it...
|
||||
if (sphereIntersection) {
|
||||
|
@ -511,8 +509,7 @@ public:
|
|||
|
||||
bool EntityTree::findInCubeOperation(OctreeElement* element, void* extraData) {
|
||||
FindEntitiesInCubeArgs* args = static_cast<FindEntitiesInCubeArgs*>(extraData);
|
||||
const AACube& elementCube = element->getAACube();
|
||||
if (elementCube.touches(args->_cube)) {
|
||||
if (element->getAACube().touches(args->_cube)) {
|
||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||
entityTreeElement->getEntities(args->_cube, args->_foundEntities);
|
||||
return true;
|
||||
|
@ -1000,13 +997,17 @@ void EntityTree::pruneTree() {
|
|||
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;
|
||||
args.packetSender = packetSender;
|
||||
args.localTree = localTree;
|
||||
args.root = glm::vec3(x, y, z);
|
||||
QVector<EntityItemID> newEntityIDs;
|
||||
args.newEntityIDs = &newEntityIDs;
|
||||
recurseTreeWithOperation(sendEntitiesOperation, &args);
|
||||
packetSender->releaseQueuedMessages();
|
||||
|
||||
return newEntityIDs;
|
||||
}
|
||||
|
||||
bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) {
|
||||
|
@ -1016,6 +1017,7 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
|
|||
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
EntityItemID newID(NEW_ENTITY, EntityItemID::getNextCreatorTokenID(), false);
|
||||
args->newEntityIDs->append(newID);
|
||||
EntityItemProperties properties = entities[i]->getProperties();
|
||||
properties.setPosition(properties.getPosition() + args->root);
|
||||
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;
|
||||
EntityTree* localTree;
|
||||
EntityEditPacketSender* packetSender;
|
||||
QVector<EntityItemID>* newEntityIDs;
|
||||
};
|
||||
|
||||
|
||||
|
@ -95,6 +96,8 @@ public:
|
|||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false);
|
||||
void removeEntityFromSimulation(EntityItem* entity);
|
||||
|
||||
/// \param position point of query in world-frame (meters)
|
||||
/// \param targetRadius radius of query (meters)
|
||||
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
|
||||
EntityItem* findEntityByID(const QUuid& id);
|
||||
EntityItem* findEntityByEntityItemID(const EntityItemID& entityID);
|
||||
|
@ -103,14 +106,14 @@ public:
|
|||
|
||||
|
||||
/// finds all entities that touch a sphere
|
||||
/// \param center the center of the sphere
|
||||
/// \param radius the radius of the sphere
|
||||
/// \param center the center of the sphere in world-frame (meters)
|
||||
/// \param radius the radius of the sphere in world-frame (meters)
|
||||
/// \param foundEntities[out] vector of const EntityItem*
|
||||
/// \remark Side effect: any initial contents in foundEntities will be lost
|
||||
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities);
|
||||
|
||||
/// finds all entities that touch a cube
|
||||
/// \param cube the query cube
|
||||
/// \param cube the query cube in world-frame (meters)
|
||||
/// \param foundEntities[out] vector of non-const EntityItem*
|
||||
/// \remark Side effect: any initial contents in entities will be lost
|
||||
void findEntities(const AACube& cube, QVector<EntityItem*>& foundEntities);
|
||||
|
@ -144,7 +147,7 @@ public:
|
|||
virtual void dumpTree();
|
||||
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);
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ EntityTreeElement* EntityTreeElement::addChildAtIndex(int index) {
|
|||
|
||||
void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) const {
|
||||
qDebug() << "EntityTreeElement::debugExtraEncodeData()... ";
|
||||
qDebug() << " element:" << getAACube();
|
||||
qDebug() << " element:" << _cube;
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
|
||||
|
@ -159,7 +159,7 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, Oct
|
|||
const bool wantDebug = false;
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "EntityTreeElement::elementEncodeComplete() element:" << getAACube();
|
||||
qDebug() << "EntityTreeElement::elementEncodeComplete() element:" << _cube;
|
||||
}
|
||||
|
||||
OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
|
||||
|
@ -194,7 +194,7 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, Oct
|
|||
= static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(childElement));
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "checking child: " << childElement->getAACube();
|
||||
qDebug() << "checking child: " << childElement->_cube;
|
||||
qDebug() << " childElement->isLeaf():" << childElement->isLeaf();
|
||||
qDebug() << " childExtraEncodeData->elementCompleted:" << childExtraEncodeData->elementCompleted;
|
||||
qDebug() << " childExtraEncodeData->subtreeCompleted:" << childExtraEncodeData->subtreeCompleted;
|
||||
|
@ -215,7 +215,7 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, Oct
|
|||
}
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "for this element: " << getAACube();
|
||||
qDebug() << "for this element: " << _cube;
|
||||
qDebug() << " WAS elementCompleted:" << thisExtraEncodeData->elementCompleted;
|
||||
qDebug() << " WAS subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
|
||||
}
|
||||
|
@ -302,7 +302,6 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
// the entity may not be in view and then in view a frame later, let the client side handle it's view
|
||||
// frustum culling on rendering.
|
||||
AACube entityCube = entity->getMaximumAACube();
|
||||
entityCube.scale(TREE_SCALE);
|
||||
if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
|
||||
includeThisEntity = false; // out of view, don't include it
|
||||
}
|
||||
|
@ -417,11 +416,11 @@ bool EntityTreeElement::bestFitEntityBounds(const EntityItem* entity) const {
|
|||
}
|
||||
|
||||
bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const {
|
||||
return containsBounds(properties.getMaximumAACubeInTreeUnits());
|
||||
return containsBounds(properties.getMaximumAACube());
|
||||
}
|
||||
|
||||
bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const {
|
||||
return bestFitBounds(properties.getMaximumAACubeInTreeUnits());
|
||||
return bestFitBounds(properties.getMaximumAACube());
|
||||
}
|
||||
|
||||
bool EntityTreeElement::containsBounds(const AACube& bounds) const {
|
||||
|
@ -441,14 +440,14 @@ bool EntityTreeElement::bestFitBounds(const AABox& bounds) const {
|
|||
}
|
||||
|
||||
bool EntityTreeElement::containsBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const {
|
||||
glm::vec3 clampedMin = glm::clamp(minPoint, 0.0f, 1.0f);
|
||||
glm::vec3 clampedMax = glm::clamp(maxPoint, 0.0f, 1.0f);
|
||||
glm::vec3 clampedMin = glm::clamp(minPoint, 0.0f, (float)TREE_SCALE);
|
||||
glm::vec3 clampedMax = glm::clamp(maxPoint, 0.0f, (float)TREE_SCALE);
|
||||
return _cube.contains(clampedMin) && _cube.contains(clampedMax);
|
||||
}
|
||||
|
||||
bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const {
|
||||
glm::vec3 clampedMin = glm::clamp(minPoint, 0.0f, 1.0f);
|
||||
glm::vec3 clampedMax = glm::clamp(maxPoint, 0.0f, 1.0f);
|
||||
glm::vec3 clampedMin = glm::clamp(minPoint, 0.0f, (float)TREE_SCALE);
|
||||
glm::vec3 clampedMax = glm::clamp(maxPoint, 0.0f, (float)TREE_SCALE);
|
||||
|
||||
if (_cube.contains(clampedMin) && _cube.contains(clampedMax)) {
|
||||
|
||||
|
@ -823,9 +822,7 @@ bool EntityTreeElement::pruneChildren() {
|
|||
|
||||
void EntityTreeElement::debugDump() {
|
||||
qDebug() << "EntityTreeElement...";
|
||||
AACube temp = getAACube();
|
||||
temp.scale((float)TREE_SCALE);
|
||||
qDebug() << " cube:" << temp;
|
||||
qDebug() << " cube:" << _cube;
|
||||
qDebug() << " has child elements:" << getChildCount();
|
||||
if (_entityItems->size()) {
|
||||
qDebug() << " has entities:" << _entityItems->size();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "ModelEntityItem.h"
|
||||
#include "SphereEntityItem.h"
|
||||
#include "TextEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
|
||||
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
|
||||
|
@ -37,6 +38,7 @@ REGISTER_ENTITY_TYPE(Box)
|
|||
REGISTER_ENTITY_TYPE(Sphere)
|
||||
REGISTER_ENTITY_TYPE(Light)
|
||||
REGISTER_ENTITY_TYPE(Text)
|
||||
REGISTER_ENTITY_TYPE(ParticleEffect)
|
||||
|
||||
|
||||
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
|
||||
|
|
|
@ -35,7 +35,8 @@ public:
|
|||
Sphere,
|
||||
Light,
|
||||
Text,
|
||||
LAST = Text
|
||||
ParticleEffect,
|
||||
LAST = ParticleEffect
|
||||
} EntityType;
|
||||
|
||||
static const QString& getEntityTypeName(EntityType entityType);
|
||||
|
|
|
@ -255,8 +255,8 @@ void ModelEntityItem::update(const quint64& now) {
|
|||
void ModelEntityItem::debugDump() const {
|
||||
qDebug() << "ModelEntityItem id:" << getEntityItemID();
|
||||
qDebug() << " edited ago:" << getEditedAgo();
|
||||
qDebug() << " position:" << getPosition() * (float)TREE_SCALE;
|
||||
qDebug() << " dimensions:" << getDimensions() * (float)TREE_SCALE;
|
||||
qDebug() << " position:" << getPosition();
|
||||
qDebug() << " dimensions:" << getDimensions();
|
||||
qDebug() << " model URL:" << getModelURL();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ MovingEntitiesOperator::~MovingEntitiesOperator() {
|
|||
|
||||
void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACube& newCube) {
|
||||
EntityTreeElement* oldContainingElement = _tree->getContainingElement(entity->getEntityItemID());
|
||||
AABox newCubeClamped = newCube.clamp(0.0f, 1.0f);
|
||||
AABox newCubeClamped = newCube.clamp(0.0f, (float)TREE_SCALE);
|
||||
|
||||
if (_wantDebug) {
|
||||
qDebug() << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------";
|
||||
|
@ -114,7 +114,7 @@ bool MovingEntitiesOperator::shouldRecurseSubTree(OctreeElement* element) {
|
|||
// If we don't have an old entity, then we don't contain the entity, otherwise
|
||||
// check the bounds
|
||||
if (_entitiesToMove.size() > 0) {
|
||||
AACube elementCube = element->getAACube();
|
||||
const AACube& elementCube = element->getAACube();
|
||||
int detailIndex = 0;
|
||||
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
class EntityToMoveDetails {
|
||||
public:
|
||||
EntityItem* entity;
|
||||
AACube oldCube;
|
||||
AACube newCube;
|
||||
AABox newCubeClamped;
|
||||
AACube oldCube; // meters
|
||||
AACube newCube; // meters
|
||||
AABox newCubeClamped; // meters
|
||||
EntityTreeElement* oldContainingElement;
|
||||
AACube oldContainingElementCube;
|
||||
AACube oldContainingElementCube; // meters
|
||||
bool oldFound;
|
||||
bool newFound;
|
||||
};
|
||||
|
|
512
libraries/entities/src/ParticleEffectEntityItem.cpp
Normal file
512
libraries/entities/src/ParticleEffectEntityItem.cpp
Normal file
|
@ -0,0 +1,512 @@
|
|||
//
|
||||
// ParticleEffectEntityItem.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Some starter code for a particle simulation entity, which could ideally be used for a variety of effects.
|
||||
// This is some really early and rough stuff here. It was enough for now to just get it up and running in the interface.
|
||||
//
|
||||
// Todo's and other notes:
|
||||
// - The simulation should restart when the AnimationLoop's max frame is reached (or passed), but there doesn't seem
|
||||
// to be a good way to set that max frame to something reasonable right now.
|
||||
// - There seems to be a bug whereby entities on the edge of screen will just pop off or on. This is probably due
|
||||
// to my lack of understanding of how entities in the octree are picked for rendering. I am updating the entity
|
||||
// dimensions based on the bounds of the sim, but maybe I need to update a dirty flag or something.
|
||||
// - This should support some kind of pre-roll of the simulation.
|
||||
// - Just to get this out the door, I just did forward Euler integration. There are better ways.
|
||||
// - Gravity always points along the Y axis. Support an actual gravity vector.
|
||||
// - Add the ability to add arbitrary forces to the simulation.
|
||||
// - Add controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented).
|
||||
// - Add drag.
|
||||
// - Add some kind of support for collisions.
|
||||
// - For simplicity, I'm currently just rendering each particle as a cross of four axis-aligned quads. Really, we'd
|
||||
// want multiple render modes, including (the most important) textured billboards (always facing camera). Also, these
|
||||
// should support animated textures.
|
||||
// - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so
|
||||
// there's no gaurantee that different clients will see simulations that look anything like the other.
|
||||
// - MORE?
|
||||
//
|
||||
// Created by Jason Rickwald on 3/2/15.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f;
|
||||
const bool ParticleEffectEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
|
||||
const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FPS = 30.0f;
|
||||
const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000;
|
||||
const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f;
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION(0.0f, 1.0f, 0.0f);
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH = 25.0f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY = -9.8f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f;
|
||||
|
||||
|
||||
EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new ParticleEffectEntityItem(entityID, properties);
|
||||
}
|
||||
|
||||
// our non-pure virtual subclass for now...
|
||||
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
EntityItem(entityItemID, properties) {
|
||||
_type = EntityTypes::ParticleEffect;
|
||||
_maxParticles = DEFAULT_MAX_PARTICLES;
|
||||
_lifespan = DEFAULT_LIFESPAN;
|
||||
_emitRate = DEFAULT_EMIT_RATE;
|
||||
_emitDirection = DEFAULT_EMIT_DIRECTION;
|
||||
_emitStrength = DEFAULT_EMIT_STRENGTH;
|
||||
_localGravity = DEFAULT_LOCAL_GRAVITY;
|
||||
_particleRadius = DEFAULT_PARTICLE_RADIUS;
|
||||
setProperties(properties);
|
||||
// this is a pretty dumb thing to do, and it should probably be changed to use a more dynamic
|
||||
// data structure in the future. I'm just trying to get some code out the door for now (and it's
|
||||
// at least time efficient (though not space efficient).
|
||||
// Also, this being a real-time application, it's doubtful we'll ever have millions of particles
|
||||
// to keep track of, so this really isn't all that bad.
|
||||
_paLife = new float[_maxParticles];
|
||||
_paPosition = new float[_maxParticles * XYZ_STRIDE]; // x,y,z
|
||||
_paVelocity = new float[_maxParticles * XYZ_STRIDE]; // x,y,z
|
||||
_paXmax = _paYmax = _paZmax = 1.0f;
|
||||
_paXmin = _paYmin = _paZmin = -1.0f;
|
||||
_randSeed = (unsigned int) glm::abs(_lifespan + _emitRate + _localGravity + getPosition().x + getPosition().y + getPosition().z);
|
||||
resetSimulation();
|
||||
_lastAnimated = usecTimestampNow();
|
||||
}
|
||||
|
||||
ParticleEffectEntityItem::~ParticleEffectEntityItem() {
|
||||
delete [] _paLife;
|
||||
delete [] _paPosition;
|
||||
delete [] _paVelocity;
|
||||
}
|
||||
|
||||
EntityItemProperties ParticleEffectEntityItem::getProperties() const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFPS, getAnimationFPS);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationSettings, getAnimationSettings);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifespan, getLifespan);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRate, getEmitRate);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitDirection, getEmitDirection);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitStrength, getEmitStrength);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(localGravity, getLocalGravity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationSettings, setAnimationSettings);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRate, setEmitRate);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitDirection, setEmitDirection);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitStrength, setEmitStrength);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localGravity, setLocalGravity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - getLastEdited();
|
||||
qDebug() << "ParticleEffectEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
}
|
||||
setLastEdited(properties.getLastEdited());
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
|
||||
|
||||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
|
||||
|
||||
// Because we're using AnimationLoop which will reset the frame index if you change it's running state
|
||||
// we want to read these values in the order they appear in the buffer, but call our setters in an
|
||||
// order that allows AnimationLoop to preserve the correct frame rate.
|
||||
float animationFPS = getAnimationFPS();
|
||||
float animationFrameIndex = getAnimationFrameIndex();
|
||||
bool animationIsPlaying = getAnimationIsPlaying();
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, animationFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, animationFrameIndex);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, animationIsPlaying);
|
||||
|
||||
if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) {
|
||||
if (animationIsPlaying != getAnimationIsPlaying()) {
|
||||
setAnimationIsPlaying(animationIsPlaying);
|
||||
}
|
||||
}
|
||||
if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) {
|
||||
setAnimationFPS(animationFPS);
|
||||
}
|
||||
if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) {
|
||||
setAnimationFrameIndex(animationFrameIndex);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_SETTINGS, setAnimationSettings);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
|
||||
READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, _maxParticles);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, _lifespan);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, _emitRate);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, _emitStrength);
|
||||
READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, _localGravity);
|
||||
READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, _particleRadius);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
||||
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ANIMATION_FPS;
|
||||
requestedProperties += PROP_ANIMATION_FRAME_INDEX;
|
||||
requestedProperties += PROP_ANIMATION_PLAYING;
|
||||
requestedProperties += PROP_ANIMATION_SETTINGS;
|
||||
requestedProperties += PROP_SHAPE_TYPE;
|
||||
requestedProperties += PROP_MAX_PARTICLES;
|
||||
requestedProperties += PROP_LIFESPAN;
|
||||
requestedProperties += PROP_EMIT_RATE;
|
||||
requestedProperties += PROP_EMIT_DIRECTION;
|
||||
requestedProperties += PROP_EMIT_STRENGTH;
|
||||
requestedProperties += PROP_LOCAL_GRAVITY;
|
||||
requestedProperties += PROP_PARTICLE_RADIUS;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, getAnimationFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, getAnimationIsPlaying());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, getAnimationSettings());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, appendValue, getMaxParticles());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, appendValue, getLifespan());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, appendValue, getEmitRate());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, appendValue, getEmitDirection());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, appendValue, getEmitStrength());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, getLocalGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, getParticleRadius());
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::isAnimatingSomething() const {
|
||||
return getAnimationIsPlaying() &&
|
||||
getAnimationFPS() != 0.0f;
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::needsToCallUpdate() const {
|
||||
return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::update(const quint64& now) {
|
||||
// only advance the frame index if we're playing
|
||||
if (getAnimationIsPlaying()) {
|
||||
float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND;
|
||||
_lastAnimated = now;
|
||||
float lastFrame = _animationLoop.getFrameIndex();
|
||||
_animationLoop.simulate(deltaTime);
|
||||
float curFrame = _animationLoop.getFrameIndex();
|
||||
if (curFrame > lastFrame) {
|
||||
stepSimulation(deltaTime);
|
||||
}
|
||||
else if (curFrame < lastFrame) {
|
||||
// we looped around, so restart the sim and only sim up to the point
|
||||
// since the beginning of the frame range.
|
||||
resetSimulation();
|
||||
stepSimulation((curFrame - _animationLoop.getFirstFrame()) / _animationLoop.getFPS());
|
||||
}
|
||||
}
|
||||
else {
|
||||
_lastAnimated = now;
|
||||
}
|
||||
|
||||
// update the dimensions
|
||||
glm::vec3 dims;
|
||||
dims.x = glm::max(glm::abs(_paXmin), glm::abs(_paXmax)) * 2.0f;
|
||||
dims.y = glm::max(glm::abs(_paYmin), glm::abs(_paYmax)) * 2.0f;
|
||||
dims.z = glm::max(glm::abs(_paZmin), glm::abs(_paZmax)) * 2.0f;
|
||||
setDimensions(dims);
|
||||
|
||||
EntityItem::update(now); // let our base class handle it's updates...
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::debugDump() const {
|
||||
quint64 now = usecTimestampNow();
|
||||
qDebug() << "PA EFFECT EntityItem id:" << getEntityItemID() << "---------------------------------------------";
|
||||
qDebug() << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
|
||||
qDebug() << " position:" << debugTreeVector(_position);
|
||||
qDebug() << " dimensions:" << debugTreeVector(_dimensions);
|
||||
qDebug() << " getLastEdited:" << debugTime(getLastEdited(), now);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::updateShapeType(ShapeType type) {
|
||||
if (type != _shapeType) {
|
||||
_shapeType = type;
|
||||
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationFrameIndex(float value) {
|
||||
#ifdef WANT_DEBUG
|
||||
if (isAnimatingSomething()) {
|
||||
qDebug() << "ParticleEffectEntityItem::setAnimationFrameIndex()";
|
||||
qDebug() << " value:" << value;
|
||||
qDebug() << " was:" << _animationLoop.getFrameIndex();
|
||||
}
|
||||
#endif
|
||||
_animationLoop.setFrameIndex(value);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationSettings(const QString& value) {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, frameIndex, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
if (settingsMap.contains("fps")) {
|
||||
float fps = settingsMap["fps"].toFloat();
|
||||
setAnimationFPS(fps);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("frameIndex")) {
|
||||
float frameIndex = settingsMap["frameIndex"].toFloat();
|
||||
#ifdef WANT_DEBUG
|
||||
if (isAnimatingSomething()) {
|
||||
qDebug() << "ParticleEffectEntityItem::setAnimationSettings() calling setAnimationFrameIndex()...";
|
||||
qDebug() << " settings:" << value;
|
||||
qDebug() << " settingsMap[frameIndex]:" << settingsMap["frameIndex"];
|
||||
qDebug(" frameIndex: %20.5f", frameIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
setAnimationFrameIndex(frameIndex);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("running")) {
|
||||
bool running = settingsMap["running"].toBool();
|
||||
if (running != getAnimationIsPlaying()) {
|
||||
setAnimationIsPlaying(running);
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsMap.contains("firstFrame")) {
|
||||
float firstFrame = settingsMap["firstFrame"].toFloat();
|
||||
setAnimationFirstFrame(firstFrame);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("lastFrame")) {
|
||||
float lastFrame = settingsMap["lastFrame"].toFloat();
|
||||
setAnimationLastFrame(lastFrame);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("loop")) {
|
||||
bool loop = settingsMap["loop"].toBool();
|
||||
setAnimationLoop(loop);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("hold")) {
|
||||
bool hold = settingsMap["hold"].toBool();
|
||||
setAnimationHold(hold);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("startAutomatically")) {
|
||||
bool startAutomatically = settingsMap["startAutomatically"].toBool();
|
||||
setAnimationStartAutomatically(startAutomatically);
|
||||
}
|
||||
|
||||
_animationSettings = value;
|
||||
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationIsPlaying(bool value) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
|
||||
_animationLoop.setRunning(value);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationFPS(float value) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
|
||||
_animationLoop.setFPS(value);
|
||||
}
|
||||
|
||||
QString ParticleEffectEntityItem::getAnimationSettings() const {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, frameIndex, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
QString value = _animationSettings;
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
|
||||
QVariant fpsValue(getAnimationFPS());
|
||||
settingsMap["fps"] = fpsValue;
|
||||
|
||||
QVariant frameIndexValue(getAnimationFrameIndex());
|
||||
settingsMap["frameIndex"] = frameIndexValue;
|
||||
|
||||
QVariant runningValue(getAnimationIsPlaying());
|
||||
settingsMap["running"] = runningValue;
|
||||
|
||||
QVariant firstFrameValue(getAnimationFirstFrame());
|
||||
settingsMap["firstFrame"] = firstFrameValue;
|
||||
|
||||
QVariant lastFrameValue(getAnimationLastFrame());
|
||||
settingsMap["lastFrame"] = lastFrameValue;
|
||||
|
||||
QVariant loopValue(getAnimationLoop());
|
||||
settingsMap["loop"] = loopValue;
|
||||
|
||||
QVariant holdValue(getAnimationHold());
|
||||
settingsMap["hold"] = holdValue;
|
||||
|
||||
QVariant startAutomaticallyValue(getAnimationStartAutomatically());
|
||||
settingsMap["startAutomatically"] = startAutomaticallyValue;
|
||||
|
||||
settingsAsJsonObject = QJsonObject::fromVariantMap(settingsMap);
|
||||
QJsonDocument newDocument(settingsAsJsonObject);
|
||||
QByteArray jsonByteArray = newDocument.toJson(QJsonDocument::Compact);
|
||||
QString jsonByteString(jsonByteArray);
|
||||
return jsonByteString;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
||||
_paXmin = _paYmin = _paZmin = -1.0f;
|
||||
_paXmax = _paYmax = _paZmax = 1.0f;
|
||||
|
||||
// update particles
|
||||
quint32 updateIter = _paHead;
|
||||
while (_paLife[updateIter] > 0.0f) {
|
||||
_paLife[updateIter] -= deltaTime;
|
||||
if (_paLife[updateIter] <= 0.0f) {
|
||||
_paLife[updateIter] = -1.0f;
|
||||
_paHead = (_paHead + 1) % _maxParticles;
|
||||
_paCount--;
|
||||
}
|
||||
else {
|
||||
// DUMB FORWARD EULER just to get it done
|
||||
int j = updateIter * XYZ_STRIDE;
|
||||
_paPosition[j] += _paVelocity[j] * deltaTime;
|
||||
_paPosition[j+1] += _paVelocity[j+1] * deltaTime;
|
||||
_paPosition[j+2] += _paVelocity[j+2] * deltaTime;
|
||||
|
||||
_paXmin = glm::min(_paXmin, _paPosition[j]);
|
||||
_paYmin = glm::min(_paYmin, _paPosition[j+1]);
|
||||
_paZmin = glm::min(_paZmin, _paPosition[j+2]);
|
||||
_paXmax = glm::max(_paXmax, _paPosition[j]);
|
||||
_paYmax = glm::max(_paYmax, _paPosition[j + 1]);
|
||||
_paZmax = glm::max(_paZmax, _paPosition[j + 2]);
|
||||
|
||||
// massless particles
|
||||
_paVelocity[j + 1] += deltaTime * _localGravity;
|
||||
}
|
||||
updateIter = (updateIter + 1) % _maxParticles;
|
||||
}
|
||||
|
||||
// emit new particles
|
||||
quint32 emitIdx = updateIter;
|
||||
_partialEmit += ((float)_emitRate) * deltaTime;
|
||||
quint32 birthed = (quint32)_partialEmit;
|
||||
_partialEmit -= (float)birthed;
|
||||
glm::vec3 randOffset;
|
||||
|
||||
for (quint32 i = 0; i < birthed; i++) {
|
||||
if (_paLife[emitIdx] < 0.0f) {
|
||||
int j = emitIdx * XYZ_STRIDE;
|
||||
_paLife[emitIdx] = _lifespan;
|
||||
randOffset.x = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
|
||||
randOffset.y = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
|
||||
randOffset.z = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
|
||||
_paVelocity[j] = (_emitDirection.x * _emitStrength) + randOffset.x;
|
||||
_paVelocity[j + 1] = (_emitDirection.y * _emitStrength) + randOffset.y;
|
||||
_paVelocity[j + 2] = (_emitDirection.z * _emitStrength) + randOffset.z;
|
||||
|
||||
// DUMB FORWARD EULER just to get it done
|
||||
_paPosition[j] += _paVelocity[j] * deltaTime;
|
||||
_paPosition[j + 1] += _paVelocity[j + 1] * deltaTime;
|
||||
_paPosition[j + 2] += _paVelocity[j + 2] * deltaTime;
|
||||
|
||||
_paXmin = glm::min(_paXmin, _paPosition[j]);
|
||||
_paYmin = glm::min(_paYmin, _paPosition[j + 1]);
|
||||
_paZmin = glm::min(_paZmin, _paPosition[j + 2]);
|
||||
_paXmax = glm::max(_paXmax, _paPosition[j]);
|
||||
_paYmax = glm::max(_paYmax, _paPosition[j + 1]);
|
||||
_paZmax = glm::max(_paZmax, _paPosition[j + 2]);
|
||||
|
||||
// massless particles
|
||||
// and simple gravity down
|
||||
_paVelocity[j + 1] += deltaTime * _localGravity;
|
||||
|
||||
emitIdx = (emitIdx + 1) % _maxParticles;
|
||||
_paCount++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::resetSimulation() {
|
||||
for (quint32 i = 0; i < _maxParticles; i++) {
|
||||
quint32 j = i * XYZ_STRIDE;
|
||||
_paLife[i] = -1.0f;
|
||||
_paPosition[j] = 0.0f;
|
||||
_paPosition[j+1] = 0.0f;
|
||||
_paPosition[j+2] = 0.0f;
|
||||
_paVelocity[j] = 0.0f;
|
||||
_paVelocity[j+1] = 0.0f;
|
||||
_paVelocity[j+2] = 0.0f;
|
||||
}
|
||||
_paCount = 0;
|
||||
_paHead = 0;
|
||||
_partialEmit = 0.0f;
|
||||
|
||||
srand(_randSeed);
|
||||
}
|
||||
|
183
libraries/entities/src/ParticleEffectEntityItem.h
Normal file
183
libraries/entities/src/ParticleEffectEntityItem.h
Normal file
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
// ParticleEffectEntityItem.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Some starter code for a particle simulation entity, which could ideally be used for a variety of effects.
|
||||
// This is some really early and rough stuff here. It was enough for now to just get it up and running in the interface.
|
||||
//
|
||||
// Todo's and other notes:
|
||||
// - The simulation should restart when the AnimationLoop's max frame is reached (or passed), but there doesn't seem
|
||||
// to be a good way to set that max frame to something reasonable right now.
|
||||
// - There seems to be a bug whereby entities on the edge of screen will just pop off or on. This is probably due
|
||||
// to my lack of understanding of how entities in the octree are picked for rendering. I am updating the entity
|
||||
// dimensions based on the bounds of the sim, but maybe I need to update a dirty flag or something.
|
||||
// - This should support some kind of pre-roll of the simulation.
|
||||
// - Just to get this out the door, I just did forward Euler integration. There are better ways.
|
||||
// - Gravity always points along the Y axis. Support an actual gravity vector.
|
||||
// - Add the ability to add arbitrary forces to the simulation.
|
||||
// - Add controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented).
|
||||
// - Add drag.
|
||||
// - Add some kind of support for collisions.
|
||||
// - For simplicity, I'm currently just rendering each particle as a cross of four axis-aligned quads. Really, we'd
|
||||
// want multiple render modes, including (the most important) textured billboards (always facing camera). Also, these
|
||||
// should support animated textures.
|
||||
// - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so
|
||||
// there's no gaurantee that different clients will see simulations that look anything like the other.
|
||||
// - MORE?
|
||||
//
|
||||
// Created by Jason Rickwald on 3/2/15.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ParticleEffectEntityItem_h
|
||||
#define hifi_ParticleEffectEntityItem_h
|
||||
|
||||
#include <AnimationLoop.h>
|
||||
#include "EntityItem.h"
|
||||
|
||||
class ParticleEffectEntityItem : public EntityItem {
|
||||
public:
|
||||
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
virtual ~ParticleEffectEntityItem();
|
||||
|
||||
ALLOW_INSTANTIATION // This class can be instantiated
|
||||
|
||||
// methods for getting/setting all properties of this entity
|
||||
virtual EntityItemProperties getProperties() const;
|
||||
virtual bool setProperties(const EntityItemProperties& properties);
|
||||
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const;
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
|
||||
|
||||
virtual void update(const quint64& now);
|
||||
virtual bool needsToCallUpdate() const;
|
||||
|
||||
const rgbColor& getColor() const { return _color; }
|
||||
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
|
||||
|
||||
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
|
||||
void setColor(const xColor& value) {
|
||||
_color[RED_INDEX] = value.red;
|
||||
_color[GREEN_INDEX] = value.green;
|
||||
_color[BLUE_INDEX] = value.blue;
|
||||
}
|
||||
|
||||
void updateShapeType(ShapeType type);
|
||||
virtual ShapeType getShapeType() const { return _shapeType; }
|
||||
|
||||
virtual void debugDump() const;
|
||||
|
||||
static const float DEFAULT_ANIMATION_FRAME_INDEX;
|
||||
void setAnimationFrameIndex(float value);
|
||||
void setAnimationSettings(const QString& value);
|
||||
|
||||
static const bool DEFAULT_ANIMATION_IS_PLAYING;
|
||||
void setAnimationIsPlaying(bool value);
|
||||
|
||||
static const float DEFAULT_ANIMATION_FPS;
|
||||
void setAnimationFPS(float value);
|
||||
|
||||
void setAnimationLoop(bool loop) { _animationLoop.setLoop(loop); }
|
||||
bool getAnimationLoop() const { return _animationLoop.getLoop(); }
|
||||
|
||||
void setAnimationHold(bool hold) { _animationLoop.setHold(hold); }
|
||||
bool getAnimationHold() const { return _animationLoop.getHold(); }
|
||||
|
||||
void setAnimationStartAutomatically(bool startAutomatically) { _animationLoop.setStartAutomatically(startAutomatically); }
|
||||
bool getAnimationStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
|
||||
|
||||
void setAnimationFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
|
||||
float getAnimationFirstFrame() const { return _animationLoop.getFirstFrame(); }
|
||||
|
||||
void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
|
||||
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
|
||||
|
||||
static const quint32 DEFAULT_MAX_PARTICLES;
|
||||
void setMaxParticles(quint32 maxParticles) { _maxParticles = maxParticles; }
|
||||
quint32 getMaxParticles() const { return _maxParticles; }
|
||||
|
||||
static const float DEFAULT_LIFESPAN;
|
||||
void setLifespan(float lifespan) { _lifespan = lifespan; }
|
||||
float getLifespan() const { return _lifespan; }
|
||||
|
||||
static const float DEFAULT_EMIT_RATE;
|
||||
void setEmitRate(float emitRate) { _emitRate = emitRate; }
|
||||
float getEmitRate() const { return _emitRate; }
|
||||
|
||||
static const glm::vec3 DEFAULT_EMIT_DIRECTION;
|
||||
void setEmitDirection(glm::vec3 emitDirection) { _emitDirection = emitDirection; }
|
||||
const glm::vec3& getEmitDirection() const { return _emitDirection; }
|
||||
|
||||
static const float DEFAULT_EMIT_STRENGTH;
|
||||
void setEmitStrength(float emitStrength) { _emitStrength = emitStrength; }
|
||||
float getEmitStrength() const { return _emitStrength; }
|
||||
|
||||
static const float DEFAULT_LOCAL_GRAVITY;
|
||||
void setLocalGravity(float localGravity) { _localGravity = localGravity; }
|
||||
float getLocalGravity() const { return _localGravity; }
|
||||
|
||||
static const float DEFAULT_PARTICLE_RADIUS;
|
||||
void setParticleRadius(float particleRadius) { _particleRadius = particleRadius; }
|
||||
float getParticleRadius() const { return _particleRadius; }
|
||||
|
||||
bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); }
|
||||
float getAnimationFrameIndex() const { return _animationLoop.getFrameIndex(); }
|
||||
float getAnimationFPS() const { return _animationLoop.getFPS(); }
|
||||
QString getAnimationSettings() const;
|
||||
|
||||
protected:
|
||||
|
||||
bool isAnimatingSomething() const;
|
||||
void stepSimulation(float deltaTime);
|
||||
void resetSimulation();
|
||||
|
||||
// the properties of this entity
|
||||
rgbColor _color;
|
||||
quint32 _maxParticles;
|
||||
float _lifespan;
|
||||
float _emitRate;
|
||||
glm::vec3 _emitDirection;
|
||||
float _emitStrength;
|
||||
float _localGravity;
|
||||
float _particleRadius;
|
||||
quint64 _lastAnimated;
|
||||
AnimationLoop _animationLoop;
|
||||
QString _animationSettings;
|
||||
ShapeType _shapeType = SHAPE_TYPE_NONE;
|
||||
|
||||
// all the internals of running the particle sim
|
||||
const quint32 XYZ_STRIDE = 3;
|
||||
float* _paLife;
|
||||
float* _paPosition;
|
||||
float* _paVelocity;
|
||||
float _partialEmit;
|
||||
quint32 _paCount;
|
||||
quint32 _paHead;
|
||||
float _paXmin;
|
||||
float _paXmax;
|
||||
float _paYmin;
|
||||
float _paYmax;
|
||||
float _paZmin;
|
||||
float _paZmax;
|
||||
unsigned int _randSeed;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ParticleEffectEntityItem_h
|
||||
|
|
@ -32,9 +32,6 @@ SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID, const Entit
|
|||
{
|
||||
_type = EntityTypes::Sphere;
|
||||
setProperties(properties);
|
||||
// NOTE: _volumeMultiplier is used to compute volume:
|
||||
// volume = _volumeMultiplier * _dimensions.x * _dimensions.y * _dimensions.z
|
||||
// The formula below looks funny because _dimension.xyz = diameter rather than radius.
|
||||
_volumeMultiplier *= PI / 6.0f;
|
||||
}
|
||||
|
||||
|
@ -117,7 +114,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons
|
|||
distance = glm::distance(origin,hitAt);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -40,10 +40,16 @@ TextEntityItem::TextEntityItem(const EntityItemID& entityItemID, const EntityIte
|
|||
setProperties(properties);
|
||||
}
|
||||
|
||||
const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
|
||||
|
||||
void TextEntityItem::setDimensions(const glm::vec3& value) {
|
||||
// NOTE: Text Entities always have a "depth" of 1cm.
|
||||
float fixedDepth = 0.01f / (float)TREE_SCALE;
|
||||
_dimensions = glm::vec3(value.x, value.y, fixedDepth);
|
||||
_dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH);
|
||||
}
|
||||
|
||||
void TextEntityItem::setDimensionsInDomainUnits(const glm::vec3& value) {
|
||||
// NOTE: Text Entities always have a "depth" of 1cm.
|
||||
_dimensions = glm::vec3(value.x * (float)TREE_SCALE, value.y * (float)TREE_SCALE, TEXT_ENTITY_ITEM_FIXED_DEPTH);
|
||||
}
|
||||
|
||||
EntityItemProperties TextEntityItem::getProperties() const {
|
||||
|
@ -136,7 +142,7 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
|
|||
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
||||
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
|
||||
plane.setNormal(normal);
|
||||
plane.setPoint(_position); // the position is definitely a point on our plane
|
||||
plane.setPoint(getPosition()); // the position is definitely a point on our plane
|
||||
|
||||
bool intersects = plane.findRayIntersection(rayInfo);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
|
||||
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
|
||||
virtual void setDimensions(const glm::vec3& value);
|
||||
virtual void setDimensionsInDomainUnits(const glm::vec3& value);
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
|
||||
|
||||
// methods for getting/setting all properties of an entity
|
||||
|
|
|
@ -46,7 +46,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
|
|||
// which can handle all potential rotations?
|
||||
// the getMaximumAACube is the relaxed form.
|
||||
_oldEntityCube = _existingEntity->getMaximumAACube();
|
||||
_oldEntityBox = _oldEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds
|
||||
_oldEntityBox = _oldEntityCube.clamp(0.0f, (float)TREE_SCALE); // clamp to domain bounds
|
||||
|
||||
// If the old properties doesn't contain the properties required to calculate a bounding box,
|
||||
// get them from the existing entity. Registration point is required to correctly calculate
|
||||
|
@ -59,8 +59,8 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
|
|||
// get the old property value and set it in our properties in order for our bounds
|
||||
// calculations to work.
|
||||
if (_properties.containsPositionChange() && !_properties.containsDimensionsChange()) {
|
||||
glm::vec3 oldDimensionsInMeters = _existingEntity->getDimensions() * (float)TREE_SCALE;
|
||||
_properties.setDimensions(oldDimensionsInMeters);
|
||||
glm::vec3 oldDimensions= _existingEntity->getDimensions();
|
||||
_properties.setDimensions(oldDimensions);
|
||||
|
||||
if (_wantDebug) {
|
||||
qDebug() << " ** setting properties dimensions - had position change, no dimension change **";
|
||||
|
@ -68,8 +68,8 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
|
|||
|
||||
}
|
||||
if (!_properties.containsPositionChange() && _properties.containsDimensionsChange()) {
|
||||
glm::vec3 oldPositionInMeters = _existingEntity->getPosition() * (float)TREE_SCALE;
|
||||
_properties.setPosition(oldPositionInMeters);
|
||||
glm::vec3 oldPosition= _existingEntity->getPosition();
|
||||
_properties.setPosition(oldPosition);
|
||||
|
||||
if (_wantDebug) {
|
||||
qDebug() << " ** setting properties position - had dimensions change, no position change **";
|
||||
|
@ -114,7 +114,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
|
|||
}
|
||||
|
||||
} else {
|
||||
_newEntityCube = _properties.getMaximumAACubeInTreeUnits();
|
||||
_newEntityCube = _properties.getMaximumAACube();
|
||||
_removeOld = true; // our properties are going to move us, so remember this for later processing
|
||||
|
||||
if (_wantDebug) {
|
||||
|
@ -122,7 +122,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
|
|||
}
|
||||
}
|
||||
|
||||
_newEntityBox = _newEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds
|
||||
_newEntityBox = _newEntityCube.clamp(0.0f, (float)TREE_SCALE); // clamp to domain bounds
|
||||
|
||||
|
||||
if (_wantDebug) {
|
||||
|
|
|
@ -1266,6 +1266,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
|
|||
QVector<QString> humanIKJointIDs(humanIKJointNames.size());
|
||||
|
||||
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
|
||||
|
||||
QMultiHash<QByteArray, WeightedIndex> blendshapeIndices;
|
||||
for (int i = 0;; i++) {
|
||||
QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i];
|
||||
|
@ -1720,12 +1721,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
|
|||
|
||||
} else if (object.properties.last() == "BlendShapeChannel") {
|
||||
QByteArray name = object.properties.at(1).toByteArray();
|
||||
|
||||
name = name.left(name.indexOf('\0'));
|
||||
if (!blendshapeIndices.contains(name)) {
|
||||
// try everything after the dot
|
||||
name = name.mid(name.lastIndexOf('.') + 1);
|
||||
}
|
||||
QString id = getID(object.properties);
|
||||
geometry.blendshapeChannelNames << name;
|
||||
foreach (const WeightedIndex& index, blendshapeIndices.values(name)) {
|
||||
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
|
||||
QString getModelNameOfMesh(int meshIndex) const;
|
||||
|
||||
QList<QString> blendshapeChannelNames;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FBXGeometry)
|
||||
|
|
|
@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
return 1;
|
||||
case PacketTypeEntityAddOrEdit:
|
||||
case PacketTypeEntityData:
|
||||
return VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES;
|
||||
return VERSION_ENTITIES_USE_METERS_AND_RADIANS;
|
||||
case PacketTypeEntityErase:
|
||||
return 2;
|
||||
case PacketTypeAudioStreamStats:
|
||||
|
|
|
@ -129,6 +129,8 @@ const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6;
|
|||
const PacketVersion VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME = 7;
|
||||
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE = 8;
|
||||
const PacketVersion VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES = 9;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_PARTICLES = 10;
|
||||
const PacketVersion VERSION_ENTITIES_USE_METERS_AND_RADIANS = 11;
|
||||
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -12,9 +12,10 @@
|
|||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
|
@ -314,13 +315,51 @@ void Resource::handleReplyTimeout() {
|
|||
"received" << _bytesReceived << "total" << _bytesTotal);
|
||||
}
|
||||
|
||||
void Resource::maybeRefresh() {
|
||||
if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
QVariant variant = reply->header(QNetworkRequest::LastModifiedHeader);
|
||||
QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url);
|
||||
if (variant.isValid() && variant.canConvert<QDateTime>() && metaData.isValid()) {
|
||||
QDateTime lastModified = variant.value<QDateTime>();
|
||||
QDateTime lastModifiedOld = metaData.lastModified();
|
||||
if (lastModified.isValid() && lastModifiedOld.isValid() &&
|
||||
lastModifiedOld == lastModified) {
|
||||
// We don't need to update, return
|
||||
return;
|
||||
}
|
||||
}
|
||||
qDebug() << "Loaded" << _url.fileName() << "from the disk cache but the network version is newer, refreshing.";
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::makeRequest() {
|
||||
_reply = NetworkAccessManager::getInstance().get(_request);
|
||||
|
||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
||||
connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished()));
|
||||
|
||||
|
||||
if (_reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool()) {
|
||||
// If the file as been updated since it was cached, refresh it
|
||||
QNetworkRequest request(_request);
|
||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().head(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &Resource::maybeRefresh);
|
||||
} else {
|
||||
if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) {
|
||||
QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url);
|
||||
if (metaData.expirationDate().isNull() || metaData.expirationDate() <= QDateTime::currentDateTime()) {
|
||||
// If the expiration date is NULL or in the past,
|
||||
// put one far enough away that it won't be an issue.
|
||||
metaData.setExpirationDate(QDateTime::currentDateTime().addYears(100));
|
||||
NetworkAccessManager::getInstance().cache()->updateMetaData(metaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_replyTimer = new QTimer(this);
|
||||
connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout()));
|
||||
_replyTimer->setSingleShot(true);
|
||||
|
|
|
@ -149,7 +149,7 @@ public:
|
|||
|
||||
/// For loading resources, returns the load progress.
|
||||
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
|
||||
|
||||
|
||||
/// Refreshes the resource.
|
||||
void refresh();
|
||||
|
||||
|
@ -169,6 +169,10 @@ signals:
|
|||
protected slots:
|
||||
|
||||
void attemptRequest();
|
||||
|
||||
/// Refreshes the resource if the last modified date on the network
|
||||
/// is greater than the last modified date in the cache.
|
||||
void maybeRefresh();
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -274,7 +274,7 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch
|
|||
|
||||
if (destinationElement->getScale() < SCALE_AT_DANGEROUSLY_DEEP_RECURSION) {
|
||||
qDebug() << "UNEXPECTED: readElementData() destination element is unreasonably small ["
|
||||
<< destinationElement->getScale() * (float)TREE_SCALE << " meters] "
|
||||
<< destinationElement->getScale() << " meters] "
|
||||
<< " Discarding " << bytesAvailable << " remaining bytes.";
|
||||
return bytesAvailable; // assume we read the entire buffer...
|
||||
}
|
||||
|
@ -706,7 +706,7 @@ bool findRayIntersectionOp(OctreeElement* element, void* extraData) {
|
|||
bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject,
|
||||
Octree::lockType lockType, bool* accurateResult, bool precisionPicking) {
|
||||
RayArgs args = { origin / (float)(TREE_SCALE), direction, element, distance, face,
|
||||
RayArgs args = { origin, direction, element, distance, face,
|
||||
intersectedObject, false, precisionPicking};
|
||||
distance = FLT_MAX;
|
||||
|
||||
|
@ -726,10 +726,6 @@ bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
|
||||
recurseTreeWithOperation(findRayIntersectionOp, &args);
|
||||
|
||||
if (args.found) {
|
||||
args.distance *= (float)(TREE_SCALE); // scale back up to meters
|
||||
}
|
||||
|
||||
if (gotLock) {
|
||||
unlock();
|
||||
}
|
||||
|
@ -753,8 +749,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
|
|||
SphereArgs* args = static_cast<SphereArgs*>(extraData);
|
||||
|
||||
// coarse check against bounds
|
||||
const AACube& box = element->getAACube();
|
||||
if (!box.expandedContains(args->center, args->radius)) {
|
||||
if (!element->getAACube().expandedContains(args->center, args->radius)) {
|
||||
return false;
|
||||
}
|
||||
if (element->hasContent()) {
|
||||
|
@ -762,7 +757,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
|
|||
if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) {
|
||||
// NOTE: it is possible for this penetration accumulation algorithm to produce a
|
||||
// final penetration vector with zero length.
|
||||
args->penetration = addPenetrations(args->penetration, elementPenetration * (float)(TREE_SCALE));
|
||||
args->penetration = addPenetrations(args->penetration, elementPenetration);
|
||||
args->found = true;
|
||||
}
|
||||
}
|
||||
|
@ -776,8 +771,8 @@ bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::v
|
|||
void** penetratedObject, Octree::lockType lockType, bool* accurateResult) {
|
||||
|
||||
SphereArgs args = {
|
||||
center / (float)(TREE_SCALE),
|
||||
radius / (float)(TREE_SCALE),
|
||||
center,
|
||||
radius,
|
||||
penetration,
|
||||
false,
|
||||
NULL };
|
||||
|
@ -838,14 +833,13 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
|
|||
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
|
||||
|
||||
// coarse check against bounds
|
||||
const AACube& box = element->getAACube();
|
||||
if (!box.expandedIntersectsSegment(args->start, args->end, args->radius)) {
|
||||
if (!element->getAACube().expandedIntersectsSegment(args->start, args->end, args->radius)) {
|
||||
return false;
|
||||
}
|
||||
if (element->hasContent()) {
|
||||
glm::vec3 nodePenetration;
|
||||
if (box.findCapsulePenetration(args->start, args->end, args->radius, nodePenetration)) {
|
||||
args->penetration = addPenetrations(args->penetration, nodePenetration * (float)(TREE_SCALE));
|
||||
if (element->getAACube().findCapsulePenetration(args->start, args->end, args->radius, nodePenetration)) {
|
||||
args->penetration = addPenetrations(args->penetration, nodePenetration);
|
||||
args->found = true;
|
||||
}
|
||||
}
|
||||
|
@ -872,8 +866,7 @@ bool findContentInCubeOp(OctreeElement* element, void* extraData) {
|
|||
ContentArgs* args = static_cast<ContentArgs*>(extraData);
|
||||
|
||||
// coarse check against bounds
|
||||
AACube cube = element->getAACube();
|
||||
cube.scale(TREE_SCALE);
|
||||
const AACube& cube = element->getAACube();
|
||||
if (!cube.touches(args->cube)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -892,12 +885,7 @@ bool findContentInCubeOp(OctreeElement* element, void* extraData) {
|
|||
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
|
||||
glm::vec3& penetration, Octree::lockType lockType, bool* accurateResult) {
|
||||
|
||||
CapsuleArgs args = {
|
||||
start / (float)(TREE_SCALE),
|
||||
end / (float)(TREE_SCALE),
|
||||
radius / (float)(TREE_SCALE),
|
||||
penetration,
|
||||
false };
|
||||
CapsuleArgs args = { start, end, radius, penetration, false };
|
||||
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
bool gotLock = false;
|
||||
|
@ -945,8 +933,7 @@ public:
|
|||
// Find the smallest colored voxel enclosing a point (if there is one)
|
||||
bool getElementEnclosingOperation(OctreeElement* element, void* extraData) {
|
||||
GetElementEnclosingArgs* args = static_cast<GetElementEnclosingArgs*>(extraData);
|
||||
AACube elementBox = element->getAACube();
|
||||
if (elementBox.contains(args->point)) {
|
||||
if (element->getAACube().contains(args->point)) {
|
||||
if (element->hasContent() && element->isLeaf()) {
|
||||
// we've reached a solid leaf containing the point, return the element.
|
||||
args->element = element;
|
||||
|
@ -1212,9 +1199,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
// If the user also asked for occlusion culling, check if this element is occluded, but only if it's not a leaf.
|
||||
// leaf occlusion is handled down below when we check child nodes
|
||||
if (params.wantOcclusionCulling && !element->isLeaf()) {
|
||||
AACube voxelBox = element->getAACube();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
OctreeProjectedPolygon* voxelPolygon = new OctreeProjectedPolygon(params.viewFrustum->getProjectedPolygon(voxelBox));
|
||||
OctreeProjectedPolygon* voxelPolygon =
|
||||
new OctreeProjectedPolygon(params.viewFrustum->getProjectedPolygon(element->getAACube()));
|
||||
|
||||
// In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion
|
||||
// culling and proceed as normal
|
||||
|
@ -1365,10 +1351,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
if (params.wantOcclusionCulling && childElement->isLeaf()) {
|
||||
// Don't check occlusion here, just add them to our distance ordered array...
|
||||
|
||||
AACube voxelBox = childElement->getAACube();
|
||||
voxelBox.scale(TREE_SCALE);
|
||||
OctreeProjectedPolygon* voxelPolygon = new OctreeProjectedPolygon(
|
||||
params.viewFrustum->getProjectedPolygon(voxelBox));
|
||||
params.viewFrustum->getProjectedPolygon(childElement->getAACube()));
|
||||
|
||||
// In order to check occlusion culling, the shadow has to be "all in view" otherwise, we ignore occlusion
|
||||
// culling and proceed as normal
|
||||
|
@ -2084,58 +2068,6 @@ bool Octree::countOctreeElementsOperation(OctreeElement* element, void* extraDat
|
|||
return true; // keep going
|
||||
}
|
||||
|
||||
void Octree::copySubTreeIntoNewTree(OctreeElement* startElement, Octree* destinationTree, bool rebaseToRoot) {
|
||||
OctreeElementBag elementBag;
|
||||
elementBag.insert(startElement);
|
||||
int chopLevels = 0;
|
||||
if (rebaseToRoot) {
|
||||
chopLevels = numberOfThreeBitSectionsInCode(startElement->getOctalCode());
|
||||
}
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS, chopLevels);
|
||||
OctreeElementExtraEncodeData extraEncodeData;
|
||||
params.extraEncodeData = &extraEncodeData;
|
||||
|
||||
OctreePacketData packetData;
|
||||
|
||||
while (!elementBag.isEmpty()) {
|
||||
OctreeElement* subTree = elementBag.extract();
|
||||
packetData.reset(); // reset the packet between usage
|
||||
// ask our tree to write a bitsteam
|
||||
encodeTreeBitstream(subTree, &packetData, elementBag, params);
|
||||
// ask destination tree to read the bitstream
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
|
||||
destinationTree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinationElement) {
|
||||
OctreeElementBag elementBag;
|
||||
// If we were given a specific element, start from there, otherwise start from root
|
||||
elementBag.insert(sourceTree->_rootElement);
|
||||
|
||||
OctreePacketData packetData;
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
|
||||
OctreeElementExtraEncodeData extraEncodeData;
|
||||
params.extraEncodeData = &extraEncodeData;
|
||||
|
||||
while (!elementBag.isEmpty()) {
|
||||
OctreeElement* subTree = elementBag.extract();
|
||||
|
||||
packetData.reset(); // reset between usage
|
||||
|
||||
// ask our tree to write a bitsteam
|
||||
sourceTree->encodeTreeBitstream(subTree, &packetData, elementBag, params);
|
||||
|
||||
// ask destination tree to read the bitstream
|
||||
bool wantImportProgress = true;
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationElement,
|
||||
0, SharedNodePointer(), wantImportProgress);
|
||||
readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::cancelImport() {
|
||||
_stopImport = true;
|
||||
}
|
||||
|
|
|
@ -270,6 +270,9 @@ public:
|
|||
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
|
||||
void recurseTreeWithPostOperation(RecurseOctreeOperation operation, void* extraData = NULL);
|
||||
|
||||
/// \param operation type of operation
|
||||
/// \param point point in world-frame (meters)
|
||||
/// \param extraData hook for user data to be interpreted by special context
|
||||
void recurseTreeWithOperationDistanceSorted(RecurseOctreeOperation operation,
|
||||
const glm::vec3& point, void* extraData = NULL);
|
||||
|
||||
|
@ -308,8 +311,13 @@ public:
|
|||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration,
|
||||
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
|
||||
|
||||
/// \param cube query cube in world-frame (meters)
|
||||
/// \param[out] cubes list of cubes (world-frame) of child elements that have content
|
||||
bool findContentInCube(const AACube& cube, CubeList& cubes);
|
||||
|
||||
/// \param point query point in world-frame (meters)
|
||||
/// \param lockType how to lock the tree (Lock, TryLock, NoLock)
|
||||
/// \param[out] accurateResult pointer to output result, will be set "true" or "false" if non-null
|
||||
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,
|
||||
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
|
||||
|
||||
|
@ -323,9 +331,6 @@ public:
|
|||
|
||||
unsigned long getOctreeElementsCount();
|
||||
|
||||
void copySubTreeIntoNewTree(OctreeElement* startElement, Octree* destinationTree, bool rebaseToRoot);
|
||||
void copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinationElement);
|
||||
|
||||
bool getShouldReaverage() const { return _shouldReaverage; }
|
||||
|
||||
void recurseElementWithOperation(OctreeElement* element, RecurseOctreeOperation operation,
|
||||
|
|
|
@ -194,14 +194,14 @@ void OctreeElement::setShouldRender(bool shouldRender) {
|
|||
}
|
||||
|
||||
void OctreeElement::calculateAACube() {
|
||||
glm::vec3 corner;
|
||||
|
||||
// copy corner into cube
|
||||
copyFirstVertexForCode(getOctalCode(),(float*)&corner);
|
||||
glm::vec3 corner;
|
||||
copyFirstVertexForCode(getOctalCode(), (float*)&corner);
|
||||
|
||||
// this tells you the "size" of the voxel
|
||||
float voxelScale = 1 / powf(2, numberOfThreeBitSectionsInCode(getOctalCode()));
|
||||
_cube.setBox(corner,voxelScale);
|
||||
float voxelScale = (float)TREE_SCALE / powf(2.0f, numberOfThreeBitSectionsInCode(getOctalCode()));
|
||||
corner *= (float)TREE_SCALE;
|
||||
_cube.setBox(corner, voxelScale);
|
||||
}
|
||||
|
||||
void OctreeElement::deleteChildAtIndex(int childIndex) {
|
||||
|
@ -1221,9 +1221,7 @@ float OctreeElement::getEnclosingRadius() const {
|
|||
}
|
||||
|
||||
ViewFrustum::location OctreeElement::inFrustum(const ViewFrustum& viewFrustum) const {
|
||||
AACube cube = _cube; // use temporary cube so we can scale it
|
||||
cube.scale(TREE_SCALE);
|
||||
return viewFrustum.cubeInFrustum(cube);
|
||||
return viewFrustum.cubeInFrustum(_cube);
|
||||
}
|
||||
|
||||
// There are two types of nodes for which we want to "render"
|
||||
|
@ -1257,14 +1255,13 @@ bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float
|
|||
// does as much math as possible in voxel scale and then scales up to TREE_SCALE at end
|
||||
float OctreeElement::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const {
|
||||
glm::vec3 furthestPoint;
|
||||
viewFrustum.getFurthestPointFromCameraVoxelScale(getAACube(), furthestPoint);
|
||||
glm::vec3 temp = viewFrustum.getPositionVoxelScale() - furthestPoint;
|
||||
float distanceToFurthestPoint = sqrtf(glm::dot(temp, temp));
|
||||
return distanceToFurthestPoint * (float)TREE_SCALE;
|
||||
viewFrustum.getFurthestPointFromCamera(_cube, furthestPoint);
|
||||
glm::vec3 temp = viewFrustum.getPosition() - furthestPoint;
|
||||
return sqrtf(glm::dot(temp, temp));
|
||||
}
|
||||
|
||||
float OctreeElement::distanceToCamera(const ViewFrustum& viewFrustum) const {
|
||||
glm::vec3 center = _cube.calcCenter() * (float)TREE_SCALE;
|
||||
glm::vec3 center = _cube.calcCenter();
|
||||
glm::vec3 temp = viewFrustum.getPosition() - center;
|
||||
float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp));
|
||||
return distanceToVoxelCenter;
|
||||
|
@ -1337,16 +1334,12 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
|
|||
|
||||
keepSearching = true; // assume that we will continue searching after this.
|
||||
|
||||
AACube cube = getAACube();
|
||||
float distanceToElementCube = std::numeric_limits<float>::max();
|
||||
float distanceToElementDetails = distance;
|
||||
BoxFace localFace;
|
||||
|
||||
AACube debugCube = cube;
|
||||
debugCube.scale((float)TREE_SCALE);
|
||||
|
||||
// if the ray doesn't intersect with our cube, we can stop searching!
|
||||
if (!cube.findRayIntersection(origin, direction, distanceToElementCube, localFace)) {
|
||||
if (!_cube.findRayIntersection(origin, direction, distanceToElementCube, localFace)) {
|
||||
keepSearching = false; // no point in continuing to search
|
||||
return false; // we did not intersect
|
||||
}
|
||||
|
@ -1358,7 +1351,7 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
|
|||
|
||||
// if the distance to the element cube is not less than the current best distance, then it's not possible
|
||||
// for any details inside the cube to be closer so we don't need to consider them.
|
||||
if (cube.contains(origin) || distanceToElementCube < distance) {
|
||||
if (_cube.contains(origin) || distanceToElementCube < distance) {
|
||||
|
||||
if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails,
|
||||
face, intersectedObject, precisionPicking, distanceToElementCube)) {
|
||||
|
@ -1392,6 +1385,7 @@ bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const g
|
|||
|
||||
bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const {
|
||||
// center and radius are in meters, so we have to scale the _cube into world-frame
|
||||
return _cube.findSpherePenetration(center, radius, penetration);
|
||||
}
|
||||
|
||||
|
@ -1529,15 +1523,15 @@ int OctreeElement::getMyChildContaining(const AACube& cube) const {
|
|||
if (cubeScale > ourScale) {
|
||||
qDebug() << "UNEXPECTED -- OctreeElement::getMyChildContaining() -- (cubeScale > ourScale)";
|
||||
qDebug() << " cube=" << cube;
|
||||
qDebug() << " elements AACube=" << getAACube();
|
||||
qDebug() << " elements AACube=" << _cube;
|
||||
qDebug() << " cubeScale=" << cubeScale;
|
||||
qDebug() << " ourScale=" << ourScale;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Determine which of our children the minimum and maximum corners of the cube live in...
|
||||
glm::vec3 cubeCornerMinimum = glm::clamp(cube.getCorner(), 0.0f, 1.0f);
|
||||
glm::vec3 cubeCornerMaximum = glm::clamp(cube.calcTopFarLeft(), 0.0f, 1.0f);
|
||||
glm::vec3 cubeCornerMinimum = glm::clamp(cube.getCorner(), 0.0f, (float)TREE_SCALE);
|
||||
glm::vec3 cubeCornerMaximum = glm::clamp(cube.calcTopFarLeft(), 0.0f, (float)TREE_SCALE);
|
||||
|
||||
if (_cube.contains(cubeCornerMinimum) && _cube.contains(cubeCornerMaximum)) {
|
||||
int childIndexCubeMinimum = getMyChildContainingPoint(cubeCornerMinimum);
|
||||
|
|
|
@ -125,6 +125,10 @@ public:
|
|||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
|
||||
|
||||
/// \param center center of sphere in meters
|
||||
/// \param radius radius of sphere in meters
|
||||
/// \param[out] penetration pointing into cube from sphere
|
||||
/// \param penetratedObject unused
|
||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const;
|
||||
|
||||
|
|
|
@ -101,7 +101,6 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
jurisdictions.unlock();
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
serverBounds.scale(TREE_SCALE);
|
||||
|
||||
ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
|
||||
|
||||
|
@ -170,7 +169,6 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
jurisdictions.unlock();
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
serverBounds.scale(TREE_SCALE);
|
||||
|
||||
ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
|
||||
if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
|
||||
|
|
|
@ -65,7 +65,8 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar
|
|||
unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray);
|
||||
QUuid sourceUUID = uuidFromPacketHeader(dataByteArray);
|
||||
PacketType expectedType = getExpectedPacketType();
|
||||
PacketVersion expectedVersion = _tree->expectedVersion(); // TODO: would be better to read this from the packet!
|
||||
// packetVersion is the second byte
|
||||
PacketVersion packetVersion = dataByteArray[1];
|
||||
|
||||
if(command == expectedType) {
|
||||
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails);
|
||||
|
@ -117,7 +118,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar
|
|||
if (sectionLength) {
|
||||
// ask the VoxelTree to read the bitstream into the tree
|
||||
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL,
|
||||
sourceUUID, sourceNode, false, expectedVersion);
|
||||
sourceUUID, sourceNode, false, packetVersion);
|
||||
_tree->lockForWrite();
|
||||
OctreePacketData packetData(packetIsCompressed);
|
||||
packetData.loadFinalizedContent(dataAt, sectionLength);
|
||||
|
|
|
@ -837,33 +837,6 @@ void ViewFrustum::getFurthestPointFromCamera(const AACube& box, glm::vec3& furth
|
|||
}
|
||||
}
|
||||
|
||||
void ViewFrustum::getFurthestPointFromCameraVoxelScale(const AACube& box, glm::vec3& furthestPoint) const {
|
||||
const glm::vec3& bottomNearRight = box.getCorner();
|
||||
float scale = box.getScale();
|
||||
float halfScale = scale * 0.5f;
|
||||
|
||||
if (_positionVoxelScale.x < bottomNearRight.x + halfScale) {
|
||||
// we are to the right of the center, so the left edge is furthest
|
||||
furthestPoint.x = bottomNearRight.x + scale;
|
||||
} else {
|
||||
furthestPoint.x = bottomNearRight.x;
|
||||
}
|
||||
|
||||
if (_positionVoxelScale.y < bottomNearRight.y + halfScale) {
|
||||
// we are below of the center, so the top edge is furthest
|
||||
furthestPoint.y = bottomNearRight.y + scale;
|
||||
} else {
|
||||
furthestPoint.y = bottomNearRight.y;
|
||||
}
|
||||
|
||||
if (_positionVoxelScale.z < bottomNearRight.z + halfScale) {
|
||||
// we are to the near side of the center, so the far side edge is furthest
|
||||
furthestPoint.z = bottomNearRight.z + scale;
|
||||
} else {
|
||||
furthestPoint.z = bottomNearRight.z;
|
||||
}
|
||||
}
|
||||
|
||||
float ViewFrustum::distanceToCamera(const glm::vec3& point) const {
|
||||
glm::vec3 temp = getPosition() - point;
|
||||
float distanceToPoint = sqrtf(glm::dot(temp, temp));
|
||||
|
|
|
@ -30,19 +30,18 @@ const float DEFAULT_KEYHOLE_RADIUS = 3.0f;
|
|||
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f;
|
||||
const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f;
|
||||
const float DEFAULT_NEAR_CLIP = 0.08f;
|
||||
const float DEFAULT_FAR_CLIP = TREE_SCALE;
|
||||
const float DEFAULT_FAR_CLIP = (float)TREE_SCALE;
|
||||
|
||||
class ViewFrustum {
|
||||
public:
|
||||
ViewFrustum();
|
||||
|
||||
// setters for camera attributes
|
||||
void setPosition(const glm::vec3& p) { _position = p; _positionVoxelScale = (p / (float)TREE_SCALE); }
|
||||
void setPosition(const glm::vec3& p) { _position = p; }
|
||||
void setOrientation(const glm::quat& orientationAsQuaternion);
|
||||
|
||||
// getters for camera attributes
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::vec3& getPositionVoxelScale() const { return _positionVoxelScale; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
const glm::vec3& getDirection() const { return _direction; }
|
||||
const glm::vec3& getUp() const { return _up; }
|
||||
|
@ -119,9 +118,6 @@ public:
|
|||
OctreeProjectedPolygon getProjectedPolygon(const AACube& box) const;
|
||||
void getFurthestPointFromCamera(const AACube& box, glm::vec3& furthestPoint) const;
|
||||
|
||||
// assumes box is in voxel scale, not TREE_SCALE, will scale view frustum's position accordingly
|
||||
void getFurthestPointFromCameraVoxelScale(const AACube& box, glm::vec3& furthestPoint) const;
|
||||
|
||||
float distanceToCamera(const glm::vec3& point) const;
|
||||
|
||||
void evalProjectionMatrix(glm::mat4& proj) const;
|
||||
|
@ -135,8 +131,7 @@ private:
|
|||
void calculateOrthographic();
|
||||
|
||||
// camera location/orientation attributes
|
||||
glm::vec3 _position = glm::vec3(0.0f); // the position in TREE_SCALE
|
||||
glm::vec3 _positionVoxelScale = glm::vec3(0.0f); // the position in voxel scale
|
||||
glm::vec3 _position = glm::vec3(0.0f); // the position in world-frame
|
||||
glm::quat _orientation = glm::quat();
|
||||
|
||||
// calculated for orientation
|
||||
|
|
|
@ -84,23 +84,22 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
// bypass const-ness so we can remember the substep
|
||||
const_cast<EntityMotionState*>(this)->_lastKinematicSubstep = substep;
|
||||
}
|
||||
worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset()));
|
||||
worldTrans.setOrigin(glmToBullet(_entity->getPosition() - ObjectMotionState::getWorldOffset()));
|
||||
worldTrans.setRotation(glmToBullet(_entity->getRotation()));
|
||||
}
|
||||
|
||||
// This callback is invoked by the physics simulation at the end of each simulation frame...
|
||||
// iff the corresponding RigidBody is DYNAMIC and has moved.
|
||||
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||
_entity->setPositionInMeters(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
|
||||
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
|
||||
_entity->setRotation(bulletToGLM(worldTrans.getRotation()));
|
||||
|
||||
glm::vec3 v;
|
||||
getVelocity(v);
|
||||
_entity->setVelocityInMeters(v);
|
||||
_entity->setVelocity(v);
|
||||
|
||||
getAngularVelocity(v);
|
||||
// DANGER! EntityItem stores angularVelocity in degrees/sec!!!
|
||||
_entity->setAngularVelocity(glm::degrees(v));
|
||||
_entity->setAngularVelocity(v);
|
||||
|
||||
_entity->setLastSimulated(usecTimestampNow());
|
||||
|
||||
|
@ -119,7 +118,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
|
||||
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
|
||||
if (flags & EntityItem::DIRTY_POSITION) {
|
||||
_sentPosition = _entity->getPositionInMeters() - ObjectMotionState::getWorldOffset();
|
||||
_sentPosition = _entity->getPosition() - ObjectMotionState::getWorldOffset();
|
||||
btTransform worldTrans;
|
||||
worldTrans.setOrigin(glmToBullet(_sentPosition));
|
||||
|
||||
|
@ -156,14 +155,13 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
|
|||
|
||||
void EntityMotionState::updateObjectVelocities() {
|
||||
if (_body) {
|
||||
_sentVelocity = _entity->getVelocityInMeters();
|
||||
_sentVelocity = _entity->getVelocity();
|
||||
setVelocity(_sentVelocity);
|
||||
|
||||
// DANGER! EntityItem stores angularVelocity in degrees/sec!!!
|
||||
_sentAngularVelocity = glm::radians(_entity->getAngularVelocity());
|
||||
_sentAngularVelocity = _entity->getAngularVelocity();
|
||||
setAngularVelocity(_sentAngularVelocity);
|
||||
|
||||
_sentAcceleration = _entity->getGravityInMeters();
|
||||
_sentAcceleration = _entity->getGravity();
|
||||
setGravity(_sentAcceleration);
|
||||
|
||||
_body->setActivationState(ACTIVE_TAG);
|
||||
|
@ -219,8 +217,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
properties.setVelocity(_sentVelocity);
|
||||
_sentAcceleration = bulletToGLM(_body->getGravity());
|
||||
properties.setGravity(_sentAcceleration);
|
||||
// DANGER! EntityItem stores angularVelocity in degrees/sec!!!
|
||||
properties.setAngularVelocity(glm::degrees(_sentAngularVelocity));
|
||||
properties.setAngularVelocity(_sentAngularVelocity);
|
||||
}
|
||||
|
||||
// RELIABLE_SEND_HACK: count number of updates for entities at rest so we can stop sending them after some limit.
|
||||
|
|
|
@ -471,17 +471,17 @@ AABox AABox::clamp(float min, float max) const {
|
|||
return AABox(clampedCorner, clampedScale);
|
||||
}
|
||||
|
||||
AABox& AABox::operator += (const glm::vec3& point) {
|
||||
_corner = glm::min(_corner, point);
|
||||
_scale = glm::max(_scale, point - _corner);
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
AABox& AABox::operator += (const AABox& box) {
|
||||
if (!box.isInvalid()) {
|
||||
(*this) += box._corner;
|
||||
_scale = glm::max(_scale, box.calcTopFarLeft() - _corner);
|
||||
}
|
||||
return (*this);
|
||||
}
|
||||
AABox& AABox::operator += (const glm::vec3& point) {
|
||||
_corner = glm::min(_corner, point);
|
||||
_scale = glm::max(_scale, point - _corner);
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
AABox& AABox::operator += (const AABox& box) {
|
||||
if (!box.isInvalid()) {
|
||||
(*this) += box._corner;
|
||||
_scale = glm::max(_scale, box.calcTopFarLeft() - _corner);
|
||||
}
|
||||
return (*this);
|
||||
}
|
||||
|
|
|
@ -72,9 +72,9 @@ public:
|
|||
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
|
||||
AABox clamp(float min, float max) const;
|
||||
|
||||
AABox& operator += (const glm::vec3& point);
|
||||
AABox& operator += (const AABox& box);
|
||||
|
||||
AABox& operator += (const glm::vec3& point);
|
||||
AABox& operator += (const AABox& box);
|
||||
|
||||
bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits<float>::infinity()); }
|
||||
|
||||
private:
|
||||
|
|
|
@ -129,18 +129,17 @@ unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNu
|
|||
void voxelDetailsForCode(const unsigned char* octalCode, VoxelPositionSize& voxelPositionSize) {
|
||||
float output[3];
|
||||
memset(&output[0], 0, 3 * sizeof(float));
|
||||
float currentScale = 1.0;
|
||||
float currentScale = 1.0f;
|
||||
|
||||
if (octalCode) {
|
||||
for (int i = 0; i < numberOfThreeBitSectionsInCode(octalCode); i++) {
|
||||
currentScale *= 0.5;
|
||||
currentScale *= 0.5f;
|
||||
int sectionIndex = sectionValue(octalCode + 1 + (BITS_IN_OCTAL * i / BITS_IN_BYTE),
|
||||
(BITS_IN_OCTAL * i) % BITS_IN_BYTE);
|
||||
|
||||
for (int j = 0; j < BITS_IN_OCTAL; j++) {
|
||||
output[j] += currentScale * (int)oneAtBit(sectionIndex, (BITS_IN_BYTE - BITS_IN_OCTAL) + j);
|
||||
output[j] += currentScale * (float)oneAtBit(sectionIndex, (BITS_IN_BYTE - BITS_IN_OCTAL) + j);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
voxelPositionSize.x = output[0];
|
||||
|
@ -152,7 +151,7 @@ void voxelDetailsForCode(const unsigned char* octalCode, VoxelPositionSize& voxe
|
|||
void copyFirstVertexForCode(const unsigned char* octalCode, float* output) {
|
||||
memset(output, 0, 3 * sizeof(float));
|
||||
|
||||
float currentScale = 0.5;
|
||||
float currentScale = 0.5f;
|
||||
|
||||
for (int i = 0; i < numberOfThreeBitSectionsInCode(octalCode); i++) {
|
||||
int sectionIndex = sectionValue(octalCode + 1 + (3 * i / 8), (3 * i) % 8);
|
||||
|
@ -165,12 +164,6 @@ void copyFirstVertexForCode(const unsigned char* octalCode, float* output) {
|
|||
}
|
||||
}
|
||||
|
||||
float * firstVertexForCode(const unsigned char* octalCode) {
|
||||
float * firstVertex = new float[3];
|
||||
copyFirstVertexForCode(octalCode, firstVertex);
|
||||
return firstVertex;
|
||||
}
|
||||
|
||||
OctalCodeComparison compareOctalCodes(const unsigned char* codeA, const unsigned char* codeB) {
|
||||
if (!codeA || !codeB) {
|
||||
return ILLEGAL_CODE;
|
||||
|
|
|
@ -43,9 +43,6 @@ const int CHECK_NODE_ONLY = -1;
|
|||
bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent,
|
||||
int descendentsChild = CHECK_NODE_ONLY);
|
||||
|
||||
// Note: copyFirstVertexForCode() is preferred because it doesn't allocate memory for the return
|
||||
// but other than that these do the same thing.
|
||||
float * firstVertexForCode(const unsigned char* octalCode);
|
||||
void copyFirstVertexForCode(const unsigned char* octalCode, float* output);
|
||||
|
||||
struct VoxelPositionSize {
|
||||
|
|
|
@ -45,12 +45,9 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
entityID.isKnownID = false; // this is a temporary workaround to allow local tree entities to be added with known IDs
|
||||
EntityItemProperties properties;
|
||||
float oneMeter = 1.0f;
|
||||
//float halfMeter = oneMeter / 2.0f;
|
||||
float halfOfDomain = TREE_SCALE * 0.5f;
|
||||
glm::vec3 positionNearOriginInMeters(oneMeter, oneMeter, oneMeter); // when using properties, these are in meter not tree units
|
||||
glm::vec3 positionAtCenterInMeters(halfOfDomain, halfOfDomain, halfOfDomain);
|
||||
glm::vec3 positionNearOriginInTreeUnits = positionNearOriginInMeters / (float)TREE_SCALE;
|
||||
glm::vec3 positionAtCenterInTreeUnits = positionAtCenterInMeters / (float)TREE_SCALE;
|
||||
glm::vec3 positionNearOrigin(oneMeter, oneMeter, oneMeter); // when using properties, these are in meter not tree units
|
||||
glm::vec3 positionAtCenter(halfOfDomain, halfOfDomain, halfOfDomain);
|
||||
|
||||
{
|
||||
testsTaken++;
|
||||
|
@ -59,28 +56,27 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
|
||||
}
|
||||
|
||||
properties.setPosition(positionAtCenterInMeters);
|
||||
properties.setPosition(positionAtCenter);
|
||||
// TODO: Fix these unit tests.
|
||||
//properties.setRadius(halfMeter);
|
||||
//properties.setModelURL("http://s3.amazonaws.com/hifi-public/ozan/theater.fbx");
|
||||
|
||||
tree.addEntity(entityID, properties);
|
||||
|
||||
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
|
||||
float targetRadius = oneMeter * 2.0f;
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenter, targetRadius);
|
||||
const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
|
||||
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
|
||||
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
const AACube& elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
|
||||
if (verbose) {
|
||||
qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
|
||||
qDebug() << "foundEntityByID=" << foundEntityByID;
|
||||
qDebug() << "containingElement=" << containingElement;
|
||||
qDebug() << "containingElement.box="
|
||||
<< elementCube.getCorner().x * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().y * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().z * TREE_SCALE << ":"
|
||||
<< elementCube.getScale() * TREE_SCALE;
|
||||
<< elementCube.getCorner().x << ","
|
||||
<< elementCube.getCorner().y << ","
|
||||
<< elementCube.getCorner().z << ":"
|
||||
<< elementCube.getScale();
|
||||
qDebug() << "elementCube.getScale()=" << elementCube.getScale();
|
||||
//containingElement->printDebugDetails("containingElement");
|
||||
}
|
||||
|
@ -103,27 +99,27 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
|
||||
}
|
||||
|
||||
glm::vec3 newPosition = positionNearOriginInMeters;
|
||||
glm::vec3 newPosition = positionNearOrigin;
|
||||
|
||||
properties.setPosition(newPosition);
|
||||
|
||||
tree.updateEntity(entityID, properties, true);
|
||||
|
||||
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOriginInTreeUnits, targetRadius);
|
||||
float targetRadius = oneMeter * 2.0f;
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOrigin, targetRadius);
|
||||
const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
|
||||
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
|
||||
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
const AACube& elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
|
||||
if (verbose) {
|
||||
qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
|
||||
qDebug() << "foundEntityByID=" << foundEntityByID;
|
||||
qDebug() << "containingElement=" << containingElement;
|
||||
qDebug() << "containingElement.box="
|
||||
<< elementCube.getCorner().x * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().y * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().z * TREE_SCALE << ":"
|
||||
<< elementCube.getScale() * TREE_SCALE;
|
||||
<< elementCube.getCorner().x << ","
|
||||
<< elementCube.getCorner().y << ","
|
||||
<< elementCube.getCorner().z << ":"
|
||||
<< elementCube.getScale();
|
||||
//containingElement->printDebugDetails("containingElement");
|
||||
}
|
||||
|
||||
|
@ -143,27 +139,27 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
|
||||
}
|
||||
|
||||
glm::vec3 newPosition = positionAtCenterInMeters;
|
||||
glm::vec3 newPosition = positionAtCenter;
|
||||
|
||||
properties.setPosition(newPosition);
|
||||
|
||||
tree.updateEntity(entityID, properties, true);
|
||||
|
||||
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
|
||||
float targetRadius = oneMeter * 2.0f;
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenter, targetRadius);
|
||||
const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
|
||||
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
|
||||
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
const AACube& elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
|
||||
if (verbose) {
|
||||
qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
|
||||
qDebug() << "foundEntityByID=" << foundEntityByID;
|
||||
qDebug() << "containingElement=" << containingElement;
|
||||
qDebug() << "containingElement.box="
|
||||
<< elementCube.getCorner().x * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().y * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().z * TREE_SCALE << ":"
|
||||
<< elementCube.getScale() * TREE_SCALE;
|
||||
<< elementCube.getCorner().x << ","
|
||||
<< elementCube.getCorner().y << ","
|
||||
<< elementCube.getCorner().z << ":"
|
||||
<< elementCube.getScale();
|
||||
//containingElement->printDebugDetails("containingElement");
|
||||
}
|
||||
|
||||
|
@ -184,11 +180,11 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
|
||||
}
|
||||
|
||||
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
|
||||
float targetRadius = oneMeter * 2.0f;
|
||||
quint64 start = usecTimestampNow();
|
||||
const EntityItem* foundEntityByRadius = NULL;
|
||||
for (int i = 0; i < TEST_ITERATIONS; i++) {
|
||||
foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
|
||||
foundEntityByRadius = tree.findClosestEntity(positionAtCenter, targetRadius);
|
||||
}
|
||||
quint64 end = usecTimestampNow();
|
||||
|
||||
|
@ -262,13 +258,11 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
float randomX = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
|
||||
float randomY = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
|
||||
float randomZ = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
|
||||
glm::vec3 randomPositionInMeters(randomX,randomY,randomZ);
|
||||
glm::vec3 randomPositionInTreeUnits = randomPositionInMeters / (float)TREE_SCALE;
|
||||
glm::vec3 randomPosition(randomX,randomY,randomZ);
|
||||
|
||||
properties.setPosition(randomPositionInMeters);
|
||||
properties.setPosition(randomPosition);
|
||||
|
||||
// TODO: fix these unit tests
|
||||
//properties.setRadius(halfMeter);
|
||||
//properties.setModelURL("http://s3.amazonaws.com/hifi-public/ozan/theater.fbx");
|
||||
|
||||
if (extraVerbose) {
|
||||
|
@ -287,14 +281,14 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
}
|
||||
|
||||
quint64 startFind = usecTimestampNow();
|
||||
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(randomPositionInTreeUnits, targetRadius);
|
||||
float targetRadius = oneMeter * 2.0f;
|
||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(randomPosition, targetRadius);
|
||||
const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
|
||||
quint64 endFind = usecTimestampNow();
|
||||
totalElapsedFind += (endFind - startFind);
|
||||
|
||||
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
|
||||
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
const AACube& elementCube = containingElement ? containingElement->getAACube() : AACube();
|
||||
|
||||
bool elementIsBestFit = containingElement->bestFitEntityBounds(foundEntityByID);
|
||||
|
||||
|
@ -303,10 +297,10 @@ void EntityTests::entityTreeTests(bool verbose) {
|
|||
qDebug() << "foundEntityByID=" << foundEntityByID;
|
||||
qDebug() << "containingElement=" << containingElement;
|
||||
qDebug() << "containingElement.box="
|
||||
<< elementCube.getCorner().x * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().y * TREE_SCALE << ","
|
||||
<< elementCube.getCorner().z * TREE_SCALE << ":"
|
||||
<< elementCube.getScale() * TREE_SCALE;
|
||||
<< elementCube.getCorner().x << ","
|
||||
<< elementCube.getCorner().y << ","
|
||||
<< elementCube.getCorner().z << ":"
|
||||
<< elementCube.getScale();
|
||||
qDebug() << "elementCube.getScale()=" << elementCube.getScale();
|
||||
//containingElement->printDebugDetails("containingElement");
|
||||
qDebug() << "elementIsBestFit=" << elementIsBestFit;
|
||||
|
|
Loading…
Reference in a new issue