mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 21:15:07 +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 <QTimer>
|
||||||
#include <EntityTree.h>
|
#include <EntityTree.h>
|
||||||
|
#include <SimpleEntitySimulation.h>
|
||||||
|
|
||||||
#include "EntityServer.h"
|
#include "EntityServer.h"
|
||||||
#include "EntityServerConsts.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* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
|
||||||
const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
|
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...
|
// nothing special to do here...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +38,12 @@ OctreeQueryNode* EntityServer::createOctreeQueryNode() {
|
||||||
Octree* EntityServer::createTree() {
|
Octree* EntityServer::createTree() {
|
||||||
EntityTree* tree = new EntityTree(true);
|
EntityTree* tree = new EntityTree(true);
|
||||||
tree->addNewlyCreatedHook(this);
|
tree->addNewlyCreatedHook(this);
|
||||||
|
if (!_entitySimulation) {
|
||||||
|
SimpleEntitySimulation* simpleSimulation = new SimpleEntitySimulation();
|
||||||
|
simpleSimulation->setEntityTree(tree);
|
||||||
|
tree->setSimulation(simpleSimulation);
|
||||||
|
_entitySimulation = simpleSimulation;
|
||||||
|
}
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ public:
|
||||||
|
|
||||||
// Subclasses must implement these methods
|
// Subclasses must implement these methods
|
||||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||||
virtual Octree* createTree();
|
|
||||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
||||||
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
||||||
|
@ -46,7 +45,11 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void pruneDeletedEntities();
|
void pruneDeletedEntities();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Octree* createTree();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
EntitySimulation* _entitySimulation;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityServer_h
|
#endif // hifi_EntityServer_h
|
||||||
|
|
|
@ -62,7 +62,6 @@ public:
|
||||||
|
|
||||||
// Subclasses must implement these methods
|
// Subclasses must implement these methods
|
||||||
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
|
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
|
||||||
virtual Octree* createTree() = 0;
|
|
||||||
virtual char getMyNodeType() const = 0;
|
virtual char getMyNodeType() const = 0;
|
||||||
virtual PacketType getMyQueryMessageType() const = 0;
|
virtual PacketType getMyQueryMessageType() const = 0;
|
||||||
virtual const char* getMyServerName() const = 0;
|
virtual const char* getMyServerName() const = 0;
|
||||||
|
@ -132,6 +131,7 @@ public slots:
|
||||||
void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual Octree* createTree() = 0;
|
||||||
bool readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result);
|
bool readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result);
|
||||||
bool readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result);
|
bool readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result);
|
||||||
bool readOptionString(const QString& optionName, const QJsonObject& settingsSectionObject, QString& result);
|
bool readOptionString(const QString& optionName, const QJsonObject& settingsSectionObject, QString& result);
|
||||||
|
|
|
@ -36,7 +36,6 @@ public:
|
||||||
|
|
||||||
// Subclasses must implement these methods
|
// Subclasses must implement these methods
|
||||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||||
virtual Octree* createTree();
|
|
||||||
virtual char getMyNodeType() const { return NodeType::VoxelServer; }
|
virtual char getMyNodeType() const { return NodeType::VoxelServer; }
|
||||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
|
virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
|
||||||
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
||||||
|
@ -50,6 +49,7 @@ public:
|
||||||
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
|
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual Octree* createTree();
|
||||||
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject);
|
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -89,11 +89,19 @@ SelectionManager = (function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
that.addEntity = function(entityID) {
|
that.addEntity = function(entityID, toggleSelection) {
|
||||||
if (entityID.isKnownID) {
|
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) {
|
if (idx == -1) {
|
||||||
that.selections.push(entityID);
|
that.selections.push(entityID);
|
||||||
|
} else if (toggleSelection) {
|
||||||
|
that.selections.splice(idx, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var idx = that.pendingSelections.indexOf(entityID);
|
var idx = that.pendingSelections.indexOf(entityID);
|
||||||
|
@ -227,7 +235,6 @@ SelectionDisplay = (function () {
|
||||||
var overlayNames = new Array();
|
var overlayNames = new Array();
|
||||||
var lastCameraPosition = Camera.getPosition();
|
var lastCameraPosition = Camera.getPosition();
|
||||||
var lastCameraOrientation = Camera.getOrientation();
|
var lastCameraOrientation = Camera.getOrientation();
|
||||||
var lastPlaneIntersection;
|
|
||||||
|
|
||||||
var handleHoverColor = { red: 224, green: 67, blue: 36 };
|
var handleHoverColor = { red: 224, green: 67, blue: 36 };
|
||||||
var handleHoverAlpha = 1.0;
|
var handleHoverAlpha = 1.0;
|
||||||
|
@ -324,21 +331,23 @@ SelectionDisplay = (function () {
|
||||||
solid: false,
|
solid: false,
|
||||||
visible: false,
|
visible: false,
|
||||||
dashed: true,
|
dashed: true,
|
||||||
lineWidth: 1.0,
|
lineWidth: 2.0,
|
||||||
ignoreRayIntersection: true // this never ray intersects
|
ignoreRayIntersection: true // this never ray intersects
|
||||||
});
|
});
|
||||||
|
|
||||||
var selectionBox = Overlays.addOverlay("cube", {
|
var selectionBox = Overlays.addOverlay("cube", {
|
||||||
position: { x:0, y: 0, z: 0},
|
position: { x:0, y: 0, z: 0},
|
||||||
size: 1,
|
size: 1,
|
||||||
color: { red: 60, green: 60, blue: 60},
|
color: { red: 255, green: 0, blue: 0},
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
solid: false,
|
solid: false,
|
||||||
visible: false,
|
visible: false,
|
||||||
dashed: true,
|
dashed: false,
|
||||||
lineWidth: 1.0,
|
lineWidth: 1.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var selectionBoxes = [];
|
||||||
|
|
||||||
var rotationDegreesDisplay = Overlays.addOverlay("text3d", {
|
var rotationDegreesDisplay = Overlays.addOverlay("text3d", {
|
||||||
position: { x:0, y: 0, z: 0},
|
position: { x:0, y: 0, z: 0},
|
||||||
text: "",
|
text: "",
|
||||||
|
@ -684,6 +693,9 @@ SelectionDisplay = (function () {
|
||||||
for (var i = 0; i < allOverlays.length; i++) {
|
for (var i = 0; i < allOverlays.length; i++) {
|
||||||
Overlays.deleteOverlay(allOverlays[i]);
|
Overlays.deleteOverlay(allOverlays[i]);
|
||||||
}
|
}
|
||||||
|
for (var i = 0; i < selectionBoxes.length; i++) {
|
||||||
|
Overlays.deleteOverlay(selectionBoxes[i]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.highlightSelectable = function(entityID) {
|
that.highlightSelectable = function(entityID) {
|
||||||
|
@ -708,13 +720,11 @@ SelectionDisplay = (function () {
|
||||||
|
|
||||||
if (event !== false) {
|
if (event !== false) {
|
||||||
pickRay = Camera.computePickRay(event.x, event.y);
|
pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
lastPlaneIntersection = rayPlaneIntersection(pickRay, properties.position, Quat.getFront(lastCameraOrientation));
|
|
||||||
|
|
||||||
var wantDebug = false;
|
var wantDebug = false;
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("select() with EVENT...... ");
|
print("select() with EVENT...... ");
|
||||||
print(" event.y:" + event.y);
|
print(" event.y:" + event.y);
|
||||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
|
||||||
Vec3.print(" current position:", properties.position);
|
Vec3.print(" current position:", properties.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -933,23 +943,6 @@ SelectionDisplay = (function () {
|
||||||
var dimensions = selectionManager.worldDimensions;
|
var dimensions = selectionManager.worldDimensions;
|
||||||
var position = selectionManager.worldPosition;
|
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(rotateOverlayTarget, { visible: rotationOverlaysVisible });
|
||||||
Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible });
|
Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible });
|
||||||
|
@ -986,7 +979,6 @@ SelectionDisplay = (function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
that.updateRotationHandles();
|
that.updateRotationHandles();
|
||||||
that.highlightSelectable();
|
that.highlightSelectable();
|
||||||
|
|
||||||
|
@ -1126,6 +1118,41 @@ SelectionDisplay = (function () {
|
||||||
visible: !(mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL"),
|
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(grabberEdgeTR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTR });
|
||||||
Overlays.editOverlay(grabberEdgeTL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTL });
|
Overlays.editOverlay(grabberEdgeTL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTL });
|
||||||
Overlays.editOverlay(grabberEdgeTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTF });
|
Overlays.editOverlay(grabberEdgeTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTF });
|
||||||
|
@ -1142,6 +1169,23 @@ SelectionDisplay = (function () {
|
||||||
var grabberMoveUpOffset = 0.1;
|
var grabberMoveUpOffset = 0.1;
|
||||||
grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z }
|
grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z }
|
||||||
Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" });
|
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) {
|
that.setOverlaysVisible = function(isVisible) {
|
||||||
|
@ -1149,6 +1193,10 @@ SelectionDisplay = (function () {
|
||||||
for (var i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
Overlays.editOverlay(allOverlays[i], { visible: isVisible });
|
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) {
|
that.unselect = function (entityID) {
|
||||||
|
@ -1242,7 +1290,6 @@ SelectionDisplay = (function () {
|
||||||
|
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("translateXZ... ");
|
print("translateXZ... ");
|
||||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
|
||||||
Vec3.print(" vector:", vector);
|
Vec3.print(" vector:", vector);
|
||||||
Vec3.print(" newPosition:", properties.position);
|
Vec3.print(" newPosition:", properties.position);
|
||||||
Vec3.print(" newPosition:", newPosition);
|
Vec3.print(" newPosition:", newPosition);
|
||||||
|
@ -1254,10 +1301,17 @@ SelectionDisplay = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
var lastXYPick = null
|
var lastXYPick = null
|
||||||
|
var upDownPickNormal = null;
|
||||||
addGrabberTool(grabberMoveUp, {
|
addGrabberTool(grabberMoveUp, {
|
||||||
mode: "TRANSLATE_UP_DOWN",
|
mode: "TRANSLATE_UP_DOWN",
|
||||||
onBegin: function(event) {
|
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();
|
SelectionManager.saveProperties();
|
||||||
|
|
||||||
|
@ -1285,11 +1339,9 @@ SelectionDisplay = (function () {
|
||||||
pickRay = Camera.computePickRay(event.x, event.y);
|
pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
|
|
||||||
// translate mode left/right based on view toward entity
|
// translate mode left/right based on view toward entity
|
||||||
var newIntersection = rayPlaneIntersection(pickRay,
|
var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal);
|
||||||
SelectionManager.worldPosition,
|
|
||||||
Quat.getFront(lastCameraOrientation));
|
|
||||||
|
|
||||||
var vector = Vec3.subtract(newIntersection, lastPlaneIntersection);
|
var vector = Vec3.subtract(newIntersection, lastXYPick);
|
||||||
vector = grid.snapToGrid(vector);
|
vector = grid.snapToGrid(vector);
|
||||||
|
|
||||||
// we only care about the Y axis
|
// we only care about the Y axis
|
||||||
|
@ -1300,7 +1352,6 @@ SelectionDisplay = (function () {
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("translateUpDown... ");
|
print("translateUpDown... ");
|
||||||
print(" event.y:" + event.y);
|
print(" event.y:" + event.y);
|
||||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
|
||||||
Vec3.print(" newIntersection:", newIntersection);
|
Vec3.print(" newIntersection:", newIntersection);
|
||||||
Vec3.print(" vector:", vector);
|
Vec3.print(" vector:", vector);
|
||||||
Vec3.print(" newPosition:", newPosition);
|
Vec3.print(" newPosition:", newPosition);
|
||||||
|
@ -1513,7 +1564,6 @@ SelectionDisplay = (function () {
|
||||||
var wantDebug = false;
|
var wantDebug = false;
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print(stretchMode);
|
print(stretchMode);
|
||||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
|
||||||
Vec3.print(" newIntersection:", newIntersection);
|
Vec3.print(" newIntersection:", newIntersection);
|
||||||
Vec3.print(" vector:", vector);
|
Vec3.print(" vector:", vector);
|
||||||
Vec3.print(" oldPOS:", oldPOS);
|
Vec3.print(" oldPOS:", oldPOS);
|
||||||
|
@ -2010,7 +2060,7 @@ SelectionDisplay = (function () {
|
||||||
that.checkMove = function() {
|
that.checkMove = function() {
|
||||||
if (SelectionManager.hasSelection() &&
|
if (SelectionManager.hasSelection() &&
|
||||||
(!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation))){
|
(!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) {
|
if (somethingClicked) {
|
||||||
pickRay = Camera.computePickRay(event.x, event.y);
|
pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
lastPlaneIntersection = rayPlaneIntersection(pickRay, selectionManager.worldPosition,
|
|
||||||
Quat.getFront(lastCameraOrientation));
|
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
print("mousePressEvent()...... " + overlayNames[result.overlayID]);
|
print("mousePressEvent()...... " + overlayNames[result.overlayID]);
|
||||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,8 @@ Grid = function(opts) {
|
||||||
color: gridColor,
|
color: gridColor,
|
||||||
alpha: gridAlpha,
|
alpha: gridAlpha,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
that.emitUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
|
@ -207,6 +209,7 @@ GridTool = function(opts) {
|
||||||
|
|
||||||
horizontalGrid.addListener(function(data) {
|
horizontalGrid.addListener(function(data) {
|
||||||
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||||
|
selectionDisplay.updateHandles();
|
||||||
});
|
});
|
||||||
|
|
||||||
webView.eventBridge.webEventReceived.connect(function(data) {
|
webView.eventBridge.webEventReceived.connect(function(data) {
|
||||||
|
|
|
@ -51,8 +51,8 @@ var toolWidth = 50;
|
||||||
|
|
||||||
var MIN_ANGULAR_SIZE = 2;
|
var MIN_ANGULAR_SIZE = 2;
|
||||||
var MAX_ANGULAR_SIZE = 45;
|
var MAX_ANGULAR_SIZE = 45;
|
||||||
var allowLargeModels = false;
|
var allowLargeModels = true;
|
||||||
var allowSmallModels = false;
|
var allowSmallModels = true;
|
||||||
var wantEntityGlow = false;
|
var wantEntityGlow = false;
|
||||||
|
|
||||||
var SPAWN_DISTANCE = 1;
|
var SPAWN_DISTANCE = 1;
|
||||||
|
@ -476,7 +476,6 @@ function findClickedEntity(event) {
|
||||||
var identify = Entities.identifyEntity(foundEntity);
|
var identify = Entities.identifyEntity(foundEntity);
|
||||||
if (!identify.isKnownID) {
|
if (!identify.isKnownID) {
|
||||||
print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")");
|
print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")");
|
||||||
selectionManager.clearSelections();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
foundEntity = identify;
|
foundEntity = identify;
|
||||||
|
@ -485,74 +484,18 @@ function findClickedEntity(event) {
|
||||||
return { pickRay: pickRay, entityID: foundEntity };
|
return { pickRay: pickRay, entityID: foundEntity };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mouseHasMovedSincePress = false;
|
||||||
|
|
||||||
function mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
|
mouseHasMovedSincePress = false;
|
||||||
|
|
||||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
|
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
var entitySelected = false;
|
|
||||||
if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
||||||
// Event handled; do nothing.
|
// Event handled; do nothing.
|
||||||
return;
|
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)) {
|
} else if (Menu.isOptionChecked(MENU_INSPECT_TOOL_ENABLED)) {
|
||||||
var result = findClickedEntity(event);
|
var result = findClickedEntity(event);
|
||||||
|
@ -572,6 +515,7 @@ function mousePressEvent(event) {
|
||||||
var highlightedEntityID = { isKnownID: false };
|
var highlightedEntityID = { isKnownID: false };
|
||||||
|
|
||||||
function mouseMoveEvent(event) {
|
function mouseMoveEvent(event) {
|
||||||
|
mouseHasMovedSincePress = true;
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
// allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing
|
// 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)) {
|
if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) {
|
||||||
|
@ -615,6 +559,72 @@ function mouseReleaseEvent(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cameraManager.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);
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
|
@ -644,9 +654,9 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
|
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: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L",
|
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",
|
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: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||||
|
|
|
@ -442,6 +442,7 @@ void Application::aboutToQuit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
|
_entities.getTree()->setSimulation(NULL);
|
||||||
qInstallMessageHandler(NULL);
|
qInstallMessageHandler(NULL);
|
||||||
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
@ -1976,7 +1977,9 @@ void Application::init() {
|
||||||
_entities.init();
|
_entities.init();
|
||||||
_entities.setViewFrustum(getViewFrustum());
|
_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 the _entityCollisionSystem to our script engine's EntityScriptingInterface
|
||||||
connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithVoxel,
|
connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithVoxel,
|
||||||
|
@ -2327,11 +2330,12 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
if (!_aboutToQuit) {
|
if (!_aboutToQuit) {
|
||||||
PerformanceTimer perfTimer("entities");
|
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...
|
_entities.update(); // update the models...
|
||||||
{
|
// The _entityCollisionSystem.updateCollisions() call below merely tries for lock,
|
||||||
PerformanceTimer perfTimer("collisions");
|
// and on failure it skips collision detection.
|
||||||
_entityCollisionSystem.update(); // collide the entities...
|
_entityCollisionSystem.updateCollisions(); // collide the entities...
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
#include <QUndoStack>
|
#include <QUndoStack>
|
||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
|
|
||||||
#include <EntityEditPacketSender.h>
|
|
||||||
#include <EntityCollisionSystem.h>
|
#include <EntityCollisionSystem.h>
|
||||||
|
#include <EntityEditPacketSender.h>
|
||||||
#include <NetworkPacket.h>
|
#include <NetworkPacket.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
|
|
|
@ -40,7 +40,6 @@ public:
|
||||||
EntityTreeRenderer(bool wantScripts);
|
EntityTreeRenderer(bool wantScripts);
|
||||||
virtual ~EntityTreeRenderer();
|
virtual ~EntityTreeRenderer();
|
||||||
|
|
||||||
virtual Octree* createTree() { return new EntityTree(true); }
|
|
||||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
||||||
virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; }
|
virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; }
|
||||||
|
@ -108,6 +107,9 @@ public slots:
|
||||||
void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
|
void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
|
||||||
void entitySciptChanging(const EntityItemID& entityID);
|
void entitySciptChanging(const EntityItemID& entityID);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Octree* createTree() { return new EntityTree(true); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void checkAndCallPreload(const EntityItemID& entityID);
|
void checkAndCallPreload(const EntityItemID& entityID);
|
||||||
void checkAndCallUnload(const EntityItemID& entityID);
|
void checkAndCallUnload(const EntityItemID& entityID);
|
||||||
|
|
|
@ -48,9 +48,6 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt
|
||||||
details.cube = details.containingElement->getAACube();
|
details.cube = details.containingElement->getAACube();
|
||||||
_entitiesToDelete << details;
|
_entitiesToDelete << details;
|
||||||
_lookingCount++;
|
_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);
|
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||||
|
|
||||||
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
|
// 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
|
// 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 all entities, and
|
||||||
// * We have not yet found the old entity, and this branch contains our old entity
|
// * this branch contains our some of the entities we're looking for.
|
||||||
// * 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.
|
|
||||||
|
|
||||||
bool keepSearching = false; // assume we don't need to search any more
|
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) {
|
if (entityTreeElement == details.containingElement) {
|
||||||
EntityItemID entityItemID = details.entity->getEntityItemID();
|
EntityItemID entityItemID = details.entity->getEntityItemID();
|
||||||
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity
|
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity
|
||||||
|
assert(theEntity);
|
||||||
|
_tree->trackDeletedEntity(theEntity);
|
||||||
entityTreeElement->removeEntityItem(theEntity); // remove it from the element
|
entityTreeElement->removeEntityItem(theEntity); // remove it from the element
|
||||||
_tree->setContainingElement(entityItemID, NULL); // update or id to element lookup
|
_tree->setContainingElement(entityItemID, NULL); // update or id to element lookup
|
||||||
delete theEntity; // now actually delete the entity!
|
delete theEntity; // now actually delete the entity!
|
||||||
|
|
|
@ -16,27 +16,32 @@
|
||||||
#include <CollisionInfo.h>
|
#include <CollisionInfo.h>
|
||||||
#include <HeadData.h>
|
#include <HeadData.h>
|
||||||
#include <HandData.h>
|
#include <HandData.h>
|
||||||
|
#include <PerfStat.h>
|
||||||
#include <SphereShape.h>
|
#include <SphereShape.h>
|
||||||
|
|
||||||
#include "EntityItem.h"
|
|
||||||
#include "EntityCollisionSystem.h"
|
#include "EntityCollisionSystem.h"
|
||||||
#include "EntityEditPacketSender.h"
|
#include "EntityEditPacketSender.h"
|
||||||
#include "EntityTree.h"
|
#include "EntityItem.h"
|
||||||
#include "EntityTreeElement.h"
|
#include "EntityTreeElement.h"
|
||||||
|
#include "EntityTree.h"
|
||||||
|
|
||||||
const int MAX_COLLISIONS_PER_Entity = 16;
|
const int MAX_COLLISIONS_PER_Entity = 16;
|
||||||
|
|
||||||
EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender,
|
EntityCollisionSystem::EntityCollisionSystem()
|
||||||
EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
|
: SimpleEntitySimulation(),
|
||||||
AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) {
|
_packetSender(NULL),
|
||||||
init(packetSender, Entities, voxels, audio, avatars);
|
_voxels(NULL),
|
||||||
|
_audio(NULL),
|
||||||
|
_avatars(NULL),
|
||||||
|
_collisions(MAX_COLLISIONS_PER_Entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityCollisionSystem::init(EntityEditPacketSender* packetSender,
|
void EntityCollisionSystem::init(EntityEditPacketSender* packetSender,
|
||||||
EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
|
EntityTree* entities, VoxelTree* voxels, AbstractAudioInterface* audio,
|
||||||
AvatarHashMap* avatars) {
|
AvatarHashMap* avatars) {
|
||||||
|
assert(entities);
|
||||||
|
setEntityTree(entities);
|
||||||
_packetSender = packetSender;
|
_packetSender = packetSender;
|
||||||
_entities = Entities;
|
|
||||||
_voxels = voxels;
|
_voxels = voxels;
|
||||||
_audio = audio;
|
_audio = audio;
|
||||||
_avatars = avatars;
|
_avatars = avatars;
|
||||||
|
@ -45,14 +50,15 @@ void EntityCollisionSystem::init(EntityEditPacketSender* packetSender,
|
||||||
EntityCollisionSystem::~EntityCollisionSystem() {
|
EntityCollisionSystem::~EntityCollisionSystem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityCollisionSystem::update() {
|
void EntityCollisionSystem::updateCollisions() {
|
||||||
|
PerformanceTimer perfTimer("collisions");
|
||||||
|
assert(_entityTree);
|
||||||
// update all Entities
|
// update all Entities
|
||||||
if (_entities->tryLockForRead()) {
|
if (_entityTree->tryLockForRead()) {
|
||||||
QList<EntityItem*>& movingEntities = _entities->getMovingEntities();
|
foreach (EntityItem* entity, _movingEntities) {
|
||||||
foreach (EntityItem* entity, movingEntities) {
|
|
||||||
checkEntity(entity);
|
checkEntity(entity);
|
||||||
}
|
}
|
||||||
_entities->unlock();
|
_entityTree->unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,9 +133,8 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
||||||
CollisionList collisions(MAX_COLLISIONS_PER_ENTITY);
|
CollisionList collisions(MAX_COLLISIONS_PER_ENTITY);
|
||||||
bool shapeCollisionsAccurate = false;
|
bool shapeCollisionsAccurate = false;
|
||||||
|
|
||||||
bool shapeCollisions = _entities->findShapeCollisions(&entityA->getCollisionShapeInMeters(),
|
bool shapeCollisions = _entityTree->findShapeCollisions(&entityA->getCollisionShapeInMeters(),
|
||||||
collisions, Octree::NoLock, &shapeCollisionsAccurate);
|
collisions, Octree::NoLock, &shapeCollisionsAccurate);
|
||||||
|
|
||||||
|
|
||||||
if (shapeCollisions) {
|
if (shapeCollisions) {
|
||||||
for(int i = 0; i < collisions.size(); i++) {
|
for(int i = 0; i < collisions.size(); i++) {
|
||||||
|
@ -203,7 +208,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
||||||
propertiesA.setPosition(newPositionA * (float)TREE_SCALE);
|
propertiesA.setPosition(newPositionA * (float)TREE_SCALE);
|
||||||
propertiesA.setLastEdited(now);
|
propertiesA.setLastEdited(now);
|
||||||
|
|
||||||
_entities->updateEntity(idA, propertiesA);
|
_entityTree->updateEntity(idA, propertiesA);
|
||||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
|
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +225,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
||||||
propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
|
propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
|
||||||
propertiesB.setLastEdited(now);
|
propertiesB.setLastEdited(now);
|
||||||
|
|
||||||
_entities->updateEntity(idB, propertiesB);
|
_entityTree->updateEntity(idB, propertiesB);
|
||||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, 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.setVelocity(velocity * (float)TREE_SCALE);
|
||||||
properties.setLastEdited(usecTimestampNow());
|
properties.setLastEdited(usecTimestampNow());
|
||||||
|
|
||||||
_entities->updateEntity(entityItemID, properties);
|
_entityTree->updateEntity(entityItemID, properties);
|
||||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties);
|
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,12 @@
|
||||||
|
|
||||||
#include <AvatarHashMap.h>
|
#include <AvatarHashMap.h>
|
||||||
#include <CollisionInfo.h>
|
#include <CollisionInfo.h>
|
||||||
#include <SharedUtil.h>
|
|
||||||
#include <OctreePacketData.h>
|
#include <OctreePacketData.h>
|
||||||
|
#include <SharedUtil.h>
|
||||||
#include <VoxelDetail.h>
|
#include <VoxelDetail.h>
|
||||||
|
|
||||||
#include "EntityItem.h"
|
#include "EntityItem.h"
|
||||||
|
#include "SimpleEntitySimulation.h"
|
||||||
|
|
||||||
class AbstractAudioInterface;
|
class AbstractAudioInterface;
|
||||||
class AvatarData;
|
class AvatarData;
|
||||||
|
@ -32,19 +33,17 @@ class EntityEditPacketSender;
|
||||||
class EntityTree;
|
class EntityTree;
|
||||||
class VoxelTree;
|
class VoxelTree;
|
||||||
|
|
||||||
class EntityCollisionSystem : public QObject {
|
class EntityCollisionSystem : public QObject, public SimpleEntitySimulation {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
EntityCollisionSystem(EntityEditPacketSender* packetSender = NULL, EntityTree* Entitys = NULL,
|
EntityCollisionSystem();
|
||||||
VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL,
|
|
||||||
AvatarHashMap* avatars = NULL);
|
|
||||||
|
|
||||||
void init(EntityEditPacketSender* packetSender, EntityTree* Entitys, VoxelTree* voxels,
|
void init(EntityEditPacketSender* packetSender, EntityTree* entities, VoxelTree* voxels,
|
||||||
AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL);
|
AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL);
|
||||||
|
|
||||||
~EntityCollisionSystem();
|
~EntityCollisionSystem();
|
||||||
|
|
||||||
void update();
|
void updateCollisions();
|
||||||
|
|
||||||
void checkEntity(EntityItem* Entity);
|
void checkEntity(EntityItem* Entity);
|
||||||
void updateCollisionWithVoxels(EntityItem* Entity);
|
void updateCollisionWithVoxels(EntityItem* Entity);
|
||||||
|
@ -65,7 +64,6 @@ private:
|
||||||
void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration);
|
void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration);
|
||||||
|
|
||||||
EntityEditPacketSender* _packetSender;
|
EntityEditPacketSender* _packetSender;
|
||||||
EntityTree* _entities;
|
|
||||||
VoxelTree* _voxels;
|
VoxelTree* _voxels;
|
||||||
AbstractAudioInterface* _audio;
|
AbstractAudioInterface* _audio;
|
||||||
AvatarHashMap* _avatars;
|
AvatarHashMap* _avatars;
|
||||||
|
|
|
@ -292,16 +292,10 @@ public:
|
||||||
uint32_t getUpdateFlags() const { return _updateFlags; }
|
uint32_t getUpdateFlags() const { return _updateFlags; }
|
||||||
void clearUpdateFlags() { _updateFlags = 0; }
|
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; }
|
SimulationState getSimulationState() const { return _simulationState; }
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class EntityTree;
|
|
||||||
void setSimulationState(SimulationState state) { _simulationState = state; }
|
void setSimulationState(SimulationState state) { _simulationState = state; }
|
||||||
|
protected:
|
||||||
|
|
||||||
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
||||||
virtual void recalculateCollisionShape();
|
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 <PerfStat.h>
|
||||||
|
|
||||||
#include "EntityTree.h"
|
#include "EntityTree.h"
|
||||||
|
#include "EntitySimulation.h"
|
||||||
|
|
||||||
#include "AddEntityOperator.h"
|
#include "AddEntityOperator.h"
|
||||||
#include "DeleteEntityOperator.h"
|
#include "DeleteEntityOperator.h"
|
||||||
#include "MovingEntitiesOperator.h"
|
#include "MovingEntitiesOperator.h"
|
||||||
#include "UpdateEntityOperator.h"
|
#include "UpdateEntityOperator.h"
|
||||||
|
|
||||||
EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) {
|
EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) {
|
||||||
_rootElement = createNewElement();
|
_rootElement = createNewElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,14 +35,14 @@ EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) {
|
||||||
|
|
||||||
void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
||||||
// this would be a good place to clean up our entities...
|
// this would be a good place to clean up our entities...
|
||||||
|
if (_simulation) {
|
||||||
|
_simulation->clearEntities();
|
||||||
|
}
|
||||||
foreach (EntityTreeElement* element, _entityToElementMap) {
|
foreach (EntityTreeElement* element, _entityToElementMap) {
|
||||||
element->cleanupEntities();
|
element->cleanupEntities();
|
||||||
}
|
}
|
||||||
_entityToElementMap.clear();
|
_entityToElementMap.clear();
|
||||||
Octree::eraseAllOctreeElements(createNewRoot);
|
Octree::eraseAllOctreeElements(createNewRoot);
|
||||||
_movingEntities.clear();
|
|
||||||
_mortalEntities.clear();
|
|
||||||
_changedEntities.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
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
|
/// Adds a new entity item to the tree
|
||||||
void EntityTree::addEntityItem(EntityItem* entityItem) {
|
void EntityTree::postAddEntity(EntityItem* entity) {
|
||||||
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
|
assert(entity);
|
||||||
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);
|
|
||||||
|
|
||||||
// check to see if we need to simulate this entity..
|
// check to see if we need to simulate this entity..
|
||||||
updateEntityState(entityItem);
|
if (_simulation) {
|
||||||
|
_simulation->addEntity(entity);
|
||||||
|
}
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
|
emit addingEntity(entity->getEntityItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
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);
|
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||||
if (!containingElement) {
|
if (!containingElement) {
|
||||||
qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
|
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);
|
UpdateEntityOperator theOperator(this, containingElement, existingEntity, tempProperties);
|
||||||
recurseTreeWithOperator(&theOperator);
|
recurseTreeWithOperator(&theOperator);
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
|
if (_simulation && existingEntity->getUpdateFlags() != 0) {
|
||||||
|
_simulation->entityChanged(existingEntity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -129,13 +123,14 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
||||||
recurseTreeWithOperator(&theOperator);
|
recurseTreeWithOperator(&theOperator);
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
|
|
||||||
updateEntityState(existingEntity);
|
if (_simulation && existingEntity->getUpdateFlags() != 0) {
|
||||||
|
_simulation->entityChanged(existingEntity);
|
||||||
|
}
|
||||||
|
|
||||||
QString entityScriptAfter = existingEntity->getScript();
|
QString entityScriptAfter = existingEntity->getScript();
|
||||||
if (entityScriptBefore != entityScriptAfter) {
|
if (entityScriptBefore != entityScriptAfter) {
|
||||||
emitEntityScriptChanging(entityID); // the entity script has changed
|
emitEntityScriptChanging(entityID); // the entity script has changed
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
containingElement = getContainingElement(entityID);
|
containingElement = getContainingElement(entityID);
|
||||||
|
@ -171,33 +166,47 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
|
||||||
result = EntityTypes::constructEntityItem(type, entityID, properties);
|
result = EntityTypes::constructEntityItem(type, entityID, properties);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
// this does the actual adding of the entity
|
// Recurse the tree and store the entity in the correct tree element
|
||||||
addEntityItem(result);
|
AddEntityOperator theOperator(this, result);
|
||||||
emitAddingEntity(entityID);
|
recurseTreeWithOperator(&theOperator);
|
||||||
|
|
||||||
|
postAddEntity(result);
|
||||||
}
|
}
|
||||||
return 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
|
// this is only needed on the server to send delete messages for recently deleted entities to the viewers
|
||||||
if (getIsServer()) {
|
if (getIsServer()) {
|
||||||
// set up the deleted entities ID
|
// set up the deleted entities ID
|
||||||
quint64 deletedAt = usecTimestampNow();
|
quint64 deletedAt = usecTimestampNow();
|
||||||
_recentlyDeletedEntitiesLock.lockForWrite();
|
_recentlyDeletedEntitiesLock.lockForWrite();
|
||||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, entityID.id);
|
_recentlyDeletedEntityItemIDs.insert(deletedAt, entity->getEntityItemID().id);
|
||||||
_recentlyDeletedEntitiesLock.unlock();
|
_recentlyDeletedEntitiesLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::emitAddingEntity(const EntityItemID& entityItemID) {
|
|
||||||
emit addingEntity(entityItemID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
|
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
|
||||||
emit entityScriptChanging(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) {
|
void EntityTree::deleteEntity(const EntityItemID& entityID) {
|
||||||
emit deletingEntity(entityID);
|
emit deletingEntity(entityID);
|
||||||
|
|
||||||
|
@ -220,29 +229,6 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs) {
|
||||||
_isDirty = true;
|
_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 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
|
/// 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
|
/// 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();
|
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) {
|
void EntityTree::entityChanged(EntityItem* entity) {
|
||||||
_changedEntities.insert(entity);
|
if (_simulation) {
|
||||||
|
_simulation->entityChanged(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::update() {
|
void EntityTree::update() {
|
||||||
// our new strategy should be to segregate entities into three classes:
|
if (_simulation) {
|
||||||
// 1) stationary things that are not changing - most models
|
lockForWrite();
|
||||||
// 2) mortal things - these are stationary but have a lifetime - then need to be checked,
|
QSet<EntityItem*> entitiesToDelete;
|
||||||
// can be touched linearly, and won't change the tree
|
_simulation->update(entitiesToDelete);
|
||||||
// 2) changing things - like things animating they can be touched linearly and they don't change the tree
|
if (entitiesToDelete.size() > 0) {
|
||||||
// 3) moving things - these need to scan the tree and update accordingly
|
// translate into list of ID's
|
||||||
// finally - all things that need to be deleted, can be handled on a single delete pass.
|
QSet<EntityItemID> idsToDelete;
|
||||||
//
|
foreach (EntityItem* entity, entitiesToDelete) {
|
||||||
// TODO: theoretically we could combine the move and delete tree passes...
|
idsToDelete.insert(entity->getEntityItemID());
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
deleteEntities(idsToDelete);
|
||||||
}
|
}
|
||||||
if (moveOperator.hasMovingEntities()) {
|
unlock();
|
||||||
PerformanceTimer perfTimer("recurseTreeWithOperator");
|
|
||||||
recurseTreeWithOperator(&moveOperator);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
|
||||||
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
||||||
bool hasSomethingNewer = false;
|
bool hasSomethingNewer = false;
|
||||||
|
@ -966,19 +798,16 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
|
||||||
|
|
||||||
EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ {
|
EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ {
|
||||||
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
||||||
if (_entityToElementMap.contains(entityItemID)) {
|
EntityTreeElement* element = _entityToElementMap.value(entityItemID);
|
||||||
return _entityToElementMap.value(entityItemID);
|
if (!element && entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){
|
||||||
} else if (entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){
|
|
||||||
// check the creator token version too...
|
// check the creator token version too...
|
||||||
EntityItemID creatorTokenOnly;
|
EntityItemID creatorTokenOnly;
|
||||||
creatorTokenOnly.id = UNKNOWN_ENTITY_ID;
|
creatorTokenOnly.id = UNKNOWN_ENTITY_ID;
|
||||||
creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID;
|
creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID;
|
||||||
creatorTokenOnly.isKnownID = false;
|
creatorTokenOnly.isKnownID = false;
|
||||||
if (_entityToElementMap.contains(creatorTokenOnly)) {
|
element = _entityToElementMap.value(creatorTokenOnly);
|
||||||
return _entityToElementMap.value(creatorTokenOnly);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
|
|
||||||
class Model;
|
class Model;
|
||||||
|
class EntitySimulation;
|
||||||
|
|
||||||
class NewlyCreatedEntityHook {
|
class NewlyCreatedEntityHook {
|
||||||
public:
|
public:
|
||||||
|
@ -78,13 +79,13 @@ public:
|
||||||
|
|
||||||
// The newer API...
|
// The newer API...
|
||||||
EntityItem* getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties);
|
EntityItem* getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
void addEntityItem(EntityItem* entityItem);
|
void postAddEntity(EntityItem* entityItem);
|
||||||
|
|
||||||
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
void deleteEntity(const EntityItemID& entityID);
|
void deleteEntity(const EntityItemID& entityID);
|
||||||
void deleteEntities(QSet<EntityItemID> entityIDs);
|
void deleteEntities(QSet<EntityItemID> entityIDs);
|
||||||
void removeEntityFromSimulationLists(const EntityItemID& entityID);
|
void removeEntityFromSimulation(EntityItem* entity);
|
||||||
|
|
||||||
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
|
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
|
||||||
EntityItem* findEntityByID(const QUuid& id);
|
EntityItem* findEntityByID(const QUuid& id);
|
||||||
|
@ -137,18 +138,14 @@ public:
|
||||||
|
|
||||||
void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
|
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 entityChanged(EntityItem* entity);
|
||||||
|
|
||||||
void trackDeletedEntity(const EntityItemID& entityID);
|
void trackDeletedEntity(EntityItem* entity);
|
||||||
|
|
||||||
void emitAddingEntity(const EntityItemID& entityItemID);
|
|
||||||
void emitEntityScriptChanging(const EntityItemID& entityItemID);
|
void emitEntityScriptChanging(const EntityItemID& entityItemID);
|
||||||
|
|
||||||
QList<EntityItem*>& getMovingEntities() { return _movingEntities; }
|
void setSimulation(EntitySimulation* simulation);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void deletingEntity(const EntityItemID& entityID);
|
void deletingEntity(const EntityItemID& entityID);
|
||||||
void addingEntity(const EntityItemID& entityID);
|
void addingEntity(const EntityItemID& entityID);
|
||||||
|
@ -157,10 +154,6 @@ signals:
|
||||||
|
|
||||||
private:
|
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 findNearPointOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
||||||
|
@ -176,11 +169,7 @@ private:
|
||||||
EntityItemFBXService* _fbxService;
|
EntityItemFBXService* _fbxService;
|
||||||
|
|
||||||
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
|
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
|
||||||
|
EntitySimulation* _simulation;
|
||||||
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityTree_h
|
#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
|
addEntityItem(entityItem); // add this new entity to this elements entities
|
||||||
entityItemID = entityItem->getEntityItemID();
|
entityItemID = entityItem->getEntityItemID();
|
||||||
_myTree->setContainingElement(entityItemID, this);
|
_myTree->setContainingElement(entityItemID, this);
|
||||||
_myTree->updateEntityState(entityItem);
|
_myTree->postAddEntity(entityItem);
|
||||||
_myTree->emitAddingEntity(entityItemID); // we just added an entity
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Move the buffer forward to read more entities
|
// Move the buffer forward to read more entities
|
||||||
|
|
|
@ -10,9 +10,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "EntityTreeHeadlessViewer.h"
|
#include "EntityTreeHeadlessViewer.h"
|
||||||
|
#include "SimpleEntitySimulation.h"
|
||||||
|
|
||||||
EntityTreeHeadlessViewer::EntityTreeHeadlessViewer() :
|
EntityTreeHeadlessViewer::EntityTreeHeadlessViewer()
|
||||||
OctreeHeadlessViewer() {
|
: OctreeHeadlessViewer(), _simulation(NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
|
EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
|
||||||
|
@ -20,9 +21,15 @@ EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
|
||||||
|
|
||||||
void EntityTreeHeadlessViewer::init() {
|
void EntityTreeHeadlessViewer::init() {
|
||||||
OctreeHeadlessViewer::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() {
|
void EntityTreeHeadlessViewer::update() {
|
||||||
if (_tree) {
|
if (_tree) {
|
||||||
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include "EntityTree.h"
|
#include "EntityTree.h"
|
||||||
|
|
||||||
|
class EntitySimulation;
|
||||||
|
|
||||||
// Generic client side Octree renderer class.
|
// Generic client side Octree renderer class.
|
||||||
class EntityTreeHeadlessViewer : public OctreeHeadlessViewer {
|
class EntityTreeHeadlessViewer : public OctreeHeadlessViewer {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -28,7 +30,6 @@ public:
|
||||||
EntityTreeHeadlessViewer();
|
EntityTreeHeadlessViewer();
|
||||||
virtual ~EntityTreeHeadlessViewer();
|
virtual ~EntityTreeHeadlessViewer();
|
||||||
|
|
||||||
virtual Octree* createTree() { return new EntityTree(true); }
|
|
||||||
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
virtual char getMyNodeType() const { return NodeType::EntityServer; }
|
||||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; }
|
||||||
virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; }
|
virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; }
|
||||||
|
@ -40,6 +41,11 @@ public:
|
||||||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||||
|
|
||||||
virtual void init();
|
virtual void init();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Octree* createTree() { return new EntityTree(true); }
|
||||||
|
|
||||||
|
EntitySimulation* _simulation;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityTreeHeadlessViewer_h
|
#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();
|
OctreeRenderer();
|
||||||
virtual ~OctreeRenderer();
|
virtual ~OctreeRenderer();
|
||||||
|
|
||||||
virtual Octree* createTree() = 0;
|
|
||||||
virtual char getMyNodeType() const = 0;
|
virtual char getMyNodeType() const = 0;
|
||||||
virtual PacketType getMyQueryMessageType() const = 0;
|
virtual PacketType getMyQueryMessageType() const = 0;
|
||||||
virtual PacketType getExpectedPacketType() const = 0;
|
virtual PacketType getExpectedPacketType() const = 0;
|
||||||
|
@ -81,6 +80,8 @@ public:
|
||||||
int getOpaqueMeshPartsRendered() const { return _opaqueMeshPartsRendered; }
|
int getOpaqueMeshPartsRendered() const { return _opaqueMeshPartsRendered; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual Octree* createTree() = 0;
|
||||||
|
|
||||||
Octree* _tree;
|
Octree* _tree;
|
||||||
bool _managedTree;
|
bool _managedTree;
|
||||||
ViewFrustum* _viewFrustum;
|
ViewFrustum* _viewFrustum;
|
||||||
|
|
|
@ -27,7 +27,6 @@ public:
|
||||||
VoxelTreeHeadlessViewer();
|
VoxelTreeHeadlessViewer();
|
||||||
virtual ~VoxelTreeHeadlessViewer();
|
virtual ~VoxelTreeHeadlessViewer();
|
||||||
|
|
||||||
virtual Octree* createTree() { return new VoxelTree(true); }
|
|
||||||
virtual char getMyNodeType() const { return NodeType::VoxelServer; }
|
virtual char getMyNodeType() const { return NodeType::VoxelServer; }
|
||||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
|
virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
|
||||||
virtual PacketType getExpectedPacketType() const { return PacketTypeVoxelData; }
|
virtual PacketType getExpectedPacketType() const { return PacketTypeVoxelData; }
|
||||||
|
@ -35,6 +34,9 @@ public:
|
||||||
VoxelTree* getTree() { return (VoxelTree*)_tree; }
|
VoxelTree* getTree() { return (VoxelTree*)_tree; }
|
||||||
|
|
||||||
virtual void init();
|
virtual void init();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Octree* createTree() { return new VoxelTree(true); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_VoxelTreeHeadlessViewer_h
|
#endif // hifi_VoxelTreeHeadlessViewer_h
|
||||||
|
|
Loading…
Reference in a new issue