Merge branch 'master' of https://github.com/highfidelity/hifi into entityCollisions

This commit is contained in:
ZappoMan 2014-12-08 14:15:20 -08:00
commit efbe8ce757
38 changed files with 501 additions and 381 deletions

View file

@ -117,6 +117,7 @@ function setupToolBars() {
leftMargin: TEXT_MARGIN, leftMargin: TEXT_MARGIN,
topMargin: TEXT_MARGIN, topMargin: TEXT_MARGIN,
alpha: ALPHA_OFF, alpha: ALPHA_OFF,
backgroundAlpha: ALPHA_OFF,
visible: true visible: true
})); }));
} }

View file

@ -115,6 +115,7 @@ function setupTimer() {
width: 0, width: 0,
height: 0, height: 0,
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true visible: true
}); });
@ -129,6 +130,7 @@ function setupTimer() {
width: slider.w, width: slider.w,
height: slider.h, height: slider.h,
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true visible: true
}); });
slider.foreground = Overlays.addOverlay("text", { slider.foreground = Overlays.addOverlay("text", {
@ -138,6 +140,7 @@ function setupTimer() {
width: slider.pos * slider.w, width: slider.pos * slider.w,
height: slider.h, height: slider.h,
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true visible: true
}); });

View file

@ -206,6 +206,7 @@ var progressDialog = (function () {
height: backgroundHeight, height: backgroundHeight,
imageURL: backgroundUrl, imageURL: backgroundUrl,
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });
@ -216,6 +217,7 @@ var progressDialog = (function () {
textColor: textColor, textColor: textColor,
backgroundColor: textBackground, backgroundColor: textBackground,
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });
@ -226,6 +228,7 @@ var progressDialog = (function () {
textColor: textColor, textColor: textColor,
backgroundColor: textBackground, backgroundColor: textBackground,
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });
@ -1169,24 +1172,22 @@ var toolBar = (function () {
menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2; menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2;
loadURLMenuItem = Overlays.addOverlay("text", { loadURLMenuItem = Overlays.addOverlay("text", {
x: newModelButton.x - menuItemWidth,
y: newModelButton.y + menuItemOffset,
height: menuItemHeight, height: menuItemHeight,
backgroundColor: menuBackgroundColor, backgroundColor: menuBackgroundColor,
topMargin: menuItemMargin, topMargin: menuItemMargin,
text: "Model URL", text: "Model URL",
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });
loadFileMenuItem = Overlays.addOverlay("text", { loadFileMenuItem = Overlays.addOverlay("text", {
x: newModelButton.x - menuItemWidth,
y: newModelButton.y + menuItemOffset + menuItemHeight,
height: menuItemHeight, height: menuItemHeight,
backgroundColor: menuBackgroundColor, backgroundColor: menuBackgroundColor,
topMargin: menuItemMargin, topMargin: menuItemMargin,
text: "Model File", text: "Model File",
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });
@ -1493,6 +1494,7 @@ var ExportMenu = function (opts) {
width: scaleViewWidth, width: scaleViewWidth,
height: height, height: height,
alpha: 0.0, alpha: 0.0,
backgroundAlpha: 0.0,
color: { red: 255, green: 255, blue: 255 }, color: { red: 255, green: 255, blue: 255 },
text: "1" text: "1"
}); });
@ -2480,6 +2482,7 @@ function Tooltip() {
text: "", text: "",
color: { red: 228, green: 228, blue: 228 }, color: { red: 228, green: 228, blue: 228 },
alpha: 0.8, alpha: 0.8,
backgroundAlpha: 0.8,
visible: false visible: false
}); });
this.show = function (doShow) { this.show = function (doShow) {

View file

@ -319,7 +319,7 @@ function ScaleSelector() {
width: this.SECOND_PART, height: this.height, width: this.SECOND_PART, height: this.height,
topMargin: 13, topMargin: 13,
text: this.scale.toString(), text: this.scale.toString(),
alpha: 0.9, backgroundAlpha: 0.0,
visible: editToolsOn visible: editToolsOn
}); });
this.powerOverlay = Overlays.addOverlay("text", { this.powerOverlay = Overlays.addOverlay("text", {
@ -327,7 +327,7 @@ function ScaleSelector() {
width: this.SECOND_PART, height: this.height, width: this.SECOND_PART, height: this.height,
leftMargin: 28, leftMargin: 28,
text: this.power.toString(), text: this.power.toString(),
alpha: 0.0, backgroundAlpha: 0.0,
visible: false visible: false
}); });
this.setScale = function(scale) { this.setScale = function(scale) {

View file

@ -117,7 +117,7 @@ function ScaleSelector() {
width: this.SECOND_PART, height: this.height, width: this.SECOND_PART, height: this.height,
topMargin: 13, topMargin: 13,
text: this.scale.toString(), text: this.scale.toString(),
alpha: 0.0, backgroundAlpha: 0.0,
visible: editToolsOn, visible: editToolsOn,
color: activeUIColor color: activeUIColor
}); });
@ -126,7 +126,7 @@ function ScaleSelector() {
width: this.SECOND_PART, height: this.height, width: this.SECOND_PART, height: this.height,
leftMargin: 28, leftMargin: 28,
text: this.power.toString(), text: this.power.toString(),
alpha: 0.0, backgroundAlpha: 0.0,
visible: false, visible: false,
color: activeUIColor color: activeUIColor
}); });

View file

@ -116,7 +116,7 @@ function ScaleSelector() {
width: this.SECOND_PART, height: this.height, width: this.SECOND_PART, height: this.height,
topMargin: 13, topMargin: 13,
text: this.scale.toString(), text: this.scale.toString(),
alpha: 0.0, backgroundAlpha: 0.0,
visible: editToolsOn, visible: editToolsOn,
color: activeUIColor color: activeUIColor
}); });
@ -125,7 +125,7 @@ function ScaleSelector() {
width: this.SECOND_PART, height: this.height, width: this.SECOND_PART, height: this.height,
leftMargin: 28, leftMargin: 28,
text: this.power.toString(), text: this.power.toString(),
alpha: 0.0, backgroundAlpha: 0.0,
visible: false, visible: false,
color: activeUIColor color: activeUIColor
}); });

View file

@ -81,7 +81,7 @@ ExportMenu = function (opts) {
y: pos.y + margin, y: pos.y + margin,
width: scaleViewWidth, width: scaleViewWidth,
height: height, height: height,
alpha: 0.0, backgroundAlpha: 0.0,
color: { red: 255, green: 255, blue: 255 }, color: { red: 255, green: 255, blue: 255 },
text: "1" text: "1"
}); });

View file

