diff --git a/examples/ControlACs.js b/examples/ControlACs.js index bbdb1cc4ef..73a4385c24 100644 --- a/examples/ControlACs.js +++ b/examples/ControlACs.js @@ -117,6 +117,7 @@ function setupToolBars() { leftMargin: TEXT_MARGIN, topMargin: TEXT_MARGIN, alpha: ALPHA_OFF, + backgroundAlpha: ALPHA_OFF, visible: true })); } diff --git a/examples/Recorder.js b/examples/Recorder.js index 8c784bcea2..ff8c449012 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -115,6 +115,7 @@ function setupTimer() { width: 0, height: 0, alpha: 1.0, + backgroundAlpha: 1.0, visible: true }); @@ -129,6 +130,7 @@ function setupTimer() { width: slider.w, height: slider.h, alpha: 1.0, + backgroundAlpha: 1.0, visible: true }); slider.foreground = Overlays.addOverlay("text", { @@ -138,6 +140,7 @@ function setupTimer() { width: slider.pos * slider.w, height: slider.h, alpha: 1.0, + backgroundAlpha: 1.0, visible: true }); diff --git a/examples/editModels.js b/examples/editModels.js index 60ca9b6ca6..c961d55bed 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -206,6 +206,7 @@ var progressDialog = (function () { height: backgroundHeight, imageURL: backgroundUrl, alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); @@ -216,6 +217,7 @@ var progressDialog = (function () { textColor: textColor, backgroundColor: textBackground, alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); @@ -226,6 +228,7 @@ var progressDialog = (function () { textColor: textColor, backgroundColor: textBackground, alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); @@ -1169,24 +1172,22 @@ var toolBar = (function () { menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2; loadURLMenuItem = Overlays.addOverlay("text", { - x: newModelButton.x - menuItemWidth, - y: newModelButton.y + menuItemOffset, height: menuItemHeight, backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model URL", alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); loadFileMenuItem = Overlays.addOverlay("text", { - x: newModelButton.x - menuItemWidth, - y: newModelButton.y + menuItemOffset + menuItemHeight, height: menuItemHeight, backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model File", alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); @@ -1493,6 +1494,7 @@ var ExportMenu = function (opts) { width: scaleViewWidth, height: height, alpha: 0.0, + backgroundAlpha: 0.0, color: { red: 255, green: 255, blue: 255 }, text: "1" }); @@ -2480,6 +2482,7 @@ function Tooltip() { text: "", color: { red: 228, green: 228, blue: 228 }, alpha: 0.8, + backgroundAlpha: 0.8, visible: false }); this.show = function (doShow) { diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 1dd825d3cf..ff096973a3 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -319,7 +319,7 @@ function ScaleSelector() { width: this.SECOND_PART, height: this.height, topMargin: 13, text: this.scale.toString(), - alpha: 0.9, + backgroundAlpha: 0.0, visible: editToolsOn }); this.powerOverlay = Overlays.addOverlay("text", { @@ -327,7 +327,7 @@ function ScaleSelector() { width: this.SECOND_PART, height: this.height, leftMargin: 28, text: this.power.toString(), - alpha: 0.0, + backgroundAlpha: 0.0, visible: false }); this.setScale = function(scale) { diff --git a/examples/fallingSand.js b/examples/fallingSand.js index 5018aeb9ca..c85196f10e 100644 --- a/examples/fallingSand.js +++ b/examples/fallingSand.js @@ -117,7 +117,7 @@ function ScaleSelector() { width: this.SECOND_PART, height: this.height, topMargin: 13, text: this.scale.toString(), - alpha: 0.0, + backgroundAlpha: 0.0, visible: editToolsOn, color: activeUIColor }); @@ -126,7 +126,7 @@ function ScaleSelector() { width: this.SECOND_PART, height: this.height, leftMargin: 28, text: this.power.toString(), - alpha: 0.0, + backgroundAlpha: 0.0, visible: false, color: activeUIColor }); diff --git a/examples/growTrees.js b/examples/growTrees.js index 13d6cdc587..a5b55eecd6 100644 --- a/examples/growTrees.js +++ b/examples/growTrees.js @@ -116,7 +116,7 @@ function ScaleSelector() { width: this.SECOND_PART, height: this.height, topMargin: 13, text: this.scale.toString(), - alpha: 0.0, + backgroundAlpha: 0.0, visible: editToolsOn, color: activeUIColor }); @@ -125,7 +125,7 @@ function ScaleSelector() { width: this.SECOND_PART, height: this.height, leftMargin: 28, text: this.power.toString(), - alpha: 0.0, + backgroundAlpha: 0.0, visible: false, color: activeUIColor }); diff --git a/examples/libraries/ExportMenu.js b/examples/libraries/ExportMenu.js index 0a47f2bd69..247391808f 100644 --- a/examples/libraries/ExportMenu.js +++ b/examples/libraries/ExportMenu.js @@ -81,7 +81,7 @@ ExportMenu = function (opts) { y: pos.y + margin, width: scaleViewWidth, height: height, - alpha: 0.0, + backgroundAlpha: 0.0, color: { red: 255, green: 255, blue: 255 }, text: "1" }); diff --git a/examples/libraries/ToolTip.js b/examples/libraries/ToolTip.js index 785a0e4d2f..590eba36d0 100644 --- a/examples/libraries/ToolTip.js +++ b/examples/libraries/ToolTip.js @@ -26,6 +26,7 @@ function Tooltip() { text: "", color: { red: 128, green: 128, blue: 128 }, alpha: 0.2, + backgroundAlpha: 0.2, visible: false }); this.show = function (doShow) { diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index 3cd2b8a97e..803a58f48e 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -390,6 +390,7 @@ var ZoomTool = function(opts) { leftMargin: 4, text: "+", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); var decreaseButton = Overlays.addOverlay("text", { @@ -403,6 +404,7 @@ var ZoomTool = function(opts) { leftMargin: 4, text: "-", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); var zoomBar = Overlays.addOverlay("text", { @@ -416,6 +418,7 @@ var ZoomTool = function(opts) { leftMargin: 4, text: "", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); var zoomHandle = Overlays.addOverlay("text", { @@ -428,6 +431,7 @@ var ZoomTool = function(opts) { leftMargin: 4, text: "", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); @@ -501,6 +505,7 @@ var ArrowTool = function(opts) { leftMargin: 4, text: "^", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); var leftButton = Overlays.addOverlay("text", { @@ -514,6 +519,7 @@ var ArrowTool = function(opts) { leftMargin: 4, text: "<", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); var rightButton = Overlays.addOverlay("text", { @@ -540,6 +546,7 @@ var ArrowTool = function(opts) { leftMargin: 4, text: "v", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); var centerButton = Overlays.addOverlay("text", { @@ -553,6 +560,7 @@ var ArrowTool = function(opts) { leftMargin: 4, text: "", alpha: 1.0, + backgroundAlpha: 1.0, visible: true, }); diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index a96eec9bbb..382b8de215 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -368,6 +368,7 @@ SelectionDisplay = (function () { color: { red: 0, green: 0, blue: 0}, backgroundColor: { red: 255, green: 255, blue: 255 }, alpha: 0.7, + backgroundAlpha: 0.7, visible: false, isFacingAvatar: true, drawInFront: true, diff --git a/examples/libraries/progressDialog.js b/examples/libraries/progressDialog.js index dff0904372..7d3b1f88e2 100644 --- a/examples/libraries/progressDialog.js +++ b/examples/libraries/progressDialog.js @@ -40,6 +40,7 @@ progressDialog = (function () { textColor: textColor, backgroundColor: textBackground, alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); @@ -50,6 +51,7 @@ progressDialog = (function () { textColor: textColor, backgroundColor: textBackground, alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index dc17f02869..5802625d7b 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -138,6 +138,7 @@ ToolBar = function(x, y, direction) { width: this.width, height: this.height, alpha: 1.0, + backgroundAlpha: 1.0, visible: false }); this.spacing = []; @@ -243,7 +244,10 @@ ToolBar = function(x, y, direction) { this.tools[tool].setAlpha(alpha); } if (this.back != null) { - Overlays.editOverlay(this.back, { alpha: alpha}); + Overlays.editOverlay(this.back, { + alpha: alpha, + backgroundAlpha: alpha + }); } } else { this.tools[tool].setAlpha(alpha); @@ -263,7 +267,7 @@ ToolBar = function(x, y, direction) { ((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING, visible: true, backgroundColor: color, - alpha: alpha + backgroundAlpha: alpha }); } } diff --git a/examples/lobby.js b/examples/lobby.js index 9e454eccc9..dd011d08a4 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -92,7 +92,7 @@ function drawLobby() { }; var orbShellProps = { - url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Lobby_v8/forStephen1/LobbyShell1.fbx", + url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Lobby_v8/forStephen1/LobbyShell1.4_LightTag.fbx", position: orbPosition, rotation: towardsMe, dimensions: orbDimensions, @@ -113,6 +113,7 @@ function drawLobby() { text: "", lineHeight: lineHeight, alpha: 0.9, + backgroundAlpha: 0.9, ignoreRayIntersection: true, visible: false, isFacingAvatar: true diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 606ff5955a..90208ba24e 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -140,24 +140,22 @@ var toolBar = (function () { menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2; loadURLMenuItem = Overlays.addOverlay("text", { - x: newModelButton.x - menuItemWidth, - y: newModelButton.y + menuItemOffset, height: menuItemHeight, backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model URL", alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); loadFileMenuItem = Overlays.addOverlay("text", { - x: newModelButton.x - menuItemWidth, - y: newModelButton.y + menuItemOffset + menuItemHeight, height: menuItemHeight, backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model File", alpha: 0.9, + backgroundAlpha: 0.9, visible: false }); diff --git a/examples/notifications.js b/examples/notifications.js index 16e9dd70b2..2c2c4a5c0b 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -115,6 +115,7 @@ function createNotification(text) { color: textColor, backgroundColor: backColor, alpha: backgroundAlpha, + backgroundAlpha: backgroundAlpha, topMargin: topMargin, leftMargin: leftMargin, font: {size: fontSize}, @@ -160,7 +161,7 @@ function fadeIn(noticeIn, buttonIn) { pauseTimer = Script.setInterval(function() { q++; qFade = q / 10.0; - Overlays.editOverlay(noticeIn, {alpha: qFade}); + Overlays.editOverlay(noticeIn, {alpha: qFade, backgroundAlpha: qFade}); Overlays.editOverlay(buttonIn, {alpha: qFade}); if (q >= 9.0) { Script.clearInterval(pauseTimer); @@ -344,7 +345,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) { pauseTimer = Script.setInterval(function() { r--; rFade = r / 10.0; - Overlays.editOverlay(noticeOut, {alpha: rFade}); + Overlays.editOverlay(noticeOut, {alpha: rFade, backgroundAlpha: rFade}); Overlays.editOverlay(buttonOut, {alpha: rFade}); if (r < 0) { dismiss(noticeOut, buttonOut, arraysOut); diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index 0d47f7ca64..4aa5b1cd75 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -69,7 +69,8 @@ var text = Overlays.addOverlay("text", { topMargin: 4, leftMargin: 4, text: "Here is some text.\nAnd a second line.", - alpha: 0.7 + alpha: 0.7, + backgroundAlpha: 0.5 }); // This will create an image overlay, which starts out as invisible @@ -170,6 +171,7 @@ var clipboardPreview = Overlays.addOverlay("clipboard", { // Demonstrate retrieving overlay properties print("Text overlay text property value =\n" + Overlays.getProperty(text, "text")); print("Text overlay alpha =\n" + Overlays.getProperty(text, "alpha")); +print("Text overlay background alpha =\n" + Overlays.getProperty(text, "backgroundAlpha")); print("Text overlay visible =\n" + Overlays.getProperty(text, "visible")); print("Text overlay font size =\n" + Overlays.getProperty(text, "font").size); print("Text overlay anchor =\n" + Overlays.getProperty(text, "anchor")); diff --git a/examples/testModelOverlaySubMeshes.js b/examples/testModelOverlaySubMeshes.js index 5195b76e60..20ec10ef7c 100644 --- a/examples/testModelOverlaySubMeshes.js +++ b/examples/testModelOverlaySubMeshes.js @@ -57,6 +57,7 @@ var statusText = Overlays.addOverlay("text", { height: 20, backgroundColor: { red: 0, green: 0, blue: 0}, alpha: 1.0, + backgroundAlpha: 1.0, color: { red: 255, green: 255, blue: 255}, topMargin: 4, leftMargin: 4, diff --git a/examples/textInputOverlayExample.js b/examples/textInputOverlayExample.js index e837e81eb5..8a5fad81cd 100644 --- a/examples/textInputOverlayExample.js +++ b/examples/textInputOverlayExample.js @@ -44,6 +44,7 @@ var inputWindow = Overlays.addOverlay("text", { color: textColor, backgroundColor: backColor, alpha: backgroundAlpha, + backgroundAlpha: backgroundAlpha, topMargin: topMargin, leftMargin: leftMargin, font: {size: fontSize}, diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index 51f46ff30c..8509fa1f67 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -150,7 +150,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } glm::quat rotation = getRotation(); - if (needsSimulation() && _model->isActive()) { + if (needsToCallUpdate() && _model->isActive()) { _model->setScaleToFit(true, dimensions); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(rotation); @@ -253,8 +253,8 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { return result; } -bool RenderableModelEntityItem::needsSimulation() const { - return _needsInitialSimulation || getSimulationState() == EntityItem::Moving; +bool RenderableModelEntityItem::needsToCallUpdate() const { + return _needsInitialSimulation || ModelEntityItem::needsToCallUpdate(); } EntityItemProperties RenderableModelEntityItem::getProperties() const { diff --git a/interface/src/entities/RenderableModelEntityItem.h b/interface/src/entities/RenderableModelEntityItem.h index e187b944d8..2401d0ea64 100644 --- a/interface/src/entities/RenderableModelEntityItem.h +++ b/interface/src/entities/RenderableModelEntityItem.h @@ -57,9 +57,11 @@ public: void** intersectedObject, bool precisionPicking) const; Model* getModel(EntityTreeRenderer* renderer); + + bool needsToCallUpdate() const; + private: void remapTextures(); - bool needsSimulation() const; Model* _model; bool _needsInitialSimulation; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index dbb423ad99..2e80fae8a0 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -16,12 +16,14 @@ #include "ui/TextRenderer.h" const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; +const float DEFAULT_BACKGROUND_ALPHA = 0.7f; const float DEFAULT_MARGIN = 0.1f; const int FIXED_FONT_POINT_SIZE = 40; const float LINE_SCALE_RATIO = 1.2f; Text3DOverlay::Text3DOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), _lineHeight(0.1f), _leftMargin(DEFAULT_MARGIN), _topMargin(DEFAULT_MARGIN), @@ -35,6 +37,7 @@ Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) : Planar3DOverlay(text3DOverlay), _text(text3DOverlay->_text), _backgroundColor(text3DOverlay->_backgroundColor), + _backgroundAlpha(text3DOverlay->_backgroundAlpha), _lineHeight(text3DOverlay->_lineHeight), _leftMargin(text3DOverlay->_leftMargin), _topMargin(text3DOverlay->_topMargin), @@ -88,8 +91,8 @@ void Text3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; xColor backgroundColor = getBackgroundColor(); - float alpha = getAlpha(); - glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, alpha); + glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, + getBackgroundAlpha()); glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; @@ -124,6 +127,7 @@ void Text3DOverlay::render(RenderArgs* args) { enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR); + float alpha = getAlpha(); QStringList lines = _text.split("\n"); int lineOffset = maxHeight; foreach(QString thisLine, lines) { @@ -166,6 +170,10 @@ void Text3DOverlay::setProperties(const QScriptValue& properties) { } } + if (properties.property("backgroundAlpha").isValid()) { + _backgroundAlpha = properties.property("backgroundAlpha").toVariant().toFloat(); + } + if (properties.property("lineHeight").isValid()) { setLineHeight(properties.property("lineHeight").toVariant().toFloat()); } @@ -200,6 +208,9 @@ QScriptValue Text3DOverlay::getProperty(const QString& property) { if (property == "backgroundColor") { return xColorToScriptValue(_scriptEngine, _backgroundColor); } + if (property == "backgroundAlpha") { + return _backgroundAlpha; + } if (property == "lineHeight") { return _lineHeight; } diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index c624830ac8..d74131391a 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -37,6 +37,7 @@ public: float getBottomMargin() const { return _bottomMargin; } bool getIsFacingAvatar() const { return _isFacingAvatar; } xColor getBackgroundColor(); + float getBackgroundAlpha() const { return _backgroundAlpha; } // setters void setText(const QString& text) { _text = text; } @@ -59,6 +60,7 @@ private: QString _text; xColor _backgroundColor; + float _backgroundAlpha; float _lineHeight; float _leftMargin; float _topMargin; diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 1d72cf7f05..272c9bc916 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -19,6 +19,7 @@ TextOverlay::TextOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _backgroundAlpha(DEFAULT_BACKGROUND_ALPHA), _leftMargin(DEFAULT_MARGIN), _topMargin(DEFAULT_MARGIN), _fontSize(DEFAULT_FONTSIZE) @@ -29,6 +30,7 @@ TextOverlay::TextOverlay(const TextOverlay* textOverlay) : Overlay2D(textOverlay), _text(textOverlay->_text), _backgroundColor(textOverlay->_backgroundColor), + _backgroundAlpha(textOverlay->_backgroundAlpha), _leftMargin(textOverlay->_leftMargin), _topMargin(textOverlay->_topMargin), _fontSize(textOverlay->_fontSize) @@ -63,11 +65,10 @@ void TextOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - const float MAX_COLOR = 255.0f; xColor backgroundColor = getBackgroundColor(); - float alpha = getAlpha(); - glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, alpha); + glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, + getBackgroundAlpha()); glBegin(GL_QUADS); glVertex2f(_bounds.left(), _bounds.top()); @@ -85,6 +86,7 @@ void TextOverlay::render(RenderArgs* args) { int y = _bounds.top() + _topMargin + topAdjust; glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR); + float alpha = getAlpha(); QStringList lines = _text.split("\n"); int lineOffset = 0; foreach(QString thisLine, lines) { @@ -125,6 +127,10 @@ void TextOverlay::setProperties(const QScriptValue& properties) { } } + if (properties.property("backgroundAlpha").isValid()) { + _backgroundAlpha = properties.property("backgroundAlpha").toVariant().toFloat(); + } + if (properties.property("leftMargin").isValid()) { setLeftMargin(properties.property("leftMargin").toVariant().toInt()); } @@ -150,6 +156,9 @@ QScriptValue TextOverlay::getProperty(const QString& property) { if (property == "backgroundColor") { return xColorToScriptValue(_scriptEngine, _backgroundColor); } + if (property == "backgroundAlpha") { + return _backgroundAlpha; + } if (property == "leftMargin") { return _leftMargin; } diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 703e15be10..754faea2bc 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -28,6 +28,7 @@ #include "Overlay2D.h" const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; +const float DEFAULT_BACKGROUND_ALPHA = 0.7f; const int DEFAULT_MARGIN = 10; const int DEFAULT_FONTSIZE = 11; const int DEFAULT_FONT_WEIGHT = 50; @@ -46,6 +47,7 @@ public: int getLeftMargin() const { return _leftMargin; } int getTopMargin() const { return _topMargin; } xColor getBackgroundColor(); + float getBackgroundAlpha() const { return _backgroundAlpha; } // setters void setText(const QString& text) { _text = text; } @@ -62,6 +64,7 @@ public: private: QString _text; xColor _backgroundColor; + float _backgroundAlpha; int _leftMargin; int _topMargin; int _fontSize; diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 9c82cb4744..f9a9ec733d 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -209,7 +209,9 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { propertiesA.setPosition(newPositionA * (float)TREE_SCALE); propertiesA.setLastEdited(now); - _entityTree->updateEntity(idA, propertiesA); + // NOTE: EntityTree::updateEntity() will cause the entity to get sorted correctly in the EntitySimulation, + // thereby waking up static non-moving entities. + _entityTree->updateEntity(entityA, propertiesA); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); } @@ -226,7 +228,9 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { propertiesB.setPosition(newPositionB * (float)TREE_SCALE); propertiesB.setLastEdited(now); - _entityTree->updateEntity(idB, propertiesB); + // NOTE: EntityTree::updateEntity() will cause the entity to get sorted correctly in the EntitySimulation, + // thereby waking up static non-moving entities. + _entityTree->updateEntity(entityB, propertiesB); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); } } @@ -332,6 +336,6 @@ void EntityCollisionSystem::applyHardCollision(EntityItem* entity, const Collisi properties.setVelocity(velocity * (float)TREE_SCALE); properties.setLastEdited(usecTimestampNow()); - _entityTree->updateEntity(entityItemID, properties); + _entityTree->updateEntity(entity, properties); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 98dda6f33f..b271439662 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -29,7 +29,7 @@ const float EntityItem::DEFAULT_LOCAL_RENDER_ALPHA = 1.0f; const float EntityItem::DEFAULT_MASS = 1.0f; const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL; const QString EntityItem::DEFAULT_USER_DATA = QString(""); -const float EntityItem::DEFAULT_DAMPING = 0.5f; +const float EntityItem::DEFAULT_DAMPING = 2.0f; const glm::vec3 EntityItem::NO_VELOCITY = glm::vec3(0, 0, 0); const float EntityItem::EPSILON_VELOCITY_LENGTH = (1.0f / 1000.0f) / (float)TREE_SCALE; // really small: 1mm/second const glm::vec3 EntityItem::DEFAULT_VELOCITY = EntityItem::NO_VELOCITY; @@ -42,7 +42,7 @@ const glm::vec3 EntityItem::DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f); const glm::vec3 EntityItem::DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); // center const glm::vec3 EntityItem::NO_ANGULAR_VELOCITY = glm::vec3(0.0f, 0.0f, 0.0f); const glm::vec3 EntityItem::DEFAULT_ANGULAR_VELOCITY = NO_ANGULAR_VELOCITY; -const float EntityItem::DEFAULT_ANGULAR_DAMPING = 0.5f; +const float EntityItem::DEFAULT_ANGULAR_DAMPING = 2.0f; const bool EntityItem::DEFAULT_VISIBLE = true; const bool EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS = false; const bool EntityItem::DEFAULT_COLLISIONS_WILL_MOVE = false; @@ -58,6 +58,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _lastEditedFromRemote = 0; _lastEditedFromRemoteInRemoteTime = 0; + _lastSimulated = 0; _lastUpdated = 0; _created = 0; // TODO: when do we actually want to make this "now" _changedOnServer = 0; @@ -88,12 +89,12 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) { _lastEdited = 0; _lastEditedFromRemote = 0; _lastEditedFromRemoteInRemoteTime = 0; + _lastSimulated = 0; _lastUpdated = 0; _created = 0; - _updateFlags = 0; + _dirtyFlags = 0; _changedOnServer = 0; initFromEntityItemID(entityItemID); - _simulationState = EntityItem::Static; } EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) { @@ -101,13 +102,13 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert _lastEdited = 0; _lastEditedFromRemote = 0; _lastEditedFromRemoteInRemoteTime = 0; + _lastSimulated = 0; _lastUpdated = 0; _created = properties.getCreated(); - _updateFlags = 0; + _dirtyFlags = 0; _changedOnServer = 0; initFromEntityItemID(entityItemID); setProperties(properties, true); // force copy - _simulationState = EntityItem::Static; } EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const { @@ -154,7 +155,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet ByteCountCoded typeCoder = getType(); QByteArray encodedType = typeCoder; - quint64 updateDelta = getLastUpdated() <= getLastEdited() ? 0 : getLastUpdated() - getLastEdited(); + quint64 updateDelta = getLastSimulated() <= getLastEdited() ? 0 : getLastSimulated() - getLastEdited(); ByteCountCoded updateDeltaCoder = updateDelta; QByteArray encodedUpdateDelta = updateDeltaCoder; EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); @@ -450,9 +451,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef ByteCountCoded updateDeltaCoder = encodedUpdateDelta; quint64 updateDelta = updateDeltaCoder; if (overwriteLocalData) { - _lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that for _lastEdited + _lastSimulated = _lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that for _lastEdited if (wantDebug) { - qDebug() << "_lastUpdated=" << _lastUpdated; + qDebug() << "_lastUpdated =" << _lastUpdated; qDebug() << "_lastEdited=" << _lastEdited; qDebug() << "lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted; } @@ -565,20 +566,20 @@ bool EntityItem::isRestingOnSurface() const { && _gravity.y < 0.0f; } -void EntityItem::update(const quint64& updateTime) { +void EntityItem::simulate(const quint64& now) { bool wantDebug = false; - if (_lastUpdated == 0) { - _lastUpdated = updateTime; + if (_lastSimulated == 0) { + _lastSimulated = now; } - float timeElapsed = (float)(updateTime - _lastUpdated) / (float)(USECS_PER_SECOND); + float timeElapsed = (float)(now - _lastSimulated) / (float)(USECS_PER_SECOND); if (wantDebug) { qDebug() << "********** EntityItem::update()"; qDebug() << " entity ID=" << getEntityItemID(); - qDebug() << " updateTime=" << updateTime; - qDebug() << " _lastUpdated=" << _lastUpdated; + qDebug() << " now=" << now; + qDebug() << " _lastSimulated=" << _lastSimulated; qDebug() << " timeElapsed=" << timeElapsed; qDebug() << " hasVelocity=" << hasVelocity(); qDebug() << " hasGravity=" << hasGravity(); @@ -611,10 +612,10 @@ void EntityItem::update(const quint64& updateTime) { } } - _lastUpdated = updateTime; + _lastSimulated = now; if (wantDebug) { - qDebug() << " ********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; + qDebug() << " ********** EntityItem::update() .... SETTING _lastSimulated=" << _lastSimulated; } if (hasAngularVelocity()) { @@ -631,13 +632,13 @@ void EntityItem::update(const quint64& updateTime) { setRotation(rotation); // handle damping for angular velocity - if (getAngularDamping() > 0.0f) { - glm::vec3 dampingResistance = getAngularVelocity() * getAngularDamping(); - glm::vec3 newAngularVelocity = getAngularVelocity() - (dampingResistance * timeElapsed); + float dampingTimescale = getAngularDamping(); + if (dampingTimescale > 0.0f) { + float dampingFactor = glm::clamp(timeElapsed / dampingTimescale, 0.0f, 1.0f); + glm::vec3 newAngularVelocity = (1.0f - dampingFactor) * getAngularVelocity(); setAngularVelocity(newAngularVelocity); if (wantDebug) { - qDebug() << " getDamping():" << getDamping(); - qDebug() << " dampingResistance:" << dampingResistance; + qDebug() << " dampingTimescale :" << dampingTimescale; qDebug() << " newAngularVelocity:" << newAngularVelocity; } } @@ -688,13 +689,15 @@ void EntityItem::update(const quint64& updateTime) { } // handle damping for velocity - glm::vec3 dampingResistance = velocity * getDamping(); - if (wantDebug) { - qDebug() << " getDamping():" << getDamping(); - qDebug() << " dampingResistance:" << dampingResistance; - qDebug() << " dampingResistance * timeElapsed:" << dampingResistance * timeElapsed; + float dampingTimescale = getDamping(); + if (dampingTimescale > 0.0f) { + float dampingFactor = glm::clamp(timeElapsed / dampingTimescale, 0.0f, 1.0f); + velocity *= (1.0f - dampingFactor); + if (wantDebug) { + qDebug() << " dampingTimescale:" << dampingTimescale; + qDebug() << " newVelocity:" << velocity; + } } - velocity -= dampingResistance * timeElapsed; if (wantDebug) { qDebug() << " velocity AFTER dampingResistance:" << velocity; @@ -707,6 +710,7 @@ void EntityItem::update(const quint64& updateTime) { velocity = NO_VELOCITY; } + // NOTE: the simulation should NOT set any DirtyFlags on this entity setPosition(position); // this will automatically recalculate our collision shape setVelocity(velocity); @@ -719,20 +723,18 @@ void EntityItem::update(const quint64& updateTime) { } } -EntityItem::SimulationState EntityItem::computeSimulationState() const { - if (hasVelocity() || (hasGravity() && !isRestingOnSurface()) || hasAngularVelocity()) { - return EntityItem::Moving; - } - if (isMortal()) { - return EntityItem::Mortal; - } - return EntityItem::Static; +bool EntityItem::isMoving() const { + return hasVelocity() || (hasGravity() && !isRestingOnSurface()) || hasAngularVelocity(); } bool EntityItem::lifetimeHasExpired() const { return isMortal() && (getAge() > getLifetime()); } +quint64 EntityItem::getExpiry() const { + return _created + (quint64)(_lifetime * (float)USECS_PER_SECOND); +} + EntityItemProperties EntityItem::getProperties() const { EntityItemProperties properties; properties._id = getID(); @@ -778,23 +780,23 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc } } - SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters); // this will call recalculate collision shape if needed - SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete - SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, setVelocityInMeters); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, setGravityInMeters); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, updatePositionInMeters); // this will call recalculate collision shape if needed + SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, updateDimensionsInMeters); // NOTE: radius is obsolete + SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, updateMass); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocityInMeters); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters); SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, setLifetime); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, updateScript); SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, setAngularVelocity); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, updateAngularVelocity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping); SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, setIgnoreForCollisions); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, setCollisionsWillMove); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, updateIgnoreForCollisions); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove); SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); @@ -948,7 +950,7 @@ void EntityItem::updatePosition(const glm::vec3& value) { if (_position != value) { _position = value; recalculateCollisionShape(); - _updateFlags |= EntityItem::UPDATE_POSITION; + _dirtyFlags |= EntityItem::DIRTY_POSITION; } } @@ -957,7 +959,7 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) { if (_position != position) { _position = position; recalculateCollisionShape(); - _updateFlags |= EntityItem::UPDATE_POSITION; + _dirtyFlags |= EntityItem::DIRTY_POSITION; } } @@ -965,7 +967,7 @@ void EntityItem::updateDimensions(const glm::vec3& value) { if (_dimensions != value) { _dimensions = value; recalculateCollisionShape(); - _updateFlags |= EntityItem::UPDATE_SHAPE; + _dirtyFlags |= EntityItem::DIRTY_SHAPE; } } @@ -974,7 +976,7 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) { if (_dimensions != dimensions) { _dimensions = dimensions; recalculateCollisionShape(); - _updateFlags |= EntityItem::UPDATE_SHAPE; + _dirtyFlags |= EntityItem::DIRTY_SHAPE; } } @@ -982,21 +984,21 @@ void EntityItem::updateRotation(const glm::quat& rotation) { if (_rotation != rotation) { _rotation = rotation; recalculateCollisionShape(); - _updateFlags |= EntityItem::UPDATE_POSITION; + _dirtyFlags |= EntityItem::DIRTY_POSITION; } } void EntityItem::updateMass(float value) { if (_mass != value) { _mass = value; - _updateFlags |= EntityItem::UPDATE_MASS; + _dirtyFlags |= EntityItem::DIRTY_MASS; } } void EntityItem::updateVelocity(const glm::vec3& value) { if (_velocity != value) { _velocity = value; - _updateFlags |= EntityItem::UPDATE_VELOCITY; + _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } @@ -1004,14 +1006,14 @@ void EntityItem::updateVelocityInMeters(const glm::vec3& value) { glm::vec3 velocity = value / (float) TREE_SCALE; if (_velocity != velocity) { _velocity = velocity; - _updateFlags |= EntityItem::UPDATE_VELOCITY; + _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } void EntityItem::updateGravity(const glm::vec3& value) { if (_gravity != value) { _gravity = value; - _updateFlags |= EntityItem::UPDATE_VELOCITY; + _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } @@ -1019,36 +1021,42 @@ void EntityItem::updateGravityInMeters(const glm::vec3& value) { glm::vec3 gravity = value / (float) TREE_SCALE; if (_gravity != gravity) { _gravity = gravity; - _updateFlags |= EntityItem::UPDATE_VELOCITY; + _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } void EntityItem::updateAngularVelocity(const glm::vec3& value) { if (_angularVelocity != value) { _angularVelocity = value; - _updateFlags |= EntityItem::UPDATE_VELOCITY; + _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } void EntityItem::updateIgnoreForCollisions(bool value) { if (_ignoreForCollisions != value) { _ignoreForCollisions = value; - _updateFlags |= EntityItem::UPDATE_COLLISION_GROUP; + _dirtyFlags |= EntityItem::DIRTY_COLLISION_GROUP; } } void EntityItem::updateCollisionsWillMove(bool value) { if (_collisionsWillMove != value) { _collisionsWillMove = value; - _updateFlags |= EntityItem::UPDATE_MOTION_TYPE; + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } } void EntityItem::updateLifetime(float value) { if (_lifetime != value) { _lifetime = value; - _updateFlags |= EntityItem::UPDATE_LIFETIME; + _dirtyFlags |= EntityItem::DIRTY_LIFETIME; } } +void EntityItem::updateScript(const QString& value) { + if (_script != value) { + _script = value; + _dirtyFlags |= EntityItem::DIRTY_SCRIPT; + } +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 8ab2992659..314b1c23d5 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -41,15 +41,17 @@ class EntityTreeElementExtraEncodeData; class EntityItem { public: - enum EntityUpdateFlags { - UPDATE_POSITION = 0x0001, - UPDATE_VELOCITY = 0x0002, - UPDATE_MASS = 0x0004, - UPDATE_COLLISION_GROUP = 0x0008, - UPDATE_MOTION_TYPE = 0x0010, - UPDATE_SHAPE = 0x0020, - UPDATE_LIFETIME = 0x0040 - //UPDATE_APPEARANCE = 0x8000, + enum EntityDirtyFlags { + DIRTY_POSITION = 0x0001, + DIRTY_VELOCITY = 0x0002, + DIRTY_MASS = 0x0004, + DIRTY_COLLISION_GROUP = 0x0008, + DIRTY_MOTION_TYPE = 0x0010, + DIRTY_SHAPE = 0x0020, + DIRTY_LIFETIME = 0x0040, + // add new simulation-relevant flags above + // all other flags below + DIRTY_SCRIPT = 0x8000 }; DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly @@ -77,12 +79,12 @@ public: /// has changed. This will be called with properties change or when new data is loaded from a stream virtual void somethingChangedNotification() { } - quint64 getLastUpdated() const { return _lastUpdated; } /// Last simulated time of this entity universal usecs + quint64 getLastSimulated() const { return _lastSimulated; } /// Last simulated time of this entity universal usecs /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } void setLastEdited(quint64 lastEdited) - { _lastEdited = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } + { _lastEdited = _lastSimulated = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } float getEditedAgo() const /// Elapsed seconds since this entity was last edited { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } @@ -121,17 +123,14 @@ public: unsigned char* bufferOut, int sizeIn, int& sizeOut); static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, size_t length, int clockSkew); - virtual void update(const quint64& now); + + // perform update + virtual void update(const quint64& now) { _lastUpdated = now; } - typedef enum SimulationState_t { - Static, - Mortal, - Moving - } SimulationState; + // perform linear extrapolation for SimpleEntitySimulation + void simulate(const quint64& now); - // computes the SimulationState that the entity SHOULD be in. - // Use getSimulationState() to find the state under which it is currently categorized. - virtual SimulationState computeSimulationState() const; + bool needsToCallUpdate() const { return false; } virtual void debugDump() const; @@ -221,11 +220,14 @@ public: /// age of this entity in seconds float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } bool lifetimeHasExpired() const; + quint64 getExpiry() const; // position, size, and bounds related helpers float getSize() const; /// get maximum dimension in domain scale units (0.0 - 1.0) AACube getMaximumAACube() const; AACube getMinimumAACube() const; + AACube getOldMaximumAACube() const { return _oldMaximumAACube; } + void setOldMaximumAACube(const AACube& cube) { _oldMaximumAACube = cube; } AABox getAABox() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0) static const QString DEFAULT_SCRIPT; @@ -278,7 +280,7 @@ public: virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; } virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } - // updateFoo() methods to be used when changes need to be accumulated in the _updateFlags + // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags void updatePosition(const glm::vec3& value); void updatePositionInMeters(const glm::vec3& value); void updateDimensions(const glm::vec3& value); @@ -293,13 +295,13 @@ public: void updateIgnoreForCollisions(bool value); void updateCollisionsWillMove(bool value); void updateLifetime(float value); + void updateScript(const QString& value); - uint32_t getUpdateFlags() const { return _updateFlags; } - void clearUpdateFlags() { _updateFlags = 0; } - - SimulationState getSimulationState() const { return _simulationState; } + uint32_t getDirtyFlags() const { return _dirtyFlags; } + void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; } - void setSimulationState(SimulationState state) { _simulationState = state; } + bool isMoving() const; + protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init @@ -309,7 +311,8 @@ protected: QUuid _id; uint32_t _creatorTokenID; bool _newlyCreated; - quint64 _lastUpdated; + quint64 _lastSimulated; // last time this entity called simulate() + quint64 _lastUpdated; // last time this entity called update() quint64 _lastEdited; // this is the last official local or remote edit time quint64 _lastEditedFromRemote; // this is the last time we received and edit from the server quint64 _lastEditedFromRemoteInRemoteTime; // time in server time space the last time we received and edit from the server @@ -324,12 +327,12 @@ protected: float _mass; glm::vec3 _velocity; glm::vec3 _gravity; - float _damping; + float _damping; // timescale float _lifetime; QString _script; glm::vec3 _registrationPoint; glm::vec3 _angularVelocity; - float _angularDamping; + float _angularDamping; // timescale bool _visible; bool _ignoreForCollisions; bool _collisionsWillMove; @@ -343,11 +346,10 @@ protected: void setRadius(float value); AACubeShape _collisionShape; - SimulationState _simulationState; // only set by EntityTree + AACube _oldMaximumAACube; // remember this so we know where the entity used to live in the tree - // UpdateFlags are set whenever a property changes that requires the change to be communicated to other - // data structures. It is the responsibility of the EntityTree to relay changes entity and clear flags. - uint32_t _updateFlags; + // DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about. + uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation }; diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 8058c2f24e..223df588d5 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -9,12 +9,178 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "EntitySimulation.h" +#include "MovingEntitiesOperator.h" void EntitySimulation::setEntityTree(EntityTree* tree) { if (_entityTree && _entityTree != tree) { - clearEntities(); + _mortalEntities.clear(); + _nextExpiry = quint64(-1); + _updateableEntities.clear(); + _entitiesToBeSorted.clear(); } _entityTree = tree; } +void EntitySimulation::updateEntities(QSet& entitiesToDelete) { + quint64 now = usecTimestampNow(); + + // these methods may accumulate entries in _entitiesToBeDeleted + expireMortalEntities(now); + callUpdateOnEntitiesThatNeedIt(now); + updateEntitiesInternal(now); + sortEntitiesThatMoved(); + + // at this point we harvest _entitiesToBeDeleted + entitiesToDelete.unite(_entitiesToDelete); + _entitiesToDelete.clear(); +} + +void EntitySimulation::expireMortalEntities(const quint64& now) { + if (now > _nextExpiry) { + // only search for expired entities if we expect to find one + _nextExpiry = quint64(-1); + QSet::iterator itemItr = _mortalEntities.begin(); + while (itemItr != _mortalEntities.end()) { + EntityItem* entity = *itemItr; + quint64 expiry = entity->getExpiry(); + if (expiry < now) { + _entitiesToDelete.insert(entity); + itemItr = _mortalEntities.erase(itemItr); + _updateableEntities.remove(entity); + _entitiesToBeSorted.remove(entity); + removeEntityInternal(entity); + } else { + if (expiry < _nextExpiry) { + // remeber the smallest _nextExpiry so we know when to start the next search + _nextExpiry = expiry; + } + ++itemItr; + } + } + } +} + +void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) { + PerformanceTimer perfTimer("updatingEntities"); + QSet::iterator itemItr = _updateableEntities.begin(); + while (itemItr != _updateableEntities.end()) { + EntityItem* entity = *itemItr; + // TODO: catch transition from needing update to not as a "change" + // so we don't have to scan for it here. + if (!entity->needsToCallUpdate()) { + itemItr = _updateableEntities.erase(itemItr); + } else { + entity->update(now); + ++itemItr; + } + } +} + +void EntitySimulation::sortEntitiesThatMoved() { + // NOTE: this is only for entities that have been moved by THIS EntitySimulation. + // External changes to entity position/shape are expected to be sorted outside of the EntitySimulation. + PerformanceTimer perfTimer("sortingEntities"); + MovingEntitiesOperator moveOperator(_entityTree); + AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f); + QSet::iterator itemItr = _entitiesToBeSorted.begin(); + while (itemItr != _entitiesToBeSorted.end()) { + EntityItem* entity = *itemItr; + // check to see if this movement has sent the entity outside of the domain. + AACube newCube = entity->getMaximumAACube(); + if (!domainBounds.touches(newCube)) { + qDebug() << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; + _entitiesToDelete.insert(entity); + _mortalEntities.remove(entity); + _updateableEntities.remove(entity); + removeEntityInternal(entity); + } else { + moveOperator.addEntityToMoveList(entity, newCube); + } + ++itemItr; + } + _entitiesToBeSorted.clear(); + + if (moveOperator.hasMovingEntities()) { + PerformanceTimer perfTimer("recurseTreeWithOperator"); + _entityTree->recurseTreeWithOperator(&moveOperator); + moveOperator.finish(); + } +} + +void EntitySimulation::addEntity(EntityItem* entity) { + assert(entity); + if (entity->isMortal()) { + _mortalEntities.insert(entity); + quint64 expiry = entity->getExpiry(); + if (expiry < _nextExpiry) { + _nextExpiry = expiry; + } + } + if (entity->needsToCallUpdate()) { + _updateableEntities.insert(entity); + } + addEntityInternal(entity); +} + +void EntitySimulation::removeEntity(EntityItem* entity) { + assert(entity); + _updateableEntities.remove(entity); + _mortalEntities.remove(entity); + _entitiesToBeSorted.remove(entity); + _entitiesToDelete.remove(entity); + removeEntityInternal(entity); +} + +void EntitySimulation::entityChanged(EntityItem* entity) { + assert(entity); + + // Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes + // it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence + // we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag. + bool wasRemoved = false; + uint32_t dirtyFlags = entity->getDirtyFlags(); + if (dirtyFlags & EntityItem::DIRTY_POSITION) { + AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f); + AACube newCube = entity->getMaximumAACube(); + if (!domainBounds.touches(newCube)) { + qDebug() << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; + _entitiesToDelete.insert(entity); + _mortalEntities.remove(entity); + _updateableEntities.remove(entity); + removeEntityInternal(entity); + wasRemoved = true; + } + } + if (!wasRemoved) { + if (dirtyFlags & EntityItem::DIRTY_LIFETIME) { + if (entity->isMortal()) { + _mortalEntities.insert(entity); + quint64 expiry = entity->getExpiry(); + if (expiry < _nextExpiry) { + _nextExpiry = expiry; + } + } else { + _mortalEntities.remove(entity); + } + entity->clearDirtyFlags(EntityItem::DIRTY_LIFETIME); + } + if (entity->needsToCallUpdate()) { + _updateableEntities.insert(entity); + } else { + _updateableEntities.remove(entity); + } + entityChangedInternal(entity); + } + entity->clearDirtyFlags(); +} + +void EntitySimulation::clearEntities() { + _mortalEntities.clear(); + _nextExpiry = quint64(-1); + _updateableEntities.clear(); + _entitiesToBeSorted.clear(); + clearEntitiesInternal(); +} diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index 770d6ebdb0..506e2ed9d0 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -14,36 +14,61 @@ #include +#include + #include "EntityTree.h" class EntitySimulation { public: EntitySimulation() : _entityTree(NULL) { } - virtual ~EntitySimulation() {} + virtual ~EntitySimulation() { setEntityTree(NULL); } /// \param tree pointer to EntityTree which is stored internally - virtual void setEntityTree(EntityTree* tree); + void setEntityTree(EntityTree* tree); /// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted. - virtual void update(QSet& entitiesToDelete) = 0; + void updateEntities(QSet& entitiesToDelete); /// \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; + void addEntity(EntityItem* entity); /// \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; + void removeEntity(EntityItem* entity); /// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation - virtual void entityChanged(EntityItem* entity) = 0; + /// call this whenever an entity was changed from some EXTERNAL event (NOT by the EntitySimulation itself) + void entityChanged(EntityItem* entity); - virtual void clearEntities() = 0; + void clearEntities(); EntityTree* getEntityTree() { return _entityTree; } protected: + + // These pure virtual methods are protected because they are not to be called will-nilly. The base class + // calls them in the right places. + virtual void updateEntitiesInternal(const quint64& now) = 0; + virtual void addEntityInternal(EntityItem* entity) = 0; + virtual void removeEntityInternal(EntityItem* entity) = 0; + virtual void entityChangedInternal(EntityItem* entity) = 0; + virtual void clearEntitiesInternal() = 0; + + void expireMortalEntities(const quint64& now); + void callUpdateOnEntitiesThatNeedIt(const quint64& now); + void sortEntitiesThatMoved(); + + // back pointer to EntityTree structure EntityTree* _entityTree; + + // We maintain multiple lists, each for its distinct purpose. + // An entity may be in more than one list. + QSet _mortalEntities; // entities that have an expiry + quint64 _nextExpiry; + QSet _updateableEntities; // entities that need update() called + QSet _entitiesToBeSorted; // entities that were moved by THIS simulation and might need to be resorted in the tree + QSet _entitiesToDelete; }; #endif // hifi_EntitySimulation_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5f9aa4a159..48ea2b7c07 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -83,6 +83,7 @@ EntityItem* EntityTree::getOrCreateEntityItem(const EntityItemID& entityID, cons /// Adds a new entity item to the tree void EntityTree::postAddEntity(EntityItem* entity) { assert(entity); + entity->setOldMaximumAACube(entity->getMaximumAACube()); // check to see if we need to simulate this entity.. if (_simulation) { _simulation->addEntity(entity); @@ -103,51 +104,64 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp qDebug() << "UNEXPECTED!!!! don't call updateEntity() on entity items that don't exist. entityID=" << entityID; return false; } - + + return updateEntityWithElement(existingEntity, properties, containingElement); +} + +bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties) { + EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID()); + if (!containingElement) { + qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID=" + << entity->getEntityItemID(); + return false; + } + return updateEntityWithElement(entity, properties, containingElement); +} + +bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties, + EntityTreeElement* containingElement) { // enforce support for locked entities. If an entity is currently locked, then the only // property we allow you to change is the locked property. - if (existingEntity->getLocked()) { + if (entity->getLocked()) { if (properties.lockedChanged()) { bool wantsLocked = properties.getLocked(); if (!wantsLocked) { EntityItemProperties tempProperties; tempProperties.setLocked(wantsLocked); - UpdateEntityOperator theOperator(this, containingElement, existingEntity, tempProperties); + UpdateEntityOperator theOperator(this, containingElement, entity, tempProperties); recurseTreeWithOperator(&theOperator); _isDirty = true; - if (_simulation && existingEntity->getUpdateFlags() != 0) { - _simulation->entityChanged(existingEntity); - } } } } else { - // check to see if we need to simulate this entity... - QString entityScriptBefore = existingEntity->getScript(); + QString entityScriptBefore = entity->getScript(); - UpdateEntityOperator theOperator(this, containingElement, existingEntity, properties); + UpdateEntityOperator theOperator(this, containingElement, entity, properties); recurseTreeWithOperator(&theOperator); + entity->setOldMaximumAACube(entity->getMaximumAACube()); _isDirty = true; - if (_simulation && existingEntity->getUpdateFlags() != 0) { - _simulation->entityChanged(existingEntity); + if (_simulation && entity->getDirtyFlags() != 0) { + _simulation->entityChanged(entity); } - QString entityScriptAfter = existingEntity->getScript(); + QString entityScriptAfter = entity->getScript(); if (entityScriptBefore != entityScriptAfter) { - emitEntityScriptChanging(entityID); // the entity script has changed + emit entityScriptChanging(entity->getEntityItemID()); // the entity script has changed } } - containingElement = getContainingElement(entityID); + // TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG). + containingElement = getContainingElement(entity->getEntityItemID()); if (!containingElement) { - qDebug() << "UNEXPECTED!!!! after updateEntity() we no longer have a containing element??? entityID=" << entityID; + qDebug() << "UNEXPECTED!!!! after updateEntity() we no longer have a containing element??? entityID=" + << entity->getEntityItemID(); return false; } return true; } - EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* result = NULL; @@ -576,7 +590,7 @@ void EntityTree::update() { if (_simulation) { lockForWrite(); QSet entitiesToDelete; - _simulation->update(entitiesToDelete); + _simulation->updateEntities(entitiesToDelete); if (entitiesToDelete.size() > 0) { // translate into list of ID's QSet idsToDelete; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index eeb0182042..9a0fb43ecb 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -82,7 +82,13 @@ public: void postAddEntity(EntityItem* entityItem); EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties); + + // use this method if you only know the entityID bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties); + + // use this method if you have a pointer to the entity (avoid an extra entity lookup) + bool updateEntity(EntityItem* entity, const EntityItemProperties& properties); + void deleteEntity(const EntityItemID& entityID); void deleteEntities(QSet entityIDs); void removeEntityFromSimulation(EntityItem* entity); @@ -156,6 +162,8 @@ signals: private: + bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties, + EntityTreeElement* containingElement); static bool findNearPointOperation(OctreeElement* element, void* extraData); static bool findInSphereOperation(OctreeElement* element, void* extraData); static bool findInCubeOperation(OctreeElement* element, void* extraData); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 70def167a2..05928990d1 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -761,7 +761,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID); bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - if (entityItem->getUpdateFlags()) { + if (entityItem->getDirtyFlags()) { _myTree->entityChanged(entityItem); } bool bestFitAfter = bestFitEntityBounds(entityItem); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 0de2035dec..565974d19e 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -373,17 +373,11 @@ bool ModelEntityItem::isAnimatingSomething() const { !getAnimationURL().isEmpty(); } -EntityItem::SimulationState ModelEntityItem::computeSimulationState() const { - // if we're animating then we need to have update() periodically called on this entity - // which means we need to categorized as Moving - return isAnimatingSomething() ? EntityItem::Moving : EntityItem::computeSimulationState(); +bool ModelEntityItem::needsToCallUpdate() const { + return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate(); } -void ModelEntityItem::update(const quint64& updateTime) { - EntityItem::update(updateTime); // let our base class handle it's updates... - - quint64 now = updateTime; - +void ModelEntityItem::update(const quint64& now) { // only advance the frame index if we're playing if (getAnimationIsPlaying()) { float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; @@ -392,6 +386,7 @@ void ModelEntityItem::update(const quint64& updateTime) { } else { _lastAnimated = now; } + EntityItem::update(now); // let our base class handle it's updates... } void ModelEntityItem::debugDump() const { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 0b2508ec80..502b21af12 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -46,7 +46,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData); virtual void update(const quint64& now); - virtual SimulationState computeSimulationState() const; + virtual bool needsToCallUpdate() const; virtual void debugDump() const; diff --git a/libraries/entities/src/MovingEntitiesOperator.cpp b/libraries/entities/src/MovingEntitiesOperator.cpp index 045e07a910..86b8de7b10 100644 --- a/libraries/entities/src/MovingEntitiesOperator.cpp +++ b/libraries/entities/src/MovingEntitiesOperator.cpp @@ -49,9 +49,10 @@ MovingEntitiesOperator::~MovingEntitiesOperator() { } -void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACube& oldCube, const AACube& newCube) { +void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACube& newCube) { EntityTreeElement* oldContainingElement = _tree->getContainingElement(entity->getEntityItemID()); AABox newCubeClamped = newCube.clamp(0.0f, 1.0f); + AACube oldCube = entity->getOldMaximumAACube(); AABox oldCubeClamped = oldCube.clamp(0.0f, 1.0f); if (_wantDebug) { @@ -290,3 +291,9 @@ OctreeElement* MovingEntitiesOperator::possiblyCreateChildAt(OctreeElement* elem } return NULL; } + +void MovingEntitiesOperator::finish() { + foreach(const EntityToMoveDetails& details, _entitiesToMove) { + details.entity->setOldMaximumAACube(details.newCube); + } +} diff --git a/libraries/entities/src/MovingEntitiesOperator.h b/libraries/entities/src/MovingEntitiesOperator.h index fbec898cec..bddf5da450 100644 --- a/libraries/entities/src/MovingEntitiesOperator.h +++ b/libraries/entities/src/MovingEntitiesOperator.h @@ -38,11 +38,12 @@ public: MovingEntitiesOperator(EntityTree* tree); ~MovingEntitiesOperator(); - void addEntityToMoveList(EntityItem* entity, const AACube& oldCube, const AACube& newCube); + void addEntityToMoveList(EntityItem* entity, const AACube& newCube); virtual bool preRecursion(OctreeElement* element); virtual bool postRecursion(OctreeElement* element); virtual OctreeElement* possiblyCreateChildAt(OctreeElement* element, int childIndex); bool hasMovingEntities() const { return _entitiesToMove.size() > 0; } + void finish(); private: EntityTree* _tree; QSet _entitiesToMove; diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index b3316978a9..17dbd46727 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -9,216 +9,64 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include +//#include #include "EntityItem.h" -#include "MovingEntitiesOperator.h" #include "SimpleEntitySimulation.h" -void SimpleEntitySimulation::update(QSet& 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::updateEntitiesInternal(const quint64& now) { + QSet::iterator itemItr = _movingEntities.begin(); + while (itemItr != _movingEntities.end()) { + EntityItem* entity = *itemItr; + if (!entity->isMoving()) { + itemItr = _movingEntities.erase(itemItr); + _movableButStoppedEntities.insert(entity); + } else { + entity->simulate(now); + _entitiesToBeSorted.insert(entity); + ++itemItr; + } } } -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; +void SimpleEntitySimulation::addEntityInternal(EntityItem* entity) { + if (entity->getCollisionsWillMove()) { + if (entity->isMoving()) { + _movingEntities.insert(entity); + } else { + _movableButStoppedEntities.insert(entity); + } } - 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::removeEntityInternal(EntityItem* entity) { + _movingEntities.remove(entity); + _movableButStoppedEntities.remove(entity); } -void SimpleEntitySimulation::clearEntities() { - foreach (EntityItem* entity, _changedEntities) { - entity->clearUpdateFlags(); - entity->setSimulationState(EntityItem::Static); +const int SIMPLE_SIMULATION_DIRTY_FLAGS = EntityItem::DIRTY_VELOCITY | EntityItem::DIRTY_MOTION_TYPE; + +void SimpleEntitySimulation::entityChangedInternal(EntityItem* entity) { + int dirtyFlags = entity->getDirtyFlags(); + if (dirtyFlags & SIMPLE_SIMULATION_DIRTY_FLAGS) { + if (entity->getCollisionsWillMove()) { + if (entity->isMoving()) { + _movingEntities.insert(entity); + _movableButStoppedEntities.remove(entity); + } else { + _movingEntities.remove(entity); + _movableButStoppedEntities.insert(entity); + } + } else { + _movingEntities.remove(entity); + _movableButStoppedEntities.remove(entity); + } } - _changedEntities.clear(); +} + +void SimpleEntitySimulation::clearEntitiesInternal() { _movingEntities.clear(); - _mortalEntities.clear(); -} - -void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet& 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(); + _movableButStoppedEntities.clear(); } -void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSet& entitiesToDelete) { - if (_entityTree && _movingEntities.size() > 0) { - PerformanceTimer perfTimer("_movingEntities"); - MovingEntitiesOperator moveOperator(_entityTree); - QList::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& entitiesToDelete) { - QList::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); -} - - diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 7d0e8f0113..92b6a28215 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -19,29 +19,17 @@ class SimpleEntitySimulation : public EntitySimulation { public: SimpleEntitySimulation() : EntitySimulation() { } - virtual ~SimpleEntitySimulation() { setEntityTree(NULL); } - - virtual void update(QSet& entitiesToDelete); - - virtual void addEntity(EntityItem* entity); - virtual void removeEntity(EntityItem* entity); - virtual void entityChanged(EntityItem* entity); - - virtual void clearEntities(); + virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); } protected: - void updateEntityState(EntityItem* entity); - void clearEntityState(EntityItem* entity); + virtual void updateEntitiesInternal(const quint64& now); + virtual void addEntityInternal(EntityItem* entity); + virtual void removeEntityInternal(EntityItem* entity); + virtual void entityChangedInternal(EntityItem* entity); + virtual void clearEntitiesInternal(); - QList& getMovingEntities() { return _movingEntities; } - - void updateChangedEntities(quint64 now, QSet& entitiesToDelete); - void updateMovingEntities(quint64 now, QSet& entitiesToDelete); - void updateMortalEntities(quint64 now, QSet& entitiesToDelete); - - QSet _changedEntities; // entities that have changed in the last frame - QList _movingEntities; // entities that need to be updated - QList _mortalEntities; // non-moving entities that need to be checked for expiry + QSet _movingEntities; + QSet _movableButStoppedEntities; }; #endif // hifi_SimpleEntitySimulation_h