mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 16:55:06 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels
This commit is contained in:
commit
6764f29cd2
25 changed files with 655 additions and 413 deletions
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <QTimer>
|
||||
#include <EntityTree.h>
|
||||
#include <SimpleEntitySimulation.h>
|
||||
|
||||
#include "EntityServer.h"
|
||||
#include "EntityServerConsts.h"
|
||||
|
@ -20,7 +21,8 @@ const char* MODEL_SERVER_NAME = "Entity";
|
|||
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
|
||||
const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
|
||||
|
||||
EntityServer::EntityServer(const QByteArray& packet) : OctreeServer(packet) {
|
||||
EntityServer::EntityServer(const QByteArray& packet)
|
||||
: OctreeServer(packet), _entitySimulation(NULL) {
|
||||
// nothing special to do here...
|
||||
}
|
||||
|
||||
|
@ -36,6 +38,12 @@ OctreeQueryNode* EntityServer::createOctreeQueryNode() {
|
|||
Octree* EntityServer::createTree() {
|
||||
EntityTree* tree = new EntityTree(true);
|
||||
tree->addNewlyCreatedHook(this);
|
||||
if (!_entitySimulation) {
|
||||
SimpleEntitySimulation* simpleSimulation = new SimpleEntitySimulation();
|
||||
simpleSimulation->setEntityTree(tree);
|
||||
tree->setSimulation(simpleSimulation);
|
||||
_entitySimulation = simpleSimulation;
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ public:
|
|||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual Octree* createTree();
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
||||
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
||||
|
@ -46,7 +45,11 @@ public:
|
|||
public slots:
|
||||
void pruneDeletedEntities();
|
||||
|
||||
protected:
|
||||
virtual Octree* createTree();
|
||||
|
||||
private:
|
||||
EntitySimulation* _entitySimulation;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityServer_h
|
||||
|
|
|
@ -62,7 +62,6 @@ public:
|
|||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
|
||||
virtual Octree* createTree() = 0;
|
||||
virtual char getMyNodeType() const = 0;
|
||||
virtual PacketType getMyQueryMessageType() const = 0;
|
||||
virtual const char* getMyServerName() const = 0;
|
||||
|
@ -132,6 +131,7 @@ public slots:
|
|||
void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
protected:
|
||||
virtual Octree* createTree() = 0;
|
||||
bool readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result);
|
||||
bool readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result);
|
||||
bool readOptionString(const QString& optionName, const QJsonObject& settingsSectionObject, QString& result);
|
||||
|
|
|
@ -36,7 +36,6 @@ public:
|
|||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual Octree* createTree();
|
||||
virtual char getMyNodeType() const { return NodeType::VoxelServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
|
||||
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
||||
|
@ -50,6 +49,7 @@ public:
|
|||
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
|
||||
|
||||
protected:
|
||||
virtual Octree* createTree();
|
||||
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject);
|
||||
|
||||
private:
|
||||
|
|
|
@ -89,11 +89,19 @@ SelectionManager = (function() {
|
|||
}
|
||||
}
|
||||
|
||||
that.addEntity = function(entityID) {
|
||||
that.addEntity = function(entityID, toggleSelection) {
|
||||
if (entityID.isKnownID) {
|
||||
var idx = that.selections.indexOf(entityID);
|
||||
var idx = -1;
|
||||
for (var i = 0; i < that.selections.length; i++) {
|
||||
if (entityID.id == that.selections[i].id) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx == -1) {
|
||||
that.selections.push(entityID);
|
||||
} else if (toggleSelection) {
|
||||
that.selections.splice(idx, 1);
|
||||
}
|
||||
} else {
|
||||
var idx = that.pendingSelections.indexOf(entityID);
|
||||
|
@ -227,7 +235,6 @@ SelectionDisplay = (function () {
|
|||
var overlayNames = new Array();
|
||||
var lastCameraPosition = Camera.getPosition();
|
||||
var lastCameraOrientation = Camera.getOrientation();
|
||||
var lastPlaneIntersection;
|
||||
|
||||
var handleHoverColor = { red: 224, green: 67, blue: 36 };
|
||||
var handleHoverAlpha = 1.0;
|
||||
|
@ -324,21 +331,23 @@ SelectionDisplay = (function () {
|
|||
solid: false,
|
||||
visible: false,
|
||||
dashed: true,
|
||||
lineWidth: 1.0,
|
||||
lineWidth: 2.0,
|
||||
ignoreRayIntersection: true // this never ray intersects
|
||||
});
|
||||
|
||||
var selectionBox = Overlays.addOverlay("cube", {
|
||||
position: { x:0, y: 0, z: 0},
|
||||
size: 1,
|
||||
color: { red: 60, green: 60, blue: 60},
|
||||
color: { red: 255, green: 0, blue: 0},
|
||||
alpha: 1,
|
||||
solid: false,
|
||||
visible: false,
|
||||
dashed: true,
|
||||
dashed: false,
|
||||
lineWidth: 1.0,
|
||||
});
|
||||
|
||||
var selectionBoxes = [];
|
||||
|
||||
var rotationDegreesDisplay = Overlays.addOverlay("text3d", {
|
||||
position: { x:0, y: 0, z: 0},
|
||||
text: "",
|
||||
|
@ -684,6 +693,9 @@ SelectionDisplay = (function () {
|
|||
for (var i = 0; i < allOverlays.length; i++) {
|
||||
Overlays.deleteOverlay(allOverlays[i]);
|
||||
}
|
||||
for (var i = 0; i < selectionBoxes.length; i++) {
|
||||
Overlays.deleteOverlay(selectionBoxes[i]);
|
||||
}
|
||||
};
|
||||
|
||||
that.highlightSelectable = function(entityID) {
|
||||
|
@ -708,13 +720,11 @@ SelectionDisplay = (function () {
|
|||
|
||||
if (event !== false) {
|
||||
pickRay = Camera.computePickRay(event.x, event.y);
|
||||
lastPlaneIntersection = rayPlaneIntersection(pickRay, properties.position, Quat.getFront(lastCameraOrientation));
|
||||
|
||||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
print("select() with EVENT...... ");
|
||||
print(" event.y:" + event.y);
|
||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
Vec3.print(" current position:", properties.position);
|
||||
}
|
||||
|
||||
|
@ -933,23 +943,6 @@ SelectionDisplay = (function () {
|
|||
var dimensions = selectionManager.worldDimensions;
|
||||
var position = selectionManager.worldPosition;
|
||||
|
||||
Overlays.editOverlay(baseOfEntityProjectionOverlay,
|
||||
{
|
||||
visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL",
|
||||
solid: true,
|
||||
// lineWidth: 2.0,
|
||||
position: {
|
||||
x: position.x,
|
||||
y: grid.getOrigin().y,
|
||||
z: position.z
|
||||
},
|
||||
dimensions: {
|
||||
x: dimensions.x,
|
||||
y: dimensions.z
|
||||
},
|
||||
rotation: rotation,
|
||||
});
|
||||
|
||||
|
||||
Overlays.editOverlay(rotateOverlayTarget, { visible: rotationOverlaysVisible });
|
||||
Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible });
|
||||
|
@ -986,7 +979,6 @@ SelectionDisplay = (function () {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
that.updateRotationHandles();
|
||||
that.highlightSelectable();
|
||||
|
||||
|
@ -1126,6 +1118,41 @@ SelectionDisplay = (function () {
|
|||
visible: !(mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL"),
|
||||
});
|
||||
|
||||
// Create more selection box overlays if we don't have enough
|
||||
var overlaysNeeded = selectionManager.selections.length - selectionBoxes.length;
|
||||
for (var i = 0; i < overlaysNeeded; i++) {
|
||||
selectionBoxes.push(
|
||||
Overlays.addOverlay("cube", {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
size: 1,
|
||||
color: { red: 255, green: 153, blue: 0 },
|
||||
alpha: 1,
|
||||
solid: false,
|
||||
visible: false,
|
||||
dashed: false,
|
||||
lineWidth: 1.0,
|
||||
ignoreRayIntersection: true,
|
||||
}));
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
// Only show individual selections boxes if there is more than 1 selection
|
||||
if (selectionManager.selections.length > 1) {
|
||||
for (; i < selectionManager.selections.length; i++) {
|
||||
var properties = Entities.getEntityProperties(selectionManager.selections[i]);
|
||||
Overlays.editOverlay(selectionBoxes[i], {
|
||||
position: properties.position,
|
||||
rotation: properties.rotation,
|
||||
dimensions: properties.dimensions,
|
||||
visible: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Hide any remaining selection boxes
|
||||
for (; i < selectionBoxes.length; i++) {
|
||||
Overlays.editOverlay(selectionBoxes[i], { visible: false });
|
||||
}
|
||||
|
||||
Overlays.editOverlay(grabberEdgeTR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTR });
|
||||
Overlays.editOverlay(grabberEdgeTL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTL });
|
||||
Overlays.editOverlay(grabberEdgeTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTF });
|
||||
|
@ -1142,6 +1169,23 @@ SelectionDisplay = (function () {
|
|||
var grabberMoveUpOffset = 0.1;
|
||||
grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z }
|
||||
Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" });
|
||||
|
||||
Overlays.editOverlay(baseOfEntityProjectionOverlay, {
|
||||
visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL",
|
||||
solid: true,
|
||||
position: {
|
||||
x: selectionManager.worldPosition.x,
|
||||
y: grid.getOrigin().y,
|
||||
z: selectionManager.worldPosition.z
|
||||
},
|
||||
dimensions: {
|
||||
x: selectionManager.worldDimensions.x,
|
||||
y: selectionManager.worldDimensions.z
|
||||
},
|
||||
rotation: Quat.fromPitchYawRollDegrees(0, 0, 0),
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
that.setOverlaysVisible = function(isVisible) {
|
||||
|
@ -1149,6 +1193,10 @@ SelectionDisplay = (function () {
|
|||
for (var i = 0; i < length; i++) {
|
||||
Overlays.editOverlay(allOverlays[i], { visible: isVisible });
|
||||
}
|
||||
length = selectionBoxes.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
Overlays.editOverlay(selectionBoxes[i], { visible: isVisible });
|
||||
}
|
||||
};
|
||||
|
||||
that.unselect = function (entityID) {
|
||||
|
@ -1242,7 +1290,6 @@ SelectionDisplay = (function () {
|
|||
|
||||
if (wantDebug) {
|
||||
print("translateXZ... ");
|
||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
Vec3.print(" vector:", vector);
|
||||
Vec3.print(" newPosition:", properties.position);
|
||||
Vec3.print(" newPosition:", newPosition);
|
||||
|
@ -1254,10 +1301,17 @@ SelectionDisplay = (function () {
|
|||
};
|
||||
|
||||
var lastXYPick = null
|
||||
var upDownPickNormal = null;
|
||||
addGrabberTool(grabberMoveUp, {
|
||||
mode: "TRANSLATE_UP_DOWN",
|
||||
onBegin: function(event) {
|
||||
lastXYPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, Quat.getFront(lastCameraOrientation));
|
||||
pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
upDownPickNormal = Quat.getFront(lastCameraOrientation);
|
||||
// Remove y component so the y-axis lies along the plane we picking on - this will
|
||||
// give movements that follow the mouse.
|
||||
upDownPickNormal.y = 0;
|
||||
lastXYPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal);
|
||||
|
||||
SelectionManager.saveProperties();
|
||||
|
||||
|
@ -1285,11 +1339,9 @@ SelectionDisplay = (function () {
|
|||
pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
// translate mode left/right based on view toward entity
|
||||
var newIntersection = rayPlaneIntersection(pickRay,
|
||||
SelectionManager.worldPosition,
|
||||
Quat.getFront(lastCameraOrientation));
|
||||
var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal);
|
||||
|
||||
var vector = Vec3.subtract(newIntersection, lastPlaneIntersection);
|
||||
var vector = Vec3.subtract(newIntersection, lastXYPick);
|
||||
vector = grid.snapToGrid(vector);
|
||||
|
||||
// we only care about the Y axis
|
||||
|
@ -1300,7 +1352,6 @@ SelectionDisplay = (function () {
|
|||
if (wantDebug) {
|
||||
print("translateUpDown... ");
|
||||
print(" event.y:" + event.y);
|
||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
Vec3.print(" newIntersection:", newIntersection);
|
||||
Vec3.print(" vector:", vector);
|
||||
Vec3.print(" newPosition:", newPosition);
|
||||
|
@ -1513,7 +1564,6 @@ SelectionDisplay = (function () {
|
|||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
print(stretchMode);
|
||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
Vec3.print(" newIntersection:", newIntersection);
|
||||
Vec3.print(" vector:", vector);
|
||||
Vec3.print(" oldPOS:", oldPOS);
|
||||
|
@ -2010,7 +2060,7 @@ SelectionDisplay = (function () {
|
|||
that.checkMove = function() {
|
||||
if (SelectionManager.hasSelection() &&
|
||||
(!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation))){
|
||||
that.select(selectionManager.selections[0], false, false);
|
||||
that.updateRotationHandles();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2262,11 +2312,8 @@ SelectionDisplay = (function () {
|
|||
|
||||
if (somethingClicked) {
|
||||
pickRay = Camera.computePickRay(event.x, event.y);
|
||||
lastPlaneIntersection = rayPlaneIntersection(pickRay, selectionManager.worldPosition,
|
||||
Quat.getFront(lastCameraOrientation));
|
||||
if (wantDebug) {
|
||||
print("mousePressEvent()...... " + overlayNames[result.overlayID]);
|
||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
print("mousePressEvent()...... " + overlayNames[result.overlayID]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,8 @@ Grid = function(opts) {
|
|||
color: gridColor,
|
||||
alpha: gridAlpha,
|
||||
});
|
||||
|
||||
that.emitUpdate();
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
|
@ -207,6 +209,7 @@ GridTool = function(opts) {
|
|||
|
||||
horizontalGrid.addListener(function(data) {
|
||||
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||
selectionDisplay.updateHandles();
|
||||
});
|
||||
|
||||
webView.eventBridge.webEventReceived.connect(function(data) {
|
||||
|
|
|
@ -51,8 +51,8 @@ var toolWidth = 50;
|
|||
|
||||
var MIN_ANGULAR_SIZE = 2;
|
||||
var MAX_ANGULAR_SIZE = 45;
|
||||
var allowLargeModels = false;
|
||||
var allowSmallModels = false;
|
||||
var allowLargeModels = true;
|
||||
var allowSmallModels = true;
|
||||
var wantEntityGlow = false;
|
||||
|
||||
var SPAWN_DISTANCE = 1;
|
||||
|
@ -476,7 +476,6 @@ function findClickedEntity(event) {
|
|||
var identify = Entities.identifyEntity(foundEntity);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")");
|
||||
selectionManager.clearSelections();
|
||||
return null;
|
||||
}
|
||||
foundEntity = identify;
|
||||
|
@ -485,74 +484,18 @@ function findClickedEntity(event) {
|
|||
return { pickRay: pickRay, entityID: foundEntity };
|
||||
}
|
||||
|
||||
var mouseHasMovedSincePress = false;
|
||||
|
||||
function mousePressEvent(event) {
|
||||
mouseHasMovedSincePress = false;
|
||||
|
||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
|
||||
return;
|
||||
}
|
||||
if (isActive) {
|
||||
var entitySelected = false;
|
||||
if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
||||
// Event handled; do nothing.
|
||||
return;
|
||||
} else {
|
||||
var result = findClickedEntity(event);
|
||||
if (result === null) {
|
||||
selectionManager.clearSelections();
|
||||
return;
|
||||
}
|
||||
var pickRay = result.pickRay;
|
||||
var foundEntity = result.entityID;
|
||||
|
||||
var properties = Entities.getEntityProperties(foundEntity);
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
} else {
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal);
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
// / | X - base of the perpendicular line
|
||||
// A---X----->B d - distance fom axis
|
||||
// x x - distance from A
|
||||
//
|
||||
// |X-A| = (P-A).B
|
||||
// X == A + ((P-A).B)B
|
||||
// d = |P-X|
|
||||
|
||||
var A = pickRay.origin;
|
||||
var B = Vec3.normalize(pickRay.direction);
|
||||
var P = properties.position;
|
||||
|
||||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (0 < x && sizeOK) {
|
||||
entitySelected = true;
|
||||
selectedEntityID = foundEntity;
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
|
||||
if (!event.isShifted) {
|
||||
selectionManager.clearSelections();
|
||||
}
|
||||
selectionManager.addEntity(foundEntity);
|
||||
|
||||
print("Model selected: " + foundEntity.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entitySelected) {
|
||||
selectionDisplay.select(selectedEntityID, event);
|
||||
}
|
||||
} else if (Menu.isOptionChecked(MENU_INSPECT_TOOL_ENABLED)) {
|
||||
var result = findClickedEntity(event);
|
||||
|
@ -572,6 +515,7 @@ function mousePressEvent(event) {
|
|||
var highlightedEntityID = { isKnownID: false };
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
mouseHasMovedSincePress = true;
|
||||
if (isActive) {
|
||||
// allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing
|
||||
if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) {
|
||||
|
@ -615,6 +559,72 @@ function mouseReleaseEvent(event) {
|
|||
}
|
||||
|
||||
cameraManager.mouseReleaseEvent(event);
|
||||
|
||||
if (!mouseHasMovedSincePress) {
|
||||
mouseClickEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function mouseClickEvent(event) {
|
||||
var result = findClickedEntity(event);
|
||||
if (result === null) {
|
||||
if (!event.isShifted) {
|
||||
selectionManager.clearSelections();
|
||||
}
|
||||
return;
|
||||
}
|
||||
var pickRay = result.pickRay;
|
||||
var foundEntity = result.entityID;
|
||||
|
||||
var properties = Entities.getEntityProperties(foundEntity);
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
} else {
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal);
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
// / | X - base of the perpendicular line
|
||||
// A---X----->B d - distance fom axis
|
||||
// x x - distance from A
|
||||
//
|
||||
// |X-A| = (P-A).B
|
||||
// X == A + ((P-A).B)B
|
||||
// d = |P-X|
|
||||
|
||||
var A = pickRay.origin;
|
||||
var B = Vec3.normalize(pickRay.direction);
|
||||
var P = properties.position;
|
||||
|
||||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (0 < x && sizeOK) {
|
||||
entitySelected = true;
|
||||
selectedEntityID = foundEntity;
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
|
||||
if (!event.isShifted) {
|
||||
selectionManager.clearSelections();
|
||||
}
|
||||
|
||||
var toggle = event.isShifted;
|
||||
selectionManager.addEntity(foundEntity, toggle);
|
||||
|
||||
print("Model selected: " + foundEntity.id);
|
||||
selectionDisplay.select(selectedEntityID, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
@ -644,9 +654,9 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Paste Models", isCheckable: true });
|
||||
afterItem: "Paste Models", isCheckable: true, isChecked: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Select Large Models", isCheckable: true });
|
||||
afterItem: "Allow Select Large Models", isCheckable: true, isChecked: true });
|
||||
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||
|
|
|
@ -442,6 +442,7 @@ void Application::aboutToQuit() {
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
_entities.getTree()->setSimulation(NULL);
|
||||
qInstallMessageHandler(NULL);
|
||||
|
||||
saveSettings();
|
||||
|
@ -1976,7 +1977,9 @@ void Application::init() {
|
|||
_entities.init();
|
||||
_entities.setViewFrustum(getViewFrustum());
|
||||
|
||||
_entityCollisionSystem.init(&_entityEditSender, _entities.getTree(), _voxels.getTree(), &_audio, &_avatarManager);
|
||||
EntityTree* entityTree = _entities.getTree();
|
||||
_entityCollisionSystem.init(&_entityEditSender, entityTree, _voxels.getTree(), &_audio, &_avatarManager);
|
||||
entityTree->setSimulation(&_entityCollisionSystem);
|
||||
|
||||
// connect the _entityCollisionSystem to our script engine's EntityScriptingInterface
|
||||
connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithVoxel,
|
||||
|
@ -2327,11 +2330,12 @@ void Application::update(float deltaTime) {
|
|||
|
||||
if (!_aboutToQuit) {
|
||||
PerformanceTimer perfTimer("entities");
|
||||
// NOTE: the _entities.update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
_entities.update(); // update the models...
|
||||
{
|
||||
PerformanceTimer perfTimer("collisions");
|
||||
_entityCollisionSystem.update(); // collide the entities...
|
||||
}
|
||||
// The _entityCollisionSystem.updateCollisions() call below merely tries for lock,
|
||||
// and on failure it skips collision detection.
|
||||
_entityCollisionSystem.updateCollisions(); // collide the entities...
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
#include <QUndoStack>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include <EntityEditPacketSender.h>
|
||||
#include <EntityCollisionSystem.h>
|
||||
#include <EntityEditPacketSender.h>
|
||||
#include <NetworkPacket.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
|
|
@ -40,7 +40,6 @@ public:
|
|||
EntityTreeRenderer(bool wantScripts);
|
||||
virtual ~EntityTreeRenderer();
|
||||
|
||||
virtual Octree* createTree() { return new EntityTree(true); }
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; }
|
||||
|
@ -108,6 +107,9 @@ public slots:
|
|||
void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
|
||||
void entitySciptChanging(const EntityItemID& entityID);
|
||||
|
||||
protected:
|
||||
virtual Octree* createTree() { return new EntityTree(true); }
|
||||
|
||||
private:
|
||||
void checkAndCallPreload(const EntityItemID& entityID);
|
||||
void checkAndCallUnload(const EntityItemID& entityID);
|
||||
|
|
|
@ -48,9 +48,6 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt
|
|||
details.cube = details.containingElement->getAACube();
|
||||
_entitiesToDelete << details;
|
||||
_lookingCount++;
|
||||
_tree->trackDeletedEntity(searchEntityID);
|
||||
// before deleting any entity make sure to remove it from our Mortal, Changing, and Moving lists
|
||||
_tree->removeEntityFromSimulationLists(searchEntityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,13 +75,9 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) {
|
|||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||
|
||||
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
|
||||
// path of the tree. For this operation, we want to recurse the branch of the tree if
|
||||
// and of the following are true:
|
||||
// * We have not yet found the old entity, and this branch contains our old entity
|
||||
// * We have not yet found the new entity, and this branch contains our new entity
|
||||
//
|
||||
// Note: it's often the case that the branch in question contains both the old entity
|
||||
// and the new entity.
|
||||
// path of the tree. For this operation, we want to recurse the branch of the tree if:
|
||||
// * We have not yet found the all entities, and
|
||||
// * this branch contains our some of the entities we're looking for.
|
||||
|
||||
bool keepSearching = false; // assume we don't need to search any more
|
||||
|
||||
|
@ -100,6 +93,8 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) {
|
|||
if (entityTreeElement == details.containingElement) {
|
||||
EntityItemID entityItemID = details.entity->getEntityItemID();
|
||||
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity
|
||||
assert(theEntity);
|
||||
_tree->trackDeletedEntity(theEntity);
|
||||
entityTreeElement->removeEntityItem(theEntity); // remove it from the element
|
||||
_tree->setContainingElement(entityItemID, NULL); // update or id to element lookup
|
||||
delete theEntity; // now actually delete the entity!
|
||||
|
|
|
@ -16,27 +16,32 @@
|
|||
#include <CollisionInfo.h>
|
||||
#include <HeadData.h>
|
||||
#include <HandData.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SphereShape.h>
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include "EntityCollisionSystem.h"
|
||||
#include "EntityEditPacketSender.h"
|
||||
#include "EntityTree.h"
|
||||
#include "EntityItem.h"
|
||||
#include "EntityTreeElement.h"
|
||||
#include "EntityTree.h"
|
||||
|
||||
const int MAX_COLLISIONS_PER_Entity = 16;
|
||||
|
||||
EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender,
|
||||
EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
|
||||
AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) {
|
||||
init(packetSender, Entities, voxels, audio, avatars);
|
||||
EntityCollisionSystem::EntityCollisionSystem()
|
||||
: SimpleEntitySimulation(),
|
||||
_packetSender(NULL),
|
||||
_voxels(NULL),
|
||||
_audio(NULL),
|
||||
_avatars(NULL),
|
||||
_collisions(MAX_COLLISIONS_PER_Entity) {
|
||||
}
|
||||
|
||||
void EntityCollisionSystem::init(EntityEditPacketSender* packetSender,
|
||||
EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
|
||||
AvatarHashMap* avatars) {
|
||||
EntityTree* entities, VoxelTree* voxels, AbstractAudioInterface* audio,
|
||||
AvatarHashMap* avatars) {
|
||||
assert(entities);
|
||||
setEntityTree(entities);
|
||||
_packetSender = packetSender;
|
||||
_entities = Entities;
|
||||
_voxels = voxels;
|
||||
_audio = audio;
|
||||
_avatars = avatars;
|
||||
|
@ -45,14 +50,15 @@ void EntityCollisionSystem::init(EntityEditPacketSender* packetSender,
|
|||
EntityCollisionSystem::~EntityCollisionSystem() {
|
||||
}
|
||||
|
||||
void EntityCollisionSystem::update() {
|
||||
void EntityCollisionSystem::updateCollisions() {
|
||||
PerformanceTimer perfTimer("collisions");
|
||||
assert(_entityTree);
|
||||
// update all Entities
|
||||
if (_entities->tryLockForRead()) {
|
||||
QList<EntityItem*>& movingEntities = _entities->getMovingEntities();
|
||||
foreach (EntityItem* entity, movingEntities) {
|
||||
if (_entityTree->tryLockForRead()) {
|
||||
foreach (EntityItem* entity, _movingEntities) {
|
||||
checkEntity(entity);
|
||||
}
|
||||
_entities->unlock();
|
||||
_entityTree->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,9 +133,8 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
|||
CollisionList collisions(MAX_COLLISIONS_PER_ENTITY);
|
||||
bool shapeCollisionsAccurate = false;
|
||||
|
||||
bool shapeCollisions = _entities->findShapeCollisions(&entityA->getCollisionShapeInMeters(),
|
||||
bool shapeCollisions = _entityTree->findShapeCollisions(&entityA->getCollisionShapeInMeters(),
|
||||
collisions, Octree::NoLock, &shapeCollisionsAccurate);
|
||||
|
||||
|
||||
if (shapeCollisions) {
|
||||
for(int i = 0; i < collisions.size(); i++) {
|
||||
|
@ -203,7 +208,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
|||
propertiesA.setPosition(newPositionA * (float)TREE_SCALE);
|
||||
propertiesA.setLastEdited(now);
|
||||
|
||||
_entities->updateEntity(idA, propertiesA);
|
||||
_entityTree->updateEntity(idA, propertiesA);
|
||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
|
||||
}
|
||||
|
||||
|
@ -220,7 +225,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
|||
propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
|
||||
propertiesB.setLastEdited(now);
|
||||
|
||||
_entities->updateEntity(idB, propertiesB);
|
||||
_entityTree->updateEntity(idB, propertiesB);
|
||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB);
|
||||
}
|
||||
}
|
||||
|
@ -326,6 +331,6 @@ void EntityCollisionSystem::applyHardCollision(EntityItem* entity, const Collisi
|
|||
properties.setVelocity(velocity * (float)TREE_SCALE);
|
||||
properties.setLastEdited(usecTimestampNow());
|
||||
|
||||
_entities->updateEntity(entityItemID, properties);
|
||||
_entityTree->updateEntity(entityItemID, properties);
|
||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties);
|
||||
}
|
||||
|
|
|
@ -20,11 +20,12 @@
|
|||
|
||||
#include <AvatarHashMap.h>
|
||||
#include <CollisionInfo.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <OctreePacketData.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <VoxelDetail.h>
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include "SimpleEntitySimulation.h"
|
||||
|
||||
class AbstractAudioInterface;
|
||||
class AvatarData;
|
||||
|
@ -32,19 +33,17 @@ class EntityEditPacketSender;
|
|||
class EntityTree;
|
||||
class VoxelTree;
|
||||
|
||||
class EntityCollisionSystem : public QObject {
|
||||
class EntityCollisionSystem : public QObject, public SimpleEntitySimulation {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EntityCollisionSystem(EntityEditPacketSender* packetSender = NULL, EntityTree* Entitys = NULL,
|
||||
VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL,
|
||||
AvatarHashMap* avatars = NULL);
|
||||
EntityCollisionSystem();
|
||||
|
||||
void init(EntityEditPacketSender* packetSender, EntityTree* Entitys, VoxelTree* voxels,
|
||||
void init(EntityEditPacketSender* packetSender, EntityTree* entities, VoxelTree* voxels,
|
||||
AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL);
|
||||
|
||||
~EntityCollisionSystem();
|
||||
|
||||
void update();
|
||||
void updateCollisions();
|
||||
|
||||
void checkEntity(EntityItem* Entity);
|
||||
void updateCollisionWithVoxels(EntityItem* Entity);
|
||||
|
@ -65,7 +64,6 @@ private:
|
|||
void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration);
|
||||
|
||||
EntityEditPacketSender* _packetSender;
|
||||
EntityTree* _entities;
|
||||
VoxelTree* _voxels;
|
||||
AbstractAudioInterface* _audio;
|
||||
AvatarHashMap* _avatars;
|
||||
|
|
|
@ -292,16 +292,10 @@ public:
|
|||
uint32_t getUpdateFlags() const { return _updateFlags; }
|
||||
void clearUpdateFlags() { _updateFlags = 0; }
|
||||
|
||||
#ifdef USE_BULLET_PHYSICS
|
||||
EntityMotionState* getMotionState() const { return _motionState; }
|
||||
virtual EntityMotionState* createMotionState() { return NULL; }
|
||||
void destroyMotionState();
|
||||
#endif // USE_BULLET_PHYSICS
|
||||
SimulationState getSimulationState() const { return _simulationState; }
|
||||
|
||||
protected:
|
||||
friend class EntityTree;
|
||||
void setSimulationState(SimulationState state) { _simulationState = state; }
|
||||
protected:
|
||||
|
||||
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
||||
virtual void recalculateCollisionShape();
|
||||
|
|
20
libraries/entities/src/EntitySimulation.cpp
Normal file
20
libraries/entities/src/EntitySimulation.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// EntitySimulation.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.11.24
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
void EntitySimulation::setEntityTree(EntityTree* tree) {
|
||||
if (_entityTree && _entityTree != tree) {
|
||||
clearEntities();
|
||||
}
|
||||
_entityTree = tree;
|
||||
}
|
||||
|
49
libraries/entities/src/EntitySimulation.h
Normal file
49
libraries/entities/src/EntitySimulation.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// EntitySimulation.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.11.24
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_EntitySimulation_h
|
||||
#define hifi_EntitySimulation_h
|
||||
|
||||
#include <QSet>
|
||||
|
||||
#include "EntityTree.h"
|
||||
|
||||
class EntitySimulation {
|
||||
public:
|
||||
EntitySimulation() : _entityTree(NULL) { }
|
||||
virtual ~EntitySimulation() {}
|
||||
|
||||
/// \param tree pointer to EntityTree which is stored internally
|
||||
virtual void setEntityTree(EntityTree* tree);
|
||||
|
||||
/// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted.
|
||||
virtual void update(QSet<EntityItem*>& entitiesToDelete) = 0;
|
||||
|
||||
/// \param entity pointer to EntityItem to add to the simulation
|
||||
/// \sideeffect the EntityItem::_simulationState member may be updated to indicate membership to internal list
|
||||
virtual void addEntity(EntityItem* entity) = 0;
|
||||
|
||||
/// \param entity pointer to EntityItem to removed from the simulation
|
||||
/// \sideeffect the EntityItem::_simulationState member may be updated to indicate non-membership to internal list
|
||||
virtual void removeEntity(EntityItem* entity) = 0;
|
||||
|
||||
/// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation
|
||||
virtual void entityChanged(EntityItem* entity) = 0;
|
||||
|
||||
virtual void clearEntities() = 0;
|
||||
|
||||
EntityTree* getEntityTree() { return _entityTree; }
|
||||
|
||||
protected:
|
||||
EntityTree* _entityTree;
|
||||
};
|
||||
|
||||
#endif // hifi_EntitySimulation_h
|
|
@ -12,13 +12,14 @@
|
|||
#include <PerfStat.h>
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
#include "AddEntityOperator.h"
|
||||
#include "DeleteEntityOperator.h"
|
||||
#include "MovingEntitiesOperator.h"
|
||||
#include "UpdateEntityOperator.h"
|
||||
|
||||
EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) {
|
||||
EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) {
|
||||
_rootElement = createNewElement();
|
||||
}
|
||||
|
||||
|
@ -34,14 +35,14 @@ EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) {
|
|||
|
||||
void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
||||
// this would be a good place to clean up our entities...
|
||||
if (_simulation) {
|
||||
_simulation->clearEntities();
|
||||
}
|
||||
foreach (EntityTreeElement* element, _entityToElementMap) {
|
||||
element->cleanupEntities();
|
||||
}
|
||||
_entityToElementMap.clear();
|
||||
Octree::eraseAllOctreeElements(createNewRoot);
|
||||
_movingEntities.clear();
|
||||
_mortalEntities.clear();
|
||||
_changedEntities.clear();
|
||||
}
|
||||
|
||||
bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
||||
|
@ -75,27 +76,17 @@ EntityItem* EntityTree::getOrCreateEntityItem(const EntityItemID& entityID, cons
|
|||
}
|
||||
|
||||
/// Adds a new entity item to the tree
|
||||
void EntityTree::addEntityItem(EntityItem* entityItem) {
|
||||
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
|
||||
EntityItemID entityID = entityItem->getEntityItemID();
|
||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||
if (containingElement) {
|
||||
qDebug() << "UNEXPECTED!!!! don't call addEntityItem() on existing EntityItems. entityID=" << entityID;
|
||||
return;
|
||||
}
|
||||
|
||||
// Recurse the tree and store the entity in the correct tree element
|
||||
AddEntityOperator theOperator(this, entityItem);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
|
||||
void EntityTree::postAddEntity(EntityItem* entity) {
|
||||
assert(entity);
|
||||
// check to see if we need to simulate this entity..
|
||||
updateEntityState(entityItem);
|
||||
|
||||
if (_simulation) {
|
||||
_simulation->addEntity(entity);
|
||||
}
|
||||
_isDirty = true;
|
||||
emit addingEntity(entity->getEntityItemID());
|
||||
}
|
||||
|
||||
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
|
||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||
if (!containingElement) {
|
||||
qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
|
||||
|
@ -119,6 +110,9 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
|||
UpdateEntityOperator theOperator(this, containingElement, existingEntity, tempProperties);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
_isDirty = true;
|
||||
if (_simulation && existingEntity->getUpdateFlags() != 0) {
|
||||
_simulation->entityChanged(existingEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -129,13 +123,14 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
|||
recurseTreeWithOperator(&theOperator);
|
||||
_isDirty = true;
|
||||
|
||||
updateEntityState(existingEntity);
|
||||
if (_simulation && existingEntity->getUpdateFlags() != 0) {
|
||||
_simulation->entityChanged(existingEntity);
|
||||
}
|
||||
|
||||
QString entityScriptAfter = existingEntity->getScript();
|
||||
if (entityScriptBefore != entityScriptAfter) {
|
||||
emitEntityScriptChanging(entityID); // the entity script has changed
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
containingElement = getContainingElement(entityID);
|
||||
|
@ -171,33 +166,47 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
|
|||
result = EntityTypes::constructEntityItem(type, entityID, properties);
|
||||
|
||||
if (result) {
|
||||
// this does the actual adding of the entity
|
||||
addEntityItem(result);
|
||||
emitAddingEntity(entityID);
|
||||
// Recurse the tree and store the entity in the correct tree element
|
||||
AddEntityOperator theOperator(this, result);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
|
||||
postAddEntity(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void EntityTree::trackDeletedEntity(const EntityItemID& entityID) {
|
||||
void EntityTree::trackDeletedEntity(EntityItem* entity) {
|
||||
if (_simulation) {
|
||||
_simulation->removeEntity(entity);
|
||||
}
|
||||
// this is only needed on the server to send delete messages for recently deleted entities to the viewers
|
||||
if (getIsServer()) {
|
||||
// set up the deleted entities ID
|
||||
quint64 deletedAt = usecTimestampNow();
|
||||
_recentlyDeletedEntitiesLock.lockForWrite();
|
||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, entityID.id);
|
||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, entity->getEntityItemID().id);
|
||||
_recentlyDeletedEntitiesLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::emitAddingEntity(const EntityItemID& entityItemID) {
|
||||
emit addingEntity(entityItemID);
|
||||
}
|
||||
|
||||
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
|
||||
emit entityScriptChanging(entityItemID);
|
||||
}
|
||||
|
||||
void EntityTree::setSimulation(EntitySimulation* simulation) {
|
||||
if (simulation) {
|
||||
// assert that the simulation's backpointer has already been properly connected
|
||||
assert(simulation->getEntityTree() == this);
|
||||
}
|
||||
if (_simulation && _simulation != simulation) {
|
||||
// It's important to clearEntities() on the simulation since taht will update each
|
||||
// EntityItem::_simulationState correctly so as to not confuse the next _simulation.
|
||||
_simulation->clearEntities();
|
||||
}
|
||||
_simulation = simulation;
|
||||
}
|
||||
|
||||
void EntityTree::deleteEntity(const EntityItemID& entityID) {
|
||||
emit deletingEntity(entityID);
|
||||
|
||||
|
@ -220,29 +229,6 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs) {
|
|||
_isDirty = true;
|
||||
}
|
||||
|
||||
void EntityTree::removeEntityFromSimulationLists(const EntityItemID& entityID) {
|
||||
EntityItem* theEntity = findEntityByEntityItemID(entityID);
|
||||
|
||||
if (theEntity) {
|
||||
// make sure to remove it from any of our simulation lists
|
||||
EntityItem::SimulationState theState = theEntity->getSimulationState();
|
||||
switch (theState) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(theEntity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(theEntity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_changedEntities.remove(theEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// This method is used to find and fix entity IDs that are shifting from creator token based to known ID based entity IDs.
|
||||
/// This should only be used on a client side (viewing) tree. The typical usage is that a local editor has been creating
|
||||
/// entities in the local tree, those entities have creatorToken based entity IDs. But those entity edits are also sent up to
|
||||
|
@ -575,183 +561,29 @@ void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncod
|
|||
extraEncodeData->clear();
|
||||
}
|
||||
|
||||
void EntityTree::updateEntityState(EntityItem* entity) {
|
||||
EntityItem::SimulationState oldState = entity->getSimulationState();
|
||||
EntityItem::SimulationState newState = entity->computeSimulationState();
|
||||
if (newState != oldState) {
|
||||
switch (oldState) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (newState) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.push_back(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.push_back(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entity->setSimulationState(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::clearEntityState(EntityItem* entity) {
|
||||
EntityItem::SimulationState oldState = entity->getSimulationState();
|
||||
switch (oldState) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entity->setSimulationState(EntityItem::Static);
|
||||
}
|
||||
|
||||
void EntityTree::entityChanged(EntityItem* entity) {
|
||||
_changedEntities.insert(entity);
|
||||
if (_simulation) {
|
||||
_simulation->entityChanged(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::update() {
|
||||
// our new strategy should be to segregate entities into three classes:
|
||||
// 1) stationary things that are not changing - most models
|
||||
// 2) mortal things - these are stationary but have a lifetime - then need to be checked,
|
||||
// can be touched linearly, and won't change the tree
|
||||
// 2) changing things - like things animating they can be touched linearly and they don't change the tree
|
||||
// 3) moving things - these need to scan the tree and update accordingly
|
||||
// finally - all things that need to be deleted, can be handled on a single delete pass.
|
||||
//
|
||||
// TODO: theoretically we could combine the move and delete tree passes...
|
||||
lockForWrite();
|
||||
quint64 now = usecTimestampNow();
|
||||
QSet<EntityItemID> entitiesToDelete;
|
||||
updateChangedEntities(now, entitiesToDelete);
|
||||
updateMovingEntities(now, entitiesToDelete);
|
||||
updateMortalEntities(now, entitiesToDelete);
|
||||
|
||||
if (entitiesToDelete.size() > 0) {
|
||||
deleteEntities(entitiesToDelete);
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
void EntityTree::updateChangedEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
|
||||
foreach (EntityItem* thisEntity, _changedEntities) {
|
||||
// check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (thisEntity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
|
||||
entitiesToDelete << thisEntity->getEntityItemID();
|
||||
clearEntityState(thisEntity);
|
||||
} else {
|
||||
updateEntityState(thisEntity);
|
||||
}
|
||||
thisEntity->clearUpdateFlags();
|
||||
}
|
||||
_changedEntities.clear();
|
||||
}
|
||||
|
||||
void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
|
||||
PerformanceTimer perfTimer("updateMovingEntities");
|
||||
if (_movingEntities.size() > 0) {
|
||||
MovingEntitiesOperator moveOperator(this);
|
||||
{
|
||||
PerformanceTimer perfTimer("_movingEntities");
|
||||
|
||||
QList<EntityItem*>::iterator item_itr = _movingEntities.begin();
|
||||
while (item_itr != _movingEntities.end()) {
|
||||
EntityItem* thisEntity = *item_itr;
|
||||
|
||||
// always check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (thisEntity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
|
||||
entitiesToDelete << thisEntity->getEntityItemID();
|
||||
// remove thisEntity from the list
|
||||
item_itr = _movingEntities.erase(item_itr);
|
||||
thisEntity->setSimulationState(EntityItem::Static);
|
||||
} else {
|
||||
AACube oldCube = thisEntity->getMaximumAACube();
|
||||
thisEntity->update(now);
|
||||
AACube newCube = thisEntity->getMaximumAACube();
|
||||
|
||||
// check to see if this movement has sent the entity outside of the domain.
|
||||
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
|
||||
if (!domainBounds.touches(newCube)) {
|
||||
qDebug() << "Entity " << thisEntity->getEntityItemID() << " moved out of domain bounds.";
|
||||
entitiesToDelete << thisEntity->getEntityItemID();
|
||||
// remove thisEntity from the list
|
||||
item_itr = _movingEntities.erase(item_itr);
|
||||
thisEntity->setSimulationState(EntityItem::Static);
|
||||
} else {
|
||||
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
|
||||
EntityItem::SimulationState newState = thisEntity->computeSimulationState();
|
||||
if (newState != EntityItem::Moving) {
|
||||
if (newState == EntityItem::Mortal) {
|
||||
_mortalEntities.push_back(thisEntity);
|
||||
}
|
||||
// remove thisEntity from the list
|
||||
item_itr = _movingEntities.erase(item_itr);
|
||||
thisEntity->setSimulationState(newState);
|
||||
} else {
|
||||
++item_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_simulation) {
|
||||
lockForWrite();
|
||||
QSet<EntityItem*> entitiesToDelete;
|
||||
_simulation->update(entitiesToDelete);
|
||||
if (entitiesToDelete.size() > 0) {
|
||||
// translate into list of ID's
|
||||
QSet<EntityItemID> idsToDelete;
|
||||
foreach (EntityItem* entity, entitiesToDelete) {
|
||||
idsToDelete.insert(entity->getEntityItemID());
|
||||
}
|
||||
deleteEntities(idsToDelete);
|
||||
}
|
||||
if (moveOperator.hasMovingEntities()) {
|
||||
PerformanceTimer perfTimer("recurseTreeWithOperator");
|
||||
recurseTreeWithOperator(&moveOperator);
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
|
||||
QList<EntityItem*>::iterator item_itr = _mortalEntities.begin();
|
||||
while (item_itr != _mortalEntities.end()) {
|
||||
EntityItem* thisEntity = *item_itr;
|
||||
thisEntity->update(now);
|
||||
// always check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (thisEntity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
|
||||
entitiesToDelete << thisEntity->getEntityItemID();
|
||||
// remove thisEntity from the list
|
||||
item_itr = _mortalEntities.erase(item_itr);
|
||||
thisEntity->setSimulationState(EntityItem::Static);
|
||||
} else {
|
||||
// check to see if this entity is no longer moving
|
||||
EntityItem::SimulationState newState = thisEntity->computeSimulationState();
|
||||
if (newState != EntityItem::Mortal) {
|
||||
if (newState == EntityItem::Moving) {
|
||||
_movingEntities.push_back(thisEntity);
|
||||
}
|
||||
// remove thisEntity from the list
|
||||
item_itr = _mortalEntities.erase(item_itr);
|
||||
thisEntity->setSimulationState(newState);
|
||||
} else {
|
||||
++item_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
|
||||
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
||||
bool hasSomethingNewer = false;
|
||||
|
@ -966,19 +798,16 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
|
|||
|
||||
EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ {
|
||||
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
||||
if (_entityToElementMap.contains(entityItemID)) {
|
||||
return _entityToElementMap.value(entityItemID);
|
||||
} else if (entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){
|
||||
EntityTreeElement* element = _entityToElementMap.value(entityItemID);
|
||||
if (!element && entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){
|
||||
// check the creator token version too...
|
||||
EntityItemID creatorTokenOnly;
|
||||
creatorTokenOnly.id = UNKNOWN_ENTITY_ID;
|
||||
creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID;
|
||||
creatorTokenOnly.isKnownID = false;
|
||||
if (_entityToElementMap.contains(creatorTokenOnly)) {
|
||||
return _entityToElementMap.value(creatorTokenOnly);
|
||||
}
|
||||
element = _entityToElementMap.value(creatorTokenOnly);
|
||||
}
|
||||
return NULL;
|
||||
return element;
|
||||
}
|
||||
|
||||
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
|
||||
class Model;
|
||||
class EntitySimulation;
|
||||
|
||||
class NewlyCreatedEntityHook {
|
||||
public:
|
||||
|
@ -78,13 +79,13 @@ public:
|
|||
|
||||
// The newer API...
|
||||
EntityItem* getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
void addEntityItem(EntityItem* entityItem);
|
||||
void postAddEntity(EntityItem* entityItem);
|
||||
|
||||
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
void deleteEntity(const EntityItemID& entityID);
|
||||
void deleteEntities(QSet<EntityItemID> entityIDs);
|
||||
void removeEntityFromSimulationLists(const EntityItemID& entityID);
|
||||
void removeEntityFromSimulation(EntityItem* entity);
|
||||
|
||||
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
|
||||
EntityItem* findEntityByID(const QUuid& id);
|
||||
|
@ -137,18 +138,14 @@ public:
|
|||
|
||||
void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
|
||||
|
||||
void updateEntityState(EntityItem* entity);
|
||||
void clearEntityState(EntityItem* entity);
|
||||
|
||||
void entityChanged(EntityItem* entity);
|
||||
|
||||
void trackDeletedEntity(const EntityItemID& entityID);
|
||||
void trackDeletedEntity(EntityItem* entity);
|
||||
|
||||
void emitAddingEntity(const EntityItemID& entityItemID);
|
||||
void emitEntityScriptChanging(const EntityItemID& entityItemID);
|
||||
|
||||
QList<EntityItem*>& getMovingEntities() { return _movingEntities; }
|
||||
|
||||
void setSimulation(EntitySimulation* simulation);
|
||||
|
||||
signals:
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
|
@ -157,10 +154,6 @@ signals:
|
|||
|
||||
private:
|
||||
|
||||
void updateChangedEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
|
||||
void updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
|
||||
void updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
|
||||
|
||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
||||
|
@ -176,11 +169,7 @@ private:
|
|||
EntityItemFBXService* _fbxService;
|
||||
|
||||
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
|
||||
|
||||
QList<EntityItem*> _movingEntities; // entities that need to be updated
|
||||
QList<EntityItem*> _mortalEntities; // entities that need to be checked for expiry
|
||||
|
||||
QSet<EntityItem*> _changedEntities; // entities that have changed in the last frame
|
||||
EntitySimulation* _simulation;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTree_h
|
||||
|
|
|
@ -768,8 +768,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
|||
addEntityItem(entityItem); // add this new entity to this elements entities
|
||||
entityItemID = entityItem->getEntityItemID();
|
||||
_myTree->setContainingElement(entityItemID, this);
|
||||
_myTree->updateEntityState(entityItem);
|
||||
_myTree->emitAddingEntity(entityItemID); // we just added an entity
|
||||
_myTree->postAddEntity(entityItem);
|
||||
}
|
||||
}
|
||||
// Move the buffer forward to read more entities
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
//
|
||||
|
||||
#include "EntityTreeHeadlessViewer.h"
|
||||
#include "SimpleEntitySimulation.h"
|
||||
|
||||
EntityTreeHeadlessViewer::EntityTreeHeadlessViewer() :
|
||||
OctreeHeadlessViewer() {
|
||||
EntityTreeHeadlessViewer::EntityTreeHeadlessViewer()
|
||||
: OctreeHeadlessViewer(), _simulation(NULL) {
|
||||
}
|
||||
|
||||
EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
|
||||
|
@ -20,9 +21,15 @@ EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
|
|||
|
||||
void EntityTreeHeadlessViewer::init() {
|
||||
OctreeHeadlessViewer::init();
|
||||
if (!_simulation) {
|
||||
SimpleEntitySimulation* simpleSimulation = new SimpleEntitySimulation();
|
||||
EntityTree* entityTree = static_cast<EntityTree*>(_tree);
|
||||
simpleSimulation->setEntityTree(entityTree);
|
||||
entityTree->setSimulation(simpleSimulation);
|
||||
_simulation = simpleSimulation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EntityTreeHeadlessViewer::update() {
|
||||
if (_tree) {
|
||||
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include "EntityTree.h"
|
||||
|
||||
class EntitySimulation;
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
class EntityTreeHeadlessViewer : public OctreeHeadlessViewer {
|
||||
Q_OBJECT
|
||||
|
@ -28,7 +30,6 @@ public:
|
|||
EntityTreeHeadlessViewer();
|
||||
virtual ~EntityTreeHeadlessViewer();
|
||||
|
||||
virtual Octree* createTree() { return new EntityTree(true); }
|
||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; }
|
||||
|
@ -40,6 +41,11 @@ public:
|
|||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
|
||||
virtual void init();
|
||||
|
||||
protected:
|
||||
virtual Octree* createTree() { return new EntityTree(true); }
|
||||
|
||||
EntitySimulation* _simulation;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTreeHeadlessViewer_h
|
||||
|
|
224
libraries/entities/src/SimpleEntitySimulation.cpp
Normal file
224
libraries/entities/src/SimpleEntitySimulation.cpp
Normal file
|
@ -0,0 +1,224 @@
|
|||
//
|
||||
// SimpleEntitySimulation.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.11.24
|
||||
// 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
|
||||
//
|
||||
|
||||
#include <AACube.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include "MovingEntitiesOperator.h"
|
||||
#include "SimpleEntitySimulation.h"
|
||||
|
||||
void SimpleEntitySimulation::update(QSet<EntityItem*>& entitiesToDelete) {
|
||||
quint64 now = usecTimestampNow();
|
||||
updateChangedEntities(now, entitiesToDelete);
|
||||
updateMovingEntities(now, entitiesToDelete);
|
||||
updateMortalEntities(now, entitiesToDelete);
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::addEntity(EntityItem* entity) {
|
||||
assert(entity && entity->getSimulationState() == EntityItem::Static);
|
||||
EntityItem::SimulationState state = entity->computeSimulationState();
|
||||
switch(state) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.push_back(entity);
|
||||
entity->setSimulationState(state);
|
||||
break;
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.push_back(entity);
|
||||
entity->setSimulationState(state);
|
||||
break;
|
||||
case EntityItem::Static:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::removeEntity(EntityItem* entity) {
|
||||
assert(entity);
|
||||
// make sure to remove it from any of our simulation lists
|
||||
EntityItem::SimulationState state = entity->getSimulationState();
|
||||
switch (state) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(entity);
|
||||
break;
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entity->setSimulationState(EntityItem::Static);
|
||||
_changedEntities.remove(entity);
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::entityChanged(EntityItem* entity) {
|
||||
assert(entity);
|
||||
// we batch all changes and deal with them in updateChangedEntities()
|
||||
_changedEntities.insert(entity);
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::clearEntities() {
|
||||
foreach (EntityItem* entity, _changedEntities) {
|
||||
entity->clearUpdateFlags();
|
||||
entity->setSimulationState(EntityItem::Static);
|
||||
}
|
||||
_changedEntities.clear();
|
||||
_movingEntities.clear();
|
||||
_mortalEntities.clear();
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete) {
|
||||
foreach (EntityItem* entity, _changedEntities) {
|
||||
// check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (entity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID();
|
||||
entitiesToDelete.insert(entity);
|
||||
clearEntityState(entity);
|
||||
} else {
|
||||
updateEntityState(entity);
|
||||
}
|
||||
entity->clearUpdateFlags();
|
||||
}
|
||||
_changedEntities.clear();
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete) {
|
||||
if (_entityTree && _movingEntities.size() > 0) {
|
||||
PerformanceTimer perfTimer("_movingEntities");
|
||||
MovingEntitiesOperator moveOperator(_entityTree);
|
||||
QList<EntityItem*>::iterator item_itr = _movingEntities.begin();
|
||||
while (item_itr != _movingEntities.end()) {
|
||||
EntityItem* entity = *item_itr;
|
||||
|
||||
// always check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (entity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID();
|
||||
entitiesToDelete.insert(entity);
|
||||
// remove entity from the list
|
||||
item_itr = _movingEntities.erase(item_itr);
|
||||
entity->setSimulationState(EntityItem::Static);
|
||||
} else {
|
||||
AACube oldCube = entity->getMaximumAACube();
|
||||
entity->update(now);
|
||||
AACube newCube = entity->getMaximumAACube();
|
||||
|
||||
// check to see if this movement has sent the entity outside of the domain.
|
||||
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
|
||||
if (!domainBounds.touches(newCube)) {
|
||||
qDebug() << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
entitiesToDelete.insert(entity);
|
||||
// remove entity from the list
|
||||
item_itr = _movingEntities.erase(item_itr);
|
||||
entity->setSimulationState(EntityItem::Static);
|
||||
} else {
|
||||
moveOperator.addEntityToMoveList(entity, oldCube, newCube);
|
||||
EntityItem::SimulationState newState = entity->computeSimulationState();
|
||||
if (newState != EntityItem::Moving) {
|
||||
if (newState == EntityItem::Mortal) {
|
||||
_mortalEntities.push_back(entity);
|
||||
}
|
||||
// remove entity from the list
|
||||
item_itr = _movingEntities.erase(item_itr);
|
||||
entity->setSimulationState(newState);
|
||||
} else {
|
||||
++item_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (moveOperator.hasMovingEntities()) {
|
||||
PerformanceTimer perfTimer("recurseTreeWithOperator");
|
||||
_entityTree->recurseTreeWithOperator(&moveOperator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete) {
|
||||
QList<EntityItem*>::iterator item_itr = _mortalEntities.begin();
|
||||
while (item_itr != _mortalEntities.end()) {
|
||||
EntityItem* entity = *item_itr;
|
||||
// always check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (entity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID();
|
||||
entitiesToDelete.insert(entity);
|
||||
// remove entity from the list
|
||||
item_itr = _mortalEntities.erase(item_itr);
|
||||
entity->setSimulationState(EntityItem::Static);
|
||||
} else {
|
||||
// check to see if this entity is no longer moving
|
||||
EntityItem::SimulationState newState = entity->computeSimulationState();
|
||||
if (newState != EntityItem::Mortal) {
|
||||
if (newState == EntityItem::Moving) {
|
||||
entity->update(now);
|
||||
_movingEntities.push_back(entity);
|
||||
}
|
||||
// remove entity from the list
|
||||
item_itr = _mortalEntities.erase(item_itr);
|
||||
entity->setSimulationState(newState);
|
||||
} else {
|
||||
++item_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::updateEntityState(EntityItem* entity) {
|
||||
EntityItem::SimulationState oldState = entity->getSimulationState();
|
||||
EntityItem::SimulationState newState = entity->computeSimulationState();
|
||||
if (newState != oldState) {
|
||||
switch (oldState) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (newState) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.push_back(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.push_back(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entity->setSimulationState(newState);
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::clearEntityState(EntityItem* entity) {
|
||||
EntityItem::SimulationState oldState = entity->getSimulationState();
|
||||
switch (oldState) {
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
entity->setSimulationState(EntityItem::Static);
|
||||
}
|
||||
|
||||
|
47
libraries/entities/src/SimpleEntitySimulation.h
Normal file
47
libraries/entities/src/SimpleEntitySimulation.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// SimpleEntitySimulation.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.11.24
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_SimpleEntitySimulation_h
|
||||
#define hifi_SimpleEntitySimulation_h
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
|
||||
/// provides simple velocity + gravity extrapolation of EntityItem's
|
||||
|
||||
class SimpleEntitySimulation : public EntitySimulation {
|
||||
public:
|
||||
SimpleEntitySimulation() : EntitySimulation() { }
|
||||
virtual ~SimpleEntitySimulation() { setEntityTree(NULL); }
|
||||
|
||||
virtual void update(QSet<EntityItem*>& entitiesToDelete);
|
||||
|
||||
virtual void addEntity(EntityItem* entity);
|
||||
virtual void removeEntity(EntityItem* entity);
|
||||
virtual void entityChanged(EntityItem* entity);
|
||||
|
||||
virtual void clearEntities();
|
||||
|
||||
protected:
|
||||
void updateEntityState(EntityItem* entity);
|
||||
void clearEntityState(EntityItem* entity);
|
||||
|
||||
QList<EntityItem*>& getMovingEntities() { return _movingEntities; }
|
||||
|
||||
void updateChangedEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete);
|
||||
void updateMovingEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete);
|
||||
void updateMortalEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete);
|
||||
|
||||
QSet<EntityItem*> _changedEntities; // entities that have changed in the last frame
|
||||
QList<EntityItem*> _movingEntities; // entities that need to be updated
|
||||
QList<EntityItem*> _mortalEntities; // non-moving entities that need to be checked for expiry
|
||||
};
|
||||
|
||||
#endif // hifi_SimpleEntitySimulation_h
|
|
@ -35,7 +35,6 @@ public:
|
|||
OctreeRenderer();
|
||||
virtual ~OctreeRenderer();
|
||||
|
||||
virtual Octree* createTree() = 0;
|
||||
virtual char getMyNodeType() const = 0;
|
||||
virtual PacketType getMyQueryMessageType() const = 0;
|
||||
virtual PacketType getExpectedPacketType() const = 0;
|
||||
|
@ -81,6 +80,8 @@ public:
|
|||
int getOpaqueMeshPartsRendered() const { return _opaqueMeshPartsRendered; }
|
||||
|
||||
protected:
|
||||
virtual Octree* createTree() = 0;
|
||||
|
||||
Octree* _tree;
|
||||
bool _managedTree;
|
||||
ViewFrustum* _viewFrustum;
|
||||
|
|
|
@ -27,7 +27,6 @@ public:
|
|||
VoxelTreeHeadlessViewer();
|
||||
virtual ~VoxelTreeHeadlessViewer();
|
||||
|
||||
virtual Octree* createTree() { return new VoxelTree(true); }
|
||||
virtual char getMyNodeType() const { return NodeType::VoxelServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketTypeVoxelData; }
|
||||
|
@ -35,6 +34,9 @@ public:
|
|||
VoxelTree* getTree() { return (VoxelTree*)_tree; }
|
||||
|
||||
virtual void init();
|
||||
|
||||
protected:
|
||||
virtual Octree* createTree() { return new VoxelTree(true); }
|
||||
};
|
||||
|
||||
#endif // hifi_VoxelTreeHeadlessViewer_h
|
||||
|
|
Loading…
Reference in a new issue