@ -26,6 +26,7 @@ function Tooltip() {
text: "", text: "",
color: { red: 128, green: 128, blue: 128 }, color: { red: 128, green: 128, blue: 128 },
alpha: 0.2, alpha: 0.2,
backgroundAlpha: 0.2,
visible: false visible: false
}); });
this.show = function (doShow) { this.show = function (doShow) {

View file

@ -390,6 +390,7 @@ var ZoomTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "+", text: "+",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });
var decreaseButton = Overlays.addOverlay("text", { var decreaseButton = Overlays.addOverlay("text", {
@ -403,6 +404,7 @@ var ZoomTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "-", text: "-",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });
var zoomBar = Overlays.addOverlay("text", { var zoomBar = Overlays.addOverlay("text", {
@ -416,6 +418,7 @@ var ZoomTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "", text: "",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });
var zoomHandle = Overlays.addOverlay("text", { var zoomHandle = Overlays.addOverlay("text", {
@ -428,6 +431,7 @@ var ZoomTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "", text: "",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });
@ -501,6 +505,7 @@ var ArrowTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "^", text: "^",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });
var leftButton = Overlays.addOverlay("text", { var leftButton = Overlays.addOverlay("text", {
@ -514,6 +519,7 @@ var ArrowTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "<", text: "<",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });
var rightButton = Overlays.addOverlay("text", { var rightButton = Overlays.addOverlay("text", {
@ -540,6 +546,7 @@ var ArrowTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "v", text: "v",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });
var centerButton = Overlays.addOverlay("text", { var centerButton = Overlays.addOverlay("text", {
@ -553,6 +560,7 @@ var ArrowTool = function(opts) {
leftMargin: 4, leftMargin: 4,
text: "", text: "",
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: true, visible: true,
}); });

View file

@ -368,6 +368,7 @@ SelectionDisplay = (function () {
color: { red: 0, green: 0, blue: 0}, color: { red: 0, green: 0, blue: 0},
backgroundColor: { red: 255, green: 255, blue: 255 }, backgroundColor: { red: 255, green: 255, blue: 255 },
alpha: 0.7, alpha: 0.7,
backgroundAlpha: 0.7,
visible: false, visible: false,
isFacingAvatar: true, isFacingAvatar: true,
drawInFront: true, drawInFront: true,

View file

@ -40,6 +40,7 @@ progressDialog = (function () {
textColor: textColor, textColor: textColor,
backgroundColor: textBackground, backgroundColor: textBackground,
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });
@ -50,6 +51,7 @@ progressDialog = (function () {
textColor: textColor, textColor: textColor,
backgroundColor: textBackground, backgroundColor: textBackground,
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });

View file

@ -138,6 +138,7 @@ ToolBar = function(x, y, direction) {
width: this.width, width: this.width,
height: this.height, height: this.height,
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
visible: false visible: false
}); });
this.spacing = []; this.spacing = [];
@ -243,7 +244,10 @@ ToolBar = function(x, y, direction) {
this.tools[tool].setAlpha(alpha); this.tools[tool].setAlpha(alpha);
} }
if (this.back != null) { if (this.back != null) {
Overlays.editOverlay(this.back, { alpha: alpha}); Overlays.editOverlay(this.back, {
alpha: alpha,
backgroundAlpha: alpha
});
} }
} else { } else {
this.tools[tool].setAlpha(alpha); this.tools[tool].setAlpha(alpha);
@ -263,7 +267,7 @@ ToolBar = function(x, y, direction) {
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING, ((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
visible: true, visible: true,
backgroundColor: color, backgroundColor: color,
alpha: alpha backgroundAlpha: alpha
}); });
} }
} }

View file

@ -92,7 +92,7 @@ function drawLobby() {
}; };
var orbShellProps = { 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, position: orbPosition,
rotation: towardsMe, rotation: towardsMe,
dimensions: orbDimensions, dimensions: orbDimensions,
@ -113,6 +113,7 @@ function drawLobby() {
text: "", text: "",
lineHeight: lineHeight, lineHeight: lineHeight,
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
ignoreRayIntersection: true, ignoreRayIntersection: true,
visible: false, visible: false,
isFacingAvatar: true isFacingAvatar: true

View file

@ -140,24 +140,22 @@ var toolBar = (function () {
menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2; menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2;
loadURLMenuItem = Overlays.addOverlay("text", { loadURLMenuItem = Overlays.addOverlay("text", {
x: newModelButton.x - menuItemWidth,
y: newModelButton.y + menuItemOffset,
height: menuItemHeight, height: menuItemHeight,
backgroundColor: menuBackgroundColor, backgroundColor: menuBackgroundColor,
topMargin: menuItemMargin, topMargin: menuItemMargin,
text: "Model URL", text: "Model URL",
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });
loadFileMenuItem = Overlays.addOverlay("text", { loadFileMenuItem = Overlays.addOverlay("text", {
x: newModelButton.x - menuItemWidth,
y: newModelButton.y + menuItemOffset + menuItemHeight,
height: menuItemHeight, height: menuItemHeight,
backgroundColor: menuBackgroundColor, backgroundColor: menuBackgroundColor,
topMargin: menuItemMargin, topMargin: menuItemMargin,
text: "Model File", text: "Model File",
alpha: 0.9, alpha: 0.9,
backgroundAlpha: 0.9,
visible: false visible: false
}); });

View file

@ -115,6 +115,7 @@ function createNotification(text) {
color: textColor, color: textColor,
backgroundColor: backColor, backgroundColor: backColor,
alpha: backgroundAlpha, alpha: backgroundAlpha,
backgroundAlpha: backgroundAlpha,
topMargin: topMargin, topMargin: topMargin,
leftMargin: leftMargin, leftMargin: leftMargin,
font: {size: fontSize}, font: {size: fontSize},
@ -160,7 +161,7 @@ function fadeIn(noticeIn, buttonIn) {
pauseTimer = Script.setInterval(function() { pauseTimer = Script.setInterval(function() {
q++; q++;
qFade = q / 10.0; qFade = q / 10.0;
Overlays.editOverlay(noticeIn, {alpha: qFade}); Overlays.editOverlay(noticeIn, {alpha: qFade, backgroundAlpha: qFade});
Overlays.editOverlay(buttonIn, {alpha: qFade}); Overlays.editOverlay(buttonIn, {alpha: qFade});
if (q >= 9.0) { if (q >= 9.0) {
Script.clearInterval(pauseTimer); Script.clearInterval(pauseTimer);
@ -344,7 +345,7 @@ function fadeOut(noticeOut, buttonOut, arraysOut) {
pauseTimer = Script.setInterval(function() { pauseTimer = Script.setInterval(function() {
r--; r--;
rFade = r / 10.0; rFade = r / 10.0;
Overlays.editOverlay(noticeOut, {alpha: rFade}); Overlays.editOverlay(noticeOut, {alpha: rFade, backgroundAlpha: rFade});
Overlays.editOverlay(buttonOut, {alpha: rFade}); Overlays.editOverlay(buttonOut, {alpha: rFade});
if (r < 0) { if (r < 0) {
dismiss(noticeOut, buttonOut, arraysOut); dismiss(noticeOut, buttonOut, arraysOut);

View file

@ -69,7 +69,8 @@ var text = Overlays.addOverlay("text", {
topMargin: 4, topMargin: 4,
leftMargin: 4, leftMargin: 4,
text: "Here is some text.\nAnd a second line.", 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 // This will create an image overlay, which starts out as invisible
@ -170,6 +171,7 @@ var clipboardPreview = Overlays.addOverlay("clipboard", {
// Demonstrate retrieving overlay properties // Demonstrate retrieving overlay properties
print("Text overlay text property value =\n" + Overlays.getProperty(text, "text")); print("Text overlay text property value =\n" + Overlays.getProperty(text, "text"));
print("Text overlay alpha =\n" + Overlays.getProperty(text, "alpha")); 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 visible =\n" + Overlays.getProperty(text, "visible"));
print("Text overlay font size =\n" + Overlays.getProperty(text, "font").size); print("Text overlay font size =\n" + Overlays.getProperty(text, "font").size);
print("Text overlay anchor =\n" + Overlays.getProperty(text, "anchor")); print("Text overlay anchor =\n" + Overlays.getProperty(text, "anchor"));

View file

@ -57,6 +57,7 @@ var statusText = Overlays.addOverlay("text", {
height: 20, height: 20,
backgroundColor: { red: 0, green: 0, blue: 0}, backgroundColor: { red: 0, green: 0, blue: 0},
alpha: 1.0, alpha: 1.0,
backgroundAlpha: 1.0,
color: { red: 255, green: 255, blue: 255}, color: { red: 255, green: 255, blue: 255},
topMargin: 4, topMargin: 4,
leftMargin: 4, leftMargin: 4,

View file

@ -44,6 +44,7 @@ var inputWindow = Overlays.addOverlay("text", {
color: textColor, color: textColor,
backgroundColor: backColor, backgroundColor: backColor,
alpha: backgroundAlpha, alpha: backgroundAlpha,
backgroundAlpha: backgroundAlpha,
topMargin: topMargin, topMargin: topMargin,
leftMargin: leftMargin, leftMargin: leftMargin,
font: {size: fontSize}, font: {size: fontSize},

View file

@ -150,7 +150,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
} }
glm::quat rotation = getRotation(); glm::quat rotation = getRotation();
if (needsSimulation() && _model->isActive()) { if (needsToCallUpdate() && _model->isActive()) {
_model->setScaleToFit(true, dimensions); _model->setScaleToFit(true, dimensions);
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
_model->setRotation(rotation); _model->setRotation(rotation);
@ -253,8 +253,8 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
return result; return result;
} }
bool RenderableModelEntityItem::needsSimulation() const { bool RenderableModelEntityItem::needsToCallUpdate() const {
return _needsInitialSimulation || getSimulationState() == EntityItem::Moving; return _needsInitialSimulation || ModelEntityItem::needsToCallUpdate();
} }
EntityItemProperties RenderableModelEntityItem::getProperties() const { EntityItemProperties RenderableModelEntityItem::getProperties() const {

View file

@ -57,9 +57,11 @@ public:
void** intersectedObject, bool precisionPicking) const; void** intersectedObject, bool precisionPicking) const;
Model* getModel(EntityTreeRenderer* renderer); Model* getModel(EntityTreeRenderer* renderer);
bool needsToCallUpdate() const;
private: private:
void remapTextures(); void remapTextures();
bool needsSimulation() const;
Model* _model; Model* _model;
bool _needsInitialSimulation; bool _needsInitialSimulation;

View file

@ -16,12 +16,14 @@
#include "ui/TextRenderer.h" #include "ui/TextRenderer.h"
const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
const float DEFAULT_MARGIN = 0.1f; const float DEFAULT_MARGIN = 0.1f;
const int FIXED_FONT_POINT_SIZE = 40; const int FIXED_FONT_POINT_SIZE = 40;
const float LINE_SCALE_RATIO = 1.2f; const float LINE_SCALE_RATIO = 1.2f;
Text3DOverlay::Text3DOverlay() : Text3DOverlay::Text3DOverlay() :
_backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundColor(DEFAULT_BACKGROUND_COLOR),
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),
_lineHeight(0.1f), _lineHeight(0.1f),
_leftMargin(DEFAULT_MARGIN), _leftMargin(DEFAULT_MARGIN),
_topMargin(DEFAULT_MARGIN), _topMargin(DEFAULT_MARGIN),
@ -35,6 +37,7 @@ Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) :
Planar3DOverlay(text3DOverlay), Planar3DOverlay(text3DOverlay),
_text(text3DOverlay->_text), _text(text3DOverlay->_text),
_backgroundColor(text3DOverlay->_backgroundColor), _backgroundColor(text3DOverlay->_backgroundColor),
_backgroundAlpha(text3DOverlay->_backgroundAlpha),
_lineHeight(text3DOverlay->_lineHeight), _lineHeight(text3DOverlay->_lineHeight),
_leftMargin(text3DOverlay->_leftMargin), _leftMargin(text3DOverlay->_leftMargin),
_topMargin(text3DOverlay->_topMargin), _topMargin(text3DOverlay->_topMargin),
@ -88,8 +91,8 @@ void Text3DOverlay::render(RenderArgs* args) {
const float MAX_COLOR = 255.0f; const float MAX_COLOR = 255.0f;
xColor backgroundColor = getBackgroundColor(); xColor backgroundColor = getBackgroundColor();
float alpha = getAlpha(); glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR,
glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, alpha); getBackgroundAlpha());
glm::vec2 dimensions = getDimensions(); glm::vec2 dimensions = getDimensions();
glm::vec2 halfDimensions = dimensions * 0.5f; 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); 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); glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR);
float alpha = getAlpha();
QStringList lines = _text.split("\n"); QStringList lines = _text.split("\n");
int lineOffset = maxHeight; int lineOffset = maxHeight;
foreach(QString thisLine, lines) { 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()) { if (properties.property("lineHeight").isValid()) {
setLineHeight(properties.property("lineHeight").toVariant().toFloat()); setLineHeight(properties.property("lineHeight").toVariant().toFloat());
} }
@ -200,6 +208,9 @@ QScriptValue Text3DOverlay::getProperty(const QString& property) {
if (property == "backgroundColor") { if (property == "backgroundColor") {
return xColorToScriptValue(_scriptEngine, _backgroundColor); return xColorToScriptValue(_scriptEngine, _backgroundColor);
} }
if (property == "backgroundAlpha") {
return _backgroundAlpha;
}
if (property == "lineHeight") { if (property == "lineHeight") {
return _lineHeight; return _lineHeight;
} }

View file

@ -37,6 +37,7 @@ public:
float getBottomMargin() const { return _bottomMargin; } float getBottomMargin() const { return _bottomMargin; }
bool getIsFacingAvatar() const { return _isFacingAvatar; } bool getIsFacingAvatar() const { return _isFacingAvatar; }
xColor getBackgroundColor(); xColor getBackgroundColor();
float getBackgroundAlpha() const { return _backgroundAlpha; }
// setters // setters
void setText(const QString& text) { _text = text; } void setText(const QString& text) { _text = text; }
@ -59,6 +60,7 @@ private:
QString _text; QString _text;
xColor _backgroundColor; xColor _backgroundColor;
float _backgroundAlpha;
float _lineHeight; float _lineHeight;
float _leftMargin; float _leftMargin;
float _topMargin; float _topMargin;

View file

@ -19,6 +19,7 @@
TextOverlay::TextOverlay() : TextOverlay::TextOverlay() :
_backgroundColor(DEFAULT_BACKGROUND_COLOR), _backgroundColor(DEFAULT_BACKGROUND_COLOR),
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),
_leftMargin(DEFAULT_MARGIN), _leftMargin(DEFAULT_MARGIN),
_topMargin(DEFAULT_MARGIN), _topMargin(DEFAULT_MARGIN),
_fontSize(DEFAULT_FONTSIZE) _fontSize(DEFAULT_FONTSIZE)
@ -29,6 +30,7 @@ TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
Overlay2D(textOverlay), Overlay2D(textOverlay),
_text(textOverlay->_text), _text(textOverlay->_text),
_backgroundColor(textOverlay->_backgroundColor), _backgroundColor(textOverlay->_backgroundColor),
_backgroundAlpha(textOverlay->_backgroundAlpha),
_leftMargin(textOverlay->_leftMargin), _leftMargin(textOverlay->_leftMargin),
_topMargin(textOverlay->_topMargin), _topMargin(textOverlay->_topMargin),
_fontSize(textOverlay->_fontSize) _fontSize(textOverlay->_fontSize)
@ -63,11 +65,10 @@ void TextOverlay::render(RenderArgs* args) {
return; // do nothing if we're not visible return; // do nothing if we're not visible
} }
const float MAX_COLOR = 255.0f; const float MAX_COLOR = 255.0f;
xColor backgroundColor = getBackgroundColor(); xColor backgroundColor = getBackgroundColor();
float alpha = getAlpha(); glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR,
glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, alpha); getBackgroundAlpha());
glBegin(GL_QUADS); glBegin(GL_QUADS);
glVertex2f(_bounds.left(), _bounds.top()); glVertex2f(_bounds.left(), _bounds.top());
@ -85,6 +86,7 @@ void TextOverlay::render(RenderArgs* args) {
int y = _bounds.top() + _topMargin + topAdjust; int y = _bounds.top() + _topMargin + topAdjust;
glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR); glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR);
float alpha = getAlpha();
QStringList lines = _text.split("\n"); QStringList lines = _text.split("\n");
int lineOffset = 0; int lineOffset = 0;
foreach(QString thisLine, lines) { 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()) { if (properties.property("leftMargin").isValid()) {
setLeftMargin(properties.property("leftMargin").toVariant().toInt()); setLeftMargin(properties.property("leftMargin").toVariant().toInt());
} }
@ -150,6 +156,9 @@ QScriptValue TextOverlay::getProperty(const QString& property) {
if (property == "backgroundColor") { if (property == "backgroundColor") {
return xColorToScriptValue(_scriptEngine, _backgroundColor); return xColorToScriptValue(_scriptEngine, _backgroundColor);
} }
if (property == "backgroundAlpha") {
return _backgroundAlpha;
}
if (property == "leftMargin") { if (property == "leftMargin") {
return _leftMargin; return _leftMargin;
} }

View file

@ -28,6 +28,7 @@
#include "Overlay2D.h" #include "Overlay2D.h"
const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
const int DEFAULT_MARGIN = 10; const int DEFAULT_MARGIN = 10;
const int DEFAULT_FONTSIZE = 11; const int DEFAULT_FONTSIZE = 11;
const int DEFAULT_FONT_WEIGHT = 50; const int DEFAULT_FONT_WEIGHT = 50;
@ -46,6 +47,7 @@ public:
int getLeftMargin() const { return _leftMargin; } int getLeftMargin() const { return _leftMargin; }
int getTopMargin() const { return _topMargin; } int getTopMargin() const { return _topMargin; }
xColor getBackgroundColor(); xColor getBackgroundColor();
float getBackgroundAlpha() const { return _backgroundAlpha; }
// setters // setters
void setText(const QString& text) { _text = text; } void setText(const QString& text) { _text = text; }
@ -62,6 +64,7 @@ public:
private: private:
QString _text; QString _text;
xColor _backgroundColor; xColor _backgroundColor;
float _backgroundAlpha;
int _leftMargin; int _leftMargin;
int _topMargin; int _topMargin;
int _fontSize; int _fontSize;

View file

@ -209,7 +209,9 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
propertiesA.setPosition(newPositionA * (float)TREE_SCALE); propertiesA.setPosition(newPositionA * (float)TREE_SCALE);
propertiesA.setLastEdited(now); 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); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
} }
@ -226,7 +228,9 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
propertiesB.setPosition(newPositionB * (float)TREE_SCALE); propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
propertiesB.setLastEdited(now); 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); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB);
} }
} }
@ -332,6 +336,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());
_entityTree->updateEntity(entityItemID, properties); _entityTree->updateEntity(entity, properties);
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties);
} }

View file

@ -29,7 +29,7 @@ const float EntityItem::DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
const float EntityItem::DEFAULT_MASS = 1.0f; const float EntityItem::DEFAULT_MASS = 1.0f;
const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL; const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL;
const QString EntityItem::DEFAULT_USER_DATA = QString(""); 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 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 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; 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::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::NO_ANGULAR_VELOCITY = glm::vec3(0.0f, 0.0f, 0.0f);
const glm::vec3 EntityItem::DEFAULT_ANGULAR_VELOCITY = NO_ANGULAR_VELOCITY; 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_VISIBLE = true;
const bool EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS = false; const bool EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS = false;
const bool EntityItem::DEFAULT_COLLISIONS_WILL_MOVE = false; const bool EntityItem::DEFAULT_COLLISIONS_WILL_MOVE = false;
@ -58,6 +58,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_lastEditedFromRemote = 0; _lastEditedFromRemote = 0;
_lastEditedFromRemoteInRemoteTime = 0; _lastEditedFromRemoteInRemoteTime = 0;
_lastSimulated = 0;
_lastUpdated = 0; _lastUpdated = 0;
_created = 0; // TODO: when do we actually want to make this "now" _created = 0; // TODO: when do we actually want to make this "now"
_changedOnServer = 0; _changedOnServer = 0;
@ -88,12 +89,12 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) {
_lastEdited = 0; _lastEdited = 0;
_lastEditedFromRemote = 0; _lastEditedFromRemote = 0;
_lastEditedFromRemoteInRemoteTime = 0; _lastEditedFromRemoteInRemoteTime = 0;
_lastSimulated = 0;
_lastUpdated = 0; _lastUpdated = 0;
_created = 0; _created = 0;
_updateFlags = 0; _dirtyFlags = 0;
_changedOnServer = 0; _changedOnServer = 0;
initFromEntityItemID(entityItemID); initFromEntityItemID(entityItemID);
_simulationState = EntityItem::Static;
} }
EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) { EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) {
@ -101,13 +102,13 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
_lastEdited = 0; _lastEdited = 0;
_lastEditedFromRemote = 0; _lastEditedFromRemote = 0;
_lastEditedFromRemoteInRemoteTime = 0; _lastEditedFromRemoteInRemoteTime = 0;
_lastSimulated = 0;
_lastUpdated = 0; _lastUpdated = 0;
_created = properties.getCreated(); _created = properties.getCreated();
_updateFlags = 0; _dirtyFlags = 0;
_changedOnServer = 0; _changedOnServer = 0;
initFromEntityItemID(entityItemID); initFromEntityItemID(entityItemID);
setProperties(properties, true); // force copy setProperties(properties, true); // force copy
_simulationState = EntityItem::Static;
} }
EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
@ -154,7 +155,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
ByteCountCoded<quint32> typeCoder = getType(); ByteCountCoded<quint32> typeCoder = getType();
QByteArray encodedType = typeCoder; QByteArray encodedType = typeCoder;
quint64 updateDelta = getLastUpdated() <= getLastEdited() ? 0 : getLastUpdated() - getLastEdited(); quint64 updateDelta = getLastSimulated() <= getLastEdited() ? 0 : getLastSimulated() - getLastEdited();
ByteCountCoded<quint64> updateDeltaCoder = updateDelta; ByteCountCoded<quint64> updateDeltaCoder = updateDelta;
QByteArray encodedUpdateDelta = updateDeltaCoder; QByteArray encodedUpdateDelta = updateDeltaCoder;
EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags propertyFlags(PROP_LAST_ITEM);
@ -450,9 +451,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta; ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta;
quint64 updateDelta = updateDeltaCoder; quint64 updateDelta = updateDeltaCoder;
if (overwriteLocalData) { 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) { if (wantDebug) {
qDebug() << "_lastUpdated=" << _lastUpdated; qDebug() << "_lastUpdated =" << _lastUpdated;
qDebug() << "_lastEdited=" << _lastEdited; qDebug() << "_lastEdited=" << _lastEdited;
qDebug() << "lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted; qDebug() << "lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted;
} }
@ -565,20 +566,20 @@ bool EntityItem::isRestingOnSurface() const {
&& _gravity.y < 0.0f; && _gravity.y < 0.0f;
} }
void EntityItem::update(const quint64& updateTime) { void EntityItem::simulate(const quint64& now) {
bool wantDebug = false; bool wantDebug = false;
if (_lastUpdated == 0) { if (_lastSimulated == 0) {
_lastUpdated = updateTime; _lastSimulated = now;
} }
float timeElapsed = (float)(updateTime - _lastUpdated) / (float)(USECS_PER_SECOND); float timeElapsed = (float)(now - _lastSimulated) / (float)(USECS_PER_SECOND);
if (wantDebug) { if (wantDebug) {
qDebug() << "********** EntityItem::update()"; qDebug() << "********** EntityItem::update()";
qDebug() << " entity ID=" << getEntityItemID(); qDebug() << " entity ID=" << getEntityItemID();
qDebug() << " updateTime=" << updateTime; qDebug() << " now=" << now;
qDebug() << " _lastUpdated=" << _lastUpdated; qDebug() << " _lastSimulated=" << _lastSimulated;
qDebug() << " timeElapsed=" << timeElapsed; qDebug() << " timeElapsed=" << timeElapsed;
qDebug() << " hasVelocity=" << hasVelocity(); qDebug() << " hasVelocity=" << hasVelocity();
qDebug() << " hasGravity=" << hasGravity(); qDebug() << " hasGravity=" << hasGravity();
@ -611,10 +612,10 @@ void EntityItem::update(const quint64& updateTime) {
} }
} }
_lastUpdated = updateTime; _lastSimulated = now;
if (wantDebug) { if (wantDebug) {
qDebug() << " ********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; qDebug() << " ********** EntityItem::update() .... SETTING _lastSimulated=" << _lastSimulated;
} }
if (hasAngularVelocity()) { if (hasAngularVelocity()) {
@ -631,13 +632,13 @@ void EntityItem::update(const quint64& updateTime) {
setRotation(rotation); setRotation(rotation);
// handle damping for angular velocity // handle damping for angular velocity
if (getAngularDamping() > 0.0f) { float dampingTimescale = getAngularDamping();
glm::vec3 dampingResistance = getAngularVelocity() * getAngularDamping(); if (dampingTimescale > 0.0f) {
glm::vec3 newAngularVelocity = getAngularVelocity() - (dampingResistance * timeElapsed); float dampingFactor = glm::clamp(timeElapsed / dampingTimescale, 0.0f, 1.0f);
glm::vec3 newAngularVelocity = (1.0f - dampingFactor) * getAngularVelocity();
setAngularVelocity(newAngularVelocity); setAngularVelocity(newAngularVelocity);
if (wantDebug) { if (wantDebug) {
qDebug() << " getDamping():" << getDamping(); qDebug() << " dampingTimescale :" << dampingTimescale;
qDebug() << " dampingResistance:" << dampingResistance;
qDebug() << " newAngularVelocity:" << newAngularVelocity; qDebug() << " newAngularVelocity:" << newAngularVelocity;
} }
} }
@ -688,13 +689,15 @@ void EntityItem::update(const quint64& updateTime) {
} }
// handle damping for velocity // handle damping for velocity
glm::vec3 dampingResistance = velocity * getDamping(); float dampingTimescale = getDamping();
if (wantDebug) { if (dampingTimescale > 0.0f) {
qDebug() << " getDamping():" << getDamping(); float dampingFactor = glm::clamp(timeElapsed / dampingTimescale, 0.0f, 1.0f);
qDebug() << " dampingResistance:" << dampingResistance; velocity *= (1.0f - dampingFactor);
qDebug() << " dampingResistance * timeElapsed:" << dampingResistance * timeElapsed; if (wantDebug) {
qDebug() << " dampingTimescale:" << dampingTimescale;
qDebug() << " newVelocity:" << velocity;
}
} }
velocity -= dampingResistance * timeElapsed;
if (wantDebug) { if (wantDebug) {
qDebug() << " velocity AFTER dampingResistance:" << velocity; qDebug() << " velocity AFTER dampingResistance:" << velocity;
@ -707,6 +710,7 @@ void EntityItem::update(const quint64& updateTime) {
velocity = NO_VELOCITY; velocity = NO_VELOCITY;
} }
// NOTE: the simulation should NOT set any DirtyFlags on this entity
setPosition(position); // this will automatically recalculate our collision shape setPosition(position); // this will automatically recalculate our collision shape
setVelocity(velocity); setVelocity(velocity);
@ -719,20 +723,18 @@ void EntityItem::update(const quint64& updateTime) {
} }
} }
EntityItem::SimulationState EntityItem::computeSimulationState() const { bool EntityItem::isMoving() const {
if (hasVelocity() || (hasGravity() && !isRestingOnSurface()) || hasAngularVelocity()) { return hasVelocity() || (hasGravity() && !isRestingOnSurface()) || hasAngularVelocity();
return EntityItem::Moving;
}
if (isMortal()) {
return EntityItem::Mortal;
}
return EntityItem::Static;
} }
bool EntityItem::lifetimeHasExpired() const { bool EntityItem::lifetimeHasExpired() const {
return isMortal() && (getAge() > getLifetime()); return isMortal() && (getAge() > getLifetime());
} }
quint64 EntityItem::getExpiry() const {
return _created + (quint64)(_lifetime * (float)USECS_PER_SECOND);
}
EntityItemProperties EntityItem::getProperties() const { EntityItemProperties EntityItem::getProperties() const {
EntityItemProperties properties; EntityItemProperties properties;
properties._id = getID(); 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(position, updatePositionInMeters); // this will call recalculate collision shape if needed
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, updateDimensionsInMeters); // NOTE: radius is obsolete
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, updateRotation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass); SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, updateMass);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, setVelocityInMeters); SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, updateVelocityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, setGravityInMeters); SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping); SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, setLifetime); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, updateScript);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint); 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(angularDamping, setAngularDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel); SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible); SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, setIgnoreForCollisions); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, updateIgnoreForCollisions);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, setCollisionsWillMove); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked); SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
@ -948,7 +950,7 @@ void EntityItem::updatePosition(const glm::vec3& value) {
if (_position != value) { if (_position != value) {
_position = value; _position = value;
recalculateCollisionShape(); recalculateCollisionShape();
_updateFlags |= EntityItem::UPDATE_POSITION; _dirtyFlags |= EntityItem::DIRTY_POSITION;
} }
} }
@ -957,7 +959,7 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) {
if (_position != position) { if (_position != position) {
_position = position; _position = position;
recalculateCollisionShape(); recalculateCollisionShape();
_updateFlags |= EntityItem::UPDATE_POSITION; _dirtyFlags |= EntityItem::DIRTY_POSITION;
} }
} }
@ -965,7 +967,7 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
if (_dimensions != value) { if (_dimensions != value) {
_dimensions = value; _dimensions = value;
recalculateCollisionShape(); recalculateCollisionShape();
_updateFlags |= EntityItem::UPDATE_SHAPE; _dirtyFlags |= EntityItem::DIRTY_SHAPE;
} }
} }
@ -974,7 +976,7 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
if (_dimensions != dimensions) { if (_dimensions != dimensions) {
_dimensions = dimensions; _dimensions = dimensions;
recalculateCollisionShape(); recalculateCollisionShape();
_updateFlags |= EntityItem::UPDATE_SHAPE; _dirtyFlags |= EntityItem::DIRTY_SHAPE;
} }
} }
@ -982,21 +984,21 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
if (_rotation != rotation) { if (_rotation != rotation) {
_rotation = rotation; _rotation = rotation;
recalculateCollisionShape(); recalculateCollisionShape();
_updateFlags |= EntityItem::UPDATE_POSITION; _dirtyFlags |= EntityItem::DIRTY_POSITION;
} }
} }
void EntityItem::updateMass(float value) { void EntityItem::updateMass(float value) {
if (_mass != value) { if (_mass != value) {
_mass = value; _mass = value;
_updateFlags |= EntityItem::UPDATE_MASS; _dirtyFlags |= EntityItem::DIRTY_MASS;
} }
} }
void EntityItem::updateVelocity(const glm::vec3& value) { void EntityItem::updateVelocity(const glm::vec3& value) {
if (_velocity != value) { if (_velocity != value) {
_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; glm::vec3 velocity = value / (float) TREE_SCALE;
if (_velocity != velocity) { if (_velocity != velocity) {
_velocity = velocity; _velocity = velocity;
_updateFlags |= EntityItem::UPDATE_VELOCITY; _dirtyFlags |= EntityItem::DIRTY_VELOCITY;
} }
} }
void EntityItem::updateGravity(const glm::vec3& value) { void EntityItem::updateGravity(const glm::vec3& value) {
if (_gravity != value) { if (_gravity != value) {
_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; glm::vec3 gravity = value / (float) TREE_SCALE;
if (_gravity != gravity) { if (_gravity != gravity) {
_gravity = gravity; _gravity = gravity;
_updateFlags |= EntityItem::UPDATE_VELOCITY; _dirtyFlags |= EntityItem::DIRTY_VELOCITY;
} }
} }
void EntityItem::updateAngularVelocity(const glm::vec3& value) { void EntityItem::updateAngularVelocity(const glm::vec3& value) {
if (_angularVelocity != value) { if (_angularVelocity != value) {
_angularVelocity = value; _angularVelocity = value;
_updateFlags |= EntityItem::UPDATE_VELOCITY; _dirtyFlags |= EntityItem::DIRTY_VELOCITY;
} }
} }
void EntityItem::updateIgnoreForCollisions(bool value) { void EntityItem::updateIgnoreForCollisions(bool value) {
if (_ignoreForCollisions != value) { if (_ignoreForCollisions != value) {
_ignoreForCollisions = value; _ignoreForCollisions = value;
_updateFlags |= EntityItem::UPDATE_COLLISION_GROUP; _dirtyFlags |= EntityItem::DIRTY_COLLISION_GROUP;
} }
} }
void EntityItem::updateCollisionsWillMove(bool value) { void EntityItem::updateCollisionsWillMove(bool value) {
if (_collisionsWillMove != value) { if (_collisionsWillMove != value) {
_collisionsWillMove = value; _collisionsWillMove = value;
_updateFlags |= EntityItem::UPDATE_MOTION_TYPE; _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
} }
} }
void EntityItem::updateLifetime(float value) { void EntityItem::updateLifetime(float value) {
if (_lifetime != value) { if (_lifetime != value) {
_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;
}
}

View file

@ -41,15 +41,17 @@ class EntityTreeElementExtraEncodeData;
class EntityItem { class EntityItem {
public: public:
enum EntityUpdateFlags { enum EntityDirtyFlags {
UPDATE_POSITION = 0x0001, DIRTY_POSITION = 0x0001,
UPDATE_VELOCITY = 0x0002, DIRTY_VELOCITY = 0x0002,
UPDATE_MASS = 0x0004, DIRTY_MASS = 0x0004,
UPDATE_COLLISION_GROUP = 0x0008, DIRTY_COLLISION_GROUP = 0x0008,
UPDATE_MOTION_TYPE = 0x0010, DIRTY_MOTION_TYPE = 0x0010,
UPDATE_SHAPE = 0x0020, DIRTY_SHAPE = 0x0020,
UPDATE_LIFETIME = 0x0040 DIRTY_LIFETIME = 0x0040,
//UPDATE_APPEARANCE = 0x8000, // add new simulation-relevant flags above
// all other flags below
DIRTY_SCRIPT = 0x8000
}; };
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly 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 /// has changed. This will be called with properties change or when new data is loaded from a stream
virtual void somethingChangedNotification() { } 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 /// Last edited time of this entity universal usecs
quint64 getLastEdited() const { return _lastEdited; } quint64 getLastEdited() const { return _lastEdited; }
void setLastEdited(quint64 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 float getEditedAgo() const /// Elapsed seconds since this entity was last edited
{ return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; }
@ -121,17 +123,14 @@ public:
unsigned char* bufferOut, int sizeIn, int& sizeOut); unsigned char* bufferOut, int sizeIn, int& sizeOut);
static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, size_t length, int clockSkew); 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 { // perform linear extrapolation for SimpleEntitySimulation
Static, void simulate(const quint64& now);
Mortal,
Moving
} SimulationState;
// computes the SimulationState that the entity SHOULD be in. bool needsToCallUpdate() const { return false; }
// Use getSimulationState() to find the state under which it is currently categorized.
virtual SimulationState computeSimulationState() const;
virtual void debugDump() const; virtual void debugDump() const;
@ -221,11 +220,14 @@ public:
/// age of this entity in seconds /// age of this entity in seconds
float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; }
bool lifetimeHasExpired() const; bool lifetimeHasExpired() const;
quint64 getExpiry() const;
// position, size, and bounds related helpers // position, size, and bounds related helpers
float getSize() const; /// get maximum dimension in domain scale units (0.0 - 1.0) float getSize() const; /// get maximum dimension in domain scale units (0.0 - 1.0)
AACube getMaximumAACube() const; AACube getMaximumAACube() const;
AACube getMinimumAACube() 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) AABox getAABox() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0)
static const QString DEFAULT_SCRIPT; static const QString DEFAULT_SCRIPT;
@ -278,7 +280,7 @@ public:
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; } virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } 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 updatePosition(const glm::vec3& value);
void updatePositionInMeters(const glm::vec3& value); void updatePositionInMeters(const glm::vec3& value);
void updateDimensions(const glm::vec3& value); void updateDimensions(const glm::vec3& value);
@ -293,13 +295,13 @@ public:
void updateIgnoreForCollisions(bool value); void updateIgnoreForCollisions(bool value);
void updateCollisionsWillMove(bool value); void updateCollisionsWillMove(bool value);
void updateLifetime(float value); void updateLifetime(float value);
void updateScript(const QString& value);
uint32_t getUpdateFlags() const { return _updateFlags; } uint32_t getDirtyFlags() const { return _dirtyFlags; }
void clearUpdateFlags() { _updateFlags = 0; } void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; }
SimulationState getSimulationState() const { return _simulationState; }
void setSimulationState(SimulationState state) { _simulationState = state; } bool isMoving() const;
protected: 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
@ -309,7 +311,8 @@ protected:
QUuid _id; QUuid _id;
uint32_t _creatorTokenID; uint32_t _creatorTokenID;
bool _newlyCreated; 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 _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 _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 quint64 _lastEditedFromRemoteInRemoteTime; // time in server time space the last time we received and edit from the server
@ -324,12 +327,12 @@ protected:
float _mass; float _mass;
glm::vec3 _velocity; glm::vec3 _velocity;
glm::vec3 _gravity; glm::vec3 _gravity;
float _damping; float _damping; // timescale
float _lifetime; float _lifetime;
QString _script; QString _script;
glm::vec3 _registrationPoint; glm::vec3 _registrationPoint;
glm::vec3 _angularVelocity; glm::vec3 _angularVelocity;
float _angularDamping; float _angularDamping; // timescale
bool _visible; bool _visible;
bool _ignoreForCollisions; bool _ignoreForCollisions;
bool _collisionsWillMove; bool _collisionsWillMove;
@ -343,11 +346,10 @@ protected:
void setRadius(float value); void setRadius(float value);
AACubeShape _collisionShape; 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 // DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
// data structures. It is the responsibility of the EntityTree to relay changes entity and clear flags. uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
uint32_t _updateFlags;
}; };

View file

@ -9,12 +9,178 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <AACube.h>
#include "EntitySimulation.h" #include "EntitySimulation.h"
#include "MovingEntitiesOperator.h"
void EntitySimulation::setEntityTree(EntityTree* tree) { void EntitySimulation::setEntityTree(EntityTree* tree) {
if (_entityTree && _entityTree != tree) { if (_entityTree && _entityTree != tree) {
clearEntities(); _mortalEntities.clear();
_nextExpiry = quint64(-1);
_updateableEntities.clear();
_entitiesToBeSorted.clear();
} }
_entityTree = tree; _entityTree = tree;
} }
void EntitySimulation::updateEntities(QSet<EntityItem*>& 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<EntityItem*>::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<EntityItem*>::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<EntityItem*>::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();
}

View file

@ -14,36 +14,61 @@
#include <QSet> #include <QSet>
#include <PerfStat.h>
#include "EntityTree.h" #include "EntityTree.h"
class EntitySimulation { class EntitySimulation {
public: public:
EntitySimulation() : _entityTree(NULL) { } EntitySimulation() : _entityTree(NULL) { }
virtual ~EntitySimulation() {} virtual ~EntitySimulation() { setEntityTree(NULL); }
/// \param tree pointer to EntityTree which is stored internally /// \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. /// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted.
virtual void update(QSet<EntityItem*>& entitiesToDelete) = 0; void updateEntities(QSet<EntityItem*>& entitiesToDelete);
/// \param entity pointer to EntityItem to add to the simulation /// \param entity pointer to EntityItem to add to the simulation
/// \sideeffect the EntityItem::_simulationState member may be updated to indicate membership to internal list /// \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 /// \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 /// \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 /// \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; } EntityTree* getEntityTree() { return _entityTree; }
protected: 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; EntityTree* _entityTree;
// We maintain multiple lists, each for its distinct purpose.
// An entity may be in more than one list.
QSet<EntityItem*> _mortalEntities; // entities that have an expiry
quint64 _nextExpiry;
QSet<EntityItem*> _updateableEntities; // entities that need update() called
QSet<EntityItem*> _entitiesToBeSorted; // entities that were moved by THIS simulation and might need to be resorted in the tree
QSet<EntityItem*> _entitiesToDelete;
}; };
#endif // hifi_EntitySimulation_h #endif // hifi_EntitySimulation_h

View file

@ -83,6 +83,7 @@ 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::postAddEntity(EntityItem* entity) { void EntityTree::postAddEntity(EntityItem* entity) {
assert(entity); assert(entity);
entity->setOldMaximumAACube(entity->getMaximumAACube());
// check to see if we need to simulate this entity.. // check to see if we need to simulate this entity..
if (_simulation) { if (_simulation) {
_simulation->addEntity(entity); _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; qDebug() << "UNEXPECTED!!!! don't call updateEntity() on entity items that don't exist. entityID=" << entityID;
return false; 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 // enforce support for locked entities. If an entity is currently locked, then the only
// property we allow you to change is the locked property. // property we allow you to change is the locked property.
if (existingEntity->getLocked()) { if (entity->getLocked()) {
if (properties.lockedChanged()) { if (properties.lockedChanged()) {
bool wantsLocked = properties.getLocked(); bool wantsLocked = properties.getLocked();
if (!wantsLocked) { if (!wantsLocked) {
EntityItemProperties tempProperties; EntityItemProperties tempProperties;
tempProperties.setLocked(wantsLocked); tempProperties.setLocked(wantsLocked);
UpdateEntityOperator theOperator(this, containingElement, existingEntity, tempProperties); UpdateEntityOperator theOperator(this, containingElement, entity, tempProperties);
recurseTreeWithOperator(&theOperator); recurseTreeWithOperator(&theOperator);
_isDirty = true; _isDirty = true;
if (_simulation && existingEntity->getUpdateFlags() != 0) {
_simulation->entityChanged(existingEntity);
}
} }
} }
} else { } else {
// check to see if we need to simulate this entity... QString entityScriptBefore = entity->getScript();
QString entityScriptBefore = existingEntity->getScript();
UpdateEntityOperator theOperator(this, containingElement, existingEntity, properties); UpdateEntityOperator theOperator(this, containingElement, entity, properties);
recurseTreeWithOperator(&theOperator); recurseTreeWithOperator(&theOperator);
entity->setOldMaximumAACube(entity->getMaximumAACube());
_isDirty = true; _isDirty = true;
if (_simulation && existingEntity->getUpdateFlags() != 0) { if (_simulation && entity->getDirtyFlags() != 0) {
_simulation->entityChanged(existingEntity); _simulation->entityChanged(entity);
} }
QString entityScriptAfter = existingEntity->getScript(); QString entityScriptAfter = entity->getScript();
if (entityScriptBefore != entityScriptAfter) { 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) { 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 false;
} }
return true; return true;
} }
EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItem* result = NULL; EntityItem* result = NULL;
@ -576,7 +590,7 @@ void EntityTree::update() {
if (_simulation) { if (_simulation) {
lockForWrite(); lockForWrite();
QSet<EntityItem*> entitiesToDelete; QSet<EntityItem*> entitiesToDelete;
_simulation->update(entitiesToDelete); _simulation->updateEntities(entitiesToDelete);
if (entitiesToDelete.size() > 0) { if (entitiesToDelete.size() > 0) {
// translate into list of ID's // translate into list of ID's
QSet<EntityItemID> idsToDelete; QSet<EntityItemID> idsToDelete;

View file

@ -82,7 +82,13 @@ public:
void postAddEntity(EntityItem* entityItem); void postAddEntity(EntityItem* entityItem);
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties); 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); 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 deleteEntity(const EntityItemID& entityID);
void deleteEntities(QSet<EntityItemID> entityIDs); void deleteEntities(QSet<EntityItemID> entityIDs);
void removeEntityFromSimulation(EntityItem* entity); void removeEntityFromSimulation(EntityItem* entity);
@ -156,6 +162,8 @@ signals:
private: private:
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
EntityTreeElement* containingElement);
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);

View file

@ -761,7 +761,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID); EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID);
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
if (entityItem->getUpdateFlags()) { if (entityItem->getDirtyFlags()) {
_myTree->entityChanged(entityItem); _myTree->entityChanged(entityItem);
} }
bool bestFitAfter = bestFitEntityBounds(entityItem); bool bestFitAfter = bestFitEntityBounds(entityItem);

View file

@ -373,17 +373,11 @@ bool ModelEntityItem::isAnimatingSomething() const {
!getAnimationURL().isEmpty(); !getAnimationURL().isEmpty();
} }
EntityItem::SimulationState ModelEntityItem::computeSimulationState() const { bool ModelEntityItem::needsToCallUpdate() const {
// if we're animating then we need to have update() periodically called on this entity return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate();
// which means we need to categorized as Moving
return isAnimatingSomething() ? EntityItem::Moving : EntityItem::computeSimulationState();
} }
void ModelEntityItem::update(const quint64& updateTime) { void ModelEntityItem::update(const quint64& now) {
EntityItem::update(updateTime); // let our base class handle it's updates...
quint64 now = updateTime;
// only advance the frame index if we're playing // only advance the frame index if we're playing
if (getAnimationIsPlaying()) { if (getAnimationIsPlaying()) {
float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND;
@ -392,6 +386,7 @@ void ModelEntityItem::update(const quint64& updateTime) {
} else { } else {
_lastAnimated = now; _lastAnimated = now;
} }
EntityItem::update(now); // let our base class handle it's updates...
} }
void ModelEntityItem::debugDump() const { void ModelEntityItem::debugDump() const {

View file

@ -46,7 +46,7 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData); EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
virtual void update(const quint64& now); virtual void update(const quint64& now);
virtual SimulationState computeSimulationState() const; virtual bool needsToCallUpdate() const;
virtual void debugDump() const; virtual void debugDump() const;

View file

@ -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()); EntityTreeElement* oldContainingElement = _tree->getContainingElement(entity->getEntityItemID());
AABox newCubeClamped = newCube.clamp(0.0f, 1.0f); AABox newCubeClamped = newCube.clamp(0.0f, 1.0f);
AACube oldCube = entity->getOldMaximumAACube();
AABox oldCubeClamped = oldCube.clamp(0.0f, 1.0f); AABox oldCubeClamped = oldCube.clamp(0.0f, 1.0f);
if (_wantDebug) { if (_wantDebug) {
@ -290,3 +291,9 @@ OctreeElement* MovingEntitiesOperator::possiblyCreateChildAt(OctreeElement* elem
} }
return NULL; return NULL;
} }
void MovingEntitiesOperator::finish() {
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
details.entity->setOldMaximumAACube(details.newCube);
}
}

View file

@ -38,11 +38,12 @@ public:
MovingEntitiesOperator(EntityTree* tree); MovingEntitiesOperator(EntityTree* tree);
~MovingEntitiesOperator(); ~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 preRecursion(OctreeElement* element);
virtual bool postRecursion(OctreeElement* element); virtual bool postRecursion(OctreeElement* element);
virtual OctreeElement* possiblyCreateChildAt(OctreeElement* element, int childIndex); virtual OctreeElement* possiblyCreateChildAt(OctreeElement* element, int childIndex);
bool hasMovingEntities() const { return _entitiesToMove.size() > 0; } bool hasMovingEntities() const { return _entitiesToMove.size() > 0; }
void finish();
private: private:
EntityTree* _tree; EntityTree* _tree;
QSet<EntityToMoveDetails> _entitiesToMove; QSet<EntityToMoveDetails> _entitiesToMove;

View file

@ -9,216 +9,64 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <AACube.h> //#include <PerfStat.h>
#include <PerfStat.h>
#include "EntityItem.h" #include "EntityItem.h"
#include "MovingEntitiesOperator.h"
#include "SimpleEntitySimulation.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) { void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
assert(entity && entity->getSimulationState() == EntityItem::Static); QSet<EntityItem*>::iterator itemItr = _movingEntities.begin();
EntityItem::SimulationState state = entity->computeSimulationState(); while (itemItr != _movingEntities.end()) {
switch(state) { EntityItem* entity = *itemItr;
case EntityItem::Moving: if (!entity->isMoving()) {
_movingEntities.push_back(entity); itemItr = _movingEntities.erase(itemItr);
entity->setSimulationState(state); _movableButStoppedEntities.insert(entity);
break; } else {
case EntityItem::Mortal: entity->simulate(now);
_mortalEntities.push_back(entity); _entitiesToBeSorted.insert(entity);
entity->setSimulationState(state); ++itemItr;
break; }
case EntityItem::Static:
default:
break;
} }
} }
void SimpleEntitySimulation::removeEntity(EntityItem* entity) { void SimpleEntitySimulation::addEntityInternal(EntityItem* entity) {
assert(entity); if (entity->getCollisionsWillMove()) {
// make sure to remove it from any of our simulation lists if (entity->isMoving()) {
EntityItem::SimulationState state = entity->getSimulationState(); _movingEntities.insert(entity);
switch (state) { } else {
case EntityItem::Moving: _movableButStoppedEntities.insert(entity);
_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) { void SimpleEntitySimulation::removeEntityInternal(EntityItem* entity) {
assert(entity); _movingEntities.remove(entity);
// we batch all changes and deal with them in updateChangedEntities() _movableButStoppedEntities.remove(entity);
_changedEntities.insert(entity);
} }
void SimpleEntitySimulation::clearEntities() { const int SIMPLE_SIMULATION_DIRTY_FLAGS = EntityItem::DIRTY_VELOCITY | EntityItem::DIRTY_MOTION_TYPE;
foreach (EntityItem* entity, _changedEntities) {
entity->clearUpdateFlags(); void SimpleEntitySimulation::entityChangedInternal(EntityItem* entity) {
entity->setSimulationState(EntityItem::Static); 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(); _movingEntities.clear();
_mortalEntities.clear(); _movableButStoppedEntities.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);
}

View file

@ -19,29 +19,17 @@
class SimpleEntitySimulation : public EntitySimulation { class SimpleEntitySimulation : public EntitySimulation {
public: public:
SimpleEntitySimulation() : EntitySimulation() { } SimpleEntitySimulation() : EntitySimulation() { }
virtual ~SimpleEntitySimulation() { setEntityTree(NULL); } virtual ~SimpleEntitySimulation() { clearEntitiesInternal(); }
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: protected:
void updateEntityState(EntityItem* entity); virtual void updateEntitiesInternal(const quint64& now);
void clearEntityState(EntityItem* entity); virtual void addEntityInternal(EntityItem* entity);
virtual void removeEntityInternal(EntityItem* entity);
virtual void entityChangedInternal(EntityItem* entity);
virtual void clearEntitiesInternal();
QList<EntityItem*>& getMovingEntities() { return _movingEntities; } QSet<EntityItem*> _movingEntities;
QSet<EntityItem*> _movableButStoppedEntities;
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 #endif // hifi_SimpleEntitySimulation_h